joeyagi/agi.h
2026-10-21 01:16:19 -05:00

698 lines
23 KiB
C

// JoeyAGI: an interpreter for Sierra's Adventure Game Interpreter (AGI) v2
// games, written from the public AGI specifications on JoeyLib.
//
// One game runs at a time, so the interpreter's state is a single AgiStateT
// (gAgi), allocated at start-up with jlAlloc to keep it out of the IIgs's
// bank-0 BSS. The modules:
// agiRes.c resource directories, VOL files, WORDS.TOK, OBJECT
// agiPic.c PICTURE decoding into the picture buffers
// agiView.c VIEW parsing
// agiGfx.c the picture buffers, screen objects' blitting, the stage
// agiObj.c screen objects: cycling, motion, collisions
// agiText.c text: display, windows, status and input lines, menus
// agiInput.c the keyboard, the command line and the parser
// agiSound.c PCjr sound resources on JoeyLib's tone and noise voices
// agiSave.c save, restore and the picture script restore replays
// agiMouse.c the pointer, click-to-walk, and the menus' pointer
// agiPatch.c changes to particular games: copy protection, script bugs
// agiVm.c the LOGIC interpreter
// agi.c start-up and the interpreter cycle
#ifndef AGI_H
#define AGI_H
#include "joey/types.h"
#include "joey/surface.h"
#include <stdio.h>
// ----- Limits and the game's geometry -----
// Resource numbers are one byte; directories may name all 256.
#define AGI_MAX_RESOURCES 256u
// VOL.x files: the directory's volume nibble.
#define AGI_MAX_VOLUMES 16u
// The picture is 160x168 AGI pixels; each is two screen pixels wide, so
// it fills 320x168 of the 320x200 screen, below the status line.
#define AGI_WIDTH 160
#define AGI_HEIGHT 168
#define AGI_PIXELS ((uint16_t)AGI_WIDTH * (uint16_t)AGI_HEIGHT)
#define AGI_MAX_X (AGI_WIDTH - 1)
#define AGI_MAX_Y (AGI_HEIGHT - 1)
#define AGI_SCREEN_PX_PER_AGI 2
// The 40x25 text grid of 8x8 character cells.
#define AGI_TEXT_COLS 40u
#define AGI_TEXT_ROWS 25u
#define AGI_CELL_PX 8
// Time: v10 delays each cycle by twentieths of a second.
#define AGI_MS_PER_SECOND 1000u
#define AGI_CYCLE_UNIT_MS 50u
// Colors. The picture starts white with priority 4 (red) everywhere.
#define AGI_COLOR_BLACK 0u
#define AGI_COLOR_RED 4u
#define AGI_COLOR_WHITE 15u
#define AGI_COLOR_COUNT 16u
#define AGI_COLOR_MASK 0x0Fu
#define AGI_PIC_BG_COLOR AGI_COLOR_WHITE
#define AGI_PIC_BG_PRIORITY 4u
// Priority values below 4 are control lines drawn into the priority
// screen: an unconditional barrier, a conditional barrier (blocks), a
// signal (alarm) line and water.
#define AGI_CTL_BARRIER 0u
#define AGI_CTL_BLOCK 1u
#define AGI_CTL_SIGNAL 2u
#define AGI_CTL_WATER 3u
#define AGI_PRIORITY_MIN 4u
#define AGI_PRIORITY_MAX 15u
// Priority bands: y below 48 is priority 4, then a band every 12 lines
// from priority 5 at y 48 to 14 at y 156..167.
#define AGI_PRI_BAND_TOP 48
#define AGI_PRI_BAND_HEIGHT 12
#define AGI_PRI_FIRST_BAND 5u
// The horizon new.room restores.
#define AGI_DEFAULT_HORIZON 36u
// Animated (screen) objects: OBJECT's header says how many a game uses.
#define AGI_MAX_OBJECTS 32u
#define AGI_EGO 0u
// Inventory items and the 40-character strings.
#define AGI_MAX_ITEMS 256u
#define AGI_ITEM_CARRIED 255u
#define AGI_MAX_STRINGS 24u
#define AGI_STRING_LEN 40u
// Variables, flags, controllers (set.key and menu items share one space).
#define AGI_NUM_VARS 256u
#define AGI_NUM_FLAGS 256u
#define AGI_NUM_CONTROLLERS 256u
#define AGI_MAX_KEYMAPS 64u
#define AGI_SCRIPT_MAX 200u
#define AGI_SCRIPT_ARGS 7u
// Menus.
#define AGI_MAX_MENUS 10u
#define AGI_MAX_MENU_ITEMS 48u
#define AGI_MENU_NAME_MAX 20u
#define AGI_MENU_ITEM_MAX 38u
// The command line and the parser.
#define AGI_INPUT_MAX 40u
#define AGI_MAX_WORDS 12u
#define AGI_WORD_MAX_LEN 40u
#define AGI_WORD_IGNORE 0u
#define AGI_WORD_ANY 1u
#define AGI_WORD_REST 9999u
#define AGI_WORD_NONE 0xFFFFu
#define AGI_WORDS_HEADER_BYTES 52u
#define AGI_WORDS_MAX 16384u
// Key codes as AGI sees them: the character, or the PC scan code in the
// high byte. Joystick buttons are 0x0101 and 0x0201.
#define AGI_KEY_NONE 0u
#define AGI_KEY_BACKSPACE 0x0008u
#define AGI_KEY_ENTER 0x000Du
#define AGI_KEY_ESCAPE 0x001Bu
#define AGI_KEY_UP 0x4800u
#define AGI_KEY_DOWN 0x5000u
#define AGI_KEY_LEFT 0x4B00u
#define AGI_KEY_RIGHT 0x4D00u
#define AGI_KEY_JOY_BUTTON_1 0x0101u
#define AGI_KEY_JOY_BUTTON_2 0x0201u
// The keypad's 5: ego stops (hold.key mode sends it when the direction key
// last pressed is let go).
#define AGI_KEY_STATIONARY 0x4C00u
// A left click, queued with the keys (no key has this code).
#define AGI_KEY_CLICK 0xF101u // ScummVM's code for it, so v19 reads alike
// A menu.input or inventory answer meaning "nothing chosen".
#define AGI_NO_CONTROLLER 0xFFu
// inputGetString with no position: on the input line.
#define AGI_NO_POSITION 0xFFu
// No set.cursor.char yet: the input line shows no cursor.
#define AGI_NO_CURSOR '\0'
// The font (agiFont.c): characters 0x20..0x7E, 8 rows each.
#define AGI_FONT_FIRST 0x20u
#define AGI_FONT_GLYPHS 95u
#define AGI_FONT_ROWS 8u
extern const uint8_t kAgiFont[AGI_FONT_GLYPHS][AGI_FONT_ROWS];
// ----- Reserved variables and flags (AGI specification 3.3 and 3.4) -----
#define VAR_ROOM 0u
#define VAR_PREV_ROOM 1u
#define VAR_EGO_EDGE 2u
#define VAR_SCORE 3u
#define VAR_OBJ_EDGE_OBJ 4u
#define VAR_OBJ_EDGE 5u
#define VAR_EGO_DIR 6u
#define VAR_MAX_SCORE 7u
#define VAR_FREE_PAGES 8u
#define VAR_BAD_WORD 9u
#define VAR_CYCLE_DELAY 10u
#define VAR_SECONDS 11u
#define VAR_MINUTES 12u
#define VAR_HOURS 13u
#define VAR_DAYS 14u
#define VAR_EGO_VIEW 16u
#define VAR_KEY 19u
#define VAR_COMPUTER 20u
#define VAR_WINDOW_TIMER 21u
#define VAR_SOUND_TYPE 22u
#define VAR_INPUT_MAX 24u
#define VAR_SELECTED_ITEM 25u
#define VAR_MONITOR 26u
#define FLAG_EGO_WATER 0u
#define FLAG_ENTERED 2u
#define FLAG_EGO_SIGNAL 3u
#define FLAG_SAID_OK 4u
#define FLAG_NEW_ROOM 5u
#define FLAG_RESTART 6u
#define FLAG_NO_SCRIPT 7u
#define FLAG_SOUND 9u
#define FLAG_FIRST_LOGIC0 11u
#define FLAG_RESTORED 12u
#define FLAG_ITEM_SELECT 13u
#define FLAG_MENU 14u
#define FLAG_WINDOW_STAYS 15u
// ----- Interpreter versions -----
// The Sierra interpreter a game shipped with, as major * 1000 + minor
// (2.435 is 2435), read from the game's AGIDATA.OVL (or AGI) when its
// folder has one; otherwise the version the quirks were measured on, or
// for an AGI v3 game whose folder says nothing (a non-DOS release) the last
// v3 interpreter, as ScummVM assumes for most.
#define AGI_VERSION_MEASURED 2435u
#define AGI_VERSION_V3_FIRST 3000u
#define AGI_VERSION_V3_ASSUMED 3149u
// 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
// What distance reports for an object not on the screen.
#define AGI_DISTANCE_FAR 255u
// Behavior of particular Sierra interpreter versions that games may lean
// on; agiQuirk says whether the game's version has one (the version ranges
// are agiRes.c's kQuirkVersions).
typedef enum {
// A window's x is a byte and the screen one line of 160-pixel rows:
// a box starting left of the screen starts 256 pixels on instead,
// and every span runs on into the next row.
AGI_QUIRK_WINDOW_BYTE_X = 0,
// A typed character first steps back over the cursor, whether or not
// one was drawn there.
AGI_QUIRK_ECHO_STEPS_BACK = 1,
// A picture line carries its error in a byte, losing steps once it
// passes 255.
AGI_QUIRK_LINE_BYTE_ERROR = 2,
AGI_QUIRK_COUNT = 3
} AgiQuirkE;
// ----- Resources (agiRes.c) -----
typedef enum {
AGI_RES_LOGIC = 0,
AGI_RES_PIC = 1,
AGI_RES_VIEW = 2,
AGI_RES_SOUND = 3,
AGI_RES_COUNT = 4
} AgiResTypeE;
typedef struct {
uint8_t volume;
uint8_t offsetHigh;
uint16_t offsetLow;
} AgiResEntryT;
// The longest v3 file-name prefix ("MH2" of MH2DIR and MH2VOL.0).
#define AGI_PREFIX_MAX 3u
// The games agiPatch.c changes, known by their v3 file prefix.
typedef enum {
AGI_GAME_UNKNOWN = 0,
AGI_GAME_KQ4,
AGI_GAME_GOLD_RUSH,
AGI_GAME_MANHUNTER_1,
AGI_GAME_MANHUNTER_2,
AGI_GAME_BLACK_CAULDRON,
AGI_GAME_DEMO
} AgiGameIdE;
typedef struct {
FILE *volFiles[AGI_MAX_VOLUMES];
uint16_t resCount[AGI_RES_COUNT];
AgiResEntryT resDir[AGI_RES_COUNT][AGI_MAX_RESOURCES];
uint8_t *words;
uint16_t wordsLen;
uint8_t *objectData;
uint16_t itemCount;
uint8_t maxObjects;
uint16_t interpreterVersion;
// AGI v3: one <prefix>DIR holds the four directories, the volumes are
// <prefix>VOL.n, and resources have 7-byte headers and may be packed.
bool v3;
char prefix[AGI_PREFIX_MAX + 1u];
AgiGameIdE id;
} AgiGameT;
// ----- Pictures and views -----
typedef struct {
uint8_t width;
uint8_t height;
uint8_t transparent;
uint8_t mirrorFlags;
const uint8_t *rle;
} AgiCelT;
typedef struct {
uint8_t celCount;
AgiCelT *cels;
} AgiLoopT;
typedef struct {
uint8_t *raw;
uint16_t rawLength;
uint8_t loopCount;
AgiLoopT *loops;
const char *description;
} AgiViewT;
// ----- Screen objects -----
typedef enum {
AGI_CYCLE_NORMAL = 0,
AGI_CYCLE_END_LOOP = 1,
AGI_CYCLE_REVERSE_LOOP = 2,
AGI_CYCLE_REVERSE = 3
} AgiCycleE;
typedef enum {
AGI_MOTION_NORMAL = 0,
AGI_MOTION_WANDER = 1,
AGI_MOTION_FOLLOW = 2,
AGI_MOTION_MOVE = 3,
AGI_MOTION_MOUSE = 4 // ego walking to a click (agiMouse.c)
} AgiMotionE;
// Directions: 0 is still, 1..8 run clockwise from north.
#define AGI_DIR_NONE 0u
#define AGI_DIR_N 1u
#define AGI_DIR_NE 2u
#define AGI_DIR_E 3u
#define AGI_DIR_SE 4u
#define AGI_DIR_S 5u
#define AGI_DIR_SW 6u
#define AGI_DIR_W 7u
#define AGI_DIR_NW 8u
#define AGI_DIR_COUNT 9u
// Edges, as v2 and v5 report them.
#define AGI_EDGE_NONE 0u
#define AGI_EDGE_TOP 1u
#define AGI_EDGE_RIGHT 2u
#define AGI_EDGE_BOTTOM 3u
#define AGI_EDGE_LEFT 4u
#define OBJ_ANIMATED 0x0001u
#define OBJ_DRAWN 0x0002u
#define OBJ_UPDATE 0x0004u
#define OBJ_CYCLING 0x0008u
#define OBJ_FIX_LOOP 0x0010u
#define OBJ_FIX_PRIORITY 0x0020u
#define OBJ_IGNORE_HORIZON 0x0040u
#define OBJ_IGNORE_BLOCKS 0x0080u
#define OBJ_IGNORE_OBJS 0x0100u
#define OBJ_ON_WATER 0x0200u
#define OBJ_ON_LAND 0x0400u
// Measured from Sierra's interpreter: an object's first step after the
// game starts or the room changes is lost, as is the first cel change
// after end.of.loop or reverse.loop on an object already drawn.
#define OBJ_SKIP_STEP 0x0800u
#define OBJ_SKIP_CYCLE 0x1000u
// Its last step left it where it was (wander and follow.ego turn aside).
#define OBJ_DIDNT_MOVE 0x2000u
// The four bytes an object's cycle and motion modes share, as in Sierra's
// interpreter: starting one mode overwrites what another left there, and
// games rely on it (King's Quest 1's eagle, Black Cauldron's witches).
#define AGI_OBJ_PARMS 4u
#define PARM_LOOP_FLAG 0u // end.of.loop, reverse.loop: the flag
#define PARM_WANDER_COUNT 0u // wander: cycles left on this heading
#define PARM_FOLLOW_STEP 0u // follow.ego: how near is there
#define PARM_FOLLOW_FLAG 1u
#define PARM_FOLLOW_COUNT 2u // follow.ego: a detour's steps left
#define PARM_MOVE_X 0u // move.obj
#define PARM_MOVE_Y 1u
#define PARM_MOVE_STEP 2u // move.obj: the step size to restore
#define PARM_MOVE_FLAG 3u
typedef struct {
const AgiViewT *view;
uint8_t viewId;
uint8_t loop;
uint8_t cel;
uint8_t width;
uint8_t height;
uint8_t priority;
int16_t x;
int16_t y;
int16_t prevX;
int16_t prevY;
uint16_t flags;
uint8_t direction;
uint8_t stepSize;
uint8_t stepTime;
uint8_t stepCount;
uint8_t cycleTime;
uint8_t cycleCount;
uint8_t cycleMode;
uint8_t motion;
uint8_t parm[AGI_OBJ_PARMS];
// Blit state: where the object was last drawn and the buffer bytes
// its cel covered there, restored when it is erased.
bool blitted;
int16_t blitX;
int16_t blitY;
uint8_t blitW;
uint8_t blitH;
uint8_t *saveUnder;
} AgiObjectT;
// ----- Text, menus, the parser -----
typedef struct {
uint16_t key;
uint8_t controller;
} AgiKeyMapT;
// One recorded picture-building command (the restore script): an AGI
// command number and its argument values (add.to.pic has seven).
typedef struct {
uint8_t op;
uint8_t args[AGI_SCRIPT_ARGS];
} AgiScriptEntryT;
typedef struct {
char text[AGI_MENU_ITEM_MAX + 1u];
uint8_t controller;
bool enabled;
} AgiMenuItemT;
typedef struct {
char name[AGI_MENU_NAME_MAX + 1u];
uint8_t firstItem;
uint8_t itemCount;
uint8_t col;
} AgiMenuT;
// ----- The interpreter state -----
typedef struct {
AgiGameT game;
uint8_t vars[AGI_NUM_VARS];
uint8_t flags[AGI_NUM_FLAGS];
char strings[AGI_MAX_STRINGS][AGI_STRING_LEN + 1u];
uint8_t controllers[AGI_NUM_CONTROLLERS];
AgiKeyMapT keyMaps[AGI_MAX_KEYMAPS];
uint8_t keyMapCount;
// The commands that built the current picture, replayed by restore.
AgiScriptEntryT script[AGI_SCRIPT_MAX];
uint8_t scriptCount;
// Logic resources (loaded on demand) and each one's scan start.
uint8_t *logicRaw[AGI_MAX_RESOURCES];
uint16_t logicLength[AGI_MAX_RESOURCES];
uint16_t logicScanStart[AGI_MAX_RESOURCES];
bool logicLoaded[AGI_MAX_RESOURCES];
uint8_t currentLogic;
bool abortLogic;
bool newRoomPending;
uint8_t newRoomNumber;
// Views loaded with load.view or on demand.
AgiViewT *views[AGI_MAX_RESOURCES];
// Screen objects.
AgiObjectT objects[AGI_MAX_OBJECTS];
uint8_t objectCount;
uint8_t horizon;
bool blockActive;
uint8_t blockX1;
uint8_t blockY1;
uint8_t blockX2;
uint8_t blockY2;
bool playerControl;
// Inventory: the room each item is in.
uint8_t itemRoom[AGI_MAX_ITEMS];
// Screen layout (configure.screen) and text state.
uint8_t picRow;
uint8_t inputRow;
uint8_t statusRow;
bool statusOn;
bool inputOn;
bool textMode;
uint8_t textFg;
uint8_t textBg;
char cursorChar;
bool windowOpen;
// The command line.
char inputLine[AGI_INPUT_MAX + 1u];
uint8_t inputLen;
char lastLine[AGI_INPUT_MAX + 1u];
uint16_t wordIds[AGI_MAX_WORDS];
char wordText[AGI_MAX_WORDS][AGI_WORD_MAX_LEN + 1u];
uint8_t wordCount;
// Menus.
AgiMenuT menus[AGI_MAX_MENUS];
AgiMenuItemT menuItems[AGI_MAX_MENU_ITEMS];
uint8_t menuCount;
uint8_t menuItemCount;
bool menuSubmitted;
bool menuRequested;
uint8_t menuLastMenu;
bool menuDenied; // allow.menu(0): a requested menu does not open
// The modes of the 2.425-and-later commands: hold.key (ego walks only
// while a direction key is down), set.simple (save and restore without
// asking, under that description), set.pri.base (a y-to-priority table
// of its own instead of the 12-row bands).
bool holdKey;
bool simpleSaveOn;
char simpleSaveDesc[AGI_STRING_LEN + 1u];
bool priorityTableSet;
uint8_t priorityTable[AGI_HEIGHT];
// The mouse (AGI 3): hidden by hide.mouse, kept to fence.mouse's box.
bool mouseHidden;
uint8_t mouseFence[4];
// Requests that end the cycle, and a restore that just did.
bool quitRequested;
bool restartRequested;
bool restored;
} AgiStateT;
extern AgiStateT *gAgi;
// ----- agiPatch.c -----
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);
// ----- agiRes.c -----
void agiDecrypt(uint8_t *data, uint16_t length);
const char *agiItemName(const AgiGameT *game, uint8_t item);
uint8_t agiItemStartRoom(const AgiGameT *game, uint8_t item);
bool agiQuirk(AgiQuirkE quirk);
void agiResClose(AgiGameT *game);
uint8_t *agiResLoad(const AgiGameT *game, AgiResTypeE type, uint16_t index, uint16_t *outLength);
bool agiResOpen(AgiGameT *game);
bool agiResPacked(const AgiGameT *game, AgiResTypeE type, uint16_t index);
bool agiResPresent(const AgiGameT *game, AgiResTypeE type, uint16_t index);
uint16_t agiWordMatch(const AgiGameT *game, const char *text, uint8_t *outLen);
// ----- agiPic.c -----
bool agiPicDecode(uint8_t *visual, uint8_t *priority, const uint8_t *data, uint16_t length);
uint8_t agiPriorityForY(int16_t y);
void agiPriorityReset(void);
void agiPrioritySetBase(uint8_t base);
int16_t agiYForPriority(uint8_t priority);
// ----- agiView.c -----
const AgiCelT *agiViewCel(const AgiViewT *view, uint8_t loop, uint8_t cel, bool *outMirror);
void agiViewDiscardUnused(void);
const AgiViewT *agiViewGet(uint8_t viewId);
// ----- agiGfx.c -----
void gfxAddToPic(uint8_t viewId, uint8_t loop, uint8_t cel, int16_t x, int16_t y, uint8_t priority, uint8_t margin);
uint8_t gfxControlAt(int16_t x, int16_t y);
void gfxDrawPic(uint8_t picId, bool clearFirst);
void gfxDrawStatic(void);
void gfxEraseAll(void);
void gfxEraseUpdating(void);
void gfxFree(void);
bool gfxInit(void);
void gfxPresentDirty(void);
void gfxRedrawAll(void);
void gfxRedrawUpdating(void);
void gfxRestorePicture(void);
void gfxShakeScreen(uint8_t times);
void gfxShowObjectCel(uint8_t viewId);
void gfxShowPic(void);
void gfxShowPriority(void);
void gfxStampObject(uint8_t n);
// ----- agiObj.c -----
void objAnimate(uint8_t n);
void objClearAll(void);
uint8_t objDirectionTo(int16_t fromX, int16_t fromY, int16_t toX, int16_t toY, uint8_t slack);
void objFixPosition(uint8_t n);
void objFollowEgo(uint8_t n, uint8_t step, uint8_t flag);
void objMouseWalk(uint8_t x, uint8_t y);
void objMoveTo(uint8_t n, uint8_t x, uint8_t y, uint8_t step, uint8_t flag);
void objPlanMotion(void);
void objSetCel(uint8_t n, uint8_t cel);
void objSetLoop(uint8_t n, uint8_t loop);
bool objSetView(uint8_t n, uint8_t viewId);
void objUnanimateAll(void);
void objUpdateAll(void);
// ----- agiMouse.c -----
void mouseClick(void);
void mouseDrawPointer(void);
void mouseErasePointer(void);
void mouseInit(void);
bool mouseOver(int16_t *col, int16_t *row);
void mousePosition(uint8_t *x, uint8_t *y);
void mouseShutdown(void);
// ----- agiText.c -----
void textClearLines(uint8_t top, uint8_t bottom, uint8_t color);
void textClearRect(uint8_t top, uint8_t left, uint8_t bottom, uint8_t right, uint8_t color);
void textCloseWindow(void);
void textDisplay(uint8_t row, uint8_t col, const char *text);
void textDrawInputLine(void);
void textDrawStatus(void);
void textEchoInput(uint8_t oldLen);
void textExpand(const char *src, char *dst, uint16_t dstCap);
void textGraphicsMode(void);
void textInventory(void);
void textMenuAdd(const char *name);
void textMenuAddItem(const char *name, uint8_t controller);
uint8_t textMenuInput(void);
void textMessageBox(const char *text);
void textPrint(const char *text, int16_t row, int16_t col, uint8_t width);
uint8_t textPrompt(char *out);
void textSetAttribute(uint8_t fg, uint8_t bg);
void textShowObject(uint8_t viewId);
void textTextMode(void);
void textUpdateStatus(void);
void textWindowPut(uint8_t line, uint8_t col, const char *text);
// ----- agiInput.c -----
bool inputGetNumber(const char *prompt, uint8_t *outValue);
bool inputGetString(char *dst, uint8_t maxLen, uint8_t row, uint8_t col, const char *prompt);
bool inputHaveKey(void);
void inputParse(const char *line);
void inputPoll(void);
void inputProcess(void);
void inputReset(void);
uint16_t inputPollKey(void);
uint16_t inputWaitKey(void);
bool inputWaitPrintClose(uint16_t timeoutHalfSeconds);
// ----- agiSound.c -----
void soundInit(void);
void soundPump(void);
void soundShutdown(void);
void soundStart(uint8_t soundId, uint8_t flag);
void soundStop(void);
// ----- agiSave.c -----
bool restoreGame(void);
void saveGame(void);
void saveScriptAdd(uint8_t op, uint8_t arg);
void saveScriptAddArgs(uint8_t op, const uint8_t *args);
void saveScriptClear(void);
// ----- agiVm.c -----
// The VM's trace, in AGI_TRACE builds (the host harness): gAgiTrace, when
// set, hears every command a logic runs (its arguments; result -1) and every
// condition (op 0xFF, no arguments; result 0 or 1), with the logic and the
// command's offset in it.
#if defined(AGI_TRACE)
typedef void (*AgiTraceFnT)(uint8_t logic, uint16_t pc, uint8_t op, const uint8_t *args, uint8_t argCount, int8_t result);
extern AgiTraceFnT gAgiTrace;
#endif
void vmCallLogic(uint8_t logicId);
void vmDiscardLogics(void);
bool vmLoadLogic(uint8_t logicId);
const char *vmMessage(uint8_t logicId, uint8_t msgId);
void vmReplay(const AgiScriptEntryT *e);
// ----- agi.c -----
void agiIdle(void);
void agiNewRoom(uint8_t room);
void agiObjectStatus(uint8_t n);
void agiQuit(bool immediately);
uint8_t agiRandom(void);
void agiResetState(void);
void agiRestartGame(void);
#endif