aboutsummaryrefslogtreecommitdiff
path: root/engines/agi
diff options
context:
space:
mode:
Diffstat (limited to 'engines/agi')
-rw-r--r--engines/agi/agi.cpp627
-rw-r--r--engines/agi/agi.h545
-rw-r--r--engines/agi/agi_v2.cpp309
-rw-r--r--engines/agi/agi_v3.cpp389
-rw-r--r--engines/agi/checks.cpp320
-rw-r--r--engines/agi/console.cpp873
-rw-r--r--engines/agi/console.h71
-rw-r--r--engines/agi/cycle.cpp423
-rw-r--r--engines/agi/font.cpp295
-rw-r--r--engines/agi/global.cpp73
-rw-r--r--engines/agi/graphics.cpp742
-rw-r--r--engines/agi/graphics.h86
-rw-r--r--engines/agi/id.cpp475
-rw-r--r--engines/agi/inv.cpp212
-rw-r--r--engines/agi/keyboard.cpp388
-rw-r--r--engines/agi/keyboard.h94
-rw-r--r--engines/agi/list.h140
-rw-r--r--engines/agi/logic.cpp116
-rw-r--r--engines/agi/logic.h49
-rw-r--r--engines/agi/lzw.cpp191
-rw-r--r--engines/agi/lzw.h33
-rw-r--r--engines/agi/menu.cpp503
-rw-r--r--engines/agi/menu.h46
-rw-r--r--engines/agi/module.mk42
-rw-r--r--engines/agi/motion.cpp232
-rw-r--r--engines/agi/objects.cpp167
-rw-r--r--engines/agi/op_cmd.cpp1513
-rw-r--r--engines/agi/op_dbg.cpp353
-rw-r--r--engines/agi/op_test.cpp419
-rw-r--r--engines/agi/opcodes.h55
-rw-r--r--engines/agi/patches.cpp144
-rw-r--r--engines/agi/picture.cpp1034
-rw-r--r--engines/agi/picture.h47
-rw-r--r--engines/agi/savegame.cpp790
-rw-r--r--engines/agi/savegame.h50
-rw-r--r--engines/agi/sound.cpp772
-rw-r--r--engines/agi/sound.h167
-rw-r--r--engines/agi/sprite.cpp868
-rw-r--r--engines/agi/sprite.h47
-rw-r--r--engines/agi/text.cpp677
-rw-r--r--engines/agi/text.h48
-rw-r--r--engines/agi/view.cpp396
-rw-r--r--engines/agi/view.h142
-rw-r--r--engines/agi/words.cpp213
44 files changed, 15176 insertions, 0 deletions
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
new file mode 100644
index 0000000000..7487cbecf9
--- /dev/null
+++ b/engines/agi/agi.cpp
@@ -0,0 +1,627 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/config-manager.h"
+
+#include "base/plugins.h"
+
+#include "backends/fs/fs.h"
+
+#include "sound/mididrv.h"
+#include "sound/mixer.h"
+
+#include "agi/agi.h"
+#include "agi/text.h"
+#include "agi/graphics.h"
+#include "agi/sprite.h"
+#include "agi/opcodes.h"
+#include "agi/keyboard.h"
+#include "agi/menu.h"
+#include "agi/savegame.h"
+
+namespace Agi {
+
+void gfx_set_palette();
+
+extern int optind;
+
+struct agi_options opt;
+struct game_id_list game_info;
+struct agi_game game;
+
+static struct agi_loader *loader; /* loader */
+
+extern struct agi_loader agi_v2;
+extern struct agi_loader agi_v3;
+
+static volatile uint32 tick_timer = 0;
+
+#define TICK_SECONDS 20
+
+static int key_control = 0;
+static int key_alt = 0;
+
+#define KEY_QUEUE_SIZE 16
+
+static int key_queue[KEY_QUEUE_SIZE];
+static int key_queue_start = 0;
+static int key_queue_end = 0;
+
+#define key_enqueue(k) do { key_queue[key_queue_end++] = (k); \
+ key_queue_end %= KEY_QUEUE_SIZE; } while (0)
+#define key_dequeue(k) do { (k) = key_queue[key_queue_start++]; \
+ key_queue_start %= KEY_QUEUE_SIZE; } while (0)
+
+static void process_events() {
+ OSystem::Event event;
+ int key = 0;
+
+ while (g_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_QUIT:
+ deinit_video();
+ deinit_machine();
+ g_system->quit();
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ key = BUTTON_LEFT;
+ mouse.button = 1;
+ key_enqueue(key);
+ mouse.x = event.mouse.x;
+ mouse.y = event.mouse.y;
+ break;
+ case OSystem::EVENT_RBUTTONDOWN:
+ key = BUTTON_RIGHT;
+ mouse.button = 2;
+ key_enqueue(key);
+ mouse.x = event.mouse.x;
+ mouse.y = event.mouse.y;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ mouse.x = event.mouse.x;
+ mouse.y = event.mouse.y;
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ case OSystem::EVENT_RBUTTONUP:
+ mouse.button = 0;
+ break;
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.flags == OSystem::KBD_CTRL) {
+ key_control |= 1;
+ key = 0;
+ break;
+ } else if (event.kbd.flags == OSystem::KBD_ALT) {
+ key_alt |= 1;
+ key = 0;
+ break;
+ } else if (event.kbd.flags == OSystem::KBD_SHIFT) {
+ key = 0;
+ break;
+ }
+
+ switch (key = event.kbd.keycode) {
+ case 256 + 20: // left arrow
+ key = KEY_LEFT;
+ break;
+ case 256 + 19: // right arrow
+ key = KEY_RIGHT;
+ break;
+ case 256 + 17: // up arrow
+ key = KEY_UP;
+ break;
+ case 256 + 18: // down arrow
+ key = KEY_DOWN;
+ break;
+ case 256 + 24: // page up
+ key = KEY_UP_RIGHT;
+ break;
+ case 256 + 25: // page down
+ key = KEY_DOWN_RIGHT;
+ break;
+ case 256 + 22: // home
+ key = KEY_UP_LEFT;
+ break;
+ case 256 + 23: // end
+ key = KEY_DOWN_LEFT;
+ break;
+ case '+':
+ key = '+';
+ break;
+ case '-':
+ key = '-';
+ break;
+ case 9:
+ key = 0x0009;
+ break;
+ case 282:
+ key = 0x3b00;
+ break;
+ case 283:
+ key = 0x3c00;
+ break;
+ case 284:
+ key = 0x3d00;
+ break;
+ case 285:
+ key = 0x3e00;
+ break;
+ case 286:
+ key = 0x3f00;
+ break;
+ case 287:
+ key = 0x4000;
+ break;
+ case 288:
+ key = 0x4100;
+ break;
+ case 289:
+ key = 0x4200;
+ break;
+ case 290:
+ key = 0x4300;
+ break;
+ case 291:
+ key = 0x4400;
+ break;
+ case 292:
+ key = KEY_STATUSLN;
+ break;
+ case 293:
+ key = KEY_PRIORITY;
+ break;
+ case 27:
+ key = 0x1b;
+ break;
+ case '\n':
+ case '\r':
+ key = KEY_ENTER;
+ break;
+ default:
+ if (!isalpha(key))
+ break;
+ if (key_control)
+ key = (key & ~0x20) - 0x40;
+ else if (key_alt)
+ key = scancode_table[(key & ~0x20) - 0x41] << 8;
+ break;
+ }
+ if (key)
+ key_enqueue(key);
+ break;
+ case OSystem::EVENT_KEYUP:
+ if (event.kbd.flags == OSystem::KBD_CTRL) {
+ key_control &= ~1;
+ key = 0;
+ break;
+ } else if (event.kbd.flags == OSystem::KBD_ALT) {
+ key_alt &= ~1;
+ key = 0;
+ break;
+ } else if (event.kbd.flags == OSystem::KBD_SHIFT) {
+ key = 0;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+int agi_is_keypress_low() {
+ process_events();
+ return key_queue_start != key_queue_end;
+}
+
+void agi_timer_low() {
+ static uint32 m = 0;
+ uint32 dm;
+
+ if (tick_timer < m)
+ m = 0;
+
+ while ((dm = tick_timer - m) < 5) {
+ process_events();
+ g_system->delayMillis(10);
+ g_system->updateScreen();
+ }
+ m = tick_timer;
+}
+
+int agi_get_keypress_low() {
+ int k;
+
+ while (key_queue_start == key_queue_end) /* block */
+ agi_timer_low();
+ key_dequeue(k);
+
+ return k;
+}
+
+static uint32 agi_timer_function_low(uint32 i) {
+ tick_timer++;
+ return i;
+}
+
+static void init_pri_table() {
+ int i, p, y = 0;
+
+ for (p = 1; p < 15; p++) {
+ for (i = 0; i < 12; i++) {
+ game.pri_table[y++] = p < 4 ? 4 : p;
+ }
+ }
+}
+
+int agi_init() {
+ int ec, i;
+
+ debug(2, "initializing");
+ debug(2, "game.ver = 0x%x", game.ver);
+
+ /* reset all flags to false and all variables to 0 */
+ for (i = 0; i < MAX_FLAGS; i++)
+ game.flags[i] = 0;
+ for (i = 0; i < MAX_VARS; i++)
+ game.vars[i] = 0;
+
+ /* clear all resources and events */
+ for (i = 0; i < MAX_DIRS; i++) {
+ memset(&game.views[i], 0, sizeof(struct agi_view));
+ memset(&game.pictures[i], 0, sizeof(struct agi_picture));
+ memset(&game.logics[i], 0, sizeof(struct agi_logic));
+ memset(&game.sounds[i], 0, sizeof(struct agi_sound));
+ }
+
+ /* clear view table */
+ for (i = 0; i < MAX_VIEWTABLE; i++)
+ memset(&game.view_table[i], 0, sizeof(struct vt_entry));
+
+ init_words();
+
+ menu_init();
+ init_pri_table();
+
+ /* clear string buffer */
+ for (i = 0; i < MAX_STRINGS; i++)
+ game.strings[i][0] = 0;
+
+ /* setup emulation */
+
+ switch (loader->int_version >> 12) {
+ case 2:
+ report("Emulating Sierra AGI v%x.%03x\n",
+ (int)(loader->int_version >> 12) & 0xF,
+ (int)(loader->int_version) & 0xFFF);
+ break;
+ case 3:
+ report("Emulating Sierra AGI v%x.002.%03x\n",
+ (int)(loader->int_version >> 12) & 0xF,
+ (int)(loader->int_version) & 0xFFF);
+ break;
+ }
+
+ game.game_flags |= opt.amiga ? ID_AMIGA : 0;
+ game.game_flags |= opt.agds ? ID_AGDS : 0;
+
+ if (game.game_flags & ID_AMIGA)
+ report("Amiga padded game detected.\n");
+
+ if (game.game_flags & ID_AGDS)
+ report("AGDS mode enabled.\n");
+
+ ec = loader->init(); /* load vol files, etc */
+
+ if (ec == err_OK)
+ ec = loader->load_objects(OBJECTS);
+
+ /* note: demogs has no words.tok */
+ if (ec == err_OK)
+ ec = loader->load_words(WORDS);
+
+ /* FIXME: load IIgs instruments and samples */
+ /* load_instruments("kq.sys16"); */
+
+ /* Load logic 0 into memory */
+ if (ec == err_OK)
+ ec = loader->load_resource(rLOGIC, 0);
+
+ return ec;
+}
+
+/*
+ * Public functions
+ */
+
+void agi_unload_resources() {
+ int i;
+
+ /* Make sure logic 0 is always loaded */
+ for (i = 1; i < MAX_DIRS; i++) {
+ loader->unload_resource(rLOGIC, i);
+ }
+ for (i = 0; i < MAX_DIRS; i++) {
+ loader->unload_resource(rVIEW, i);
+ loader->unload_resource(rPICTURE, i);
+ loader->unload_resource(rSOUND, i);
+ }
+}
+
+int agi_deinit() {
+ int ec;
+
+ clean_input(); /* remove all words from memory */
+ agi_unload_resources(); /* unload resources in memory */
+ loader->unload_resource(rLOGIC, 0);
+ ec = loader->deinit();
+ unload_objects();
+ unload_words();
+
+ clear_image_stack();
+
+ return ec;
+}
+
+int agi_detect_game() {
+ int ec = err_OK;
+
+ loader = &agi_v2;
+ ec = loader->detect_game();
+
+ if (ec != err_OK) {
+ loader = &agi_v3;
+ ec = loader->detect_game();
+ }
+
+ return ec;
+}
+
+int agi_version() {
+ return loader->version;
+}
+
+int agi_get_release() {
+ return loader->int_version;
+}
+
+void agi_set_release(int n) {
+ loader->int_version = n;
+}
+
+int agi_load_resource(int r, int n) {
+ int i;
+
+ i = loader->load_resource(r, n);
+#ifdef PATCH_LOGIC
+ if (r == rLOGIC)
+ patch_logic(n);
+#endif
+
+ return i;
+}
+
+int agi_unload_resource(int r, int n) {
+ return loader->unload_resource(r, n);
+}
+
+const char *_savePath;
+extern AGIMusic *g_agi_music;
+
+struct GameSettings {
+ const char *gameid;
+ const char *description;
+ byte id;
+ uint32 features;
+ const char *detectname;
+};
+
+static const GameSettings agi_settings[] = {
+ {"agi", "AGI game", GID_AGI, MDT_ADLIB, "VIEWDIR"},
+ {NULL, NULL, 0, 0, NULL}
+};
+
+Common::RandomSource * rnd;
+
+AgiEngine::AgiEngine(OSystem * syst) : Engine(syst) {
+
+ // Setup mixer
+ if (!_mixer->isReady()) {
+ warning("Sound initialization failed.");
+ }
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+
+ _savePath = _saveFileMan->getSavePath();
+
+ const GameSettings *g;
+
+ const char *gameid = ConfMan.get("gameid").c_str();
+ for (g = agi_settings; g->gameid; ++g)
+ if (!scumm_stricmp(g->gameid, gameid))
+ _gameId = g->id;
+
+ rnd = new Common::RandomSource();
+
+ game.clock_enabled = false;
+ game.state = STATE_INIT;
+}
+
+void AgiEngine::initialize() {
+ memset(&opt, 0, sizeof(struct agi_options));
+ opt.gamerun = GAMERUN_RUNGAME;
+#ifdef USE_HIRES
+ opt.hires = true;
+#endif
+ opt.soundemu = SOUND_EMU_NONE;
+
+ init_machine();
+
+ game.color_fg = 15;
+ game.color_bg = 0;
+
+ *game.name = 0;
+
+ game.sbuf = (uint8 *) calloc(_WIDTH, _HEIGHT);
+#ifdef USE_HIRES
+ game.hires = (uint8 *) calloc(_WIDTH * 2, _HEIGHT);
+#endif
+
+ init_sprites();
+ init_video();
+
+ tick_timer = 0;
+ Common::g_timer->installTimerProc((Common::Timer::TimerProc) agi_timer_function_low, 10 * 1000, NULL);
+
+ console_init();
+
+ game.ver = -1; /* Don't display the conf file warning */
+
+ debugC(2, kDebugLevelMain, "Detect game");
+ if (agi_detect_game() == err_OK) {
+ game.state = STATE_LOADED;
+ debugC(2, kDebugLevelMain, "game loaded");
+ } else {
+ report("Could not open AGI game");
+ }
+
+ debugC(2, kDebugLevelMain, "Init sound");
+ init_sound();
+ g_agi_music = new AGIMusic(_mixer);
+}
+
+AgiEngine::~AgiEngine() {
+ agi_deinit();
+ delete g_agi_music;
+ deinit_sound();
+ deinit_video();
+ deinit_sprites();
+#ifdef USE_HIRES
+ free(game.hires);
+#endif
+ free(game.sbuf);
+ deinit_machine();
+ delete rnd;
+}
+
+void AgiEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+}
+
+int AgiEngine::init() {
+ // Initialize backend
+ _system->beginGFXTransaction();
+ initCommonGFX(false);
+ _system->initSize(320, 200);
+ _system->endGFXTransaction();
+
+ Common::addSpecialDebugLevel(kDebugLevelMain, "Main", "Generic debug level");
+ Common::addSpecialDebugLevel(kDebugLevelResources, "Resources", "Resources debugging");
+ Common::addSpecialDebugLevel(kDebugLevelSprites, "Sprites", "Sprites debugging");
+ Common::addSpecialDebugLevel(kDebugLevelInventory, "Inventory", "Inventory debugging");
+ Common::addSpecialDebugLevel(kDebugLevelInput, "Input", "Input events debugging");
+ Common::addSpecialDebugLevel(kDebugLevelMenu, "Menu", "Menu debugging");
+ Common::addSpecialDebugLevel(kDebugLevelScripts, "Scrpits", "Scripts debugging");
+ Common::addSpecialDebugLevel(kDebugLevelSound, "Sound", "Sound debugging");
+ Common::addSpecialDebugLevel(kDebugLevelText, "Text", "Text output debugging");
+
+ initialize();
+
+ gfx_set_palette();
+
+ return 0;
+}
+
+int AgiEngine::go() {
+ _system->showMouse(true);
+
+ report(" \nAGI engine " VERSION " is ready.\n");
+ if (game.state < STATE_LOADED) {
+ console_prompt();
+ do {
+ main_cycle();
+ } while (game.state < STATE_RUNNING);
+ if (game.ver < 0)
+ game.ver = 0; /* Enable conf file warning */
+ }
+
+ run_game();
+
+ return 0;
+}
+
+} // End of namespace Agi
+
+GameList Engine_AGI_gameIDList() {
+ GameList games;
+ const Agi::GameSettings *g = Agi::agi_settings;
+
+ while (g->gameid) {
+ games.push_back(*g);
+ g++;
+ }
+
+ return games;
+}
+
+GameDescriptor Engine_AGI_findGameID(const char *gameid) {
+ const Agi::GameSettings *g = Agi::agi_settings;
+ while (g->gameid) {
+ if (0 == scumm_stricmp(gameid, g->gameid))
+ break;
+ g++;
+ }
+ return *g;
+}
+
+DetectedGameList Engine_AGI_detectGames(const FSList &fslist) {
+ DetectedGameList detectedGames;
+ const Agi::GameSettings * g;
+
+ for (g = Agi::agi_settings; g->gameid; ++g) {
+ // Iterate over all files in the given directory
+ for (FSList::const_iterator file = fslist.begin();
+ file != fslist.end(); ++file) {
+ const char *gameName = file->displayName().c_str();
+
+ if (0 == scumm_stricmp(g->detectname, gameName)) {
+ // Match found, add to list of candidates, then abort inner loop.
+ detectedGames.push_back(*g);
+ break;
+ }
+ }
+ }
+ return detectedGames;
+}
+
+PluginError Engine_AGI_create(OSystem *syst, Engine **engine) {
+ assert(engine);
+ *engine = new Agi::AgiEngine(syst);
+ return kNoError;
+}
+
+REGISTER_PLUGIN(AGI, "AGI Engine");
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
new file mode 100644
index 0000000000..f6d890c80c
--- /dev/null
+++ b/engines/agi/agi.h
@@ -0,0 +1,545 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_H
+#define __AGI_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/endian.h"
+#include "common/util.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/system.h"
+
+#include "base/engine.h"
+
+namespace Agi {
+
+typedef signed int Err;
+
+/*
+ * Version and other definitions
+ */
+#define INLINE __inline
+#define VERSION __DATE__ " " __TIME__
+
+/* Default features -- can be overriden in portdefs.h */
+#define USE_CONSOLE
+#define USE_PCM_SOUND
+#define USE_IIGS_SOUND
+#define USE_HIRES
+#define USE_MOUSE
+#define AGDS_SUPPORT
+
+#define TITLE "AGI engine"
+
+#define DIR_ "dir"
+#define LOGDIR "logdir"
+#define PICDIR "picdir"
+#define VIEWDIR "viewdir"
+#define SNDDIR "snddir"
+#define OBJECTS "object"
+#define WORDS "words.tok"
+
+#define MAX_DIRS 256
+#define MAX_VARS 256
+#define MAX_FLAGS (256 >> 3)
+#define MAX_VIEWTABLE 255 /* KQ3 uses o255! */
+#define MAX_WORDS 20
+#define MAX_STRINGS 24 /* MAX_STRINGS + 1 used for get.num */
+#define MAX_STRINGLEN 40
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+
+#define _EMPTY 0xfffff
+#define EGO_OWNED 0xff
+
+#define CRYPT_KEY_SIERRA "Avis Durgan"
+#define CRYPT_KEY_AGDS "Alex Simkin"
+
+#ifndef INLINE
+#define INLINE
+#endif
+
+#ifndef USE_PCM_SOUND
+#undef USE_IIGS_SOUND
+#endif
+
+#define MSG_BOX_COLOUR 0x0f /* White */
+#define MSG_BOX_TEXT 0x00 /* Black */
+#define MSG_BOX_LINE 0x04 /* Red */
+#define STATUS_FG 0x00 /* Black */
+#define STATUS_BG 0x0f /* White */
+#define PATCH_LOGIC /* disable copy protection on some games */
+
+} // End of namespace Agi
+
+/* AGI resources */
+#include "agi/console.h"
+#include "agi/view.h"
+#include "agi/picture.h"
+#include "agi/logic.h"
+#include "agi/sound.h"
+
+namespace Agi {
+
+int getflag(int);
+void setflag(int, int);
+void flipflag(int);
+int getvar(int);
+void setvar(int, int);
+void decrypt(uint8 * mem, int len);
+void release_sprites(void);
+int main_cycle(void);
+int view_pictures(void);
+int parse_cli(int, char **);
+int run_game(void);
+int init_machine(void);
+int deinit_machine(void);
+int get_direction(int x, int y, int x0, int y0, int s);
+void inventory(void);
+void list_games(void);
+uint32 match_crc(uint32, char *, int);
+int v2id_game(void);
+int v3id_game(void);
+int v4id_game(uint32 ver);
+void update_timer(void);
+int get_app_dir(char *app_dir, unsigned int size);
+
+enum {
+ NO_GAMEDIR = 0,
+ GAMEDIR
+};
+
+enum AGIErrors {
+ err_OK = 0,
+ err_DoNothing,
+ err_BadCLISwitch,
+ err_InvalidAGIFile,
+ err_BadFileOpen,
+ err_NotEnoughMemory,
+ err_BadResource,
+ err_UnknownAGIVersion,
+ err_RestartGame,
+ err_NoLoopsInView,
+ err_ViewDataError,
+ err_NoGameList,
+
+ err_Unk = 127
+};
+
+enum kDebugLevels {
+ kDebugLevelMain = 1 << 0,
+ kDebugLevelResources = 1 << 1,
+ kDebugLevelSprites = 1 << 2,
+ kDebugLevelInventory = 1 << 3,
+ kDebugLevelInput = 1 << 4,
+ kDebugLevelMenu = 1 << 5,
+ kDebugLevelScripts = 1 << 6,
+ kDebugLevelSound = 1 << 7,
+ kDebugLevelText = 1 << 8
+};
+
+/**
+ * AGI resources.
+ */
+enum {
+ rLOGIC = 1,
+ rSOUND,
+ rVIEW,
+ rPICTURE
+};
+
+enum {
+ RES_LOADED = 1,
+ RES_COMPRESSED = 0x40
+};
+
+enum {
+ lCOMMAND_MODE = 1,
+ lTEST_MODE
+};
+
+struct game_id_list {
+ struct game_id_list *next;
+ uint32 version;
+ uint32 crc;
+ char *gName;
+ char *switches;
+};
+
+#ifdef USE_MOUSE
+struct mouse {
+ int button;
+ unsigned int x;
+ unsigned int y;
+};
+#endif
+
+/**
+ * Command-line options.
+ */
+struct agi_options {
+#define GAMERUN_RUNGAME 0
+#define GAMERUN_PICVIEW 1
+#define GAMERUN_WORDS 2
+#define GAMERUN_OBJECTS 3
+#define GAMERUN_GAMES 4
+#define GAMERUN_CRC 5
+ int gamerun; /**< game run mode*/
+ int emuversion; /**< AGI version to emulate */
+ int agds; /**< enable AGDS mode */
+ int amiga; /**< enable Amiga mode */
+ int fullscreen; /**< use full screen mode if available */
+ int nosound; /**< disable sound */
+ int egapal; /**< use PC EGA palette */
+ int cgaemu; /**< use PC CGA emulation */
+#ifdef USE_HIRES
+ int hires; /**< use hi-res pictures */
+#endif
+ int soundemu; /**< sound emulation mode */
+#ifdef USE_MOUSE
+ int agimouse; /**< AGI Mouse 1.0 emulation */
+#endif
+};
+
+extern struct agi_options opt;
+extern uint8 *exec_name;
+
+extern volatile uint32 clock_ticks;
+extern volatile uint32 clock_count;
+extern volatile uint32 msg_box_secs2;
+
+#ifdef USE_CONSOLE
+extern struct agi_debug debug_;
+#endif
+
+#ifdef USE_MOUSE
+extern struct mouse mouse;
+#endif
+
+int console_keyhandler(int);
+int console_init(void);
+void console_cycle(void);
+void console_lock(void);
+void console_prompt(void);
+#define report printf
+
+enum GameId {
+ GID_AGI = 1
+};
+
+extern Common::RandomSource * rnd;
+extern const char *_savePath;
+
+class AgiEngine:public::Engine {
+ int _gameId;
+
+ void errorString(const char *buf_input, char *buf_output);
+
+protected:
+ int init();
+ int go();
+ void shutdown();
+ void initialize();
+
+
+public:
+ AgiEngine(OSystem * syst);
+ virtual ~ AgiEngine();
+ int getGameId() {
+ return _gameId;
+}};
+
+#define WIN_TO_PIC_X(x) ((x) / 2)
+#define WIN_TO_PIC_Y(y) ((y) < 8 ? 999 : (y) >= (8 + _HEIGHT) ? 999 : (y) - 8)
+
+/**
+ * AGI variables.
+ */
+enum {
+ V_cur_room = 0, /* 0 */
+ V_prev_room,
+ V_border_touch_ego,
+ V_score,
+ V_border_code,
+ V_border_touch_obj, /* 5 */
+ V_ego_dir,
+ V_max_score,
+ V_free_pages,
+ V_word_not_found,
+ V_time_delay, /* 10 */
+ V_seconds,
+ V_minutes,
+ V_hours,
+ V_days,
+ V_joystick_sensitivity, /* 15 */
+ V_ego_view_resource,
+ V_agi_err_code,
+ V_agi_err_code_info,
+ V_key,
+ V_computer, /* 20 */
+ V_window_reset,
+ V_soundgen,
+ V_volume,
+ V_max_input_chars,
+ V_sel_item, /* 25 */
+ V_monitor
+};
+
+/**
+ * AGI flags
+ */
+enum {
+ F_ego_water = 0, /* 0 */
+ F_ego_invisible,
+ F_entered_cli,
+ F_ego_touched_p2,
+ F_said_accepted_input,
+ F_new_room_exec, /* 5 */
+ F_restart_game,
+ F_script_blocked,
+ F_joy_sensitivity,
+ F_sound_on,
+ F_debugger_on, /* 10 */
+ F_logic_zero_firsttime,
+ F_restore_just_ran,
+ F_status_selects_items,
+ F_menus_work,
+ F_output_mode, /* 15 */
+ F_auto_restart
+};
+
+struct agi_event {
+ uint16 data;
+ uint8 occured;
+};
+
+struct agi_object {
+ int location;
+ char *name;
+};
+
+struct agi_word {
+ int id;
+ char *word;
+};
+
+struct agi_dir {
+ uint8 volume;
+ uint32 offset;
+ uint32 len;
+ uint32 clen;
+ uint8 flags;
+ /* 0 = not in mem, can be freed
+ * 1 = in mem, can be released
+ * 2 = not in mem, cant be released
+ * 3 = in mem, cant be released
+ * 0x40 = was compressed
+ */
+};
+
+struct agi_block {
+ int active;
+ int x1, y1;
+ int x2, y2;
+ uint8 *buffer; /* used for window background */
+};
+
+#define EGO_VIEW_TABLE 0
+#define HORIZON 36
+#define _WIDTH 160
+#define _HEIGHT 168
+
+/**
+ * AGI game structure.
+ * This structure contains all global data of an AGI game executed
+ * by the interpreter.
+ */
+struct agi_game {
+#define STATE_INIT 0x00
+#define STATE_LOADED 0x01
+#define STATE_RUNNING 0x02
+ int state; /**< state of the interpreter */
+
+ char name[8]; /**< lead in id (e.g. `GR' for goldrush) */
+ char id[8]; /**< game id */
+ uint32 crc; /**< game CRC */
+
+ /* game flags and variables */
+ uint8 flags[MAX_FLAGS];
+ /**< 256 1-bit flags */
+ uint8 vars[MAX_VARS];
+ /**< 256 variables */
+
+ /* internal variables */
+ int horizon; /**< horizon y coordinate */
+ int line_status; /**< line number to put status on */
+ int line_user_input;
+ /**< line to put user input on */
+ int line_min_print;
+ /**< num lines to print on */
+ int cursor_pos; /**< column where the input cursor is */
+ uint8 input_buffer[40];
+ /**< buffer for user input */
+ uint8 echo_buffer[40];
+ /**< buffer for echo.line */
+ int keypress;
+#define INPUT_NORMAL 0x01
+#define INPUT_GETSTRING 0x02
+#define INPUT_MENU 0x03
+#define INPUT_NONE 0x04
+ int input_mode; /**< keyboard input mode */
+ int input_enabled;
+ /**< keyboard input enabled */
+ int lognum; /**< current logic number */
+
+ /* internal flags */
+ int player_control;
+ /**< player is in control */
+ int quit_prog_now;
+ /**< quit now */
+ int status_line; /**< status line on/off */
+ int clock_enabled;
+ /**< clock is on/off */
+ int exit_all_logics;
+ /**< break cycle after new.room */
+ int picture_shown;
+ /**< show.pic has been issued */
+ int has_prompt; /**< input prompt has been printed */
+#define ID_AGDS 0x00000001
+#define ID_AMIGA 0x00000002
+ int game_flags; /**< agi options flags */
+
+ uint8 pri_table[_HEIGHT];
+ /**< priority table */
+
+ /* windows */
+ uint32 msg_box_ticks;
+ /**< timed message box tick counter */
+ struct agi_block block;
+ struct agi_block window;
+ int has_window;
+
+ /* graphics & text */
+ int gfx_mode;
+ char cursor_char;
+ unsigned int color_fg;
+ unsigned int color_bg;
+ uint8 *sbuf; /**< 160x168 AGI screen buffer */
+#ifdef USE_HIRES
+ uint8 *hires; /**< 320x168 hi-res buffer */
+#endif
+
+ /* player command line */
+ struct agi_word ego_words[MAX_WORDS];
+ int num_ego_words;
+
+ unsigned int num_objects;
+
+ struct agi_event ev_keyp[MAX_DIRS];
+ /**< keyboard keypress events */
+ char strings[MAX_STRINGS + 1][MAX_STRINGLEN];
+ /**< strings */
+
+ /* directory entries for resources */
+ struct agi_dir dir_logic[MAX_DIRS];
+ struct agi_dir dir_pic[MAX_DIRS];
+ struct agi_dir dir_view[MAX_DIRS];
+ struct agi_dir dir_sound[MAX_DIRS];
+
+ /* resources */
+ struct agi_picture pictures[MAX_DIRS];
+ /**< AGI picture resources */
+ struct agi_logic logics[MAX_DIRS];
+ /**< AGI logic resources */
+ struct agi_view views[MAX_DIRS]; /**< AGI view resources */
+ struct agi_sound sounds[MAX_DIRS];
+ /**< AGI sound resources */
+
+ /* view table */
+ struct vt_entry view_table[MAX_VIEWTABLE];
+
+ int32 ver; /**< detected game version */
+
+ int simple_save; /**< select simple savegames */
+};
+
+/**
+ *
+ */
+struct agi_loader {
+ int version;
+ int int_version;
+ int (*init) (void);
+ int (*deinit) (void);
+ int (*detect_game) ();
+ int (*load_resource) (int, int);
+ int (*unload_resource) (int, int);
+ int (*load_objects) (char *);
+ int (*load_words) (char *);
+};
+
+extern struct agi_game game;
+
+int agi_init(void);
+int agi_deinit(void);
+int agi_version(void);
+int agi_get_release(void);
+void agi_set_release(int);
+int agi_detect_game();
+int agi_load_resource(int, int);
+int agi_unload_resource(int, int);
+void agi_unload_resources(void);
+
+/* words */
+int show_words(void);
+int load_words(char *);
+void unload_words(void);
+int find_word(char *word, int *flen);
+void dictionary_words(char *);
+
+/* objects */
+int show_objects(void);
+int load_objects(char *fname);
+int alloc_objects(int);
+void unload_objects(void);
+char *object_name(unsigned int);
+int object_get_location(unsigned int);
+void object_set_location(unsigned int, int);
+
+void new_input_mode(int);
+void old_input_mode(void);
+
+int run_logic(int);
+
+void agi_timer_low();
+int agi_get_keypress_low();
+int agi_is_keypress_low();
+
+} // End of namespace Agi
+
+#endif /* __AGI_H */
diff --git a/engines/agi/agi_v2.cpp b/engines/agi/agi_v2.cpp
new file mode 100644
index 0000000000..deb849c200
--- /dev/null
+++ b/engines/agi/agi_v2.cpp
@@ -0,0 +1,309 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+static int agi_v2_init(void);
+static int agi_v2_deinit(void);
+static int agi_v2_detect_game();
+static int agi_v2_load_resource(int, int);
+static int agi_v2_unload_resource(int, int);
+static int agi_v2_load_objects(char *);
+static int agi_v2_load_words(char *);
+
+struct agi_loader agi_v2 = {
+ 2,
+ 0,
+ agi_v2_init,
+ agi_v2_deinit,
+ agi_v2_detect_game,
+ agi_v2_load_resource,
+ agi_v2_unload_resource,
+ agi_v2_load_objects,
+ agi_v2_load_words
+};
+
+static int agi_v2_detect_game() {
+ if (!Common::File::exists(LOGDIR) ||
+ !Common::File::exists(PICDIR) ||
+ !Common::File::exists(SNDDIR) ||
+ !Common::File::exists(VIEWDIR))
+ return err_InvalidAGIFile;
+
+ agi_v2.int_version = 0x2917; /* setup for 2.917 */
+ return v2id_game();
+}
+
+static int agi_v2_load_dir(struct agi_dir *agid, char *fname) {
+ Common::File fp;
+ uint8 *mem;
+ uint32 flen;
+ unsigned int i;
+ char *path;
+
+ path = fname;
+ report("Loading directory: %s\n", path);
+
+ if ((!fp.open(path))) {
+ return err_BadFileOpen;
+ }
+
+ fp.seek(0, SEEK_END);
+ flen = fp.pos();
+ fp.seek(0, SEEK_SET);
+
+ if ((mem = (uint8 *) malloc(flen + 32)) == NULL) {
+ fp.close();
+ return err_NotEnoughMemory;
+ }
+
+ fp.read(mem, flen);
+
+ /* set all directory resources to gone */
+ for (i = 0; i < MAX_DIRS; i++) {
+ agid[i].volume = 0xff;
+ agid[i].offset = _EMPTY;
+ }
+
+ /* build directory entries */
+ for (i = 0; i < flen; i += 3) {
+ agid[i / 3].volume = *(mem + i) >> 4;
+ agid[i / 3].offset = READ_BE_UINT24(mem + i) & (uint32) _EMPTY;
+ debugC(3, kDebugLevelResources, "%d: volume %d, offset 0x%05x", i / 3, agid[i / 3].volume, agid[i / 3].offset);
+ }
+
+ free(mem);
+ fp.close();
+
+ return err_OK;
+}
+
+static int agi_v2_init() {
+ int ec = err_OK;
+
+ /* load directory files */
+ ec = agi_v2_load_dir(game.dir_logic, LOGDIR);
+ if (ec == err_OK)
+ ec = agi_v2_load_dir(game.dir_pic, PICDIR);
+ if (ec == err_OK)
+ ec = agi_v2_load_dir(game.dir_view, VIEWDIR);
+ if (ec == err_OK)
+ ec = agi_v2_load_dir(game.dir_sound, SNDDIR);
+
+ return ec;
+}
+
+static int agi_v2_deinit() {
+ int ec = err_OK;
+
+#if 0
+ /* unload words */
+ agi_v2_unload_words();
+
+ /* unload objects */
+ agi_v2_unload_objects();
+#endif
+
+ return ec;
+}
+
+static int agi_v2_unload_resource(int t, int n) {
+ debugC(3, kDebugLevelResources, "unload resource");
+
+ switch (t) {
+ case rLOGIC:
+ unload_logic(n);
+ break;
+ case rPICTURE:
+ unload_picture(n);
+ break;
+ case rVIEW:
+ unload_view(n);
+ break;
+ case rSOUND:
+ unload_sound(n);
+ break;
+ }
+
+ return err_OK;
+}
+
+/*
+ * This function does noting but load a raw resource into memory,
+ * if further decoding is required, it must be done by another
+ * routine. NULL is returned if unsucsessfull.
+ */
+static uint8 *agi_v2_load_vol_res(struct agi_dir *agid) {
+ uint8 *data = NULL;
+ char x[MAX_PATH], *path;
+ Common::File fp;
+ unsigned int sig;
+
+ sprintf(x, "vol.%i", agid->volume);
+ path = x;
+ debugC(3, kDebugLevelResources, "Vol res: path = %s", path);
+
+ if (agid->offset != _EMPTY && fp.open(path)) {
+ debugC(3, kDebugLevelResources, "loading resource at offset %d", agid->offset);
+ fp.seek(agid->offset, SEEK_SET);
+ fp.read(&x, 5);
+ if ((sig = READ_BE_UINT16((uint8 *) x)) == 0x1234) {
+ agid->len = READ_LE_UINT16((uint8 *) x + 3);
+ data = (uint8 *) calloc(1, agid->len + 32);
+ if (data != NULL) {
+ fp.read(data, agid->len);
+ } else {
+ abort();
+ }
+ } else {
+#if 0
+ /* FIXME: call some panic handler instead of
+ * deiniting directly
+ */
+ deinit_video_mode();
+#endif
+ report("Error: bad signature %04x\n", sig);
+ // fprintf (stderr, "ACK! BAD RESOURCE!!!\n");
+ return 0;
+ }
+ fp.close();
+ } else {
+ /* we have a bad volume resource */
+ /* set that resource to NA */
+ agid->offset = _EMPTY;
+ }
+
+ return data;
+}
+
+/*
+ * Loads a resource into memory, a raw resource is loaded in
+ * with above routine, then further decoded here.
+ */
+int agi_v2_load_resource(int t, int n) {
+ int ec = err_OK;
+ uint8 *data = NULL;
+
+ debugC(3, kDebugLevelResources, "(t = %d, n = %d)", t, n);
+ if (n > MAX_DIRS)
+ return err_BadResource;
+
+ switch (t) {
+ case rLOGIC:
+ if (~game.dir_logic[n].flags & RES_LOADED) {
+ debugC(3, kDebugLevelResources, "loading logic resource %d", n);
+ agi_v2.unload_resource(rLOGIC, n);
+
+ /* load raw resource into data */
+ data = agi_v2_load_vol_res(&game.dir_logic[n]);
+
+ game.logics[n].data = data;
+ ec = data ? decode_logic(n) : err_BadResource;
+
+ game.logics[n].sIP = 2;
+ }
+
+ /* if logic was cached, we get here */
+ /* reset code pointers incase it was cached */
+
+ game.logics[n].cIP = game.logics[n].sIP;
+ break;
+ case rPICTURE:
+ /* if picture is currently NOT loaded *OR* cacheing is off,
+ * unload the resource (caching == off) and reload it
+ */
+
+ debugC(3, kDebugLevelResources, "loading picture resource %d", n);
+ if (game.dir_pic[n].flags & RES_LOADED)
+ break;
+
+ /* if loaded but not cached, unload it */
+ /* if cached but not loaded, etc */
+ agi_v2.unload_resource(rPICTURE, n);
+ data = agi_v2_load_vol_res(&game.dir_pic[n]);
+
+ if (data != NULL) {
+ game.pictures[n].rdata = data;
+ game.dir_pic[n].flags |= RES_LOADED;
+ } else {
+ ec = err_BadResource;
+ }
+ break;
+ case rSOUND:
+ debugC(3, kDebugLevelResources, "loading sound resource %d", n);
+ if (game.dir_sound[n].flags & RES_LOADED)
+ break;
+
+ data = agi_v2_load_vol_res(&game.dir_sound[n]);
+
+ if (data != NULL) {
+ game.sounds[n].rdata = data;
+ game.dir_sound[n].flags |= RES_LOADED;
+ decode_sound(n);
+ } else {
+ ec = err_BadResource;
+ }
+ break;
+ case rVIEW:
+ /* Load a VIEW resource into memory...
+ * Since VIEWS alter the view table ALL the time
+ * can we cache the view? or must we reload it all
+ * the time?
+ */
+ if (game.dir_view[n].flags & RES_LOADED)
+ break;
+
+ debugC(3, kDebugLevelResources, "loading view resource %d", n);
+ agi_v2.unload_resource(rVIEW, n);
+ data = agi_v2_load_vol_res(&game.dir_view[n]);
+ if (data) {
+ game.views[n].rdata = data;
+ game.dir_view[n].flags |= RES_LOADED;
+ ec = decode_view(n);
+ } else {
+ ec = err_BadResource;
+ }
+ break;
+ default:
+ ec = err_BadResource;
+ break;
+ }
+
+ return ec;
+}
+
+static int agi_v2_load_objects(char *fname) {
+ return load_objects(fname);
+}
+
+static int agi_v2_load_words(char *fname) {
+ return load_words(fname);
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/agi_v3.cpp b/engines/agi/agi_v3.cpp
new file mode 100644
index 0000000000..5c99472b78
--- /dev/null
+++ b/engines/agi/agi_v3.cpp
@@ -0,0 +1,389 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/lzw.h"
+
+#include "backends/fs/fs.h"
+
+#include "common/config-manager.h"
+
+namespace Agi {
+
+static int agi_v3_init(void);
+static int agi_v3_deinit(void);
+static int agi_v3_detect_game();
+static int agi_v3_load_resource(int, int);
+static int agi_v3_unload_resource(int, int);
+static int agi_v3_load_objects(char *);
+static int agi_v3_load_words(char *);
+
+struct agi_loader agi_v3 = {
+ 3,
+ 0,
+ agi_v3_init,
+ agi_v3_deinit,
+ agi_v3_detect_game,
+ agi_v3_load_resource,
+ agi_v3_unload_resource,
+ agi_v3_load_objects,
+ agi_v3_load_words
+};
+
+int agi_v3_detect_game() {
+ int ec = err_Unk;
+ bool found = false;
+
+ FSList fslist;
+ FilesystemNode dir(ConfMan.get("path"));
+
+ if (!dir.listDir(fslist, FilesystemNode::kListFilesOnly)) {
+ warning("AgiEngine: invalid game path '%s'",
+ dir.path().c_str());
+ return err_InvalidAGIFile;
+ }
+
+ for (FSList::const_iterator file = fslist.begin();
+ file != fslist.end() && !found; ++file) {
+ Common::String f = file->displayName();
+ f.toLowercase();
+
+ if (f.hasSuffix("vol.0")) {
+ strncpy(game.name, f.c_str(), f.size() > 5 ? f.size() - 5 : f.size());
+ debugC(3, kDebugLevelMain, "game.name = %s", game.name);
+ agi_v3.int_version = 0x3149; // setup for 3.002.149
+ ec = v3id_game();
+
+ found = true;
+ }
+ }
+
+ if (!found) {
+ debugC(3, kDebugLevelMain, "not found");
+ ec = err_InvalidAGIFile;
+ }
+
+ return ec;
+}
+
+static int agi_v3_load_dir(struct agi_dir *agid, Common::File *fp,
+ uint32 offs, uint32 len) {
+ int ec = err_OK;
+ uint8 *mem;
+ unsigned int i;
+
+ fp->seek(offs, SEEK_SET);
+ if ((mem = (uint8 *) malloc(len + 32)) != NULL) {
+ fp->read(mem, len);
+
+ /* set all directory resources to gone */
+ for (i = 0; i < MAX_DIRS; i++) {
+ agid[i].volume = 0xff;
+ agid[i].offset = _EMPTY;
+ }
+
+ /* build directory entries */
+ for (i = 0; i < len; i += 3) {
+ agid[i / 3].volume = *(mem + i) >> 4;
+ agid[i / 3].offset = READ_BE_UINT24(mem + i) & (uint32) _EMPTY;
+ }
+
+ free(mem);
+ } else {
+ ec = err_NotEnoughMemory;
+ }
+
+ return ec;
+}
+
+struct agi3vol {
+ uint32 sddr;
+ uint32 len;
+};
+
+int agi_v3_init(void) {
+ int ec = err_OK;
+ struct agi3vol agi_vol3[4];
+ int i;
+ uint16 xd[4];
+ Common::File fp;
+ Common::String path;
+
+ path = Common::String(game.name) + DIR_;
+
+ if (!fp.open(path)) {
+ printf("Failed to open \"%s\"\n", path.c_str());
+ return err_BadFileOpen;
+ }
+ /* build offset table for v3 directory format */
+ fp.read(&xd, 8);
+ fp.seek(0, SEEK_END);
+
+ for (i = 0; i < 4; i++)
+ agi_vol3[i].sddr = READ_LE_UINT16((uint8 *) & xd[i]);
+
+ agi_vol3[0].len = agi_vol3[1].sddr - agi_vol3[0].sddr;
+ agi_vol3[1].len = agi_vol3[2].sddr - agi_vol3[1].sddr;
+ agi_vol3[2].len = agi_vol3[3].sddr - agi_vol3[2].sddr;
+ agi_vol3[3].len = fp.pos() - agi_vol3[3].sddr;
+
+ if (agi_vol3[3].len > 256 * 3)
+ agi_vol3[3].len = 256 * 3;
+
+ fp.seek(0, SEEK_SET);
+
+ /* read in directory files */
+ ec = agi_v3_load_dir(game.dir_logic, &fp, agi_vol3[0].sddr,
+ agi_vol3[0].len);
+
+ if (ec == err_OK) {
+ ec = agi_v3_load_dir(game.dir_pic, &fp, agi_vol3[1].sddr, agi_vol3[1].len);
+ }
+
+ if (ec == err_OK) {
+ ec = agi_v3_load_dir(game.dir_view, &fp, agi_vol3[2].sddr, agi_vol3[2].len);
+ }
+
+ if (ec == err_OK) {
+ ec = agi_v3_load_dir(game.dir_sound, &fp, agi_vol3[3].sddr, agi_vol3[3].len);
+ }
+
+ return ec;
+}
+
+int agi_v3_deinit() {
+ int ec = err_OK;
+
+#if 0
+ /* unload words */
+ agi_v3_unload_words();
+
+ /* unload objects */
+ agi_v3_unload_objects();
+#endif
+
+ return ec;
+}
+
+int agi_v3_unload_resource(int t, int n) {
+ switch (t) {
+ case rLOGIC:
+ unload_logic(n);
+ break;
+ case rPICTURE:
+ unload_picture(n);
+ break;
+ case rVIEW:
+ unload_view(n);
+ break;
+ case rSOUND:
+ unload_sound(n);
+ break;
+ }
+
+ return err_OK;
+}
+
+/*
+ * This function does noting but load a raw resource into memory.
+ * If further decoding is required, it must be done by another
+ * routine.
+ *
+ * NULL is returned if unsucsessful.
+ */
+uint8 *agi_v3_load_vol_res(struct agi_dir *agid) {
+ char x[MAX_PATH];
+ uint8 *data = NULL, *comp_buffer;
+ Common::File fp;
+ Common::String path;
+
+ debugC(3, kDebugLevelResources, "(%p)", agid);
+ sprintf(x, "vol.%i", agid->volume);
+ path = Common::String(game.name) + x;
+
+ if (agid->offset != _EMPTY && fp.open(path)) {
+ fp.seek(agid->offset, SEEK_SET);
+ fp.read(&x, 7);
+
+ if (READ_BE_UINT16((uint8 *) x) != 0x1234) {
+#if 0
+ /* FIXME */
+ deinit_video_mode();
+#endif
+ debugC(3, kDebugLevelResources, "path = %s", path.c_str());
+ debugC(3, kDebugLevelResources, "offset = %d", agid->offset);
+ debugC(3, kDebugLevelResources, "x = %x %x", x[0], x[1]);
+ error("ACK! BAD RESOURCE");
+
+ g_system->quit();
+ }
+
+ agid->len = READ_LE_UINT16((uint8 *) x + 3); /* uncompressed size */
+ agid->clen = READ_LE_UINT16((uint8 *) x + 5); /* compressed len */
+
+ comp_buffer = (uint8 *)calloc(1, agid->clen + 32);
+ fp.read(comp_buffer, agid->clen);
+
+ if (x[2] & 0x80 || agid->len == agid->clen) {
+ /* do not decompress */
+ data = comp_buffer;
+
+#if 0
+ /* CM: added to avoid problems in
+ * convert_v2_v3_pic() when clen > len
+ * e.g. Sierra demo 4, first picture
+ * (Tue Mar 16 13:13:43 EST 1999)
+ */
+ agid->len = agid->clen;
+
+ /* Now removed to fix Gold Rush! in demo4 */
+#endif
+ } else {
+ /* it is compressed */
+ data = (uint8 *)calloc(1, agid->len + 32);
+ LZW_expand(comp_buffer, data, agid->len);
+ free(comp_buffer);
+ agid->flags |= RES_COMPRESSED;
+ }
+
+ fp.close();
+ } else {
+ /* we have a bad volume resource */
+ /* set that resource to NA */
+ agid->offset = _EMPTY;
+ }
+
+ return data;
+}
+
+/*
+ * Loads a resource into memory, a raw resource is loaded in
+ * with above routine, then further decoded here.
+ */
+int agi_v3_load_resource(int t, int n) {
+ int ec = err_OK;
+ uint8 *data = NULL;
+
+ if (n > MAX_DIRS)
+ return err_BadResource;
+
+ switch (t) {
+ case rLOGIC:
+ /* load resource into memory, decrypt messages at the end
+ * and build the message list (if logic is in memory)
+ */
+ if (~game.dir_logic[n].flags & RES_LOADED) {
+ /* if logic is already in memory, unload it */
+ agi_v3.unload_resource(rLOGIC, n);
+
+ /* load raw resource into data */
+ data = agi_v3_load_vol_res(&game.dir_logic[n]);
+ game.logics[n].data = data;
+
+ /* uncompressed logic files need to be decrypted */
+ if (data != NULL) {
+ /* resloaded flag gets set by decode logic */
+ /* needed to build string table */
+ ec = decode_logic(n);
+ game.logics[n].sIP = 2;
+ } else {
+ ec = err_BadResource;
+ }
+
+ /*logics[n].sIP=2; *//* saved IP = 2 */
+ /*logics[n].cIP=2; *//* current IP = 2 */
+
+ game.logics[n].cIP = game.logics[n].sIP;
+ }
+
+ /* if logic was cached, we get here */
+ /* reset code pointers incase it was cached */
+
+ game.logics[n].cIP = game.logics[n].sIP;
+ break;
+ case rPICTURE:
+ /* if picture is currently NOT loaded *OR* cacheing is off,
+ * unload the resource (caching==off) and reload it
+ */
+ if (~game.dir_pic[n].flags & RES_LOADED) {
+ agi_v3.unload_resource(rPICTURE, n);
+ data = agi_v3_load_vol_res(&game.dir_pic[n]);
+ if (data != NULL) {
+ data = convert_v3_pic(data, game.dir_pic[n].len);
+ game.pictures[n].rdata = data;
+ game.dir_pic[n].flags |= RES_LOADED;
+ } else {
+ ec = err_BadResource;
+ }
+ }
+ break;
+ case rSOUND:
+ if (game.dir_sound[n].flags & RES_LOADED)
+ break;
+
+ if ((data = agi_v3_load_vol_res(&game.dir_sound[n])) != NULL) {
+ game.sounds[n].rdata = data;
+ game.dir_sound[n].flags |= RES_LOADED;
+ decode_sound(n);
+ } else {
+ ec = err_BadResource;
+ }
+ break;
+ case rVIEW:
+ /* Load a VIEW resource into memory...
+ * Since VIEWS alter the view table ALL the time can we
+ * cache the view? or must we reload it all the time?
+ */
+ /* load a raw view from a VOL file into data */
+ if (game.dir_view[n].flags & RES_LOADED)
+ break;
+
+ agi_v3.unload_resource(rVIEW, n);
+ if ((data = agi_v3_load_vol_res(&game.dir_view[n])) != NULL) {
+ game.views[n].rdata = data;
+ game.dir_view[n].flags |= RES_LOADED;
+ ec = decode_view(n);
+ } else {
+ ec = err_BadResource;
+ }
+ break;
+ default:
+ ec = err_BadResource;
+ break;
+ }
+
+ return ec;
+}
+
+static int agi_v3_load_objects(char *fname) {
+ return load_objects(fname);
+}
+
+static int agi_v3_load_words(char *fname) {
+ return load_words(fname);
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/checks.cpp b/engines/agi/checks.cpp
new file mode 100644
index 0000000000..1042ef1579
--- /dev/null
+++ b/engines/agi/checks.cpp
@@ -0,0 +1,320 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "agi/agi.h"
+
+namespace Agi {
+
+static int check_position(struct vt_entry *v) {
+ debugC(4, kDebugLevelSprites, "check position @ %d, %d", v->x_pos, v->y_pos);
+
+ if (v->x_pos < 0 ||
+ v->x_pos + v->x_size > _WIDTH ||
+ v->y_pos - v->y_size + 1 < 0 ||
+ v->y_pos >= _HEIGHT ||
+ ((~v->flags & IGNORE_HORIZON) && v->y_pos <= game.horizon)) {
+ debugC(4, kDebugLevelSprites, "check position failed: x=%d, y=%d, h=%d, w=%d",
+ v->x_pos, v->y_pos, v->x_size, v->y_size);
+ return 0;
+ }
+
+ /* MH1 needs this, but it breaks LSL1 */
+ if (agi_get_release() >= 0x3000) {
+ if (v->y_pos < v->y_size)
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Check if there's another object on the way
+ */
+static int check_collision(struct vt_entry *v) {
+ struct vt_entry *u;
+
+ if (v->flags & IGNORE_OBJECTS)
+ return 0;
+
+ for (u = game.view_table; u < &game.view_table[MAX_VIEWTABLE]; u++) {
+ if ((u->flags & (ANIMATED | DRAWN)) != (ANIMATED | DRAWN))
+ continue;
+
+ if (u->flags & IGNORE_OBJECTS)
+ continue;
+
+ /* Same object, check next */
+ if (v->entry == u->entry)
+ continue;
+
+ /* No horizontal overlap, check next */
+ if (v->x_pos + v->x_size < u->x_pos ||
+ v->x_pos > u->x_pos + u->x_size)
+ continue;
+
+ /* Same y, return error! */
+ if (v->y_pos == u->y_pos)
+ goto return_1;
+
+ /* Crossed the baseline, return error! */
+ if ((v->y_pos > u->y_pos && v->y_pos2 < u->y_pos2) ||
+ (v->y_pos < u->y_pos && v->y_pos2 > u->y_pos2)) {
+ goto return_1;
+ }
+ }
+
+ return 0;
+
+ return_1:
+ debugC(4, kDebugLevelSprites, "check returns 1 (object %d)", v->entry);
+ return 1;
+}
+
+static int check_priority(struct vt_entry *v) {
+ int i, trigger, water, pass, pri;
+ uint8 *p0;
+
+ if (~v->flags & FIXED_PRIORITY) {
+ /* Priority bands */
+ v->priority = game.pri_table[v->y_pos];
+ }
+
+ trigger = 0;
+ water = 0;
+ pass = 1;
+
+ if (v->priority == 0x0f)
+ goto _check_ego;
+
+ water = 1;
+
+ p0 = &game.sbuf[v->x_pos + v->y_pos * _WIDTH];
+
+ for (i = 0; i < v->x_size; i++, p0++) {
+ pri = *p0 >> 4;
+
+ if (pri == 0) { /* unconditional black. no go at all! */
+ pass = 0;
+ break;
+ }
+
+ if (pri == 3) /* water surface */
+ continue;
+
+ water = 0;
+
+ if (pri == 1) { /* conditional blue */
+ if (v->flags & IGNORE_BLOCKS)
+ continue;
+
+ debugC(4, kDebugLevelSprites, "Blocks observed!");
+ pass = 0;
+ break;
+ }
+
+ if (pri == 2) { /* trigger */
+ debugC(4, kDebugLevelSprites, "stepped on trigger");
+#ifdef USE_CONSOLE
+ if (!debug_.ignoretriggers)
+#endif
+ trigger = 1;
+ }
+ }
+
+ if (pass) {
+ if (!water && v->flags & ON_WATER)
+ pass = 0;
+ if (water && v->flags & ON_LAND)
+ pass = 0;
+ }
+
+ _check_ego:
+ if (v->entry == 0) {
+ setflag(F_ego_touched_p2, trigger ? true : false);
+ setflag(F_ego_water, water ? true : false);
+ }
+
+ return pass;
+}
+
+/*
+ * Public functions
+ */
+
+/**
+ * Update position of objects
+ * This function updates the position of all animated, updating view
+ * table entries according to its motion type, step size, etc. The
+ * new position must be valid according to the sprite positioning
+ * rules, otherwise the previous position will be kept.
+ */
+void update_position() {
+ struct vt_entry *v;
+ int x, y, old_x, old_y, border;
+
+ game.vars[V_border_code] = 0;
+ game.vars[V_border_touch_ego] = 0;
+ game.vars[V_border_touch_obj] = 0;
+
+ for (v = game.view_table; v < &game.view_table[MAX_VIEWTABLE]; v++) {
+ if ((v->flags & (ANIMATED | UPDATE | DRAWN)) != (ANIMATED | UPDATE | DRAWN)) {
+ continue;
+ }
+
+ if (v->step_time_count != 0) {
+ if (--v->step_time_count != 0)
+ continue;
+ }
+
+ v->step_time_count = v->step_time;
+
+ x = old_x = v->x_pos;
+ y = old_y = v->y_pos;
+
+ /* If object has moved, update its position */
+ if (~v->flags & UPDATE_POS) {
+ int dx[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
+ int dy[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
+ x += v->step_size * dx[v->direction];
+ y += v->step_size * dy[v->direction];
+ }
+
+ /* Now check if it touched the borders */
+ border = 0;
+
+ /* Check left/right borders */
+ if (x < 0) {
+ x = 0;
+ border = 4;
+ } else if (x <= 0 && agi_get_release() == 0x3086) { /* KQ4 */
+ x = 0; /* See bug #590462 */
+ border = 4;
+ } else if (v->entry == 0 && x == 0 && v->flags & ADJ_EGO_XY) {
+ /* Extra test to walk west clicking the mouse */
+ x = 0;
+ border = 4;
+ } else if (x + v->x_size > _WIDTH) {
+ x = _WIDTH - v->x_size;
+ border = 2;
+ }
+
+ /* Check top/bottom borders. */
+ if (y - v->y_size + 1 < 0) {
+ y = v->y_size - 1;
+ border = 1;
+ } else if (y > _HEIGHT - 1) {
+ y = _HEIGHT - 1;
+ border = 3;
+ } else if ((~v->flags & IGNORE_HORIZON) && y <= game.horizon) {
+ debugC(4, kDebugLevelSprites, "y = %d, horizon = %d", y, game.horizon);
+ y = game.horizon + 1;
+ border = 1;
+ }
+
+ /* Test new position. rollback if test fails */
+ v->x_pos = x;
+ v->y_pos = y;
+ if (check_collision(v) || !check_priority(v)) {
+ v->x_pos = old_x;
+ v->y_pos = old_y;
+ border = 0;
+ fix_position(v->entry);
+ }
+
+ if (border != 0) {
+ if (v == game.view_table) {
+ game.vars[V_border_touch_ego] = border;
+ } else {
+ game.vars[V_border_code] = v->entry;
+ game.vars[V_border_touch_obj] = border;
+ }
+ if (v->motion == MOTION_MOVE_OBJ) {
+ in_destination(v);
+ }
+ }
+
+ v->flags &= ~UPDATE_POS;
+ }
+}
+
+/**
+ * Adjust position of a sprite
+ * This function adjusts the position of a sprite moving it until
+ * certain criteria is matched. According to priority and control line
+ * data, a sprite may not always appear at the location we specified.
+ * This behaviour is also known as the "Budin-Sonneveld effect".
+ *
+ * @param n view table entry number
+ */
+void fix_position(int n) {
+ struct vt_entry *v = &game.view_table[n];
+ int count, dir, size;
+
+ debugC(4, kDebugLevelSprites, "adjusting view table entry #%d (%d,%d)", n, v->x_pos, v->y_pos);
+
+ /* test horizon */
+ if ((~v->flags & IGNORE_HORIZON) && v->y_pos <= game.horizon)
+ v->y_pos = game.horizon + 1;
+
+ dir = 0;
+ count = size = 1;
+
+ while (!check_position(v) || check_collision(v) || !check_priority(v)) {
+ switch (dir) {
+ case 0: /* west */
+ v->x_pos--;
+ if (--count)
+ continue;
+ dir = 1;
+ break;
+ case 1: /* south */
+ v->y_pos++;
+ if (--count)
+ continue;
+ dir = 2;
+ size++;
+ break;
+ case 2: /* east */
+ v->x_pos++;
+ if (--count)
+ continue;
+ dir = 3;
+ break;
+ case 3: /* north */
+ v->y_pos--;
+ if (--count)
+ continue;
+ dir = 0;
+ size++;
+ break;
+ }
+
+ count = size;
+ }
+
+ debugC(4, kDebugLevelSprites, "view table entry #%d position adjusted to (%d,%d)", n, v->x_pos, v->y_pos);
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/console.cpp b/engines/agi/console.cpp
new file mode 100644
index 0000000000..cbfa8b2b08
--- /dev/null
+++ b/engines/agi/console.cpp
@@ -0,0 +1,873 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2002 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/graphics.h"
+#include "agi/sprite.h"
+#include "agi/text.h"
+#include "agi/keyboard.h"
+#include "agi/opcodes.h"
+#include "agi/console.h"
+
+namespace Agi {
+
+#ifdef USE_CONSOLE
+
+/*
+ * The main interpreter engine has not been designed to have a console, and a few
+ * kludges were needed to make the console work. First of all, the main
+ * interpreter loop works at cycle level, not instruction level. To keep
+ * the illusion that the console is threaded, a console updating function
+ * is called from loops inside instructions such as get.string().
+ */
+
+#define CONSOLE_LINES_ONSCREEN 20
+#define CONSOLE_PROMPT "$"
+#define CONSOLE_CURSOR_HOLLOW 1
+#define CONSOLE_CURSOR_SOLID 2
+#define CONSOLE_CURSOR_EMPTY 3
+#define CONSOLE_COLOR 14
+#define CONSOLE_SCROLLUP_KEY KEY_PGUP
+#define CONSOLE_SCROLLDN_KEY KEY_PGDN
+#define CONSOLE_START_KEY KEY_HOME
+#define CONSOLE_END_KEY KEY_END
+#define CONSOLE_INPUT_SIZE 39
+
+struct console_command {
+ char cmd[8];
+ char dsc[40];
+ void (*handler) (void);
+};
+
+struct agi_console console;
+struct agi_debug debug_;
+
+/* This used to be a linked list, but we reduce total memory footprint
+ * by implementing it as a statically allocated array.
+ */
+#define MAX_CCMD 16
+static struct console_command ccmd_list[MAX_CCMD];
+static int num_ccmd = 0;
+
+static uint8 has_console = 0;
+static uint8 console_input = 0;
+
+static char *_p0, *_p1, *_p2, *_p3, *_p4, *_p5; /* FIXME: array */
+static char _p[80];
+static int _pn;
+
+/*
+ * Console line management
+ */
+
+static int first_line = 0;
+
+static void add_console_line(char *s) {
+ int last_line;
+
+ last_line = (CONSOLE_LINES_BUFFER - 1 + first_line) % CONSOLE_LINES_BUFFER;
+
+ strncpy(console.line[last_line], s, CONSOLE_LINE_SIZE);
+ first_line %= CONSOLE_LINES_BUFFER;
+}
+
+static char *get_console_line(int n) {
+ return console.line[(n + first_line) % CONSOLE_LINES_BUFFER];
+}
+
+static char *get_last_console_line() {
+ int last_line = (CONSOLE_LINES_BUFFER - 1 + first_line) % CONSOLE_LINES_BUFFER;
+
+ return console.line[last_line];
+}
+
+/*
+ * Console command parsing
+ * 'o' commands added by Shaun Jackman <sjackman@shaw.ca>, 11 Apr 2002
+ */
+
+static int console_parse(char *b) {
+ struct console_command *d;
+ int i;
+
+ for (; *b && *b == ' '; b++) {
+ } /* eat spaces */
+ for (; *b && b[strlen(b) - 1] == ' '; b[strlen(b) - 1] = 0) {
+ }
+ if (!*b)
+ return 0;
+
+ /* get or set flag/var/obj values */
+ if ((b[0] == 'f' || b[0] == 'v' || b[0] == 'o') && isdigit(b[1])) {
+ char *e;
+ int f = (int)strtoul(&b[1], &e, 10);
+ if (*e == 0) {
+ if (f >= 0 && f <= 255) {
+ switch (b[0]) {
+ case 'f':
+ report(getflag(f) ? "true\n" : "false\n");
+ break;
+ case 'v':
+ report("%3d\n", getvar(f));
+ break;
+ case 'o':
+ report("%3d]%-24s(%3d)\n", f, object_name(f), object_get_location(f));
+ break;
+ }
+ return 0;
+ }
+ return -1;
+ } else if (*e == '=') {
+ int n = (int)strtoul(e + 1, NULL, 0);
+ switch (b[0]) {
+ case 'f':
+ setflag(f, !!n);
+ break;
+ case 'v':
+ setvar(f, n);
+ break;
+ case 'o':
+ object_set_location(f, n);
+ break;
+ }
+ return 0;
+ }
+ }
+
+ /* tokenize the input line */
+ if (strchr(b, ' '))
+ strcpy(_p, strchr(b, ' ') + 1);
+ _p0 = strtok(b, " ");
+ _p1 = strtok(NULL, " ");
+ _p2 = strtok(NULL, " ");
+ _p3 = strtok(NULL, " ");
+ _p4 = strtok(NULL, " ");
+ _p5 = strtok(NULL, " ");
+
+ /* set number of parameters. ugh, must rewrite this later */
+ _pn = 5;
+ if (!_p5)
+ _pn = 4;
+ if (!_p4)
+ _pn = 3;
+ if (!_p3)
+ _pn = 2;
+ if (!_p2)
+ _pn = 1;
+ if (!_p1)
+ _pn = 0;
+
+ debugC(5, kDebugLevelMain, "number of parameters: %d", _pn);
+
+ for (i = 0; i < num_ccmd; i++) {
+ d = &ccmd_list[i];
+ if (!strcmp(_p0, d->cmd) && d->handler) {
+ d->handler();
+ return 0;
+ }
+ }
+
+ for (i = 0; logic_names_cmd[i].name; i++) {
+ if (!strcmp(_p0, logic_names_cmd[i].name)) {
+ uint8 p[16];
+ if (_pn != logic_names_cmd[i].num_args) {
+ report("AGI command wants %d arguments\n", logic_names_cmd[i].num_args);
+ return 0;
+ }
+ p[0] = _p1 ? (char)strtoul(_p1, NULL, 0) : 0;
+ p[1] = _p2 ? (char)strtoul(_p2, NULL, 0) : 0;
+ p[2] = _p3 ? (char)strtoul(_p3, NULL, 0) : 0;
+ p[3] = _p4 ? (char)strtoul(_p4, NULL, 0) : 0;
+ p[4] = _p5 ? (char)strtoul(_p5, NULL, 0) : 0;
+
+ debugC(5, kDebugLevelMain, "ccmd: %s %d %d %d", logic_names_cmd[i].name, p[0], p[1], p[2]);
+
+ execute_agi_command(i, p);
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Console commands
+ */
+
+static void ccmd_help() {
+ int i;
+
+ if (!_p1) {
+ report("Command Description\n");
+ report("------- --------------------------------\n");
+ for (i = 0; i < num_ccmd; i++)
+ report("%-8s%s\n", ccmd_list[i].cmd, ccmd_list[i].dsc);
+ return;
+ }
+
+ for (i = 0; i < num_ccmd; i++) {
+ if (!strcmp(ccmd_list[i].cmd, _p1) && ccmd_list[i].handler) {
+ report("%s\n", ccmd_list[i].dsc);
+ return;
+ }
+ }
+
+ report("Unknown command or no help available\n");
+
+ return;
+}
+
+static void ccmd_ver() {
+ report(VERSION "\n");
+ return;
+}
+
+static void ccmd_crc() {
+ char name[80];
+ report("0x%05x\n", game.crc);
+ if (match_crc(game.crc, name, 80))
+ report("%s\n", name);
+ else
+ report("Unknown game\n");
+ return;
+}
+
+static void ccmd_quit() {
+ deinit_video();
+ /* deinit_machine (); */
+ exit(0);
+}
+
+#if 0
+static void ccmd_exec() {
+ if (game.state < STATE_LOADED) {
+ report("No game loaded.\n");
+ return;
+ }
+
+ if (game.state >= STATE_RUNNING) {
+ report("Game already running.\n");
+ return;
+ }
+
+ report("Executing AGI game.\n");
+ game.state = STATE_RUNNING;
+}
+#endif
+
+#ifdef USE_HIRES
+
+static void ccmd_hires() {
+ if (_pn != 1 || (strcmp(_p1, "on") && strcmp(_p1, "off"))) {
+ report("Usage: hires on|off\n");
+ return;
+ }
+
+ opt.hires = !strcmp(_p1, "on");
+ erase_both();
+ show_pic();
+ blit_both();
+ return;
+}
+
+#endif
+
+static void ccmd_agiver() {
+ int ver, maj, min;
+
+ ver = agi_get_release();
+ maj = (ver >> 12) & 0xf;
+ min = ver & 0xfff;
+
+ report(maj <= 2 ? "%x.%03x\n" : "%x.002.%03x\n", maj, min);
+ return;
+}
+
+static void ccmd_flags() {
+ int i, j;
+
+ report(" ");
+ for (j = 0; j < 10; j++)
+ report("%d ", j);
+ report("\n");
+
+ for (i = 0; i < 255;) {
+ report("%3d ", i);
+ for (j = 0; j < 10; j++, i++) {
+ report("%c ", getflag(i) ? 'T' : 'F');
+ }
+ report("\n");
+ }
+ return;
+}
+
+static void ccmd_vars() {
+ int i, j;
+
+ for (i = 0; i < 255;) {
+ for (j = 0; j < 5; j++, i++) {
+ report("%03d:%3d ", i, getvar(i));
+ }
+ report("\n");
+ }
+ return;
+}
+
+#if 0
+
+static void ccmd_say() {
+ setflag(F_entered_cli, true);
+ dictionary_words(_p);
+}
+
+static void ccmd_inv() {
+ unsigned int i, j;
+
+ for (j = i = 0; i < game.num_objects; i++) {
+ if (object_get_location(i) == EGO_OWNED) {
+ report("%3d]%-16.16s", i, object_name(i));
+ if (j % 2)
+ report("\n");
+ j++;
+ }
+ }
+ if (j == 0) {
+ report("none\n");
+ return;
+ }
+ if (j % 2)
+ report("\n");
+}
+
+#endif
+
+static void ccmd_objs() {
+ unsigned int i;
+
+ for (i = 0; i < game.num_objects; i++) {
+ report("%3d]%-24s(%3d)\n", i, object_name(i), object_get_location(i));
+ }
+ return;
+}
+
+static void ccmd_opcode() {
+ if (_pn != 1 || (strcmp(_p1, "on") && strcmp(_p1, "off"))) {
+ report("Usage: opcode on|off\n");
+ return;
+ }
+
+ debug_.opcodes = !strcmp(_p1, "on");
+ return;
+}
+
+static void ccmd_logic0() {
+ if (_pn != 1 || (strcmp(_p1, "on") && strcmp(_p1, "off"))) {
+ report("Usage: logic0 on|off\n");
+ return;
+ }
+
+ debug_.logic0 = !strcmp(_p1, "on");
+ return;
+}
+
+static void ccmd_trigger() {
+ if (_pn != 1 || (strcmp(_p1, "on") && strcmp(_p1, "off"))) {
+ report("Usage: trigger on|off\n");
+ return;
+ }
+
+ debug_.ignoretriggers = strcmp(_p1, "on");
+ return;
+}
+
+static void ccmd_step() {
+ debug_.enabled = 1;
+
+ if (_pn == 0) {
+ debug_.steps = 1;
+ return;
+ }
+
+ debug_.steps = strtoul(_p1, NULL, 0);
+ return;
+}
+
+static void ccmd_debug() {
+ debug_.enabled = 1;
+ debug_.steps = 0;
+ return;
+}
+
+static void ccmd_cont() {
+ debug_.enabled = 0;
+ debug_.steps = 0;
+ return;
+}
+
+/*
+ * Register console commands
+ */
+static void console_cmd(char *cmd, char *dsc, void (*handler) (void)) {
+ assert(num_ccmd < MAX_CCMD);
+
+ strcpy(ccmd_list[num_ccmd].cmd, cmd);
+ strcpy(ccmd_list[num_ccmd].dsc, dsc);
+ ccmd_list[num_ccmd].handler = handler;
+
+ num_ccmd++;
+}
+
+/* Console reporting */
+
+/* A slightly modified strtok() for report() */
+static char *get_token(char *s, char d) {
+ static char *x;
+ char *n, *m;
+
+ if (s)
+ x = s;
+
+ m = x;
+ n = strchr(x, d);
+
+ if (n) {
+ *n = 0;
+ x = n + 1;
+ } else {
+ x = strchr(x, 0);
+ }
+
+ return m;
+}
+
+static void build_console_lines(int n) {
+ int j, y1;
+ char *line;
+
+ clear_console_screen(GFX_HEIGHT - n * 10);
+
+ for (j = CONSOLE_LINES_ONSCREEN - n; j < CONSOLE_LINES_ONSCREEN; j++) {
+ line = get_console_line(console.first_line + j);
+ print_text_console(line, 0, j, strlen(line) + 1, CONSOLE_COLOR, 0);
+ }
+
+ y1 = console.y - n * 10;
+ if (y1 < 0)
+ y1 = 0;
+
+ /* CM:
+ * This will cause blinking when using menus+console, but this
+ * function is called by report() which can be called from any
+ * point with or without sprites placed. If we protect the
+ * flush_block() call with redraw/release sprites cloning will
+ * happen when activating the console. This could be fixed by
+ * keeping a control flag in redraw/release to not do the
+ * actions twice, but I don't want to do that -- not yet.
+ */
+ flush_block(0, y1, GFX_WIDTH - 1, console.y);
+}
+
+/*
+ * Public functions
+ */
+
+int console_init() {
+ console_cmd("agiver", "Show emulated Sierra AGI version", ccmd_agiver);
+ console_cmd("cont", "Resume interpreter execution", ccmd_cont);
+ console_cmd("debug", "Stop interpreter execution", ccmd_debug);
+ console_cmd("crc", "Show AGI game CRC", ccmd_crc);
+#if 0
+ console_cmd("exec", "Execute loaded AGI game", ccmd_exec);
+#endif
+ console_cmd("flags", "Dump all AGI flags", ccmd_flags);
+ console_cmd("help", "List available commands", ccmd_help);
+#ifdef USE_HIRES
+ console_cmd("hires", "Turn hi-res mode on/off", ccmd_hires);
+#endif
+ console_cmd("logic0", "Turn logic 0 debugging on/off", ccmd_logic0);
+ console_cmd("objs", "List all objects and locations", ccmd_objs);
+ console_cmd("opcode", "Turn opcodes on/off in debug", ccmd_opcode);
+ console_cmd("quit", "Quit the interpreter", ccmd_quit);
+#if 0
+ console_cmd("inv", "List current inventory", ccmd_inv);
+ console_cmd("say", "Pass argument to the AGI parser", ccmd_say);
+#endif
+ console_cmd("step", "Execute the next AGI instruction", ccmd_step);
+ console_cmd("trigger", "Turn trigger lines on/off", ccmd_trigger);
+ console_cmd("vars", "Dump all AGI variables", ccmd_vars);
+ console_cmd("ver", "Show interpreter version", ccmd_ver);
+
+ console.active = 1;
+ console.input_active = 1;
+ console.index = 0;
+ console.max_y = 150;
+ console.y = console.max_y;
+ console.first_line = CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN;
+
+ debug_.enabled = 0;
+ debug_.opcodes = 0;
+ debug_.logic0 = 1;
+ debug_.priority = 0;
+
+ has_console = 1;
+ clear_screen(0);
+
+ return err_OK;
+}
+
+static void build_console_layer() {
+ build_console_lines(console.max_y / 10);
+}
+
+void report(char *message, ...) {
+ char x[512], y[512], z[64], *msg, *n;
+ va_list args;
+ int i, s, len;
+
+ if (!has_console)
+ return;
+
+ va_start(args, message);
+#ifdef HAVE_VSNPRINTF
+ vsnprintf(y, 510, (char *)message, args);
+#else
+ vsprintf(y, (char *)message, args);
+#endif
+ va_end(args);
+
+ if (console_input) {
+ strcpy(z, get_last_console_line());
+ strcpy(x, ">");
+ } else {
+ strcpy(x, get_last_console_line());
+ }
+
+ strcat(x, y);
+
+ len = 40;
+ msg = n = word_wrap_string(x, &len);
+
+ for (s = 1; *n; n++)
+ if (*n == '\n')
+ s++;
+
+ add_console_line(get_token(msg, '\n'));
+ for (i = 1; i < s; i++) {
+ first_line++;
+ n = get_token(NULL, '\n');
+ add_console_line(n);
+ }
+
+ console.first_line = CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN;
+
+ if (console_input) {
+ add_console_line(z);
+ }
+
+ /* Build layer */
+ if (console.y) {
+ if (s > 1)
+ build_console_layer();
+ else
+ build_console_lines(1);
+ }
+
+ free(msg);
+
+ do_update();
+}
+
+void console_cycle() {
+ static int old_y = 0;
+ static int blink = 0;
+ static char cursor[] = " ";
+
+ /* If no game has been loaded, keep the console visible! */
+ if (game.state < STATE_RUNNING) {
+ console.active = 1;
+ console.count = 10;
+ }
+
+ /* Initial console auto-hide timer */
+ if (console.count > 0)
+ console.count--;
+ if (console.count == 0) {
+ console.active = 0;
+ console.count = -1;
+ }
+
+ if (console.active) {
+ if (console.y < console.max_y)
+ console.y += 15;
+ else
+ console.y = console.max_y;
+ } else {
+ console.count = -1;
+
+ if (console.y > 0)
+ console.y -= 15;
+ else
+ console.y = 0;
+ }
+
+ /* console shading animation */
+ if (old_y != console.y) {
+ int y = console.y;
+ if (old_y > console.y) {
+ /* going up */
+ y = old_y;
+ }
+ flush_block(0, 0, GFX_WIDTH - 1, y);
+ old_y = console.y;
+ }
+
+ blink++;
+ blink %= 16;
+ if (console.input_active) {
+ *cursor = blink < 8 ?
+ CONSOLE_CURSOR_SOLID : CONSOLE_CURSOR_EMPTY;
+ } else {
+ *cursor = CONSOLE_CURSOR_HOLLOW;
+ }
+ if (console.y > 0
+ && console.first_line ==
+ CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN) {
+ int16 y1 = console.y - 10, y2 = console.y - 1;
+ if (y1 < 0)
+ y1 = 0;
+ if (y2 < 0)
+ y2 = 0;
+ print_text_console(cursor, (1 + console.index), 19, 2, CONSOLE_COLOR, 0);
+ flush_block((1 + console.index) * 8, y1, (1 + console.index) * 8 + 7, y2 - 1);
+ }
+
+ do_update();
+}
+
+/* Return true if key was handled */
+int console_keyhandler(int k) {
+ static char buffer[CONSOLE_INPUT_SIZE];
+ int16 y1, y2;
+ char m[2];
+
+#ifdef USE_MOUSE
+ /* Right button switches console on/off */
+ if (!opt.agimouse) /* AGI Mouse uses right button */
+ if (k == BUTTON_RIGHT)
+ k = CONSOLE_ACTIVATE_KEY;
+#endif
+
+ if (!console.active) {
+ if (k == CONSOLE_ACTIVATE_KEY) {
+ console.active = 1;
+ return true;
+ }
+ return false;
+ }
+
+ if (!console.input_active) {
+ if (k == CONSOLE_SWITCH_KEY) {
+ console.input_active = 1;
+ return true;
+ }
+ if (k == CONSOLE_ACTIVATE_KEY) {
+ console.active = 0;
+ return true;
+ }
+ return false;
+ }
+
+ y1 = console.y - 10;
+ y2 = console.y - 1;
+
+ if (y1 < 0)
+ y1 = 0;
+ if (y2 < 0)
+ y2 = 0;
+
+#ifdef USE_MOUSE
+ /* Ignore left button in console */
+ if (k == BUTTON_LEFT)
+ return true;
+#endif
+
+ /* this code breaks scrolling up, maybe it shoud only be executed
+ * in the default case?
+ */
+ if (k) {
+ /* make sure it's not enter or a scroll key */
+ if ((k != KEY_ENTER) && (k != CONSOLE_SCROLLUP_KEY) && (k != CONSOLE_SCROLLDN_KEY)
+ && (k != CONSOLE_START_KEY)) {
+ /* on any other input reset the console to the bottom */
+ if (console.first_line != CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN) {
+ console.first_line = CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN;
+ build_console_layer();
+ }
+ console.count = -1;
+ }
+ }
+
+ switch (k) {
+ case KEY_ENTER:
+ console_lock();
+ console.index = 0;
+ report("\n");
+
+ if (console_parse(buffer) != 0)
+ report("What? Where?\n");
+
+ buffer[0] = 0;
+ console_prompt();
+ break;
+ case KEY_BACKSPACE:{
+ char *x;
+ if (!console.index)
+ break;
+ x = get_last_console_line();
+ x[console.index] = 0;
+ *m = CONSOLE_CURSOR_EMPTY;
+ print_text_console(m, (console.index + 1), 19, 2, CONSOLE_COLOR, 0);
+ flush_block((console.index + 1) * CHAR_COLS, y1, (console.index + 1 + 1) * CHAR_COLS - 1, y2);
+ console.index--;
+ buffer[console.index] = 0;
+ }
+ break;
+ case CONSOLE_ACTIVATE_KEY:
+ console.active = !console.active;
+ if (console.active)
+ build_console_layer();
+ break;
+ case CONSOLE_SWITCH_KEY:
+ console.count = -1;
+ if (console.y)
+ console.input_active = !console.input_active;
+ break;
+ case CONSOLE_SCROLLUP_KEY:
+ console.count = -1;
+ if (!console.y)
+ break;
+ if (console.first_line > (CONSOLE_LINES_ONSCREEN / 2))
+ console.first_line -= CONSOLE_LINES_ONSCREEN / 2;
+ else
+ console.first_line = 0;
+ build_console_layer();
+ break;
+ case CONSOLE_SCROLLDN_KEY:
+ console.count = -1;
+ if (!console.y)
+ break;
+ if (console.first_line < (CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN - CONSOLE_LINES_ONSCREEN / 2))
+ console.first_line += CONSOLE_LINES_ONSCREEN / 2;
+ else
+ console.first_line = CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN;
+ build_console_layer();
+ break;
+ case CONSOLE_START_KEY:
+ console.count = -1;
+ if (console.y)
+ console.first_line = 0;
+ break;
+ case CONSOLE_END_KEY:
+ console.count = -1;
+ if (console.y)
+ console.first_line = CONSOLE_LINES_BUFFER - CONSOLE_LINES_ONSCREEN;
+ break;
+ default:
+ if (k >= 0x20 && k <= 0x7f && (console.index < CONSOLE_INPUT_SIZE - 2)) {
+ char l[42];
+ buffer[console.index] = k;
+ *m = k;
+ m[1] = 0;
+ console.index++;
+
+ sprintf(l, "%s%c", get_last_console_line(), k);
+ strncpy(get_last_console_line(), l, CONSOLE_LINE_SIZE);
+
+ buffer[console.index] = 0;
+ print_text_console(m, console.index, 19, 2, CONSOLE_COLOR, 0);
+ flush_block(console.index * 8, y1, console.index * 8 + 7, y2);
+ }
+ break;
+ }
+
+ do_update();
+
+ return true;
+}
+
+void console_prompt() {
+ report(CONSOLE_PROMPT);
+ console_input = 1;
+}
+
+void console_lock() {
+ console_input = 0;
+}
+
+#else
+
+void *debug;
+
+void report(char *message, ...) {
+ /* dummy */
+}
+
+int console_init() {
+ return 0;
+}
+
+/* Date: Sun, 14 Oct 2001 23:02:02 -0700
+ * From: Vasyl Tsvirkunov <vasyl@pacbell.net>
+ *
+ * This one was rather harmless and affected only builds without console.
+ * In SQ1&2 (and likely some others) name entry screen did not update
+ * properly. The bug caused by implicit assumption in cycle.c that
+ * console_cycle() updates the screen. Well, it does, if console is enabled.
+ * The fix is simple. In the second version of console_cycle() in console.c
+ * (the "dummy" one) add call to do_update(). The thing raises some
+ * questions about overall architecture of main cycle, but otherwise the fix
+ * works just fine.
+ */
+void console_cycle() {
+ do_update();
+}
+
+void console_lock() {
+ /* dummy */
+}
+
+void console_prompt() {
+ /* dummy */
+}
+
+int console_keyhandler(int i) {
+ return false;
+}
+
+#endif /* USE_CONSOLE */
+
+} // End of namespace Agi
diff --git a/engines/agi/console.h b/engines/agi/console.h
new file mode 100644
index 0000000000..1a211f9826
--- /dev/null
+++ b/engines/agi/console.h
@@ -0,0 +1,71 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_CONSOLE_H
+#define __AGI_CONSOLE_H
+
+namespace Agi {
+
+#ifdef USE_CONSOLE
+
+#define CONSOLE_LINES_BUFFER 80
+#define CONSOLE_LINE_SIZE (GFX_WIDTH / 8)
+#define CONSOLE_ACTIVATE_KEY '`'
+#define CONSOLE_SWITCH_KEY '~'
+
+struct agi_console {
+ int active;
+ int input_active;
+ int index;
+ int y;
+ int max_y;
+ int first_line;
+ int count;
+ char *line[CONSOLE_LINES_BUFFER];
+};
+
+struct agi_debug {
+ int enabled;
+ int opcodes;
+ int logic0;
+ int steps;
+ int priority;
+ int statusline;
+ int ignoretriggers;
+};
+
+extern struct agi_console console;
+
+#endif /* USE_CONSOLE */
+
+int console_keyhandler(int);
+int console_init(void);
+void console_cycle(void);
+void console_lock(void);
+void console_prompt(void);
+void report(char *, ...);
+
+} // End of namespace Agi
+
+#endif /* __AGI_CONSOLE_H */
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
new file mode 100644
index 0000000000..1c5365e4d1
--- /dev/null
+++ b/engines/agi/cycle.cpp
@@ -0,0 +1,423 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/text.h"
+#include "agi/sprite.h"
+#include "agi/graphics.h"
+#include "agi/keyboard.h"
+#include "agi/menu.h"
+#include "agi/savegame.h"
+
+namespace Agi {
+
+#define TICK_SECONDS 20
+
+#ifdef USE_MOUSE
+struct mouse mouse;
+#endif
+
+volatile uint32 clock_ticks;
+volatile uint32 clock_count;
+
+/**
+ * Set up new room.
+ * This function is called when ego enters a new room.
+ * @param n room number
+ */
+void new_room(int n) {
+ struct vt_entry *v;
+ int i;
+
+ debugC(4, kDebugLevelMain, "*** room %d ***", n);
+ stop_sound();
+
+ i = 0;
+ for (v = game.view_table; v < &game.view_table[MAX_VIEWTABLE]; v++) {
+ v->entry = i++;
+ v->flags &= ~(ANIMATED | DRAWN);
+ v->flags |= UPDATE;
+ v->step_time = 1;
+ v->step_time_count = 1;
+ v->cycle_time = 1;
+ v->cycle_time_count = 1;
+ v->step_size = 1;
+ }
+ agi_unload_resources();
+
+ game.player_control = true;
+ game.block.active = false;
+ game.horizon = 36;
+ game.vars[V_prev_room] = game.vars[V_cur_room];
+ game.vars[V_cur_room] = n;
+ game.vars[V_border_touch_obj] = 0;
+ game.vars[V_border_code] = 0;
+ game.vars[V_ego_view_resource] = game.view_table[0].current_view;
+
+ agi_load_resource(rLOGIC, n);
+
+ /* Reposition ego in the new room */
+ switch (game.vars[V_border_touch_ego]) {
+ case 1:
+ game.view_table[0].y_pos = _HEIGHT - 1;
+ break;
+ case 2:
+ game.view_table[0].x_pos = 0;
+ break;
+ case 3:
+ game.view_table[0].y_pos = HORIZON + 1;
+ break;
+ case 4:
+ game.view_table[0].x_pos = _WIDTH - game.view_table[0].x_size;
+ break;
+ }
+
+ game.vars[V_border_touch_ego] = 0;
+ setflag(F_new_room_exec, true);
+
+ game.exit_all_logics = true;
+
+ write_status();
+ write_prompt();
+}
+
+static void reset_controllers() {
+ int i;
+
+ for (i = 0; i < MAX_DIRS; i++) {
+ game.ev_keyp[i].occured = false;
+ }
+}
+
+static void interpret_cycle() {
+ int old_sound, old_score;
+
+ if (game.player_control)
+ game.vars[V_ego_dir] = game.view_table[0].direction;
+ else
+ game.view_table[0].direction = game.vars[V_ego_dir];
+
+ check_all_motions();
+
+ old_score = game.vars[V_score];
+ old_sound = getflag(F_sound_on);
+
+ game.exit_all_logics = false;
+ while (run_logic(0) == 0 && !game.quit_prog_now) {
+ game.vars[V_word_not_found] = 0;
+ game.vars[V_border_touch_obj] = 0;
+ game.vars[V_border_code] = 0;
+ old_score = game.vars[V_score];
+ setflag(F_entered_cli, false);
+ game.exit_all_logics = false;
+ reset_controllers();
+ }
+ reset_controllers();
+
+ game.view_table[0].direction = game.vars[V_ego_dir];
+
+ if (game.vars[V_score] != old_score || getflag(F_sound_on) != old_sound)
+ write_status();
+
+ game.vars[V_border_touch_obj] = 0;
+ game.vars[V_border_code] = 0;
+ setflag(F_new_room_exec, false);
+ setflag(F_restart_game, false);
+ setflag(F_restore_just_ran, false);
+
+ if (game.gfx_mode) {
+ update_viewtable();
+ do_update();
+ }
+}
+
+/**
+ * Update AGI interpreter timer.
+ */
+void update_timer() {
+ clock_count++;
+ if (clock_count <= TICK_SECONDS)
+ return;
+
+ clock_count -= TICK_SECONDS;
+
+ if (!game.clock_enabled)
+ return;
+
+ setvar(V_seconds, getvar(V_seconds) + 1);
+ if (getvar(V_seconds) < 60)
+ return;
+
+ setvar(V_seconds, 0);
+ setvar(V_minutes, getvar(V_minutes) + 1);
+ if (getvar(V_minutes) < 60)
+ return;
+
+ setvar(V_minutes, 0);
+ setvar(V_hours, getvar(V_hours) + 1);
+ if (getvar(V_hours) < 24)
+ return;
+
+ setvar(V_hours, 0);
+ setvar(V_days, getvar(V_days) + 1);
+}
+
+static int old_mode = -1;
+
+void new_input_mode(int i) {
+ old_mode = game.input_mode;
+ game.input_mode = i;
+}
+
+void old_input_mode() {
+ game.input_mode = old_mode;
+}
+
+/* If main_cycle returns false, don't process more events! */
+int main_cycle() {
+ unsigned int key, kascii;
+ struct vt_entry *v = &game.view_table[0];
+
+ poll_timer(); /* msdos driver -> does nothing */
+ update_timer();
+
+ if (game.ver == 0) {
+ message_box("Warning: game CRC not listed, assuming AGI version 2.917.");
+ game.ver = -1;
+ }
+
+ key = do_poll_keyboard();
+
+#ifdef USE_MOUSE
+ /* In AGI Mouse emulation mode we must update the mouse-related
+ * vars in every interpreter cycle.
+ */
+ if (opt.agimouse) {
+ game.vars[28] = mouse.x / 2;
+ game.vars[29] = mouse.y;
+ }
+#endif
+
+#ifdef USE_CONSOLE
+ if (key == KEY_PRIORITY) {
+ erase_both();
+ debug_.priority = !debug_.priority;
+ show_pic();
+ blit_both();
+ commit_both();
+ key = 0;
+ }
+
+ if (key == KEY_STATUSLN) {
+ debug_.statusline = !debug_.statusline;
+ write_status();
+ key = 0;
+ }
+#endif
+
+ /* Click-to-walk mouse interface */
+ if (game.player_control && v->flags & ADJ_EGO_XY) {
+ v->direction = get_direction(v->x_pos, v->y_pos, v->parm1, v->parm2, v->step_size);
+
+ if (v->direction == 0)
+ in_destination(v);
+ }
+
+ kascii = KEY_ASCII(key);
+
+ if (!console_keyhandler(key)) {
+ if (kascii)
+ setvar(V_key, kascii);
+ process_key:
+ switch (game.input_mode) {
+ case INPUT_NORMAL:
+ if (!handle_controller(key)) {
+ if (key == 0 || !game.input_enabled)
+ break;
+ handle_keys(key);
+
+ /* if ESC pressed, activate menu before
+ * accept.input from the interpreter cycle
+ * sets the input mode to normal again
+ * (closes: #540856)
+ */
+ if (key == KEY_ESCAPE) {
+ key = 0;
+ goto process_key;
+ }
+
+ /* commented out to close bug #438872
+ * if (key) game.keypress = key;
+ */
+ }
+ break;
+ case INPUT_GETSTRING:
+ handle_controller(key);
+ handle_getstring(key);
+ setvar(V_key, 0); /* clear ENTER key */
+ break;
+ case INPUT_MENU:
+ menu_keyhandler(key);
+ console_cycle();
+ return false;
+ case INPUT_NONE:
+ handle_controller(key);
+ if (key)
+ game.keypress = key;
+ break;
+ }
+ } else {
+ if (game.input_mode == INPUT_MENU) {
+ console_cycle();
+ return false;
+ }
+ }
+
+ console_cycle();
+
+ if (game.msg_box_ticks > 0)
+ game.msg_box_ticks--;
+
+ return true;
+}
+
+static int play_game() {
+ int ec = err_OK;
+
+ debugC(2, kDebugLevelMain, "initializing...");
+ debugC(2, kDebugLevelMain, "game.ver = 0x%x", game.ver);
+
+ stop_sound();
+ clear_screen(0);
+
+ game.horizon = HORIZON;
+ game.player_control = false;
+
+ setflag(F_logic_zero_firsttime, true); /* not in 2.917 */
+ setflag(F_new_room_exec, true); /* needed for MUMG and SQ2! */
+ setflag(F_sound_on, true); /* enable sound */
+ setvar(V_time_delay, 2); /* "normal" speed */
+
+ game.gfx_mode = true;
+ game.quit_prog_now = false;
+ game.clock_enabled = true;
+ game.line_user_input = 22;
+
+#ifdef USE_MOUSE
+ if (opt.agimouse)
+ report("Using AGI Mouse 1.0 protocol\n");
+#endif
+
+ report("Running AGI script.\n");
+
+#ifdef USE_CONSOLE
+ console.count = 5;
+ console_prompt();
+#endif
+
+ setflag(F_entered_cli, false);
+ setflag(F_said_accepted_input, false);
+ game.vars[V_word_not_found] = 0;
+ game.vars[V_key] = 0;
+
+ debugC(2, kDebugLevelMain, "Entering main loop");
+ do {
+
+ if (!main_cycle())
+ continue;
+
+ if (getvar(V_time_delay) == 0 ||
+ (1 + clock_count) % getvar(V_time_delay) == 0) {
+ if (!game.has_prompt && game.input_mode == INPUT_NORMAL) {
+ write_prompt();
+ game.has_prompt = 1;
+ } else
+ if (game.has_prompt && game.input_mode == INPUT_NONE) {
+ write_prompt();
+ game.has_prompt = 0;
+ }
+
+ interpret_cycle();
+
+ setflag(F_entered_cli, false);
+ setflag(F_said_accepted_input, false);
+ game.vars[V_word_not_found] = 0;
+ game.vars[V_key] = 0;
+ }
+
+ if (game.quit_prog_now == 0xff)
+ ec = err_RestartGame;
+
+ } while (game.quit_prog_now == 0);
+
+ stop_sound();
+
+ return ec;
+}
+
+int run_game() {
+ int i, ec = err_OK;
+
+#ifdef USE_HIRES
+ if (opt.cgaemu)
+ opt.hires = 0;
+#endif
+
+ for (i = 0; i < MAX_DIRS; i++)
+ memset(&game.ev_keyp[i], 0, sizeof(struct agi_event));
+
+ /* Execute the game */
+ do {
+ debugC(2, kDebugLevelMain, "game loop");
+ debugC(2, kDebugLevelMain, "game.ver = 0x%x", game.ver);
+
+ if (agi_init() != err_OK)
+ break;
+ if (ec == err_RestartGame)
+ setflag(F_restart_game, true);
+
+ setvar(V_computer, 0); /* IBM PC (4 = Atari ST) */
+ setvar(V_soundgen, 1); /* IBM PC SOUND */
+ setvar(V_monitor, 0x3); /* EGA monitor */
+ setvar(V_max_input_chars, 38);
+ game.input_mode = INPUT_NONE;
+ game.input_enabled = 0;
+ game.has_prompt = 0;
+
+ game.state = STATE_RUNNING;
+ ec = play_game();
+ game.state = STATE_LOADED;
+ agi_deinit();
+ } while (ec == err_RestartGame);
+
+ menu_deinit();
+
+ release_image_stack();
+
+ return ec;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/font.cpp b/engines/agi/font.cpp
new file mode 100644
index 0000000000..487107dfe8
--- /dev/null
+++ b/engines/agi/font.cpp
@@ -0,0 +1,295 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+/* 8x8 font patterns */
+uint8 cur_font[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, /* cursor hollow */
+ 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, /* cursor solid */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* cursor empty */
+ 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00,
+ 0x3C, 0x3C, 0x18, 0xFF, 0xE7, 0x18, 0x3C, 0x00,
+ 0x10, 0x38, 0x7C, 0xFE, 0xEE, 0x10, 0x38, 0x00,
+ 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00,
+ 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF,
+ 0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* \n */
+ 0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78,
+ 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18,
+ 0x08, 0x0C, 0x0A, 0x0A, 0x08, 0x78, 0xF0, 0x00,
+ 0x18, 0x14, 0x1A, 0x16, 0x72, 0xE2, 0x0E, 0x1C,
+ 0x10, 0x54, 0x38, 0xEE, 0x38, 0x54, 0x10, 0x00,
+ 0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00,
+ 0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00,
+ 0x18, 0x3C, 0x5A, 0x18, 0x5A, 0x3C, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
+ 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x00,
+ 0x1C, 0x22, 0x38, 0x44, 0x44, 0x38, 0x88, 0x70,
+ 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x00,
+ 0x18, 0x3C, 0x5A, 0x18, 0x5A, 0x3C, 0x18, 0x7E,
+ 0x18, 0x3C, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x5A, 0x3C, 0x18, 0x00,
+ 0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00,
+ 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00,
+ 0x00, 0x24, 0x42, 0xFF, 0x42, 0x24, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x7C, 0xFE, 0xFE, 0x00, 0x00,
+ 0x00, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00,
+ 0x6C, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00,
+ 0x10, 0x7C, 0xD0, 0x7C, 0x16, 0xFC, 0x10, 0x00,
+ 0x00, 0x66, 0xAC, 0xD8, 0x36, 0x6A, 0xCC, 0x00,
+ 0x38, 0x4C, 0x38, 0x78, 0xCE, 0xCC, 0x7A, 0x00,
+ 0x30, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
+ 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
+ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x20,
+ 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00,
+ 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00,
+ 0x7C, 0xCE, 0xDE, 0xF6, 0xE6, 0xE6, 0x7C, 0x00,
+ 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x7E, 0x00,
+ 0x7C, 0xC6, 0x06, 0x1C, 0x70, 0xC6, 0xFE, 0x00,
+ 0x7C, 0xC6, 0x06, 0x3C, 0x06, 0xC6, 0x7C, 0x00,
+ 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00,
+ 0xFE, 0xC0, 0xFC, 0x06, 0x06, 0xC6, 0x7C, 0x00,
+ 0x7C, 0xC6, 0xC0, 0xFC, 0xC6, 0xC6, 0x7C, 0x00,
+ 0xFE, 0xC6, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x7C, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0x7C, 0x00,
+ 0x7C, 0xC6, 0xC6, 0x7E, 0x06, 0xC6, 0x7C, 0x00,
+ 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+ 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x10, 0x20,
+ 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00,
+ 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00,
+ 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00,
+ 0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00,
+ 0x7C, 0x82, 0x9E, 0xA6, 0x9E, 0x80, 0x7C, 0x00,
+ 0x7C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00,
+ 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00,
+ 0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00,
+ 0xFC, 0x66, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00,
+ 0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00,
+ 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00,
+ 0x7C, 0xC6, 0xC6, 0xC0, 0xCE, 0xC6, 0x7E, 0x00,
+ 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00,
+ 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
+ 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00,
+ 0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00,
+ 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00,
+ 0x82, 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00,
+ 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00,
+ 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
+ 0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00,
+ 0x7C, 0xC6, 0xC6, 0xC6, 0xD6, 0xDE, 0x7C, 0x06,
+ 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xE6, 0x00,
+ 0x7C, 0xC6, 0xC0, 0x7C, 0x06, 0xC6, 0x7C, 0x00,
+ 0x7E, 0x5A, 0x5A, 0x18, 0x18, 0x18, 0x3C, 0x00,
+ 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
+ 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x10, 0x00,
+ 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x82, 0x00,
+ 0xC6, 0x6C, 0x38, 0x38, 0x38, 0x6C, 0xC6, 0x00,
+ 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x3C, 0x00,
+ 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00,
+ 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
+ 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00,
+ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
+ 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
+ 0x30, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00,
+ 0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00,
+ 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC6, 0x7C, 0x00,
+ 0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00,
+ 0x00, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0x7C, 0x00,
+ 0x1C, 0x36, 0x30, 0x78, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x78,
+ 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00,
+ 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00,
+ 0x00, 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0xCC, 0x78,
+ 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00,
+ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
+ 0x00, 0x00, 0xCC, 0xFE, 0xD6, 0xD6, 0xD6, 0x00,
+ 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x00,
+ 0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0,
+ 0x00, 0x00, 0x7C, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E,
+ 0x00, 0x00, 0xDE, 0x76, 0x60, 0x60, 0xF0, 0x00,
+ 0x00, 0x00, 0x7C, 0xC0, 0x7C, 0x06, 0x7C, 0x00,
+ 0x10, 0x30, 0xFC, 0x30, 0x30, 0x34, 0x18, 0x00,
+ 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00,
+ 0x00, 0x00, 0xC6, 0xC6, 0x6C, 0x38, 0x10, 0x00,
+ 0x00, 0x00, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x00,
+ 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00,
+ 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8,
+ 0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00,
+ 0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0E, 0x00,
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+ 0xE0, 0x30, 0x30, 0x18, 0x30, 0x30, 0xE0, 0x00,
+ 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /*0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00, */
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*replacement 0x7F */
+
+#ifdef AGDS_SUPPORT
+ 0x1E, 0x36, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00,
+ 0x7C, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00,
+ 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00,
+ 0x7E, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00,
+ 0x38, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0xFE, 0xC6,
+ 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00,
+ 0xDB, 0xDB, 0x7E, 0x3C, 0x7E, 0xDB, 0xDB, 0x00,
+ 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00,
+ 0x66, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00,
+ 0x3C, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00,
+ 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00,
+ 0x1E, 0x36, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00,
+ 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00,
+ 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0x7E, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00,
+ 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00,
+ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00,
+ 0x7E, 0xDB, 0xDB, 0xDB, 0x7E, 0x18, 0x18, 0x00,
+ 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7F, 0x03,
+ 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00,
+ 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xFF, 0x00,
+ 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xFF, 0x03,
+ 0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00,
+ 0xC6, 0xC6, 0xC6, 0xF6, 0xDE, 0xDE, 0xF6, 0x00,
+ 0x60, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00,
+ 0x78, 0x8C, 0x06, 0x3E, 0x06, 0x8C, 0x78, 0x00,
+ 0xCE, 0xDB, 0xDB, 0xFB, 0xDB, 0xDB, 0xCE, 0x00,
+ 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x00,
+ 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3A, 0x00,
+ 0x00, 0x3C, 0x60, 0x3C, 0x66, 0x66, 0x3C, 0x00,
+ 0x00, 0x00, 0x7C, 0x66, 0x7C, 0x66, 0x7C, 0x00,
+ 0x00, 0x00, 0x7E, 0x60, 0x60, 0x60, 0x60, 0x00,
+ 0x00, 0x00, 0x3C, 0x6C, 0x6C, 0x6C, 0xFE, 0xC6,
+ 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00,
+ 0x00, 0x00, 0xDB, 0x7E, 0x3C, 0x7E, 0xDB, 0x00,
+ 0x00, 0x00, 0x3C, 0x66, 0x0C, 0x66, 0x3C, 0x00,
+ 0x00, 0x00, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x00,
+ 0x00, 0x18, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x00,
+ 0x00, 0x00, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00,
+ 0x00, 0x00, 0x1E, 0x36, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x00, 0xC6, 0xFE, 0xFE, 0xD6, 0xC6, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00,
+ 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0x00, 0x00, 0x7E, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+ 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
+ 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0xFE, 0x06, 0xF6, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0xFE, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, 0x18,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x00,
+ 0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00,
+ 0x00, 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x3E, 0x06, 0x7C, 0x00,
+ 0x00, 0x00, 0x7E, 0xDB, 0xDB, 0x7E, 0x18, 0x00,
+ 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7F, 0x03,
+ 0x00, 0x00, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x00,
+ 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xFF, 0x00,
+ 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0xDB, 0xFF, 0x03,
+ 0x00, 0x00, 0xE0, 0x60, 0x7C, 0x66, 0x7C, 0x00,
+ 0x00, 0x00, 0xC6, 0xC6, 0xF6, 0xDE, 0xF6, 0x00,
+ 0x00, 0x00, 0x60, 0x60, 0x7C, 0x66, 0x7C, 0x00,
+ 0x00, 0x00, 0x7C, 0x06, 0x3E, 0x06, 0x7C, 0x00,
+ 0x00, 0x00, 0xCE, 0xDB, 0xFB, 0xDB, 0xCE, 0x00,
+ 0x00, 0x00, 0x3E, 0x66, 0x3E, 0x36, 0x66, 0x00,
+ 0x00, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00,
+ 0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x7C, 0x00,
+ 0x00, 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30,
+ 0x00, 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C,
+ 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, 0x70,
+ 0x00, 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18,
+ 0x00, 0x76, 0xDC, 0x00, 0x76, 0xDC, 0x00, 0x00,
+ 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00,
+ 0x03, 0x02, 0x06, 0x04, 0xCC, 0x68, 0x38, 0x10,
+ 0x3C, 0x42, 0x99, 0xA1, 0xA1, 0x99, 0x42, 0x3C,
+ 0x30, 0x48, 0x10, 0x20, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7C, 0x7C, 0x7C, 0x7C, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x7E, 0x00
+#endif /* AGDS_SUPPORT */
+};
+
+} // End of namespace Agi
diff --git a/engines/agi/global.cpp b/engines/agi/global.cpp
new file mode 100644
index 0000000000..404726f2b7
--- /dev/null
+++ b/engines/agi/global.cpp
@@ -0,0 +1,73 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+int getflag(int n) {
+ uint8 *set = (uint8 *) & game.flags;
+
+ set += n >> 3;
+ return (*set & (1 << (n & 0x07))) != 0;
+}
+
+void setflag(int n, int v) {
+ uint8 *set = (uint8 *) & game.flags;
+
+ set += n >> 3;
+ if (v)
+ *set |= 1 << (n & 0x07); /* set bit */
+ else
+ *set &= ~(1 << (n & 0x07)); /* clear bit */
+}
+
+void flipflag(int n) {
+ uint8 *set = (uint8 *) & game.flags;
+
+ set += n >> 3;
+ *set ^= 1 << (n & 0x07); /* flip bit */
+}
+
+void setvar(int var, int val) {
+ game.vars[var] = val;
+}
+
+int getvar(int var) {
+ return game.vars[var];
+}
+
+void decrypt(uint8 *mem, int len) {
+ uint8 *key;
+ int i;
+
+ key = opt.agds ? (uint8 *)CRYPT_KEY_AGDS : (uint8 *)CRYPT_KEY_SIERRA;
+
+ for (i = 0; i < len; i++)
+ *(mem + i) ^= *(key + (i % 11));
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/graphics.cpp b/engines/agi/graphics.cpp
new file mode 100644
index 0000000000..fbe0228efa
--- /dev/null
+++ b/engines/agi/graphics.cpp
@@ -0,0 +1,742 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/graphics.h"
+
+namespace Agi {
+
+#define DEV_X0(x) ((x) << 1)
+#define DEV_X1(x) (((x) << 1) + 1)
+#define DEV_Y(x) (x)
+
+#ifndef MAX_INT
+# define MAX_INT (int)((unsigned)~0 >> 1)
+#endif
+
+static uint8 *agi_screen;
+#ifdef USE_CONSOLE
+static uint8 *console_screen;
+#endif
+
+static unsigned char *screen;
+
+extern uint8 cur_font[];
+
+/**
+ * 16 color RGB palette (plus 16 transparent colors).
+ * This array contains the 6-bit RGB values of the EGA palette exported
+ * to the console drivers.
+ */
+uint8 ega_palette[16 * 3] = {
+ 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x2a,
+ 0x00, 0x2a, 0x00,
+ 0x00, 0x2a, 0x2a,
+ 0x2a, 0x00, 0x00,
+ 0x2a, 0x00, 0x2a,
+ 0x2a, 0x15, 0x00,
+ 0x2a, 0x2a, 0x2a,
+ 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x3f,
+ 0x15, 0x3f, 0x15,
+ 0x15, 0x3f, 0x3f,
+ 0x3f, 0x15, 0x15,
+ 0x3f, 0x15, 0x3f,
+ 0x3f, 0x3f, 0x15,
+ 0x3f, 0x3f, 0x3f
+};
+
+/**
+ * 16 color amiga-ish palette.
+ */
+uint8 new_palette[16 * 3] = {
+ 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f,
+ 0x00, 0x2A, 0x00,
+ 0x00, 0x2A, 0x2A,
+ 0x33, 0x00, 0x00,
+ 0x2f, 0x1c, 0x37,
+ 0x23, 0x14, 0x00,
+ 0x2f, 0x2f, 0x2f,
+ 0x15, 0x15, 0x15,
+ 0x00, 0x2f, 0x3f,
+ 0x00, 0x33, 0x15,
+ 0x15, 0x3F, 0x3F,
+ 0x3f, 0x27, 0x23,
+ 0x3f, 0x15, 0x3f,
+ 0x3b, 0x3b, 0x00,
+ 0x3F, 0x3F, 0x3F
+};
+
+uint8 palette[32 * 3];
+
+static uint16 cga_map[16] = {
+ 0x0000, /* 0 - black */
+ 0x0d00, /* 1 - blue */
+ 0x0b00, /* 2 - green */
+ 0x0f00, /* 3 - cyan */
+ 0x000b, /* 4 - red */
+ 0x0b0d, /* 5 - magenta */
+ 0x000d, /* 6 - brown */
+ 0x0b0b, /* 7 - gray */
+ 0x0d0d, /* 8 - dark gray */
+ 0x0b0f, /* 9 - light blue */
+ 0x0b0d, /* 10 - light green */
+ 0x0f0d, /* 11 - light cyan */
+ 0x0f0d, /* 12 - light red */
+ 0x0f00, /* 13 - light magenta */
+ 0x0f0b, /* 14 - yellow */
+ 0x0f0f /* 15 - white */
+};
+
+struct update_block {
+ int x1, y1;
+ int x2, y2;
+};
+
+static struct update_block update = {
+ MAX_INT, MAX_INT, 0, 0
+};
+
+struct gfx_driver *gfx; /* graphics driver */
+
+/*
+ * Layer 4: 640x480? ================== User display
+ * ^
+ * | do_update(), put_block()
+ * |
+ * Layer 3: 640x480? ================== Framebuffer
+ * ^
+ * | flush_block(), put_pixels()
+ * |
+ * Layer 2: 320x200 ================== AGI engine screen (console), put_pixel()
+ * |
+ * Layer 1: 160x168 ================== AGI screen
+ */
+
+#ifdef USE_CONSOLE
+
+/**
+ * Draws a row of pixels in the output device framebuffer.
+ * This function adds the console layer using transparent colors if
+ * appropriate.
+ */
+static void put_pixels(const int x, const int y, const int w, uint8 *p) {
+ int i;
+ uint8 _b[GFX_WIDTH] = { 0 };
+ uint8 *b, *c = NULL;
+
+ if (console.y <= y) {
+ gfx_putpixels(x, y, w, p);
+ return;
+ }
+
+ b = &_b[0];
+ c = &console_screen[x + (y + GFX_HEIGHT - 1 - console.y) * GFX_WIDTH];
+
+ for (i = 0; i < w; i++, c++, p++) {
+ *b++ = *c ? *c : *p + 16;
+ }
+
+ gfx_putpixels(x, y, w, _b);
+}
+
+static void init_console() {
+ int i;
+
+ /* Console */
+ console.line[0] = (char *)calloc(CONSOLE_LINES_BUFFER, CONSOLE_LINE_SIZE + 1);
+ for (i = 1; i < CONSOLE_LINES_BUFFER; i++)
+ console.line[i] = console.line[i - 1] + CONSOLE_LINE_SIZE + 1;
+}
+
+#else
+
+static void put_pixels(const int x, const int y, const int w, uint8 *p) {
+ gfx->put_pixels(x, y, w, p);
+}
+
+static void init_console()
+{
+}
+
+#endif /* USE_CONSOLE */
+
+#define SHAKE_MAG 3
+static uint8 *shake_h, *shake_v;
+
+void shake_start() {
+ int i;
+
+ if ((shake_h = (uint8 *)malloc(GFX_WIDTH * SHAKE_MAG)) == NULL)
+ return;
+
+ if ((shake_v = (uint8 *)malloc(SHAKE_MAG * (GFX_HEIGHT - SHAKE_MAG))) == NULL) {
+ free(shake_h);
+ return;
+ }
+
+ for (i = 0; i < GFX_HEIGHT - SHAKE_MAG; i++) {
+ memcpy(shake_v + i * SHAKE_MAG, agi_screen + i * GFX_WIDTH, SHAKE_MAG);
+ }
+
+ for (i = 0; i < SHAKE_MAG; i++) {
+ memcpy(shake_h + i * GFX_WIDTH, agi_screen + i * GFX_WIDTH, GFX_WIDTH);
+ }
+}
+
+void shake_screen(int n) {
+ int i;
+
+ if (n == 0) {
+ for (i = 0; i < (GFX_HEIGHT - SHAKE_MAG); i++) {
+ memmove(&agi_screen[GFX_WIDTH * i],
+ &agi_screen[GFX_WIDTH * (i + SHAKE_MAG) + SHAKE_MAG],
+ GFX_WIDTH - SHAKE_MAG);
+ }
+ } else {
+ for (i = GFX_HEIGHT - SHAKE_MAG - 1; i >= 0; i--) {
+ memmove(&agi_screen[GFX_WIDTH * (i + SHAKE_MAG) + SHAKE_MAG],
+ &agi_screen[GFX_WIDTH * i], GFX_WIDTH - SHAKE_MAG);
+ }
+ }
+}
+
+void shake_end() {
+ int i;
+
+ for (i = 0; i < GFX_HEIGHT - SHAKE_MAG; i++) {
+ memcpy(agi_screen + i * GFX_WIDTH, shake_v + i * SHAKE_MAG, SHAKE_MAG);
+ }
+
+ for (i = 0; i < SHAKE_MAG; i++) {
+ memcpy(agi_screen + i * GFX_WIDTH, shake_h + i * GFX_WIDTH, GFX_WIDTH);
+ }
+
+ flush_block(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
+
+ free(shake_v);
+ free(shake_h);
+}
+
+void put_text_character(int l, int x, int y, unsigned int c, int fg, int bg) {
+ int x1, y1, xx, yy, cc;
+ uint8 *p;
+
+ p = cur_font + ((unsigned int)c * CHAR_LINES);
+ for (y1 = 0; y1 < CHAR_LINES; y1++) {
+ for (x1 = 0; x1 < CHAR_COLS; x1++) {
+ xx = x + x1;
+ yy = y + y1;
+ cc = (*p & (1 << (7 - x1))) ? fg : bg;
+#ifdef USE_CONSOLE
+ if (l) {
+ console_screen[xx + yy * GFX_WIDTH] = cc;
+ } else
+#endif
+ {
+ agi_screen[xx + yy * GFX_WIDTH] = cc;
+ }
+ }
+
+ p++;
+ }
+ /* FIXME: we don't want this when we're writing on the
+ * console!
+ */
+ flush_block(x, y, x + CHAR_COLS - 1, y + CHAR_LINES - 1);
+}
+
+void draw_rectangle(int x1, int y1, int x2, int y2, int c) {
+ int y, w, h;
+ uint8 *p0;
+
+ if (x1 >= GFX_WIDTH)
+ x1 = GFX_WIDTH - 1;
+ if (y1 >= GFX_HEIGHT)
+ y1 = GFX_HEIGHT - 1;
+ if (x2 >= GFX_WIDTH)
+ x2 = GFX_WIDTH - 1;
+ if (y2 >= GFX_HEIGHT)
+ y2 = GFX_HEIGHT - 1;
+
+ w = x2 - x1 + 1;
+ h = y2 - y1 + 1;
+ p0 = &agi_screen[x1 + y1 * GFX_WIDTH];
+ for (y = 0; y < h; y++) {
+ memset(p0, c, w);
+ p0 += GFX_WIDTH;
+ }
+}
+
+static void draw_frame(int x1, int y1, int x2, int y2, int c1, int c2) {
+ int y, w;
+ uint8 *p0;
+
+ /* top line */
+ w = x2 - x1 + 1;
+ p0 = &agi_screen[x1 + y1 * GFX_WIDTH];
+ memset(p0, c1, w);
+
+ /* bottom line */
+ p0 = &agi_screen[x1 + y2 * GFX_WIDTH];
+ memset(p0, c2, w);
+
+ /* side lines */
+ for (y = y1; y <= y2; y++) {
+ agi_screen[x1 + y * GFX_WIDTH] = c1;
+ agi_screen[x2 + y * GFX_WIDTH] = c2;
+ }
+}
+
+void draw_box(int x1, int y1, int x2, int y2, int colour1, int colour2, int m) {
+ x1 += m;
+ y1 += m;
+ x2 -= m;
+ y2 -= m;
+
+ draw_rectangle(x1, y1, x2, y2, colour1);
+ draw_frame(x1 + 2, y1 + 2, x2 - 2, y2 - 2, colour2, colour2);
+ flush_block(x1, y1, x2, y2);
+}
+
+void print_character(int x, int y, char c, int fg, int bg) {
+ x *= CHAR_COLS;
+ y *= CHAR_LINES;
+
+ put_text_character(0, x, y, c, fg, bg);
+ // redundant! already inside put_text_character!
+ // flush_block (x, y, x + CHAR_COLS - 1, y + CHAR_LINES - 1);
+}
+
+/**
+ * Draw button
+ * @param x x coordinate of the button
+ * @param y y coordinate of the button
+ * @param a set if the button has focus
+ * @param p set if the button is pressed
+ */
+void draw_button(int x, int y, char *s, int a, int p) {
+ int len = strlen(s);
+ int x1, y1, x2, y2;
+
+ x1 = x - 3;
+ y1 = y - 3;
+ x2 = x + CHAR_COLS * len + 2;
+ y2 = y + CHAR_LINES + 2;
+
+ while (*s) {
+ put_text_character(0, x + (!!p), y + (!!p), *s++,
+ a ? 15 : 0, a ? 0 : 15);
+ x += CHAR_COLS;
+ }
+
+ x1 -= 2;
+ y1 -= 2;
+ x2 += 2;
+ y2 += 2;
+
+ flush_block(x1, y1, x2, y2);
+}
+
+#ifdef USE_MOUSE
+int test_button(int x, int y, char *s) {
+ int len = strlen(s);
+ int x1, y1, x2, y2;
+
+ x1 = x - 3;
+ y1 = y - 3;
+ x2 = x + CHAR_COLS * len + 2;
+ y2 = y + CHAR_LINES + 2;
+
+ if ((int)mouse.x >= x1 && (int)mouse.y >= y1
+ && (int)mouse.x <= x2 && (int)mouse.y <= y2)
+ return true;
+
+ return false;
+}
+#endif
+
+void put_block(int x1, int y1, int x2, int y2) {
+ gfx_putblock(x1, y1, x2, y2);
+}
+
+void put_screen() {
+ put_block(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
+}
+
+void poll_timer() {
+ agi_timer_low();
+}
+
+int get_key() {
+ return agi_get_keypress_low();
+}
+
+int keypress() {
+ return agi_is_keypress_low();
+}
+
+/*
+ * Public functions
+ */
+
+/**
+ * Initialize the color palette
+ * This function initializes the color palette using the specified 16-color
+ * RGB palette and creates 16 extra palette entries with translucent colors
+ * for the interpreter console.
+ * @param p A pointer to the 16-color RGB palette.
+ */
+void init_palette(uint8 *p) {
+ int i;
+
+ for (i = 0; i < 48; i++) {
+ palette[i] = p[i];
+ palette[i + 48] = (p[i] + 0x30) >> 2;
+ }
+}
+
+void gfx_set_palette() {
+ int i;
+ byte pal[32 * 4];
+
+ for (i = 0; i < 32; i++) {
+ pal[i * 4 + 0] = palette[i * 3 + 0] << 2;
+ pal[i * 4 + 1] = palette[i * 3 + 1] << 2;
+ pal[i * 4 + 2] = palette[i * 3 + 2] << 2;
+ pal[i * 4 + 3] = 0;
+ }
+ g_system->setPalette(pal, 0, 32);
+}
+
+void gfx_putpixels(int x, int y, int w, uint8 *p) {
+ uint8 *p0 = screen + x + y * 320;
+ memcpy(p0, p, w);
+}
+
+/* put a block onto the screen */
+void gfx_putblock(int x1, int y1, int x2, int y2) {
+ if (x1 >= GFX_WIDTH)
+ x1 = GFX_WIDTH - 1;
+ if (y1 >= GFX_HEIGHT)
+ y1 = GFX_HEIGHT - 1;
+ if (x2 >= GFX_WIDTH)
+ x2 = GFX_WIDTH - 1;
+ if (y2 >= GFX_HEIGHT)
+ y2 = GFX_HEIGHT - 1;
+
+ // force full update until fix wrong partial updates
+ g_system->copyRectToScreen(screen, 320, 0, 0, 320, 200);
+ //g_system->copyRectToScreen(screen, 320, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ //g_system->updateScreen();
+}
+
+static const byte mouseCursorArrow[] = {
+ 0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x70, 0x00,
+ 0x78, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x7F, 0x00,
+ 0x7F, 0x80, 0x7C, 0x00, 0x6C, 0x00, 0x46, 0x00,
+ 0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0xC0, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF8, 0x00,
+ 0xFC, 0x00, 0xFE, 0x00, 0xFF, 0x00, 0xFF, 0x80,
+ 0xFF, 0xC0, 0xFF, 0xC0, 0xFE, 0x00, 0xFF, 0x00,
+ 0xCF, 0x00, 0x07, 0x80, 0x07, 0x80, 0x03, 0x80
+};
+
+/**
+ * Initialize graphics device.
+ *
+ * @see deinit_video()
+ */
+int init_video() {
+ if (opt.egapal)
+ init_palette(ega_palette);
+ else
+ init_palette(new_palette);
+
+ init_console();
+
+ if ((agi_screen = (uint8 *)calloc(GFX_WIDTH, GFX_HEIGHT)) == NULL)
+ return err_NotEnoughMemory;
+
+#ifdef USE_CONSOLE
+ if ((console_screen = (uint8 *)calloc(GFX_WIDTH, GFX_HEIGHT)) == NULL) {
+ free(agi_screen);
+ return err_NotEnoughMemory;
+ }
+#endif
+
+ gfx_set_palette();
+
+ byte mouseCursor[16 * 16];
+ const byte *src = mouseCursorArrow;
+ for (int i = 0; i < 32; ++i) {
+ int offs = i * 8;
+ for (byte mask = 0x80; mask != 0; mask >>= 1) {
+ if (src[0] & mask) {
+ mouseCursor[offs] = 2;
+ } else if (src[32] & mask) {
+ mouseCursor[offs] = 0;
+ } else {
+ mouseCursor[offs] = 0xFF;
+ }
+ ++offs;
+ }
+ ++src;
+ }
+ g_system->setMouseCursor(mouseCursor, 16, 16, 1, 1);
+
+ return err_OK;
+}
+
+/**
+ * Deinitialize graphics device.
+ *
+ * @see init_video()
+ */
+int deinit_video() {
+ free(agi_screen);
+#ifdef USE_CONSOLE
+ free(console_screen);
+#endif
+
+ return err_OK;
+}
+
+int init_machine() {
+ screen = (unsigned char *)malloc(320 * 200);
+ clock_count = 0;
+ clock_ticks = 0;
+
+ return err_OK;
+}
+
+int deinit_machine() {
+ free(screen);
+
+ return err_OK;
+}
+
+/**
+ * Write pixels on the output device.
+ * This function writes a row of pixels on the output device. Only the
+ * lower 4 bits of each pixel in the row will be used, making this
+ * function suitable for use with rows from the AGI screen.
+ * @param x x coordinate of the row start (AGI coord.)
+ * @param y y coordinate of the row start (AGI coord.)
+ * @param n number of pixels in the row
+ * @param p pointer to the row start in the AGI screen
+ */
+void put_pixels_a(int x, int y, int n, uint8 *p) {
+ if (opt.cgaemu) {
+ for (x *= 2; n--; p++, x += 2) {
+ register uint16 q = (cga_map[(*p & 0xf0) >> 4] << 4) | cga_map[*p & 0x0f];
+#ifdef USE_CONSOLE
+ if (debug_.priority)
+ q >>= 4;
+#endif
+ *(uint16 *)&agi_screen[x + y * GFX_WIDTH] = q & 0x0f0f;
+ }
+ } else {
+ for (x *= 2; n--; p++, x += 2) {
+ register uint16 q = ((uint16) * p << 8) | *p;
+#ifdef USE_CONSOLE
+ if (debug_.priority)
+ q >>= 4;
+#endif
+ *(uint16 *)&agi_screen[x + y * GFX_WIDTH] = q & 0x0f0f;
+ }
+ }
+}
+
+#ifdef USE_HIRES
+
+void put_pixels_hires(int x, int y, int n, uint8 *p) {
+ //y += CHAR_LINES;
+ for (; n--; p++, x++) {
+ uint8 q = *p;
+#ifdef USE_CONSOLE
+ if (debug_.priority)
+ q >>= 4;
+#endif
+ agi_screen[x + y * GFX_WIDTH] = q & 0x0f;
+ }
+}
+
+#endif
+
+/**
+ * Schedule blocks for blitting on the output device.
+ * This function gets the coordinates of a block in the AGI screen and
+ * schedule it to be updated in the output device.
+ * @param x1 x coordinate of the upper left corner of the block (AGI coord.)
+ * @param y1 y coordinate of the upper left corner of the block (AGI coord.)
+ * @param x2 x coordinate of the lower right corner of the block (AGI coord.)
+ * @param y2 y coordinate of the lower right corner of the block (AGI coord.)
+ *
+ * @see do_update()
+ */
+void schedule_update(int x1, int y1, int x2, int y2) {
+ if (x1 < update.x1)
+ update.x1 = x1;
+ if (y1 < update.y1)
+ update.y1 = y1;
+ if (x2 > update.x2)
+ update.x2 = x2;
+ if (y2 > update.y2)
+ update.y2 = y2;
+}
+
+/**
+ * Update scheduled blocks on the output device.
+ * This function exposes the blocks scheduled for updating to the output
+ * device. Blocks can be scheduled at any point of the AGI cycle.
+ *
+ * @see schedule_update()
+ */
+void do_update() {
+ if (update.x1 <= update.x2 && update.y1 <= update.y2) {
+ gfx_putblock(update.x1, update.y1, update.x2, update.y2);
+ }
+
+ /* reset update block variables */
+ update.x1 = MAX_INT;
+ update.y1 = MAX_INT;
+ update.x2 = 0;
+ update.y2 = 0;
+}
+
+/**
+ * Updates a block of the framebuffer with contents of the AGI engine screen.
+ * This function updates a block in the output device with the contents of
+ * the AGI engine screen, handling console transparency.
+ * @param x1 x coordinate of the upper left corner of the block
+ * @param y1 y coordinate of the upper left corner of the block
+ * @param x2 x coordinate of the lower right corner of the block
+ * @param y2 y coordinate of the lower right corner of the block
+ *
+ * @see flush_block_a()
+ */
+void flush_block(int x1, int y1, int x2, int y2) {
+ int y, w;
+ uint8 *p0;
+
+ schedule_update(x1, y1, x2, y2);
+
+ p0 = &agi_screen[x1 + y1 * GFX_WIDTH];
+ w = x2 - x1 + 1;
+
+ for (y = y1; y <= y2; y++) {
+ put_pixels(x1, y, w, p0);
+ p0 += GFX_WIDTH;
+ }
+}
+
+/**
+ * Updates a block of the framebuffer receiving AGI picture coordinates.
+ * @param x1 x AGI picture coordinate of the upper left corner of the block
+ * @param y1 y AGI picture coordinate of the upper left corner of the block
+ * @param x2 x AGI picture coordinate of the lower right corner of the block
+ * @param y2 y AGI picture coordinate of the lower right corner of the block
+ *
+ * @see flush_block()
+ */
+void flush_block_a(int x1, int y1, int x2, int y2) {
+ //y1 += 8;
+ //y2 += 8;
+ flush_block(DEV_X0(x1), DEV_Y(y1), DEV_X1(x2), DEV_Y(y2));
+}
+
+/**
+ * Updates the framebuffer with contents of the AGI engine screen (console-aware).
+ * This function updates the output device with the contents of the AGI
+ * screen, handling console transparency.
+ */
+void flush_screen() {
+ flush_block(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
+}
+
+/**
+ * Clear the output device screen (console-aware).
+ * This function clears the output device screen and updates the
+ * output device. Contents of the AGI screen are left untouched. This
+ * function can be used to simulate a switch to a text mode screen in
+ * a graphic-only device.
+ * @param c color to clear the screen
+ */
+void clear_screen(int c) {
+ memset(agi_screen, c, GFX_WIDTH * GFX_HEIGHT);
+ flush_screen();
+}
+
+#ifdef USE_CONSOLE
+/**
+ * Clear the console screen.
+ * This function clears the top n lines of the console screen.
+ * @param n number of lines to clear (in pixels)
+ */
+void clear_console_screen(int n) {
+ memset(console_screen + n * GFX_WIDTH, 0, (GFX_HEIGHT - n) * GFX_WIDTH);
+}
+#endif
+
+/**
+ * Save a block of the AGI engine screen
+ */
+void save_block(int x1, int y1, int x2, int y2, uint8 *b) {
+ uint8 *p0;
+ int w, h;
+
+ p0 = &agi_screen[x1 + GFX_WIDTH * y1];
+ w = x2 - x1 + 1;
+ h = y2 - y1 + 1;
+ while (h--) {
+ memcpy(b, p0, w);
+ b += w;
+ p0 += GFX_WIDTH;
+ }
+}
+
+/**
+ * Restore a block of the AGI engine screen
+ */
+void restore_block(int x1, int y1, int x2, int y2, uint8 *b) {
+ uint8 *p0;
+ int w, h;
+
+ p0 = &agi_screen[x1 + GFX_WIDTH * y1];
+ w = x2 - x1 + 1;
+ h = y2 - y1 + 1;
+ while (h--) {
+ memcpy(p0, b, w);
+ b += w;
+ p0 += GFX_WIDTH;
+ }
+ flush_block(x1, y1, x2, y2);
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/graphics.h b/engines/agi/graphics.h
new file mode 100644
index 0000000000..3931677441
--- /dev/null
+++ b/engines/agi/graphics.h
@@ -0,0 +1,86 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_GRAPHICS_H
+#define __AGI_GRAPHICS_H
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+#define GFX_WIDTH 320
+#define GFX_HEIGHT 200
+#define CHAR_COLS 8
+#define CHAR_LINES 8
+
+extern uint8 palette[];
+
+/* Transparent layer */
+extern uint8 layer1_data[];
+extern uint8 layer2_data[];
+
+void gfx_putpixels(int x, int y, int w, uint8 * p);
+void gfx_putblock(int x1, int y1, int x2, int y2);
+
+void put_text_character(int, int, int, unsigned int, int, int);
+void shake_screen(int);
+void shake_start(void);
+void shake_end(void);
+void save_screen(void);
+void restore_screen(void);
+
+int init_video(void);
+int deinit_video(void);
+void schedule_update(int, int, int, int);
+void do_update(void);
+void put_screen(void);
+void flush_block(int, int, int, int);
+void flush_block_a(int, int, int, int);
+void put_pixels_a(int, int, int, uint8 *);
+void flush_screen(void);
+void clear_screen(int);
+void clear_console_screen(int);
+void draw_box(int, int, int, int, int, int, int);
+void draw_button(int, int, char *, int, int);
+int test_button(int, int, char *);
+void draw_rectangle(int, int, int, int, int);
+void save_block(int, int, int, int, uint8 *);
+void restore_block(int, int, int, int, uint8 *);
+void init_palette(uint8 *);
+
+void put_pixel(int, int, int);
+
+#ifdef USE_HIRES
+void put_pixels_hires(int x, int y, int n, uint8 * p);
+#endif
+int keypress(void);
+int get_key(void);
+void print_character(int, int, char, int, int);
+void poll_timer(void);
+
+} // End of namespace Agi
+
+#endif /* __AGI_GRAPHICS_H */
diff --git a/engines/agi/id.cpp b/engines/agi/id.cpp
new file mode 100644
index 0000000000..28c1c00200
--- /dev/null
+++ b/engines/agi/id.cpp
@@ -0,0 +1,475 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/opcodes.h"
+
+namespace Agi {
+
+/*
+ * Determine what AGI v2 system to emulate, these are the major version
+ * to emulate, thus 2.915 comes under 2.917, 2.4xxx is 2.440, etc.
+ *
+ * 0x2089
+ * 0x2272
+ * 0x2440
+ * 0x2917
+ * 0x2936
+ */
+
+const char *ids_database = "\
+# CRC Int Ver [options] Game name # Comment \n\
+ \n\
+#---------------------------------------------------------------------------- \n\
+# PC-DOS versions \n\
+#---------------------------------------------------------------------------- \n\
+ \n\
+0x484AA 0x2440 AGI Demo 1 (PC) 05/87 [AGI 2.425] # A.K.A. Demo 5 \n\
+0x8CC43 0x2917 AGI Demo 2 (PC 5.25) 11/87 [v1] [AGI 2.915] # Demo 1 \n\
+0x8856C 0x2917 AGI Demo 2 (PC 3.5) 11/87 [AGI 2.915] \n\
+0x843AC 0x2917 AGI Demo 2 (PC 5.25) 01/88 [v2] [AGI 2.917] # Demo 3 \n\
+0x89592 0x3149 AGI Demo 3 (PC) 09/88 [AGI 3.002.102] # Demo 4 \n\
+0x24A18 0x2440 Black Cauldron (PC) 2.00 6/14/87 [AGI 2.439] \n\
+0x22B50 0x3149 Black Cauldron (PC 5.25) 2.10 11/10/88 [AGI 3.002.098] \n\
+0x23E0E 0x3149 Black Cauldron (PC 3.5) 2.10 11/10/88 [AGI 3.002.098] \n\
+0xB5A25 0x3149 Gold Rush! (PC 5.25) 2.01 12/22/88 [AGI 3.002.149] \n\
+0xB1C9E 0x3149 Gold Rush! (PC 3.5) 2.01 12/22/88 [AGI 3.002.149] \n\
+0x49EDA 0x2917 King's Quest 1 (PC 5.25/3.5) 2.0F [AGI 2.917] # also 2.425 \n\
+0x633CB 0x2440 King's Quest 2 (PC 5.25/3.5) 2.1 [AGI 2.411] \n\
+0x63338 0x2917 King's Quest 2 (PC 5.25/3.5) 2.2 [AGI 2.426] # also 2.917 \n\
+0x88673 0x2272 King's Quest 3 (PC) 1.01 11/08/86 [AGI 2.272] \n\
+0x840D3 0x2440 King's Quest 3 (PC 5.25) 2.00 5/25/87 [AGI 2.435] \n\
+0x83191 0x2440 King's Quest 3 (PC 3.5) 2.00 5/25/87 [AGI 2.435] \n\
+0x83695 0x2936 King's Quest 3 (PC 3.5) 2.14 3/15/88 [AGI 2.936] \n\
+0x8410B 0x2936 King's Quest 3 (PC 5.25) 2.14 3/15/88 [AGI 2.936] \n\
+0xB124B 0x3086 King's Quest 4 (PC 3.5) 2.0 7/27/88 [AGI 3.002.086] \n\
+0xB291F 0x3086 King's Quest 4 (PC 3.5) 2.2 9/27/88 [AGI 3.002.086] \n\
+0xB3722 0x3086 King's Quest 4 (PC 5.25) 2.3 9/27/88 [AGI 3.002.086] \n\
+0x9CB15 0x3149 King's Quest 4 demo (PC) [AGI 3.002.102] \n\
+0x6F5E1 0x2440 Leisure Suit Larry 1 (PC 5.25/3.5) 1.00 6/1/87 [AGI 2.440] \n\
+0x4C16D 0x3149 Manhunter NY (PC 5.25) 1.22 8/31/88 [AGI 3.002.107] # also 3.003.102 \n\
+0x49687 0x3149 Manhunter NY (PC 3.5) 1.22 8/31/88 [AGI 3.002.102] \n\
+0x53971 0x3149 Manhunter SF (PC 3.5) 3.02 7/26/89 [AGI 3.002.149] \n\
+0x584F9 0x3149 Manhunter SF (PC 5.25) 3.03 8/17/89 [AGI 3.002.149] \n\
+0x5D77C 0x2917 Mixed-Up Mother Goose (PC) [AGI 2.915] \n\
+0x5D7C6 0x2917 Mixed Up Mother Goose (PC) [AGI 2.915] (Broken) \n\
+0x7F18B 0x2917 Police Quest 1 (PC) 2.0A 10/23/87 [AGI 2.903/2.911] \n\
+0x7EF35 0x2917 Police Quest 1 (PC) 2.0E 11/17/87 [AGI 2.915] \n\
+0x7EF06 0x2917 Police Quest 1 (PC 5.25/ST) 2.0G 12/03/87 [AGI 2.917] \n\
+0x7E0BC 0x2917 Police Quest 1 (PC 3.5) 2.0G 12/03/87 [AGI 2.917] \n\
+0x67FCC 0x2089 Space Quest 1 (PC) 1.0 [AGI 2.089] \n\
+0x68036 0x2089 Space Quest 1 (PC) 1.0X [AGI 2.089] \n\
+0x67F6E 0x2272 Space Quest 1 (PC) 1.1A [AGI 2.272] \n\
+0x68244 0x2440 Space Quest 1 (PC 5.25/3.5) 2.2 [AGI 2.426/2.917] \n\
+0x8DB32 0x2917 Space Quest 2 (PC 5.25) 2.0A [AGI 2.912] \n\
+0x8D825 0x2917 Space Quest 2 (PC 3.5) 2.0A [AGI 2.912] \n\
+0x8DA3E 0x2917 Space Quest 2 (PC 5.25/ST) 2.0C/A [AGI 2.915] \n\
+0x8E6A7 0x2917 Space Quest 2 (PC 3.5) 2.0C/B [AGI 2.917] \n\
+0x8E29B 0x2936 Space Quest 2 (PC 3.5) 2.0F [AGI 2.936] \n\
+0x8DF84 0x2936 Space Quest 2 (PC 5.25) 2.0D [AGI 2.936] \n\
+0x8DE46 0x2936 Space Quest 2 (PC 3.5) 2.0D [AGI 2.936] \n\
+0x8E310 0x2936 Space Quest 2 (PC 5.25) 2.0F [AGI 2.936] \n\
+0x31677 0x2272 Xmas Card 1986 (PC) [AGI 2.272] \n\
+ \n\
+#---------------------------------------------------------------------------- \n\
+# Apple //gs versions \n\
+# all guessed interpreter versions \n\
+# \n\
+# Notes: \n\
+# - (CE) in Apple IIgs versions stands for Carlos Escobar --PDD \n\
+#---------------------------------------------------------------------------- \n\
+ \n\
+0x93260 0x2917 AGI Demo 2 (IIgs) 1.0C (Censored) \n\
+0x285FB 0x3149 Black Cauldron (Apple IIgs) 1.0O 2/24/89 (CE) # 2.24.89 (CE) \n\
+0xB6F67 0x3149 Gold Rush! (Apple IIgs) 1.0M 2/28/89 (CE) aka 2.01 12/22/88 \n\
+0x4A9E8 0x2272 King's Quest 1 (IIgs) 1.0S-88223 \n\
+0x79D1B 0x2917 King's Quest 2 (IIgs) 2.0A 6/16/88 (CE) \n\
+0x85CD4 0x2917 King's Quest 3 (IIgs) 2.0A 8/28/88 (CE) \n\
+0xAF778 0x3086 King's Quest 4 (IIgs) 1.0K 11/22/88 (CE) \n\
+0x6E41E 0x2440 Leisure Suit Larry 1 (IIgs) 1.0E \n\
+0x4C705 0x3149 Manhunter NY (IIgs) 2.0E 10/05/88 (CE) \n\
+0x5F4E8 0x2917 Mixed Up Mother Goose (IIgs) \n\
+0x7DB3F 0x2917 Police Quest 1 (IIgs) 2.0A-88318 \n\
+0x7DBE5 0x2917 Police Quest 1 (IIgs) 2.0B-88421 \n\
+0x69EC0 0x2917 Space Quest 1 (IIgs) 2.2 \n\
+0x8E983 0x2936 Space Quest 2 (IIgs) 2.0A 7/25/88 (CE) \n\
+ \n\
+#---------------------------------------------------------------------------- \n\
+# Macintosh versions \n\
+# all guessed interpreter versions \n\
+#---------------------------------------------------------------------------- \n\
+ \n\
+0x4C02C 0x2440 King's Quest 1 (Mac) 2.0C \n\
+0x6382E 0x2440 King's Quest 2 (Mac) 2.0R \n\
+0x8410B 0x2440 King's Quest 3 (Mac) 2.14 3/15/88 \n\
+0x78202 0x2440 Leisure Suit Larry 1 (Mac) 1.05 6/26/87 \n\
+0x7EF06 0x2440 Police Quest 1 (Mac) 2.0G 12/3/87 \n\
+0x6A277 0x2440 Space Quest 1 (Mac) 1.5D \n\
+0x8DF84 0x2936 Space Quest 2 (Mac) 2.0D \n\
+ \n\
+#---------------------------------------------------------------------------- \n\
+# Atari ST versions \n\
+# all guessed interpreter versions \n\
+# \n\
+# Notes: \n\
+# - Chris Iden wrote the Atari ST port of AGI --PDD \n\
+#---------------------------------------------------------------------------- \n\
+ \n\
+0x1A7AF 0x2272 Donald Duck's Playground (ST) 1.0A 8/8/86 \n\
+0x4A079 0x2272 King's Quest 1 (ST) 1.0V \n\
+0x882B7 0x2272 King's Quest 3 (ST) 1.02 11/18/86 \n\
+0xB68AB 0x3149 Gold Rush! (ST) 1.01 1/13/89 aka 2.01 12/22/88 \n\
+0x6F7E1 0x2440 Leisure Suit Larry 1 (ST) 1.04 6/18/87 \n\
+0x4CE19 0x3149 Manhunter NY (ST) 1.03 10/20/88 \n\
+0x5360B 0x3149 Manhunter SF (ST) 1.0 7/29/89 \n\
+0x69597 0x2440 Space Quest 1 (ST) 1.1A \n\
+ \n\
+#---------------------------------------------------------------------------- \n\
+# Amiga versions \n\
+# Use option -A- to enable padding \n\
+# \n\
+# Notes: \n\
+# - Amiga KQ3 (2.333) seems to need interpreter version 3.002.086 \n\
+#---------------------------------------------------------------------------- \n\
+ \n\
+0x25640 0x2440 [A] Black Cauldron (Amiga) 2.00 6/14/87 # guessed int \n\
+0x1AFBA 0x2272 [A] Donald Duck's Playground (Amiga) 1.0C # guessed int \n\
+0xB3E1A 0x3149 [A] Gold Rush! (Amiga) 1.01 1/13/89 aka 2.05 3/9/89 # 2.316 \n\
+0x49C6B 0x2440 [A] King's Quest 1 (Amiga) 1.0U # 2.082 \n\
+0x5D395 0x2440 [A] King's Quest 2 (Amiga) 2.0J # guessed int \n\
+0x5BCE6 0x2440 [A] King's Quest 2 (Amiga) 2.0J (Broken) \n\
+0x5F4B9 0x2440 [A] King's Quest 2 (Amiga) 2.0J (Broken) # 2.176 \n\
+0x888C1 0x2440 [A] King's Quest 3 (Amiga) 1.01 11/8/86 \n\
+0x84793 0x3086 [A] King's Quest 3 (Amiga) 2.15 11/15/89 # 2.333 \n\
+0x6FDDB 0x2440 [A] Leisure Suit Larry 1 (Amiga) 1.05 6/26/87 # x.yyy \n\
+0x4BA94 0x3149 [A] Manhunter NY (Amiga) 1.06 3/18/89 # x.yyy \n\
+0x53D51 0x3086 [A] Manhunter SF (Amiga) 3.06 8/17/89 # 2.333 \n\
+0x5CFB1 0x3086 [A] Mixed-Up Mother Goose (Amiga) 1.1 # guessed int \n\
+0x7F752 0x3149 [A] Police Quest 1 (Amiga) 2.0B 2/22/89 # 2.310 \n\
+0x696DD 0x2440 [A] Space Quest 1 (Amiga) 1.2 # 2.082 \n\
+0x8FEA6 0x2936 [A] Space Quest 2 (Amiga) 2.0F # 2.202 \n\
+ \n\
+#---------------------------------------------------------------------------- \n\
+# CoCo versions \n\
+# what version of DOS AGI does CoCo 2.023 correspond with? \n\
+# guessing 2.272 because the PC version 1.0 is 2.272; doesn't fit date though \n\
+# \n\
+# Notes: \n\
+# - Chris Iden wrote the CoCo port of AGI --PDD \n\
+#---------------------------------------------------------------------------- \n\
+ \n\
+0x7CBE8 0x2272 King's Quest 3 (CoCo3) 1.0C 6/27/88 # 2.023 \n\
+0x70D35 0x2440 Leisure Suit Larry 1 (CoCo3) \n\
+ \n\
+#---------------------------------------------------------------------------- \n\
+# AGDS games \n\
+# Use option -a- for AGDS games \n\
+#---------------------------------------------------------------------------- \n\
+ \n\
+0x5501A 0x2440 [a] Groza # AGDS sample game \n\
+ \n\
+#---------------------------------------------------------------------------- \n\
+# Fan-made AGI games \n\
+#---------------------------------------------------------------------------- \n\
+ \n\
+0x3F2F7 0x2917 [m] AGI Mouse 0.7 Demo \n\
+0x3F744 0x2917 [m] AGI Mouse 1.0 Demo # 2.917 6/24/00 \n\
+0x3F74F 0x2917 [m] AGI Mouse 1.1 Demo # 2.917 1/01/01 \n\
+0x17599 0x2917 [m] Sliding Tile Game v1.00 # 2.917 6/02/01 \n\
+0x785c4 0x2936 [m] Jolimie v0.6 # 2.936 2000 \n\
+#Jolimie uses AGIPal only and not AGIMouse; no way to separate these currently \n\
+0x40D80 0x2440 AGI Trek # 2.440 9/21/98 \n\
+0x64CB7 0x2440 Space Trek 1.0 # 2.440 12/13/98 \n\
+0x6596A 0x2917 Space Trek (remake) # 2.917 6/09/99 \n\
+0x96909 0x2917 Operation: RECON teaser 1.1 # \n\
+0x185A6 0x2917 AGI Piano v1.0 # ? 1998 \n\
+0x91ACF 0x2917 Dave's Quest .07 # ? \n\
+0x620F6 0x2917 Time Quest demo D0.2 # ? 1998 \n\
+0x7466F 0x2917 Tex McPhilip I # ? 2000 \n\
+0x9E400 0x2917 Tex McPhilip II # ? 2000 \n\
+0xAB9A8 0x2917 Justin Quest 1.0 # 2.917 \n\
+0x7D473 0x2917 The Ruby Cast demo 0.2 # 2.917 1998 \n\
+0xB4D7A 0x2917 Residence 44 Quest 1.0a # 2.917 1999 \n\
+0x5D077 0x2917 Escape Quest demo # ? 1998 \n\
+0x5A434 0x2917 Acidopolis (1.0) demo # ? \n\
+0x45CDF 0x2917 Go West, Young Hippie demo # 2.917 \n\
+0x4C9DC 0x2917 Speeder Bike Challenge v1.0 # ? \n\
+0x112BF9 0x2917 Space Quest 0: Replicated 1.04 # 6/27/2003 \n\
+0x6E70F 0x2917 Space Quest: The Lost Chapter v10.0 \n\
+0x5859E 0x2917 Phantasmagoria \n\
+0x7B5DF 0x2917 Dashiki demo \n\
+0x9405B 0x2917 Dashiki 256-color demo (Unsupported) \n\
+0x6ADCD 0x2917 Jen's Quest 0.1 demo \n\
+0x4EE64 0x2917 Monkey Man \n\
+";
+
+int setup_v2_game(int ver, uint32 crc);
+int setup_v3_game(int ver, uint32 crc);
+int v4id_game(uint32 crc);
+
+uint32 match_crc(uint32 crc, char *name, int len) {
+ char *c, *t, buf[256];
+ uint32 id, ver;
+
+ Common::MemoryReadStream f((const byte *)ids_database, strlen(ids_database));
+
+ while (!f.eos()) {
+ f.readLine(buf, 256);
+ c = strchr(buf, '#');
+ if (c)
+ *c = 0;
+
+ /* Remove spaces at end of line */
+ if (strlen(buf)) {
+ for (c = buf + strlen(buf) - 1;
+ *c == ' ' || *c == '\t'; *c-- = 0) {
+ }
+ }
+
+ t = strtok(buf, " \t\r\n");
+ if (t == NULL)
+ continue;
+ id = strtoul(t, NULL, 0);
+
+ t = strtok(NULL, " \t\r\n");
+ if (t == NULL)
+ continue;
+ ver = strtoul(t, NULL, 0);
+
+ t = strtok(NULL, "\n\r");
+ for (; *t == ' ' || *t == '\t'; t++);
+
+ if (id == crc) {
+ /* Now we must check options enclosed in brackets
+ * like [A] for Amiga
+ */
+
+ if (*t == '[') {
+ while (*t != ']') {
+ switch (*t++) {
+ case 'A':
+ opt.amiga = true;
+ break;
+ case 'a':
+ opt.agds = true;
+ break;
+#ifdef USE_MOUSE
+ case 'm':
+ opt.agimouse = true;
+ break;
+#endif
+ }
+ }
+ t++;
+
+ for (; (*t == ' ' || *t == '\t') && *t; t++) {
+ }
+ }
+
+ strncpy(name, t, len);
+ return ver;
+ }
+ }
+
+ return 0;
+}
+
+static uint32 match_version(uint32 crc) {
+ int ver;
+ char name[80];
+
+ if ((ver = match_crc(crc, name, 80)) > 0)
+ report("AGI game detected: %s\n\n", name);
+
+ return ver;
+}
+
+int v2id_game() {
+ int y, ver;
+ uint32 len, c, crc;
+ uint8 *buff;
+ Common::File fp;
+ char *fn[] = { "viewdir", "logdir", "picdir", "snddir", "words.tok", "object", "" };
+
+ buff = (uint8 *)malloc(8192);
+
+ for (crc = y = 0; fn[y][0]; y++) {
+ char *path = fn[y];
+ if (fp.open(path)) {
+ for (len = 1; len > 0;) {
+ memset(buff, 0, 8192);
+ len = fp.read(buff, 8000);
+ for (c = 0; c < len; c++)
+ crc += *(buff + c);
+ }
+ fp.close();
+ }
+ }
+ free(buff);
+
+ report("Computed CRC: 0x%05x\n", crc);
+ ver = match_version(crc);
+ game.crc = crc;
+ game.ver = ver;
+ debugC(2, kDebugLevelMain, "game.ver = 0x%x", game.ver);
+ agi_set_release(ver);
+ return setup_v2_game(ver, crc);
+}
+
+/*
+ * Currently, there is no known difference between v3.002.098 -> v3.002.149
+ * So version emulated;
+ *
+ * 0x0086,
+ * 0x0149
+ */
+
+int v3id_game() {
+ int ec = err_OK, y, ver;
+ uint32 len, c, crc;
+ uint8 *buff;
+ Common::File fp;
+ char *fn[] = { "words.tok", "object", "" };
+ Common::String path;
+
+ buff = (uint8 *)malloc(8192);
+
+ for (crc = 0, y = 0; fn[y][0] != 0x0; y++) {
+ path = fn[y];
+ if (fp.open(path)) {
+ len = 1;
+ while (len > 0) {
+ memset(buff, 0, 8192);
+ len = fp.read(buff, 8000);
+ for (c = 0; c < len; c++)
+ crc += *(buff + c);
+ }
+ fp.close();
+ }
+ }
+
+ /* now do the directory file */
+
+ path = Common::String(game.name) + DIR_;
+
+ if (fp.open(path)) {
+ for (len = 1; len > 0;) {
+ memset(buff, 0, 8192);
+ len = fp.read(buff, 8000);
+ for (c = 0; c < len; c++)
+ crc += *(buff + c);
+ }
+ fp.close();
+ }
+
+ free(buff);
+
+ report("Computed CRC: 0x%05x\n", crc);
+ ver = match_version(crc);
+ game.crc = crc;
+ game.ver = ver;
+ agi_set_release(ver);
+
+ ec = setup_v3_game(ver, crc);
+
+ return ec;
+}
+
+/**
+ *
+ */
+int setup_v2_game(int ver, uint32 crc) {
+ int ec = err_OK;
+
+ if (ver == 0) {
+ report("Unknown v2 Sierra game: %08x\n\n", crc);
+ agi_set_release(0x2917);
+ }
+
+ /* setup the differences in the opcodes and other bits in the
+ * AGI v2 specs
+ */
+ if (opt.emuversion)
+ agi_set_release(opt.emuversion);
+
+ if (opt.agds)
+ agi_set_release(0x2440); /* ALL AGDS games built for 2.440 */
+
+ switch (agi_get_release()) {
+ case 0x2089:
+ logic_names_cmd[0x86].num_args = 0; /* quit: 0 args */
+ logic_names_cmd[0x97].num_args = 3; /* print.at: 3 args */
+ logic_names_cmd[0x98].num_args = 3; /* print.at.v: 3 args */
+ break;
+ case 0x2272:
+ /* KQ3 0x88673 (2.272) requires print.at with 4 arguments */
+ break;
+ case 0x2440:
+ break;
+ case 0x2917:
+ break;
+ case 0x2936:
+ break;
+ default:
+ report("** Cannot setup for unknown version\n");
+ ec = err_UnknownAGIVersion;
+ break;
+ }
+
+ return ec;
+}
+
+/**
+ *
+ */
+int setup_v3_game(int ver, uint32 crc) {
+ int ec = err_OK;
+
+ if (ver == 0) {
+ report("Unknown v3 Sierra game: %08x\n\n", crc);
+ agi_set_release(ver = 0x3149);
+ }
+
+ if (opt.emuversion)
+ agi_set_release(ver = opt.emuversion);
+
+ switch (ver) {
+ case 0x3086:
+ logic_names_cmd[0xad].num_args = 1; /* 173 : 1 args */
+ break;
+ case 0x3149:
+ logic_names_cmd[0xad].num_args = 0; /* 173 : 0 args */
+ break;
+ default:
+ report("Error: cannot setup for unknown version\n");
+ ec = err_UnknownAGIVersion;
+ break;
+ }
+
+ return ec;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/inv.cpp b/engines/agi/inv.cpp
new file mode 100644
index 0000000000..4d4014027e
--- /dev/null
+++ b/engines/agi/inv.cpp
@@ -0,0 +1,212 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/sprite.h"
+#include "agi/graphics.h"
+#include "agi/keyboard.h"
+#include "agi/text.h"
+#include "agi/keyboard.h"
+
+namespace Agi {
+
+/*
+ * Messages and coordinates
+ */
+
+#define NOTHING_X 16
+#define NOTHING_Y 3
+#define NOTHING_MSG "nothing"
+
+#define ANY_KEY_X 4
+#define ANY_KEY_Y 24
+#define ANY_KEY_MSG "Press a key to return to the game"
+
+#define YOUHAVE_X 11
+#define YOUHAVE_Y 0
+#define YOUHAVE_MSG "You are carrying:"
+
+#define SELECT_X 2
+#define SELECT_Y 24
+#define SELECT_MSG "Press ENTER to select, ESC to cancel"
+
+static uint8 *intobj = NULL;
+
+static void print_item(int n, int fg, int bg)
+{
+ print_text(object_name(intobj[n]), 0, n % 2 ? 39 - strlen(object_name(intobj[n])) : 1,
+ (n / 2) + 2, 40, fg, bg);
+}
+
+#ifdef USE_MOUSE
+static int find_item() {
+ int r, c;
+
+ r = mouse.y / CHAR_LINES;
+ c = mouse.x / CHAR_COLS;
+
+ debugC(6, kDebugLevelInventory, "r = %d, c = %d", r, c);
+
+ if (r < 2)
+ return -1;
+
+ return (r - 2) * 2 + (c > 20);
+}
+#endif
+
+static int show_items() {
+ unsigned int x, i;
+
+ for (x = i = 0; x < game.num_objects; x++) {
+ if (object_get_location(x) == EGO_OWNED) {
+ /* add object to our list! */
+ intobj[i] = x;
+ print_item(i, STATUS_FG, STATUS_BG);
+ i++;
+ }
+ }
+
+ if (i == 0) {
+ print_text(NOTHING_MSG, 0, NOTHING_X, NOTHING_Y, 40, STATUS_FG, STATUS_BG);
+ }
+
+ return i;
+}
+
+static void select_items(int n) {
+ int fsel = 0;
+
+ while (42) {
+ if (n > 0)
+ print_item(fsel, STATUS_BG, STATUS_FG);
+
+ switch (wait_any_key()) {
+ case KEY_ENTER:
+ setvar(V_sel_item, intobj[fsel]);
+ goto exit_select;
+ case KEY_ESCAPE:
+ setvar(V_sel_item, 0xff);
+ goto exit_select;
+ case KEY_UP:
+ if (fsel >= 2)
+ fsel -= 2;
+ break;
+ case KEY_DOWN:
+ if (fsel + 2 < n)
+ fsel += 2;
+ break;
+ case KEY_LEFT:
+ if (fsel % 2 == 1)
+ fsel--;
+ break;
+ case KEY_RIGHT:
+ if (fsel % 2 == 0 && fsel + 1 < n)
+ fsel++;
+ break;
+#ifdef USE_MOUSE
+ case BUTTON_LEFT:{
+ int i = find_item();
+ if (i >= 0 && i < n) {
+ setvar(V_sel_item, intobj[fsel = i]);
+ debugC(6, kDebugLevelInventory, "item found: %d", fsel);
+ show_items();
+ print_item(fsel, STATUS_BG, STATUS_FG);
+ do_update();
+ goto exit_select;
+ }
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+
+ show_items();
+ do_update();
+ }
+
+ exit_select:
+ debugC(6, kDebugLevelInventory, "selected: %d", fsel);
+}
+
+/*
+ * Public functions
+ */
+
+/**
+ * Display inventory items.
+ */
+void inventory() {
+ int old_fg, old_bg;
+ int n;
+
+ /* screen is white with black text */
+ old_fg = game.color_fg;
+ old_bg = game.color_bg;
+ game.color_fg = 0;
+ game.color_bg = 15;
+ clear_screen(game.color_bg);
+
+ print_text(YOUHAVE_MSG, 0, YOUHAVE_X, YOUHAVE_Y, 40, STATUS_FG, STATUS_BG);
+
+ /* FIXME: doesn't check if objects overflow off screen... */
+
+ intobj = (uint8 *) malloc(4 + game.num_objects);
+ memset(intobj, 0, (4 + game.num_objects));
+
+ n = show_items();
+
+ if (getflag(F_status_selects_items)) {
+ print_text(SELECT_MSG, 0, SELECT_X, SELECT_Y, 40, STATUS_FG, STATUS_BG);
+ } else {
+ print_text(ANY_KEY_MSG, 0, ANY_KEY_X, ANY_KEY_Y, 40, STATUS_FG, STATUS_BG);
+ }
+
+ flush_screen();
+
+ /* If flag 13 is set, we want to highlight & select an item.
+ * opon selection, put objnum in var 25. Then on esc put in
+ * var 25 = 0xff.
+ */
+
+ if (getflag(F_status_selects_items))
+ select_items(n);
+
+ free(intobj);
+
+ if (!getflag(F_status_selects_items))
+ wait_any_key();
+
+ clear_screen(0);
+ write_status();
+ show_pic();
+ game.color_fg = old_fg;
+ game.color_bg = old_bg;
+ game.has_prompt = 0;
+ flush_lines(game.line_user_input, 24);
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp
new file mode 100644
index 0000000000..998b95f1cd
--- /dev/null
+++ b/engines/agi/keyboard.cpp
@@ -0,0 +1,388 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/graphics.h"
+#include "agi/keyboard.h"
+#include "agi/menu.h"
+#include "agi/text.h" /* remove later */
+
+namespace Agi {
+
+char last_sentence[40];
+
+#ifdef USE_CONSOLE
+extern struct agi_console console;
+#endif
+
+/* FIXME */
+extern int open_dialogue;
+
+struct string_data {
+ int x;
+ int y;
+ int len;
+ int str;
+};
+
+struct string_data stringdata;
+
+/*
+ * IBM-PC keyboard scancodes
+ */
+uint8 scancode_table[26] = {
+ 30, /* A */
+ 48, /* B */
+ 46, /* C */
+ 32, /* D */
+ 18, /* E */
+ 33, /* F */
+ 34, /* G */
+ 35, /* H */
+ 23, /* I */
+ 36, /* J */
+ 37, /* K */
+ 38, /* L */
+ 50, /* M */
+ 49, /* N */
+ 24, /* O */
+ 25, /* P */
+ 16, /* Q */
+ 19, /* R */
+ 31, /* S */
+ 20, /* T */
+ 22, /* U */
+ 47, /* V */
+ 17, /* W */
+ 45, /* X */
+ 21, /* Y */
+ 44 /* Z */
+};
+
+void init_words() {
+ game.num_ego_words = 0;
+}
+
+void clean_input() {
+ while (game.num_ego_words)
+ free(game.ego_words[--game.num_ego_words].word);
+}
+
+void get_string(int x, int y, int len, int str) {
+ new_input_mode(INPUT_GETSTRING);
+ stringdata.x = x;
+ stringdata.y = y;
+ stringdata.len = len;
+ stringdata.str = str;
+}
+
+/**
+ * Raw key grabber.
+ * poll_keyboard() is the raw key grabber (above the gfx driver, that is).
+ * It handles console keys and insulates AGI from the console. In the main
+ * loop, handle_keys() handles keyboard input and ego movement.
+ */
+int do_poll_keyboard() {
+ int key = 0;
+
+ /* If a key is ready, rip it */
+ if (keypress()) {
+ key = get_key();
+ debugC(3, kDebugLevelInput, "key %02x pressed", key);
+ }
+
+ return key;
+}
+
+int handle_controller(int key) {
+ struct vt_entry *v = &game.view_table[0];
+ int i;
+
+ /* The Black Cauldron needs KEY_ESCAPE to use menus */
+ if (key == 0 /*|| key == KEY_ESCAPE */ )
+ return false;
+
+ debugC(3, kDebugLevelInput, "key = %04x", key);
+
+ for (i = 0; i < MAX_DIRS; i++) {
+ if (game.ev_keyp[i].data == key) {
+ debugC(3, kDebugLevelInput, "event %d: key press", i);
+ game.ev_keyp[i].occured = true;
+ report("event AC:%i occured\n", i);
+ return true;
+ }
+ }
+
+#ifdef USE_MOUSE
+ if (key == BUTTON_LEFT) {
+ if (getflag(F_menus_work) && mouse.y <= CHAR_LINES) {
+ new_input_mode(INPUT_MENU);
+ return true;
+ }
+ }
+#endif
+
+ if (game.player_control) {
+ int d = 0;
+
+ if (!KEY_ASCII(key)) {
+ switch (key) {
+ case KEY_UP:
+ d = 1;
+ break;
+ case KEY_DOWN:
+ d = 5;
+ break;
+ case KEY_LEFT:
+ d = 7;
+ break;
+ case KEY_RIGHT:
+ d = 3;
+ break;
+ case KEY_UP_RIGHT:
+ d = 2;
+ break;
+ case KEY_DOWN_RIGHT:
+ d = 4;
+ break;
+ case KEY_UP_LEFT:
+ d = 8;
+ break;
+ case KEY_DOWN_LEFT:
+ d = 6;
+ break;
+ }
+ }
+#ifdef USE_MOUSE
+ if (!opt.agimouse) {
+ /* Handle mouse button events */
+ if (key == BUTTON_LEFT) {
+ v->flags |= ADJ_EGO_XY;
+ v->parm1 = WIN_TO_PIC_X(mouse.x);
+ v->parm2 = WIN_TO_PIC_Y(mouse.y);
+ return true;
+ }
+ }
+#endif
+
+ v->flags &= ~ADJ_EGO_XY;
+
+ if (d || key == KEY_STATIONARY) {
+ v->direction = v->direction == d ? 0 : d;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void handle_getstring(int key) {
+ static int pos = 0; /* Cursor position */
+ static char buf[40];
+
+ if (KEY_ASCII(key) == 0)
+ return;
+
+ debugC(3, kDebugLevelInput, "handling key: %02x", key);
+
+ switch (key) {
+ case KEY_ENTER:
+ debugC(3, kDebugLevelInput, "KEY_ENTER");
+ game.has_prompt = 0;
+ buf[pos] = 0;
+ strcpy(game.strings[stringdata.str], buf);
+ debugC(3, kDebugLevelInput, "buffer=[%s]", buf);
+ buf[pos = 0] = 0;
+ new_input_mode(INPUT_NORMAL);
+ print_character(stringdata.x + strlen(game.strings[stringdata.str]) + 1,
+ stringdata.y, ' ', game.color_fg, game.color_bg);
+ return;
+ case KEY_ESCAPE:
+ debugC(3, kDebugLevelInput, "KEY_ESCAPE");
+ game.has_prompt = 0;
+ buf[pos = 0] = 0;
+ strcpy(game.strings[stringdata.str], buf);
+ new_input_mode(INPUT_NORMAL);
+ /* new_input_mode (INPUT_MENU); */
+ break;
+ case KEY_BACKSPACE: /*0x08: */
+ if (!pos)
+ break;
+
+ print_character(stringdata.x + (pos + 1), stringdata.y,
+ ' ', game.color_fg, game.color_bg);
+
+ pos--;
+ buf[pos] = 0;
+ break;
+ default:
+ if (key < 0x20 || key > 0x7f)
+ break;
+
+ if (pos >= stringdata.len)
+ break;
+
+ buf[pos++] = key;
+ buf[pos] = 0;
+
+ /* Echo */
+ print_character(stringdata.x + pos, stringdata.y, buf[pos - 1],
+ game.color_fg, game.color_bg);
+
+ break;
+ }
+
+ /* print cursor */
+ print_character(stringdata.x + pos + 1, stringdata.y,
+ (char)game.cursor_char, game.color_fg, game.color_bg);
+}
+
+void handle_keys(int key) {
+ uint8 *p = NULL;
+ int c = 0;
+ static uint8 formated_entry[256];
+ int l = game.line_user_input;
+ int fg = game.color_fg, bg = game.color_bg;
+
+ setvar(V_word_not_found, 0);
+
+ debugC(3, kDebugLevelInput, "handling key: %02x", key);
+
+ switch (key) {
+ case KEY_ENTER:
+ debugC(3, kDebugLevelInput, "KEY_ENTER");
+ game.keypress = 0;
+
+ /* Remove all leading spaces */
+ for (p = game.input_buffer; *p && *p == 0x20; p++);
+
+ /* Copy to internal buffer */
+ for (; *p; p++) {
+ /* Squash spaces */
+ if (*p == 0x20 && *(p + 1) == 0x20) {
+ p++;
+ continue;
+ }
+ formated_entry[c++] = tolower(*p);
+ }
+ formated_entry[c++] = 0;
+
+ /* Handle string only if it's not empty */
+ if (formated_entry[0]) {
+ strcpy((char *)game.echo_buffer, (const char *)game.input_buffer);
+ strcpy(last_sentence, (const char *)formated_entry);
+ dictionary_words(last_sentence);
+ }
+
+ /* Clear to start a new line */
+ game.has_prompt = 0;
+ game.input_buffer[game.cursor_pos = 0] = 0;
+ debugC(3, kDebugLevelInput, "clear lines");
+ clear_lines(l, l + 1, bg);
+ flush_lines(l, l + 1);
+
+ break;
+ case KEY_ESCAPE:
+ debugC(3, kDebugLevelInput, "KEY_ESCAPE");
+ new_input_mode(INPUT_MENU);
+ break;
+ case KEY_BACKSPACE:
+ /* Ignore backspace at start of line */
+ if (game.cursor_pos == 0)
+ break;
+
+ /* erase cursor */
+ print_character(game.cursor_pos + 1, l, ' ', fg, bg);
+ game.input_buffer[--game.cursor_pos] = 0;
+ /* Print cursor */
+ print_character(game.cursor_pos + 1, l, game.cursor_char, fg, bg);
+ break;
+ default:
+ /* Ignore invalid keystrokes */
+ if (key < 0x20 || key > 0x7f)
+ break;
+
+ /* Maximum input size reached */
+ if (game.cursor_pos >= getvar(V_max_input_chars))
+ break;
+
+ game.input_buffer[game.cursor_pos++] = key;
+ game.input_buffer[game.cursor_pos] = 0;
+
+ /* echo */
+ print_character(game.cursor_pos, l, game.input_buffer[game.cursor_pos - 1], fg, bg);
+
+ /* Print cursor */
+ print_character(game.cursor_pos + 1, l, game.cursor_char, fg, bg);
+ break;
+ }
+}
+
+int wait_key() {
+ int key;
+
+ /* clear key queue */
+ while (keypress()) {
+ get_key();
+ }
+
+ debugC(3, kDebugLevelInput, "waiting...");
+ while (42) {
+ poll_timer(); /* msdos driver -> does nothing */
+ key = do_poll_keyboard();
+ if (!console_keyhandler(key)) {
+ if (key == KEY_ENTER || key == KEY_ESCAPE
+#ifdef USE_MOUSE
+ || key == BUTTON_LEFT
+#endif
+ )
+ break;
+ }
+ console_cycle();
+ }
+ return key;
+}
+
+int wait_any_key() {
+ int key;
+
+ /* clear key queue */
+ while (keypress()) {
+ get_key();
+ }
+
+ debugC(3, kDebugLevelInput, "waiting...");
+ while (42) {
+ poll_timer(); /* msdos driver -> does nothing */
+ key = do_poll_keyboard();
+ if (!console_keyhandler(key) && key)
+ break;
+ console_cycle();
+ }
+ return key;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/keyboard.h b/engines/agi/keyboard.h
new file mode 100644
index 0000000000..7e507e5f97
--- /dev/null
+++ b/engines/agi/keyboard.h
@@ -0,0 +1,94 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_KEYBOARD_H
+#define __AGI_KEYBOARD_H
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+/* QNX4 has a KEY_DOWN defined which we don't need to care about */
+#undef KEY_DOWN
+
+/* Allegro defines these */
+#undef KEY_BACKSPACE
+#undef KEY_ENTER
+#undef KEY_LEFT
+#undef KEY_RIGHT
+#undef KEY_UP
+#undef KEY_PGUP
+#undef KEY_PGDN
+#undef KEY_HOME
+#undef KEY_END
+
+#define KEY_BACKSPACE 0x08
+#define KEY_ESCAPE 0x1B
+#define KEY_ENTER 0x0D
+#define KEY_UP 0x4800
+#define KEY_DOWN 0x5000
+#define KEY_LEFT 0x4B00
+#define KEY_STATIONARY 0x4C00
+#define KEY_RIGHT 0x4D00
+
+#define KEY_DOWN_LEFT 0x4F00
+#define KEY_DOWN_RIGHT 0x5100
+#define KEY_UP_LEFT 0x4700
+#define KEY_UP_RIGHT 0x4900
+
+#define KEY_STATUSLN 0xd900 /* F11 */
+#define KEY_PRIORITY 0xda00 /* F12 */
+
+#define KEY_PGUP 0x4900 /* Page Up (fixed by Ziv Barber) */
+#define KEY_PGDN 0x5100 /* Page Down */
+#define KEY_HOME 0x4700 /* Home */
+#define KEY_END 0x4f00 /* End * */
+
+#ifdef USE_MOUSE
+#define BUTTON_LEFT 0xF101 /* Left mouse button */
+#define BUTTON_RIGHT 0xF202 /* Right mouse button */
+#endif
+
+#define KEY_SCAN(k) (k >> 8)
+#define KEY_ASCII(k) (k & 0xff)
+
+extern uint8 scancode_table[];
+
+void init_words(void);
+void clean_input(void);
+int do_poll_keyboard(void);
+void clean_keyboard(void);
+void handle_keys(int);
+void handle_getstring(int);
+int handle_controller(int);
+void get_string(int, int, int, int);
+uint16 agi_get_keypress(void);
+int wait_key(void);
+int wait_any_key(void);
+
+} // End of namespace Agi
+
+#endif /* __AGI_KEYBOARD_H */
diff --git a/engines/agi/list.h b/engines/agi/list.h
new file mode 100644
index 0000000000..999c591ce0
--- /dev/null
+++ b/engines/agi/list.h
@@ -0,0 +1,140 @@
+/*
+ * $Id$
+ *
+ * List management macros from the Linux kernel
+ */
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+/**
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static INLINE void __list_add(struct list_head *tnew, struct list_head *prev, struct list_head *next) {
+ next->prev = tnew;
+ tnew->next = next;
+ tnew->prev = prev;
+ prev->next = tnew;
+}
+
+/**
+ * add a new entry
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * @param new new entry to be added
+ * @param head list head to add it after
+ */
+static INLINE void list_add(struct list_head *tnew, struct list_head *head) {
+ __list_add(tnew, head, head->next);
+}
+
+/**
+ * add a new entry
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * @new: new entry to be added
+ * @head: list head to add it before
+ */
+static INLINE void list_add_tail(struct list_head *tnew, struct list_head *head) {
+ __list_add(tnew, head->prev, head);
+}
+
+/**
+ * Delete a list entry (makes prev/next entries point to each other)
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static INLINE void __list_del(struct list_head *prev, struct list_head *next) {
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * deletes entry from list.
+ * @param entry the element to delete from the list.
+ */
+static INLINE void list_del(struct list_head *entry) {
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * tests whether a list is empty
+ * @param head the list to test.
+ */
+static INLINE int list_empty(struct list_head *head) {
+ return head->next == head;
+}
+
+/**
+ * join two lists
+ * @param list the new list to add.
+ * @param head the place to add it in the first list.
+ */
+static INLINE void list_splice(struct list_head *list, struct list_head *head) {
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+/**
+ * get the struct for this entry
+ * @param ptr the &struct list_head pointer.
+ * @param type the type of the struct this is embedded in.
+ * @param member the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * iterate over a list
+ * @param pos the &struct list_head to use as a loop counter.
+ * @param head the head for your list.
+ */
+#define list_for_each(pos, head, next) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+} // End of namespace Agi
+
+#endif
diff --git a/engines/agi/logic.cpp b/engines/agi/logic.cpp
new file mode 100644
index 0000000000..1929b3fb3c
--- /dev/null
+++ b/engines/agi/logic.cpp
@@ -0,0 +1,116 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+/**
+ * Decode logic resource
+ * This function decodes messages from the specified raw logic resource
+ * into a message list.
+ * @param n The number of the logic resource to decode.
+ */
+int decode_logic(int n) {
+ int ec = err_OK;
+ int mstart, mend, mc;
+ uint8 *m0;
+
+ /* decrypt messages at end of logic + build message list */
+
+ /* report ("decoding logic #%d\n", n); */
+ m0 = game.logics[n].data;
+
+ mstart = READ_LE_UINT16(m0) + 2;
+ mc = *(m0 + mstart);
+ mend = READ_LE_UINT16(m0 + mstart + 1);
+ m0 += mstart + 3; /* cover header info */
+ mstart = mc << 1;
+
+ /* if the logic was not compressed, decrypt the text messages
+ * only if there are more than 0 messages
+ */
+ if ((~game.dir_logic[n].flags & RES_COMPRESSED) && mc > 0)
+ decrypt(m0 + mstart, mend - mstart); /* decrypt messages */
+
+ /* build message list */
+ m0 = game.logics[n].data;
+ mstart = READ_LE_UINT16(m0) + 2; /* +2 covers pointer */
+ game.logics[n].num_texts = *(m0 + mstart);
+
+ /* resetp logic pointers */
+ game.logics[n].sIP = 2;
+ game.logics[n].cIP = 2;
+ game.logics[n].size = READ_LE_UINT16(m0) + 2; /* logic end pointer */
+
+ /* allocate list of pointers to point into our data */
+
+ game.logics[n].texts = (char **)calloc(1 + game.logics[n].num_texts, sizeof(char *));
+
+ /* cover header info */
+ m0 += mstart + 3;
+
+ if (game.logics[n].texts != NULL) {
+ /* move list of strings into list to make real pointers */
+ for (mc = 0; mc < game.logics[n].num_texts; mc++) {
+ mend = READ_LE_UINT16(m0 + mc * 2);
+ game.logics[n].texts[mc] = mend ? (char *)m0 + mend - 2 : (char *)"";
+ }
+ /* set loaded flag now its all completly loaded */
+ game.dir_logic[n].flags |= RES_LOADED;
+ } else {
+ /* unload data
+ * blah DF YA WANKER!!@!@# frag. i'm so dumb. not every logic
+ * has text
+ */
+ free(game.logics[n].data);
+ ec = err_NotEnoughMemory;
+ }
+
+ return ec;
+}
+
+/**
+ * Unload logic resource
+ * This function unloads the specified logic resource, freeing any
+ * memory chunks allocated for this resource.
+ * @param n The number of the logic resource to unload
+ */
+void unload_logic(int n) {
+ if (game.dir_logic[n].flags & RES_LOADED) {
+ free(game.logics[n].data);
+ if (game.logics[n].num_texts)
+ free(game.logics[n].texts);
+ game.logics[n].num_texts = 0;
+ game.dir_logic[n].flags &= ~RES_LOADED;
+ }
+
+ /* if cached, we end up here */
+ game.logics[n].sIP = 2;
+ game.logics[n].cIP = 2;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/logic.h b/engines/agi/logic.h
new file mode 100644
index 0000000000..cd6fbf752c
--- /dev/null
+++ b/engines/agi/logic.h
@@ -0,0 +1,49 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_LOGIC_H
+#define __AGI_LOGIC_H
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+/**
+ * AGI logic resource structure.
+ */
+struct agi_logic {
+ uint8 *data; /**< raw resource data */
+ int size; /**< size of data */
+ int sIP; /**< saved IP */
+ int cIP; /**< current IP */
+ int num_texts; /**< number of messages */
+ char **texts; /**< message list */
+};
+
+int decode_logic(int);
+void unload_logic(int);
+
+} // End of namespace Agi
+
+#endif /* __AGI_LOGIC_H */
diff --git a/engines/agi/lzw.cpp b/engines/agi/lzw.cpp
new file mode 100644
index 0000000000..29259a5bc9
--- /dev/null
+++ b/engines/agi/lzw.cpp
@@ -0,0 +1,191 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/***************************************************************************
+** decomp.c
+**
+** Routines that deal with AGI version 3 specific features.
+** The original LZW code is from DJJ, October 1989, p.86.
+** It has been modified to handle AGI compression.
+**
+** (c) 1997 Lance Ewing
+***************************************************************************/
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/lzw.h"
+
+namespace Agi {
+
+#define MAXBITS 12
+#define TABLE_SIZE 18041 /* strange number */
+#define START_BITS 9
+
+static int32 BITS, MAX_VALUE, MAX_CODE;
+static uint32 *prefix_code;
+static uint8 *append_character;
+static uint8 *decode_stack;
+static int32 input_bit_count = 0; /* Number of bits in input bit buffer */
+static uint32 input_bit_buffer = 0L;
+
+static void initLZW() {
+ decode_stack = (uint8 *)calloc(1, 8192);
+ prefix_code = (uint32 *)malloc(TABLE_SIZE * sizeof(uint32));
+ append_character = (uint8 *)malloc(TABLE_SIZE * sizeof(uint8));
+ input_bit_count = 0; /* Number of bits in input bit buffer */
+ input_bit_buffer = 0L;
+}
+
+static void closeLZW() {
+ free(decode_stack);
+ free(prefix_code);
+ free(append_character);
+}
+
+/***************************************************************************
+** setBITS
+**
+** Purpose: To adjust the number of bits used to store codes to the value
+** passed in.
+***************************************************************************/
+int setBITS(int32 value) {
+ if (value == MAXBITS)
+ return true;
+
+ BITS = value;
+ MAX_VALUE = (1 << BITS) - 1;
+ MAX_CODE = MAX_VALUE - 1;
+
+ return false;
+}
+
+/***************************************************************************
+** decode_string
+**
+** Purpose: To return the string that the code taken from the input buffer
+** represents. The string is returned as a stack, i.e. the characters are
+** in reverse order.
+***************************************************************************/
+static uint8 *decode_string(uint8 *buffer, uint32 code) {
+ uint32 i;
+
+ for (i = 0; code > 255;) {
+ *buffer++ = append_character[code];
+ code = prefix_code[code];
+ if (i++ >= 4000) {
+ fprintf(stderr, "lzw: error in code expansion.\n");
+ abort();
+ }
+ }
+ *buffer = code;
+
+ return buffer;
+}
+
+/***************************************************************************
+** input_code
+**
+** Purpose: To return the next code from the input buffer.
+***************************************************************************/
+static uint32 input_code(uint8 **input) {
+ uint32 r;
+
+ while (input_bit_count <= 24) {
+ input_bit_buffer |= (uint32) * (*input)++ << input_bit_count;
+ input_bit_count += 8;
+ }
+ r = (input_bit_buffer & 0x7FFF) % (1 << BITS);
+ input_bit_buffer >>= BITS;
+ input_bit_count -= BITS;
+
+ return r;
+}
+
+/***************************************************************************
+** expand
+**
+** Purpose: To uncompress the data contained in the input buffer and store
+** the result in the output buffer. The fileLength parameter says how
+** many bytes to uncompress. The compression itself is a form of LZW that
+** adjusts the number of bits that it represents its codes in as it fills
+** up the available codes. Two codes have special meaning:
+**
+** code 256 = start over
+** code 257 = end of data
+***************************************************************************/
+void LZW_expand(uint8 *in, uint8 *out, int32 len) {
+ int32 c, lzwnext, lzwnew, lzwold;
+ uint8 *s, *end;
+
+ initLZW();
+
+ setBITS(START_BITS); /* Starts at 9-bits */
+ lzwnext = 257; /* Next available code to define */
+
+ end = (unsigned char *)((long)out + (long)len);
+
+ lzwold = input_code(&in); /* Read in the first code */
+ c = lzwold;
+ lzwnew = input_code(&in);
+
+ while ((out < end) && (lzwnew != 0x101)) {
+ if (lzwnew == 0x100) {
+ /* Code to "start over" */
+ lzwnext = 258;
+ setBITS(START_BITS);
+ lzwold = input_code(&in);
+ c = lzwold;
+ *out++ = (char)c;
+ lzwnew = input_code(&in);
+ } else {
+ if (lzwnew >= lzwnext) {
+ /* Handles special LZW scenario */
+ *decode_stack = c;
+ s = decode_string(decode_stack + 1, lzwold);
+ } else
+ s = decode_string(decode_stack, lzwnew);
+
+ /* Reverse order of decoded string and
+ * store in out buffer
+ */
+ c = *s;
+ while (s >= decode_stack)
+ *out++ = *s--;
+
+ if (lzwnext > MAX_CODE)
+ setBITS(BITS + 1);
+
+ prefix_code[lzwnext] = lzwold;
+ append_character[lzwnext] = c;
+ lzwnext++;
+ lzwold = lzwnew;
+
+ lzwnew = input_code(&in);
+ }
+ }
+ closeLZW();
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/lzw.h b/engines/agi/lzw.h
new file mode 100644
index 0000000000..5923f98055
--- /dev/null
+++ b/engines/agi/lzw.h
@@ -0,0 +1,33 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_LZW_H
+#define __AGI_LZW_H
+
+namespace Agi {
+
+void LZW_expand(uint8 *, uint8 *, int32);
+
+} // End of namespace Agi
+#endif
diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp
new file mode 100644
index 0000000000..950b054cb3
--- /dev/null
+++ b/engines/agi/menu.cpp
@@ -0,0 +1,503 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2002 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/sprite.h"
+#include "agi/graphics.h"
+#include "agi/keyboard.h"
+#include "agi/menu.h"
+#include "agi/text.h"
+#include "agi/list.h"
+
+namespace Agi {
+
+struct agi_menu {
+ struct list_head list; /**< list head for menubar list */
+ struct list_head down; /**< list head for menu options */
+ int index; /**< number of menu in menubar */
+ int width; /**< width of menu in characters */
+ int height; /**< height of menu in characters */
+ int col; /**< column of menubar entry */
+ int wincol; /**< column of menu window */
+ char *text; /**< menu name */
+};
+
+struct agi_menu_option {
+ struct list_head list; /**< list head for menu options */
+ int enabled; /**< option is enabled or disabled */
+ int event; /**< menu event */
+ int index; /**< number of option in this menu */
+ char *text; /**< text of menu option */
+};
+
+static LIST_HEAD(menubar);
+
+static int h_cur_menu;
+static int v_cur_menu;
+
+static struct agi_menu *get_menu(int i) {
+ struct list_head *h;
+ struct agi_menu *m;
+
+ list_for_each(h, &menubar, next) {
+ m = list_entry(h, struct agi_menu, list);
+ if (m->index == i)
+ return m;
+ }
+ return NULL;
+}
+
+static struct agi_menu_option *get_menu_option(int i, int j) {
+ struct list_head *h;
+ struct agi_menu *m;
+ struct agi_menu_option *d;
+
+ m = get_menu(i);
+
+ list_for_each(h, &m->down, next) {
+ d = list_entry(h, struct agi_menu_option, list);
+ if (d->index == j)
+ return d;
+ }
+
+ return NULL;
+}
+
+static void draw_menu_bar() {
+ struct list_head *h;
+ struct agi_menu *m;
+
+ clear_lines(0, 0, MENU_BG);
+ flush_lines(0, 0);
+
+ list_for_each(h, &menubar, next) {
+ m = list_entry(h, struct agi_menu, list);
+ print_text(m->text, 0, m->col, 0, 40, MENU_FG, MENU_BG);
+ }
+
+}
+
+static void draw_menu_hilite(int cur_menu) {
+ struct agi_menu *m;
+
+ m = get_menu(cur_menu);
+ debugC(6, kDebugLevelMenu, "[%s]", m->text);
+ print_text(m->text, 0, m->col, 0, 40, MENU_BG, MENU_FG);
+ flush_lines(0, 0);
+}
+
+/* draw box and pulldowns. */
+static void draw_menu_option(int h_menu) {
+ struct list_head *h;
+ struct agi_menu *m = NULL;
+ struct agi_menu_option *d = NULL;
+
+ /* find which vertical menu it is */
+ m = get_menu(h_menu);
+
+ draw_box(m->wincol * CHAR_COLS, 1 * CHAR_LINES, (m->wincol + m->width + 2) * CHAR_COLS,
+ (1 + m->height + 2) * CHAR_LINES, MENU_BG, MENU_LINE, 0);
+
+ list_for_each(h, &m->down, next) {
+ d = list_entry(h, struct agi_menu_option, list);
+ print_text(d->text, 0, m->wincol + 1, d->index + 2, m->width + 2,
+ d->enabled ? MENU_FG : MENU_DISABLED, MENU_BG);
+ }
+}
+
+static void draw_menu_option_hilite(int h_menu, int v_menu) {
+ struct agi_menu *m;
+ struct agi_menu_option *d;
+
+ m = get_menu(h_menu);
+ d = get_menu_option(h_menu, v_menu);
+
+ print_text(d->text, 0, m->wincol + 1, v_menu + 2, m->width + 2,
+ MENU_BG, d->enabled ? MENU_FG : MENU_DISABLED);
+}
+
+static void new_menu_selected(int i) {
+ show_pic();
+ draw_menu_bar();
+ draw_menu_hilite(i);
+ draw_menu_option(i);
+}
+
+#ifdef USE_MOUSE
+static int mouse_over_text(unsigned int line, unsigned int col, char *s) {
+ if (mouse.x < col * CHAR_COLS)
+ return false;
+
+ if (mouse.x > (col + strlen(s)) * CHAR_COLS)
+ return false;
+
+ if (mouse.y < line * CHAR_LINES)
+ return false;
+
+ if (mouse.y >= (line + 1) * CHAR_LINES)
+ return false;
+
+ return true;
+}
+#endif
+
+static int h_index;
+static int v_index;
+static int h_col;
+static int h_max_menu;
+static int v_max_menu[10];
+
+#if 0
+static void add_about_option() {
+ struct agi_menu *m;
+ struct agi_menu_option *d;
+ char text[] = "About AGI engine";
+
+ d = malloc(sizeof(struct agi_menu_option));
+ d->text = strdup(text);
+ d->enabled = true;
+ d->event = 255;
+ d->index = (v_max_menu[0] += 1);
+
+ m = list_entry(menubar.next, struct agi_menu, list);
+ list_add_tail(&d->list, &m->down);
+ m->height++;
+ if (m->width < strlen(text))
+ m->width = strlen(text);
+}
+#endif
+
+/*
+ * Public functions
+ */
+
+void menu_init() {
+ h_index = 0;
+ h_col = 1;
+ h_cur_menu = 0;
+ v_cur_menu = 0;
+}
+
+void menu_deinit() {
+ struct list_head *h, *h2, *v, *v2;
+ struct agi_menu *m = NULL;
+ struct agi_menu_option *d = NULL;
+
+ for (h = (&menubar)->prev; h != (&menubar); h = h2) {
+ m = list_entry(h, struct agi_menu, list);
+ h2 = h->prev;
+ debugC(3, kDebugLevelMenu, "deiniting hmenu %s", m->text);
+ for (v = (&m->down)->prev; v != (&m->down); v = v2) {
+ d = list_entry(v, struct agi_menu_option, list);
+ v2 = v->prev;
+ debugC(3, kDebugLevelMenu, " deiniting vmenu %s", d->text);
+ list_del(v);
+ free(d->text);
+ free(d);
+ }
+ list_del(h);
+ free(m->text);
+ free(m);
+ }
+}
+
+void menu_add(char *s) {
+ struct agi_menu *m;
+
+ m = (agi_menu *) malloc(sizeof(struct agi_menu));
+ m->text = strdup(s);
+ while (m->text[strlen(m->text) - 1] == ' ')
+ m->text[strlen(m->text) - 1] = 0;
+ m->down.next = &m->down;
+ m->down.prev = &m->down;
+ m->width = 0;
+ m->height = 0;
+ m->index = h_index++;
+ m->col = h_col;
+ m->wincol = h_col - 1;
+ v_index = 0;
+ v_max_menu[m->index] = 0;
+ h_col += strlen(m->text) + 1;
+ h_max_menu = m->index;
+
+ debugC(3, kDebugLevelMenu, "add menu: '%s' %02x", s, m->text[strlen(m->text)]);
+ list_add_tail(&m->list, &menubar);
+}
+
+void menu_add_item(char *s, int code) {
+ struct agi_menu *m;
+ struct agi_menu_option *d;
+ int l;
+
+ d = (agi_menu_option *) malloc(sizeof(struct agi_menu_option));
+ d->text = strdup(s);
+ d->enabled = true;
+ d->event = code;
+ d->index = v_index++;
+
+ m = list_entry(menubar.prev, struct agi_menu, list);
+ m->height++;
+
+ v_max_menu[m->index] = d->index;
+
+ l = strlen(d->text);
+ if (l > 40)
+ l = 38;
+ if (m->wincol + l > 38)
+ m->wincol = 38 - l;
+ if (l > m->width)
+ m->width = l;
+
+ debugC(3, kDebugLevelMenu, "Adding menu item: %s (size = %d)", s, m->height);
+ list_add_tail(&d->list, &m->down);
+}
+
+void menu_submit() {
+ struct list_head *h, *h2;
+ struct agi_menu *m = NULL;
+
+ debugC(3, kDebugLevelMenu, "Submitting menu");
+
+ /* add_about_option (); */
+
+ /* If a menu has no options, delete it */
+ for (h = (&menubar)->prev; h != (&menubar); h = h2) {
+ m = list_entry(h, struct agi_menu, list);
+ h2 = h->prev;
+ if ((&m->down)->prev == (&m->down)) {
+ list_del(h);
+ free(m->text);
+ free(m);
+ h_max_menu--;
+ }
+ }
+}
+
+int menu_keyhandler(int key) {
+ static int clock_val;
+ static int menu_active = false;
+ struct agi_menu_option *d;
+ struct list_head *h;
+ struct agi_menu *m;
+ static int button_used = 0;
+
+ if (!getflag(F_menus_work))
+ return false;
+
+ if (!menu_active) {
+ clock_val = game.clock_enabled;
+ game.clock_enabled = false;
+ draw_menu_bar();
+ }
+#ifdef USE_MOUSE
+ /*
+ * Mouse handling
+ */
+ if (mouse.button) {
+ int hmenu, vmenu;
+
+ button_used = 1; /* Button has been used at least once */
+ if (mouse.y <= CHAR_LINES) {
+ /* on the menubar */
+ hmenu = 0;
+
+ list_for_each(h, &menubar, next) {
+ m = list_entry(h, struct agi_menu, list);
+ if (mouse_over_text(0, m->col, m->text)) {
+ break;
+ } else {
+ hmenu++;
+ }
+ }
+
+ if (hmenu <= h_max_menu) {
+ if (h_cur_menu != hmenu) {
+ v_cur_menu = -1;
+ new_menu_selected(hmenu);
+ }
+ h_cur_menu = hmenu;
+ }
+ } else {
+ /* not in menubar */
+ struct agi_menu_option *do1;
+
+ vmenu = 0;
+
+ m = get_menu(h_cur_menu);
+ list_for_each(h, &m->down, next) {
+ do1 = list_entry(h, struct agi_menu_option, list);
+ if (mouse_over_text(2 + do1->index, m->wincol + 1, do1->text)) {
+ break;
+ } else {
+ vmenu++;
+ }
+ }
+
+ if (vmenu <= v_max_menu[h_cur_menu]) {
+ if (v_cur_menu != vmenu) {
+ draw_menu_option(h_cur_menu);
+ draw_menu_option_hilite(h_cur_menu, vmenu);
+ }
+ v_cur_menu = vmenu;
+ }
+ }
+ } else if (button_used) {
+ /* Button released */
+ button_used = 0;
+
+ debugC(6, kDebugLevelMenu | kDebugLevelInput, "button released!");
+
+ if (v_cur_menu < 0)
+ v_cur_menu = 0;
+
+ draw_menu_option_hilite(h_cur_menu, v_cur_menu);
+
+ if (mouse.y <= CHAR_LINES) {
+ /* on the menubar */
+ } else {
+ /* see which option we selected */
+ m = get_menu(h_cur_menu);
+ list_for_each(h, &m->down, next) {
+ d = list_entry(h, struct agi_menu_option, list);
+ if (mouse_over_text(2 + d->index,
+ m->wincol + 1, d->text)) {
+ /* activate that option */
+ if (d->enabled) {
+ debugC(6, kDebugLevelMenu | kDebugLevelInput, "event %d registered", d->event);
+ game.ev_keyp[d->event].occured = true;
+ game.ev_keyp[d->event].data = d->event;
+ goto exit_menu;
+ }
+ }
+ }
+ goto exit_menu;
+ }
+ }
+#endif /* USE_MOUSE */
+
+ if (!menu_active) {
+ if (h_cur_menu >= 0) {
+ draw_menu_hilite(h_cur_menu);
+ draw_menu_option(h_cur_menu);
+ if (!button_used && v_cur_menu >= 0)
+ draw_menu_option_hilite(h_cur_menu, v_cur_menu);
+ }
+ menu_active = true;
+ }
+
+ switch (key) {
+ case KEY_ESCAPE:
+ debugC(6, kDebugLevelMenu | kDebugLevelInput, "KEY_ESCAPE");
+ goto exit_menu;
+ case KEY_ENTER:
+ debugC(6, kDebugLevelMenu | kDebugLevelInput, "KEY_ENTER");
+ d = get_menu_option(h_cur_menu, v_cur_menu);
+ if (d->enabled) {
+ debugC(6, kDebugLevelMenu | kDebugLevelInput, "event %d registered", d->event);
+ game.ev_keyp[d->event].occured = true;
+ goto exit_menu;
+ }
+ break;
+ case KEY_DOWN:
+ case KEY_UP:
+ v_cur_menu += key == KEY_DOWN ? 1 : -1;
+
+ if (v_cur_menu < 0)
+ v_cur_menu = 0;
+ if (v_cur_menu > v_max_menu[h_cur_menu])
+ v_cur_menu = v_max_menu[h_cur_menu];
+
+ draw_menu_option(h_cur_menu);
+ draw_menu_option_hilite(h_cur_menu, v_cur_menu);
+ break;
+ case KEY_RIGHT:
+ case KEY_LEFT:
+ h_cur_menu += key == KEY_RIGHT ? 1 : -1;
+
+ if (h_cur_menu < 0)
+ h_cur_menu = h_max_menu;
+ if (h_cur_menu > h_max_menu)
+ h_cur_menu = 0;
+
+ v_cur_menu = 0;
+ new_menu_selected(h_cur_menu);
+ draw_menu_option_hilite(h_cur_menu, v_cur_menu);
+ break;
+ }
+
+ return true;
+
+exit_menu:
+ button_used = 0;
+ show_pic();
+ write_status();
+
+ setvar(V_key, 0);
+ game.keypress = 0;
+ game.clock_enabled = clock_val;
+ old_input_mode();
+ debugC(3, kDebugLevelMenu, "exit_menu: input mode reset to %d", game.input_mode);
+ menu_active = false;
+
+ return true;
+}
+
+void menu_set_item(int event, int state) {
+ struct list_head *h, *v;
+ struct agi_menu *m = NULL;
+ struct agi_menu_option *d = NULL;
+
+ /* scan all menus for event number # */
+
+ debugC(6, kDebugLevelMenu, "event = %d, state = %d", event, state);
+ list_for_each(h, &menubar, next) {
+ m = list_entry(h, struct agi_menu, list);
+ list_for_each(v, &m->down, next) {
+ d = list_entry(v, struct agi_menu_option, list);
+ if (d->event == event) {
+ d->enabled = state;
+ return;
+ }
+ }
+ }
+}
+
+void menu_enable_all() {
+ struct list_head *h, *v;
+ struct agi_menu *m = NULL;
+ struct agi_menu_option *d = NULL;
+
+ list_for_each(h, &menubar, next) {
+ m = list_entry(h, struct agi_menu, list);
+ list_for_each(v, &m->down, next) {
+ d = list_entry(v, struct agi_menu_option, list);
+ d->enabled = true;
+ }
+ }
+
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/menu.h b/engines/agi/menu.h
new file mode 100644
index 0000000000..095f802bd4
--- /dev/null
+++ b/engines/agi/menu.h
@@ -0,0 +1,46 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_MENU_H
+#define __AGI_MENU_H
+
+namespace Agi {
+
+#define MENU_BG 0x0f /* White */
+#define MENU_DISABLED 0x07 /* Grey */
+
+#define MENU_FG 0x00 /* Black */
+#define MENU_LINE 0x00 /* Black */
+
+void menu_init(void);
+void menu_deinit(void);
+void menu_add(char *);
+void menu_add_item(char *, int);
+void menu_submit(void);
+void menu_set_item(int, int);
+int menu_keyhandler(int);
+void menu_enable_all(void);
+
+} // End of namespace Agi
+#endif
diff --git a/engines/agi/module.mk b/engines/agi/module.mk
new file mode 100644
index 0000000000..a5a81f2b15
--- /dev/null
+++ b/engines/agi/module.mk
@@ -0,0 +1,42 @@
+MODULE := engines/agi
+
+MODULE_OBJS = \
+ agi.o \
+ agi_v2.o \
+ agi_v3.o \
+ checks.o \
+ console.o \
+ cycle.o \
+ font.o \
+ global.o \
+ graphics.o \
+ id.o \
+ inv.o \
+ keyboard.o \
+ logic.o \
+ lzw.o \
+ menu.o \
+ motion.o \
+ objects.o \
+ op_cmd.o \
+ op_dbg.o \
+ op_test.o \
+ patches.o \
+ picture.o \
+ savegame.o \
+ sound.o \
+ sprite.o \
+ text.o \
+ view.o \
+ words.o
+
+MODULE_DIRS += \
+ engines/agi
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/agi/motion.cpp b/engines/agi/motion.cpp
new file mode 100644
index 0000000000..61d14dc1a3
--- /dev/null
+++ b/engines/agi/motion.cpp
@@ -0,0 +1,232 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+static int check_step(int delta, int step) {
+ return (-step >= delta) ? 0 : (step <= delta) ? 2 : 1;
+}
+
+static int check_block(int x, int y) {
+ if (x <= game.block.x1 || x >= game.block.x2)
+ return false;
+
+ if (y <= game.block.y1 || y >= game.block.y2)
+ return false;
+
+ return true;
+}
+
+static void changepos(struct vt_entry *v) {
+ int b, x, y;
+ int dx[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
+ int dy[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
+
+ x = v->x_pos;
+ y = v->y_pos;
+ b = check_block(x, y);
+
+ x += v->step_size * dx[v->direction];
+ y += v->step_size * dy[v->direction];
+
+ if (check_block(x, y) == b) {
+ v->flags &= ~MOTION;
+ } else {
+ v->flags |= MOTION;
+ v->direction = 0;
+ if /*_is_ego_view*/ (v)
+ game.vars[V_ego_dir] = 0;
+ }
+}
+
+static void motion_wander(struct vt_entry *v) {
+ if (v->parm1--) {
+ if (~v->flags & DIDNT_MOVE)
+ return;
+ }
+
+ v->direction = rnd->getRandomNumber(8);
+
+ if /*_is_ego_view */ (v) {
+ game.vars[V_ego_dir] = v->direction;
+ while (v->parm1 < 6) {
+ v->parm1 = rnd->getRandomNumber(50); /* huh? */
+ }
+ }
+}
+
+static void motion_followego(struct vt_entry *v) {
+ int ego_x, ego_y;
+ int obj_x, obj_y;
+ int dir;
+
+ ego_x = game.view_table[0].x_pos + game.view_table[0].x_size / 2;
+ ego_y = game.view_table[0].y_pos;
+
+ obj_x = v->x_pos + v->x_size / 2;
+ obj_y = v->y_pos;
+
+ /* Get direction to reach ego */
+ dir = get_direction(obj_x, obj_y, ego_x, ego_y, v->parm1);
+
+ /* Already at ego coordinates */
+ if (dir == 0) {
+ v->direction = 0;
+ v->motion = MOTION_NORMAL;
+ setflag(v->parm2, true);
+ return;
+ }
+
+ if (v->parm3 == 0xff) {
+ v->parm3 = 0;
+ } else if (v->flags & DIDNT_MOVE) {
+ int d;
+
+ while ((v->direction = rnd->getRandomNumber(8)) == 0) {
+ }
+
+ d = (abs(ego_y - obj_y) + abs(ego_x - obj_x)) / 2;
+
+ if (d < v->step_size) {
+ v->parm3 = v->step_size;
+ return;
+ }
+
+ while ((v->parm3 = rnd->getRandomNumber(d)) < v->step_size) {
+ }
+ return;
+ }
+
+ if (v->parm3 != 0) {
+ int k;
+
+ /* DF: this is ugly and I dont know why this works, but
+ * other line does not! (watcom complained about lvalue)
+ *
+ * if (((int8)v->parm3 -= v->step_size) < 0)
+ * v->parm3 = 0;
+ */
+ k = v->parm3;
+ k -= v->step_size;
+ v->parm3 = k;
+
+ if ((int8) v->parm3 < 0)
+ v->parm3 = 0;
+ } else {
+ v->direction = dir;
+ }
+}
+
+static void motion_moveobj(struct vt_entry *v) {
+ v->direction = get_direction(v->x_pos, v->y_pos, v->parm1, v->parm2, v->step_size);
+
+ /* Update V6 if ego */
+ if (v == game.view_table)
+ game.vars[V_ego_dir] = v->direction;
+
+ if (v->direction == 0)
+ in_destination(v);
+}
+
+static void check_motion(struct vt_entry *v) {
+ switch (v->motion) {
+ case MOTION_WANDER:
+ motion_wander(v);
+ break;
+ case MOTION_FOLLOW_EGO:
+ motion_followego(v);
+ break;
+ case MOTION_MOVE_OBJ:
+ motion_moveobj(v);
+ break;
+ }
+
+ if ((game.block.active && (~v->flags & IGNORE_BLOCKS)) && v->direction)
+ changepos(v);
+}
+
+/*
+ * Public functions
+ */
+
+/**
+ *
+ */
+void check_all_motions() {
+ struct vt_entry *v;
+
+ for (v = game.view_table; v < &game.view_table[MAX_VIEWTABLE]; v++) {
+ if ((v->flags & (ANIMATED | UPDATE | DRAWN)) == (ANIMATED | UPDATE | DRAWN)
+ && v->step_time_count == 1) {
+ check_motion(v);
+ }
+ }
+}
+
+/**
+ * Check if given entry is at destination point.
+ * This function is used to updated the flags of an object with move.obj
+ * type motion that * has reached its final destination coordinates.
+ * @param v Pointer to view table entry
+ */
+void in_destination(struct vt_entry *v) {
+ if (v->motion == MOTION_MOVE_OBJ) {
+ v->step_size = v->parm3;
+ setflag(v->parm4, true);
+ }
+ v->motion = MOTION_NORMAL;
+ if (v == game.view_table)
+ game.player_control = true;
+}
+
+/**
+ * Wrapper for static function motion_moveobj().
+ * This function is used by cmd_move_object() in the first motion cycle
+ * after setting the motion mode to MOTION_MOVE_OBJ.
+ * @param v Pointer to view table entry
+ */
+void move_obj(struct vt_entry *v) {
+ motion_moveobj(v);
+}
+
+/**
+ * Get direction from motion coordinates
+ * This function gets the motion direction from the current and previous
+ * object coordinates and the step size.
+ * @param x0 Original x coordinate of the object
+ * @param y0 Original y coordinate of the object
+ * @param x x coordinate of the object
+ * @param y y coordinate of the object
+ * @param s step size
+ */
+int get_direction(int x0, int y0, int x, int y, int s) {
+ int dir_table[9] = { 8, 1, 2, 7, 0, 3, 6, 5, 4 };
+ return dir_table[check_step(x - x0, s) + 3 * check_step(y - y0, s)];
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/objects.cpp b/engines/agi/objects.cpp
new file mode 100644
index 0000000000..352dabcf95
--- /dev/null
+++ b/engines/agi/objects.cpp
@@ -0,0 +1,167 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+extern int decode_objects(uint8 *mem, uint32 flen);
+
+static struct agi_object *objects; /* objects in the game */
+
+int alloc_objects(int n) {
+ if ((objects = (agi_object *) calloc(n, sizeof(struct agi_object))) == NULL)
+ return err_NotEnoughMemory;
+
+ return err_OK;
+}
+
+int decode_objects(uint8 *mem, uint32 flen) {
+ unsigned int i, so, padsize;
+
+ padsize = game.game_flags & ID_AMIGA ? 4 : 3;
+
+ game.num_objects = 0;
+ objects = NULL;
+
+ /* check if first pointer exceeds file size
+ * if so, its encrypted, else it is not
+ */
+
+ if (READ_LE_UINT16(mem) > flen) {
+ report("Decrypting objects... ");
+ decrypt(mem, flen);
+ report("done.\n");
+ }
+
+ /* alloc memory for object list
+ * byte 3 = number of animated objects. this is ignored.. ??
+ */
+ if (READ_LE_UINT16(mem) / padsize >= 256) {
+#ifdef AGDS_SUPPORT
+ /* die with no error! AGDS game needs not to die to work!! :( */
+ return err_OK;
+#else
+ /* no AGDS support, die with error */
+ return err_BadResource;
+#endif
+ }
+
+ game.num_objects = READ_LE_UINT16(mem) / padsize;
+ debugC(5, kDebugLevelResources, "num_objects = %d (padsize = %d)", game.num_objects, padsize);
+
+ if (alloc_objects(game.num_objects) != err_OK)
+ return err_NotEnoughMemory;
+
+ /* build the object list */
+ for (i = 0, so = padsize; i < game.num_objects; i++, so += padsize) {
+ int offset;
+
+ (objects + i)->location = *(mem + so + 2);
+ offset = READ_LE_UINT16(mem + so) + padsize;
+
+ if ((uint) offset < flen) {
+ (objects + i)->name = (char *)strdup((const char *)mem + offset);
+ } else {
+ printf("ERROR: object %i name beyond object filesize! "
+ "(%04x > %04x)\n", i, offset, flen);
+ (objects + i)->name = strdup("");
+ }
+ }
+ report("Reading objects: %d objects read.\n", game.num_objects);
+
+ return err_OK;
+
+}
+
+int load_objects(char *fname) {
+ Common::File fp;
+ uint32 flen;
+ uint8 *mem;
+ char *path;
+
+ objects = NULL;
+ game.num_objects = 0;
+
+ debugC(5, kDebugLevelResources, "(fname = %s)", fname);
+ path = fname;
+ report("Loading objects: %s\n", path);
+
+ if (!fp.open(path))
+ return err_BadFileOpen;
+
+ fp.seek(0, SEEK_END);
+ flen = fp.pos();
+ fp.seek(0, SEEK_SET);
+
+ if ((mem = (uint8 *) calloc(1, flen + 32)) == NULL) {
+ fp.close();
+ return err_NotEnoughMemory;
+ }
+
+ fp.read(mem, flen);
+ fp.close();
+
+ decode_objects(mem, flen);
+ free(mem);
+ return err_OK;
+}
+
+void unload_objects() {
+ unsigned int i;
+
+ if (objects != NULL) {
+ for (i = 0; i < game.num_objects; i++)
+ free(objects[i].name);
+ free(objects);
+ }
+}
+
+void object_set_location(unsigned int n, int i) {
+ if (n >= game.num_objects) {
+ report("Error: Can't access object %d.\n", n);
+ return;
+ }
+ objects[n].location = i;
+}
+
+int object_get_location(unsigned int n) {
+ if (n >= game.num_objects) {
+ report("Error: Can't access object %d.\n", n);
+ return 0;
+ }
+ return objects[n].location;
+}
+
+char *object_name(unsigned int n) {
+ if (n >= game.num_objects) {
+ report("Error: Can't access object %d.\n", n);
+ return "";
+ }
+ return objects[n].name;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
new file mode 100644
index 0000000000..042556b09e
--- /dev/null
+++ b/engines/agi/op_cmd.cpp
@@ -0,0 +1,1513 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/sprite.h"
+#include "agi/graphics.h"
+#include "agi/keyboard.h"
+#include "agi/opcodes.h"
+#include "agi/menu.h"
+#include "agi/savegame.h"
+#include "agi/text.h"
+
+namespace Agi {
+
+#define p0 (p[0])
+#define p1 (p[1])
+#define p2 (p[2])
+#define p3 (p[3])
+#define p4 (p[4])
+#define p5 (p[5])
+#define p6 (p[6])
+
+#define ip cur_logic->cIP
+#define vt game.view_table[p0]
+
+static struct agi_logic *cur_logic;
+
+int timer_hack; /* Workaround for timer loop in MH1 */
+
+#define _v game.vars
+#define cmd(x) static void cmd_##x (uint8 *p)
+
+cmd(increment) {
+ if (_v[p0] != 0xff)
+ ++_v[p0];
+}
+
+cmd(decrement) {
+ if (_v[p0] != 0)
+ --_v[p0];
+}
+
+cmd(assignn) {
+ _v[p0] = p1;
+}
+
+cmd(addn) {
+ _v[p0] += p1;
+}
+
+cmd(subn) {
+ _v[p0] -= p1;
+}
+
+cmd(assignv) {
+ _v[p0] = _v[p1];
+}
+
+cmd(addv) {
+ _v[p0] += _v[p1];
+}
+
+cmd(subv) {
+ _v[p0] -= _v[p1];
+}
+
+cmd(mul_n) {
+ _v[p0] *= p1;
+}
+
+cmd(mul_v) {
+ _v[p0] *= _v[p1];
+}
+
+cmd(div_n) {
+ _v[p0] /= p1;
+}
+
+cmd(div_v) {
+ _v[p0] /= _v[p1];
+}
+
+cmd(random) {
+ _v[p2] = rnd->getRandomNumber(p1 - p0) + p0;
+}
+
+cmd(lindirectn) {
+ _v[_v[p0]] = p1;
+}
+
+cmd(lindirectv) {
+ _v[_v[p0]] = _v[p1];
+}
+
+cmd(rindirect) {
+ _v[p0] = _v[_v[p1]];
+}
+
+cmd(set) {
+ setflag(*p, true);
+}
+
+cmd(reset) {
+ setflag(*p, false);
+}
+
+cmd(toggle) {
+ setflag(*p, !getflag(*p));
+}
+
+cmd(set_v) {
+ setflag(_v[p0], true);
+}
+
+cmd(reset_v) {
+ setflag(_v[p0], false);
+}
+
+cmd(toggle_v) {
+ setflag(_v[p0], !getflag(_v[p0]));
+}
+
+cmd(new_room) {
+ new_room(p0);
+}
+
+cmd(new_room_f) {
+ new_room(_v[p0]);
+}
+
+cmd(load_view) {
+ agi_load_resource(rVIEW, p0);
+}
+
+cmd(load_logic) {
+ agi_load_resource(rLOGIC, p0);
+}
+
+cmd(load_sound) {
+ agi_load_resource(rSOUND, p0);
+}
+
+cmd(load_view_f) {
+ agi_load_resource(rVIEW, _v[p0]);
+}
+
+cmd(load_logic_f) {
+ agi_load_resource(rLOGIC, _v[p0]);
+}
+
+cmd(discard_view) {
+ agi_unload_resource(rVIEW, p0);
+}
+
+cmd(object_on_anything) {
+ vt.flags &= ~(ON_WATER | ON_LAND);
+}
+
+cmd(object_on_land) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ vt.flags |= ON_LAND;
+}
+
+cmd(object_on_water) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ vt.flags |= ON_WATER;
+}
+
+cmd(observe_horizon) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ vt.flags &= ~IGNORE_HORIZON;
+}
+
+cmd(ignore_horizon) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ vt.flags |= IGNORE_HORIZON;
+}
+
+cmd(observe_objs) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ vt.flags &= ~IGNORE_OBJECTS;
+}
+
+cmd(ignore_objs) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ vt.flags |= IGNORE_OBJECTS;
+}
+
+cmd(observe_blocks) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ vt.flags &= ~IGNORE_BLOCKS;
+}
+
+cmd(ignore_blocks) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ vt.flags |= IGNORE_BLOCKS;
+}
+
+cmd(set_horizon) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ game.horizon = p0;
+}
+
+cmd(get_priority) {
+ _v[p1] = vt.priority;
+}
+
+cmd(set_priority) {
+ vt.flags |= FIXED_PRIORITY;
+ vt.priority = p1;
+}
+
+cmd(set_priority_f) {
+ vt.flags |= FIXED_PRIORITY;
+ vt.priority = _v[p1];
+}
+
+cmd(release_priority) {
+ vt.flags &= ~FIXED_PRIORITY;
+}
+
+cmd(set_upper_left) { /* do nothing (AGI 2.917) */
+}
+
+cmd(start_update) {
+ start_update(&vt);
+}
+
+cmd(stop_update) {
+ stop_update(&vt);
+}
+
+cmd(current_view) {
+ _v[p1] = vt.current_view;
+}
+
+cmd(current_cel) {
+ _v[p1] = vt.current_cel;
+ debugC(4, kDebugLevelScripts, "v%d=%d", p1, _v[p1]);
+}
+
+cmd(current_loop) {
+ _v[p1] = vt.current_loop;
+}
+
+cmd(last_cel) {
+ _v[p1] = vt.loop_data->num_cels - 1;
+}
+
+cmd(set_cel) {
+ set_cel(&vt, p1);
+ vt.flags &= ~DONTUPDATE;
+}
+
+cmd(set_cel_f) {
+ set_cel(&vt, _v[p1]);
+ vt.flags &= ~DONTUPDATE;
+}
+
+cmd(set_view) {
+ debugC(4, kDebugLevelScripts, "o%d, %d", p0, p1);
+ set_view(&vt, p1);
+}
+
+cmd(set_view_f) {
+ set_view(&vt, _v[p1]);
+}
+
+cmd(set_loop) {
+ set_loop(&vt, p1);
+}
+
+cmd(set_loop_f) {
+ set_loop(&vt, _v[p1]);
+}
+
+cmd(number_of_loops) {
+ _v[p1] = vt.num_loops;
+}
+
+cmd(fix_loop) {
+ vt.flags |= FIX_LOOP;
+}
+
+cmd(release_loop) {
+ vt.flags &= ~FIX_LOOP;
+}
+
+cmd(step_size) {
+ vt.step_size = _v[p1];
+}
+
+cmd(step_time) {
+ vt.step_time = vt.step_time_count = _v[p1];
+}
+
+cmd(cycle_time) {
+ vt.cycle_time = vt.cycle_time_count = _v[p1];
+}
+
+cmd(stop_cycling) {
+ vt.flags &= ~CYCLING;
+}
+
+cmd(start_cycling) {
+ vt.flags |= CYCLING;
+}
+
+cmd(normal_cycle) {
+ vt.cycle = CYCLE_NORMAL;
+ vt.flags |= CYCLING;
+}
+
+cmd(reverse_cycle) {
+ vt.cycle = CYCLE_REVERSE;
+ vt.flags |= CYCLING;
+}
+
+cmd(set_dir) {
+ vt.direction = _v[p1];
+}
+
+cmd(get_dir) {
+ _v[p1] = vt.direction;
+}
+
+cmd(get_room_f) {
+ _v[p1] = object_get_location(_v[p0]);
+}
+
+cmd(put) {
+ object_set_location(p0, _v[p1]);
+}
+
+cmd(put_f) {
+ object_set_location(_v[p0], _v[p1]);
+}
+
+cmd(drop) {
+ object_set_location(p0, 0);
+}
+
+cmd(get) {
+ object_set_location(p0, EGO_OWNED);
+}
+
+cmd(get_f) {
+ object_set_location(_v[p0], EGO_OWNED);
+}
+
+cmd(word_to_string) {
+ strcpy(game.strings[p0], game.ego_words[p1].word);
+}
+
+cmd(open_dialogue) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ game.has_window = true;
+}
+
+cmd(close_dialogue) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ game.has_window = false;
+}
+
+cmd(close_window) {
+ close_window();
+}
+
+cmd(status_line_on) {
+ game.status_line = true;
+ write_status();
+}
+
+cmd(status_line_off) {
+ game.status_line = false;
+ write_status();
+}
+
+cmd(show_obj) {
+ show_obj(p0);
+}
+
+cmd(show_obj_v) {
+ show_obj(_v[p0]);
+}
+
+cmd(sound) {
+ start_sound(p0, p1);
+}
+
+cmd(stop_sound) {
+ stop_sound();
+}
+
+cmd(menu_input) {
+ new_input_mode(INPUT_MENU);
+}
+
+cmd(enable_item) {
+ menu_set_item(p0, true);
+}
+
+cmd(disable_item) {
+ menu_set_item(p0, false);
+}
+
+cmd(submit_menu) {
+ menu_submit();
+}
+
+cmd(set_scan_start) {
+ cur_logic->sIP = cur_logic->cIP;
+}
+
+cmd(reset_scan_start) {
+ cur_logic->sIP = 2;
+}
+
+cmd(save_game) {
+ game.simple_save ? savegame_simple() : savegame_dialog();
+}
+
+cmd(load_game) {
+ game.simple_save ? loadgame_simple() : loadgame_dialog();
+}
+
+cmd(init_disk) { /* do nothing */
+}
+
+cmd(log) { /* do nothing */
+}
+
+cmd(trace_on) { /* do nothing */
+}
+
+cmd(trace_info) { /* do nothing */
+}
+
+cmd(show_mem) {
+ message_box("Enough memory");
+}
+
+cmd(init_joy) { /* do nothing */ ;
+}
+
+cmd(script_size) {
+ report("script.size(%d)\n", p0);
+}
+
+cmd(cancel_line) {
+ report("cancel.line\n");
+}
+
+cmd(obj_status_f) {
+ report("obj.status.f\n");
+}
+
+/* unknown commands:
+ * unk_170: Force savegame name -- j5
+ * unk_171: script save -- j5
+ * unk_172: script restore -- j5
+ * unk_173: Activate keypressed control (ego only moves while key is pressed)
+ * unk_174: Change priority table (used in KQ4) -- j5
+ * unk_177: Disable menus completely -- j5
+ * unk_181: Deactivate keypressed control (default control of ego)
+ */
+cmd(set_simple) {
+ game.simple_save = true;
+}
+
+cmd(pop_script) {
+ report("pop.script\n");
+}
+
+cmd(hold_key) {
+ report("hold.key\n");
+}
+
+cmd(discard_sound) {
+ report("discard.sound\n");
+}
+
+cmd(hide_mouse) {
+ report("hide.mouse\n");
+}
+
+cmd(allow_menu) {
+ report("allow.menu\n");
+}
+
+cmd(show_mouse) {
+ report("show.mouse\n");
+}
+
+cmd(fence_mouse) {
+ report("fence.mouse\n");
+}
+
+cmd(release_key) {
+ report("release.key\n");
+}
+
+cmd(adj_ego_move_to_x_y) {
+ game.view_table[0].flags |= ADJ_EGO_XY;
+}
+
+cmd(parse) {
+ _v[V_word_not_found] = 0;
+ setflag(F_entered_cli, false);
+ setflag(F_said_accepted_input, false);
+
+ dictionary_words(agi_sprintf(game.strings[p0]));
+}
+
+cmd(call) {
+ int old_cIP;
+ int old_lognum;
+
+ /* CM: we don't save sIP because set.scan.start can be
+ * used in a called script (fixes xmas demo)
+ */
+ old_cIP = cur_logic->cIP;
+ old_lognum = game.lognum;
+
+ run_logic(p0);
+
+ game.lognum = old_lognum;
+ cur_logic = &game.logics[game.lognum];
+ cur_logic->cIP = old_cIP;
+}
+
+cmd(call_f) {
+ cmd_call(&_v[p0]);
+}
+
+cmd(draw_pic) {
+ debugC(6, kDebugLevelScripts, "=== draw pic %d ===", _v[p0]);
+ erase_both();
+ decode_picture(_v[p0], true);
+ blit_both();
+ game.picture_shown = 0;
+ debugC(6, kDebugLevelScripts, "--- end of draw pic %d ---", _v[p0]);
+}
+
+cmd(show_pic) {
+ debugC(6, kDebugLevelScripts, "=== show pic ===");
+ setflag(F_output_mode, false);
+ cmd_close_window(NULL);
+ show_pic();
+ game.picture_shown = 1;
+ debugC(6, kDebugLevelScripts, "--- end of show pic ---");
+}
+
+cmd(load_pic) {
+ erase_both();
+ agi_load_resource(rPICTURE, _v[p0]);
+ blit_both();
+}
+
+cmd(discard_pic) {
+ debugC(6, kDebugLevelScripts, "--- discard pic ---");
+ /* do nothing */
+}
+
+cmd(overlay_pic) {
+ debugC(6, kDebugLevelScripts, "--- overlay pic ---");
+ erase_both();
+ decode_picture(_v[p0], false);
+ blit_both();
+ game.picture_shown = 0;
+ commit_both();
+}
+
+cmd(show_pri_screen) {
+#ifdef USE_CONSOLE
+ debug_.priority = 1;
+ erase_both();
+ show_pic();
+ blit_both();
+ wait_key();
+ debug_.priority = 0;
+ erase_both();
+ show_pic();
+ blit_both();
+#endif
+}
+
+cmd(animate_obj) {
+ if (vt.flags & ANIMATED)
+ return;
+
+ debugC(4, kDebugLevelScripts, "animate vt entry #%d", p0);
+ vt.flags = ANIMATED | UPDATE | CYCLING;
+ vt.motion = MOTION_NORMAL;
+ vt.cycle = CYCLE_NORMAL;
+ vt.direction = 0;
+}
+
+cmd(unanimate_all) {
+ int i;
+ for (i = 0; i < MAX_VIEWTABLE; i++)
+ game.view_table[i].flags &= ~(ANIMATED | DRAWN);
+}
+
+cmd(draw) {
+ if (vt.flags & DRAWN)
+ return;
+
+ if (vt.y_size <= 0 || vt.x_size <= 0)
+ return;
+
+ debugC(4, kDebugLevelScripts, "draw entry %d", vt.entry);
+
+ vt.flags |= UPDATE;
+ if (agi_get_release() >= 0x3000) {
+ set_loop(&vt, vt.current_loop);
+ set_cel(&vt, vt.current_cel);
+ }
+ fix_position(p0);
+ vt.x_pos2 = vt.x_pos;
+ vt.y_pos2 = vt.y_pos;
+ vt.cel_data_2 = vt.cel_data;
+ erase_upd_sprites();
+ vt.flags |= DRAWN;
+
+ if (agi_get_release() <= 0x2440) /* See bug #546562 */
+ vt.flags |= ANIMATED;
+
+ blit_upd_sprites();
+ vt.flags &= ~DONTUPDATE;
+
+ commit_block(vt.x_pos, vt.y_pos - vt.y_size + 1, vt.x_pos + vt.x_size - 1, vt.y_pos);
+
+ debugC(4, kDebugLevelScripts, "vt entry #%d flags = %02x", p0, vt.flags);
+}
+
+cmd(erase) {
+ if (~vt.flags & DRAWN)
+ return;
+
+ erase_upd_sprites();
+ if (vt.flags & UPDATE) {
+ vt.flags &= ~DRAWN;
+ } else {
+ erase_nonupd_sprites();
+ vt.flags &= ~DRAWN;
+ blit_nonupd_sprites();
+ }
+ blit_upd_sprites();
+
+ commit_block(vt.x_pos, vt.y_pos - vt.y_size + 1, vt.x_pos + vt.x_size - 1, vt.y_pos);
+}
+
+cmd(position) {
+ vt.x_pos = vt.x_pos2 = p1;
+ vt.y_pos = vt.y_pos2 = p2;
+}
+
+cmd(position_f) {
+ vt.x_pos = vt.x_pos2 = _v[p1];
+ vt.y_pos = vt.y_pos2 = _v[p2];
+}
+
+cmd(get_posn) {
+ game.vars[p1] = (unsigned char)vt.x_pos;
+ game.vars[p2] = (unsigned char)vt.y_pos;
+}
+
+cmd(reposition) {
+ int dx = (int8) _v[p1], dy = (int8) _v[p2];
+
+ debugC(4, kDebugLevelScripts, "dx=%d, dy=%d", dx, dy);
+ vt.flags |= UPDATE_POS;
+
+ if (dx < 0 && vt.x_pos < -dx)
+ vt.x_pos = 0;
+ else
+ vt.x_pos += dx;
+
+ if (dy < 0 && vt.y_pos < -dy)
+ vt.y_pos = 0;
+ else
+ vt.y_pos += dy;
+
+ fix_position(p0);
+}
+
+cmd(reposition_to) {
+ vt.x_pos = p1;
+ vt.y_pos = p2;
+ vt.flags |= UPDATE_POS;
+ fix_position(p0);
+}
+
+cmd(reposition_to_f) {
+ vt.x_pos = _v[p1];
+ vt.y_pos = _v[p2];
+ vt.flags |= UPDATE_POS;
+ fix_position(p0);
+}
+
+cmd(add_to_pic) {
+ add_to_pic(p0, p1, p2, p3, p4, p5, p6);
+}
+
+cmd(add_to_pic_f) {
+ add_to_pic(_v[p0], _v[p1], _v[p2], _v[p3], _v[p4], _v[p5], _v[p6]);
+}
+
+cmd(force_update) {
+ erase_both();
+ blit_both();
+ commit_both();
+}
+
+cmd(reverse_loop) {
+ debugC(4, kDebugLevelScripts, "o%d, f%d", p0, p1);
+ vt.cycle = CYCLE_REV_LOOP;
+ vt.flags |= (DONTUPDATE | UPDATE | CYCLING);
+ vt.parm1 = p1;
+ setflag(p1, false);
+}
+
+cmd(end_of_loop) {
+ debugC(4, kDebugLevelScripts, "o%d, f%d", p0, p1);
+ vt.cycle = CYCLE_END_OF_LOOP;
+ vt.flags |= (DONTUPDATE | UPDATE | CYCLING);
+ vt.parm1 = p1;
+ setflag(p1, false);
+}
+
+cmd(block) {
+ debugC(4, kDebugLevelScripts, "x1=%d, y1=%d, x2=%d, y2=%d", p0, p1, p2, p3);
+ game.block.active = true;
+ game.block.x1 = p0;
+ game.block.y1 = p1;
+ game.block.x2 = p2;
+ game.block.y2 = p3;
+}
+
+cmd(unblock) {
+ game.block.active = false;
+}
+
+cmd(normal_motion) {
+ vt.motion = MOTION_NORMAL;
+}
+
+cmd(stop_motion) {
+ vt.direction = 0;
+ vt.motion = MOTION_NORMAL;
+ if (p0 == 0) { /* ego only */
+ _v[V_ego_dir] = 0;
+ game.player_control = false;
+ }
+}
+
+cmd(start_motion) {
+ vt.motion = MOTION_NORMAL;
+ if (p0 == 0) { /* ego only */
+ _v[V_ego_dir] = 0;
+ game.player_control = true;
+ }
+}
+
+cmd(player_control) {
+ game.player_control = true;
+ game.view_table[0].motion = MOTION_NORMAL;
+}
+
+cmd(program_control) {
+ game.player_control = false;
+}
+
+cmd(follow_ego) {
+ vt.motion = MOTION_FOLLOW_EGO;
+ vt.parm1 = p1 > vt.step_size ? p1 : vt.step_size;
+ vt.parm2 = p2;
+ vt.parm3 = 0xff;
+ setflag(p2, false);
+ vt.flags |= UPDATE;
+}
+
+cmd(move_obj) {
+ /* _D (_D_WARN "o=%d, x=%d, y=%d, s=%d, f=%d", p0, p1, p2, p3, p4); */
+
+ vt.motion = MOTION_MOVE_OBJ;
+ vt.parm1 = p1;
+ vt.parm2 = p2;
+ vt.parm3 = vt.step_size;
+ vt.parm4 = p4;
+
+ if (p3 != 0)
+ vt.step_size = p3;
+
+ setflag(p4, false);
+ vt.flags |= UPDATE;
+
+ if (p0 == 0)
+ game.player_control = false;
+
+ /* AGI 2.272 (ddp, xmas) doesn't call move_obj! */
+ if (agi_get_release() > 0x2272)
+ move_obj(&vt);
+}
+
+cmd(move_obj_f) {
+ vt.motion = MOTION_MOVE_OBJ;
+ vt.parm1 = _v[p1];
+ vt.parm2 = _v[p2];
+ vt.parm3 = vt.step_size;
+ vt.parm4 = p4;
+
+ if (_v[p3] != 0)
+ vt.step_size = _v[p3];
+
+ setflag(p4, false);
+ vt.flags |= UPDATE;
+
+ if (p0 == 0)
+ game.player_control = false;
+
+ /* AGI 2.272 (ddp, xmas) doesn't call move_obj! */
+ if (agi_get_release() > 0x2272)
+ move_obj(&vt);
+}
+
+cmd(wander) {
+ if (p0 == 0)
+ game.player_control = false;
+ vt.motion = MOTION_WANDER;
+ vt.flags |= UPDATE;
+}
+
+cmd(set_game_id) {
+ if (cur_logic->texts && (p0 - 1) <= cur_logic->num_texts)
+ strncpy(game.id, cur_logic->texts[p0 - 1], 8);
+ else
+ game.id[0] = 0;
+
+ report("Game ID: \"%s\"\n", game.id);
+}
+
+cmd(pause) {
+ int tmp = game.clock_enabled;
+ char *b[] = { "Continue", NULL };
+
+ game.clock_enabled = false;
+ selection_box(" Game is paused. \n\n\n", b);
+ game.clock_enabled = tmp;
+}
+
+cmd(set_menu) {
+ debugC(4, kDebugLevelScripts, "text %02x of %02x", p0, cur_logic->num_texts);
+ if (cur_logic->texts != NULL && p0 < cur_logic->num_texts)
+ menu_add(cur_logic->texts[p0 - 1]);
+}
+
+cmd(set_menu_item) {
+ debugC(4, kDebugLevelScripts, "text %02x of %02x", p0, cur_logic->num_texts);
+ if (cur_logic->texts != NULL && p0 <= cur_logic->num_texts)
+ menu_add_item(cur_logic->texts[p0 - 1], p1);
+}
+
+cmd(version) {
+ char ver_msg[] = TITLE " v" VERSION;
+ char ver2_msg[] =
+ "\n"
+ " \n\n"
+ "Emulating Sierra AGI v%x.%03x\n";
+ char ver3_msg[] =
+ "\n"
+ " \n\n"
+ " Emulating AGI v%x.002.%03x\n";
+ /* no Sierra as it wraps textbox */
+ char *r, *q;
+ int ver, maj, min;
+ char msg[256];
+
+ ver = agi_get_release();
+ maj = (ver >> 12) & 0xf;
+ min = ver & 0xfff;
+
+ q = maj == 2 ? ver2_msg : ver3_msg;
+ r = strchr(q + 1, '\n');
+
+ strncpy(q + 1 + ((r - q > 0 ? r - q : 1) / 4), ver_msg, strlen(ver_msg));
+ sprintf(msg, q, maj, min);
+ message_box(msg);
+}
+
+cmd(configure_screen) {
+ game.line_min_print = p0;
+ game.line_user_input = p1;
+ game.line_status = p2;
+}
+
+cmd(text_screen) {
+ debugC(4, kDebugLevelScripts, "switching to text mode");
+ game.gfx_mode = false;
+ /*
+ * Simulates the "bright background bit" of the PC video
+ * controller.
+ */
+ if (game.color_bg)
+ game.color_bg |= 0x08;
+ clear_screen(game.color_bg);
+}
+
+cmd(graphics) {
+ debugC(4, kDebugLevelScripts, "switching to graphics mode");
+ if (!game.gfx_mode) {
+ game.gfx_mode = true;
+ clear_screen(0);
+ show_pic();
+ write_status();
+ write_prompt();
+ }
+}
+
+cmd(set_text_attribute) {
+ game.color_fg = p0;
+ game.color_bg = p1;
+
+ if (game.gfx_mode) {
+ if (game.color_bg != 0) {
+ game.color_fg = 0;
+ game.color_bg = 15;
+ }
+ }
+}
+
+cmd(status) {
+ inventory();
+}
+
+cmd(quit) {
+ char *buttons[] = { "Quit", "Continue", NULL };
+
+ stop_sound();
+ if (p0) {
+ game.quit_prog_now = true;
+ } else {
+ if (selection_box
+ (" Quit the game, or continue? \n\n\n", buttons) == 0) {
+ game.quit_prog_now = true;
+ }
+ }
+}
+
+cmd(restart_game) {
+ char *buttons[] = { "Restart", "Continue", NULL };
+ int sel;
+
+ stop_sound();
+ sel = getflag(F_auto_restart) ? 1 :
+ selection_box(" Restart game, or continue? \n\n\n", buttons);
+
+ if (sel == 0) {
+ game.quit_prog_now = 0xff;
+ setflag(F_restart_game, true);
+ menu_enable_all();
+ }
+}
+
+cmd(distance) {
+ int16 x1, y1, x2, y2, d;
+ struct vt_entry *v0 = &game.view_table[p0];
+ struct vt_entry *v1 = &game.view_table[p1];
+
+ if (v0->flags & DRAWN && v1->flags & DRAWN) {
+ x1 = v0->x_pos + v0->x_size / 2;
+ y1 = v0->y_pos;
+ x2 = v1->x_pos + v1->x_size / 2;
+ y2 = v1->y_pos;
+ d = abs(x1 - x2) + abs(y1 - y2);
+ if (d > 0xfe)
+ d = 0xfe;
+ } else {
+ d = 0xff;
+ }
+ _v[p2] = (unsigned char)d;
+}
+
+cmd(accept_input) {
+ debugC(4, kDebugLevelScripts | kDebugLevelInput, "input normal");
+ new_input_mode(INPUT_NORMAL);
+ game.input_enabled = true;
+}
+
+cmd(prevent_input) {
+ debugC(4, kDebugLevelScripts | kDebugLevelInput, "no input");
+ new_input_mode(INPUT_NONE);
+ game.input_enabled = false;
+}
+
+cmd(get_string) {
+ int tex, row, col;
+
+ debugC(4, kDebugLevelScripts, "%d %d %d %d %d", p0, p1, p2, p3, p4);
+
+ tex = p1 - 1;
+ row = p2;
+ col = p3;
+
+ /* Workaround for SQLC bug.
+ * See bug #792125 for details
+ */
+ if (row > 24)
+ row = 24;
+ if (col > 39)
+ col = 39;
+
+ new_input_mode(INPUT_GETSTRING);
+
+ if (cur_logic->texts != NULL && cur_logic->num_texts >= tex) {
+ int len = strlen(cur_logic->texts[tex]);
+ print_text(cur_logic->texts[tex], 0, col, row, len, game.color_fg, game.color_bg);
+ get_string(col + len - 1, row, p4, p0);
+
+ /* SGEO: display input char */
+ print_character((col + len), row, game.cursor_char, game.color_fg, game.color_bg);
+ }
+
+ do {
+ main_cycle();
+ } while (game.input_mode == INPUT_GETSTRING);
+}
+
+cmd(get_num) {
+ debugC(4, kDebugLevelScripts, "%d %d", p0, p1);
+ new_input_mode(INPUT_GETSTRING);
+
+ if (cur_logic->texts != NULL && cur_logic->num_texts >= (p0 - 1)) {
+ int len = strlen(cur_logic->texts[p0 - 1]);
+ print_text(cur_logic->texts[p0 - 1], 0, 0, 22, len, game.color_fg, game.color_bg);
+ get_string(len - 1, 22, 3, MAX_STRINGS);
+
+ /* CM: display input char */
+ print_character((p3 + len), 22, game.cursor_char, game.color_fg, game.color_bg);
+ }
+
+ do {
+ main_cycle();
+ } while (game.input_mode == INPUT_GETSTRING);
+
+ _v[p1] = atoi(game.strings[MAX_STRINGS]);
+ debugC(4, kDebugLevelScripts, "[%s] -> %d", game.strings[MAX_STRINGS], _v[p1]);
+ clear_lines(22, 22, game.color_bg);
+ flush_lines(22, 22);
+}
+
+cmd(set_cursor_char) {
+ if (cur_logic->texts != NULL && (p0 - 1) <= cur_logic->num_texts) {
+ game.cursor_char = *cur_logic->texts[p0 - 1];
+ } else {
+ /* default */
+ game.cursor_char = '_';
+ }
+}
+
+cmd(set_key) {
+ int key;
+
+ debugC(4, kDebugLevelScripts, "%d %d %d", p0, p1, p2);
+
+ if (game.ev_keyp[p2].data != 0) /* TBC sets c23 (ESC) twice! */
+ return;
+
+ key = 256 * p1 + p0;
+ game.ev_keyp[p2].data = key;
+ game.ev_keyp[p2].occured = false;
+}
+
+cmd(set_string) {
+ /* CM: to avoid crash in Groza (str = 150) */
+ if (p0 > MAX_STRINGS)
+ return;
+ strcpy(game.strings[p0], cur_logic->texts[p1 - 1]);
+}
+
+cmd(display) {
+ print_text(cur_logic->texts[p2 - 1], p1, 0, p0, 40, game.color_fg, game.color_bg);
+}
+
+cmd(display_f) {
+ debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+ print_text(cur_logic->texts[_v[p2] - 1], _v[p1], 0, _v[p0], 40, game.color_fg, game.color_bg);
+}
+
+cmd(clear_text_rect) {
+ int c, x1, y1, x2, y2;
+
+ if ((c = p4) != 0)
+ c = 15;
+ x1 = p1 * CHAR_COLS;
+ y1 = p0 * CHAR_LINES;
+ x2 = (p3 + 1) * CHAR_COLS - 1;
+ y2 = (p2 + 1) * CHAR_LINES - 1;
+
+ /* Added to prevent crash with x2 = 40 in the iigs demo */
+ if (x1 > GFX_WIDTH)
+ x1 = GFX_WIDTH - 1;
+ if (x2 > GFX_WIDTH)
+ x2 = GFX_WIDTH - 1;
+ if (y1 > GFX_HEIGHT)
+ y1 = GFX_HEIGHT - 1;
+ if (y2 > GFX_HEIGHT)
+ y2 = GFX_HEIGHT - 1;
+
+ draw_rectangle(x1, y1, x2, y2, c);
+ flush_block(x1, y1, x2, y2);
+}
+
+cmd(toggle_monitor) {
+ report("toggle.monitor\n");
+#ifdef USE_HIRES
+ opt.hires = !opt.hires;
+ erase_both();
+ show_pic();
+ blit_both();
+#endif
+}
+
+cmd(echo_line) {
+ strcpy((char *)game.input_buffer, (const char *)game.echo_buffer);
+ game.cursor_pos = strlen((char *)game.input_buffer);
+ game.has_prompt = 0;
+}
+
+cmd(clear_lines) {
+ uint8 l;
+
+ /* Residence 44 calls clear.lines(24,0,0), see bug #558423 */
+ l = p1 ? p1 : p0;
+
+ clear_lines(p0, l, p2);
+ flush_lines(p0, l);
+}
+
+cmd(print) {
+ int n = p0 < 1 ? 1 : p0;
+ print(cur_logic->texts[n - 1], 0, 0, 0);
+}
+
+cmd(print_f) {
+ int n = _v[p0] < 1 ? 1 : _v[p0];
+ print(cur_logic->texts[n - 1], 0, 0, 0);
+}
+
+cmd(print_at) {
+ int n = p0 < 1 ? 1 : p0;
+ debugC(4, kDebugLevelScripts, "%d %d %d %d", p0, p1, p2, p3);
+ print(cur_logic->texts[n - 1], p1, p2, p3);
+}
+
+cmd(print_at_v) {
+ int n = _v[p0] < 1 ? 1 : _v[p0];
+ print(cur_logic->texts[n - 1], p1, p2, p3);
+}
+
+cmd(push_script) {
+#ifdef USE_MOUSE
+ if (opt.agimouse) {
+ game.vars[27] = mouse.button;
+ game.vars[28] = mouse.x / 2;
+ game.vars[29] = mouse.y;
+ } else
+#endif
+ report("push.script\n");
+}
+
+cmd(set_pri_base) {
+ int i, x, pri;
+
+ report("Priority base set to %d\n", p0);
+
+ /* game.alt_pri = true; */
+ x = (_HEIGHT - p0) * _HEIGHT / 10;
+
+ for (i = 0; i < _HEIGHT; i++) {
+ pri = (i - p0) < 0 ? 4 : (i - p0) * _HEIGHT / x + 5;
+ if (pri > 15)
+ pri = 15;
+ game.pri_table[i] = pri;
+ }
+}
+
+cmd(mouse_posn) {
+#ifdef USE_MOUSE
+ _v[p0] = WIN_TO_PIC_X(mouse.x);
+ _v[p1] = WIN_TO_PIC_Y(mouse.y);
+#endif
+}
+
+cmd(shake_screen) {
+ int i;
+
+#ifdef USE_MOUSE
+ /* AGI Mouse 1.1 uses shake.screen values between 100 and 109 to
+ * set the palette.
+ */
+ if (opt.agimouse && p0 >= 100 && p0 < 110) {
+ report("not implemented: AGI Mouse palettes\n");
+ return;
+ } else
+#endif
+ shake_start();
+
+ commit_both(); /* Fixes SQ1 demo */
+ for (i = 4 * p0; i; i--) {
+ shake_screen(i & 1);
+ flush_block(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
+ main_cycle();
+ }
+ shake_end();
+}
+
+static void (*agi_command[183]) (uint8 *) = {
+ NULL, /* 0x00 */
+ cmd_increment,
+ cmd_decrement,
+ cmd_assignn,
+ cmd_assignv,
+ cmd_addn,
+ cmd_addv,
+ cmd_subn,
+ cmd_subv, /* 0x08 */
+ cmd_lindirectv,
+ cmd_rindirect,
+ cmd_lindirectn,
+ cmd_set,
+ cmd_reset,
+ cmd_toggle,
+ cmd_set_v,
+ cmd_reset_v, /* 0x10 */
+ cmd_toggle_v,
+ cmd_new_room,
+ cmd_new_room_f,
+ cmd_load_logic,
+ cmd_load_logic_f,
+ cmd_call,
+ cmd_call_f,
+ cmd_load_pic, /* 0x18 */
+ cmd_draw_pic,
+ cmd_show_pic,
+ cmd_discard_pic,
+ cmd_overlay_pic,
+ cmd_show_pri_screen,
+ cmd_load_view,
+ cmd_load_view_f,
+ cmd_discard_view, /* 0x20 */
+ cmd_animate_obj,
+ cmd_unanimate_all,
+ cmd_draw,
+ cmd_erase,
+ cmd_position,
+ cmd_position_f,
+ cmd_get_posn,
+ cmd_reposition, /* 0x28 */
+ cmd_set_view,
+ cmd_set_view_f,
+ cmd_set_loop,
+ cmd_set_loop_f,
+ cmd_fix_loop,
+ cmd_release_loop,
+ cmd_set_cel,
+ cmd_set_cel_f, /* 0x30 */
+ cmd_last_cel,
+ cmd_current_cel,
+ cmd_current_loop,
+ cmd_current_view,
+ cmd_number_of_loops,
+ cmd_set_priority,
+ cmd_set_priority_f,
+ cmd_release_priority, /* 0x38 */
+ cmd_get_priority,
+ cmd_stop_update,
+ cmd_start_update,
+ cmd_force_update,
+ cmd_ignore_horizon,
+ cmd_observe_horizon,
+ cmd_set_horizon,
+ cmd_object_on_water, /* 0x40 */
+ cmd_object_on_land,
+ cmd_object_on_anything,
+ cmd_ignore_objs,
+ cmd_observe_objs,
+ cmd_distance,
+ cmd_stop_cycling,
+ cmd_start_cycling,
+ cmd_normal_cycle, /* 0x48 */
+ cmd_end_of_loop,
+ cmd_reverse_cycle,
+ cmd_reverse_loop,
+ cmd_cycle_time,
+ cmd_stop_motion,
+ cmd_start_motion,
+ cmd_step_size,
+ cmd_step_time, /* 0x50 */
+ cmd_move_obj,
+ cmd_move_obj_f,
+ cmd_follow_ego,
+ cmd_wander,
+ cmd_normal_motion,
+ cmd_set_dir,
+ cmd_get_dir,
+ cmd_ignore_blocks, /* 0x58 */
+ cmd_observe_blocks,
+ cmd_block,
+ cmd_unblock,
+ cmd_get,
+ cmd_get_f,
+ cmd_drop,
+ cmd_put,
+ cmd_put_f, /* 0x60 */
+ cmd_get_room_f,
+ cmd_load_sound,
+ cmd_sound,
+ cmd_stop_sound,
+ cmd_print,
+ cmd_print_f,
+ cmd_display,
+ cmd_display_f, /* 0x68 */
+ cmd_clear_lines,
+ cmd_text_screen,
+ cmd_graphics,
+ cmd_set_cursor_char,
+ cmd_set_text_attribute,
+ cmd_shake_screen,
+ cmd_configure_screen,
+ cmd_status_line_on, /* 0x70 */
+ cmd_status_line_off,
+ cmd_set_string,
+ cmd_get_string,
+ cmd_word_to_string,
+ cmd_parse,
+ cmd_get_num,
+ cmd_prevent_input,
+ cmd_accept_input, /* 0x78 */
+ cmd_set_key,
+ cmd_add_to_pic,
+ cmd_add_to_pic_f,
+ cmd_status,
+ cmd_save_game,
+ cmd_load_game,
+ cmd_init_disk,
+ cmd_restart_game, /* 0x80 */
+ cmd_show_obj,
+ cmd_random,
+ cmd_program_control,
+ cmd_player_control,
+ cmd_obj_status_f,
+ cmd_quit,
+ cmd_show_mem,
+ cmd_pause, /* 0x88 */
+ cmd_echo_line,
+ cmd_cancel_line,
+ cmd_init_joy,
+ cmd_toggle_monitor,
+ cmd_version,
+ cmd_script_size,
+ cmd_set_game_id,
+ cmd_log, /* 0x90 */
+ cmd_set_scan_start,
+ cmd_reset_scan_start,
+ cmd_reposition_to,
+ cmd_reposition_to_f,
+ cmd_trace_on,
+ cmd_trace_info,
+ cmd_print_at,
+ cmd_print_at_v, /* 0x98 */
+ cmd_discard_view,
+ cmd_clear_text_rect,
+ cmd_set_upper_left,
+ cmd_set_menu,
+ cmd_set_menu_item,
+ cmd_submit_menu,
+ cmd_enable_item,
+ cmd_disable_item, /* 0xa0 */
+ cmd_menu_input,
+ cmd_show_obj_v,
+ cmd_open_dialogue,
+ cmd_close_dialogue,
+ cmd_mul_n,
+ cmd_mul_v,
+ cmd_div_n,
+ cmd_div_v, /* 0xa8 */
+ cmd_close_window,
+ cmd_set_simple,
+ cmd_push_script,
+ cmd_pop_script,
+ cmd_hold_key,
+ cmd_set_pri_base,
+ cmd_discard_sound,
+ cmd_hide_mouse, /* 0xb0 */
+ cmd_allow_menu,
+ cmd_show_mouse,
+ cmd_fence_mouse,
+ cmd_mouse_posn,
+ cmd_release_key,
+ cmd_adj_ego_move_to_x_y
+};
+
+#define CMD_BSIZE 12
+
+/**
+ * Execute a logic script
+ * @param n Number of the logic resource to execute
+ */
+int run_logic(int n) {
+ uint8 op = 0;
+ uint8 p[CMD_BSIZE] = { 0 };
+ uint8 *code = NULL;
+ int num = 0;
+
+ /* If logic not loaded, load it */
+ if (~game.dir_logic[n].flags & RES_LOADED) {
+ debugC(4, kDebugLevelScripts, "logic %d not loaded!", n);
+ agi_load_resource(rLOGIC, n);
+ }
+
+ game.lognum = n;
+ cur_logic = &game.logics[game.lognum];
+
+ code = cur_logic->data;
+ cur_logic->cIP = cur_logic->sIP;
+
+ timer_hack = 0;
+ while (ip < game.logics[n].size && !game.quit_prog_now) {
+#ifdef USE_CONSOLE
+ if (debug_.enabled) {
+ if (debug_.steps > 0) {
+ if (debug_.logic0 || n) {
+ debug_console(n, lCOMMAND_MODE, NULL);
+ debug_.steps--;
+ }
+ } else {
+ blit_both();
+ console_prompt();
+ do {
+ main_cycle();
+ } while (!debug_.steps && debug_.enabled);
+ console_lock();
+ erase_both();
+ }
+ }
+#endif
+
+ switch (op = *(code + ip++)) {
+ case 0xff: /* if (open/close) */
+ test_if_code(n);
+ break;
+ case 0xfe: /* goto */
+ /* +2 covers goto size */
+ ip += 2 + ((int16)READ_LE_UINT16(code + ip));
+ /* timer must keep running even in goto loops,
+ * but AGI engine can't do that :(
+ */
+ if (timer_hack > 20) {
+ poll_timer();
+ update_timer();
+ timer_hack = 0;
+ }
+ break;
+ case 0x00: /* return */
+ return 1;
+ default:
+ num = logic_names_cmd[op].num_args;
+ memmove(p, code + ip, num);
+ memset(p + num, 0, CMD_BSIZE - num);
+ agi_command[op] (p);
+ ip += num;
+ }
+
+ if (game.exit_all_logics)
+ break;
+ }
+
+ return 0; /* after executing new.room() */
+}
+
+void execute_agi_command(uint8 op, uint8 *p) {
+#ifdef USE_CONSOLE
+ debugC(2, kDebugLevelScripts, "%s %d %d %d", logic_names_cmd[op].name, p[0], p[1], p[2]);
+#endif
+ agi_command[op] (p);
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/op_dbg.cpp b/engines/agi/op_dbg.cpp
new file mode 100644
index 0000000000..2a8af3bc68
--- /dev/null
+++ b/engines/agi/op_dbg.cpp
@@ -0,0 +1,353 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/opcodes.h"
+
+namespace Agi {
+
+#define ip (game.logics[lognum].cIP)
+#define code (game.logics[lognum].data)
+
+#ifdef _L
+#undef _L
+#endif
+
+#ifdef USE_CONSOLE
+#define _L(a,b,c) { a, b, c }
+#else
+#define _L(a,b,c) { b, c }
+#endif
+
+struct agi_logicnames logic_names_test[] = {
+ _L("", 0, 0x00),
+ _L("equaln", 2, 0x80),
+ _L("equalv", 2, 0xC0),
+ _L("lessn", 2, 0x80),
+ _L("lessv", 2, 0xC0),
+ _L("greatern", 2, 0x80),
+ _L("greaterv", 2, 0xC0),
+ _L("isset", 1, 0x00),
+ _L("issetv", 1, 0x80),
+ _L("has", 1, 0x00),
+ _L("obj.in.room", 2, 0x40),
+ _L("posn", 5, 0x00),
+ _L("controller", 1, 0x00),
+ _L("have.key", 0, 0x00),
+
+ /* Not 0 args. Has variable number. */
+ _L("said", 0, 0x00),
+
+ _L("compare.strings", 2, 0x00),
+ _L("obj.in.box", 5, 0x00),
+ _L("center.posn", 5, 0x00),
+ _L("right.posn", 5, 0x00)
+};
+
+struct agi_logicnames logic_names_if[] = {
+ _L("OR", 0, 0x00),
+ _L("NOT", 0, 0x00),
+ _L("ELSE", 0, 0x00),
+ _L("IF", 0, 0x00)
+};
+
+struct agi_logicnames logic_names_cmd[] = {
+ _L("return", 0, 0x00), /* 00 */
+ _L("increment", 1, 0x80), /* 01 */
+ _L("decrement", 1, 0x80), /* 02 */
+ _L("assignn", 2, 0x80), /* 03 */
+ _L("assignv", 2, 0xC0), /* 04 */
+ _L("addn", 2, 0x80), /* 05 */
+ _L("addv", 2, 0xC0), /* 06 */
+ _L("subn", 2, 0x80), /* 07 */
+ _L("subv", 2, 0xC0), /* 08 */
+ _L("lindirectv", 2, 0xC0), /* 09 */
+ _L("rindirect", 2, 0xC0), /* 0A */
+ _L("lindirectn", 2, 0x80), /* 0B */
+ _L("set", 1, 0x00), /* 0C */
+ _L("reset", 1, 0x00), /* 0D */
+ _L("toggle", 1, 0x00), /* 0E */
+ _L("set.v", 1, 0x80), /* 0F */
+ _L("reset.v", 1, 0x80), /* 10 */
+ _L("toggle.v", 1, 0x80), /* 11 */
+ _L("new.room", 1, 0x00), /* 12 */
+ _L("new.room.v", 1, 0x80), /* 13 */
+ _L("load.logics", 1, 0x00), /* 14 */
+ _L("load.logics.v", 1, 0x80), /* 15 */
+ _L("call", 1, 0x00), /* 16 */
+ _L("call.v", 1, 0x80), /* 17 */
+ _L("load.pic", 1, 0x80), /* 18 */
+ _L("draw.pic", 1, 0x80), /* 19 */
+ _L("show.pic", 0, 0x00), /* 1A */
+ _L("discard.pic", 1, 0x80), /* 1B */
+ _L("overlay.pic", 1, 0x80), /* 1C */
+ _L("show.pri.screen", 0, 0x00), /* 1D */
+ _L("load.view", 1, 0x00), /* 1E */
+ _L("load.view.v", 1, 0x80), /* 1F */
+ _L("discard.view", 1, 0x00), /* 20 */
+ _L("animate.obj", 1, 0x00), /* 21 */
+ _L("unanimate.all", 0, 0x00), /* 22 */
+ _L("draw", 1, 0x00), /* 23 */
+ _L("erase", 1, 0x00), /* 24 */
+ _L("position", 3, 0x00), /* 25 */
+ _L("position.v", 3, 0x60), /* 26 */
+ _L("get.posn", 3, 0x60), /* 27 */
+ _L("reposition", 3, 0x60), /* 28 */
+ _L("set.view", 2, 0x00), /* 29 */
+ _L("set.view.v", 2, 0x40), /* 2A */
+ _L("set.loop", 2, 0x00), /* 2B */
+ _L("set.loop.v", 2, 0x40), /* 2C */
+ _L("fix.loop", 1, 0x00), /* 2D */
+ _L("release.loop", 1, 0x00), /* 2E */
+ _L("set.cel", 2, 0x00), /* 2F */
+ _L("set.cel.v", 2, 0x40), /* 30 */
+ _L("last.cel", 2, 0x40), /* 31 */
+ _L("current.cel", 2, 0x40), /* 32 */
+ _L("current.loop", 2, 0x40), /* 33 */
+ _L("current.view", 2, 0x40), /* 34 */
+ _L("number.of.loops", 2, 0x40), /* 35 */
+ _L("set.priority", 2, 0x00), /* 36 */
+ _L("set.priority.v", 2, 0x40), /* 37 */
+ _L("release.priority", 1, 0x00), /* 38 */
+ _L("get.priority", 2, 0x40), /* 39 */
+ _L("stop.update", 1, 0x00), /* 3A */
+ _L("start.update", 1, 0x00), /* 3B */
+ _L("force.update", 1, 0x00), /* 3C */
+ _L("ignore.horizon", 1, 0x00), /* 3D */
+ _L("observe.horizon", 1, 0x00), /* 3E */
+ _L("set.horizon", 1, 0x00), /* 3F */
+ _L("object.on.water", 1, 0x00), /* 40 */
+ _L("object.on.land", 1, 0x00), /* 41 */
+ _L("object.on.anything", 1, 0x00), /* 42 */
+ _L("ignore.objs", 1, 0x00), /* 43 */
+ _L("observe.objs", 1, 0x00), /* 44 */
+ _L("distance", 3, 0x20), /* 45 */
+ _L("stop.cycling", 1, 0x00), /* 46 */
+ _L("start.cycling", 1, 0x00), /* 47 */
+ _L("normal.cycle", 1, 0x00), /* 48 */
+ _L("end.of.loop", 2, 0x00), /* 49 */
+ _L("reverse.cycle", 1, 0x00), /* 4A */
+ _L("reverse.loop", 2, 0x00), /* 4B */
+ _L("cycle.time", 2, 0x40), /* 4C */
+ _L("stop.motion", 1, 0x00), /* 4D */
+ _L("start.motion", 1, 0x00), /* 4E */
+ _L("step.size", 2, 0x40), /* 4F */
+ _L("step.time", 2, 0x40), /* 50 */
+ _L("move.obj", 5, 0x00), /* 51 */
+ _L("move.obj.v", 5, 0x70), /* 52 */
+ _L("follow.ego", 3, 0x00), /* 53 */
+ _L("wander", 1, 0x00), /* 54 */
+ _L("normal.motion", 1, 0x00), /* 55 */
+ _L("set.dir", 2, 0x40), /* 56 */
+ _L("get.dir", 2, 0x40), /* 57 */
+ _L("ignore.blocks", 1, 0x00), /* 58 */
+ _L("observe.blocks", 1, 0x00), /* 59 */
+ _L("block", 4, 0x00), /* 5A */
+ _L("unblock", 0, 0x00), /* 5B */
+ _L("get", 1, 0x00), /* 5C */
+ _L("get.v", 1, 0x80), /* 5D */
+ _L("drop", 1, 0x00), /* 5E */
+ _L("put", 2, 0x00), /* 5F */
+ _L("put.v", 2, 0x40), /* 60 */
+ _L("get.room.v", 2, 0xC0), /* 61 */
+ _L("load.sound", 1, 0x00), /* 62 */
+ _L("sound", 2, 0x00), /* 63 */
+ _L("stop.sound", 0, 0x00), /* 64 */
+ _L("print", 1, 0x00), /* 65 */
+ _L("print.v", 1, 0x80), /* 66 */
+ _L("display", 3, 0x00), /* 67 */
+ _L("display.v", 3, 0xE0), /* 68 */
+ _L("clear.lines", 3, 0x00), /* 69 */
+ _L("text.screen", 0, 0x00), /* 6A */
+ _L("graphics", 0, 0x00), /* 6B */
+ _L("set.cursor.char", 1, 0x00), /* 6C */
+ _L("set.text.attribute", 2, 0x00), /* 6D */
+ _L("shake.screen", 1, 0x00), /* 6E */
+ _L("configure.screen", 3, 0x00), /* 6F */
+ _L("status.line.on", 0, 0x00), /* 70 */
+ _L("status.line.off", 0, 0x00), /* 71 */
+ _L("set.string", 2, 0x00), /* 72 */
+ _L("get.string", 5, 0x00), /* 73 */
+ _L("word.to.string", 2, 0x00), /* 74 */
+ _L("parse", 1, 0x00), /* 75 */
+ _L("get.num", 2, 0x40), /* 76 */
+ _L("prevent.input", 0, 0x00), /* 77 */
+ _L("accept.input", 0, 0x00), /* 78 */
+ _L("set.key", 3, 0x00), /* 79 */
+ _L("add.to.pic", 7, 0x00), /* 7A */
+ _L("add.to.pic.v", 7, 0xFE), /* 7B */
+ _L("status", 0, 0x00), /* 7C */
+ _L("save.game", 0, 0x00), /* 7D */
+ _L("restore.game", 0, 0x00), /* 7E */
+ _L("init.disk", 0, 0x00), /* 7F */
+ _L("restart.game", 0, 0x00), /* 80 */
+ _L("show.obj", 1, 0x00), /* 81 */
+ _L("random", 3, 0x20), /* 82 */
+ _L("program.control", 0, 0x00), /* 83 */
+ _L("player.control", 0, 0x00), /* 84 */
+ _L("obj.status.v", 1, 0x80), /* 85 */
+ /* 0 args for AGI version 2.089 */
+ _L("quit", 1, 0x00), /* 86 */
+
+ _L("show.mem", 0, 0x00), /* 87 */
+ _L("pause", 0, 0x00), /* 88 */
+ _L("echo.line", 0, 0x00), /* 89 */
+ _L("cancel.line", 0, 0x00), /* 8A */
+ _L("init.joy", 0, 0x00), /* 8B */
+ _L("toggle.monitor", 0, 0x00), /* 8C */
+ _L("version", 0, 0x00), /* 8D */
+ _L("script.size", 1, 0x00), /* 8E */
+ _L("set.game.id", 1, 0x00), /* 8F */
+ _L("log", 1, 0x00), /* 90 */
+ _L("set.scan.start", 0, 0x00), /* 91 */
+ _L("reset.scan.start", 0, 0x00), /* 92 */
+ _L("reposition.to", 3, 0x00), /* 93 */
+ _L("reposition.to.v", 3, 0x60), /* 94 */
+ _L("trace.on", 0, 0x00), /* 95 */
+ _L("trace.info", 3, 0x00), /* 96 */
+
+ /* 3 args for AGI versions before 2.440 */
+ _L("print.at", 4, 0x00), /* 97 */
+ _L("print.at.v", 4, 0x80), /* 98 */
+
+ _L("discard.view.v", 1, 0x80), /* 99 */
+ _L("clear.text.rect", 5, 0x00), /* 9A */
+ _L("set.upper.left", 2, 0x00), /* 9B */
+ _L("set.menu", 1, 0x00), /* 9C */
+ _L("set.menu.item", 2, 0x00), /* 9D */
+ _L("submit.menu", 0, 0x00), /* 9E */
+ _L("enable.item", 1, 0x00), /* 9F */
+ _L("disable.item", 1, 0x00), /* A0 */
+ _L("menu.input", 0, 0x00), /* A1 */
+ _L("show.obj.v", 1, 0x01), /* A2 */
+ _L("open.dialogue", 0, 0x00), /* A3 */
+ _L("close.dialogue", 0, 0x00), /* A4 */
+ _L("mul.n", 2, 0x80), /* A5 */
+ _L("mul.v", 2, 0xC0), /* A6 */
+ _L("div.n", 2, 0x80), /* A7 */
+ _L("div.v", 2, 0xC0), /* A8 */
+ _L("close.window", 0, 0x00), /* A9 */
+
+ _L("set.simple", 1, 0x00), /* AA */
+ _L("push.script", 0, 0x00), /* AB */
+ _L("pop.script", 0, 0x00), /* AC */
+ _L("hold.key", 0, 0x00), /* AD */
+ _L("set.pri.base", 1, 0x00), /* AE */
+ _L("discard.sound", 1, 0x00), /* AF */
+
+ /* 1 arg for AGI version 3.002.086 */
+ _L("hide.mouse", 0, 0x00), /* B0 */
+
+ _L("allow.menu", 1, 0x00), /* B1 */
+ _L("show.mouse", 0, 0x00), /* B2 */
+ _L("fence.mouse", 4, 0x00), /* B3 */
+ _L("mouse.posn", 2, 0x00), /* B4 */
+ _L("release.key", 0, 0x00), /* B5 */
+ _L("adj.ego.move.to.xy", 0, 0x00), /* B6 */
+ _L(NULL, 0, 0x00)
+};
+
+#ifdef USE_CONSOLE
+
+void debug_console(int lognum, int mode, char *str) {
+ struct agi_logicnames *x;
+ uint8 a, c, z;
+
+ if (str) {
+ report(" %s\n", str);
+ return;
+ }
+
+ report("%03d:%04x ", lognum, ip);
+
+ switch (*(code + ip)) {
+ case 0xFC:
+ case 0xFD:
+ case 0xFE:
+ case 0xFF:
+ x = logic_names_if;
+
+ if (debug_.opcodes) {
+ report("%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
+ " ",
+ (uint8) * (code + (0 + ip)) & 0xFF,
+ (uint8) * (code + (1 + ip)) & 0xFF,
+ (uint8) * (code + (2 + ip)) & 0xFF,
+ (uint8) * (code + (3 + ip)) & 0xFF,
+ (uint8) * (code + (4 + ip)) & 0xFF,
+ (uint8) * (code + (5 + ip)) & 0xFF,
+ (uint8) * (code + (6 + ip)) & 0xFF,
+ (uint8) * (code + (7 + ip)) & 0xFF,
+ (uint8) * (code + (8 + ip)) & 0xFF);
+ }
+ report("%s ", (x + *(code + ip) - 0xFC)->name);
+ break;
+ default:
+ x = mode == lCOMMAND_MODE ? logic_names_cmd : logic_names_test;
+ a = (unsigned char)(x + *(code + ip))->num_args;
+ c = (unsigned char)(x + *(code + ip))->arg_mask;
+
+ if (debug_.opcodes) {
+ report("%02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
+ " ",
+ (uint8) * (code + (0 + ip)) & 0xFF,
+ (uint8) * (code + (1 + ip)) & 0xFF,
+ (uint8) * (code + (2 + ip)) & 0xFF,
+ (uint8) * (code + (3 + ip)) & 0xFF,
+ (uint8) * (code + (4 + ip)) & 0xFF,
+ (uint8) * (code + (5 + ip)) & 0xFF,
+ (uint8) * (code + (6 + ip)) & 0xFF,
+ (uint8) * (code + (7 + ip)) & 0xFF,
+ (uint8) * (code + (8 + ip)) & 0xFF);
+ }
+ report("%s ", (x + *(code + ip))->name);
+
+ for (z = 1; a > 0;) {
+ if (~c & 0x80) {
+ report("%d", *(code + (ip + z)));
+ } else {
+ report("v%d[%d]", *(code + (ip + z)), getvar(*(code + (ip + z))));
+ }
+ c <<= 1;
+ z++;
+ if (--a > 0)
+ report(",");
+ }
+ break;
+ }
+
+ report("\n");
+}
+
+#else
+
+void debug_console(int lognum, int mode, char *str) {
+ /* dummy */
+}
+
+#endif /* USE_CONSOLE */
+
+} // End of namespace Agi
diff --git a/engines/agi/op_test.cpp b/engines/agi/op_test.cpp
new file mode 100644
index 0000000000..d4a98bb0ac
--- /dev/null
+++ b/engines/agi/op_test.cpp
@@ -0,0 +1,419 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/keyboard.h"
+#include "agi/opcodes.h"
+
+namespace Agi {
+
+static uint8 test_obj_right(uint8, uint8, uint8, uint8, uint8);
+static uint8 test_obj_centre(uint8, uint8, uint8, uint8, uint8);
+static uint8 test_obj_in_box(uint8, uint8, uint8, uint8, uint8);
+static uint8 test_posn(uint8, uint8, uint8, uint8, uint8);
+static uint8 test_said(uint8, uint8 *);
+static uint8 test_controller(uint8);
+static uint8 test_keypressed(void);
+static uint8 test_compare_strings(uint8, uint8);
+
+#define ip (game.logics[lognum].cIP)
+#define code (game.logics[lognum].data)
+
+#define test_equal(v1,v2) (getvar(v1) == (v2))
+#define test_less(v1,v2) (getvar(v1) < (v2))
+#define test_greater(v1,v2) (getvar(v1) > (v2))
+#define test_isset(flag) (getflag (flag))
+#define test_has(obj) (object_get_location (obj) == EGO_OWNED)
+#define test_obj_in_room(obj,v) (object_get_location (obj) == getvar (v))
+
+extern int timer_hack; /* For the timer loop in MH1 logic 153 */
+
+static uint8 test_compare_strings(uint8 s1, uint8 s2) {
+ char ms1[MAX_STRINGLEN];
+ char ms2[MAX_STRINGLEN];
+ int j, k, l;
+
+ strcpy(ms1, game.strings[s1]);
+ strcpy(ms2, game.strings[s2]);
+
+ l = strlen(ms1);
+ for (k = 0, j = 0; k < l; k++) {
+ switch (ms1[k]) {
+ case 0x20:
+ case 0x09:
+ case '-':
+ case '.':
+ case ',':
+ case ':':
+ case ';':
+ case '!':
+ case '\'':
+ break;
+
+ default:
+ ms1[j++] = toupper(ms1[k]);
+ break;
+ }
+ }
+ ms1[j] = 0x0;
+
+ l = strlen(ms2);
+ for (k = 0, j = 0; k < l; k++) {
+ switch (ms2[k]) {
+ case 0x20:
+ case 0x09:
+ case '-':
+ case '.':
+ case ',':
+ case ':':
+ case ';':
+ case '!':
+ case '\'':
+ break;
+
+ default:
+ ms2[j++] = toupper(ms2[k]);
+ break;
+ }
+ }
+ ms2[j] = 0x0;
+
+ return !strcmp(ms1, ms2);
+}
+
+static uint8 test_keypressed() {
+ int x = game.keypress;
+
+ game.keypress = 0;
+ if (!x) {
+ int mode = game.input_mode;
+ game.input_mode = INPUT_NONE;
+ main_cycle();
+ game.input_mode = mode;
+ }
+
+ if (x)
+ debugC(5, kDebugLevelScripts | kDebugLevelInput, "keypress = %02x", x);
+
+ return x;
+}
+
+static uint8 test_controller(uint8 cont) {
+ return game.ev_keyp[cont].occured;
+}
+
+static uint8 test_posn(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ struct vt_entry *v = &game.view_table[n];
+ uint8 r;
+
+ r = v->x_pos >= x1 && v->y_pos >= y1 && v->x_pos <= x2 && v->y_pos <= y2;
+
+ debugC(7, kDebugLevelScripts, "(%d,%d) in (%d,%d,%d,%d): %s", v->x_pos, v->y_pos, x1, y1, x2, y2, r ? "true" : "false");
+
+ return r;
+}
+
+static uint8 test_obj_in_box(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ struct vt_entry *v = &game.view_table[n];
+
+ return v->x_pos >= x1 &&
+ v->y_pos >= y1 && v->x_pos + v->x_size - 1 <= x2 && v->y_pos <= y2;
+}
+
+/* if n is in centre of box */
+static uint8 test_obj_centre(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ struct vt_entry *v = &game.view_table[n];
+
+ return v->x_pos + v->x_size / 2 >= x1 &&
+ v->x_pos + v->x_size / 2 <= x2 && v->y_pos >= y1 && v->y_pos <= y2;
+}
+
+/* if nect N is in right corner */
+static uint8 test_obj_right(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ struct vt_entry *v = &game.view_table[n];
+
+ return v->x_pos + v->x_size - 1 >= x1 &&
+ v->x_pos + v->x_size - 1 <= x2 && v->y_pos >= y1 && v->y_pos <= y2;
+}
+
+/* When player has entered something, it is parsed elsewhere */
+static uint8 test_said(uint8 nwords, uint8 *cc) {
+ int c, n = game.num_ego_words;
+ int z = 0;
+
+ if (getflag(F_said_accepted_input) || !getflag(F_entered_cli))
+ return false;
+
+ /* FR:
+ * I think the reason for the code below is to add some speed....
+ *
+ * if (nwords != num_ego_words)
+ * return false;
+ *
+ * In the disco scene in Larry 1 when you type "examine blonde",
+ * inside the logic is expected ( said("examine", "blonde", "rol") )
+ * where word("rol") = 9999
+ *
+ * According to the interpreter code 9999 means that whatever the
+ * user typed should be correct, but it looks like code 9999 means that
+ * if the string is empty at this point, the entry is also correct...
+ *
+ * With the removal of this code, the behaviour of the scene was
+ * corrected
+ */
+
+ for (c = 0; nwords && n; c++, nwords--, n--) {
+ z = READ_LE_UINT16(cc);
+ cc += 2;
+
+ switch (z) {
+ case 9999: /* rest of line (empty string counts to...) */
+ nwords = 1;
+ break;
+ case 1: /* any word */
+ break;
+ default:
+ if (game.ego_words[c].id != z)
+ return false;
+ break;
+ }
+ }
+
+ /* The entry string should be entirely parsed, or last word = 9999 */
+ if (n && z != 9999)
+ return false;
+
+ /* The interpreter string shouldn't be entirely parsed, but next
+ * word must be 9999.
+ */
+ if (nwords != 0 && READ_LE_UINT16(cc) != 9999)
+ return false;
+
+ setflag(F_said_accepted_input, true);
+
+ return true;
+}
+
+int test_if_code(int lognum) {
+ int ec = true;
+ int retval = true;
+ uint8 op = 0;
+ uint8 not_test = false;
+ uint8 or_test = false;
+ uint16 last_ip = ip;
+ uint8 p[16] = { 0 };
+
+ while (retval && !game.quit_prog_now) {
+#ifdef USE_CONSOLE
+ if (debug_.enabled && (debug_.logic0 || lognum))
+ debug_console(lognum, lTEST_MODE, NULL);
+#endif
+
+ last_ip = ip;
+ op = *(code + ip++);
+ memmove(p, (code + ip), 16);
+
+ switch (op) {
+ case 0xFF: /* END IF, TEST true */
+ goto end_test;
+ case 0xFD:
+ not_test = !not_test;
+ continue;
+ case 0xFC: /* OR */
+ /* if or_test is ON and we hit 0xFC, end of OR, then
+ * or is STILL false so break.
+ */
+ if (or_test) {
+ ec = false;
+ retval = false;
+ goto end_test;
+ }
+
+ or_test = true;
+ continue;
+
+ case 0x00:
+ /* return true? */
+ goto end_test;
+ case 0x01:
+ ec = test_equal(p[0], p[1]);
+ if (p[0] == 11)
+ timer_hack++;
+ break;
+ case 0x02:
+ ec = test_equal(p[0], getvar(p[1]));
+ if (p[0] == 11 || p[1] == 11)
+ timer_hack++;
+ break;
+ case 0x03:
+ ec = test_less(p[0], p[1]);
+ if (p[0] == 11)
+ timer_hack++;
+ break;
+ case 0x04:
+ ec = test_less(p[0], getvar(p[1]));
+ if (p[0] == 11 || p[1] == 11)
+ timer_hack++;
+ break;
+ case 0x05:
+ ec = test_greater(p[0], p[1]);
+ if (p[0] == 11)
+ timer_hack++;
+ break;
+ case 0x06:
+ ec = test_greater(p[0], getvar(p[1]));
+ if (p[0] == 11 || p[1] == 11)
+ timer_hack++;
+ break;
+ case 0x07:
+ ec = test_isset(p[0]);
+ break;
+ case 0x08:
+ ec = test_isset(getvar(p[0]));
+ break;
+ case 0x09:
+ ec = test_has(p[0]);
+ break;
+ case 0x0A:
+ ec = test_obj_in_room(p[0], p[1]);
+ break;
+ case 0x0B:
+ ec = test_posn(p[0], p[1], p[2], p[3], p[4]);
+ break;
+ case 0x0C:
+ ec = test_controller(p[0]);
+ break;
+ case 0x0D:
+ ec = test_keypressed();
+ break;
+ case 0x0E:
+ ec = test_said(p[0], (uint8 *) code + (ip + 1));
+ ip = last_ip;
+ ip++; /* skip opcode */
+ ip += p[0] * 2; /* skip num_words * 2 */
+ ip++; /* skip num_words opcode */
+ break;
+ case 0x0F:
+ debugC(7, kDebugLevelScripts, "comparing [%s], [%s]", game.strings[p[0]], game.strings[p[1]]);
+ ec = test_compare_strings(p[0], p[1]);
+ break;
+ case 0x10:
+ ec = test_obj_in_box(p[0], p[1], p[2], p[3], p[4]);
+ break;
+ case 0x11:
+ ec = test_obj_centre(p[0], p[1], p[2], p[3], p[4]);
+ break;
+ case 0x12:
+ ec = test_obj_right(p[0], p[1], p[2], p[3], p[4]);
+ break;
+ default:
+ ec = false;
+ goto end_test;
+ }
+
+ if (op <= 0x12)
+ ip += logic_names_test[op].num_args;
+
+ /* exchange ec value */
+ if (not_test)
+ ec = !ec;
+
+ /* not is only enabled for 1 test command */
+ not_test = false;
+
+ if (or_test && ec) {
+ /* a true inside an OR statement passes
+ * ENTIRE statement scan for end of OR
+ */
+
+ /* CM: test for opcode < 0xfc changed from 'op' to
+ * '*(code+ip)', to avoid problem with the 0xfd (NOT)
+ * opcode byte. Changed a bad ip += ... ip++ construct.
+ * This should fix the crash with Larry's logic.0 code:
+ *
+ * if ((isset(4) ||
+ * !isset(2) ||
+ * v30 == 2 ||
+ * v30 == 1)) {
+ * goto Label1;
+ * }
+ *
+ * The bytecode is:
+ * ff fc 07 04 fd 07 02 01 1e 02 01 1e 01 fc ff
+ */
+
+ /* find end of OR */
+ while (*(code + ip) != 0xFC) {
+ if (*(code + ip) == 0x0E) { /* said */
+ ip++;
+ /* cover count + ^words */
+ ip += 1 + ((*(code + ip)) * 2);
+ continue;
+ }
+
+ if (*(code + ip) < 0xFC)
+ ip += logic_names_test[*(code + ip)].num_args;
+ ip++;
+ }
+ ip++;
+
+ or_test = false;
+ retval = true;
+ } else {
+ retval = or_test ? retval || ec : retval && ec;
+ }
+ }
+ end_test:
+
+ /* if false, scan for end of IP? */
+ if (retval)
+ ip += 2;
+ else {
+ ip = last_ip;
+ while (*(code + ip) != 0xff) {
+ if (*(code + ip) == 0x0e) {
+ ip++;
+ ip += (*(code + ip)) * 2 + 1;
+ } else if (*(code + ip) < 0xfc) {
+ ip += logic_names_test[*(code + ip)].num_args;
+ ip++;
+ } else {
+ ip++;
+ }
+ }
+ ip++; /* skip over 0xFF */
+ ip += READ_LE_UINT16(code + ip) + 2;
+ }
+
+#ifdef USE_CONSOLE
+ if (debug_.enabled && (debug_.logic0 || lognum))
+ debug_console(lognum, 0xFF, retval ? (char *)"=true" : (char *)"=false");
+#endif
+
+ return retval;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/opcodes.h b/engines/agi/opcodes.h
new file mode 100644
index 0000000000..236ba19e68
--- /dev/null
+++ b/engines/agi/opcodes.h
@@ -0,0 +1,55 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_OPCODES_H
+#define __AGI_OPCODES_H
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+struct agi_logicnames {
+#ifdef USE_CONSOLE /* ifndef NO_DEBUG */
+ char *name;
+#endif
+ uint16 num_args;
+ uint16 arg_mask;
+};
+
+extern struct agi_logicnames logic_names_test[];
+extern struct agi_logicnames logic_names_cmd[];
+extern struct agi_logicnames logic_names_if[];
+
+void debug_console(int, int, char *);
+int test_if_code(int);
+void new_room(int);
+void execute_agi_command(uint8, uint8 *);
+
+#ifdef PATCH_LOGIC
+void patch_logic(int);
+#endif
+
+} // End of namespace Agi
+
+#endif /* __AGI_OPCODES_H */
diff --git a/engines/agi/patches.cpp b/engines/agi/patches.cpp
new file mode 100644
index 0000000000..a666c84686
--- /dev/null
+++ b/engines/agi/patches.cpp
@@ -0,0 +1,144 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/opcodes.h"
+
+namespace Agi {
+
+#ifdef PATCH_LOGIC
+
+#define ip (game.logics[n].cIP)
+#define code (game.logics[n].data)
+#define size (game.logics[n].size)
+
+/*
+ * Patches
+ */
+
+static uint8 kq4data_find[] = {
+ 0x0C, 0x04, 0xFF, 0x07, 0x05, 0xFF, 0x15, 0x00,
+ 0x03, 0x0A, 0x00, 0x77, 0x83, 0x71, 0x0D, 0x97,
+ 0x03, 0x98, 0xCE, 0x18, 0x98, 0x19, 0x98, 0x1B,
+ 0x98, 0x0C, 0x5A, 0x1A, 0x00
+};
+
+static uint8 kq4data_fix[] = {
+ /* v19 = 0
+ * new.room(96)
+ * return
+ */
+ 0x03, 0x13, 0x0, 0x12, 0x60, 0x00
+};
+
+static uint8 grdata_find[] = {
+ 0x0C, 0x04, 0xFF, 0x07, 0x05, 0xFF, 0x16, 0x00,
+ 0x0C, 0x96, 0x03, 0x0A, 0x00, 0x77, 0x83, 0x71,
+ 0x0D, 0xD9, 0x03, 0xDC, 0xBF, 0x18, 0xDC, 0x19,
+ 0xDC, 0x1B, 0xDC, 0x0C, 0x95, 0x1A
+};
+
+static uint8 grdata_fix[] = {
+ /* reset(227)
+ * v19 = 0
+ * v246 = 1
+ * set(15)
+ * new.room(73)
+ */
+ 0x0D, 0xE3, 0x03, 0x13, 0x00, 0x03, 0xF6, 0x01,
+ 0x0C, 0x0F, 0x12, 0x49
+};
+
+#if 0
+static uint8 lsl1data_find[] = {
+ 0xFF, 0xFD, 0x07, 0x1E, 0xFC, 0x07, 0x6D, 0x01,
+ 0x5F, 0x03, 0xFC, 0xFF, 0x12, 0x00, 0x0C, 0x6D,
+ 0x78, 0x8A, 0x77, 0x69, 0x16, 0x18, 0x00, 0x0D,
+ 0x30, 0x0D, 0x55, 0x78, 0x65, 0x0A
+};
+
+static uint8 lsl1data_fix[] = {
+ /* set(109)
+ * reset(48)
+ * reset(85)
+ * accept.input()
+ * new.room(11)
+ */
+ 0x0C, 0x6D, 0x0D, 0x30, 0x0D, 0x55, 0x78, 0x12,
+ 0x0B
+};
+#endif
+
+static uint8 mh1data_find[] = {
+ 0xFF, 0x07, 0x05, 0xFF, 0xE6, 0x00,
+ 0x03, 0x0A, 0x02, 0x77, 0x83, 0x71,
+ 0x6F, 0x01, 0x17, 0x00, 0x03, 0x00,
+ 0x9F, 0x03, 0x37, 0x00, 0x03, 0x32,
+ 0x03, 0x03, 0x3B, 0x00, 0x6C, 0x03
+};
+
+static uint8 mh1data_fix[] = {
+ 0x0C, 0x05, 0x16, 0x5A, 0x12, 0x99
+};
+
+void patch_logic(int n) {
+ switch (n) {
+#if 0
+ /* ALT-X in the questions takes care of that */
+ case 6:
+ /* lsl1 bypass questions */
+ if (!strcmp(game.id, "LLLLL")) {
+ if (!memcmp(lsl1data_find, (code + ip), 30))
+ memmove((code + ip), lsl1data_fix, 9);
+ }
+ break;
+#endif
+ case 125:
+ /* gold rush code break */
+ if (!strcmp(game.id, "GR")) {
+ if (!memcmp(grdata_find, (code + ip), 30))
+ memmove((code + ip), grdata_fix, 12);
+ }
+ break;
+ case 140:
+ /* kings quest 4 code break */
+ if (!strcmp(game.id, "KQ4")) {
+ if (memcmp(kq4data_find, (code + ip), 29) == 0)
+ memmove((code + ip), kq4data_fix, 6);
+ }
+ break;
+ case 159:
+ /* manhunter 1 amiga */
+ if (ip + 30 < size && !memcmp(mh1data_find, (code + ip), 30)) {
+ memmove((code + ip), mh1data_fix, 6);
+ }
+ break;
+ }
+}
+
+#endif
+
+} // End of namespace Agi
diff --git a/engines/agi/picture.cpp b/engines/agi/picture.cpp
new file mode 100644
index 0000000000..1b9ab22cdf
--- /dev/null
+++ b/engines/agi/picture.cpp
@@ -0,0 +1,1034 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/graphics.h"
+#include "agi/savegame.h"
+
+namespace Agi {
+
+#define next_byte data[foffs++]
+
+static uint8 *data;
+static uint32 flen;
+static uint32 foffs;
+
+static uint8 pat_code;
+static uint8 pat_num;
+static uint8 pri_on;
+static uint8 scr_on;
+static uint8 scr_colour;
+static uint8 pri_colour;
+
+static uint8 circles[][15] = { /* agi circle bitmaps */
+ {0x80},
+ {0xfc},
+ {0x5f, 0xf4},
+ {0x66, 0xff, 0xf6, 0x60},
+ {0x23, 0xbf, 0xff, 0xff, 0xee, 0x20},
+ {0x31, 0xe7, 0x9e, 0xff, 0xff, 0xde, 0x79, 0xe3, 0x00},
+ {0x38, 0xf9, 0xf3, 0xef, 0xff, 0xff, 0xff, 0xfe, 0xf9, 0xf3, 0xe3, 0x80},
+ {0x18, 0x3c, 0x7e, 0x7e, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x7e, 0x7e, 0x3c, 0x18}
+};
+
+static uint8 splatter_map[32] = { /* splatter brush bitmaps */
+ 0x20, 0x94, 0x02, 0x24, 0x90, 0x82, 0xa4, 0xa2,
+ 0x82, 0x09, 0x0a, 0x22, 0x12, 0x10, 0x42, 0x14,
+ 0x91, 0x4a, 0x91, 0x11, 0x08, 0x12, 0x25, 0x10,
+ 0x22, 0xa8, 0x14, 0x24, 0x00, 0x50, 0x24, 0x04
+};
+
+static uint8 splatter_start[128] = { /* starting bit position */
+ 0x00, 0x18, 0x30, 0xc4, 0xdc, 0x65, 0xeb, 0x48,
+ 0x60, 0xbd, 0x89, 0x05, 0x0a, 0xf4, 0x7d, 0x7d,
+ 0x85, 0xb0, 0x8e, 0x95, 0x1f, 0x22, 0x0d, 0xdf,
+ 0x2a, 0x78, 0xd5, 0x73, 0x1c, 0xb4, 0x40, 0xa1,
+ 0xb9, 0x3c, 0xca, 0x58, 0x92, 0x34, 0xcc, 0xce,
+ 0xd7, 0x42, 0x90, 0x0f, 0x8b, 0x7f, 0x32, 0xed,
+ 0x5c, 0x9d, 0xc8, 0x99, 0xad, 0x4e, 0x56, 0xa6,
+ 0xf7, 0x68, 0xb7, 0x25, 0x82, 0x37, 0x3a, 0x51,
+ 0x69, 0x26, 0x38, 0x52, 0x9e, 0x9a, 0x4f, 0xa7,
+ 0x43, 0x10, 0x80, 0xee, 0x3d, 0x59, 0x35, 0xcf,
+ 0x79, 0x74, 0xb5, 0xa2, 0xb1, 0x96, 0x23, 0xe0,
+ 0xbe, 0x05, 0xf5, 0x6e, 0x19, 0xc5, 0x66, 0x49,
+ 0xf0, 0xd1, 0x54, 0xa9, 0x70, 0x4b, 0xa4, 0xe2,
+ 0xe6, 0xe5, 0xab, 0xe4, 0xd2, 0xaa, 0x4c, 0xe3,
+ 0x06, 0x6f, 0xc6, 0x4a, 0xa4, 0x75, 0x97, 0xe1
+};
+
+static void fix_pixel_bothsides(int x, int y);
+
+static void put_virt_pixel(int x, int y, int res) {
+ uint8 *p;
+ int width = _WIDTH * res;
+
+ if (x < 0 || y < 0 || x >= width || y >= _HEIGHT)
+ return;
+
+ p =
+#ifdef USE_HIRES
+ res > 1 ? &game.hires[y * width + x] :
+#endif
+ &game.sbuf[y * width + x];
+
+ if (pri_on)
+ *p = (pri_colour << 4) | (*p & 0x0f);
+ if (scr_on)
+ *p = scr_colour | (*p & 0xf0);
+}
+
+/* For the flood fill routines */
+
+/* MH2 needs stack size > 300 */
+#define STACK_SIZE 512
+static unsigned int stack_ptr;
+static uint16 stack[STACK_SIZE];
+
+static INLINE void _PUSH(uint16 c) {
+ assert(stack_ptr < STACK_SIZE);
+
+ stack[stack_ptr] = c;
+ stack_ptr++;
+}
+
+static INLINE uint16 _POP() {
+ if (stack_ptr == 0)
+ return 0xffff;
+
+ stack_ptr--;
+ return stack[stack_ptr];
+}
+
+/**
+ * Draw an AGI line.
+ * A line drawing routine sent by Joshua Neal, modified by Stuart George
+ * (fixed >>2 to >>1 and some other bugs like x1 instead of y1, etc.)
+ * @param x1 x coordinate of start point
+ * @param y1 y coordinate of start point
+ * @param x2 x coordinate of end point
+ * @param y2 y coordinate of end point
+ * @param res horizontal resolution multiplier
+ */
+static void draw_line(int x1, int y1, int x2, int y2, int res) {
+ int i, x, y, deltaX, deltaY, stepX, stepY, errorX, errorY, detdelta;
+ int width = _WIDTH * res;
+
+ /* CM: Do clipping */
+#define clip(x, y) if((x)>=(y)) (x)=(y)
+ clip(x1, width - 1);
+ clip(x2, width - 1);
+ clip(y1, _HEIGHT - 1);
+ clip(y2, _HEIGHT - 1);
+
+ /* Vertical line */
+
+ if (x1 == x2) {
+ if (y1 > y2) {
+ y = y1;
+ y1 = y2;
+ y2 = y;
+ }
+
+ for (; y1 <= y2; y1++) {
+ put_virt_pixel(x1, y1, res);
+#ifdef USE_HIRES
+ if (res > 1)
+ fix_pixel_bothsides(x1, y1);
+#endif
+ }
+
+ return;
+ }
+
+ /* Horizontal line */
+
+ if (y1 == y2) {
+ if (x1 > x2) {
+ x = x1;
+ x1 = x2;
+ x2 = x;
+ }
+#ifdef USE_HIRES
+ if (res > 1)
+ fix_pixel_bothsides(x1, y1);
+#endif
+
+ for (; x1 <= x2; x1++)
+ put_virt_pixel(x1, y1, res);
+
+#ifdef USE_HIRES
+ if (res > 1) {
+ put_virt_pixel(x1, y1, res);
+ fix_pixel_bothsides(x1, y1);
+ }
+#endif
+
+ return;
+ }
+
+ y = y1;
+ x = x1;
+
+ stepY = 1;
+ deltaY = y2 - y1;
+ if (deltaY < 0) {
+ stepY = -1;
+ deltaY = -deltaY;
+ }
+
+ stepX = 1;
+ deltaX = x2 - x1;
+ if (deltaX < 0) {
+ stepX = -1;
+ deltaX = -deltaX;
+ }
+
+ if (deltaY > deltaX) {
+ i = deltaY;
+ detdelta = deltaY;
+ errorX = deltaY / 2;
+ errorY = 0;
+ } else {
+ i = deltaX;
+ detdelta = deltaX;
+ errorX = 0;
+ errorY = deltaX / 2;
+ }
+
+ put_virt_pixel(x, y, res);
+#ifdef USE_HIRES
+ if (res > 1)
+ fix_pixel_bothsides(x, y);
+#endif
+
+ do {
+ errorY += deltaY;
+ if (errorY >= detdelta) {
+ errorY -= detdelta;
+ y += stepY;
+ }
+
+ errorX += deltaX;
+ if (errorX >= detdelta) {
+ errorX -= detdelta;
+ x += stepX;
+ }
+
+ put_virt_pixel(x, y, res);
+#ifdef USE_HIRES
+ if (res > 1)
+ fix_pixel_bothsides(x, y);
+#endif
+ i--;
+ } while (i > 0);
+
+#ifdef USE_HIRES
+ if (res > 1) {
+ put_virt_pixel(x, y, res);
+ fix_pixel_bothsides(x, y);
+ }
+#endif
+}
+
+/**
+ * Draw a relative AGI line.
+ * Draws short lines relative to last position. (drawing action 0xF7)
+ * @param res horizontal resolution multiplier
+ */
+static void dynamic_draw_line(int res) {
+ int x1, y1, disp, dx, dy;
+
+ x1 = next_byte * res;
+ y1 = next_byte;
+
+ put_virt_pixel(x1, y1, res);
+
+ while (42) {
+ if ((disp = next_byte) >= 0xf0)
+ break;
+
+ dx = ((disp & 0xf0) >> 4) & 0x0f;
+ dy = (disp & 0x0f);
+
+ if (dx & 0x08)
+ dx = -(dx & 0x07);
+ if (dy & 0x08)
+ dy = -(dy & 0x07);
+
+ dx *= res;
+
+ draw_line(x1, y1, x1 + dx, y1 + dy, res);
+ x1 += dx;
+ y1 += dy;
+ }
+ foffs--;
+}
+
+/**************************************************************************
+** absoluteLine
+**
+** Draws long lines to actual locations (cf. relative) (drawing action 0xF6)
+**************************************************************************/
+static void absolute_draw_line(int res) {
+ int x1, y1, x2, y2;
+
+ x1 = next_byte * res;
+ y1 = next_byte;
+ put_virt_pixel(x1, y1, res);
+
+ while (42) {
+ if ((x2 = next_byte) >= 0xf0)
+ break;
+
+ if ((y2 = next_byte) >= 0xf0)
+ break;
+
+ x2 *= res;
+
+ draw_line(x1, y1, x2, y2, res);
+ x1 = x2;
+ y1 = y2;
+ }
+ foffs--;
+}
+
+/**************************************************************************
+** okToFill
+**************************************************************************/
+static INLINE int is_ok_fill_here(int x, int y) {
+ uint8 p;
+
+ if (x < 0 || x >= _WIDTH || y < 0 || y >= _HEIGHT)
+ return false;
+
+ if (!scr_on && !pri_on)
+ return false;
+
+ p = game.sbuf[y * _WIDTH + x];
+
+ if (!pri_on && scr_on && scr_colour != 15)
+ return (p & 0x0f) == 15;
+
+ if (pri_on && !scr_on && pri_colour != 4)
+ return (p >> 4) == 4;
+
+ return (scr_on && (p & 0x0f) == 15 && scr_colour != 15);
+}
+
+/**************************************************************************
+** agi_fill
+**************************************************************************/
+static void fill_scanline(int x, int y) {
+ unsigned int c;
+ int newspan_up, newspan_down;
+
+ if (!is_ok_fill_here(x, y))
+ return;
+
+ /* Scan for left border */
+ for (c = x - 1; is_ok_fill_here(c, y); c--);
+
+ newspan_up = newspan_down = 1;
+ for (c++; is_ok_fill_here(c, y); c++) {
+ put_virt_pixel(c, y, 1);
+ if (is_ok_fill_here(c, y - 1)) {
+ if (newspan_up) {
+ _PUSH(c + 320 * (y - 1));
+ newspan_up = 0;
+ }
+ } else {
+ newspan_up = 1;
+ }
+
+ if (is_ok_fill_here(c, y + 1)) {
+ if (newspan_down) {
+ _PUSH(c + 320 * (y + 1));
+ newspan_down = 0;
+ }
+ } else {
+ newspan_down = 1;
+ }
+ }
+}
+
+static void agi_fill(unsigned int x, unsigned int y) {
+ _PUSH(x + 320 * y);
+
+ while (42) {
+ uint16 c = _POP();
+
+ /* Exit if stack is empty */
+ if (c == 0xffff)
+ break;
+
+ x = c % 320;
+ y = c / 320;
+
+ fill_scanline(x, y);
+ }
+
+ stack_ptr = 0;
+}
+
+/**************************************************************************
+** xCorner
+**
+** Draws an xCorner (drawing action 0xF5)
+**************************************************************************/
+static void x_corner(int res) {
+ int x1, x2, y1, y2;
+
+ x1 = next_byte * res;
+ y1 = next_byte;
+ put_virt_pixel(x1, y1, res);
+
+ while (42) {
+ x2 = next_byte;
+
+ if (x2 >= 0xf0)
+ break;
+
+ x2 *= res;
+
+ draw_line(x1, y1, x2, y1, res);
+ x1 = x2;
+ y2 = next_byte;
+
+ if (y2 >= 0xf0)
+ break;
+
+ draw_line(x1, y1, x1, y2, res);
+ y1 = y2;
+ }
+ foffs--;
+}
+
+/**************************************************************************
+** yCorner
+**
+** Draws an yCorner (drawing action 0xF4)
+**************************************************************************/
+static void y_corner(int res) {
+ int x1, x2, y1, y2;
+
+ x1 = next_byte * res;
+ y1 = next_byte;
+ put_virt_pixel(x1, y1, res);
+
+ while (42) {
+ y2 = next_byte;
+
+ if (y2 >= 0xF0)
+ break;
+
+ draw_line(x1, y1, x1, y2, res);
+ y1 = y2;
+ x2 = next_byte;
+
+ if (x2 >= 0xf0)
+ break;
+
+ x2 *= res;
+
+ draw_line(x1, y1, x2, y1, res);
+ x1 = x2;
+ }
+
+ foffs--;
+}
+
+/**************************************************************************
+** fill
+**
+** AGI flood fill. (drawing action 0xF8)
+**************************************************************************/
+static void fill() {
+ int x1, y1;
+
+ while ((x1 = next_byte) < 0xF0 && (y1 = next_byte) < 0xf0)
+ agi_fill(x1, y1);
+
+ foffs--;
+}
+
+/**************************************************************************
+** plotPattern
+**
+** Draws pixels, circles, squares, or splatter brush patterns depending
+** on the pattern code.
+**************************************************************************/
+
+static int plot_pattern_point(int x, int y, int bitpos, int res) {
+ if (pat_code & 0x20) {
+ if ((splatter_map[bitpos >> 3] >> (7 - (bitpos & 7))) & 1) {
+#ifdef USE_HIRES
+ if (res > 1) {
+ /* extra randomness in hi-res brush fill
+ */
+ if (rnd->getRandomNumber(3))
+ put_virt_pixel(x * 2, y, 2);
+ if (!rnd->getRandomNumber(3))
+ put_virt_pixel(x * 2 + 1, y, 2);
+ } else
+#endif
+ {
+ put_virt_pixel(x, y, 1);
+ }
+ }
+ bitpos++;
+ if (bitpos == 0xff)
+ bitpos = 0;
+ } else {
+#ifdef USE_HIRES
+ if (res > 1) {
+ /* double width pixels make MUMG and others
+ * look nicer
+ */
+ put_virt_pixel(x * 2, y, 2);
+ put_virt_pixel(x * 2 + 1, y, 2);
+ } else
+#endif
+ {
+ put_virt_pixel(x, y, 1);
+ }
+ }
+
+ return bitpos;
+}
+
+static void plot_pattern(int x, int y, int res) {
+ int32 circlePos = 0;
+ uint32 x1, y1, pensize, bitpos = splatter_start[pat_num];
+
+ pensize = (pat_code & 7);
+
+ if (x < (int)pensize)
+ x = pensize - 1;
+ if (y < (int)pensize)
+ y = pensize;
+
+ for (y1 = y - pensize; y1 <= y + pensize; y1++) {
+ for (x1 = x - (pensize + 1) / 2; x1 <= x + pensize / 2; x1++) {
+ if (pat_code & 0x10) { /* Square */
+ bitpos = plot_pattern_point (x1, y1, bitpos, res);
+ } else { /* Circle */
+ if ((circles[pat_code & 7][circlePos >> 3] >> (7 - (circlePos & 7))) & 1) {
+ bitpos = plot_pattern_point(x1, y1, bitpos, res);
+ }
+ circlePos++;
+ }
+ }
+ }
+}
+
+/**************************************************************************
+** plotBrush
+**
+** Plots points and various brush patterns.
+**************************************************************************/
+static void plot_brush(int res) {
+ int x1, y1;
+
+ while (42) {
+ if (pat_code & 0x20) {
+ if ((pat_num = next_byte) >= 0xF0)
+ break;
+ pat_num = (pat_num >> 1) & 0x7f;
+ }
+
+ if ((x1 = next_byte) >= 0xf0)
+ break;
+
+ if ((y1 = next_byte) >= 0xf0)
+ break;
+
+ plot_pattern(x1, y1, res);
+ }
+
+ foffs--;
+}
+
+#ifdef USE_HIRES
+
+static void fix_pixel_bothsides(int x, int y) {
+ uint8 *p, *s;
+
+ if (x >= (_WIDTH * 2) - 2)
+ return;
+
+ /* Sometimes a solid color area in the lo-res pic is made
+ * with lines, and we want to keep this effect in the
+ * hi-res pic.
+ */
+ p = &game.hires[y * (_WIDTH * 2) + x];
+ if ((*(p - 2) & 0x0f) == scr_colour)
+ put_virt_pixel(x - 1, y, 2);
+ if ((*(p + 2) & 0x0f) == scr_colour)
+ put_virt_pixel(x + 1, y, 2);
+
+ /* If two lines are contiguous in the lo-res pic, make them
+ * contiguous in the hi-res pic. This condition is needed
+ * in some scenes like in front of Lefty's in LSL1, to draw
+ * the pole. Note: it adds artifacts in some cases.
+ */
+ s = &game.sbuf[y * _WIDTH + x / 2];
+ if ((*(p - 1) & 0x0f) != (*(s - 1) & 0x0f))
+ put_virt_pixel(x - 1, y, 2);
+}
+
+/**************************************************************************
+** okToFill
+**************************************************************************/
+static INLINE int hires_fill_here(int x, int y) {
+ uint8 *p, *s;
+
+ if (x < 0 || x >= _WIDTH || y < 0 || y >= _HEIGHT)
+ return false;
+
+ if (!scr_on && !pri_on)
+ return false;
+
+ p = &game.hires[(int32) y * (_WIDTH * 2) + x * 2];
+ s = &game.sbuf[y * _WIDTH + x];
+
+ if (scr_on) {
+ if (scr_colour == 0x0f)
+ return false;
+ if ((*p & 0x0f) != 0x0f || (*(p + 1) & 0x0f) != 0x0f)
+ return false;
+ if ((*s & 0x0f) != scr_colour)
+ return false;
+ }
+
+ if (pri_on) {
+ if (pri_colour == 0x04)
+ return false;
+ if ((*p >> 4) != 0x04 || (*(p + 1) >> 4) != 0x04)
+ return false;
+ if ((*s >> 4) != pri_colour)
+ return false;
+ }
+
+ return true;
+}
+
+static void fix_pixel_left(int x, int y) {
+ uint8 *p;
+
+ if (!scr_on)
+ return;
+
+ p = &game.hires[y * (_WIDTH * 2) + x * 2 + 1];
+ if ((*p & 0x0f) == 0x0f)
+ put_virt_pixel(2 * x + 1, y, 2);
+ else if ((*p & 0x0f) == (*(p - 1) & 0x0f))
+ put_virt_pixel(2 * x + 1, y, 2);
+}
+
+static void fix_pixel_right(int x, int y) {
+ int idx = y * (_WIDTH * 2) + x * 2;
+
+ if (idx >= 160 * 168)
+ return;
+
+ if (scr_on && (game.hires[idx] & 0x0f) == 0x0f)
+ put_virt_pixel(2 * x, y, 2);
+}
+
+static void fix_pixel_here(int x, int y) {
+ uint8 p;
+
+ p = game.hires[y * (_WIDTH * 2) + x * 2 + 1];
+ if (scr_on && (p & 0x0f) == 0x0f)
+ put_virt_pixel(2 * x + 1, y, 2);
+}
+
+/**************************************************************************
+** agiFill
+**************************************************************************/
+static void hires_fill_scanline(int x, int y) {
+ unsigned int c;
+ int newspan_up, newspan_down;
+
+ if (!hires_fill_here(x, y))
+ return;
+
+ /* Scan for left border */
+ for (c = x - 1; c > 0 && hires_fill_here(c, y); c--);
+ fix_pixel_left(c, y);
+
+ newspan_up = newspan_down = 1;
+ for (c++; hires_fill_here(c, y); c++) {
+ put_virt_pixel(c * 2, y, 2);
+ fix_pixel_here(c, y);
+
+ if (hires_fill_here(c, y - 1)) {
+ if (newspan_up) {
+ _PUSH(c + 320 * (y - 1));
+ newspan_up = 0;
+ }
+ } else {
+ newspan_up = 1;
+ }
+
+ if (hires_fill_here(c, y + 1)) {
+ if (newspan_down) {
+ _PUSH(c + 320 * (y + 1));
+ newspan_down = 0;
+ }
+ } else {
+ newspan_down = 1;
+ }
+ }
+
+ fix_pixel_right(c, y);
+}
+
+static void _hires_fill(unsigned int x, unsigned int y) {
+ _PUSH(x + 320 * y);
+
+ while (42) {
+ uint16 c = _POP();
+
+ /* Exit if stack is empty */
+ if (c == 0xffff)
+ break;
+
+ x = c % 320;
+ y = c / 320;
+
+ hires_fill_scanline(x, y);
+ }
+
+ stack_ptr = 0;
+}
+
+/**************************************************************************
+** fill
+**
+** AGI flood fill. (drawing action 0xF8)
+**************************************************************************/
+static void hires_fill() {
+ int x1, y1;
+
+ while ((x1 = next_byte) < 0xf0 && (y1 = next_byte) < 0xf0) {
+ _hires_fill(x1, y1);
+ }
+
+ foffs--;
+}
+
+/**
+ * Show AGI picture.
+ * This function copies a ``hidden'' AGI picture to the output device.
+ */
+void show_hires_pic() {
+ int y, offset;
+ int32 i;
+
+ i = 0;
+ offset = game.line_min_print * CHAR_LINES;
+ for (y = 0; y < _HEIGHT; y++) {
+ put_pixels_hires(0, y + offset, _WIDTH * 2, &game.hires[i]);
+ i += _WIDTH * 2;
+ }
+
+ flush_screen();
+}
+
+void fix_hires_picture() {
+ uint8 *p, *b;
+ int i;
+
+ p = game.hires;
+ b = game.sbuf;
+
+ for (i = 0; p < &game.hires[_WIDTH * _HEIGHT * 2] - 1; p++, i++) {
+ if ((*p & 0x0f) == 0x0f && (*b & 0x0f) != 0x0f) {
+ if ((*(p + 1) & 0x0f) != 0x0f)
+ *p = *(p + 1);
+ else
+ *p = *b;
+ }
+ if ((*p >> 4) == 4 && (*b >> 4) != 4 && (*(b + 1) >> 4) != 4) {
+ *p = (*p & 0x0f) | (*b & 0xf0);
+ }
+ b += (i & 1);
+ }
+}
+
+#endif /* USE_HIRES */
+
+static void draw_picture() {
+ uint8 act;
+ int drawing;
+#ifdef USE_HIRES
+ int save_foffs;
+#endif
+
+ pat_code = 0;
+ pat_num = 0;
+ pri_on = scr_on = false;
+ scr_colour = 0xf;
+ pri_colour = 0x4;
+
+ drawing = 1;
+
+ debugC(8, kDebugLevelMain, "Drawing picture");
+ for (drawing = 1; drawing && foffs < flen;) {
+
+#ifdef USE_HIRES
+ save_foffs = foffs;
+#endif
+
+ act = next_byte;
+ switch (act) {
+ case 0xf0: /* set colour on screen */
+ scr_colour = next_byte;
+ scr_colour &= 0xF; /* for v3 drawing diff */
+ scr_on = true;
+ break;
+ case 0xf1: /* disable screen drawing */
+ scr_on = false;
+ break;
+ case 0xf2: /* set colour on priority */
+ pri_colour = next_byte;
+ pri_colour &= 0xf; /* for v3 drawing diff */
+ pri_on = true;
+ break;
+ case 0xf3: /* disable priority screen */
+ pri_on = false;
+ break;
+ case 0xf4: /* y-corner */
+ y_corner(1);
+ break;
+ case 0xf5: /* x-corner */
+ x_corner(1);
+ break;
+ case 0xf6: /* absolute draw lines */
+ absolute_draw_line(1);
+ break;
+ case 0xf7: /* dynamic draw lines */
+ dynamic_draw_line(1);
+ break;
+ case 0xf8: /* fill */
+ fill();
+ break;
+ case 0xf9: /* set pattern */
+ pat_code = next_byte;
+ break;
+ case 0xfA: /* plot brush */
+ plot_brush(1);
+ break;
+ case 0xFF: /* end of pic data */
+ default:
+ drawing = 0;
+ break;
+ }
+
+#ifdef USE_HIRES
+ foffs = save_foffs;
+
+ act = next_byte;
+ switch (act) {
+ case 0xf0: /* set colour on screen */
+ scr_colour = next_byte;
+ scr_colour &= 0xF; /* for v3 drawing diff */
+ scr_on = true;
+ break;
+ case 0xf1: /* disable screen drawing */
+ scr_on = false;
+ break;
+ case 0xf2: /* set colour on priority */
+ pri_colour = next_byte;
+ pri_colour &= 0xf; /* for v3 drawing diff */
+ pri_on = true;
+ break;
+ case 0xf3: /* disable priority screen */
+ pri_on = false;
+ break;
+ case 0xf4: /* y-corner */
+ y_corner(2);
+ break;
+ case 0xf5: /* x-corner */
+ x_corner(2);
+ break;
+ case 0xf6: /* absolute draw lines */
+ absolute_draw_line(2);
+ break;
+ case 0xf7: /* dynamic draw lines */
+ dynamic_draw_line(2);
+ break;
+ case 0xf8: /* fill */
+ hires_fill();
+ break;
+ case 0xf9: /* set pattern */
+ pat_code = next_byte;
+ break;
+ case 0xfA: /* plot brush */
+ plot_brush(2);
+ break;
+ case 0xFF: /* end of pic data */
+ default:
+ drawing = 0;
+ break;
+ }
+#endif
+ }
+}
+
+/*
+ * Public functions
+ */
+
+/**
+ *
+ */
+uint8 *convert_v3_pic(uint8 *data, uint32 len) {
+ uint8 d, old = 0, x, *in, *xdata, *out, mode = 0;
+ uint32 i, ulen;
+
+ xdata = (uint8 *) malloc(len + len / 2);
+
+ out = xdata;
+ in = data;
+
+ for (i = ulen = 0; i < len; i++, ulen++) {
+ d = *in++;
+
+ *out++ = x = mode ? ((d & 0xF0) >> 4) + ((old & 0x0F) << 4) : d;
+
+ if (x == 0xFF) {
+ ulen++;
+ break;
+ }
+
+ if (x == 0xf0 || x == 0xf2) {
+ if (mode) {
+ *out++ = d & 0x0F;
+ ulen++;
+ } else {
+ d = *in++;
+ *out++ = (d & 0xF0) >> 4;
+ i++, ulen++;
+ }
+
+ mode = !mode;
+ }
+
+ old = d;
+ }
+
+ free(data);
+ xdata = (uint8 *)realloc(xdata, ulen);
+
+ return xdata;
+}
+
+/**
+ * Decode an AGI picture resource.
+ * This function decodes an AGI picture resource into the correct slot
+ * and draws it on the AGI screen, optionally cleaning the screen before
+ * drawing.
+ * @param n AGI picture resource number
+ * @param clear clear AGI screen before drawing
+ */
+int decode_picture(int n, int clear) {
+ debugC(8, kDebugLevelResources, "(%d)", n);
+
+ pat_code = 0;
+ pat_num = 0;
+ pri_on = scr_on = false;
+ scr_colour = 0xF;
+ pri_colour = 0x4;
+
+ data = game.pictures[n].rdata;
+ flen = game.dir_pic[n].len;
+ foffs = 0;
+
+ if (clear) {
+ memset(game.sbuf, 0x4f, _WIDTH * _HEIGHT);
+#ifdef USE_HIRES
+ memset(game.hires, 0x4f, _WIDTH * 2 * _HEIGHT);
+#endif
+ }
+
+ draw_picture();
+
+#ifdef USE_HIRES
+ fix_hires_picture();
+#endif
+
+ if (clear)
+ clear_image_stack();
+ record_image_stack_call(ADD_PIC, n, clear, 0, 0, 0, 0, 0);
+
+ return err_OK;
+}
+
+/**
+ * Unload an AGI picture resource.
+ * This function unloads an AGI picture resource and deallocates
+ * resource data.
+ * @param n AGI picture resource number
+ */
+int unload_picture(int n) {
+ /* remove visual buffer & priority buffer if they exist */
+ if (game.dir_pic[n].flags & RES_LOADED) {
+ free(game.pictures[n].rdata);
+ game.dir_pic[n].flags &= ~RES_LOADED;
+ }
+
+ return err_OK;
+}
+
+/**
+ * Show AGI picture.
+ * This function copies a ``hidden'' AGI picture to the output device.
+ */
+void show_pic() {
+ int i, y;
+ int offset;
+
+ debugC(8, kDebugLevelMain, "Show picture!");
+#ifdef USE_HIRES
+ if (opt.hires) {
+ show_hires_pic();
+ return;
+ }
+#endif
+
+ i = 0;
+ offset = game.line_min_print * CHAR_LINES;
+ for (y = 0; y < _HEIGHT; y++) {
+ put_pixels_a(0, y + offset, _WIDTH, &game.sbuf[i]);
+ i += _WIDTH;
+ }
+
+ flush_screen();
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/picture.h b/engines/agi/picture.h
new file mode 100644
index 0000000000..0828cdecc1
--- /dev/null
+++ b/engines/agi/picture.h
@@ -0,0 +1,47 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_PICTURE_H
+#define __AGI_PICTURE_H
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+/**
+ * AGI picture resource.
+ */
+struct agi_picture {
+ uint32 flen; /**< size of raw data */
+ uint8 *rdata; /**< raw vector image data */
+};
+
+int decode_picture(int, int);
+int unload_picture(int);
+void show_pic(void);
+uint8 *convert_v3_pic(uint8 *data, uint32 len);
+
+} // End of namespace Agi
+
+#endif /* __AGI_PICTURE_H */
diff --git a/engines/agi/savegame.cpp b/engines/agi/savegame.cpp
new file mode 100644
index 0000000000..e4efc4402f
--- /dev/null
+++ b/engines/agi/savegame.cpp
@@ -0,0 +1,790 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * Savegame support by Vasyl Tsvirkunov <vasyl@pacbell.net>
+ * Multi-slots by Claudio Matsuoka <claudio@helllabs.org>
+ */
+
+#include "common/stdafx.h"
+
+#include "agi/agi.h"
+#include "agi/graphics.h"
+#include "agi/sprite.h"
+#include "agi/text.h"
+#include "agi/savegame.h"
+#include "agi/keyboard.h"
+#include "agi/menu.h"
+
+namespace Agi {
+
+#if defined(WIN32)
+#define MKDIR(a,b) mkdir(a)
+#else
+#define MKDIR(a,b) mkdir(a,b)
+#endif
+
+/* Image stack support */
+struct image_stack_element {
+ uint8 type;
+ uint8 pad;
+ int16 parm1;
+ int16 parm2;
+ int16 parm3;
+ int16 parm4;
+ int16 parm5;
+ int16 parm6;
+ int16 parm7;
+};
+
+#define INITIAL_IMAGE_STACK_SIZE 32
+static int stack_size = 0;
+static struct image_stack_element *image_stack = NULL;
+static int image_stack_pointer = 0;
+
+void clear_image_stack(void) {
+ image_stack_pointer = 0;
+}
+
+void release_image_stack(void) {
+ if (image_stack)
+ free(image_stack);
+ image_stack = NULL;
+ stack_size = image_stack_pointer = 0;
+}
+
+void record_image_stack_call(uint8 type, int16 p1, int16 p2, int16 p3,
+ int16 p4, int16 p5, int16 p6, int16 p7) {
+ struct image_stack_element *pnew;
+
+ if (image_stack_pointer == stack_size) {
+ if (stack_size == 0) { /* first call */
+ image_stack = (struct image_stack_element *)
+ malloc(INITIAL_IMAGE_STACK_SIZE * sizeof(struct image_stack_element));
+ stack_size = INITIAL_IMAGE_STACK_SIZE;
+ } else { /* has to grow */
+ struct image_stack_element *new_stack;
+ new_stack = (struct image_stack_element *)
+ malloc(2 * stack_size * sizeof(struct image_stack_element));
+ memcpy(new_stack, image_stack, stack_size * sizeof(struct image_stack_element));
+ free(image_stack);
+ image_stack = new_stack;
+ stack_size *= 2;
+ }
+ }
+
+ pnew = &image_stack[image_stack_pointer];
+ image_stack_pointer++;
+
+ pnew->type = type;
+ pnew->parm1 = p1;
+ pnew->parm2 = p2;
+ pnew->parm3 = p3;
+ pnew->parm4 = p4;
+ pnew->parm5 = p5;
+ pnew->parm6 = p6;
+ pnew->parm7 = p7;
+}
+
+void replay_image_stack_call(uint8 type, int16 p1, int16 p2, int16 p3,
+ int16 p4, int16 p5, int16 p6, int16 p7) {
+ switch (type) {
+ case ADD_PIC:
+ debugC(8, kDebugLevelMain, "--- decoding picture %d ---", p1);
+ agi_load_resource(rPICTURE, p1);
+ decode_picture(p1, p2);
+ break;
+ case ADD_VIEW:
+ agi_load_resource(rVIEW, p1);
+ add_to_pic(p1, p2, p3, p4, p5, p6, p7);
+ break;
+ }
+}
+
+/* */
+
+static char *strSig = "AGI:";
+
+static void write_uint8(Common::File *f, int8 val) {
+ f->write(&val, 1);
+}
+
+static void write_sint16(Common::File *f, int16 val) {
+ static uint8 buf[2];
+ buf[0] = (uint8) ((val >> 8) & 255);
+ buf[1] = (uint8) (val & 255);
+ f->write(buf, 2);
+}
+
+static void write_uint16(Common::File *f, uint16 val) {
+ static uint8 buf[2];
+ buf[0] = (uint8) ((val >> 8) & 255);
+ buf[1] = (uint8) (val & 255);
+ f->write(buf, 2);
+}
+
+static uint8 read_uint8(Common::File *f) {
+ static uint8 buf[1];
+ f->read(buf, 1);
+ return buf[0];
+}
+
+static uint16 read_uint16(Common::File *f) {
+ static uint8 buf[2];
+ f->read(buf, 2);
+ return (buf[0] << 8) | buf[1];
+}
+
+static int16 read_sint16(Common::File *f) {
+ static uint8 buf[2];
+ f->read(buf, 2);
+ return (int16) ((buf[0] << 8) | buf[1]);
+}
+
+static void write_string(Common::File *f, char *s) {
+ write_sint16(f, (int16) strlen(s));
+ f->write(s, strlen(s));
+}
+
+static void read_string(Common::File *f, char *s) {
+ int16 size = read_sint16(f);
+ f->read(s, size);
+ s[size] = (char)0;
+}
+
+static void write_bytes(Common::File *f, char *s, int16 size) {
+ f->write(s, size);
+}
+
+static void read_bytes(Common::File *f, char *s, int16 size) {
+ f->read(s, size);
+}
+
+/*
+ * Version 0: view table has 64 entries
+ * Version 1: view table has 256 entries (needed in KQ3)
+ */
+#define SAVEGAME_VERSION 1
+
+int save_game(char *s, char *d) {
+ int16 i;
+ struct image_stack_element *ptr = image_stack;
+ Common::File f;
+
+ f.open(s, Common::File::kFileWriteMode);
+
+ if (!f.isOpen())
+ return err_BadFileOpen;
+
+ write_bytes(&f, strSig, 8);
+ write_string(&f, d);
+
+ write_uint8(&f, (uint8) SAVEGAME_VERSION);
+ write_uint8(&f, (uint8) game.state);
+ /* game.name */
+ write_string(&f, game.id);
+ /* game.crc */
+
+ for (i = 0; i < MAX_FLAGS; i++)
+ write_uint8(&f, game.flags[i]);
+ for (i = 0; i < MAX_VARS; i++)
+ write_uint8(&f, game.vars[i]);
+
+ write_sint16(&f, (int8) game.horizon);
+ write_sint16(&f, (int16) game.line_status);
+ write_sint16(&f, (int16) game.line_user_input);
+ write_sint16(&f, (int16) game.line_min_print);
+ /* game.cursor_pos */
+ /* game.input_buffer */
+ /* game.echo_buffer */
+ /* game.keypress */
+ write_sint16(&f, (int16) game.input_mode);
+ write_sint16(&f, (int16) game.lognum);
+
+ write_sint16(&f, (int16) game.player_control);
+ write_sint16(&f, (int16) game.quit_prog_now);
+ write_sint16(&f, (int16) game.status_line);
+ write_sint16(&f, (int16) game.clock_enabled);
+ write_sint16(&f, (int16) game.exit_all_logics);
+ write_sint16(&f, (int16) game.picture_shown);
+ write_sint16(&f, (int16) game.has_prompt);
+ write_sint16(&f, (int16) game.game_flags);
+
+ /* Reversed to keep compatibility with old savegames */
+ write_sint16(&f, (int16)!game.input_enabled);
+
+ for (i = 0; i < _HEIGHT; i++)
+ write_uint8(&f, game.pri_table[i]);
+
+ /* game.msg_box_ticks */
+ /* game.block */
+ /* game.window */
+ /* game.has_window */
+
+ write_sint16(&f, (int16)game.gfx_mode);
+ write_uint8(&f, game.cursor_char);
+ write_sint16(&f, (int16)game.color_fg);
+ write_sint16(&f, (int16)game.color_bg);
+
+ /* game.hires (#ifdef USE_HIRES) */
+ /* game.sbuf */
+ /* game.ego_words */
+ /* game.num_ego_words */
+
+ write_sint16(&f, (int16)game.num_objects);
+ for (i = 0; i < (int16)game.num_objects; i++)
+ write_sint16(&f, (int16)object_get_location(i));
+
+ /* game.ev_keyp */
+ for (i = 0; i < MAX_STRINGS; i++)
+ write_string(&f, game.strings[i]);
+
+ /* record info about loaded resources */
+ for (i = 0; i < MAX_DIRS; i++) {
+ write_uint8(&f, game.dir_logic[i].flags);
+ write_sint16(&f, (int16)game.logics[i].sIP);
+ write_sint16(&f, (int16)game.logics[i].cIP);
+ }
+ for (i = 0; i < MAX_DIRS; i++)
+ write_uint8(&f, game.dir_pic[i].flags);
+ for (i = 0; i < MAX_DIRS; i++)
+ write_uint8(&f, game.dir_view[i].flags);
+ for (i = 0; i < MAX_DIRS; i++)
+ write_uint8(&f, game.dir_sound[i].flags);
+
+ /* game.pictures */
+ /* game.logics */
+ /* game.views */
+ /* game.sounds */
+
+ for (i = 0; i < MAX_VIEWTABLE; i++) {
+ struct vt_entry *v = &game.view_table[i];
+
+ write_uint8(&f, v->step_time);
+ write_uint8(&f, v->step_time_count);
+ write_uint8(&f, v->entry);
+ write_sint16(&f, v->x_pos);
+ write_sint16(&f, v->y_pos);
+ write_uint8(&f, v->current_view);
+
+ /* v->view_data */
+
+ write_uint8(&f, v->current_loop);
+ write_uint8(&f, v->num_loops);
+
+ /* v->loop_data */
+
+ write_uint8(&f, v->current_cel);
+ write_uint8(&f, v->num_cels);
+
+ /* v->cel_data */
+ /* v->cel_data_2 */
+
+ write_sint16(&f, v->x_pos2);
+ write_sint16(&f, v->y_pos2);
+
+ /* v->s */
+
+ write_sint16(&f, v->x_size);
+ write_sint16(&f, v->y_size);
+ write_uint8(&f, v->step_size);
+ write_uint8(&f, v->cycle_time);
+ write_uint8(&f, v->cycle_time_count);
+ write_uint8(&f, v->direction);
+
+ write_uint8(&f, v->motion);
+ write_uint8(&f, v->cycle);
+ write_uint8(&f, v->priority);
+
+ write_uint16(&f, v->flags);
+
+ write_uint8(&f, v->parm1);
+ write_uint8(&f, v->parm2);
+ write_uint8(&f, v->parm3);
+ write_uint8(&f, v->parm4);
+ }
+
+ /* Save image stack */
+
+ for (i = 0; i < image_stack_pointer; i++) {
+ ptr = &image_stack[i];
+ write_uint8(&f, ptr->type);
+ write_sint16(&f, ptr->parm1);
+ write_sint16(&f, ptr->parm2);
+ write_sint16(&f, ptr->parm3);
+ write_sint16(&f, ptr->parm4);
+ write_sint16(&f, ptr->parm5);
+ write_sint16(&f, ptr->parm6);
+ write_sint16(&f, ptr->parm7);
+ }
+ write_uint8(&f, 0);
+
+ f.close();
+
+ return err_OK;
+}
+
+int load_game(char *s) {
+ int i, ver, vt_entries = MAX_VIEWTABLE;
+ uint8 t;
+ int16 parm[7];
+ char sig[8];
+ char id[8];
+ char description[256];
+ Common::File f;
+
+ f.open(s);
+
+ if (!f.isOpen())
+ return err_BadFileOpen;
+
+ read_bytes(&f, sig, 8);
+ if (strncmp(sig, strSig, 8)) {
+ f.close();
+ return err_BadFileOpen;
+ }
+
+ read_string(&f, description);
+
+ ver = read_uint8(&f);
+ if (ver == 0)
+ vt_entries = 64;
+ game.state = read_uint8(&f);
+ /* game.name - not saved */
+ read_string(&f, id);
+ if (strcmp(id, game.id)) {
+ f.close();
+ return err_BadFileOpen;
+ }
+ /* game.crc - not saved */
+
+ for (i = 0; i < MAX_FLAGS; i++)
+ game.flags[i] = read_uint8(&f);
+ for (i = 0; i < MAX_VARS; i++)
+ game.vars[i] = read_uint8(&f);
+
+ game.horizon = read_sint16(&f);
+ game.line_status = read_sint16(&f);
+ game.line_user_input = read_sint16(&f);
+ game.line_min_print = read_sint16(&f);
+
+ /* These are never saved */
+ game.cursor_pos = 0;
+ game.input_buffer[0] = 0;
+ game.echo_buffer[0] = 0;
+ game.keypress = 0;
+
+ game.input_mode = read_sint16(&f);
+ game.lognum = read_sint16(&f);
+
+ game.player_control = read_sint16(&f);
+ game.quit_prog_now = read_sint16(&f);
+ game.status_line = read_sint16(&f);
+ game.clock_enabled = read_sint16(&f);
+ game.exit_all_logics = read_sint16(&f);
+ game.picture_shown = read_sint16(&f);
+ game.has_prompt = read_sint16(&f);
+ game.game_flags = read_sint16(&f);
+ game.input_enabled = !read_sint16(&f);
+
+ for (i = 0; i < _HEIGHT; i++)
+ game.pri_table[i] = read_uint8(&f);
+
+ if (game.has_window)
+ close_window();
+ game.msg_box_ticks = 0;
+ game.block.active = false;
+ /* game.window - fixed by close_window() */
+ /* game.has_window - fixed by close_window() */
+
+ game.gfx_mode = read_sint16(&f);
+ game.cursor_char = read_uint8(&f);
+ game.color_fg = read_sint16(&f);
+ game.color_bg = read_sint16(&f);
+
+ /* game.hires (#ifdef USE_HIRES) - rebuilt from image stack */
+ /* game.sbuf - rebuilt from image stack */
+
+ /* game.ego_words - fixed by clean_input */
+ /* game.num_ego_words - fixed by clean_input */
+
+ game.num_objects = read_sint16(&f);
+ for (i = 0; i < (int16) game.num_objects; i++)
+ object_set_location(i, read_sint16(&f));
+
+ /* Those are not serialized */
+ for (i = 0; i < MAX_DIRS; i++) {
+ game.ev_keyp[i].occured = false;
+ }
+
+ for (i = 0; i < MAX_STRINGS; i++)
+ read_string(&f, game.strings[i]);
+
+ for (i = 0; i < MAX_DIRS; i++) {
+ if (read_uint8(&f) & RES_LOADED)
+ agi_load_resource(rLOGIC, i);
+ else
+ agi_unload_resource(rLOGIC, i);
+ game.logics[i].sIP = read_sint16(&f);
+ game.logics[i].cIP = read_sint16(&f);
+ }
+
+ for (i = 0; i < MAX_DIRS; i++) {
+ if (read_uint8(&f) & RES_LOADED)
+ agi_load_resource(rPICTURE, i);
+ else
+ agi_unload_resource(rPICTURE, i);
+ }
+
+ for (i = 0; i < MAX_DIRS; i++) {
+ if (read_uint8(&f) & RES_LOADED)
+ agi_load_resource(rVIEW, i);
+ else
+ agi_unload_resource(rVIEW, i);
+ }
+
+ for (i = 0; i < MAX_DIRS; i++) {
+ if (read_uint8(&f) & RES_LOADED)
+ agi_load_resource(rSOUND, i);
+ else
+ agi_unload_resource(rSOUND, i);
+ }
+
+ /* game.pictures - loaded above */
+ /* game.logics - loaded above */
+ /* game.views - loaded above */
+ /* game.sounds - loaded above */
+
+ for (i = 0; i < vt_entries; i++) {
+ struct vt_entry *v = &game.view_table[i];
+
+ v->step_time = read_uint8(&f);
+ v->step_time_count = read_uint8(&f);
+ v->entry = read_uint8(&f);
+ v->x_pos = read_sint16(&f);
+ v->y_pos = read_sint16(&f);
+ v->current_view = read_uint8(&f);
+
+ /* v->view_data - fixed below */
+
+ v->current_loop = read_uint8(&f);
+ v->num_loops = read_uint8(&f);
+
+ /* v->loop_data - fixed below */
+
+ v->current_cel = read_uint8(&f);
+ v->num_cels = read_uint8(&f);
+
+ /* v->cel_data - fixed below */
+ /* v->cel_data_2 - fixed below */
+
+ v->x_pos2 = read_sint16(&f);
+ v->y_pos2 = read_sint16(&f);
+
+ /* v->s - fixed below */
+
+ v->x_size = read_sint16(&f);
+ v->y_size = read_sint16(&f);
+ v->step_size = read_uint8(&f);
+ v->cycle_time = read_uint8(&f);
+ v->cycle_time_count = read_uint8(&f);
+ v->direction = read_uint8(&f);
+
+ v->motion = read_uint8(&f);
+ v->cycle = read_uint8(&f);
+ v->priority = read_uint8(&f);
+
+ v->flags = read_uint16(&f);
+
+ v->parm1 = read_uint8(&f);
+ v->parm2 = read_uint8(&f);
+ v->parm3 = read_uint8(&f);
+ v->parm4 = read_uint8(&f);
+ }
+ for (i = vt_entries; i < MAX_VIEWTABLE; i++) {
+ memset(&game.view_table[i], 0, sizeof(struct vt_entry));
+ }
+
+ /* Fix some pointers in viewtable */
+
+ for (i = 0; i < MAX_VIEWTABLE; i++) {
+ struct vt_entry *v = &game.view_table[i];
+
+ if (game.dir_view[v->current_view].offset == _EMPTY)
+ continue;
+
+ if (!(game.dir_view[v->current_view].flags & RES_LOADED))
+ agi_load_resource(rVIEW, v->current_view);
+
+ set_view(v, v->current_view); /* Fix v->view_data */
+ set_loop(v, v->current_loop); /* Fix v->loop_data */
+ set_cel(v, v->current_cel); /* Fix v->cel_data */
+ v->cel_data_2 = v->cel_data;
+ v->s = NULL; /* not sure if it is used... */
+ }
+
+ erase_both();
+
+ /* Clear input line */
+ clear_screen(0);
+ write_status();
+
+ /* Recreate background from saved image stack */
+ clear_image_stack();
+ while ((t = read_uint8(&f)) != 0) {
+ for (i = 0; i < 7; i++)
+ parm[i] = read_sint16(&f);
+ replay_image_stack_call(t, parm[0], parm[1], parm[2],
+ parm[3], parm[4], parm[5], parm[6]);
+ }
+
+ f.close();
+
+ setflag(F_restore_just_ran, true);
+
+ game.has_prompt = 0; /* force input line repaint if necessary */
+ clean_input();
+
+ erase_both();
+ blit_both();
+ commit_both();
+ show_pic();
+ do_update();
+
+ return err_OK;
+}
+
+#define NUM_SLOTS 12
+
+static int select_slot() {
+ int i, key, active = 0;
+ int rc = -1;
+ int hm = 2, vm = 3; /* box margins */
+ char desc[NUM_SLOTS][40];
+
+ for (i = 0; i < NUM_SLOTS; i++) {
+ char name[MAX_PATH];
+ Common::File f;
+ char sig[8];
+ sprintf(name, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, i);
+ f.open(name);
+ if (!f.isOpen()) {
+ strcpy(desc[i], " (empty slot)");
+ } else {
+ read_bytes(&f, sig, 8);
+ if (strncmp(sig, strSig, 8)) {
+ strcpy(desc[i], "(corrupt file)");
+ } else {
+ read_string(&f, desc[i]);
+ }
+ f.close();
+ }
+ }
+
+ while (42) {
+ char dstr[64];
+ for (i = 0; i < NUM_SLOTS; i++) {
+ sprintf(dstr, "[%-32.32s]", desc[i]);
+ print_text(dstr, 0, hm + 1, vm + 4 + i,
+ (40 - 2 * hm) - 1, i == active ? MSG_BOX_COLOUR : MSG_BOX_TEXT,
+ i == active ? MSG_BOX_TEXT : MSG_BOX_COLOUR);
+
+ }
+
+ poll_timer(); /* msdos driver -> does nothing */
+ key = do_poll_keyboard();
+ if (!console_keyhandler(key)) {
+ switch (key) {
+ case KEY_ENTER:
+ rc = active;
+ strncpy(game.strings[MAX_STRINGS], desc[i], MAX_STRINGLEN);
+ goto press;
+ case KEY_ESCAPE:
+ rc = -1;
+ goto getout;
+#ifdef USE_MOUSE
+ case BUTTON_LEFT:
+ break;
+#endif
+ case KEY_DOWN:
+ active++;
+ active %= NUM_SLOTS;
+ break;
+ case KEY_UP:
+ active--;
+ if (active < 0)
+ active = NUM_SLOTS - 1;
+ break;
+ }
+ }
+ console_cycle();
+ }
+
+press:
+ debugC(8, kDebugLevelMain | kDebugLevelInput, "Button pressed: %d", rc);
+
+getout:
+ close_window();
+ return rc;
+}
+
+int savegame_simple() {
+ char path[MAX_PATH];
+
+ sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, 0);
+ save_game(path, "Default savegame");
+
+ return err_OK;
+}
+
+int savegame_dialog() {
+ char path[MAX_PATH];
+ char *desc;
+ char *buttons[] = { "Do as I say!", "I regret", NULL };
+ char dstr[200];
+ int rc, slot = 0;
+ int hm, vm, hp, vp; /* box margins */
+ int w;
+
+ hm = 2;
+ vm = 3;
+ hp = hm * CHAR_COLS;
+ vp = vm * CHAR_LINES;
+ w = (40 - 2 * hm) - 1;
+
+ sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot);
+
+ draw_window(hp, vp, GFX_WIDTH - hp, GFX_HEIGHT - vp);
+ print_text("Select a slot in which you wish to save the game:",
+ 0, hm + 1, vm + 1, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
+ print_text("Press ENTER to select, ESC cancels",
+ 0, hm + 1, vm + 17, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
+
+ slot = select_slot();
+ if (slot < 0) /* ESC pressed */
+ return err_OK;
+
+ /* Get savegame description */
+ draw_window(hp, vp + 5 * CHAR_LINES, GFX_WIDTH - hp,
+ GFX_HEIGHT - vp - 9 * CHAR_LINES);
+ print_text("Enter a description for this game:",
+ 0, hm + 1, vm + 6, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
+ draw_rectangle(3 * CHAR_COLS, 11 * CHAR_LINES - 1,
+ 37 * CHAR_COLS, 12 * CHAR_LINES, MSG_BOX_TEXT);
+ flush_block(3 * CHAR_COLS, 11 * CHAR_LINES - 1,
+ 37 * CHAR_COLS, 12 * CHAR_LINES);
+
+ get_string(2, 11, 33, MAX_STRINGS);
+ print_character(3, 11, game.cursor_char, MSG_BOX_COLOUR, MSG_BOX_TEXT);
+ do {
+ main_cycle();
+ } while (game.input_mode == INPUT_GETSTRING);
+ close_window();
+
+ desc = game.strings[MAX_STRINGS];
+ sprintf(dstr, "Are you sure you want to save the game "
+ "described as:\n\n%s\n\nin slot %d?\n\n\n", desc, slot);
+
+ rc = selection_box(dstr, buttons);
+
+ if (rc != 0) {
+ message_box("Game NOT saved.");
+ return err_OK;
+ }
+
+ sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot);
+ debugC(8, kDebugLevelMain | kDebugLevelResources, "file is [%s]", path);
+
+ save_game(path, desc);
+
+ message_box("Game saved.");
+
+ return err_OK;
+}
+
+int loadgame_simple() {
+ char path[MAX_PATH];
+ int rc = 0;
+
+ sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, 0);
+
+ erase_both();
+ stop_sound();
+ close_window();
+
+ if ((rc = load_game(path)) == err_OK) {
+ message_box("Game restored.");
+ game.exit_all_logics = 1;
+ menu_enable_all();
+ } else {
+ message_box("Error restoring game.");
+ }
+
+ return rc;
+}
+
+int loadgame_dialog() {
+ char path[MAX_PATH];
+ int rc, slot = 0;
+ int hm, vm, hp, vp; /* box margins */
+ int w;
+
+ hm = 2;
+ vm = 3;
+ hp = hm * CHAR_COLS;
+ vp = vm * CHAR_LINES;
+ w = (40 - 2 * hm) - 1;
+
+ sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot);
+
+ erase_both();
+ stop_sound();
+
+ draw_window(hp, vp, GFX_WIDTH - hp, GFX_HEIGHT - vp);
+ print_text("Select a game which you wish to\nrestore:",
+ 0, hm + 1, vm + 1, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
+ print_text("Press ENTER to select, ESC cancels",
+ 0, hm + 1, vm + 17, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
+
+ slot = select_slot();
+
+ if (slot < 0) {
+ message_box("Game NOT restored.");
+ return err_OK;
+ }
+
+ sprintf(path, "%s/%05X_%s_%02d.sav", _savePath, game.crc, game.id, slot);
+
+ if ((rc = load_game(path)) == err_OK) {
+ message_box("Game restored.");
+ game.exit_all_logics = 1;
+ menu_enable_all();
+ } else {
+ message_box("Error restoring game.");
+ }
+
+ return rc;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/savegame.h b/engines/agi/savegame.h
new file mode 100644
index 0000000000..ac62bb66e7
--- /dev/null
+++ b/engines/agi/savegame.h
@@ -0,0 +1,50 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_SAVEGAME_H
+#define __AGI_SAVEGAME_H
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+int savegame_dialog(void);
+int loadgame_dialog(void);
+int savegame_simple(void);
+int loadgame_simple(void);
+
+/* Image stack support */
+#define ADD_PIC 1
+#define ADD_VIEW 2
+
+void clear_image_stack(void);
+void record_image_stack_call(uint8 type, int16 p1, int16 p2, int16 p3,
+ int16 p4, int16 p5, int16 p6, int16 p7);
+void replay_image_stack_call(uint8 type, int16 p1, int16 p2, int16 p3,
+ int16 p4, int16 p5, int16 p6, int16 p7);
+void release_image_stack(void);
+
+} // End of namespace Agi
+
+#endif
diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp
new file mode 100644
index 0000000000..963e55c0b3
--- /dev/null
+++ b/engines/agi/sound.cpp
@@ -0,0 +1,772 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "sound/mixer.h"
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+#define USE_INTERPOLATION
+#define USE_CHORUS
+
+/* TODO: add support for variable sampling rate in the output device
+ */
+
+#ifdef USE_IIGS_SOUND
+
+/**
+ * AGI engine sound envelope structure.
+ */
+struct sound_envelope {
+ uint8 bp;
+ uint8 inc_hi;
+ uint8 inc_lo;
+};
+
+struct sound_wavelist {
+ uint8 top;
+ uint8 addr;
+ uint8 size;
+ uint8 mode;
+ uint8 rel_hi;
+ uint8 rel_lo;
+};
+
+struct sound_instrument {
+ struct sound_envelope env[8];
+ uint8 relseg;
+ uint8 priority;
+ uint8 bendrange;
+ uint8 vibdepth;
+ uint8 vibspeed;
+ uint8 spare;
+ uint8 wac;
+ uint8 wbc;
+ struct sound_wavelist wal[8];
+ struct sound_wavelist wbl[8];
+};
+
+struct sound_iigs_sample {
+ uint8 type_lo;
+ uint8 type_hi;
+ uint8 srate_lo;
+ uint8 srate_hi;
+ uint16 unknown[2];
+ uint8 size_lo;
+ uint8 size_hi;
+ uint16 unknown2[13];
+};
+
+#if 0
+static struct sound_instrument *instruments;
+static int num_instruments;
+static uint8 *wave;
+#endif
+
+#endif
+
+static int playing;
+static struct channel_info chn[NUM_CHANNELS];
+static int endflag = -1;
+static int playing_sound = -1;
+static uint8 *song;
+static uint8 env;
+
+struct sound_driver *snd;
+
+extern struct sound_driver sound_dummy;
+
+static void stop_note(int i);
+static void play_note(int i, int freq, int vol);
+
+#ifdef USE_PCM_SOUND
+
+int16 *snd_buffer;
+static int16 *waveform;
+
+static int16 waveform_ramp[WAVEFORM_SIZE] = {
+ 0, 8, 16, 24, 32, 40, 48, 56,
+ 64, 72, 80, 88, 96, 104, 112, 120,
+ 128, 136, 144, 152, 160, 168, 176, 184,
+ 192, 200, 208, 216, 224, 232, 240, 255,
+ 0, -248, -240, -232, -224, -216, -208, -200,
+ -192, -184, -176, -168, -160, -152, -144, -136,
+ -128, -120, -112, -104, -96, -88, -80, -72,
+ -64, -56, -48, -40, -32, -24, -16, -8 /* Ramp up */
+};
+
+static int16 waveform_square[WAVEFORM_SIZE] = {
+ 255, 230, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 220, 220, 220, 110,
+ -255, -230, -220, -220, -220, -220, -220, -220,
+ -220, -220, -220, -220, -220, -220, -220, -220,
+ -220, -220, -220, -220, -220, -220, -220, -220,
+ -220, -220, -220, -110, 0, 0, 0, 0 /* Square */
+};
+
+static int16 waveform_mac[WAVEFORM_SIZE] = {
+ 45, 110, 135, 161, 167, 173, 175, 176,
+ 156, 137, 123, 110, 91, 72, 35, -2,
+ -60, -118, -142, -165, -170, -176, -177, -179,
+ -177, -176, -164, -152, -117, -82, -17, 47,
+ 92, 137, 151, 166, 170, 173, 171, 169,
+ 151, 133, 116, 100, 72, 43, -7, -57,
+ -99, -141, -156, -170, -174, -177, -178, -179,
+ -175, -172, -165, -159, -137, -114, -67, -19
+};
+
+#endif /* USE_PCM_SOUND */
+
+#ifdef USE_IIGS_SOUND
+
+static uint16 period[] = {
+ 1024, 1085, 1149, 1218, 1290, 1367,
+ 1448, 1534, 1625, 1722, 1825, 1933
+};
+
+static struct agi_note play_sample[] = {
+ {0xff, 0x7f, 0x18, 0x00, 0x7f},
+ {0xff, 0xff, 0x00, 0x00, 0x00},
+ {0xff, 0xff, 0x00, 0x00, 0x00},
+ {0xff, 0xff, 0x00, 0x00, 0x00}
+};
+
+static int note_to_period(int note) {
+ return 10 * (period[note % 12] >> (note / 12 - 3));
+}
+
+#endif /* USE_IIGS_SOUND */
+
+void unload_sound(int resnum) {
+ if (game.dir_sound[resnum].flags & RES_LOADED) {
+ if (game.sounds[resnum].flags & SOUND_PLAYING)
+ /* FIXME: Stop playing */
+ ;
+
+ /* Release RAW data for sound */
+ free(game.sounds[resnum].rdata);
+ game.sounds[resnum].rdata = NULL;
+ game.dir_sound[resnum].flags &= ~RES_LOADED;
+ }
+}
+
+void decode_sound(int resnum) {
+#ifdef USE_IIGS_SOUND
+ int type, size;
+ int16 *buf;
+ uint8 *src;
+ struct sound_iigs_sample *smp;
+
+ debugC(3, kDebugLevelSound, "(%d)", resnum);
+ type = READ_LE_UINT16(game.sounds[resnum].rdata);
+
+ if (type == AGI_SOUND_SAMPLE) {
+ /* Convert sample data to 16 bit signed format
+ */
+ smp = (struct sound_iigs_sample *)game.sounds[resnum].rdata;
+ size = ((int)smp->size_hi << 8) + smp->size_lo;
+ src = (uint8 *) game.sounds[resnum].rdata;
+ buf = (int16 *) calloc(1, 54 + (size << 1) + 100); /* FIXME */
+ memcpy(buf, src, 54);
+ for (; size--; buf[size + 54] = ((int16) src[size + 54] - 0x80) << 4); /* FIXME */
+ game.sounds[resnum].rdata = (uint8 *) buf;
+ free(src);
+ }
+#endif /* USE_IIGS_SOUND */
+}
+
+void start_sound(int resnum, int flag) {
+ int i, type;
+#ifdef USE_IIGS_SOUND
+ struct sound_iigs_sample *smp;
+#endif
+
+ if (game.sounds[resnum].flags & SOUND_PLAYING)
+ return;
+
+ stop_sound();
+
+ if (game.sounds[resnum].rdata == NULL)
+ return;
+
+ type = READ_LE_UINT16(game.sounds[resnum].rdata);
+
+ if (type != AGI_SOUND_SAMPLE && type != AGI_SOUND_MIDI && type != AGI_SOUND_4CHN)
+ return;
+
+ game.sounds[resnum].flags |= SOUND_PLAYING;
+ game.sounds[resnum].type = type;
+ playing_sound = resnum;
+ song = (uint8 *) game.sounds[resnum].rdata;
+
+ switch (type) {
+#ifdef USE_IIGS_SOUND
+ case AGI_SOUND_SAMPLE:
+ debugC(3, kDebugLevelSound, "IIGS sample");
+ smp = (struct sound_iigs_sample *)game.sounds[resnum].rdata;
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ chn[i].type = type;
+ chn[i].flags = 0;
+ chn[i].ins = (int16 *) & game.sounds[resnum].rdata[54];
+ chn[i].size = ((int)smp->size_hi << 8) + smp->size_lo;
+ chn[i].ptr = &play_sample[i];
+ chn[i].timer = 0;
+ chn[i].vol = 0;
+ chn[i].end = 0;
+ }
+ break;
+ case AGI_SOUND_MIDI:
+ debugC(3, kDebugLevelSound, "IIGS MIDI sequence");
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ chn[i].type = type;
+ chn[i].flags = AGI_SOUND_LOOP | AGI_SOUND_ENVELOPE;
+ chn[i].ins = waveform;
+ chn[i].size = WAVEFORM_SIZE;
+ chn[i].vol = 0;
+ chn[i].end = 0;
+ }
+
+ chn[0].timer = *(song + 2);
+ chn[0].ptr = (struct agi_note *)(song + 3);
+ break;
+#endif
+ case AGI_SOUND_4CHN:
+ /* Initialize channel info */
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ chn[i].type = type;
+ chn[i].flags = AGI_SOUND_LOOP;
+ if (env) {
+ chn[i].flags |= AGI_SOUND_ENVELOPE;
+ chn[i].adsr = AGI_SOUND_ENV_ATTACK;
+ }
+#ifdef USE_PCM_SOUND
+ chn[i].ins = waveform;
+ chn[i].size = WAVEFORM_SIZE;
+#endif
+ chn[i].ptr = (struct agi_note *)(song + (song[i << 1] | (song[(i << 1) + 1] << 8)));
+ chn[i].timer = 0;
+ chn[i].vol = 0;
+ chn[i].end = 0;
+ }
+ break;
+ }
+
+#ifdef USE_PCM_SOUND
+ memset(snd_buffer, 0, BUFFER_SIZE << 1);
+#endif
+ endflag = flag;
+
+ /* Nat Budin reports that the flag should be reset when sound starts
+ */
+ setflag(endflag, false);
+
+ /* FIXME: should wait for sound time instead of setting the flag
+ * immediately
+ */
+ if (opt.nosound) {
+ setflag(endflag, true);
+ stop_sound();
+ }
+}
+
+void stop_sound() {
+ int i;
+
+ endflag = -1;
+ for (i = 0; i < NUM_CHANNELS; i++)
+ stop_note(i);
+
+ if (playing_sound != -1) {
+ game.sounds[playing_sound].flags &= ~SOUND_PLAYING;
+ playing_sound = -1;
+ }
+}
+
+static int16 *buffer;
+
+int init_sound() {
+ int r = -1;
+
+#ifdef USE_PCM_SOUND
+ buffer = snd_buffer = (int16 *) calloc(2, BUFFER_SIZE);
+#endif
+
+ env = false;
+
+#ifdef USE_PCM_SOUND
+ switch (opt.soundemu) {
+ case SOUND_EMU_NONE:
+ waveform = waveform_ramp;
+ env = true;
+ break;
+ case SOUND_EMU_AMIGA:
+ case SOUND_EMU_PC:
+ waveform = waveform_square;
+ break;
+ case SOUND_EMU_MAC:
+ waveform = waveform_mac;
+ break;
+ }
+#endif
+
+ report("Initializing sound:\n");
+
+ report("sound: envelopes ");
+ if (env) {
+ report("enabled (decay=%d, sustain=%d)\n", ENV_DECAY, ENV_SUSTAIN);
+ } else {
+ report("disabled\n");
+ }
+
+#ifdef USE_IIGS_SOUND
+ /*load_instruments ("demo.sys"); */
+#endif
+
+ return r;
+}
+
+void deinit_sound(void) {
+ debugC(3, kDebugLevelSound, "()");
+ if (snd)
+ snd->deinit();
+#ifdef USE_PCM_SOUND
+ free(snd_buffer);
+#endif
+}
+
+static void stop_note(int i) {
+ chn[i].adsr = AGI_SOUND_ENV_RELEASE;
+
+#ifdef USE_CHORUS
+ /* Stop chorus ;) */
+ if (chn[i].type == AGI_SOUND_4CHN &&
+ opt.soundemu == SOUND_EMU_NONE && i < 3) {
+ stop_note(i + 4);
+ }
+#endif
+
+#ifdef __TURBOC__
+ if (i == 0)
+ nosound();
+#endif
+}
+
+static void play_note(int i, int freq, int vol) {
+ if (!getflag(F_sound_on))
+ vol = 0;
+ else if (vol && opt.soundemu == SOUND_EMU_PC)
+ vol = 160;
+
+#ifdef USE_PCM_SOUND
+ chn[i].phase = 0;
+#endif
+
+ chn[i].freq = freq;
+ chn[i].vol = vol;
+ chn[i].env = 0x10000;
+ chn[i].adsr = AGI_SOUND_ENV_ATTACK;
+
+#ifdef USE_CHORUS
+ /* Add chorus ;) */
+ if (chn[i].type == AGI_SOUND_4CHN &&
+ opt.soundemu == SOUND_EMU_NONE && i < 3) {
+ int newfreq = freq * 1007 / 1000;
+ if (freq == newfreq)
+ newfreq++;
+ play_note(i + 4, newfreq, vol * 2 / 3);
+ }
+#endif
+
+#ifdef __TURBOC__
+ if (i == 0)
+ sound(freq);
+#endif
+}
+
+#ifdef USE_IIGS_SOUND
+
+void play_midi_sound() {
+ uint8 *p;
+ uint8 parm1, parm2;
+ static uint8 cmd, ch;
+
+ playing = 1;
+
+ if (chn[0].timer > 0) {
+ chn[0].timer -= 2;
+ return;
+ }
+
+ p = (uint8 *) chn[0].ptr;
+
+ if (*p & 0x80) {
+ cmd = *p++;
+ ch = cmd & 0x0f;
+ cmd >>= 4;
+ }
+
+ switch (cmd) {
+ case 0x08:
+ parm1 = *p++;
+ parm2 = *p++;
+ if (ch < NUM_CHANNELS)
+ stop_note(ch);
+ break;
+ case 0x09:
+ parm1 = *p++;
+ parm2 = *p++;
+ if (ch < NUM_CHANNELS)
+ play_note(ch, note_to_period(parm1), 127);
+ break;
+ case 0x0b:
+ parm1 = *p++;
+ parm2 = *p++;
+ debugC(3, kDebugLevelSound, "controller %02x, ch %02x, val %02x", parm1, ch, parm2);
+ break;
+ case 0x0c:
+ parm1 = *p++;
+#if 0
+ if (ch < NUM_CHANNELS) {
+ chn[ch].ins = (uint16 *) & wave[waveaddr[parm1]];
+ chn[ch].size = wavesize[parm1];
+ }
+ debugC(3, kDebugLevelSound, "set patch %02x (%d,%d), ch %02x",
+ parm1, waveaddr[parm1], wavesize[parm1], ch);
+#endif
+ break;
+ }
+
+ chn[0].timer = *p++;
+ chn[0].ptr = (struct agi_note *)p;
+
+ if (*p >= 0xfc) {
+ debugC(3, kDebugLevelSound, "end of sequence");
+ playing = 0;
+ return;
+ }
+}
+
+void play_sample_sound() {
+ play_note(0, 11025 * 10, 200);
+ playing = 1;
+}
+
+#endif /* USE_IIGS_SOUND */
+
+void play_agi_sound() {
+ int i, freq;
+
+ for (playing = i = 0; i < (opt.soundemu == SOUND_EMU_PC ? 1 : 4); i++) {
+ playing |= !chn[i].end;
+
+ if (chn[i].end)
+ continue;
+
+ if ((--chn[i].timer) <= 0) {
+ stop_note(i);
+ freq = ((chn[i].ptr->frq_0 & 0x3f) << 4) | (int)(chn[i].ptr->frq_1 & 0x0f);
+
+ if (freq) {
+ uint8 v = chn[i].ptr->vol & 0x0f;
+ play_note(i, freq * 10, v == 0xf ? 0 : 0xff - (v << 1));
+ }
+
+ chn[i].timer = ((int)chn[i].ptr->dur_hi << 8) | chn[i].ptr->dur_lo;
+
+ if (chn[i].timer == 0xffff) {
+ chn[i].end = 1;
+ chn[i].vol = 0;
+ chn[i].env = 0;
+#ifdef USE_CHORUS
+ /* chorus */
+ if (chn[i].type == AGI_SOUND_4CHN && opt.soundemu == SOUND_EMU_NONE && i < 3) {
+ chn[i + 4].vol = 0;
+ chn[i + 4].env = 0;
+ }
+#endif
+ }
+ chn[i].ptr++;
+ }
+ }
+}
+
+void play_sound() {
+ int i;
+
+ if (endflag == -1)
+ return;
+
+#ifdef USE_IIGS_SOUND
+ if (chn[0].type == AGI_SOUND_MIDI) {
+ /* play_midi_sound (); */
+ playing = 0;
+ } else if (chn[0].type == AGI_SOUND_SAMPLE) {
+ play_sample_sound();
+ } else
+#endif
+ play_agi_sound();
+
+ if (!playing) {
+ for (i = 0; i < NUM_CHANNELS; chn[i++].vol = 0);
+
+ if (endflag != -1)
+ setflag(endflag, true);
+
+ if (playing_sound != -1)
+ game.sounds[playing_sound].flags &= ~SOUND_PLAYING;
+ playing_sound = -1;
+ endflag = -1;
+ }
+}
+
+#ifdef USE_PCM_SOUND
+
+uint32 mix_sound(void) {
+ register int i, p;
+ int16 *src;
+ int c, b, m;
+
+ memset(snd_buffer, 0, BUFFER_SIZE << 1);
+
+ for (c = 0; c < NUM_CHANNELS; c++) {
+ if (!chn[c].vol)
+ continue;
+
+ m = chn[c].flags & AGI_SOUND_ENVELOPE ?
+ chn[c].vol * chn[c].env >> 16 : chn[c].vol;
+
+ if (chn[c].type != AGI_SOUND_4CHN || c != 3) {
+ src = chn[c].ins;
+
+ p = chn[c].phase;
+ for (i = 0; i < BUFFER_SIZE; i++) {
+ b = src[p >> 8];
+#ifdef USE_INTERPOLATION
+ b += ((src[((p >> 8) + 1) % chn[c].size] - src[p >> 8]) * (p & 0xff)) >> 8;
+#endif
+ snd_buffer[i] += (b * m) >> 4;
+
+ p += (uint32) 118600 *4 / chn[c].freq;
+
+ /* FIXME */
+ if (chn[c].flags & AGI_SOUND_LOOP) {
+ p %= chn[c].size << 8;
+ } else {
+ if (p >= chn[c].size << 8) {
+ p = chn[c].vol = 0;
+ chn[c].end = 1;
+ break;
+ }
+ }
+
+ }
+ chn[c].phase = p;
+ } else {
+ /* Add white noise */
+ for (i = 0; i < BUFFER_SIZE; i++) {
+ b = rnd->getRandomNumber(255) - 128;
+ snd_buffer[i] += (b * m) >> 4;
+ }
+ }
+
+ switch (chn[c].adsr) {
+ case AGI_SOUND_ENV_ATTACK:
+ /* not implemented */
+ chn[c].adsr = AGI_SOUND_ENV_DECAY;
+ break;
+ case AGI_SOUND_ENV_DECAY:
+ if (chn[c].env > chn[c].vol * ENV_SUSTAIN + ENV_DECAY) {
+ chn[c].env -= ENV_DECAY;
+ } else {
+ chn[c].env = chn[c].vol * ENV_SUSTAIN;
+ chn[c].adsr = AGI_SOUND_ENV_SUSTAIN;
+ }
+ break;
+ case AGI_SOUND_ENV_SUSTAIN:
+ break;
+ case AGI_SOUND_ENV_RELEASE:
+ if (chn[c].env >= ENV_RELEASE) {
+ chn[c].env -= ENV_RELEASE;
+ } else {
+ chn[c].env = 0;
+ }
+ }
+ }
+
+ return BUFFER_SIZE;
+}
+
+#ifdef USE_IIGS_SOUND
+
+#if 0
+int load_instruments(char *fname) {
+ Common::File fp;
+ int i, j, k;
+ struct sound_instrument ai;
+ int num_wav;
+ char *path;
+
+ path = "sierrast";
+
+ if (!fp.open(path))
+ return err_BadFileOpen;
+ report("Loading samples: %s\n", path);
+
+ if ((wave = malloc(0x10000 * 2)) == NULL)
+ return err_NotEnoughMemory;
+
+ fp.read(wave, 0x10000);
+ fp.close();
+ for (i = 0x10000; i--;) {
+ ((int16 *) wave)[i] = 2 * ((int16) wave[i] - 128);
+ }
+
+ fp = fopen("bla", "w");
+ fwrite(wave, 2, 0x10000, fp);
+ fclose(fp);
+
+ report("Loading instruments: %s\n", path);
+
+ if ((fp = fopen(path, "rb")) == NULL)
+ return err_BadFileOpen;
+
+ fseek(fp, 0x8469, SEEK_SET);
+
+ for (num_wav = j = 0; j < 40; j++) {
+ fread(&ai, 1, 32, fp);
+
+ if (ai.env[0].bp > 0x7f)
+ break;
+
+#if 0
+ printf("Instrument %d loaded ----------------\n", j);
+ printf("Envelope:\n");
+ for (i = 0; i < 8; i++)
+ printf("[seg %d]: BP %02x Inc %04x\n", i, ai.env[i].bp,
+ ((int)ai.env[i].inc_hi << 8) | ai.env[i].inc_lo);
+ printf("rel seg: %d, pri inc: %d, bend range: %d, vib dep: %d, "
+ "vib spd: %d\n", ai.relseg, ai.priority,
+ ai.bendrange, ai.vibdepth, ai.vibspeed);
+ printf("A wave count: %d, B wave count: %d\n", ai.wac, ai.wbc);
+#endif
+
+ for (k = 0; k < ai.wac; k++, num_wav++) {
+ fread(&ai.wal[k], 1, 6, fp);
+#if 0
+ printf("[A %d of %d] top: %02x, wave address: %02x, "
+ "size: %02x, mode: %02x, relPitch: %04x\n", k + 1,
+ ai.wac, ai.wal[k].top, ai.wal[k].addr, ai.wal[k].size,
+ ai.wal[k].mode, ((int)ai.wal[k].rel_hi << 8) | ai.wal[k].rel_lo);
+#endif
+ }
+
+ for (k = 0; k < ai.wbc; k++, num_wav++) {
+ fread(&ai.wbl[k], 1, 6, fp);
+#if 0
+ printf("[B %d of %d] top: %02x, wave address: %02x, "
+ "size: %02x, mode: %02x, relPitch: %04x\n", k + 1, ai.wbc,
+ ai.wbl[k].top, ai.wbl[k].addr, ai.wbl[k].size,
+ ai.wbl[k].mode, ((int)ai.wbl[k].rel_hi << 8) | ai.wbl[k].rel_lo);
+#endif
+ }
+ waveaddr[j] = 256 * ai.wal[0].addr;
+ wavesize[j] = 256 * (1 << ((ai.wal[0].size) & 0x07));
+#if 1
+ printf("%d addr = %d\n", j, waveaddr[j]);
+ printf(" size = %d\n", wavesize[j]);
+#endif
+ }
+
+ num_instruments = j;
+ printf("%d Ensoniq 5503 instruments loaded. (%d waveforms)\n", num_instruments, num_wav);
+
+ fclose(fp);
+
+ return err_OK;
+}
+
+void unload_instruments() {
+ free(instruments);
+}
+#endif
+
+#endif /* USE_IIGS_SOUND */
+
+static void fill_audio(void *udata, int16 * stream, uint len) {
+ int16 *origData = stream;
+ len <<= 2;
+ uint origLen = len;
+
+ uint32 p = 0;
+ static uint32 n = 0, s = 0;
+
+ debugC(5, kDebugLevelSound, "(%p, %p, %d)", udata, stream, len);
+ memcpy(stream, (uint8 *) buffer + s, p = n);
+ for (n = 0, len -= p; n < len; p += n, len -= n) {
+ play_sound();
+ n = mix_sound() << 1;
+ if (len < n) {
+ memcpy((uint8 *) stream + p, buffer, len);
+ s = len;
+ n -= s;
+ return;
+ } else {
+ memcpy((uint8 *) stream + p, buffer, n);
+ }
+ }
+ play_sound();
+ n = mix_sound() << 1;
+ memcpy((uint8 *) stream + p, buffer, s = len);
+ n -= s;
+}
+
+AGIMusic::AGIMusic(Audio::Mixer *pMixer) {
+ _mixer = pMixer;
+ _sampleRate = pMixer->getOutputRate();
+ _mixer->setupPremix(this);
+}
+
+void AGIMusic::premixerCall(int16 *data, uint len) {
+ Agi::fill_audio(NULL, data, len);
+}
+
+void AGIMusic::setVolume(uint8 volume) {
+ // TODO
+}
+
+AGIMusic::~AGIMusic(void) {
+ _mixer->setupPremix(NULL);
+}
+
+AGIMusic *g_agi_music;
+
+#endif /* USE_PCM_SOUND */
+
+} // End of namespace Agi
diff --git a/engines/agi/sound.h b/engines/agi/sound.h
new file mode 100644
index 0000000000..5af9fe1a5f
--- /dev/null
+++ b/engines/agi/sound.h
@@ -0,0 +1,167 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_SOUND_H
+#define __AGI_SOUND_H
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+#define BUFFER_SIZE 410
+
+#define SOUND_EMU_NONE 0
+#define SOUND_EMU_PC 1
+#define SOUND_EMU_TANDY 2
+#define SOUND_EMU_MAC 3
+#define SOUND_EMU_AMIGA 4
+
+#define SOUND_PLAYING 0x01
+#define WAVEFORM_SIZE 64
+#define ENV_ATTACK 10000 /**< envelope attack rate */
+#define ENV_DECAY 1000 /**< envelope decay rate */
+#define ENV_SUSTAIN 100 /**< envelope sustain level */
+#define ENV_RELEASE 7500 /**< envelope release rate */
+#define NUM_CHANNELS 7 /**< number of sound channels */
+
+/**
+ * AGI engine sound driver structure.
+ */
+struct sound_driver {
+ char *description;
+ int (*init) (int16 * buffer);
+ void (*deinit) (void);
+};
+
+/**
+ * AGI sound resource structure.
+ */
+struct agi_sound {
+ uint32 flen; /**< size of raw data */
+ uint8 *rdata; /**< raw sound data */
+ uint8 flags; /**< sound flags */
+ uint16 type; /**< sound resource type */
+};
+
+/**
+ * AGI sound note structure.
+ */
+struct agi_note {
+ uint8 dur_lo; /**< LSB of note duration */
+ uint8 dur_hi; /**< MSB of note duration */
+ uint8 frq_0; /**< LSB of note frequency */
+ uint8 frq_1; /**< MSB of note frequency */
+ uint8 vol; /**< note volume */
+};
+
+/**
+ * AGI engine sound channel structure.
+ */
+struct channel_info {
+#define AGI_SOUND_SAMPLE 0x0001
+#define AGI_SOUND_MIDI 0x0002
+#define AGI_SOUND_4CHN 0x0008
+ uint32 type;
+ struct agi_note *ptr;
+#ifdef USE_PCM_SOUND
+ int16 *ins;
+ int32 size;
+ uint32 phase;
+#endif
+#define AGI_SOUND_LOOP 0x0001
+#define AGI_SOUND_ENVELOPE 0x0002
+ uint32 flags;
+#define AGI_SOUND_ENV_ATTACK 3
+#define AGI_SOUND_ENV_DECAY 2
+#define AGI_SOUND_ENV_SUSTAIN 1
+#define AGI_SOUND_ENV_RELEASE 0
+ uint32 adsr;
+ int32 timer;
+ uint32 end;
+ uint32 freq;
+ uint32 vol;
+ uint32 env;
+};
+
+void decode_sound(int);
+void unload_sound(int);
+void play_sound(void);
+int init_sound(void);
+void deinit_sound(void);
+void start_sound(int, int);
+void stop_sound(void);
+uint32 mix_sound(void);
+void __init_sound(void);
+int load_instruments(char *fname);
+
+extern struct sound_driver *snd;
+
+#endif /* __AGI_SOUND_H */
+
+#ifdef USE_PCM_SOUND
+
+} // End of namespace Agi
+
+#include "sound/audiostream.h"
+
+namespace Audio {
+class Mixer;
+} // End of namespace Audio
+
+namespace Agi {
+
+class AGIMusic : public Audio::AudioStream {
+public:
+ AGIMusic(Audio::Mixer * pMixer);
+ ~AGIMusic(void);
+ virtual void setVolume(uint8 volume);
+
+ // AudioStream API
+ int readBuffer(int16 * buffer, const int numSamples) {
+ premixerCall(buffer, numSamples / 2);
+ return numSamples;
+ }
+
+ bool isStereo() const {
+ return false;
+ }
+
+ bool endOfData() const {
+ return false;
+ }
+
+ int getRate() const {
+ return _sampleRate;
+ }
+
+private:
+ Audio::Mixer * _mixer;
+ uint32 _sampleRate;
+
+ void premixerCall(int16 * buf, uint len);
+};
+
+} // End of namespace Agi
+
+#endif
diff --git a/engines/agi/sprite.cpp b/engines/agi/sprite.cpp
new file mode 100644
index 0000000000..3a3ba7354e
--- /dev/null
+++ b/engines/agi/sprite.cpp
@@ -0,0 +1,868 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "agi/agi.h"
+#include "agi/list.h"
+#include "agi/sprite.h"
+#include "agi/graphics.h"
+#include "agi/text.h"
+#include "agi/savegame.h"
+
+namespace Agi {
+
+/**
+ * Sprite structure.
+ * This structure holds information on visible and priority data of
+ * a rectangular area of the AGI screen. Sprites are chained in two
+ * circular lists, one for updating and other for non-updating sprites.
+ */
+struct sprite {
+ struct list_head list;
+ struct vt_entry *v; /**< pointer to view table entry */
+ int16 x_pos; /**< x coordinate of the sprite */
+ int16 y_pos; /**< y coordinate of the sprite */
+ int16 x_size; /**< width of the sprite */
+ int16 y_size; /**< height of the sprite */
+ uint8 *buffer; /**< buffer to store background data */
+#ifdef USE_HIRES
+ uint8 *hires; /**< buffer for hi-res background */
+#endif
+};
+
+/*
+ * Sprite pool replaces dynamic allocation
+ */
+#undef ALLOC_DEBUG
+
+#ifdef USE_HIRES
+#define POOL_SIZE 68000 /* Gold Rush mine room needs > 50000 */
+ /* Speeder bike challenge needs > 67000 */
+#else
+#define POOL_SIZE 25000
+#endif
+static uint8 *sprite_pool;
+static uint8 *pool_top;
+
+static void *pool_alloc(int size) {
+ uint8 *x;
+
+ /* Adjust size to 32-bit boundary to prevent data misalignment
+ * errors.
+ */
+ size = (size + 3) & ~3;
+
+ x = pool_top;
+ pool_top += size;
+
+ if (pool_top >= (uint8 *)sprite_pool + POOL_SIZE) {
+ debugC(1, kDebugLevelMain | kDebugLevelResources, "not enough memory");
+ pool_top = x;
+ return NULL;
+ }
+
+ return x;
+}
+
+static void pool_release(void *s) {
+ pool_top = (uint8 *)s;
+}
+
+/*
+ * Blitter functions
+ */
+
+/* Blit one pixel considering the priorities */
+
+static void blit_pixel(uint8 *p, uint8 *end, uint8 col, int spr, int width, int *hidden) {
+ int epr = 0, pr = 0; /* effective and real priorities */
+
+ /* CM: priority 15 overrides control lines and is ignored when
+ * tracking effective priority. This tweak is needed to fix
+ * bug #451768, and should not affect Sierra games because
+ * sprites shouldn't have priority 15 (like the AGI Mouse
+ * demo "mouse pointer")
+ *
+ * Update: this solution breaks other games, and can't be used.
+ */
+
+ if (p >= end)
+ return;
+
+ /* Check if we're on a control line */
+ if ((pr = *p & 0xf0) < 0x30) {
+ uint8 *p1;
+ /* Yes, get effective priority going down */
+ for (p1 = p; p1 < end && (epr = *p1 & 0xf0) < 0x30;
+ p1 += width);
+ if (p1 >= end)
+ epr = 0x40;
+ } else {
+ epr = pr;
+ }
+
+ if (spr >= epr) {
+ /* Keep control line information visible, but put our
+ * priority over water (0x30) surface
+ */
+ *p = (pr < 0x30 ? pr : spr) | col;
+ *hidden = false;
+
+ /* Except if our priority is 15, which should never happen
+ * (fixes bug #451768)
+ *
+ * Update: breaks other games, can't be used
+ *
+ * if (spr == 0xf0)
+ * *p = spr | col;
+ */
+ }
+}
+
+#ifdef USE_HIRES
+
+#define X_FACT 2 /* Horizontal hires factor */
+
+static int blit_hires_cel(int x, int y, int spr, struct view_cel *c) {
+ uint8 *q = NULL;
+ uint8 *h0, *h, *end;
+ int i, j, t, m, col;
+ int hidden = true;
+
+ q = c->data;
+ t = c->transparency;
+ m = c->mirror;
+ spr <<= 4;
+ h0 = &game.hires[(x + y * _WIDTH + m * (c->width - 1)) * X_FACT];
+
+ end = game.hires + _WIDTH * X_FACT * _HEIGHT;
+
+ for (i = 0; i < c->height; i++) {
+ h = h0;
+ while (*q) {
+ col = (*q & 0xf0) >> 4;
+ for (j = *q & 0x0f; j; j--, h += X_FACT * (1 - 2 * m)) {
+ if (col != t) {
+ blit_pixel(h, end, col, spr, _WIDTH * X_FACT, &hidden);
+ blit_pixel(h + 1, end, col, spr, _WIDTH * X_FACT, &hidden);
+ }
+ }
+ q++;
+ }
+ h0 += _WIDTH * X_FACT;
+ q++;
+ }
+ return hidden;
+}
+
+#endif
+
+static int blit_cel(int x, int y, int spr, struct view_cel *c) {
+ uint8 *p0, *p, *q = NULL, *end;
+ int i, j, t, m, col;
+ int hidden = true;
+
+ /* Fixes bug #477841 (crash in PQ1 map C4 when y == -2) */
+ if (y < 0)
+ y = 0;
+ if (x < 0)
+ x = 0;
+ if (y >= _HEIGHT)
+ y = _HEIGHT - 1;
+ if (x >= _WIDTH)
+ x = _WIDTH - 1;
+
+#ifdef USE_HIRES
+ if (opt.hires)
+ blit_hires_cel(x, y, spr, c);
+#endif
+
+ q = c->data;
+ t = c->transparency;
+ m = c->mirror;
+ spr <<= 4;
+ p0 = &game.sbuf[x + y * _WIDTH + m * (c->width - 1)];
+
+ end = game.sbuf + _WIDTH * _HEIGHT;
+
+ for (i = 0; i < c->height; i++) {
+ p = p0;
+ while (*q) {
+ col = (*q & 0xf0) >> 4;
+ for (j = *q & 0x0f; j; j--, p += 1 - 2 * m) {
+ if (col != t) {
+ blit_pixel(p, end, col, spr, _WIDTH, &hidden);
+ }
+ }
+ q++;
+ }
+ p0 += _WIDTH;
+ q++;
+ }
+
+ return hidden;
+}
+
+static void objs_savearea(struct sprite *s) {
+ int y;
+ int16 x_pos = s->x_pos, y_pos = s->y_pos;
+ int16 x_size = s->x_size, y_size = s->y_size;
+ uint8 *p0, *q;
+#ifdef USE_HIRES
+ uint8 *h0, *k;
+#endif
+
+ if (x_pos + x_size > _WIDTH)
+ x_size = _WIDTH - x_pos;
+
+ if (x_pos < 0) {
+ x_size += x_pos;
+ x_pos = 0;
+ }
+
+ if (y_pos + y_size > _HEIGHT)
+ y_size = _HEIGHT - y_pos;
+
+ if (y_pos < 0) {
+ y_size += y_pos;
+ y_pos = 0;
+ }
+
+ if (x_size <= 0 || y_size <= 0)
+ return;
+
+ p0 = &game.sbuf[x_pos + y_pos * _WIDTH];
+ q = s->buffer;
+#ifdef USE_HIRES
+ h0 = &game.hires[(x_pos + y_pos * _WIDTH) * 2];
+ k = s->hires;
+#endif
+ for (y = 0; y < y_size; y++) {
+ memcpy(q, p0, x_size);
+ q += x_size;
+ p0 += _WIDTH;
+#ifdef USE_HIRES
+ memcpy(k, h0, x_size * 2);
+ k += x_size * 2;
+ h0 += _WIDTH * 2;
+#endif
+ }
+}
+
+static void objs_restorearea(struct sprite *s) {
+ int y, offset;
+ int16 x_pos = s->x_pos, y_pos = s->y_pos;
+ int16 x_size = s->x_size, y_size = s->y_size;
+ uint8 *p0, *q;
+#ifdef USE_HIRES
+ uint8 *h0, *k;
+#endif
+
+ if (x_pos + x_size > _WIDTH)
+ x_size = _WIDTH - x_pos;
+
+ if (x_pos < 0) {
+ x_size += x_pos;
+ x_pos = 0;
+ }
+
+ if (y_pos + y_size > _HEIGHT)
+ y_size = _HEIGHT - y_pos;
+
+ if (y_pos < 0) {
+ y_size += y_pos;
+ y_pos = 0;
+ }
+
+ if (x_size <= 0 || y_size <= 0)
+ return;
+
+ p0 = &game.sbuf[x_pos + y_pos * _WIDTH];
+ q = s->buffer;
+#ifdef USE_HIRES
+ h0 = &game.hires[(x_pos + y_pos * _WIDTH) * 2];
+ k = s->hires;
+#endif
+ offset = game.line_min_print * CHAR_LINES;
+ for (y = 0; y < y_size; y++) {
+ memcpy(p0, q, x_size);
+ put_pixels_a(x_pos, y_pos + y + offset, x_size, p0);
+ q += x_size;
+ p0 += _WIDTH;
+#ifdef USE_HIRES
+ memcpy(h0, k, x_size * 2);
+ if (opt.hires) {
+ put_pixels_hires(x_pos * 2, y_pos + y + offset, x_size * 2, h0);
+ }
+ k += x_size * 2;
+ h0 += _WIDTH * 2;
+#endif
+ }
+}
+
+/*
+ * Sprite management functions
+ */
+
+static LIST_HEAD(spr_upd_head);
+static LIST_HEAD(spr_nonupd_head);
+
+/**
+ * Condition to determine whether a sprite will be in the 'updating' list.
+ */
+static int test_updating(struct vt_entry *v) {
+ /* Sanity check (see bug #779302) */
+ if (~game.dir_view[v->current_view].flags & RES_LOADED)
+ return 0;
+
+ return (v->flags & (ANIMATED | UPDATE | DRAWN)) == (ANIMATED | UPDATE | DRAWN);
+}
+
+/**
+ * Condition to determine whether a sprite will be in the 'non-updating' list.
+ */
+static int test_not_updating(struct vt_entry *v) {
+ /* Sanity check (see bug #779302) */
+ if (~game.dir_view[v->current_view].flags & RES_LOADED)
+ return 0;
+
+ return (v->flags & (ANIMATED | UPDATE | DRAWN)) == (ANIMATED | DRAWN);
+}
+
+/**
+ * Convert sprite priority to y value.
+ */
+static INLINE int prio_to_y(int p) {
+ int i;
+
+ if (p == 0)
+ return -1;
+
+ for (i = 167; i >= 0; i--) {
+ if (game.pri_table[i] < p)
+ return i;
+ }
+
+ return -1; /* (p - 5) * 12 + 48; */
+}
+
+/**
+ * Create and initialize a new sprite structure.
+ */
+static struct sprite *new_sprite(struct vt_entry *v) {
+ struct sprite *s;
+
+ s = (struct sprite *)pool_alloc(sizeof(struct sprite));
+ if (s == NULL)
+ return NULL;
+
+ s->v = v; /* link sprite to associated view table entry */
+ s->x_pos = v->x_pos;
+ s->y_pos = v->y_pos - v->y_size + 1;
+ s->x_size = v->x_size;
+ s->y_size = v->y_size;
+ s->buffer = (uint8 *) pool_alloc(s->x_size * s->y_size);
+#ifdef USE_HIRES
+ s->hires = (uint8 *) pool_alloc(s->x_size * s->y_size * 2);
+#endif
+ v->s = s; /* link view table entry to this sprite */
+
+ return s;
+}
+
+/**
+ * Insert sprite in the specified sprite list.
+ */
+static void spr_addlist(struct list_head *head, struct vt_entry *v) {
+ struct sprite *s;
+
+ s = new_sprite(v);
+ list_add_tail(&s->list, head);
+}
+
+/**
+ * Sort sprites from lower y values to build a sprite list.
+ */
+static struct list_head *build_list(struct list_head *head,
+ int (*test) (struct vt_entry *)) {
+ int i, j, k;
+ struct vt_entry *v;
+ struct vt_entry *entry[0x100];
+ int y_val[0x100];
+ int min_y = 0xff, min_index = 0;
+
+ /* fill the arrays with all sprites that satisfy the 'test'
+ * condition and their y values
+ */
+ i = 0;
+ for (v = game.view_table; v < &game.view_table[MAX_VIEWTABLE]; v++) {
+ if (test(v)) {
+ entry[i] = v;
+ y_val[i] = v->flags & FIXED_PRIORITY ? prio_to_y(v->priority) : v->y_pos;
+ i++;
+ }
+ }
+
+ /* now look for the smallest y value in the array and put that
+ * sprite in the list
+ */
+ for (j = 0; j < i; j++) {
+ min_y = 0xff;
+ for (k = 0; k < i; k++) {
+ if (y_val[k] < min_y) {
+ min_index = k;
+ min_y = y_val[k];
+ }
+ }
+
+ y_val[min_index] = 0xff;
+ spr_addlist(head, entry[min_index]);
+ }
+
+ return head;
+}
+
+/**
+ * Build list of updating sprites.
+ */
+static struct list_head *build_upd_blitlist() {
+ return build_list(&spr_upd_head, test_updating);
+}
+
+/**
+ * Build list of non-updating sprites.
+ */
+static struct list_head *build_nonupd_blitlist() {
+ return build_list(&spr_nonupd_head, test_not_updating);
+}
+
+/**
+ * Clear the given sprite list.
+ */
+static void free_list(struct list_head *head) {
+ struct list_head *h;
+ struct sprite *s;
+
+ list_for_each(h, head, prev) {
+ s = list_entry(h, struct sprite, list);
+ list_del(h);
+#ifdef USE_HIRES
+ pool_release(s->hires);
+#endif
+ pool_release(s->buffer);
+ pool_release(s);
+ }
+}
+
+/**
+ * Copy sprites from the pic buffer to the screen buffer, and check if
+ * sprites of the given list have moved.
+ */
+static void commit_sprites(struct list_head *head) {
+ struct list_head *h;
+
+ list_for_each(h, head, next) {
+ struct sprite *s = list_entry(h, struct sprite, list);
+ int x1, y1, x2, y2, w, h;
+
+ w = (s->v->cel_data->width > s->v->cel_data_2->width) ?
+ s->v->cel_data->width : s->v->cel_data_2->width;
+
+ h = (s->v->cel_data->height >
+ s->v->cel_data_2->height) ? s->v->cel_data->
+ height : s->v->cel_data_2->height;
+
+ s->v->cel_data_2 = s->v->cel_data;
+
+ if (s->v->x_pos < s->v->x_pos2) {
+ x1 = s->v->x_pos;
+ x2 = s->v->x_pos2 + w - 1;
+ } else {
+ x1 = s->v->x_pos2;
+ x2 = s->v->x_pos + w - 1;
+ }
+
+ if (s->v->y_pos < s->v->y_pos2) {
+ y1 = s->v->y_pos - h + 1;
+ y2 = s->v->y_pos2;
+ } else {
+ y1 = s->v->y_pos2 - h + 1;
+ y2 = s->v->y_pos;
+ }
+
+ commit_block(x1, y1, x2, y2);
+
+ if (s->v->step_time_count != s->v->step_time)
+ continue;
+
+ if (s->v->x_pos == s->v->x_pos2 && s->v->y_pos == s->v->y_pos2) {
+ s->v->flags |= DIDNT_MOVE;
+ continue;
+ }
+
+ s->v->x_pos2 = s->v->x_pos;
+ s->v->y_pos2 = s->v->y_pos;
+ s->v->flags &= ~DIDNT_MOVE;
+ }
+
+#ifdef USE_CONSOLE
+ if (debug_.statusline)
+ write_status();
+#endif
+}
+
+/**
+ * Erase all sprites in the given list.
+ */
+static void erase_sprites(struct list_head *head) {
+ struct list_head *h;
+
+ list_for_each(h, head, prev) {
+ struct sprite *s = list_entry(h, struct sprite, list);
+ objs_restorearea(s);
+ }
+
+ free_list(head);
+}
+
+/**
+ * Blit all sprites in the given list.
+ */
+static void blit_sprites(struct list_head *head) {
+ struct list_head *h = NULL;
+ int hidden;
+
+ list_for_each(h, head, next) {
+ struct sprite *s = list_entry(h, struct sprite, list);
+ objs_savearea(s);
+ debugC(8, kDebugLevelSprites, "s->v->entry = %d (prio %d)", s->v->entry, s->v->priority);
+ hidden = blit_cel(s->x_pos, s->y_pos, s->v->priority, s->v->cel_data);
+ if (s->v->entry == 0) { /* if ego, update f1 */
+ setflag(F_ego_invisible, hidden);
+ }
+ }
+}
+
+/*
+ * Public functions
+ */
+
+void commit_upd_sprites() {
+ commit_sprites(&spr_upd_head);
+}
+
+void commit_nonupd_sprites() {
+ commit_sprites(&spr_nonupd_head);
+}
+
+/* check moves in both lists */
+void commit_both() {
+ commit_upd_sprites();
+ commit_nonupd_sprites();
+}
+
+/**
+ * Erase updating sprites.
+ * This function follows the list of all updating sprites and restores
+ * the visible and priority data of their background buffers back to
+ * the AGI screen.
+ *
+ * @see erase_nonupd_sprites()
+ * @see erase_both()
+ */
+void erase_upd_sprites() {
+ erase_sprites(&spr_upd_head);
+}
+
+/**
+ * Erase non-updating sprites.
+ * This function follows the list of all non-updating sprites and restores
+ * the visible and priority data of their background buffers back to
+ * the AGI screen.
+ *
+ * @see erase_upd_sprites()
+ * @see erase_both()
+ */
+void erase_nonupd_sprites() {
+ erase_sprites(&spr_nonupd_head);
+}
+
+/**
+ * Erase all sprites.
+ * This function follows the lists of all updating and non-updating
+ * sprites and restores the visible and priority data of their background
+ * buffers back to the AGI screen.
+ *
+ * @see erase_upd_sprites()
+ * @see erase_nonupd_sprites()
+ */
+void erase_both() {
+ erase_upd_sprites();
+ erase_nonupd_sprites();
+}
+
+/**
+ * Blit updating sprites.
+ * This function follows the list of all updating sprites and blits
+ * them on the AGI screen.
+ *
+ * @see blit_nonupd_sprites()
+ * @see blit_both()
+ */
+void blit_upd_sprites() {
+ debugC(7, kDebugLevelSprites, "blit updating");
+ blit_sprites(build_upd_blitlist());
+}
+
+/**
+ * Blit non-updating sprites.
+ * This function follows the list of all non-updating sprites and blits
+ * them on the AGI screen.
+ *
+ * @see blit_upd_sprites()
+ * @see blit_both()
+ */
+void blit_nonupd_sprites() {
+ debugC(7, kDebugLevelSprites, "blit non-updating");
+ blit_sprites(build_nonupd_blitlist());
+}
+
+/**
+ * Blit all sprites.
+ * This function follows the lists of all updating and non-updating
+ * sprites and blits them on the AGI screen.
+ *
+ * @see blit_upd_sprites()
+ * @see blit_nonupd_sprites()
+ */
+void blit_both() {
+ blit_nonupd_sprites();
+ blit_upd_sprites();
+}
+
+/**
+ * Add view to picture.
+ * This function is used to implement the add.to.pic AGI command. It
+ * copies the specified cel from a view resource on the current picture.
+ * This cel is not a sprite, it can't be moved or removed.
+ * @param view number of view resource
+ * @param loop number of loop in the specified view resource
+ * @param cel number of cel in the specified loop
+ * @param x x coordinate to place the view
+ * @param y y coordinate to place the view
+ * @param pri priority to use
+ * @param mar if < 4, create a margin around the the base of the cel
+ */
+void add_to_pic(int view, int loop, int cel, int x, int y, int pri, int mar) {
+ struct view_cel *c = NULL;
+ int x1, y1, x2, y2, y3;
+ uint8 *p1, *p2;
+
+ debugC(3, kDebugLevelSprites, "v=%d, l=%d, c=%d, x=%d, y=%d, p=%d, m=%d", view, loop, cel, x, y, pri, mar);
+
+ record_image_stack_call(ADD_VIEW, view, loop, cel, x, y, pri, mar);
+
+ /*
+ * Was hardcoded to 8, changed to pri_table[y] to fix Gold
+ * Rush (see bug #587558)
+ */
+ if (pri == 0)
+ pri = game.pri_table[y];
+
+ c = &game.views[view].loop[loop].cel[cel];
+
+ x1 = x;
+ y1 = y - c->height + 1;
+ x2 = x + c->width - 1;
+ y2 = y;
+
+ if (x1 < 0) {
+ x2 -= x1;
+ x1 = 0;
+ }
+ if (y1 < 0) {
+ y2 -= y1;
+ y1 = 0;
+ }
+ if (x2 >= _WIDTH)
+ x2 = _WIDTH - 1;
+ if (y2 >= _HEIGHT)
+ y2 = _HEIGHT - 1;
+
+ erase_both();
+
+ debugC(4, kDebugLevelSprites, "blit_cel (%d, %d, %d, c)", x, y, pri);
+ blit_cel(x1, y1, pri, c);
+
+ /* If margin is 0, 1, 2, or 3, the base of the cel is
+ * surrounded with a rectangle of the corresponding priority.
+ * If margin >= 4, this extra margin is not shown.
+ */
+ if (mar < 4) {
+ /* add rectangle around object, don't clobber control
+ * info in priority data. The box extends to the end of
+ * its priority band!
+ *
+ * SQ1 needs +1 (see bug #810331)
+ */
+ y3 = (y2 / 12) * 12 + 1;
+
+ p1 = &game.sbuf[x1 + y3 * _WIDTH];
+ p2 = &game.sbuf[x2 + y3 * _WIDTH];
+
+ for (y = y3; y <= y2; y++) {
+ if ((*p1 >> 4) >= 4)
+ *p1 = (mar << 4) | (*p1 & 0x0f);
+ if ((*p2 >> 4) >= 4)
+ *p2 = (mar << 4) | (*p2 & 0x0f);
+ p1 += _WIDTH;
+ p2 += _WIDTH;
+ }
+
+ debugC(4, kDebugLevelSprites, "pri box: %d %d %d %d (%d)", x1, y3, x2, y2, mar);
+ p1 = &game.sbuf[x1 + y3 * _WIDTH];
+ p2 = &game.sbuf[x1 + y2 * _WIDTH];
+ for (x = x1; x <= x2; x++) {
+ if ((*p1 >> 4) >= 4)
+ *p1 = (mar << 4) | (*p1 & 0x0f);
+ if ((*p2 >> 4) >= 4)
+ *p2 = (mar << 4) | (*p2 & 0x0f);
+ p1++;
+ p2++;
+ }
+ }
+
+ blit_both();
+
+ debugC(4, kDebugLevelSprites, "commit_block (%d, %d, %d, %d)", x1, y1, x2, y2);
+ commit_block(x1, y1, x2, y2);
+}
+
+/**
+ * Show object and description
+ * This function shows an object from the player's inventory, displaying
+ * a message box with the object description.
+ * @param n Number of the object to show
+ */
+void show_obj(int n) {
+ struct view_cel *c;
+ struct sprite s;
+ int x1, y1, x2, y2;
+
+ agi_load_resource(rVIEW, n);
+ if (!(c = &game.views[n].loop[0].cel[0]))
+ return;
+
+ x1 = (_WIDTH - c->width) / 2;
+ y1 = 112;
+ x2 = x1 + c->width - 1;
+ y2 = y1 + c->height - 1;
+
+ s.x_pos = x1;
+ s.y_pos = y1;
+ s.x_size = c->width;
+ s.y_size = c->height;
+ s.buffer = (uint8 *)malloc(s.x_size * s.y_size);
+#ifdef USE_HIRES
+ s.hires = (uint8 *)malloc(s.x_size * s.y_size * 2);
+#endif
+
+ objs_savearea(&s);
+ blit_cel(x1, y1, s.x_size, c);
+ commit_block(x1, y1, x2, y2);
+ message_box(game.views[n].descr);
+ objs_restorearea(&s);
+ commit_block(x1, y1, x2, y2);
+
+ free(s.buffer);
+
+ /* Added to fix a memory leak --Vasyl */
+#ifdef USE_HIRES
+ free(s.hires);
+#endif
+}
+
+void commit_block(int x1, int y1, int x2, int y2) {
+ int i, w, offset;
+ uint8 *q;
+#ifdef USE_HIRES
+ uint8 *h;
+#endif
+
+ if (!game.picture_shown)
+ return;
+
+ /* Clipping */
+ if (x1 < 0)
+ x1 = 0;
+ if (x2 < 0)
+ x2 = 0;
+ if (y1 < 0)
+ y1 = 0;
+ if (y2 < 0)
+ y2 = 0;
+ if (x1 >= _WIDTH)
+ x1 = _WIDTH - 1;
+ if (x2 >= _WIDTH)
+ x2 = _WIDTH - 1;
+ if (y1 >= _HEIGHT)
+ y1 = _HEIGHT - 1;
+ if (y2 >= _HEIGHT)
+ y2 = _HEIGHT - 1;
+
+ debugC(7, kDebugLevelSprites, "%d, %d, %d, %d", x1, y1, x2, y2);
+
+ w = x2 - x1 + 1;
+ q = &game.sbuf[x1 + _WIDTH * y1];
+#ifdef USE_HIRES
+ h = &game.hires[(x1 + _WIDTH * y1) * 2];
+#endif
+ offset = game.line_min_print * CHAR_LINES;
+ for (i = y1; i <= y2; i++) {
+ put_pixels_a(x1, i + offset, w, q);
+ q += _WIDTH;
+#ifdef USE_HIRES
+ if (opt.hires) {
+ put_pixels_hires(x1 * 2, i + offset, w * 2, h);
+ }
+ h += _WIDTH * 2;
+#endif
+ }
+
+ flush_block_a(x1, y1 + offset, x2, y2 + offset);
+}
+
+int init_sprites() {
+ if ((sprite_pool = (uint8 *)malloc(POOL_SIZE)) == NULL)
+ return err_NotEnoughMemory;
+
+ pool_top = sprite_pool;
+
+ return err_OK;
+}
+
+void deinit_sprites() {
+ free(sprite_pool);
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sprite.h b/engines/agi/sprite.h
new file mode 100644
index 0000000000..a3d0d8b871
--- /dev/null
+++ b/engines/agi/sprite.h
@@ -0,0 +1,47 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_SPRITE_H
+#define __AGI_SPRITE_H
+
+namespace Agi {
+
+int init_sprites(void);
+void deinit_sprites(void);
+void erase_upd_sprites(void);
+void erase_nonupd_sprites(void);
+void erase_both(void);
+void blit_upd_sprites(void);
+void blit_nonupd_sprites(void);
+void blit_both(void);
+void commit_upd_sprites(void);
+void commit_nonupd_sprites(void);
+void commit_both(void);
+void add_to_pic(int, int, int, int, int, int, int);
+void show_obj(int);
+void commit_block(int, int, int, int);
+
+} // End of namespace Agi
+
+#endif /* __AGI_SPRITE_H */
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp
new file mode 100644
index 0000000000..5619e1cf02
--- /dev/null
+++ b/engines/agi/text.cpp
@@ -0,0 +1,677 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "agi/agi.h"
+#include "agi/sprite.h" /* for commit_both() */
+#include "agi/graphics.h"
+#include "agi/keyboard.h"
+#include "agi/text.h"
+
+namespace Agi {
+
+static void print_text2(int l, char *msg, int foff, int xoff, int yoff,
+ int len, int fg, int bg) {
+ int x1, y1;
+ int maxx, minx, ofoff;
+ int update;
+ /* Note: Must be unsigned to use AGDS cyrillic characters! */
+ unsigned char *m;
+
+ /* kludge! */
+ update = 1;
+ if (l == 2) {
+ update = l = 0;
+ }
+
+ /* FR: strings with len == 1 were not printed
+ */
+ if (len == 1) {
+ put_text_character(l, xoff + foff, yoff, *msg, fg, bg);
+ maxx = 1;
+ minx = 0;
+ ofoff = foff;
+ y1 = 0; /* Check this */
+ } else {
+ maxx = 0;
+ minx = GFX_WIDTH;
+ ofoff = foff;
+
+ for (m = (unsigned char *)msg, x1 = y1 = 0; *m; m++) {
+
+ if (*m >= 0x20 || *m == 1 || *m == 2 || *m == 3) {
+ /* FIXME */
+ int ypos;
+
+ ypos = (y1 * CHAR_LINES) + yoff;
+
+ if ((x1 != (len - 1) || x1 == 39) && (ypos <= (GFX_HEIGHT - CHAR_LINES))) {
+ int xpos;
+
+ xpos = (x1 * CHAR_COLS) + xoff + foff;
+
+ if (xpos >= GFX_WIDTH)
+ continue;
+
+ put_text_character(l, xpos, ypos, *m, fg, bg);
+
+ if (x1 > maxx)
+ maxx = x1;
+ if (x1 < minx)
+ minx = x1;
+ }
+
+ x1++;
+ /* DF: changed the len-1 to len... */
+ if (x1 == len && m[1] != '\n')
+ y1++, x1 = foff = 0;
+ } else {
+ y1++;
+ x1 = foff = 0;
+ }
+ }
+ }
+
+ if (l)
+ return;
+
+ if (maxx < minx)
+ return;
+
+ maxx *= CHAR_COLS;
+ minx *= CHAR_COLS;
+
+ if (update) {
+ schedule_update(foff + xoff + minx, yoff, ofoff + xoff + maxx + CHAR_COLS - 1,
+ yoff + y1 * CHAR_LINES + CHAR_LINES + 1);
+ /* Making synchronous text updates reduces CPU load
+ * when updating status line and input area
+ */
+ do_update();
+ }
+}
+
+/* len is in characters, not pixels!!
+ */
+static void blit_textbox(char *p, int y, int x, int len) {
+ /* if x | y = -1, then centre the box */
+ int xoff, yoff, lin, h, w;
+ char *msg, *m;
+
+ debugC(3, kDebugLevelText, "x=%d, y=%d, len=%d", x, y, len);
+ if (game.window.active)
+ close_window();
+
+ if (x == 0 && y == 0 && len == 0)
+ x = y = -1;
+
+ if (len <= 0 || len >= 40)
+ len = 32;
+
+ xoff = x * CHAR_COLS;
+ yoff = y * CHAR_LINES;
+ len--;
+
+ m = msg = word_wrap_string(agi_sprintf(p), &len);
+
+ for (lin = 1; *m; m++) {
+ /* Test \r for MacOS 8 */
+ if (*m == '\n' || *m == '\r')
+ lin++;
+ }
+
+ if (lin * CHAR_LINES > GFX_HEIGHT)
+ lin = (GFX_HEIGHT / CHAR_LINES);
+
+ w = (len + 2) * CHAR_COLS;
+ h = (lin + 2) * CHAR_LINES;
+
+ if (xoff < 0)
+ xoff = (GFX_WIDTH - w - CHAR_COLS) / 2;
+ else
+ xoff -= CHAR_COLS;
+
+ if (yoff < 0)
+ yoff = (GFX_HEIGHT - 3 * CHAR_LINES - h) / 2;
+
+ draw_window(xoff, yoff, xoff + w - 1, yoff + h - 1);
+
+ print_text2(2, msg, 0, CHAR_COLS + xoff, CHAR_LINES + yoff,
+ len + 1, MSG_BOX_TEXT, MSG_BOX_COLOUR);
+
+ free(msg);
+
+ do_update();
+}
+
+static void erase_textbox() {
+ if (!game.window.active) {
+ debugC(3, kDebugLevelText, "no window active");
+ return;
+ }
+
+ debugC(4, kDebugLevelText, "x1=%d, y1=%d, x2=%d, y2=%d", game.window.x1,
+ game.window.y1, game.window.x2, game.window.y2);
+
+ restore_block(game.window.x1, game.window.y1,
+ game.window.x2, game.window.y2, game.window.buffer);
+
+ free(game.window.buffer);
+ game.window.active = false;
+
+ do_update();
+}
+
+/*
+ * Public functions
+ */
+
+/**
+ * Print text in the AGI engine screen.
+ */
+void print_text(char *msg, int f, int x, int y, int len, int fg, int bg) {
+ f *= CHAR_COLS;
+ x *= CHAR_COLS;
+ y *= CHAR_LINES;
+
+ debugC(4, kDebugLevelText, "%s, %d, %d, %d, %d, %d, %d", msg, f, x, y, len, fg, bg);
+ print_text2(0, agi_sprintf(msg), f, x, y, len, fg, bg);
+}
+
+/**
+ * Print text in the AGI engine console.
+ */
+void print_text_console(char *msg, int x, int y, int len, int fg, int bg) {
+ x *= CHAR_COLS;
+ y *= 10;
+
+ print_text2(1, msg, 0, x, y, len, fg, bg);
+}
+
+/**
+ * Wrap text line to the specified width.
+ * @param str String to wrap.
+ * @param len Length of line.
+ */
+char *word_wrap_string(char *str, int *len) {
+ /* If the message has a long word (longer than 31 character) then
+ * loop in line 239 (for (; *v != ' '; v--, c--);) can wrap
+ * around 0 and write large number in c. This causes returned
+ * length to be negative (!) and eventually crashes in calling
+ * code. The fix is simple -- remove unsigned in maxc, c, l
+ * declaration. --Vasyl
+ */
+ char *msg, *v, *e;
+ int maxc, c, l = *len;
+
+ v = msg = strdup(str);
+ e = msg + strlen(msg);
+ maxc = 0;
+
+ while (42) {
+ debugC(3, kDebugLevelText, "[%s], %d", msg, maxc);
+ if (strchr(v, ' ') == NULL && (int)strlen(v) > l) {
+ debugC(1, kDebugLevelText | kDebugLevelMain, "Word too long in message");
+ l = strlen(v);
+ }
+ /* Must include \r for MacOS 8 */
+ while ((c = strcspn(v, "\n\r")) <= l) {
+ debugC(3, kDebugLevelText, "c = %d, maxc = %d", c, maxc);
+ if (c > maxc)
+ maxc = c;
+ if ((v += c + 1) >= e)
+ goto end;
+ }
+ c = l;
+ if ((v += l) >= e)
+ break;
+
+ /* The same line that caused that bug I mentioned
+ * should also do another check:
+ * for (; *v != ' ' && *v != '\n'; v--, c--);
+ * While this does not matter in most cases, in the case of
+ * long words it caused extra \n inserted in the line
+ * preceding long word. This one is definitely non-critical;
+ * one might argue that the function is not supposed to deal
+ * with long words. BTW, that condition at the beginning of
+ * the while loop that checks word length does not make much
+ * sense -- it verifies the length of the first word but for
+ * the rest it does something odd. Overall, even with these
+ * changes the function is still not completely robust.
+ * --Vasyl
+ */
+ if (*v != ' ')
+ for (; *v != ' ' && *v != '\n' && *v != '\r';
+ v--, c--);
+ if (c > maxc)
+ maxc = c;
+ *v++ = '\n';
+ }
+ end:
+ *len = maxc;
+ return msg;
+}
+
+/**
+ * Remove existing window, if any.
+ */
+void close_window() {
+ debugC(4, kDebugLevelText, "close window");
+ erase_both();
+ erase_textbox(); /* remove window, if any */
+ blit_both();
+ commit_both(); /* redraw sprites */
+ game.has_window = false;
+}
+
+/**
+ * Display a message box.
+ * This function displays the specified message in a text box
+ * centered in the screen and waits until a key is pressed.
+ * @param p The text to be displayed
+ */
+int message_box(char *s) {
+ int k;
+
+ erase_both();
+ blit_textbox(s, -1, -1, -1);
+ blit_both();
+ k = wait_key();
+ debugC(4, kDebugLevelText, "wait_key returned %02x", k);
+ close_window();
+
+ return k;
+}
+
+/**
+ * Display a message box with buttons.
+ * This function displays the specified message in a text box
+ * centered in the screen and waits until a button is pressed.
+ * @param p The text to be displayed
+ * @param b NULL-terminated list of button labels
+ */
+int selection_box(char *m, char **b) {
+ int x, y, i, s;
+ int key, active = 0;
+ int rc = -1;
+ int bx[5], by[5];
+
+ erase_both();
+ blit_textbox(m, -1, -1, -1);
+
+ x = game.window.x1 + 5 * CHAR_COLS / 2;
+ y = game.window.y2 - 5 * CHAR_LINES / 2;
+ s = game.window.x2 - game.window.x1 + 1 - 5 * CHAR_COLS;
+ debugC(3, kDebugLevelText, "s = %d", s);
+
+ /* Automatically position buttons */
+ for (i = 0; b[i]; i++) {
+ s -= CHAR_COLS * strlen(b[i]);
+ }
+
+ if (i > 1) {
+ debugC(3, kDebugLevelText, "s / %d = %d", i - 1, s / (i - 1));
+ s /= (i - 1);
+ } else {
+ x += s / 2;
+ }
+
+ for (i = 0; b[i]; i++) {
+ bx[i] = x;
+ by[i] = y;
+ x += CHAR_COLS * strlen(b[i]) + s;
+ }
+
+ blit_both();
+
+ /* clear key queue */
+ while (keypress()) {
+ get_key();
+ }
+
+ debugC(4, kDebugLevelText, "waiting...");
+ while (42) {
+ for (i = 0; b[i]; i++)
+ draw_button(bx[i], by[i], b[i], i == active, 0);
+
+ poll_timer(); /* msdos driver -> does nothing */
+ key = do_poll_keyboard();
+ if (!console_keyhandler(key)) {
+ switch (key) {
+ case KEY_ENTER:
+ rc = active;
+ goto press;
+ case KEY_ESCAPE:
+ rc = -1;
+ goto getout;
+#ifdef USE_MOUSE
+ case BUTTON_LEFT:
+ for (i = 0; b[i]; i++) {
+ if (test_button(bx[i], by[i], b[i])) {
+ rc = active = i;
+ goto press;
+ }
+ }
+ break;
+#endif
+ case 0x09: /* Tab */
+ debugC(3, kDebugLevelText, "Focus change");
+ active++;
+ active %= i;
+ break;
+ }
+ }
+ console_cycle();
+ }
+
+ press:
+ debugC(4, kDebugLevelText, "Button pressed: %d", rc);
+
+ getout:
+ close_window();
+ debugC(2, kDebugLevelText, "Result = %d", rc);
+
+ return rc;
+}
+
+/**
+ *
+ */
+int print(char *p, int lin, int col, int len) {
+ if (p == NULL)
+ return 0;
+
+ debugC(4, kDebugLevelText, "lin = %d, col = %d, len = %d", lin, col, len);
+
+ if (col == 0 && lin == 0 && len == 0)
+ lin = col = -1;
+
+ if (len == 0)
+ len = 30;
+
+ blit_textbox(p, lin, col, len);
+
+ if (getflag(F_output_mode)) {
+ /* non-blocking window */
+ setflag(F_output_mode, false);
+ return 1;
+ }
+
+ /* blocking */
+
+ if (game.vars[V_window_reset] == 0) {
+ int k;
+ setvar(V_key, 0);
+ k = wait_key();
+ close_window();
+ return k;
+ }
+
+ /* timed window */
+
+ debugC(3, kDebugLevelText, "f15==0, v21==%d => timed", getvar(21));
+ game.msg_box_ticks = getvar(V_window_reset) * 10;
+ setvar(V_key, 0);
+
+ do {
+ main_cycle();
+ if (game.keypress == KEY_ENTER) {
+ debugC(4, kDebugLevelText, "KEY_ENTER");
+ setvar(V_window_reset, 0);
+ game.keypress = 0;
+ break;
+ }
+ } while (game.msg_box_ticks > 0);
+
+ setvar(V_window_reset, 0);
+
+ close_window();
+
+ return 0;
+}
+
+/**
+ *
+ */
+static void print_status(char *message, ...) {
+ char x[42];
+ va_list args;
+
+ va_start(args, message);
+
+#ifdef HAVE_VSNPRINTF
+ vsnprintf(x, 41, message, args);
+#else
+ vsprintf(x, message, args);
+#endif
+
+ va_end(args);
+
+ debugC(4, kDebugLevelText, "fg=%d, bg=%d", STATUS_FG, STATUS_BG);
+ print_text(x, 0, 0, game.line_status, 40, STATUS_FG, STATUS_BG);
+}
+
+static char *safe_strcat(char *s, const char *t) {
+ if (t != NULL)
+ strcat(s, t);
+
+ return s;
+}
+
+/**
+ * Formats AGI string.
+ * This function turns a AGI string into a real string expanding values
+ * according to the AGI format specifiers.
+ * @param s string containing the format specifier
+ * @param n logic number
+ */
+#define MAX_LEN 768
+char *agi_sprintf(char *s) {
+ static char y[MAX_LEN];
+ char x[MAX_LEN];
+ char z[16], *p;
+
+ debugC(3, kDebugLevelText, "logic %d, '%s'", game.lognum, s);
+ p = x;
+
+ for (*p = 0; *s;) {
+ switch (*s) {
+ case '\\':
+ s++;
+ goto literal;
+ case '%':
+ s++;
+ switch (*s++) {
+ int i;
+ case 'v':
+ i = strtoul(s, NULL, 10);
+ while (*s >= '0' && *s <= '9')
+ s++;
+ sprintf(z, "%015i", getvar(i));
+
+ i = 99;
+ if (*s == '|') {
+ s++;
+ i = strtoul(s, NULL, 10);
+ while (*s >= '0' && *s <= '9')
+ s++;
+ }
+
+ if (i == 99) {
+ /* remove all leading 0 */
+ /* don't remove the 3rd zero if 000 */
+ for (i = 0;
+ z[i] == '0' && i < 14; i++);
+ } else {
+ i = 15 - i;
+ }
+ safe_strcat(p, z + i);
+ break;
+ case '0':
+ i = strtoul(s, NULL, 10) - 1;
+ safe_strcat(p, object_name(i));
+ break;
+ case 'g':
+ i = strtoul(s, NULL, 10) - 1;
+ safe_strcat(p, game.logics[0].texts[i]);
+ break;
+ case 'w':
+ i = strtoul(s, NULL, 10) - 1;
+ safe_strcat(p, game.ego_words[i].word);
+ break;
+ case 's':
+ i = strtoul(s, NULL, 10);
+ safe_strcat(p, game.strings[i]);
+ break;
+ case 'm':
+ i = strtoul(s, NULL, 10) - 1;
+ if (game.logics[game.lognum].num_texts > i)
+ safe_strcat(p, agi_sprintf(game. logics[game.lognum].texts[i]));
+ break;
+ }
+
+ while (*s >= '0' && *s <= '9')
+ s++;
+ while (*p)
+ p++;
+ break;
+
+ default:
+ literal:
+ assert(p < x + MAX_LEN);
+ *p++ = *s++;
+ *p = 0;
+ break;
+ }
+ }
+
+ strcpy(y, x);
+ return y;
+}
+
+/**
+ * Write the status line.
+ */
+void write_status() {
+ char x[64];
+
+#ifdef USE_CONSOLE
+ if (debug_.statusline) {
+#ifdef USE_MOUSE
+ print_status("%3d(%03d) %3d,%3d(%3d,%3d) ",
+ getvar(0), getvar(1), game.view_table[0].x_pos,
+ game.view_table[0].y_pos, WIN_TO_PIC_X(mouse.x),
+ WIN_TO_PIC_Y(mouse.y));
+#else
+ print_status("%3d(%03d) %3d,%3d ",
+ getvar(0), getvar(1), game.view_table[0].x_pos,
+ game.view_table[0].y_pos);
+#endif
+ return;
+ }
+#endif /* USE_CONSOLE */
+
+ if (!game.status_line) {
+ int l = game.line_status;
+ clear_lines(l, l, 0);
+ flush_lines(l, l);
+ return;
+ }
+
+ sprintf(x, " Score:%i of %-3i", game.vars[V_score], game.vars[V_max_score]);
+ print_status("%-17s Sound:%s ", x, getflag(F_sound_on) ? "on " : "off");
+}
+
+/**
+ * Print user input prompt.
+ */
+void write_prompt() {
+ int l, fg, bg, pos;
+
+ if (!game.input_enabled || game.input_mode != INPUT_NORMAL)
+ return;
+
+ l = game.line_user_input;
+ fg = game.color_fg;
+ bg = game.color_bg;
+ pos = game.cursor_pos;
+
+ debugC(4, kDebugLevelText, "erase line %d", l);
+ clear_lines(l, l, game.color_bg);
+
+ debugC(4, kDebugLevelText, "prompt = '%s'", agi_sprintf(game.strings[0]));
+ print_text(game.strings[0], 0, 0, l, 1, fg, bg);
+ print_text((char *)game.input_buffer, 0, 1, l, pos + 1, fg, bg);
+ print_character(pos + 1, l, game.cursor_char, fg, bg);
+
+ flush_lines(l, l);
+ do_update();
+}
+
+/**
+ * Clear text lines in the screen.
+ * @param l1 start line
+ * @param l2 end line
+ * @param c color
+ */
+void clear_lines(int l1, int l2, int c) {
+ /* do we need to adjust for +8 on topline?
+ * inc for endline so it matches the correct num
+ * ie, from 22 to 24 is 3 lines, not 2 lines.
+ */
+
+ l1 *= CHAR_LINES;
+ l2 *= CHAR_LINES;
+ l2 += CHAR_LINES - 1;
+
+ draw_rectangle(0, l1, GFX_WIDTH - 1, l2, c);
+}
+
+/**
+ *
+ */
+void flush_lines(int l1, int l2) {
+ l1 *= CHAR_LINES;
+ l2 *= CHAR_LINES;
+ l2 += CHAR_LINES - 1;
+
+ flush_block(0, l1, GFX_WIDTH - 1, l2);
+}
+
+/**
+ *
+ */
+void draw_window(int x1, int y1, int x2, int y2) {
+ game.window.active = true;
+ game.window.x1 = x1;
+ game.window.y1 = y1;
+ game.window.x2 = x2;
+ game.window.y2 = y2;
+ game.window.buffer = (uint8 *) malloc((x2 - x1 + 1) * (y2 - y1 + 1));
+
+ debugC(4, kDebugLevelText, "x1=%d, y1=%d, x2=%d, y2=%d", x1, y1, x2, y2);
+ save_block(x1, y1, x2, y2, game.window.buffer);
+ draw_box(x1, y1, x2, y2, MSG_BOX_COLOUR, MSG_BOX_LINE, 2);
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/text.h b/engines/agi/text.h
new file mode 100644
index 0000000000..4ce37bc7fb
--- /dev/null
+++ b/engines/agi/text.h
@@ -0,0 +1,48 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_TEXT_H
+#define __AGI_TEXT_H
+
+#include "agi/agi.h"
+
+namespace Agi {
+
+int message_box(char *);
+int selection_box(char *, char **);
+void close_window(void);
+void draw_window(int, int, int, int);
+void print_text(char *, int, int, int, int, int, int);
+void print_text_console(char *, int, int, int, int, int);
+int print(char *, int, int, int);
+char *word_wrap_string(char *, int *);
+char *agi_sprintf(char *);
+void write_status(void);
+void write_prompt(void);
+void clear_lines(int, int, int);
+void flush_lines(int, int);
+
+} // End of namespace Agi
+
+#endif /* __AGI_TEXT_H */
diff --git a/engines/agi/view.cpp b/engines/agi/view.cpp
new file mode 100644
index 0000000000..b8fc559b84
--- /dev/null
+++ b/engines/agi/view.cpp
@@ -0,0 +1,396 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2003 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "agi/agi.h"
+#include "agi/sprite.h"
+
+namespace Agi {
+
+static void _set_cel(struct vt_entry *v, int n) {
+ struct view_loop *current_vl;
+ struct view_cel *current_vc;
+
+ v->current_cel = n;
+
+ current_vl = &game.views[v->current_view].loop[v->current_loop];
+
+ /* Added by Amit Vainsencher <amitv@subdimension.com> to prevent
+ * crash in KQ1 -- not in the Sierra interpreter
+ */
+ if (current_vl->num_cels == 0)
+ return;
+
+ if (!(v->flags & UPDATE)
+ && (agi_get_release() >= 0x3000))
+ return;
+
+ current_vc = &current_vl->cel[n];
+ v->cel_data = current_vc;
+ v->x_size = current_vc->width;
+ v->y_size = current_vc->height;
+}
+
+static void _set_loop(struct vt_entry *v, int n) {
+ struct view_loop *current_vl;
+ debugC(7, kDebugLevelResources, "vt entry #%d, loop = %d", v->entry, n);
+
+ /* Added to avoid crash when leaving the arcade machine in MH1
+ * -- not in AGI 2.917
+ */
+ if (n >= v->num_loops)
+ n = 0;
+
+ v->current_loop = n;
+ current_vl = &game.views[v->current_view].loop[v->current_loop];
+
+ v->num_cels = current_vl->num_cels;
+ if (v->current_cel >= v->num_cels)
+ v->current_cel = 0;
+
+ if (!(v->flags & UPDATE) && (agi_get_release() >= 0x3000))
+ return;
+
+ v->loop_data = &game.views[v->current_view].loop[n];
+}
+
+static void update_view(struct vt_entry *v) {
+ int cel, last_cel;
+
+ if (v->flags & DONTUPDATE) {
+ v->flags &= ~DONTUPDATE;
+ return;
+ }
+
+ cel = v->current_cel;
+ last_cel = v->num_cels - 1;
+
+ switch (v->cycle) {
+ case CYCLE_NORMAL:
+ if (++cel > last_cel)
+ cel = 0;
+ break;
+ case CYCLE_END_OF_LOOP:
+ if (cel < last_cel) {
+ debugC(5, kDebugLevelResources, "cel %d (last = %d)", cel + 1, last_cel);
+ if (++cel != last_cel)
+ break;
+ }
+ setflag(v->parm1, true);
+ v->flags &= ~CYCLING;
+ v->direction = 0;
+ v->cycle = CYCLE_NORMAL;
+ break;
+ case CYCLE_REV_LOOP:
+ if (cel) {
+ if (--cel)
+ break;
+ }
+ setflag(v->parm1, true);
+ v->flags &= ~CYCLING;
+ v->direction = 0;
+ v->cycle = CYCLE_NORMAL;
+ break;
+ case CYCLE_REVERSE:
+ if (cel == 0) {
+ cel = last_cel;
+ } else {
+ cel--;
+ }
+ break;
+ }
+
+ set_cel(v, cel);
+}
+
+/*
+ * Public functions
+ */
+
+/**
+ * Decode an AGI view resource.
+ * This function decodes the raw data of the specified AGI view resource
+ * and fills the corresponding views array element.
+ * @param n number of view resource to decode
+ */
+int decode_view(int n) {
+ int loop, cel;
+ uint8 *v, *lptr;
+ uint16 lofs, cofs;
+ struct view_loop *vl;
+ struct view_cel *vc;
+
+ debugC(5, kDebugLevelResources, "decode_view(%d)", n);
+ v = game.views[n].rdata;
+
+ assert(v != NULL);
+
+ game.views[n].descr = READ_LE_UINT16(v + 3) ? (char *)(v + READ_LE_UINT16(v + 3)) : (char *)(v + 3);
+
+ /* if no loops exist, return! */
+ if ((game.views[n].num_loops = *(v + 2)) == 0)
+ return err_NoLoopsInView;
+
+ /* allocate memory for all views */
+ game.views[n].loop = (view_loop *)
+ calloc(game.views[n].num_loops, sizeof(struct view_loop));
+
+ if (game.views[n].loop == NULL)
+ return err_NotEnoughMemory;
+
+ /* decode all of the loops in this view */
+ lptr = v + 5; /* first loop address */
+
+ for (loop = 0; loop < game.views[n].num_loops; loop++, lptr += 2) {
+ lofs = READ_LE_UINT16(lptr); /* loop header offset */
+ vl = &game.views[n].loop[loop]; /* the loop struct */
+
+ vl->num_cels = *(v + lofs);
+ debugC(6, kDebugLevelResources, "view %d, num_cels = %d", n, vl->num_cels);
+ vl->cel = (view_cel *) calloc(vl->num_cels, sizeof(struct view_cel));
+ if (vl->cel == NULL) {
+ free(game.views[n].loop);
+ game.views[n].num_loops = 0;
+ return err_NotEnoughMemory;
+ }
+
+ /* decode the cells */
+ for (cel = 0; cel < vl->num_cels; cel++) {
+ cofs = lofs + READ_LE_UINT16(v + lofs + 1 + (cel * 2));
+ vc = &vl->cel[cel];
+
+ vc->width = *(v + cofs);
+ vc->height = *(v + cofs + 1);
+ vc->transparency = *(v + cofs + 2) & 0xf;
+ vc->mirror_loop = (*(v + cofs + 2) >> 4) & 0x7;
+ vc->mirror = (*(v + cofs + 2) >> 7) & 0x1;
+
+ /* skip over width/height/trans|mirror data */
+ cofs += 3;
+
+ vc->data = v + cofs;
+ /* If mirror_loop is pointing to the current loop,
+ * then this is the original.
+ */
+ if (vc->mirror_loop == loop)
+ vc->mirror = 0;
+ } /* cel */
+ } /* loop */
+
+ return err_OK;
+}
+
+/**
+ * Unloads all data in a view resource
+ * @param n number of view resource
+ */
+void unload_view(int n) {
+ int x;
+
+ debugC(5, kDebugLevelResources, "discard view %d", n);
+ if (~game.dir_view[n].flags & RES_LOADED)
+ return;
+
+ /* Rebuild sprite list, see bug #779302 */
+ erase_both();
+ blit_both();
+ commit_both();
+
+ /* free all the loops */
+ for (x = 0; x < game.views[n].num_loops; x++)
+ free(game.views[n].loop[x].cel);
+
+ free(game.views[n].loop);
+ free(game.views[n].rdata);
+
+ game.dir_view[n].flags &= ~RES_LOADED;
+}
+
+/**
+ * Set a view table entry to use the specified cel of the current loop.
+ * @param v pointer to view table entry
+ * @param n number of cel
+ */
+void set_cel(struct vt_entry *v, int n) {
+ assert(v->view_data != NULL);
+ assert(v->num_cels >= n);
+
+ _set_cel(v, n);
+
+ /* If position isn't appropriate, update it accordingly */
+ if (v->x_pos + v->x_size > _WIDTH) {
+ v->flags |= UPDATE_POS;
+ v->x_pos = _WIDTH - v->x_size;
+ }
+ if (v->y_pos - v->y_size + 1 < 0) {
+ v->flags |= UPDATE_POS;
+ v->y_pos = v->y_size - 1;
+ }
+ if (v->y_pos <= game.horizon && (~v->flags & IGNORE_HORIZON)) {
+ v->flags |= UPDATE_POS;
+ v->y_pos = game.horizon + 1;
+ }
+}
+
+/**
+ * Set a view table entry to use the specified loop of the current view.
+ * @param v pointer to view table entry
+ * @param n number of loop
+ */
+void set_loop(struct vt_entry *v, int n) {
+ assert(v->view_data != NULL);
+ assert(v->num_loops >= n);
+ _set_loop(v, n);
+ set_cel(v, v->current_cel);
+}
+
+/**
+ * Set a view table entry to use the specified view resource.
+ * @param v pointer to view table entry
+ * @param n number of AGI view resource
+ */
+void set_view(struct vt_entry *v, int n) {
+ v->view_data = &game.views[n];
+ v->current_view = n;
+ v->num_loops = v->view_data->num_loops;
+ set_loop(v, v->current_loop >= v->num_loops ? 0 : v->current_loop);
+}
+
+/**
+ * Set the view table entry as updating.
+ * @param v pointer to view table entry
+ */
+void start_update(struct vt_entry *v) {
+ if (~v->flags & UPDATE) {
+ erase_both();
+ v->flags |= UPDATE;
+ blit_both();
+ }
+}
+
+/**
+ * Set the view table entry as non-updating.
+ * @param v pointer to view table entry
+ */
+void stop_update(struct vt_entry *v) {
+ if (v->flags & UPDATE) {
+ erase_both();
+ v->flags &= ~UPDATE;
+ blit_both();
+ }
+}
+
+/* loops to use according to direction and number of loops in
+ * the view resource
+ */
+static int loop_table_2[] = {
+ 0x04, 0x04, 0x00, 0x00, 0x00, 0x04, 0x01, 0x01, 0x01
+};
+
+static int loop_table_4[] = {
+ 0x04, 0x03, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x01
+};
+
+/**
+ * Update view table entries.
+ * This function is called at the end of each interpreter cycle
+ * to update the view table entries and blit the sprites.
+ */
+void update_viewtable() {
+ struct vt_entry *v;
+ int i, loop;
+
+ i = 0;
+ for (v = game.view_table; v < &game.view_table[MAX_VIEWTABLE]; v++) {
+ if ((v->flags & (ANIMATED | UPDATE | DRAWN)) != (ANIMATED | UPDATE | DRAWN)) {
+ continue;
+ }
+
+ i++;
+
+ loop = 4;
+ if (~v->flags & FIX_LOOP) {
+ switch (v->num_loops) {
+ case 2:
+ case 3:
+ loop = loop_table_2[v->direction];
+ break;
+ case 4:
+ loop = loop_table_4[v->direction];
+ break;
+ default:
+ /* for KQ4 */
+ if (agi_get_release() == 0x3086)
+ loop = loop_table_4[v->direction];
+ break;
+ }
+ }
+
+ /* AGI 2.272 (ddp, xmas) doesn't test step_time_count! */
+ if (loop != 4 && loop != v->current_loop) {
+ if (agi_get_release() <= 0x2272 ||
+ v->step_time_count == 1) {
+ set_loop(v, loop);
+ }
+ }
+
+ if (~v->flags & CYCLING)
+ continue;
+
+ if (v->cycle_time_count == 0)
+ continue;
+
+ if (--v->cycle_time_count == 0) {
+ update_view(v);
+ v->cycle_time_count = v->cycle_time;
+ }
+ }
+
+ if (i) {
+#ifdef USE_CONSOLE
+ /* To correctly update sprites when we use the console
+ * we must work with all sprites.
+ */
+ if (console.y > 0) {
+ erase_both();
+ update_position();
+ blit_both();
+ commit_both();
+ } else
+#endif
+ /* If we're not using the console, updating only
+ * the active sprites lets us save some CPU cycles.
+ * This is how the original Sierra AGI works.
+ */
+ {
+ erase_upd_sprites();
+ update_position();
+ blit_upd_sprites();
+ commit_upd_sprites();
+ }
+
+ game.view_table[0].flags &= ~(ON_WATER | ON_LAND);
+ }
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/view.h b/engines/agi/view.h
new file mode 100644
index 0000000000..3394c8609a
--- /dev/null
+++ b/engines/agi/view.h
@@ -0,0 +1,142 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2001 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __AGI_VIEW_H
+#define __AGI_VIEW_H
+
+namespace Agi {
+
+struct view_cel {
+ uint8 height;
+ uint8 width;
+ uint8 transparency;
+ uint8 mirror_loop;
+ uint8 mirror;
+ uint8 *data;
+};
+
+struct view_loop {
+ int num_cels;
+ struct view_cel *cel;
+};
+
+/**
+ * AGI view resource structure.
+ */
+struct agi_view {
+ int num_loops;
+ struct view_loop *loop;
+ char *descr;
+ uint8 *rdata;
+};
+
+/**
+ * AGI view table entry
+ */
+struct vt_entry {
+ uint8 step_time;
+ uint8 step_time_count;
+ uint8 entry;
+ int16 x_pos;
+ int16 y_pos;
+ uint8 current_view;
+ struct agi_view *view_data;
+ uint8 current_loop;
+ uint8 num_loops;
+ struct view_loop *loop_data;
+ uint8 current_cel;
+ uint8 num_cels;
+ struct view_cel *cel_data;
+ struct view_cel *cel_data_2;
+ int16 x_pos2;
+ int16 y_pos2;
+ void *s;
+ int16 x_size;
+ int16 y_size;
+ uint8 step_size;
+ uint8 cycle_time;
+ uint8 cycle_time_count;
+ uint8 direction;
+
+#define MOTION_NORMAL 0
+#define MOTION_WANDER 1
+#define MOTION_FOLLOW_EGO 2
+#define MOTION_MOVE_OBJ 3
+ uint8 motion;
+
+#define CYCLE_NORMAL 0
+#define CYCLE_END_OF_LOOP 1
+#define CYCLE_REV_LOOP 2
+#define CYCLE_REVERSE 3
+ uint8 cycle;
+
+ uint8 priority;
+
+#define DRAWN 0x0001
+#define IGNORE_BLOCKS 0x0002
+#define FIXED_PRIORITY 0x0004
+#define IGNORE_HORIZON 0x0008
+#define UPDATE 0x0010
+#define CYCLING 0x0020
+#define ANIMATED 0x0040
+#define MOTION 0x0080
+#define ON_WATER 0x0100
+#define IGNORE_OBJECTS 0x0200
+#define UPDATE_POS 0x0400
+#define ON_LAND 0x0800
+#define DONTUPDATE 0x1000
+#define FIX_LOOP 0x2000
+#define DIDNT_MOVE 0x4000
+#define ADJ_EGO_XY 0x8000
+ uint16 flags;
+
+ uint8 parm1;
+ uint8 parm2;
+ uint8 parm3;
+ uint8 parm4;
+}; /* struct vt_entry */
+
+/* Motion */
+void check_all_motions(void);
+void move_obj(struct vt_entry *);
+void in_destination(struct vt_entry *);
+void fix_position(int);
+void update_position(void);
+
+/* View table management */
+void set_cel(struct vt_entry *, int);
+void set_loop(struct vt_entry *, int);
+void set_view(struct vt_entry *, int);
+void start_update(struct vt_entry *);
+void stop_update(struct vt_entry *);
+void update_viewtable(void);
+
+void unload_view(int);
+int decode_view(int);
+void add_to_pic(int, int, int, int, int, int, int);
+void draw_obj(int);
+
+} // End of namespace Agi
+
+#endif /* __AGI_VIEW_H */
diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp
new file mode 100644
index 0000000000..77cdd97667
--- /dev/null
+++ b/engines/agi/words.cpp
@@ -0,0 +1,213 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * Copyright (C) 1999-2002 Sarien Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * New find_word algorithm by Thomas Akesson <tapilot@home.se>
+ */
+
+#include "agi/agi.h"
+#include "agi/keyboard.h" /* for clean_input() */
+
+namespace Agi {
+
+static uint8 *words; /* words in the game */
+static uint32 words_flen; /* length of word memory */
+
+/*
+ * Local implementation to avoid problems with strndup() used by
+ * gcc 3.2 Cygwin (see #635984)
+ */
+static char *my_strndup(char *src, int n) {
+ char *tmp = strncpy((char *)malloc(n + 1), src, n);
+ tmp[n] = 0;
+ return tmp;
+}
+
+int load_words(char *fname) {
+ Common::File fp;
+ uint32 flen;
+ uint8 *mem = NULL;
+ char *path = NULL;
+
+ words = NULL;
+
+ path = fname;
+
+ if (!fp.open(path)) {
+ report("Warning: can't open %s\n", path);
+ return err_OK /*err_BadFileOpen */ ;
+ }
+ report("Loading dictionary: %s\n", path);
+
+ fp.seek(0, SEEK_END);
+ flen = fp.pos();
+ words_flen = flen;
+ fp.seek(0, SEEK_SET);
+
+ if ((mem = (uint8 *)calloc(1, flen + 32)) == NULL) {
+ fp.close();
+ return err_NotEnoughMemory;
+ }
+
+ fp.read(mem, flen);
+ fp.close();
+
+ words = mem;
+
+ return err_OK;
+}
+
+void unload_words() {
+ if (words != NULL) {
+ free(words);
+ words = NULL;
+ }
+}
+
+/**
+ * Find a word in the dictionary
+ * Uses an algorithm hopefully like the one Sierra used. Returns the ID
+ * of the word and the length in flen. Returns -1 if not found.
+ *
+ * Thomas Åkesson, November 2001
+ */
+int find_word(char *word, int *flen) {
+ int mchr = 0; /* matched chars */
+ int len, fchr, id = -1;
+ uint8 *p = words;
+ uint8 *q = words + words_flen;
+ *flen = 0;
+
+ debugC(2, kDebugLevelScripts, "find_word(%s)", word);
+ if (word[0] >= 'a' && word[0] <= 'z')
+ fchr = word[0] - 'a';
+ else
+ return -1;
+
+ len = strlen(word);
+
+ /* Get the offset to the first word beginning with the
+ * right character
+ */
+ p += READ_BE_UINT16(p + 2 * fchr);
+
+ while (p[0] >= mchr) {
+ if (p[0] == mchr) {
+ p++;
+ /* Loop through all matching characters */
+ while ((p[0] ^ word[mchr]) == 0x7F && mchr < len) {
+ mchr++;
+ p++;
+ }
+ /* Check if this is the last character of the word
+ * and if it matches
+ */
+ if ((p[0] ^ word[mchr]) == 0xFF && mchr < len) {
+ mchr++;
+ if (word[mchr] == 0 || word[mchr] == 0x20) {
+ id = READ_BE_UINT16(p + 1);
+ *flen = mchr;
+ }
+ }
+ }
+ if (p >= q)
+ return -1;
+
+ /* Step to the next word */
+ while (p[0] < 0x80)
+ p++;
+ p += 3;
+ }
+
+ return id;
+}
+
+void dictionary_words(char *msg) {
+ char *p = NULL;
+ char *q = NULL;
+ int wid, wlen;
+
+ debugC(2, kDebugLevelScripts, "msg = \"%s\"", msg);
+
+ clean_input();
+
+ for (p = msg; p && *p && getvar(V_word_not_found) == 0;) {
+ if (*p == 0x20)
+ p++;
+
+ if (*p == 0)
+ break;
+
+ wid = find_word(p, &wlen);
+ debugC(2, kDebugLevelScripts, "find_word(p) == %d", wid);
+
+ switch (wid) {
+ case -1:
+ debugC(2, kDebugLevelScripts, "unknown word");
+ game.ego_words[game.num_ego_words].word = strdup(p);
+ q = game.ego_words[game.num_ego_words].word;
+ game.ego_words[game.num_ego_words].id = 19999;
+ setvar(V_word_not_found, 1 + game.num_ego_words);
+ game.num_ego_words++;
+ p += strlen(p);
+ break;
+ case 0:
+ /* ignore this word */
+ debugC(2, kDebugLevelScripts, "ignore word");
+ p += wlen;
+ q = NULL;
+ break;
+ default:
+ /* an OK word */
+ debugC(3, kDebugLevelScripts, "ok word (%d)", wid);
+ game.ego_words[game.num_ego_words].id = wid;
+ game.ego_words[game.num_ego_words].word = my_strndup(p, wlen);
+ game.num_ego_words++;
+ p += wlen;
+ break;
+ }
+
+ if (p != NULL && *p) {
+ debugC(2, kDebugLevelScripts, "p = %s", p);
+ *p = 0;
+ p++;
+ }
+
+ if (q != NULL) {
+ for (; (*q != 0 && *q != 0x20); q++);
+ if (*q) {
+ *q = 0;
+ q++;
+ }
+ }
+ }
+
+ debugC(4, kDebugLevelScripts, "num_ego_words = %d", game.num_ego_words);
+ if (game.num_ego_words > 0) {
+ setflag(F_entered_cli, true);
+ setflag(F_said_accepted_input, false);
+ }
+}
+
+} // End of namespace Agi