]> git.sur5r.net Git - cc65/commitdiff
Added new sample gunzip65 from Piotr
authorcuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Mon, 6 Oct 2003 10:54:46 +0000 (10:54 +0000)
committercuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Mon, 6 Oct 2003 10:54:46 +0000 (10:54 +0000)
git-svn-id: svn://svn.cc65.org/cc65/trunk@2474 b7a2c559-68d2-44c3-8de9-860c34a00d81

samples/.cvsignore
samples/Makefile
samples/README
samples/gunzip65.c [new file with mode: 0644]

index 181c11b3e67e02054942230b6220966d50d7e09d..3746fbe0bd8e0684205382ff307660bd33d06ff6 100644 (file)
@@ -1,5 +1,6 @@
 ascii
 fire
+gunzip65
 hello
 mousedemo
 nachtm
index 88b4f8e1d9c84db3b0260d1675563d9ef9919906..f8fc0ffe7bf00c6098ec4a2423a45db92fcb3621 100644 (file)
@@ -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 $@ $^
 
 
 # --------------------------------------------------------------------------
index 572ec96b353c15bf49fe08c1aa52314572829e31..caa4645a7829b65b868c91cbdc8545e462a7fd5a 100644 (file)
@@ -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
                <gngking@erols.com>.
-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 <fox@scene.pl>.
+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 (file)
index 0000000..3b84982
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * gunzip65 - a gunzip utility for 6502-based machines.
+ *
+ * Piotr Fusik <fox@scene.pl>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <zlib.h>
+
+#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;
+}