/* Decrunches 'Eye of the Beholder' CPS files. */ /* (C) 2000 Kyzer/CSG */ /* fails on XANATH1.CPS & XDEATH3.CPS (they both 'overflow') */ #include #include #include #define GETWORD(x) ((x)[0]|((x)[1]<<8)) #define GETLONG(x) ((x)[0]|((x)[1]<<8)|((x)[2]<<16)|((x)[3]<<24)) #define MAXSRCLEN (64*1024) /* max length of crunched data */ #define MAXDESTLEN (1024*1024) /* max length of unpacked data */ #define EMPTYFILE (0) #define OVERFLOW (-1) #define UNKNOWNCOMP (-2) /* compression mode 0 = no compression */ int cps_copy(unsigned char *src, unsigned char *dest) { int len, x; /* get the length of the 'uncompressed' data */ if ((len = GETLONG(src)) == 0) return EMPTYFILE; /* check for source overflow. we can't overflow the destination */ if (len < 0 || len > (MAXSRCLEN - GETWORD(&src[4]) - 6)) return OVERFLOW; /* skip to the start of data and do a direct copy from source to dest */ src += GETWORD(&src[4]) + 6; for(x = len; x--;) *dest++ = *src++; return len; } /* compression mode 3 = run-length encoding */ int cps_rle(unsigned char *src, unsigned char *dest) { int len, x; char rep; /* get the length of the 'uncompressed' data */ if ((len = GETLONG(src)) == 0) return EMPTYFILE; /* check for destination overflow. */ if (len < 0 || len > MAXDESTLEN) return OVERFLOW; /* skip to start of data */ src += GETWORD(&src[4]) + 6; /* run while we have room left in output buffer */ for (x = len; x > 0;) { int rlen = *src++; if (rlen < 128) { /* direct copy, no RLE */ x -= rlen; if (x < 0) return OVERFLOW; while (rlen--) *dest++ = *src++; } else { /* repeated character RLE */ if (rlen == 0) { rlen = GETWORD(src); src+=2; } else rlen = 256-rlen; x -= rlen; if (x < 0) return OVERFLOW; rep = *src++; while (rlen--) *dest++ = rep; } } return len - x; } /* compression mode 4 = LZ77 style compression */ int cps_lz77(unsigned char *src, unsigned char *dest) { unsigned char *s = src, *d = dest, *rep; unsigned int len, code; while ((s-src) < MAXSRCLEN) { code = *s++; if (code == 0xFE) { /* mini-RLE */ len = GETWORD(s); s += 2; code = *s++; if ((d+len-dest) > MAXDESTLEN) return OVERFLOW; while (len--) *d++ = code; } else { if (code >= 0xC0) { if (code == 0xFF) {len=GETWORD(s); s+=2;} else {len=(code & 0x3F)+3;} rep = dest + GETWORD(s); s += 2; } else if (code >= 0x80) { if (code == 0x80) break; len = code & 0x3F; rep = s; s += len; } else /* code < 0x80 */ { len = (code >> 4) + 3; rep = d-(((code & 0x0F) << 8) | *s++); } if ((d+len-dest) > MAXDESTLEN) return OVERFLOW; while (len--) *d++ = *rep++; } } return d - dest; } void decrunch(char *file, unsigned char *src, unsigned char *dest) { FILE *fd; int len, slen; if ((fd = fopen(file, "rb"))) { len = fread(src, 1, MAXSRCLEN+2, fd); fclose(fd); slen = GETWORD(src); src += 2; if (slen != len && (slen+2) != len) { printf("%s: length warning: stored=%d, actual=%d\n", file, slen+2, len); } switch (*src) { case 0: len = cps_copy(src+2, dest); break; case 3: len = cps_rle (src+2, dest); break; case 4: len = cps_lz77(src+8, dest); break; default: printf("%s: unknown compression type %d\n", file, *src); return; } switch (len) { case EMPTYFILE: printf("%s: zero-length file\n", file); return; case OVERFLOW: printf("%s: output overflow\n", file); return; default: /* save file - last char of filename is changed to '_' */ file[strlen(file)-1] = '_'; if ((fd = fopen(file, "wb"))) { slen = fwrite(dest, 1, len, fd); if (slen > 0) printf("%s: saved as %d bytes\n", file, slen); else printf("%s: write failed\n", file); fclose(fd); } else { printf("%s: save failed", file); } } /* end switch */ } else { printf("%s: can't open file\n", file); } } int main(int argc, char *argv[]) { unsigned char *src, *dest; if (argc <= 1) { printf("Usage: %s \n", argv[0]); return 1; } src = (unsigned char *) malloc(MAXSRCLEN + 512); dest = (unsigned char *) malloc(MAXDESTLEN + 512); if (!src || !dest) { printf("not enough memory\n"); return 1; } argv++; while (*argv) decrunch(*argv++, src, dest); return 0; }