2 ** gunzip65 - a gunzip utility for 6502-based machines.
4 ** Piotr Fusik <fox@scene.pl>
6 ** This should be considered as a test of my zlib-compatible library
7 ** rather than a real application.
8 ** It's not user-friendly, fault-tolerant, whatever.
9 ** However, it really works for real GZIP files, provided they are small
10 ** enough to fit in buffer[] (after decompression!).
24 ** Emulate inflatemem() if using original zlib.
25 ** As you can see, this program is quite portable.
27 unsigned inflatemem(char* dest, const char* source)
31 stream.next_in = (Bytef*) source;
32 stream.avail_in = 65535;
34 stream.next_out = dest;
35 stream.avail_out = 65535;
37 stream.zalloc = (alloc_func) 0;
38 stream.zfree = (free_func) 0;
40 inflateInit2(&stream, -MAX_WBITS);
41 inflate(&stream, Z_FINISH);
44 return stream.total_out;
49 ** Structure of a GZIP file:
52 ** Offset 0: Signature (2 bytes: 0x1f, 0x8b)
53 ** Offset 2: Compression method (1 byte: 8 == "deflate")
54 ** Offset 3: Flags (1 byte: see below)
55 ** Offset 4: File date and time (4 bytes)
56 ** Offset 8: Extra flags (1 byte)
57 ** Offset 9: Target OS (1 byte: DOS, Amiga, Unix, etc.)
58 ** if (flags & FEXTRA) { 2 bytes of length, then length bytes }
59 ** if (flags & FNAME) { ASCIIZ filename }
60 ** if (flags & FCOMMENT) { ASCIIZ comment }
61 ** if (flags & FHCRC) { 2 bytes of CRC }
63 ** 2. Deflate compressed data.
66 ** Offset 0: CRC-32 (4 bytes)
67 ** Offset 4: uncompressed file length (4 bytes)
70 /* Flags in the GZIP header. */
71 #define FTEXT 1 /* Extra text */
72 #define FHCRC 2 /* Header CRC */
73 #define FEXTRA 4 /* Extra field */
74 #define FNAME 8 /* File name */
75 #define FCOMMENT 16 /* File comment */
78 ** We read whole GZIP file into this buffer.
79 ** Then we use this buffer for the decompressed data.
81 static unsigned char buffer[26000];
84 ** Get a 16-bit little-endian unsigned number, using unsigned char* p.
85 ** On many machines this could be (*(unsigned short*) p),
86 ** but I really like portability. :-)
88 #define GET_WORD(p) (*(p) + ((unsigned) (p)[1] << 8))
90 /* Likewise, for a 32-bit number. */
91 #define GET_LONG(p) (GET_WORD(p) + ((unsigned long) GET_WORD(p + 2) << 16))
94 ** Uncompress a GZIP file.
95 ** On entry, buffer[] should contain the whole GZIP file contents,
96 ** and the argument complen should be equal to the length of the GZIP file.
97 ** On return, buffer[] contains the uncompressed data, and the returned
98 ** value is the length of the uncompressed data.
100 unsigned uncompress_buffer(unsigned complen)
104 unsigned long unclen;
108 /* check GZIP signature */
109 if (buffer[0] != 0x1f || buffer[1] != 0x8b) {
110 puts("Not GZIP format");
114 /* check compression method (it is always (?) "deflate") */
115 if (buffer[2] != 8) {
116 puts("Unsupported compression method");
120 /* get CRC from GZIP trailer */
121 crc = GET_LONG(buffer + complen - 8);
123 /* get uncompressed length from GZIP trailer */
124 unclen = GET_LONG(buffer + complen - 4);
125 if (unclen > sizeof(buffer)) {
126 puts("Uncompressed size too big");
130 /* skip extra field, file name, comment and crc */
132 if (buffer[3] & FEXTRA)
133 ptr = buffer + 12 + GET_WORD(buffer + 10);
134 if (buffer[3] & FNAME)
136 if (buffer[3] & FCOMMENT)
138 if (buffer[3] & FHCRC)
142 ** calculate length of raw "deflate" data
143 ** (without the GZIP header and 8-byte trailer)
145 complen -= (ptr - buffer) + 8;
148 ** We will move the compressed data to the end of buffer[].
149 ** Thus the compressed data and the decompressed data (written from
150 ** the beginning of buffer[]) may overlap, as long as the decompressed
151 ** data doesn't go further than unread compressed data.
152 ** ptr2 points to the beginning of compressed data at the end
155 ptr2 = buffer + sizeof(buffer) - complen;
156 /* move the compressed data to end of buffer[] */
157 memmove(ptr2, ptr, complen);
160 puts("Inflating...");
161 unclen2 = inflatemem(buffer, ptr2);
163 /* verify uncompressed length */
164 if (unclen2 != (unsigned) unclen) {
165 puts("Uncompressed size does not match");
170 puts("Calculating CRC...");
171 if (crc32(crc32(0L, Z_NULL, 0), buffer, unclen2) != crc) {
172 puts("CRC mismatch");
176 /* return number of uncompressed bytes */
181 ** Get a filename from standard input.
183 char* get_fname(void)
185 static char filename[100];
187 fgets(filename, sizeof(filename), stdin);
188 len = strlen(filename);
189 if (len >= 1 && filename[len - 1] == '\n')
190 filename[len - 1] = '\0';
200 /* allow user to read exit messages */
201 if (doesclrscrafterexit()) {
202 atexit((void (*)) getchar);
204 #endif /* __CC65__ */
207 puts("GZIP file name:");
208 fp = fopen(get_fname(), "rb");
210 puts("Can't open GZIP file");
213 length = fread(buffer, 1, sizeof(buffer), fp);
215 if (length == sizeof(buffer)) {
216 puts("File is too long");
221 length = uncompress_buffer(length);
225 /* write uncompressed file */
226 puts("Uncompressed file name:");
227 fp = fopen(get_fname(), "wb");
229 puts("Can't create output file");
232 if (fwrite(buffer, 1, length, fp) != length) {
233 puts("Error while writing output file");