/* Dump the contents of Abersoft's "Adventure 1" (hereinafter ABER0210) * * Copyright 2016, John Elliott * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include #include #include /* This program does a minimal Unquill-style dump of the game's data * structures. For ease we have a 64k buffer containing the game; this * means that if you wanted to compile for DOS, it would be * necessary to reduce the buffer size to 40k or so and start mucking * about with offsets when accessing it. */ unsigned char zxram[0x10000]; /* ABER0210 does not have a list of short names for the rooms. For ease of * annotation, provide one. */ static const char *room_hints[256] = { "Ylem", /* 0 */ "End of road", "Hill", "In building", "Valley", "Forest by valley", "Forest", "2-inch slit", "Grate", "Under grate", "Cobbled crawl", /* 10 */ "Debris room", "Sloping canyon", "Bird chamber", "Top of pit", "Hall of mists", "Crack is too small", "East bank of fissure", "Low room", "Hall of mountain king", "Bottom of pit", /* 20 */ "You didn't make it", "Dome is unclimbable", "West end of twopit room", "Eastern pit", "Western pit", "Climb up the plant", "West of fissure", "Low N/S passage", "South side chamber", "West side chamber", /* 30 */ ".", "Snake blocks way", "Y2", "Jumble of rock", "Window overlooking pit", "Dirty broken passage", "Brink of small pit", "Small pit", "Dusty rock room", "Passage N of mists", /* 40 */ "West end of mists", "Alike maze 1", "Alike maze 2", "Alike maze 3", "Alike maze 4", "Dead end 1", "Dead end 2", "Dead end 3", "Alike maze 5", "Alike maze 6", /* 50 */ "Alike maze 7", "Alike maze 8", "Alike maze 9", "Dead end 4", "Alike maze 10", "Dead end 5", "Above bird chamber", "Dead end 6", "Passage N of mists 2", "E end of long hall", /* 60 */ "W end of long hall", "Crossover", "Dead end 7", "Complex junction", "Bedquilt", "Swiss cheese room", "East end of twopit room", "Low round room", "Secret N/S canyon 1", "Secret N/S canyon 2", /* 70 */ "Secret canyon junction", "Large low room", "Dead end 8", "Secret E/W canyon", "Wide place", "Canyon too tight", "Tall E/W canyon", "Mass of boulders", "1ft sewer pipes", "Alike maze 11", /* 80 */ "Dead end 9", "Dead end 10", "Alike maze 12", "Alike maze 13", "Dead end 11", "Dead end 12", "Alike maze 14", "Long narrow corridor", "Nothing to climb", "Climbed plant", /* 90 */ "Top of steep incline", "Giant room", "Passage blocked", "Giant passage", "Whirlpool cavern", "Soft room", "Oriental room", "Wide path", "Alcove", "Plover room", /* 100 */ "Dark room", "Arched hall", "Sedimentary room", "Sloping corridor", "Cul-de-sac", "Ante-room", "Different maze 1", "Witt's End", "Mirror canyon", "Window 2", /* 110 */ "Stalactite room", "Different maze 2", "Edge of reservoir", "Dead end 13", "Repository NE", "Repository SW", "Deep chasm", "Long winding corridor", "Secret canyon 1", "Secret canyon 2", /* 120 */ "Secret canyon 3", "Far side of chasm", "Long E/W corridor", "Fork in path", "Warm corridor", "Breathtaking view", "Small chamber with boulders", "Limestone passage", "Entrance to barren room", "Barren room", /* 130 */ "Different maze 3", "Different maze 4", "Different maze 5", "Different maze 6", "Different maze 7", "Different maze 8", "Different maze 9", "Different maze 10", "Different maze 11", "Dead end 14" }; /* Neither does it have a list of short names for the objects */ static const char *object_hints[] = { "", "Keys", "Lamp", "Lit lamp", "Locked grate 1", "Locked grate 2", "Open grate 1", "Open grate 2", "Cage", "Rod with star", "Rod without star", "Steps leading down", "Steps leading up", "Bird", "Bird in cage", "Closed Rusty Door", "Open Rusty Door", "Pillow", "Snake (awake)", "Snake (drowsy)", "Crystal bridge 1", "Crystal bridge 2", "Crystal bridge vanished 1", "Crystal bridge vanished 2", "Stone tablet on wall", "Clam", "Oyster", "Magazine", "Food", "Bottle of water", "Empty bottle", "Bottle of oil", "Tiny plant", "12-foot plant", "Beanstalk", "Top of beanstalk", "Beanstalk in pit", "Shadowy figure", "Axe", "Axe by bear", "Dragon 1", "Dragon 2", "Body of dragon", "?", "Wooden bridge 1", "Wooden bridge 2", "Wrecked bridge 1", "Wrecked bridge 2", "Troll 1", "Troll 2", "No troll 1", "No troll 2", "Ferocious bear", "Gentle bear", "Bear following you", "Vending machine", "Batteries", "Nugget", "Flute", "Diamonds", "Silver", "Jewellery", "Coins", "Treasure chest", "Nest of eggs", "Trident", "Vase", "Vase on the pillow", "Broken vase", "Emerald", "Platinum pyramid", "Pearl", "Rug", "Dragon on rug 1", "Dragon on rug 2", "Spices", "Bear chained", "Golden chain", "Flute under body" }; /* Set this to 0 to suppress printing addresses -- useful to diff the * dumps of the original and bugfixed versions. */ static int gl_showaddr = 1; /* Display an address, or blanks if addresses are suppressed */ void showaddr(unsigned short addr) { if (gl_showaddr) printf("%04x: ", addr); else printf(" "); } /* Read a little-endian word from Spectrum memory */ unsigned short peek2(unsigned short addr) { return zxram[addr] + 256 * zxram[addr+1]; } /* Dump compression tokens */ void dump_tokens() { int n, t; int offset, len; printf("Tokens:\n=======\n"); for (t = 0x81, n = 0x6979; n < 0x69ba; n++, t++) { offset = 0x69bb + zxram[n]; len = zxram[n+1] - zxram[n]; showaddr(offset); printf("Token 0x%02x = '%-*.*s'\n", t, len, len, zxram + offset); } } /* Expand a compression token to a text buffer */ void expand_token(unsigned int t, char *buf, unsigned int *len) { int offset; offset = 0x69bb + zxram[t+0x6979]; *len = zxram[t+1+0x6979] - zxram[t+0x6979]; if (*len > 28) *len = 28; sprintf(buf, "<%-*.*s> ", *len, *len, zxram + offset); (*len) += 3; } /* Dump a compressed string (room description, or message */ void dump_room(unsigned int offset, unsigned int len, int indent) { int x = 0; char word[32]; unsigned int wlen; if (indent == 6) { printf("%*s", indent, " "); x = indent; } while (len) { unsigned char c = zxram[offset++]; --len; if (c == 0x7F || c == 0x0d) { printf("\n%*s", indent, " "); x = indent; continue; } if (c >= 0x80) { expand_token(c - 0x80, word, &wlen); } else { word[0] = c; word[1] = 0; wlen = 1; } if (wlen + x > 80 && x > 6) { printf("\n%*s", indent, " "); x = indent; } printf("%s", word); x += wlen; } printf(".\n"); } void dump_rooms() { int n, r; int offset, len; printf("Rooms:\n======\n"); for (r = 1, n = 0x6ABA; n < 0x6BD2; n += 2, r++) { offset = peek2(n) + 0x6BE6; len = peek2(n+2) - peek2(n); showaddr(offset); printf("Room 0x%02x ;%s\n", r, room_hints[r]); dump_room(offset, len, 6); } } void dump_vocab() { int n; printf("Vocab:\n======\n"); for (n = 0x901D; n < 0x947d; n += 5) { showaddr(n); printf("%-4.4s %d\n", zxram + n, zxram[n+4]); } } char *lookup_word(int w) { static char buf[5]; int n; sprintf(buf, "?%02x?", w); for (n = 0x901D; n < 0x947d; n += 5) { if (zxram[n+4] == w) { sprintf(buf, "%-4.4s", zxram + n); return buf; } } return buf; } void dump_connections() { int n, m, r; int offset, len, dest; printf("Connections:\n============\n"); for (n = 0x9491, r=1; n < 0x95A9; n += 2, r++) { offset = peek2(n) + 0x95ED; len = peek2(n+2) - peek2(n); if (!len) continue; showaddr(offset); printf("Connections for location 0x%02x ; %s\n", r, room_hints[r]); for (m = 0; m < len; m += 2) { dest = zxram[offset + m + 1]; printf(" %s to 0x%02x ; %s\n", lookup_word(zxram[offset + m]), dest, room_hints[dest]); } } } void dump_objects() { int n, o; int offset, len; printf("Objects:\n========\n"); for (n = 0x9AB1, o = 1; n < 0x9B4D; n += 2, o++) { offset = peek2(n) + 0x9B78; len = peek2(n+2) - peek2(n); showaddr(offset); printf("Object 0x%02x '", o); /* Skip initial CR */ if (zxram[offset] == 0x0D) { ++offset; --len; } while (len > 0) { if (zxram[offset] == 0x0D) { printf("\n "); } else putchar(zxram[offset]); ++offset; --len; } printf("'\n"); } } void dump_messages() { int n, m; int offset, len; printf("Messages:\n=========\n"); for (n = 0xC289, m = 1; n < 0xc417; n += 2, m++) { offset = peek2(n) + 0xc419; len = peek2(n+2) - peek2(n); showaddr(offset); printf("Message 0x%02x:\n", m); dump_room(offset, len, 6); } } void dump_obstate() { int n; printf("Object state:\n==============\n"); for (n = 1; n <= 0x4E; n++) { showaddr(0xA7F7 + 2*n); printf("Object 0x%02x location %02x flags %02x ; %s\n", n, zxram[0xA7F7+2*n], zxram[0xA7F8+2*n], object_hints[n]); } } void show_cond(int c, int p) { switch (c) { case 0: printf("AT 0x%02x ; %s", p, room_hints[p]); break; case 1: printf("PRESENT 0x%02x ; %s", p, object_hints[p]); break; case 2: printf("RAND %d", p); break; case 3: printf("NOTPRES 0x%02x ; %s", p, object_hints[p]); break; case 4: printf("NOTZERO 0x%02x", p); break; case 5: printf("ZERO 0x%02x", p); break; case 6: printf("CARRIED 0x%02x ; %s", p, object_hints[p]); break; case 7: printf("HERE 0x%02x ; %s", p, object_hints[p]); break; case 8: printf("NOTHERE 0x%02x ; %s", p, object_hints[p]); break; case 9: printf("NOTCARR 0x%02x ; %s", p, object_hints[p]); break; case 10:printf("EXISTS 0x%02x ; %s", p, object_hints[p]); break; } } void show_message(unsigned short m) { unsigned short offset = peek2(0xC287 + 2*m); unsigned short len = peek2(0xC289 + 2*m) - offset; offset += 0xC419; dump_room(offset, len, 35); } void show_act(int a, int p) { switch (a) { case 0: printf("INVEN"); break; case 1: printf("GET 0x%02x ; %s", p, object_hints[p]); break; case 2: printf("DROP 0x%02x ; %s", p, object_hints[p]); break; case 3: printf("MESSAGE 0x%02x ; ", p); show_message(p); break; case 4: printf("DESC"); break; case 5: printf("END"); break; case 6: printf("GOTO 0x%02x ; %s", p, room_hints[p]); break; case 7: printf("SET 0x%02x", p); break; case 8: printf("CLEAR 0x%02x", p); break; case 9: printf("EXIT"); /* 01: End of game */ break; case 10:printf("OKAY"); break; case 11:printf("QUIT"); /* 02: Ask for confirmation */ break; case 12:printf("CREATE 0x%02x ; %s", p, object_hints[p]); break; case 13:printf("DESTROY 0x%02x ; %s", p, object_hints[p]); break; case 14:printf("LET 0x%02x 0x04", p); break; case 15:printf("SCORE"); /* 03: Display score */ break; case 16:printf("TARGET 0x%02x ; %s", p, room_hints[p]); break; case 17:printf("PLACE 0x%02x ; %s", p, object_hints[p]); break; } } void dump_response() { int cond, act, nextcond, nextact, c, p, a; int offset = 0xA8C1; printf("Response table:\n===============\n"); while (offset < 0xB293) { showaddr(offset); printf("%-4.4s ", lookup_word(zxram[offset])); if (zxram[offset+1] == 0xFF) printf("_ "); else printf("%-4.4s ", lookup_word(zxram[offset+1])); offset += 2; cond = peek2(offset); /* Offset to conditions */ act = peek2(offset+2); /* Offset to actions */ offset += 4; nextcond = peek2(offset+2); /* Offset to next condition */ nextact = peek2(offset+4); /* Offset to next action */ if (cond < nextcond) { printf(" Conditions:\n"); while (cond < nextcond) { c = zxram[0xB3B1 + cond]; p = zxram[0xB3B2 + cond]; showaddr(0xB3B1 + cond); printf(" %02x %02x ", c, p); cond += 2; show_cond(c, p); putchar('\n'); } } if (act < nextact) { printf(" Actions:\n"); while (act < nextact) { a = zxram[0xB7FD + act]; p = zxram[0xB7FE + act]; showaddr(0xB7FD + act); printf(" %02x %02x ", a, p); act += 2; show_act(a, p); putchar('\n'); } } } } void dump_process() { int cond, act, nextcond, nextact, c, p, a; int offset = 0xBF69; printf("Process table:\n==============\n"); while (offset < 0xBFDD) { showaddr(offset); printf("_ "); printf("_ "); cond = peek2(offset); /* Offset to conditions */ act = peek2(offset+2); /* Offset to actions */ nextcond = peek2(offset+4); /* Offset to next condition */ nextact = peek2(offset+6); /* Offset to next action */ offset += 4; if (cond < nextcond) { printf(" Conditions:\n"); while (cond < nextcond) { c = zxram[0xC031 + cond]; p = zxram[0xC032 + cond]; showaddr(0xC031 + cond); printf(" %02x %02x ", c, p); cond += 2; show_cond(c, p); putchar('\n'); } } if (act < nextact) { printf(" Actions:\n"); while (act < nextact) { a = zxram[0xC0F9 + act]; p = zxram[0xC0FA + act]; showaddr(0xC0F9 + act); printf(" %02x %02x ", a, p); act += 2; show_act(a, p); putchar('\n'); } } } } int main(int argc, char **argv) { const char *filename = "code"; FILE *fp; if (argc > 1) { filename = argv[1]; } fp = fopen(filename, "rb"); if (!fp) { perror(filename); return 1; } fread(zxram + 0x68F9, 1, 38663, fp); fclose(fp); dump_tokens(); dump_rooms(); dump_vocab(); dump_connections(); dump_objects(); dump_obstate(); dump_response(); dump_process(); dump_messages(); }