summaryrefslogtreecommitdiffstats
path: root/main.c
blob: d3a6d15d4d11b565cf122d62d4692915ffb94f6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*	Copyright (C) 2020 Gentoo-libre Install

	This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>

int main(int argc, char *argv[])
{
if (!argv[1]||!argv[2]){fprintf(stderr,"Usage: %s <binary file> <granulation level (0|1|2|3|4)>\n", argv[0]); exit(1);}

int granulation;
/* For excessively large files, it's useful to skip a number of bytes after a false positive to save time despite the (small) chance of missing some png's */
switch (*argv[2])
	{
	case '0':
		granulation = 8;
		break;
	case '1':
		granulation = 64;
		break;
	case '2':
		granulation = 4096;
		break;
	case '3':
		granulation = 8192;
		break;
	case '4':
		granulation = 10000;
		break;
	default:
		fprintf(stderr,"Valid granulation levels: 0|1|2|3|4\n");
		exit(1);
	}

FILE *fp = fopen(argv[1],"rb");
if (!fp){fprintf(stderr,"Could not open file!\n"); exit(1);}

fseek(fp, 0L, SEEK_END);

int length = ftell(fp);

/* While we could read the file chunk by chunk, it's easier to just dump it into memory */
char *data = malloc(length);
if (!data){fprintf(stderr,"Malloc failed!\n"); exit(1);}

rewind(fp);


if (fread(data, 1, length, fp) != length){fprintf(stderr, "Bytes read and filesize length do not match!"); exit(1);}
fclose(fp);

if (mkdir("pngs", S_IRWXU | S_IRWXG) == -1 && errno != EEXIST){fprintf(stderr,"Could not create png directory!\n"); exit(1);}


char *header;
int offset = 0;
int size;

int count = 0;
char filename[5+4+4+1];

/* Search for png magic number in file */
while (header=memmem(data+offset, length-offset, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8))
	{
	/* Check for IHDR header, if this doesn't exist, there was a false positive, so skip ahead by granulation bytes */
	if (memcmp(header+8+4,"IHDR",4)){size = granulation; goto skip;}

	fprintf(stderr,"Found png at: %d\n", offset);

	/* Check for EOF magic number */
	char *iend = memmem(header+8, length-offset-8, "IEND", 4);
	if (!iend){fprintf(stderr, "Png has no end!\n"); exit(1);}

	/* +4 bytes for the IEND and +4 for the crc after IEND */
	size = iend-header+8;
	fprintf(stderr,"Png size: %d\n", size);

	/* Exit if digits no longer fit in filename */
	if (count > 9999){fprintf(stderr,"What are you doing?\n"); exit(1);}

	snprintf(filename, 14, "pngs/%d.png",count++);
	FILE *out = fopen(filename,"wb");
	if (!out){fprintf(stderr,"Could not write file: %s!\n", filename); exit(1);}

	fprintf(stderr,"Writing png to: %s\n",filename);

	fwrite(header, 1, size, out);
	fclose(out);

	skip:;
	if (offset+size >= length){break;}
	offset += size;
	}

free(data);

return 0;
}