#include #include #include #include #include #include /* * Another World game data unpacker * Only DOS version are supported */ #define MAX_MEMINFO 256 #define MSG_OPENERR "can't open file %s.\n" #define MSG_WRITEERR "can't create file %s.\n" #define MSG_UNPKERR "error while unpack file %s.\n" struct __attribute__ ((__packed__)) memrec { uint8_t state; uint8_t type; uint32_t unk0; uint8_t unk1; uint8_t bank; uint32_t offset; uint16_t unk2; uint16_t pksize; uint16_t unk3; uint16_t size; }; FILE *fp; struct memrec meminfo[MAX_MEMINFO]; int count; uint8_t *pk; uint32_t size, datasize; uint32_t start, idx, oidx; uint32_t crc, chk; uint32_t pkint(int adr) { return ((pk[adr]) << 24) | ((pk[adr + 1]) << 16) | ((pk[adr + 2]) << 8) | (pk[adr + 3]); } bool rcr(bool cf) { bool rcf = chk & 1; chk >>= 1; if(cf) chk |= 0x80000000; return rcf; } bool nextChunk() { bool cf = rcr(false); if(chk == 0) { chk = pkint(idx); idx -= 4; crc ^= chk; cf = rcr(true); } return cf; } int getCode(int numChunks) { int c = 0; while(numChunks--) { c <<= 1; if(nextChunk()) c |= 1; } return c; } void decUnk1(int numChunks, int addCount) { int count = getCode(numChunks) + addCount + 1; datasize -= count; while(count--) { pk[oidx] = getCode(8); --oidx; } } void decUnk2(int numChunks) { int i = getCode(numChunks); int count = size + 1; datasize -= count; while(count--) { pk[oidx] = pk[oidx + i]; --oidx; } } bool unpack() { size = 0; datasize = pkint(idx); idx -= 4; oidx = start + datasize - 1; crc = pkint(idx); idx -= 4; chk = pkint(idx); idx -= 4; crc ^= chk; do { if(!nextChunk()) { size = 1; if(!nextChunk()) decUnk1(3, 0); else decUnk2(8); } else { int c = getCode(2); if(c == 3) { decUnk1(8, 8); } else { if(c < 2) { size = c + 2; decUnk2(c + 9); } else { size = getCode(8); decUnk2(12); } } } } while(datasize > 0); return crc == 0; } bool pkload(int i) { struct memrec *r = &meminfo[i]; char name[64]; char *nnum = (r->bank <= 0xF)?"0":""; snprintf(name, 64, "BANK%s%X", nnum, r->bank); if((fp = fopen(name, "r")) == NULL) { printf(MSG_OPENERR, name); return 0; } pk = malloc(r->size); fseek(fp, r->offset, SEEK_SET); fread(pk, r->pksize, 1, fp); fclose(fp); if(r->pksize == r->size) { return true; } else { start = 0; idx = start + r->pksize - 4; return unpack(); } } void readmem() { count = 0; printf("NUM STATE TYPE BANK OFFSET PKSIZE SIZE\n"); while(count < MAX_MEMINFO) { struct memrec *r = &meminfo[count]; fread(r, sizeof(struct memrec), 1, fp); r->offset = ntohl(r->offset); r->pksize = ntohs(r->pksize); r->size = ntohs(r->size); printf("%3i %5u %4u %4X %8X %6u %5u\n", count, r->state, r->type, r->bank, r->offset, r->pksize, r->size); if(r->state == 0xFF) break; ++count; } } int main(int argc, char **argv) { if((fp = fopen("MEMLIST.BIN", "r")) == NULL) { printf(MSG_OPENERR, "MEMLIST.BIN"); exit(1); } readmem(); fclose(fp); for(int i = 0; i < count - 1; i++) { char name[64]; char *ext; switch(meminfo[i].type) { case 0: /* sound */ ext = "snd"; break; case 1: /* music */ ext = "mus"; break; case 2: /* bitmap */ ext = "bmp"; break; case 3: /* palette */ ext = "pal"; break; case 4: /* code */ ext = "obj"; break; case 5: /* polygons */ ext = "spr"; break; default: /* other */ ext = "unk"; break; } snprintf(name, 64, "file%i.%s", i, ext); if(!pkload(i)) { printf(MSG_UNPKERR, name); goto error; } if((fp = fopen(name, "w")) == NULL) { printf(MSG_WRITEERR, name); goto error; } fwrite(pk, meminfo[i].size, 1, fp); fclose(fp); error: free(pk); } return 0; }