diff --git a/PLAN.md b/PLAN.md index 4710204..7380c73 100644 --- a/PLAN.md +++ b/PLAN.md @@ -28,25 +28,27 @@ card. `games/` holds all sixteen and four fan games. ScummVM's detection table (~/claude/scratch/joeyagi/scummvmAgi) names the known releases by their directory files' MD5s. -On 2026-10-20 Scott's copy of the eXo ScummVM collection -(~/Downloads/eXo/eXoScummVM/eXo/eXoScummVM: read, never changed) added nine -releases to `games/` and the other machines' releases to `games/machines/` -(Phase 5, which test-agi.sh does not run yet): 13 Amiga, 11 Apple IIgs, 10 -Atari ST, and 10 Macintosh. eXo's folders hold only the data, because eXo plays -them in ScummVM. Where a release lacks Sierra's interpreter, one of the same -version stands in (the "Interpreter" column), so the oracle can run it. +Two of Scott's collections added to them (both read, never changed): -Still to obtain (asked of Scott 2026-10-20), each with Sierra's interpreter -files: -- Sierra's 3.002.086 (any DOS King's Quest IV AGI as shipped); -- 2.089 (Space Quest 1 1.0X, 1986-09-24) and 2.230 (the Christmas card's - version 1, 1986-10-13); -- a stock 2.936 (King's Quest III 2.14 or Space Quest II 2.0D/2.0F as - shipped), to confirm the copy in Caitlyn's Destiny; -- older releases on interpreters already covered (King's Quest 1 1.0U, King's - Quest III 1.01, Space Quest 1 1.1A, King's Quest 2 2.1, Donald Duck's - Playground 1.50, Police Quest 2.0E, the demos); -- the booter disk images (Phase 5). +- **eXo ScummVM** (~/Downloads/eXo/eXoScummVM/eXo/eXoScummVM, 2026-10-20): + the data of nine releases, and the other machines' releases in + `games/machines/` (Phase 5; test-agi.sh does not look there yet): 13 + Amiga, 11 Apple IIgs, 10 Atari ST, and 10 Macintosh. +- **A TOSEC-style set of Sierra's DOS floppies** + (~/Downloads/20220303_20220303_0527, 2026-10-21), each named with its + interpreter. Its disk images were copied out with mtools, taking the + cracked first disk where the original is copy-protected (DOSBox cannot + run the disk check). It gave the eXo releases their own interpreters, + fifteen more releases, and the booters in `games/booters/` (Phase 5). + +Every DOS folder now runs on its own Sierra interpreter. The one exception +is King's Quest IV's three releases: Sierra's first asks a word from the +manual, picked at random, so their sessions are JoeyAGI's alone. + +Still to obtain: the Christmas card's version 1 (interpreter 2.230, +1986-10-13), King's Quest II 2.1, Donald Duck's Playground 1.50 as files, and +the demos ScummVM lists that `games/` lacks (King's Quest III and Space Quest +I, Demo 2 for the Tandy, the King's Quest IV and Larry demos). | Folder | Game | Interpreter (AGIDATA.OVL) | Data | |--------|------|---------------------------|------| @@ -73,15 +75,31 @@ files: | kq6agi | King's Quest VI AGI demake (fan, Brandon Kouri) | 2.917 | 1469 KB | | sqtlc | Space Quest: The Lost Chapter, mouse version (fan; AGI Mouse interpreter) | 2.917 | 1550 KB | | sq0 | Space Quest 0: Replicated (fan; its interpreter is a Windows program) | none (2.435 assumed) | 1032 KB | -| kq3v214 | King's Quest III 2.14, 1988-03-15, 3.5" (eXo; its sound 36 shipped cut to 1035 of its 2281 bytes) | 2.936, from caitlyn | 643 KB | -| sq2v20f | Space Quest II 2.0F, 1989-01-05, 3.5" (eXo) | 2.936, from caitlyn | 637 KB | -| pq1v20g | Police Quest 2.0G, 1987-12-03 (eXo) | 2.917, AGI Tetris's (fan; kq1's quits at once on this game) | 694 KB | -| bcv210 | Black Cauldron 2.10, 1988-11-10, 5.25" (eXo; AGI 3) | 3.002.149: gr's, its built-in file prefix changed from GR to BC (a v3 interpreter carries its game's prefix, at 0x60D7) | 300 KB | -| mh2v303 | Manhunter 2 3.03, 1989-08-17, 5.25" (eXo) | 3.002.149, mh2's (3.02) | 1183 KB | -| grv201 | Gold Rush 2.01, 1988-12-22, 3.5", the original (eXo). gr, which ScummVM lists as pirated, is a cracked copy: its logic 129, the speed test, is the original's logic 73 | 3.002.149 (its own) | 1169 KB | -| kq4 | King's Quest IV 2.3, 1988-09-27, DOS (eXo) | none here (3.002.086, by its KQ4 prefix) | 1360 KB | +| kq3v214 | King's Quest III 2.14, 1988-03-15, 3.5" (its sound 36 shipped cut to 1035 of its 2281 bytes) | 2.936 | 643 KB | +| sq2v20f | Space Quest II 2.0F, 1989-01-05, 3.5" | 2.936 | 637 KB | +| pq1v20g | Police Quest 2.0G, 1987-12-03 | 2.917 | 694 KB | +| bcv210 | Black Cauldron 2.10, 1988-11-10, 5.25" (AGI 3; the interpreter is one SIERRA.EXE) | 3.002.098 (ScummVM takes 3.002.149) | 300 KB | +| mh2v303 | Manhunter 2 3.03, 1989-08-17, 5.25" | 3.002.149 | 1183 KB | +| grv201 | Gold Rush 2.01, 1988-12-22, 3.5", the original. gr, which ScummVM lists as pirated, is a cracked copy: its logic 129, the speed test, is the original's logic 73 | 3.002.149 | 1169 KB | +| grv30 | Gold Rush 3.0, 1998-12-22, The Software Farm's re-release: its logic 129 goes from the speed test straight to room 73, past the quiz | 3.002.149 | 1193 KB | +| kq4 | King's Quest IV 2.3, 1988-09-27 | 3.002.086 | 1360 KB | +| kq4v20 | King's Quest IV 2.0, 1988-07-27 | 3.002.086 | 1358 KB | +| kq4v22 | King's Quest IV 2.2, 1988-09-27 | 3.002.086 | 1367 KB | +| kq1v10u | King's Quest 1.0U, 1986 | 2.272 | 332 KB | +| kq3v101 | King's Quest III 1.01, 1986-11-08 | 2.272 | 642 KB | +| sq1v11a | Space Quest 1.1A, 1986-11-13 | 2.272 | 520 KB | +| sq1v10x | Space Quest 1.0X, 1986-09-24 | 2.089 | 574 KB | +| pq1v20e | Police Quest 2.0E, 1987-11-17 | 2.915 | 694 KB | +| sq2v20c | Space Quest II 2.0C, 3.5" | 2.917 | 636 KB | +| sq2v20d | Space Quest II 2.0D, 1988-03-14, 3.5" | 2.936 | 637 KB | +| kq1i425 | King's Quest 2.0F (kq1's data) | 2.425 | 337 KB | +| kq2i426 | King's Quest II 2.2 (kq2's data) | 2.426 | 481 KB | +| lsl1i917 | Leisure Suit Larry 1.00 (lsl1's data) | 2.917 | 448 KB | +| sq1i917 | Space Quest 2.2 (sq1's data) | 2.917 | 522 KB | +| mh1i102 | Manhunter 1.22 (mh1's data) | 3.002.102 | 1026 KB | | ddpst | Donald Duck's Playground 1.0A, 1986-08-08: the Atari ST release's files, which eXo files as DOS | none (2.435 assumed; ScummVM takes 2.272) | 165 KB | -| caitlyn | Caitlyn's Destiny (fan, eXo): the source of the 2.936 interpreter (its AGI.COM is Sierra's SIERRA.COM; no fan hack files) | 2.936 | 1258 KB | +| caitlyn | Caitlyn's Destiny (fan). Its interpreter says 2.936 but is 512 bytes larger than Sierra's | 2.936 | 1258 KB | +| booters/ | Phase 5: Black Cauldron 1.1J, 1.1K, 1.1M, and Tandy; Donald Duck's Playground 1.0Q (2.001); King's Quest 1984 for CGA, CGA+RGB, PCjr, Tandy, and Tandy+PCjr (the Game Adaptation Language); King's Quest II 1.0W, 1.1H, and Tandy | v1 and earlier | 7.3 MB of images | Starting point (2026-10-19): every v2 game but Mixed-Up Mother Goose boots through its title into play on the host harness; Mother Goose stays black; the @@ -118,15 +136,15 @@ Progress (2026-10-19, end of day): the host). - agiPatch.c: King's Quest IV's zombie distance; its manual question and Gold Rush's quiz skipped; Gold Rush's clock fed the real cycle rates. - - The eXo releases (above): King's Quest III 2.14 and Space Quest II 2.0F - match Sierra's 2.936 at the start, and Police Quest 2.0G, Black Cauldron - 2.10, and Manhunter 2 3.03 match theirs. test-agi.sh 94/94. + - The new releases (above) start as on their own Sierra interpreters + (STATUS.md, "Gates"), with three fixes (STATUS.md, bugs 35 + to 37): 2.089's status line, Manhunter 1's Field Guide question + skipped, and oracle.sh's choice of program. test-agi.sh 124/124. ## Phase 0: a test bench for every game -1. `scripts/oracle.sh` for any game, each run on its own interpreter (each game - folder ships one, or one of the same version stands in: fourteen versions - from 2.272 to 3.002.149). +1. `scripts/oracle.sh` for any game, each run on its own interpreter (each DOS + game folder ships Sierra's: seventeen versions, 2.089 to 3.002.149). 2. The probe games run on every one of those interpreters, to measure what differs between real versions (ScummVM treats every 2.426 to 2.440 interpreter as one); v3 interpreters need the probes in v3 format. @@ -182,8 +200,10 @@ Progress (2026-10-19, end of day): - A scripted walkthrough per game on the host harness (virtual time): the main path, deaths, the score, the endings, checked at checkpoints against the game's own interpreter. -- Copy-protection quizzes skipped (decision 3); Larry's age quiz has its - own way past, Alt+X (a set.key that sets f109). +- Copy-protection quizzes skipped (decision 3: King's Quest IV, Gold Rush, + and Manhunter 1 so far, agiPatch.c); Larry's age quiz has its own way + past, Alt+X (a set.key that sets f109). An oracle session for King's + Quest IV must read its random question and answer from the manual. ## Phase 4: every game on every machine diff --git a/STATUS.md b/STATUS.md index 11c932a..49fecb9 100644 --- a/STATUS.md +++ b/STATUS.md @@ -41,7 +41,7 @@ behavior, never copied (it is GPL), and Sierra's interpreter still decides. - **All six ports build;** the IIgs disk boots and draws (it was blank when the dive started), and the ST, Amiga and X68000 builds draw KQ3 correctly (`screenshots/port-*.png`). -- **Gates:** `make check` passes: host 94/94 (see "Gates"), disk PASS. +- **Gates:** `make check` passes: host 124/124 (see "Gates"), disk PASS. Not verified: playing KQ3 to the end, death scenes, the later lands (Llewdor, the ship, Daventry) beyond their pictures and a few teleports, audio by ear, @@ -81,9 +81,9 @@ Sierra's interpreter. | Subsystem | Status | Evidence | |-----------|--------|----------| | Resources (directories, VOL, WORDS.TOK, OBJECT) | Works. Files read whole into buffers of their own size from the Memory Manager on the IIgs; a missing or empty directory just means none of that type. | `resources-kq3`: 125 logics, 97 pictures, 216 views, 34 sounds load and decode | -| AGI v3 (2026-10-20) | Works: DIR (four directories in one file), VOL.n (prefix from the known games: KQ4, GR, MH, MH2, BC, DM), 7-byte headers, LZW (9 to 11 bits) and nibble pictures expanded at load, packed logics' messages not decrypted, OBJECT decrypted only when encrypted. Gold Rush (both copies), Manhunter 1 and 2 (3.02 and 3.03), Demo 3, Black Cauldron 2.10, and King's Quest IV (DOS and Apple IIgs) load and start; on Sierra's interpreter (as .COM) the DOS ones with an interpreter here start the same. 3.002.086's rules (loops for any loop count, the x <= 0 left border, one-argument hide.mouse and hold.key) and v3's (draw reapplies loop and cel; new.room ends a walk to a click) are in | resources for all of them; `gr-start`, `grv201-start`, `mh1-start`, `mh2-start`, `mh2v303-start`, `bcv210-start`, `agidemo3-start`, `kq4-start`, `kq4gs-start` | +| AGI v3 (2026-10-20) | Works: DIR (four directories in one file), VOL.n (prefix from the known games: KQ4, GR, MH, MH2, BC, DM), 7-byte headers, LZW (9 to 11 bits) and nibble pictures expanded at load, packed logics' messages not decrypted, OBJECT decrypted only when encrypted. Gold Rush (both copies), Manhunter 1 and 2 (3.02 and 3.03), Demo 3, Black Cauldron 2.10 (3.002.098), Gold Rush 3.0, and King's Quest IV (2.0, 2.2, and 2.3 for DOS, and the Apple IIgs release) load and start; on Sierra's interpreter (as .COM) the DOS ones start the same, King's Quest IV's past a manual question JoeyAGI skips. 3.002.086's rules (loops for any loop count, the x <= 0 left border, one-argument hide.mouse and hold.key) and v3's (draw reapplies loop and cel; new.room ends a walk to a click) are in | resources for all of them; `gr-start`, `grv201-start`, `mh1-start`, `mh2-start`, `mh2v303-start`, `bcv210-start`, `grv30-start`, `agidemo3-start`, `kq4-start`, `kq4v20-start`, `kq4v22-start`, `kq4gs-start` | | Mouse (2026-10-20) | Works on the host (the DOS interpreters had none; this follows the Amiga, ST, IIgs, and Mac ones as ScummVM describes them): a JoeyLib sprite pointer drawn at each present and kept to fence.mouse's box; a left click on the menu bar opens the menus, in the picture walks ego to it (its baseline's middle; a direction key, or in v3 a new room, stops it); the menus follow the pointer and a click picks; a click closes a message window | `kq3-mouse` (the pointer, a walk, the bar, a hovered item, a click choosing Help, a click closing it) | -| Game patches (agiPatch.c) | King's Quest IV's zombie distance; the manual questions of King's Quest IV (rooms 141 to 143) and Gold Rush (room 125) skipped to where a right answer leads; Gold Rush's clock fed the real cycle rates | `kq4-start`, `kq4gs-start`, `grv201-start`; the clock run at Normal, Fast, and Fastest on the host (10 game seconds in 10 seconds each) | +| Game patches (agiPatch.c) | King's Quest IV's zombie distance; the manual questions of King's Quest IV (rooms 141 to 143) and Gold Rush (room 125) skipped to where a right answer leads; Manhunter 1's Field Guide question (logic 159) patched as it loads into a jump to the right answer's path; Gold Rush's clock fed the real cycle rates | `kq4-start`, `kq4gs-start`, `grv201-start`, `mh1i102-start`; the clock run at Normal, Fast, and Fastest on the host (10 game seconds in 10 seconds each) | | PICTURE | Exact: the spec's line (exact half rounds away from the start) with Sierra's byte-sized error term, corners, relative lines, scanline fill (visual, priority-only and both), solid and splatter pens of every size with the texture wrap at bit 254 | 194/194 KQ3 frames pixel-exact; `pictures` probe visual + priority exact (`screenshots/probe-pictures-*.png`); `winedge` probe's long diagonal | | VIEW | Works: loops, mirrored loops, cel transparency, descriptions; one allocation per view | `resources-kq3` (every cel decoded), `setview`, `parser` (show.obj) | | Object drawing | Exact: blits stacked with save-unders, not-updated objects under updated ones, sorted by baseline (or band top for fixed priority), control lines take the priority below them, add.to.pic margins (an outline of the control value from the baseline up to its band's top) | `priority` probe incl. the priority screen exact | @@ -237,6 +237,22 @@ Found 2026-10-20 with the eXo releases (PLAN.md, "The games"): Such a sound is now listed as truncated; the player already ends a voice where the resource ends. +Found 2026-10-21 with Sierra's DOS floppies (PLAN.md, "The games"): + +35. **2.089 words the status line its own way:** "Score: 0 of 202" and + "Sound is on" from column 27 (Space Quest 1.0X); 2.272 already has + "Score:0 of 202" and "Sound:on". Gated on 2.089 and below. +36. **Manhunter 1 asks about its Field Guide** in room 159 and quits on a + wrong answer; games/mh1 is a cracked copy that never asks. The + question's random(6,61,96) is patched as the logic loads into a jump + to the right answer's path, the crack's own change (agiPatchLogic, + which checks the bytes first). Manhunter 2's logic 159 already skips + its question. +37. **oracle.sh picked the wrong program** for a release with an installer: + Gold Rush 3.0 ships GR.COM and no SIERRA.COM, so _MSTEST.EXE ran. + An existing .COM is now taken first, and SIERRA.EXE (Black + Cauldron 2.10's whole interpreter) before any other .EXE. + Fixed earlier in the dive (before the gates existed), all verified against Sierra's interpreter: the picture decoder (line algorithm, fill semantics, pens, splatter textures); window geometry and the wrap rule; display wrapping; @@ -304,12 +320,16 @@ takes 2.435, the version measured, into `game.interpreterVersion`; the one place per-version behavior is decided. The IIgs disk now carries AGIDATA.OVL, and `resources-kq3` checks KQ3 reads as 2435. -Only one Sierra interpreter, 2.435, was at hand, so whether the quirks -differ between versions could not be measured: all three are on for 2.000 -to 2.999 (every version JoeyAGI plays), and narrowing a range is a one-line -change once another interpreter is measured. Off, each falls back to the -plain behavior (a clipped window, an echo from the end of the typed text, -an exact line). +Only one Sierra interpreter, 2.435, was at hand when the three quirks +below were measured, so all three are on for 2.000 to 2.999, and +narrowing a range is a one-line change. Off, each falls back to the plain +behavior (a clipped window, an echo from the end of the typed text, an +exact line). Since 2026-10-21 games/ holds every DOS interpreter from +2.089 to 3.002.149 (seventeen), so the probes can now measure each +(PLAN.md, Phase 0 item 2). Rules found since are gated where they act: +2.089's status line (bug 35), 2.272's motion rules (agiObj.c), the +commands of 2.425 to 3.098 (agiVm.c), and 3.002.086's (agiObj.c, +agiPic.c). - **AGI_QUIRK_WINDOW_BYTE_X -- print.at at column 0 or 1.** A window's box is x = 4 * col - 5 AGI pixels, y = 8 * row - 5 screen rows, 4 * width + 10 @@ -627,7 +647,7 @@ the earlier rewrite touched nearly every command. | Gate | Result | |------|--------| -| host (`scripts/test-agi.sh`) | 94/94: resources for the 22 v2 and 9 v3 folders in games/ (every resource loads and decodes, a sound shipped cut short listed as truncated; KQ3's interpreter version and picture hashes are checked), 29 probes (shake pins every frame of its recording), 5 KQ3 sessions (opening, play, saverestore, teleport, mouse), and a start session for each of the other 29 games (tests/GAME/start.scr: titles, menus, the first room, typed answers) | +| host (`scripts/test-agi.sh`) | 124/124: resources for the 33 v2 and 13 v3 folders in games/ (every resource loads and decodes, a sound shipped cut short listed as truncated; KQ3's interpreter version and picture hashes are checked), 29 probes (shake pins every frame of its recording), 5 KQ3 sessions (opening, play, saverestore, teleport, mouse), and a start session for each of the other 44 game folders (tests/GAME/start.scr: titles, menus, the first room, typed answers) | | disk (IIgs `AGI.2mg` on a CFFA2, MAME) | PASS (boots, launches, draws: distinctNibbles=6) | `scripts/oracle.sh` (not part of `make check`; needs Sierra's interpreters in @@ -659,13 +679,30 @@ shots, 2026-10-19): 8 identical; the rest differ in color-cycled logos, the fireplace's flames, other things in motion, and AGI Mouse's pointer in The Lost Chapter (JoeyAGI's own pointer differs). -The eXo releases' start sessions (2026-10-20, scratch pair.sh): King's -Quest III 2.14 and Space Quest II 2.0F on the 2.936 from Caitlyn's Destiny, -Police Quest 2.0G on AGI Tetris's 2.917, and Black Cauldron 2.10 and -Manhunter 2 3.03 on 3.002.149 all agree with Sierra's but for chance (the -Police Quest title's bullet holes). Gold Rush 2.01's original goes to its -quiz on Sierra's, which JoeyAGI skips, so its session is marked "JoeyAGI -only" and oracle.sh leaves it out. +The eXo and floppy releases' start sessions (2026-10-20/21, scratch +pair.sh), each on its own Sierra interpreter: King's Quest 1.0U (2.272), +King's Quest 2.0F (2.425), King's Quest II 2.2 (2.426), Space Quest 1.1A +(2.272) and 2.2 (2.917), Space Quest 1.0X (2.089, after fix 35), Space +Quest II 2.0C, 2.0D, and 2.0F, King's Quest III 2.14, Police Quest 2.0E +and 2.0G, Larry on 2.917, Black Cauldron 2.10 (3.002.098), Manhunter 2 +3.03, and Gold Rush 3.0 agree with Sierra's but for chance (bullet holes, +Larry's quiz question, the goose). Leads: + +- **Titles that run ahead on Sierra's:** King's Quest III 1.01 reaches its + last credits window by 17 s, ours by 30 s, and every Space Quest 1 + title is a screen ahead at 12 s. Probably the screenshot key, which + reaches Sierra's interpreter and hurries a title that waits for a key + (as with KQ3's opening); unconfirmed. +- **Gold Rush:** Gold Rush 3.0 agrees through the speed test and Brooklyn + Heights, but 6 s after the Return Sierra's Jerrod is further down the + path than ours (gr-start's lead, now on a copy both run from the same + start). + +Sessions marked "JoeyAGI only", which oracle.sh leaves out, are where +Sierra's interpreter asks a manual question JoeyAGI skips: Gold Rush 2.01 +(the claim jumper), Manhunter 1's original data (the Field Guide), and +King's Quest IV's three releases (a word from the manual, picked at +random). ## Screenshots diff --git a/agi.h b/agi.h index 9cb7dcd..5df41ed 100644 --- a/agi.h +++ b/agi.h @@ -198,8 +198,9 @@ extern const uint8_t kAgiFont[AGI_FONT_GLYPHS][AGI_FONT_ROWS]; #define AGI_VERSION_MEASURED 2435u #define AGI_VERSION_V3_FIRST 3000u #define AGI_VERSION_V3_ASSUMED 3149u -// Interpreters whose rules differ: 2.272 (the Christmas card, Donald -// Duck) and 3.002.086 (every King's Quest IV). +// Interpreters whose rules differ: 2.089 (Space Quest 1.0X), 2.272 (the +// Christmas card, Donald Duck), and 3.002.086 (every King's Quest IV). +#define AGI_VERSION_2089 2089u #define AGI_VERSION_2272 2272u #define AGI_VERSION_KQ4 3086u @@ -526,6 +527,7 @@ extern AgiStateT *gAgi; void agiPatchCycle(void); uint8_t agiPatchDistance(uint8_t var, uint8_t distance); +void agiPatchLogic(uint8_t logicId, uint8_t *code, uint16_t length); uint8_t agiPatchRoom(uint8_t room); diff --git a/agiPatch.c b/agiPatch.c index c220216..d9c8397 100644 --- a/agiPatch.c +++ b/agiPatch.c @@ -4,6 +4,8 @@ #include "agi.h" +#include + #include "joey/core.h" @@ -39,6 +41,19 @@ typedef struct { uint8_t after; } QuestionRoomsT; +// Where skipping the room would skip what a right answer does, the logic +// is changed as it loads instead: a few bytes of its code, made only over +// the bytes expected, so other releases are left alone. +#define LOGIC_PATCH_BYTES 3u + +typedef struct { + AgiGameIdE game; + uint8_t logic; + uint16_t offset; + uint8_t expect[LOGIC_PATCH_BYTES]; + uint8_t replace[LOGIC_PATCH_BYTES]; +} LogicPatchT; + // ----- Tables ----- @@ -56,6 +71,14 @@ static const QuestionRoomsT kQuestionRooms[] = { { AGI_GAME_GOLD_RUSH, 125u, 125u, 73u } }; +static const LogicPatchT kLogicPatches[] = { + // Manhunter 1 asks about its Field Guide in room 159; a wrong answer + // quits. random(6,61,96), which picks the question (code offset 245), + // becomes goto 443, the right answer's path (player control, logic 90's + // keys, room 153), as the cracked copy has it. + { AGI_GAME_MANHUNTER_1, 159u, 245u, { 0x82u, 0x06u, 0x3Du }, { 0xFEu, 0xC3u, 0x00u } } +}; + // ----- Public API (alphabetical) ----- @@ -90,6 +113,20 @@ uint8_t agiPatchDistance(uint8_t var, uint8_t distance) { } +// A logic's code as it loads (after the header), patched in place. +void agiPatchLogic(uint8_t logicId, uint8_t *code, uint16_t length) { + const LogicPatchT *p; + uint8_t i; + + for (i = 0u; i < sizeof(kLogicPatches) / sizeof(kLogicPatches[0]); i++) { + p = &kLogicPatches[i]; + if (gAgi->game.id == p->game && logicId == p->logic && (uint32_t)p->offset + LOGIC_PATCH_BYTES <= length && memcmp(&code[p->offset], p->expect, LOGIC_PATCH_BYTES) == 0) { + memcpy(&code[p->offset], p->replace, LOGIC_PATCH_BYTES); + } + } +} + + // The room new.room really goes to: a manual question is skipped. uint8_t agiPatchRoom(uint8_t room) { const QuestionRoomsT *q; diff --git a/agiText.c b/agiText.c index 6b27e7e..b60b184 100644 --- a/agiText.c +++ b/agiText.c @@ -49,6 +49,9 @@ #define WINDOW_FRAME AGI_COLOR_RED #define STATUS_SCORE_COL 1u #define STATUS_SOUND_COL 30u +// 2.089 words it "Score: 0 of 202" and "Sound is on", the sound from +// column 27 (measured with sound on). +#define STATUS_SOUND_2089 27u #define FORMAT_MAX 8u #define NUMBER_MAX_DIGITS 3u #define DECIMAL_BASE 10u @@ -773,15 +776,17 @@ void textDrawInputLine(void) { // The status line: the score at the left and whether sound is on. void textDrawStatus(void) { char buf[AGI_TEXT_COLS + 1u]; + bool old; if (gAgi->textMode || !gAgi->statusOn) { return; } + old = gAgi->game.interpreterVersion <= AGI_VERSION_2089; jlFillRect(jlStageGet(), 0, (int16_t)((int16_t)gAgi->statusRow * AGI_CELL_PX), (uint16_t)(AGI_TEXT_COLS * AGI_CELL_PX), AGI_CELL_PX, AGI_COLOR_WHITE); - snprintf(buf, sizeof(buf), "Score:%u of %u", (unsigned)gAgi->vars[VAR_SCORE], (unsigned)gAgi->vars[VAR_MAX_SCORE]); + snprintf(buf, sizeof(buf), old ? "Score: %u of %u" : "Score:%u of %u", (unsigned)gAgi->vars[VAR_SCORE], (unsigned)gAgi->vars[VAR_MAX_SCORE]); drawText(gAgi->statusRow, STATUS_SCORE_COL, buf, AGI_COLOR_BLACK, AGI_COLOR_WHITE); - snprintf(buf, sizeof(buf), "Sound:%s", gAgi->flags[FLAG_SOUND] ? "on" : "off"); - drawText(gAgi->statusRow, STATUS_SOUND_COL, buf, AGI_COLOR_BLACK, AGI_COLOR_WHITE); + snprintf(buf, sizeof(buf), old ? "Sound is %s" : "Sound:%s", gAgi->flags[FLAG_SOUND] ? "on" : "off"); + drawText(gAgi->statusRow, old ? STATUS_SOUND_2089 : STATUS_SOUND_COL, buf, AGI_COLOR_BLACK, AGI_COLOR_WHITE); gShownScore = gAgi->vars[VAR_SCORE]; gShownSound = gAgi->flags[FLAG_SOUND] != 0u; } diff --git a/agiVm.c b/agiVm.c index 0e4ccf5..b5e34cc 100644 --- a/agiVm.c +++ b/agiVm.c @@ -1578,6 +1578,7 @@ bool vmLoadLogic(uint8_t logicId) { if (!agiResPacked(&gAgi->game, AGI_RES_LOGIC, logicId)) { decryptMessages(raw, length); } + agiPatchLogic(logicId, raw + LOGIC_HEADER_BYTES, codeLen); gAgi->logicRaw[logicId] = raw; gAgi->logicLength[logicId] = length; return true; diff --git a/games/README.md b/games/README.md index 31204c5..18263ab 100644 --- a/games/README.md +++ b/games/README.md @@ -20,13 +20,13 @@ interpreter reads the game from its `DATA` folder; `joey-run --data games/kq3` copies a game there for one run. Releases for other machines go one level down, in `machines/` (`kq1amiga`, -`kq1gs`, `kq1st`, `kq1mac`, and so on), where the host tests do not look -yet (PLAN.md, Phase 5). +`kq1gs`, `kq1st`, `kq1mac`, and so on), and the booter disk images in +`booters/`; the host tests do not look there yet (PLAN.md, Phase 5). `scripts/oracle.sh` plays a game's sessions on the Sierra interpreter in its -folder. A release that came without one can be given another release's of -the same version; PLAN.md's games table says which. A v3 interpreter carries -its game's file prefix, so it opens only that game's files. +folder, so keep Sierra's files (`AGI`, `AGIDATA.OVL`, and the loader) with +each game. One game's interpreter does not run another: each `AGI` is keyed +to its own loader, and a v3 one carries its game's file prefix. ## Required files per game diff --git a/scripts/oracle.sh b/scripts/oracle.sh index 02989cd..9eb1d96 100755 --- a/scripts/oracle.sh +++ b/scripts/oracle.sh @@ -51,23 +51,28 @@ parallel=${ORACLE_JOBS:-4} # The launcher Sierra shipped with game $2 (games/$2) in folder $1. A v3 # game runs as .COM, the name its installer gives SIERRA.COM (the # interpreter takes its file prefix, GR of GRDIR, from it), so in a copy -# of one that file is made. +# of one that has only SIERRA.COM that file is made. Black Cauldron 2.10's +# interpreter is one SIERRA.EXE; a release with an installer also has +# SETUP.EXE and the like, so SIERRA.EXE comes before any other .EXE. launcher() { local dir=$1 local id=$2 local exe local d - if [[ ! -f $dir/LOGDIR && -f $dir/SIERRA.COM ]]; then + if [[ ! -f $dir/LOGDIR ]]; then for d in "$dir"/*DIR; do - if [[ -f $d ]]; then + [[ -f $d ]] || continue + if [[ ! -f ${d%DIR}.COM && -f $dir/SIERRA.COM ]]; then cp "$dir/SIERRA.COM" "${d%DIR}.COM" + fi + if [[ -f ${d%DIR}.COM ]]; then basename "${d%DIR}.COM" return 0 fi done fi - for exe in "$dir/${id^^}.COM" "$dir/SIERRA.COM" "$dir"/*.[Ee][Xx][Ee]; do + for exe in "$dir/${id^^}.COM" "$dir/SIERRA.COM" "$dir/SIERRA.EXE" "$dir"/*.[Ee][Xx][Ee]; do if [[ -f $exe ]]; then basename "$exe" return 0 diff --git a/tests/expected/grv30/start.txt b/tests/expected/grv30/start.txt new file mode 100644 index 0000000..e534d82 --- /dev/null +++ b/tests/expected/grv30/start.txt @@ -0,0 +1,4 @@ +s1 9f9fa489 +s2 a35d4f15 +s3 5a454ac2 +s4 a682c8fc diff --git a/tests/expected/kq1i425/start.txt b/tests/expected/kq1i425/start.txt new file mode 100644 index 0000000..2858c71 --- /dev/null +++ b/tests/expected/kq1i425/start.txt @@ -0,0 +1,5 @@ +k01 b13ad04d +k02 3e3e97ad +k03 b80a0692 +k04 f4ada91e +k05 a228bc50 diff --git a/tests/expected/kq1v10u/start.txt b/tests/expected/kq1v10u/start.txt new file mode 100644 index 0000000..c297465 --- /dev/null +++ b/tests/expected/kq1v10u/start.txt @@ -0,0 +1,5 @@ +k01 f9b12f13 +k02 65008073 +k03 b80a0692 +k04 f4ada91e +k05 a228bc50 diff --git a/tests/expected/kq2i426/start.txt b/tests/expected/kq2i426/start.txt new file mode 100644 index 0000000..9299162 --- /dev/null +++ b/tests/expected/kq2i426/start.txt @@ -0,0 +1,5 @@ +k01 6c4d72fc +k02 1468a291 +k03 41e7e7b8 +k04 0194cfae +k05 41e7e7b8 diff --git a/tests/expected/kq3v101/start.txt b/tests/expected/kq3v101/start.txt new file mode 100644 index 0000000..14638e6 --- /dev/null +++ b/tests/expected/kq3v101/start.txt @@ -0,0 +1,4 @@ +o01 7fecf505 +o02 321dac71 +o03 40acb32d +o04 a420b222 diff --git a/tests/expected/kq4v20/start.txt b/tests/expected/kq4v20/start.txt new file mode 100644 index 0000000..a8fd2a6 --- /dev/null +++ b/tests/expected/kq4v20/start.txt @@ -0,0 +1,6 @@ +s1 2e9906b7 +s2 2acfb799 +s3 56f8d2fb +s4 348680cf +s5 b51e0291 +s6 dfe12332 diff --git a/tests/expected/kq4v22/start.txt b/tests/expected/kq4v22/start.txt new file mode 100644 index 0000000..cb996ef --- /dev/null +++ b/tests/expected/kq4v22/start.txt @@ -0,0 +1,6 @@ +s1 2e9906b7 +s2 2acfb799 +s3 56f8d2fb +s4 ff6daa07 +s5 b51e0291 +s6 dfe12332 diff --git a/tests/expected/lsl1i917/start.txt b/tests/expected/lsl1i917/start.txt new file mode 100644 index 0000000..786bad6 --- /dev/null +++ b/tests/expected/lsl1i917/start.txt @@ -0,0 +1,8 @@ +l01 9961df51 +l02 e3462831 +l03 54ac58a5 +l04 4839f56a +l05 13859150 +l06 76a0e2d2 +l07 300661d9 +l08 24e226b9 diff --git a/tests/expected/mh1i102/start.txt b/tests/expected/mh1i102/start.txt new file mode 100644 index 0000000..4bdbf11 --- /dev/null +++ b/tests/expected/mh1i102/start.txt @@ -0,0 +1,4 @@ +s1 0c9307fd +s2 b35b29d9 +s3 f17f55d5 +s4 b60478bf diff --git a/tests/expected/pq1v20e/start.txt b/tests/expected/pq1v20e/start.txt new file mode 100644 index 0000000..f545c32 --- /dev/null +++ b/tests/expected/pq1v20e/start.txt @@ -0,0 +1,5 @@ +p01 f3cc67fd +p02 088d7363 +p03 2dd5daef +p04 5f41010f +p05 2dd5daef diff --git a/tests/expected/sq1i917/start.txt b/tests/expected/sq1i917/start.txt new file mode 100644 index 0000000..4c37273 --- /dev/null +++ b/tests/expected/sq1i917/start.txt @@ -0,0 +1,6 @@ +s01 9c9fd51d +s02 bae6fb93 +s03 80d7df3a +s04 d23de88e +s05 25d119ac +s06 66c57e84 diff --git a/tests/expected/sq1v10x/start.txt b/tests/expected/sq1v10x/start.txt new file mode 100644 index 0000000..9acb408 --- /dev/null +++ b/tests/expected/sq1v10x/start.txt @@ -0,0 +1,6 @@ +s01 9c9fd51d +s02 bae6fb93 +s03 80d7df3a +s04 fcd46177 +s05 6c4d9ccd +s06 32a33055 diff --git a/tests/expected/sq1v11a/start.txt b/tests/expected/sq1v11a/start.txt new file mode 100644 index 0000000..4c37273 --- /dev/null +++ b/tests/expected/sq1v11a/start.txt @@ -0,0 +1,6 @@ +s01 9c9fd51d +s02 bae6fb93 +s03 80d7df3a +s04 d23de88e +s05 25d119ac +s06 66c57e84 diff --git a/tests/expected/sq2v20c/start.txt b/tests/expected/sq2v20c/start.txt new file mode 100644 index 0000000..68befae --- /dev/null +++ b/tests/expected/sq2v20c/start.txt @@ -0,0 +1,5 @@ +s01 73eff810 +s02 d7838181 +s03 39b92971 +s04 2232343b +s05 160cfe5d diff --git a/tests/expected/sq2v20d/start.txt b/tests/expected/sq2v20d/start.txt new file mode 100644 index 0000000..803fa6e --- /dev/null +++ b/tests/expected/sq2v20d/start.txt @@ -0,0 +1,5 @@ +s01 94092eea +s02 d7838181 +s03 39b92971 +s04 2232343b +s05 160cfe5d diff --git a/tests/grv30/start.scr b/tests/grv30/start.scr new file mode 100644 index 0000000..09611f6 --- /dev/null +++ b/tests/grv30/start.scr @@ -0,0 +1,14 @@ +# Gold Rush 3.0, 1998-12-22 (The Software Farm's re-release): logic 129's +# speed test goes straight on to room 73, past the quiz, so this one runs +# on Sierra's interpreter too. The titles, Brooklyn Heights, and the next +# screen. +wait 4 +shot s1 +wait 8 +shot s2 +key Return +wait 4 +shot s3 +key Return +wait 6 +shot s4 diff --git a/tests/kq1i425/start.scr b/tests/kq1i425/start.scr new file mode 100644 index 0000000..a2b830e --- /dev/null +++ b/tests/kq1i425/start.scr @@ -0,0 +1,16 @@ +# King's Quest 2.0F on interpreter 2.425 (games/kq1 is 2.0F on 2.917), as +# tests/kq1/start.scr. +wait 4 +shot k01 +wait 8 +shot k02 +key Return +wait 6 +shot k03 +type look +key Return +wait 2 +shot k04 +clear +wait 1 +shot k05 diff --git a/tests/kq1v10u/start.scr b/tests/kq1v10u/start.scr new file mode 100644 index 0000000..7e207ab --- /dev/null +++ b/tests/kq1v10u/start.scr @@ -0,0 +1,17 @@ +# King's Quest 1.0U (interpreter 2.272), as tests/kq1/start.scr: the title +# and the credits, then Graham by the castle, a look around, and the window +# closed. +wait 4 +shot k01 +wait 8 +shot k02 +key Return +wait 6 +shot k03 +type look +key Return +wait 2 +shot k04 +clear +wait 1 +shot k05 diff --git a/tests/kq2i426/start.scr b/tests/kq2i426/start.scr new file mode 100644 index 0000000..26570bd --- /dev/null +++ b/tests/kq2i426/start.scr @@ -0,0 +1,16 @@ +# King's Quest II 2.2 on interpreter 2.426 (games/kq2 is 2.2 on 2.917), as +# tests/kq2/start.scr. +wait 4 +shot k01 +wait 8 +shot k02 +key Return +wait 6 +shot k03 +type look +key Return +wait 2 +shot k04 +clear +wait 1 +shot k05 diff --git a/tests/kq3v101/start.scr b/tests/kq3v101/start.scr new file mode 100644 index 0000000..d4a8f13 --- /dev/null +++ b/tests/kq3v101/start.scr @@ -0,0 +1,10 @@ +# King's Quest III 1.01 (interpreter 2.272): the title, the credits over +# Manannan's face, and the house. +wait 3 +shot o01 +wait 7 +shot o02 +wait 7 +shot o03 +wait 13 +shot o04 diff --git a/tests/kq4/start.scr b/tests/kq4/start.scr index b8bf260..3e48ff1 100644 --- a/tests/kq4/start.scr +++ b/tests/kq4/start.scr @@ -1,6 +1,7 @@ -# The start of King's Quest IV 2.3 for DOS (3.002.086; no interpreter -# here, so JoeyAGI's alone): the title, the credits, the throne room, and -# Rosella on Tamir's beach (the manual question skipped, agiPatch.c). +# The start of King's Quest IV 2.3 for DOS (3.002.086): the title, the +# credits, the throne room, and Rosella on Tamir's beach. +# JoeyAGI only: Sierra's interpreter first asks a word from the manual, +# picked at random, which JoeyAGI skips (agiPatch.c). wait 4 shot s1 key Return diff --git a/tests/kq4v20/start.scr b/tests/kq4v20/start.scr new file mode 100644 index 0000000..ac4c02f --- /dev/null +++ b/tests/kq4v20/start.scr @@ -0,0 +1,18 @@ +# King's Quest IV 2.0, 1988-07-27 (3.002.086), as tests/kq4/start.scr. +# JoeyAGI only: Sierra's interpreter first asks a word from the manual, +# picked at random, which JoeyAGI skips (agiPatch.c). +wait 4 +shot s1 +key Return +wait 6 +shot s2 +wait 8 +shot s3 +key Return +wait 8 +shot s4 +wait 10 +shot s5 +key Return +wait 8 +shot s6 diff --git a/tests/kq4v22/start.scr b/tests/kq4v22/start.scr new file mode 100644 index 0000000..55511a1 --- /dev/null +++ b/tests/kq4v22/start.scr @@ -0,0 +1,18 @@ +# King's Quest IV 2.2, 1988-09-27 (3.002.086), as tests/kq4/start.scr. +# JoeyAGI only: Sierra's interpreter first asks a word from the manual, +# picked at random, which JoeyAGI skips (agiPatch.c). +wait 4 +shot s1 +key Return +wait 6 +shot s2 +wait 8 +shot s3 +key Return +wait 8 +shot s4 +wait 10 +shot s5 +key Return +wait 8 +shot s6 diff --git a/tests/lsl1i917/start.scr b/tests/lsl1i917/start.scr new file mode 100644 index 0000000..4613631 --- /dev/null +++ b/tests/lsl1i917/start.scr @@ -0,0 +1,27 @@ +# Leisure Suit Larry 1.00 on interpreter 2.917 (games/lsl1 is on 2.440), as +# tests/lsl1/start.scr. +wait 4 +shot l01 +wait 8 +shot l02 +key Return +wait 4 +shot l03 +key Return +wait 4 +shot l04 +type 30 +key Return +wait 2 +shot l05 +key Return +wait 2 +shot l06 +down alt +key x +up alt +wait 2 +shot l07 +key Return +wait 6 +shot l08 diff --git a/tests/mh1i102/start.scr b/tests/mh1i102/start.scr new file mode 100644 index 0000000..50cbbad --- /dev/null +++ b/tests/mh1i102/start.scr @@ -0,0 +1,15 @@ +# Manhunter 1.22 on interpreter 3.002.102 (games/mh1 is on 3.002.107), as +# tests/mh1/start.scr. +# JoeyAGI only: this is the original data, so Sierra's interpreter asks about +# the Field Guide (logic 159) and quits on a wrong answer; JoeyAGI skips it +# (agiPatch.c). games/mh1, a cracked copy, never asks. +wait 4 +shot s1 +wait 8 +shot s2 +key Return +wait 4 +shot s3 +key Return +wait 6 +shot s4 diff --git a/tests/pq1v20e/start.scr b/tests/pq1v20e/start.scr new file mode 100644 index 0000000..895c5f4 --- /dev/null +++ b/tests/pq1v20e/start.scr @@ -0,0 +1,15 @@ +# Police Quest 2.0E (interpreter 2.915), as tests/pq1/start.scr. +wait 4 +shot p01 +wait 8 +shot p02 +key Return +wait 6 +shot p03 +type look +key Return +wait 2 +shot p04 +clear +wait 1 +shot p05 diff --git a/tests/sq1i917/start.scr b/tests/sq1i917/start.scr new file mode 100644 index 0000000..117274c --- /dev/null +++ b/tests/sq1i917/start.scr @@ -0,0 +1,20 @@ +# Space Quest 2.2 on interpreter 2.917 (games/sq1 is 2.2 on 2.426), as +# tests/sq1/start.scr. +wait 4 +shot s01 +wait 8 +shot s02 +key Return +wait 4 +shot s03 +type Roger +key Return +wait 2 +shot s04 +type Wilco +key Return +wait 6 +shot s05 +clear +wait 2 +shot s06 diff --git a/tests/sq1v10x/start.scr b/tests/sq1v10x/start.scr new file mode 100644 index 0000000..4e56599 --- /dev/null +++ b/tests/sq1v10x/start.scr @@ -0,0 +1,20 @@ +# Space Quest 1.0X (interpreter 2.089), as tests/sq1/start.scr: the title, +# the log-on, and the alarm aboard the Arcada. +wait 4 +shot s01 +wait 8 +shot s02 +key Return +wait 4 +shot s03 +type Roger +key Return +wait 2 +shot s04 +type Wilco +key Return +wait 6 +shot s05 +clear +wait 2 +shot s06 diff --git a/tests/sq1v11a/start.scr b/tests/sq1v11a/start.scr new file mode 100644 index 0000000..bce6192 --- /dev/null +++ b/tests/sq1v11a/start.scr @@ -0,0 +1,19 @@ +# Space Quest 1.1A (interpreter 2.272), as tests/sq1/start.scr. +wait 4 +shot s01 +wait 8 +shot s02 +key Return +wait 4 +shot s03 +type Roger +key Return +wait 2 +shot s04 +type Wilco +key Return +wait 6 +shot s05 +clear +wait 2 +shot s06 diff --git a/tests/sq2v20c/start.scr b/tests/sq2v20c/start.scr new file mode 100644 index 0000000..e70d10a --- /dev/null +++ b/tests/sq2v20c/start.scr @@ -0,0 +1,14 @@ +# Space Quest II 2.0C (interpreter 2.917), as tests/sq2/start.scr. +wait 4 +shot s01 +wait 8 +shot s02 +key Return +wait 4 +shot s03 +wait 8 +shot s04 +type Roger +key Return +wait 4 +shot s05 diff --git a/tests/sq2v20d/start.scr b/tests/sq2v20d/start.scr new file mode 100644 index 0000000..33251dd --- /dev/null +++ b/tests/sq2v20d/start.scr @@ -0,0 +1,14 @@ +# Space Quest II 2.0D (interpreter 2.936), as tests/sq2/start.scr. +wait 4 +shot s01 +wait 8 +shot s02 +key Return +wait 4 +shot s03 +wait 8 +shot s04 +type Roger +key Return +wait 4 +shot s05