#include #include #include #define IXA_GET_BITS(var, nbits) do { \ if (bits_left >= nbits) { \ (var) = bit_buffer >> (32 - nbits); \ bits_left -= nbits; bit_buffer <<= nbits; \ } \ else { \ (var) = bit_buffer >> (32 - nbits); \ bit_buffer = fgetc(in)|(fgetc(in)<<8)|(fgetc(in)<<16)|(fgetc(in)<<24);\ (var) |= bit_buffer >> (bits_left + 32 - nbits); \ bit_buffer <<= (nbits - bits_left); \ bits_left += 32 - nbits; \ } \ } while (0) #define IXA_BYTE_OUT(byte) do { \ switch (rle_state) { \ case 0: if (!--rle_len) rle_state = 1; break; \ case 1: if (byte > 127) rle_state = 2, rle_len = byte - 127; \ else rle_state = 3, rle_len = byte + 1; break; \ case 2: while (rle_len--) fputc(byte, out); rle_state = 1; break; \ case 3: fputc(byte, out); if (!--rle_len) rle_state = 1; break; \ } \ } while (0) void ixa_unpack(FILE *in, FILE *out) { unsigned char window[1024], *w = &window[1], *wend = &window[1024]; unsigned char x, rle_state=0, rle_len=4, bits_left=0, *src; unsigned int pos, len, bit_buffer; window[0] = 0; while (1) { IXA_GET_BITS(x, 1); if (x) { IXA_GET_BITS(x, 8); IXA_BYTE_OUT(x); *w++ = x; if (w == wend) w = &window[0]; } else { IXA_GET_BITS(pos, 10); src = &window[pos]; if (pos == 0) break; /* end of stream */ IXA_GET_BITS(len, 4); len += 2; while (len--) { *w++ = x = *src++; IXA_BYTE_OUT(x); if (src == wend) src = &window[0]; if (w == wend) w = &window[0]; } } } } /* IXA file format: * * all offsets/sizes/counts are in little-endian order * * iXalance file header: * 8 bytes: "IXALANCE" * 32 bytes: name of demo (ASCII, may be null terminated) * 4 bytes: offset of directory header * 4 bytes: unknown ("type") * * iXalance directory header: * 4 bytes: number of directory entries * x bytes: the directory entries, 12 bytes each * * iXalance directory entry: * 4 bytes: offset of entry in file * 4 bytes: compressed size * 4 bytes: uncompressed size * * first directory entry (entry 0) is the demo script. All other entries * are LZSS/RLE compressed. * * iXalance script bytes: * 1: "push executable" (next byte = executable dir entry id) * 2: "pop part" (remove current picture/code from execution stack) * 3: "play music" (next byte = music dir entry id) * 4: "push picture" (next byte = picture dir entry id) * 5: "wait music" (next two bytes = position, line) */ #define EndGetI32(a) ((((a)[3])<<24)|(((a)[2])<<16)|(((a)[1])<<8)|((a)[0])) struct ixafile { long offset; unsigned int comp, ucomp; unsigned char type; }; void dump_ixa(char *name, FILE *fh) { unsigned char buf[48], dname[33], fname[40], c, *p; char *types[4] = {"unk", "exe", "xm", "raw"}; unsigned int i, len; struct ixafile *f; FILE *out; /* read main header */ if ((fread(buf,1,48,fh) < 48) || (memcmp(&buf[0],"IXALANCE",8) != 0)) return; printf("%s: type %u demo «%-32.32s»\n",name, EndGetI32(&buf[44]), &buf[8]); /* copy demo name, remove spaces */ p=dname; for (i = 0; i < 32; i++) if (buf[8+i]!=' ') *p++=buf[8+i]; *p='\0'; /* read dir header */ if (fseek(fh, EndGetI32(&buf[40]), SEEK_SET) || (fread(buf, 1, 4, fh) < 4) || ((len = EndGetI32(&buf[0])) < 1) || !(f = calloc(len, sizeof(struct ixafile)))) return; printf("%s: %d files in directory\n", name, len); /* read dir entries */ for (i = 0; i < len; i++) { if (fread(buf, 1, 12, fh) < 12) return; f[i].offset = EndGetI32(&buf[0]); f[i].comp = EndGetI32(&buf[4]); f[i].ucomp = EndGetI32(&buf[8]); f[i].type = 0; } /* dump script */ printf("%s: DEMO SCRIPT\n", name); if (fseek(fh, f[0].offset, SEEK_SET)) return; for (i = 0; i < f[0].comp; i++) { switch (c = fgetc(fh)) { case 1: c = fgetc(fh); i++; f[c].type = 1; printf("%s: PUSH EXE %d\n", name, c); break; case 2: printf("%s: POP\n", name); break; case 3: c = fgetc(fh); i++; f[c].type = 2; printf("%s: PLAY MUS %d\n", name, c); break; case 4: c = fgetc(fh); i++; f[c].type = 3; printf("%s: PUSH PIC %d\n", name, c); break; case 5: printf("%s: WAIT MUS POS=%d ROW=%d\n", name, fgetc(fh), fgetc(fh)); i += 2; break; default: printf("%s: *** BAD SCRIPT BYTE %d\n", name, c); break; } } /* dump parts */ for (i = 1; i < len; i++) { if (f[i].ucomp == 0) continue; if (fseek(fh, f[i].offset, SEEK_SET)) return; sprintf(fname, "%s%03d.%s", dname, i, types[f[i].type]); printf("%s: unpacking %-39s (%u bytes)\n", name, fname, f[i].ucomp); if ((out = fopen(fname, "wb"))) { ixa_unpack(fh, out); fclose(out); } } } int main(int argc, char *argv[]) { if (argc < 2) { printf("Usage: %s \n", argv[0]); } else { for (argv++; *argv; argv++) { FILE *fh; if ((fh = fopen(*argv, "rb"))) { dump_ixa(*argv, fh); fclose(fh); } } } return 0; }