From 0f9f3d6abd7caf134b4c2dd45d24ac3d762f2e50 Mon Sep 17 00:00:00 2001 From: cuz Date: Mon, 6 Oct 2003 10:54:46 +0000 Subject: [PATCH] Added new sample gunzip65 from Piotr git-svn-id: svn://svn.cc65.org/cc65/trunk@2474 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- samples/.cvsignore | 1 + samples/Makefile | 35 +++---- samples/README | 11 ++- samples/gunzip65.c | 228 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 257 insertions(+), 18 deletions(-) create mode 100644 samples/gunzip65.c diff --git a/samples/.cvsignore b/samples/.cvsignore index 181c11b3e..3746fbe0b 100644 --- a/samples/.cvsignore +++ b/samples/.cvsignore @@ -1,5 +1,6 @@ ascii fire +gunzip65 hello mousedemo nachtm diff --git a/samples/Makefile b/samples/Makefile index 88b4f8e1d..f8fc0ffe7 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -32,34 +32,37 @@ C1541 = c1541 # -------------------------------------------------------------------------- # Rules how to make each one of the binaries -EXELIST=ascii fire hello mousedemo nachtm plasma sieve tgidemo +EXELIST=ascii fire gunzip65 hello mousedemo nachtm plasma sieve tgidemo .PHONY: all all: $(EXELIST) -ascii: $(CRT0) ascii.o $(CLIB) - @$(LD) -t $(SYS) -m ascii.map -Ln ascii.lbl -o $@ $^ +ascii: $(CRT0) ascii.o $(CLIB) + @$(LD) -t $(SYS) -m $(basename $@).map -o $@ $^ -fire: $(CRT0) fire.o $(CLIB) - @$(LD) -t $(SYS) -m fire.map -Ln fire.lbl -o $@ $^ +fire: $(CRT0) fire.o $(CLIB) + @$(LD) -t $(SYS) -m $(basename $@).map -o $@ $^ -hello: $(CRT0) hello.o $(CLIB) - @$(LD) -t $(SYS) -m hello.map -Ln hello.lbl -o $@ $^ +gunzip65: $(CRT0) gunzip65.o $(CLIB) + @$(LD) -t $(SYS) -m $(basename $@).map -o $@ $^ + +hello: $(CRT0) hello.o $(CLIB) + @$(LD) -t $(SYS) -m $(basename $@).map -o $@ $^ mousedemo: $(CRT0) mousedemo.o $(CLIB) - @$(LD) -t $(SYS) -m mousedemo.map -Ln mousedemo.lbl -o $@ $^ + @$(LD) -t $(SYS) -m $(basename $@).map -o $@ $^ -nachtm: $(CRT0) nachtm.o $(CLIB) - @$(LD) -t $(SYS) -vm -m nachtm.map -Ln nachtm.lbl -o $@ $^ +nachtm: $(CRT0) nachtm.o $(CLIB) + @$(LD) -t $(SYS) -m $(basename $@).map -o $@ $^ -plasma: $(CRT0) plasma.o $(CLIB) - @$(LD) -t $(SYS) -m plasma.map -Ln nachtm.lbl -o $@ $^ +plasma: $(CRT0) plasma.o $(CLIB) + @$(LD) -t $(SYS) -m $(basename $@).map -o $@ $^ -sieve: $(CRT0) sieve.o $(CLIB) - @$(LD) -t $(SYS) -m sieve.map -Ln sieve.lbl -o $@ $^ +sieve: $(CRT0) sieve.o $(CLIB) + @$(LD) -t $(SYS) -m $(basename $@).map -o $@ $^ -tgidemo: $(CRT0) tgidemo.o $(CLIB) - @$(LD) -t $(SYS) -m tgidemo.map -Ln tgidemo.lbl -o $@ $^ +tgidemo: $(CRT0) tgidemo.o $(CLIB) + @$(LD) -t $(SYS) -m $(basename $@).map -o $@ $^ # -------------------------------------------------------------------------- diff --git a/samples/README b/samples/README index 572ec96b3..caa4645a7 100644 --- a/samples/README +++ b/samples/README @@ -21,9 +21,9 @@ List of supplied sample programs: ----------------------------------------------------------------------------- Name: ascii Description: Shows the ASCII (or ATASCII, PETSCII) codes of typed - characters. Written and contributed by Greg King + characters. Written and contributed by Greg King . -Platforms: All platforms with conio or stdio (compile time +Platforms: All platforms with conio or stdio (compile time configurable). ----------------------------------------------------------------------------- @@ -32,6 +32,13 @@ Description: Another graphics demo written by groepaz/hitmen. Platforms: The program is currently only running on the C64, but should be portable to the C128 and CBM510 (and maybe more machines). +----------------------------------------------------------------------------- +Name: gunzip65 +Description: A gunzip utility for 6502 based machines written by Piotr + Fusik . +Platforms: Runs on all platforms with file I/O (currently the Atari and + most Commodore machines). + ----------------------------------------------------------------------------- Name: hello Description: A nice "Hello world" type program that uses the conio diff --git a/samples/gunzip65.c b/samples/gunzip65.c new file mode 100644 index 000000000..3b84982b7 --- /dev/null +++ b/samples/gunzip65.c @@ -0,0 +1,228 @@ +/* + * gunzip65 - a gunzip utility for 6502-based machines. + * + * Piotr Fusik + * + * This should be considered as a test of my zlib-compatible library + * rather than a real application. + * It's not user-friendly, fault-tolerant, whatever. + * However, it really works for real GZIP files, provided they are small + * enough to fit in buffer[] (after decompression!). + */ + +#include +#include +#include + +#ifndef __CC65__ +/* + * Emulate inflatemem() if using original zlib. + * As you can see, this program is quite portable. + */ +unsigned inflatemem(char* dest, const char* source) +{ + z_stream stream; + + stream.next_in = (Bytef*) source; + stream.avail_in = 65535; + + stream.next_out = dest; + stream.avail_out = 65535; + + stream.zalloc = (alloc_func) 0; + stream.zfree = (free_func) 0; + + inflateInit2(&stream, -MAX_WBITS); + inflate(&stream, Z_FINISH); + inflateEnd(&stream); + + return stream.total_out; +} +#endif /* __CC65__ */ + +/* + * Structure of a GZIP file: + * + * 1. GZIP header: + * Offset 0: Signature (2 bytes: 0x1f, 0x8b) + * Offset 2: Compression method (1 byte: 8 == "deflate") + * Offset 3: Flags (1 byte: see below) + * Offset 4: File date and time (4 bytes) + * Offset 8: Extra flags (1 byte) + * Offset 9: Target OS (1 byte: DOS, Amiga, Unix, etc.) + * if (flags & FEXTRA) { 2 bytes of length, then length bytes } + * if (flags & FNAME) { ASCIIZ filename } + * if (flags & FCOMMENT) { ASCIIZ comment } + * if (flags & FHCRC) { 2 bytes of CRC } + * + * 2. Deflate compressed data. + * + * 3. GZIP trailer: + * Offset 0: CRC-32 (4 bytes) + * Offset 4: uncompressed file length (4 bytes) + */ + +/* Flags in the GZIP header. */ +#define FTEXT 1 /* Extra text */ +#define FHCRC 2 /* Header CRC */ +#define FEXTRA 4 /* Extra field */ +#define FNAME 8 /* File name */ +#define FCOMMENT 16 /* File comment */ + +/* + * We read whole GZIP file into this buffer. + * Then we use this buffer for the decompressed data. + */ +static unsigned char buffer[26000]; + +/* + * Get a 16-bit little-endian unsigned number, using unsigned char* p. + * On many machines this could be (*(unsigned short*) p), + * but I really like portability. :-) + */ +#define GET_WORD(p) (*(p) + ((unsigned) (p)[1] << 8)) + +/* Likewise, for a 32-bit number. */ +#define GET_LONG(p) (GET_WORD(p) + ((unsigned long) GET_WORD(p + 2) << 16)) + +/* + * Uncompress a GZIP file. + * On entry, buffer[] should contain the whole GZIP file contents, + * and the argument complen should be equal to the length of the GZIP file. + * On return, buffer[] contains the uncompressed data, and the returned + * value is the length of the uncompressed data. + */ +unsigned uncompress_buffer(unsigned complen) +{ + unsigned char* ptr; + unsigned long crc; + unsigned long unclen; + void* ptr2; + unsigned unclen2; + + /* check GZIP signature */ + if (buffer[0] != 0x1f || buffer[1] != 0x8b) { + puts("Not GZIP format"); + return 0; + } + + /* check compression method (it is always (?) "deflate") */ + if (buffer[2] != 8) { + puts("Unsupported compression method"); + return 0; + } + + /* get CRC from GZIP trailer */ + crc = GET_LONG(buffer + complen - 8); + + /* get uncompressed length from GZIP trailer */ + unclen = GET_LONG(buffer + complen - 4); + if (unclen > sizeof(buffer)) { + puts("Uncompressed size too big"); + return 0; + } + + /* skip extra field, file name, comment and crc */ + ptr = buffer + 10; + if (buffer[3] & FEXTRA) + ptr = buffer + 12 + GET_WORD(buffer + 10); + if (buffer[3] & FNAME) + while (*ptr++ != 0); + if (buffer[3] & FCOMMENT) + while (*ptr++ != 0); + if (buffer[3] & FHCRC) + ptr += 2; + + /* + * calculate length of raw "deflate" data + * (without the GZIP header and 8-byte trailer) + */ + complen -= (ptr - buffer) + 8; + + /* + * We will move the compressed data to the end of buffer[]. + * Thus the compressed data and the decompressed data (written from + * the beginning of buffer[]) may overlap, as long as the decompressed + * data doesn't go further than unread compressed data. + * ptr2 points to the beginning of compressed data at the end + * of buffer[]. + */ + ptr2 = buffer + sizeof(buffer) - complen; + /* move the compressed data to end of buffer[] */ + memmove(ptr2, ptr, complen); + + /* uncompress */ + puts("Inflating..."); + unclen2 = inflatemem(buffer, ptr2); + + /* verify uncompressed length */ + if (unclen2 != (unsigned) unclen) { + puts("Uncompressed size does not match"); + return 0; + } + + /* verify CRC */ + puts("Calculating CRC..."); + if (crc32(crc32(0L, Z_NULL, 0), buffer, unclen2) != crc) { + puts("CRC mismatch"); + return 0; + } + + /* return number of uncompressed bytes */ + return unclen2; +} + +/* + * Get a filename from standard input. + */ +char* get_fname(void) +{ + static char filename[100]; + unsigned len; + fgets(filename, sizeof(filename), stdin); + len = strlen(filename); + if (len >= 1 && filename[len - 1] == '\n') + filename[len - 1] = '\0'; + return filename; +} + +int main(void) +{ + FILE* fp; + unsigned length; + + /* read GZIP file */ + puts("GZIP file name:"); + fp = fopen(get_fname(), "rb"); + if (!fp) { + puts("Can't open GZIP file"); + return 1; + } + length = fread(buffer, 1, sizeof(buffer), fp); + fclose(fp); + if (length == sizeof(buffer)) { + puts("File is too long"); + return 1; + } + + /* decompress */ + length = uncompress_buffer(length); + if (length == 0) + return 1; + + /* write uncompressed file */ + puts("Uncompressed file name:"); + fp = fopen(get_fname(), "wb"); + if (!fp) { + puts("Can't create output file"); + return 1; + } + if (fwrite(buffer, 1, length, fp) != length) { + puts("Error while writing output file"); + return 1; + } + fclose(fp); + + puts("Ok."); + return 0; +}