diff options
Diffstat (limited to 'engines/simon/simon.cpp')
-rw-r--r-- | engines/simon/simon.cpp | 4292 |
1 files changed, 4292 insertions, 0 deletions
diff --git a/engines/simon/simon.cpp b/engines/simon/simon.cpp new file mode 100644 index 0000000000..78d0eac221 --- /dev/null +++ b/engines/simon/simon.cpp @@ -0,0 +1,4292 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 The ScummVM project + * + * 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 "backends/fs/fs.h" + +#include "base/gameDetector.h" +#include "base/plugins.h" + +#include "common/config-manager.h" +#include "common/file.h" +#include "common/md5.h" +#include "common/system.h" + +#include "gui/about.h" +#include "gui/message.h" + +#include "simon/simon.h" +#include "simon/intern.h" +#include "simon/vga.h" +#include "simon/debugger.h" + +#include "sound/mididrv.h" +#ifdef _WIN32_WCE +extern bool isSmartphone(void); +#endif + +#ifdef PALMOS_68K +#include "globals.h" +#endif + +using Common::File; + +struct ObsoleteTargets { + const char *from; + const char *to; + Common::Platform platform; + + GameSettings toGameSettings() const { + GameSettings dummy = { from, "Obsolete Target", 0 }; + return dummy; + } +}; + +/** + * Conversion table mapping old obsolete target names to the + * corresponding new target and platform combination. + * + */ +static ObsoleteTargets obsoleteTargetsTable[] = { + {"simon1acorn", "simon1", Common::kPlatformAcorn}, + {"simon1amiga", "simon1", Common::kPlatformAmiga}, + {"simon1cd32", "simon1", Common::kPlatformAmiga}, + {"simon1dos", "simon1", Common::kPlatformPC}, + {"simon1talkie", "simon1", Common::kPlatformPC}, + {"simon1win", "simon1", Common::kPlatformWindows}, + {"simon2dos", "simon2", Common::kPlatformPC}, + {"simon2talkie", "simon2", Common::kPlatformPC}, + {"simon2mac", "simon2", Common::kPlatformMacintosh}, + {"simon2win", "simon2", Common::kPlatformWindows}, + {NULL, NULL, Common::kPlatformUnknown} +}; + +static const GameSettings simonGames[] = { + // Simon the Sorcerer 1 & 2 (not SCUMM games) + {"feeble", "The Feeble Files", 0}, + {"simon1", "Simon the Sorcerer 1", 0}, + {"simon2", "Simon the Sorcerer 2", 0}, + + {"simon1acorn", "Simon the Sorcerer 1 (Acorn)", 0}, + {"simon1amiga", "Simon the Sorcerer 1 (Amiga)", 0}, + {"simon1cd32", "Simon the Sorcerer 1 Talkie (Amiga CD32)", 0}, + {"simon1demo", "Simon the Sorcerer 1 (DOS Demo)", 0}, + {"simon1dos", "Simon the Sorcerer 1 (DOS)", 0}, + {"simon1talkie", "Simon the Sorcerer 1 Talkie", 0}, + {"simon1win", "Simon the Sorcerer 1 Talkie (Windows)", 0}, + {"simon2dos", "Simon the Sorcerer 2 (DOS)", 0}, + {"simon2talkie", "Simon the Sorcerer 2 Talkie", 0}, + {"simon2win", "Simon the Sorcerer 2 Talkie (Windows)", 0}, + {"simon2mac", "Simon the Sorcerer 2 Talkie (Amiga or Mac)", 0}, + + {NULL, NULL, 0} +}; + +GameList Engine_SIMON_gameList() { + GameList games; + const GameSettings *g = simonGames; + while (g->gameid) { + games.push_back(*g); + g++; + } + + return games; +} + +DetectedGameList Engine_SIMON_detectGames(const FSList &fslist) { + return Simon::GAME_ProbeGame(fslist); +} + +Engine *Engine_SIMON_create(GameDetector *detector, OSystem *syst) { + const ObsoleteTargets *o = obsoleteTargetsTable; + while (o->from) { + if (!scumm_stricmp(detector->_game.gameid, o->from)) { + detector->_game.gameid = o->to; + + ConfMan.set("gameid", o->to); + + if (o->platform != Common::kPlatformUnknown) + ConfMan.set("platform", Common::getPlatformCode(o->platform)); + + warning("Target upgraded from %s to %s", o->from, o->to); + ConfMan.flushToDisk(); + break; + } + o++; + } + + return new Simon::SimonEngine(detector, syst); +} + +REGISTER_PLUGIN(SIMON, "Simon the Sorcerer") + +namespace Simon { + +#ifdef PALMOS_68K +#define PTR(a) a +static const GameSpecificSettings *simon1_settings; +static const GameSpecificSettings *simon1acorn_settings; +static const GameSpecificSettings *simon1amiga_settings; +static const GameSpecificSettings *simon1demo_settings; +static const GameSpecificSettings *simon2win_settings; +static const GameSpecificSettings *simon2dos_settings; +static const GameSpecificSettings *feeblefiles_settings; +#else +#define PTR(a) &a +static const GameSpecificSettings simon1_settings = { + "SIMON.GME", // gme_filename + "SIMON.WAV", // wav_filename + "SIMON.VOC", // voc_filename + "SIMON.MP3", // mp3_filename + "SIMON.OGG", // vorbis_filename + "SIMON.FLA", // flac_filename + "EFFECTS.VOC", // voc_effects_filename + "EFFECTS.MP3", // mp3_effects_filename + "EFFECTS.OGG", // vorbis_effects_filename + "EFFECTS.FLA", // flac_effects_filename + "GAMEPC", // gamepc_filename +}; + +static const GameSpecificSettings simon1acorn_settings = { + "DATA", // gme_filename + "", // wav_filename + "SIMON", // voc_filename + "SIMON.MP3", // mp3_filename + "SIMON.OGG", // vorbis_filename + "SIMON.FLA", // flac_filename + "EFFECTS", // voc_effects_filename + "EFFECTS.MP3", // mp3_effects_filename + "EFFECTS.OGG", // vorbis_effects_filename + "EFFECTS.FLA", // flac_effects_filename + "GAMEBASE", // gamepc_filename +}; + +static const GameSpecificSettings simon1amiga_settings = { + "", // gme_filename + "", // wav_filename + "", // voc_filename + "SIMON.MP3", // mp3_filename + "SIMON.OGG", // vorbis_filename + "SIMON.FLA", // flac_filename + "", // voc_effects_filename + "", // mp3_effects_filename + "", // vorbis_effects_filename + "", // flac_effects_filename + "gameamiga", // gamepc_filename +}; + +static const GameSpecificSettings simon1demo_settings = { + "", // gme_filename + "", // wav_filename + "", // voc_filename + "", // mp3_filename + "", // vorbis_filename + "", // flac_filename + "", // voc_effects_filename + "", // mp3_effects_filename + "", // vorbis_effects_filename + "", // flac_effects_filename + "GDEMO", // gamepc_filename +}; + +static const GameSpecificSettings simon2win_settings = { + "SIMON2.GME", // gme_filename + "SIMON2.WAV", // wav_filename + "SIMON2.VOC", // voc_filename + "SIMON2.MP3", // mp3_filename + "SIMON2.OGG", // vorbis_filename + "SIMON2.FLA", // flac_filename + "", // voc_effects_filename + "", // mp3_effects_filename + "", // vorbis_effects_filename + "", // flac_effects_filename + "GSPTR30", // gamepc_filename +}; + +static const GameSpecificSettings simon2dos_settings = { + "SIMON2.GME", // gme_filename + "", // wav_filename + "", // voc_filename + "", // mp3_filename + "", // vorbis_filename + "", // flac_filename + "", // voc_effects_filename + "", // mp3_effects_filename + "", // vorbis_effects_filename + "", // flac_effects_filename + "GAME32", // gamepc_filename +}; + +static const GameSpecificSettings feeblefiles_settings = { + "", // gme_filename + "VOICES.WAV", // wav_filename + "VOICES.VOC", // voc_filename + "VOICES.MP3", // mp3_filename + "VOICES.OGG", // vorbis_filename + "VOICES.FLA", // flac_filename + "", // voc_effects_filename + "", // mp3_effects_filename + "", // vorbis_effects_filename + "", // flac_effects_filename + "GAME22", // gamepc_filename +}; +#endif + +SimonEngine::SimonEngine(GameDetector *detector, OSystem *syst) + : Engine(syst), midi(syst) { + _vcPtr = 0; + _vc_get_out_of_code = 0; + _gameOffsetsPtr = 0; + + _debugger = 0; + setupVgaOpcodes(); + + _keyPressed = 0; + + _gameFile = 0; + + _strippedTxtMem = 0; + _textSize = 0; + _stringTabNum = 0; + _stringTabPos = 0; + _stringtab_numalloc = 0; + _stringTabPtr = 0; + + _itemArrayPtr = 0; + _itemArraySize = 0; + _itemArrayInited = 0; + + _itemHeapPtr = 0; + _itemHeapCurPos = 0; + _itemHeapSize = 0; + + _iconFilePtr = 0; + + _tblList = 0; + + _codePtr = 0; + + _localStringtable = 0; + _stringIdLocalMin = 1; + _stringIdLocalMax = 0; + + _tablesHeapPtr = 0; + _tablesHeapPtrOrg = 0; + _tablesheapPtrNew = 0; + _tablesHeapSize = 0; + _tablesHeapCurPos = 0; + _tablesHeapCurPosOrg = 0; + _tablesHeapCurPosNew = 0; + + _subroutineList = 0; + _subroutineListOrg = 0; + _subroutine = 0; + + _dxSurfacePitch = 0; + + _recursionDepth = 0; + + _lastVgaTick = 0; + + _marks = 0; + + _scriptVar2 = 0; + _runScriptReturn1 = 0; + _skipVgaWait = 0; + _noParentNotify = 0; + _beardLoaded = 0; + _hitarea_unk_3 = 0; + _mortalFlag = 0; + _updateScreen = 0; + _usePaletteDelay = 0; + _syncFlag2 = 0; + _inCallBack = 0; + _cepeFlag = 0; + _copyPartialMode = 0; + _speed = 1; + _fastMode = 0; + _dxUse3Or4ForLock = 0; + + _debugMode = 0; + _pause = 0; + _startMainScript = 0; + _continousMainScript = 0; + _startVgaScript = 0; + _continousVgaScript = 0; + _drawImagesDebug = 0; + _dumpImages = 0; + _speech = true; + _subtitles = true; + _fade = true; + _mouseCursor = 0; + _vgaVar9 = 0; + _scriptUnk1 = 0; + _vgaVar6 = 0; + _scrollX = 0; + _scrollY = 0; + _scrollXMax = 0; + _scrollYMax = 0; + _scrollCount = 0; + _scrollFlag = 0; + _scrollHeight = 0; + _scrollWidth = 0; + _scrollImage = 0; + _vgaVar8 = 0; + + _scriptVerb = 0; + _scriptNoun1 = 0; + _scriptNoun2 = 0; + _scriptAdj1 = 0; + _scriptAdj2 = 0; + + _curWindow = 0; + _textWindow = 0; + + _subjectItem = 0; + _objectItem = 0; + _item1 = 0; + + _hitAreaObjectItem = 0; + _lastHitArea = 0; + _lastHitArea2Ptr = 0; + _lastHitArea3 = 0; + _leftButtonDown = 0; + _hitAreaSubjectItem = 0; + _hitAreaPtr5 = 0; + _hitAreaPtr7 = 0; + _needHitAreaRecalc = 0; + _verbHitArea = 0; + _hitAreaUnk4 = 0; + _mouseHideCount = 0; + + _windowNum = 0; + + _printCharCurPos = 0; + _printCharMaxPos = 0; + _numLettersToPrint = 0; + + _lastTime = 0; + + _firstTimeStruct = 0; + _pendingDeleteTimeEvent = 0; + + _base_time = 0; + + _mouseX = 0; + _mouseY = 0; + _mouseXOld = 0; + _mouseYOld = 0; + + _dummyItem1 = new Item(); + _dummyItem2 = new Item(); + _dummyItem3 = new Item(); + + _lockWord = 0; + _scrollUpHitArea = 0; + _scrollDownHitArea = 0; + + _videoVar7 = 0xFFFF; + _paletteColorCount = 0; + + _videoVar4 = 0; + _videoVar5 = 0; + _fastFadeOutFlag = 0; + _unkPalFlag = 0; + _exitCutscene = 0; + _skipSpeech = 0; + _paletteFlag = 0; + + _soundFileId = 0; + _lastMusicPlayed = -1; + _nextMusicToPlay = -1; + + _showPreposition = 0; + _showMessageFlag = 0; + + _videoNumPalColors = 0; + + _vgaSpriteChanged = 0; + + _vgaBufFreeStart = 0; + _vgaBufEnd = 0; + _vgaBufStart = 0; + _vgaFileBufOrg = 0; + _vgaFileBufOrg2 = 0; + + _curVgaFile1 = 0; + _curVgaFile2 = 0; + _curSfxFile = 0; + + _timer1 = 0; + _timer5 = 0; + _timer4 = 0; + + _frameRate = 1; + + _vgaCurFile2 = 0; + _vgaWaitFor = 0; + _vgaCurFileId = 0; + _vgaCurSpriteId = 0; + + _nextVgaTimerToProcess = 0; + + memset(_vcItemArray, 0, sizeof(_vcItemArray)); + memset(_itemArray6, 0, sizeof(_itemArray6)); + + memset(_stringIdArray2, 0, sizeof(_stringIdArray2)); + memset(_stringIdArray3, 0, sizeof(_stringIdArray3)); + memset(_speechIdArray4, 0, sizeof(_speechIdArray4)); + + memset(_bitArray, 0, sizeof(_bitArray)); + memset(_variableArray, 0, sizeof(_variableArray)); + + memset(_windowArray, 0, sizeof(_windowArray)); + + memset(_fcsData1, 0, sizeof(_fcsData1)); + memset(_fcsData2, 0, sizeof(_fcsData2)); + + _freeStringSlot = 0; + + memset(_stringReturnBuffer, 0, sizeof(_stringReturnBuffer)); + + memset(_pathFindArray, 0, sizeof(_pathFindArray)); + + memset(_pathValues, 0, sizeof(_pathValues)); + _PVCount = 0; + _GPVCount = 0; + + memset(_pathValues1, 0, sizeof(_pathValues1)); + _PVCount1 = 0; + _GPVCount1 = 0; + + memset(_paletteBackup, 0, sizeof(_paletteBackup)); + memset(_palette, 0, sizeof(_palette)); + + memset(_videoBuf1, 0, sizeof(_videoBuf1)); + + _fcs_list = new FillOrCopyStruct[16]; + + memset(_lettersToPrintBuf, 0, sizeof(_lettersToPrintBuf)); + + _numScreenUpdates = 0; + _vgaTickCounter = 0; + + _sound = 0; + + _effectsPaused = false; + _ambientPaused = false; + _musicPaused = false; + + _dumpFile = 0; + + _saveLoadType = 0; + _saveLoadSlot = 0; + memset(_saveLoadName, 0, sizeof(_saveLoadName)); + + _saveLoadRowCurPos = 0; + _numSaveGameRows = 0; + _saveDialogFlag = false; + _saveOrLoad = false; + _saveLoadFlag = false; + + _sdlMouseX = 0; + _sdlMouseY = 0; + + _sdl_buf_3 = 0; + _sdl_buf = 0; + _sdl_buf_attached = 0; + + _vc10BasePtrOld = 0; + memcpy (_hebrew_char_widths, + "\x5\x5\x4\x6\x5\x3\x4\x5\x6\x3\x5\x5\x4\x6\x5\x3\x4\x6\x5\x6\x6\x6\x5\x5\x5\x6\x5\x6\x6\x6\x6\x6", 32); + +} + +int SimonEngine::init(GameDetector &detector) { + + // Add default file directories for Acorn version + File::addDefaultDirectory(_gameDataPath + "execute/"); + File::addDefaultDirectory(_gameDataPath + "EXECUTE/"); + + // Detect game + if (!initGame()) { + return -1; + } + + if (getGameType() == GType_FF) { + _screenWidth = 640; + _screenHeight = 480; + } else { + _screenWidth = 320; + _screenHeight = 200; + } + + // Setup mixer + if (!_mixer->isReady()) + warning("Sound initialization failed. " + "Features of the game that depend on sound synchronization will most likely break"); + set_volume(ConfMan.getInt("sfx_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + + _system->beginGFXTransaction(); + initCommonGFX(detector); + _system->initSize(_screenWidth, _screenHeight); + if (getGameType() == GType_FF) + _system->setGraphicsMode("1x"); + _system->endGFXTransaction(); + + // Setup midi driver + MidiDriver *driver = 0; + if (getPlatform() == Common::kPlatformAmiga) { + driver = MidiDriver::createMidi(MD_NULL); // Create fake MIDI driver for Simon1Amiga and Simon2CD32 for now + _native_mt32 = false; + } else { + int midiDriver = MidiDriver::detectMusicDriver(MDT_ADLIB | MDT_MIDI); + _native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")); + driver = MidiDriver::createMidi(midiDriver); + if (_native_mt32) { + driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); + } + } + + midi.mapMT32toGM (getGameType() == GType_SIMON1 && !_native_mt32); + + midi.set_driver(driver); + int ret = midi.open(); + if (ret) + warning ("MIDI Player init failed: \"%s\"", midi.getErrorName (ret)); + midi.set_volume(ConfMan.getInt("music_volume")); + + _debugMode = (gDebugLevel >= 0); + _language = Common::parseLanguage(ConfMan.get("language")); + + if (ConfMan.hasKey("music_mute") && ConfMan.getBool("music_mute") == 1) + midi.pause(_musicPaused ^= 1); + + if ((getGameType() == GType_SIMON2) && ConfMan.hasKey("speech_mute") && ConfMan.getBool("speech_mute") == 1) + _speech = 0; + + if ((getGameType() == GType_SIMON1 && _language > 1) || ((getGameType() == GType_SIMON2) && _language == Common::HB_ISR)) { + if (ConfMan.hasKey("subtitles") && ConfMan.getBool("subtitles") == 0) + _subtitles = 0; + } else + _subtitles = ConfMan.getBool("subtitles"); + + // Make sure either speech or subtitles is enabled + if ((getFeatures() & GF_TALKIE) && !_speech && !_subtitles) + _subtitles = 1; + + if (ConfMan.hasKey("fade") && ConfMan.getBool("fade") == 0) + _fade = 0; + + if (ConfMan.hasKey("slow_down") && ConfMan.getInt("slow_down") >= 1) + _speed = ConfMan.getInt("slow_down"); + + // FIXME Use auto dirty rects cleanup code to reduce CPU usage + g_system->setFeatureState(OSystem::kFeatureAutoComputeDirtyRects, true); + + VGA_DELAY_BASE = 1; + if (getGameType() == GType_FF) { + NUM_VIDEO_OP_CODES = 85; +#ifndef PALMOS_68K + VGA_MEM_SIZE = 7500000; +#else + VGA_MEM_SIZE = gVars->memory[kMemSimon2Games]; +#endif + TABLES_MEM_SIZE = 200000; + } else if (getGameType() == GType_SIMON2) { + TABLE_INDEX_BASE = 1580 / 4; + TEXT_INDEX_BASE = 1500 / 4; + NUM_VIDEO_OP_CODES = 75; +#ifndef PALMOS_68K + VGA_MEM_SIZE = 2000000; +#else + VGA_MEM_SIZE = gVars->memory[kMemSimon2Games]; +#endif + TABLES_MEM_SIZE = 100000; + // Check whether to use MT-32 MIDI tracks in Simon the Sorcerer 2 + if ((getGameType() == GType_SIMON2) && _native_mt32) + MUSIC_INDEX_BASE = (1128 + 612) / 4; + else + MUSIC_INDEX_BASE = 1128 / 4; + SOUND_INDEX_BASE = 1660 / 4; + } else { + TABLE_INDEX_BASE = 1576 / 4; + TEXT_INDEX_BASE = 1460 / 4; + NUM_VIDEO_OP_CODES = 64; +#ifndef PALMOS_68K + VGA_MEM_SIZE = 1000000; +#else + VGA_MEM_SIZE = gVars->memory[kMemSimon1Games]; +#endif + TABLES_MEM_SIZE = 50000; + MUSIC_INDEX_BASE = 1316 / 4; + SOUND_INDEX_BASE = 0; + } + + if (getGameType() == GType_FF) { + gss = PTR(feeblefiles_settings); + } else if (getGameType() == GType_SIMON2) { + if (getFeatures() & GF_TALKIE) { + gss = PTR(simon2win_settings); + + // Add default file directories + File::addDefaultDirectory(_gameDataPath + "voices/"); + File::addDefaultDirectory(_gameDataPath + "VOICES/"); + } else { + gss = PTR(simon2dos_settings); + } + } else if (getGameType() == GType_SIMON1) { + if (getPlatform() == Common::kPlatformAcorn) { + gss = PTR(simon1acorn_settings); + } else if (getPlatform() == Common::kPlatformAmiga) { + gss = PTR(simon1amiga_settings); + } else if (getGameId() == GID_SIMON1DEMO) { + gss = PTR(simon1demo_settings); + } else { + gss = PTR(simon1_settings); + } + } + + if ((getGameType() == GType_SIMON1) && (getFeatures() & GF_TALKIE)) { + // Add default file directories + switch (_language) { + case Common::HB_ISR: + File::addDefaultDirectory(_gameDataPath + "hebrew/"); + File::addDefaultDirectory(_gameDataPath + "HEBREW/"); + break; + case Common::ES_ESP: + File::addDefaultDirectory(_gameDataPath + "spanish/"); + File::addDefaultDirectory(_gameDataPath + "SPANISH/"); + break; + case Common::IT_ITA: + File::addDefaultDirectory(_gameDataPath + "italian/"); + File::addDefaultDirectory(_gameDataPath + "ITALIAN/"); + break; + case Common::FR_FRA: + File::addDefaultDirectory(_gameDataPath + "french/"); + File::addDefaultDirectory(_gameDataPath + "FRENCH/"); + break; + } + } + + return 0; +} + +SimonEngine::~SimonEngine() { + delete _gameFile; + + midi.close(); + + free(_stringTabPtr); + free(_itemArrayPtr); + free(_itemHeapPtr - _itemHeapCurPos); + free(_tablesHeapPtr - _tablesHeapCurPos); + free(_tblList); + free(_iconFilePtr); + free(_gameOffsetsPtr); + + delete _dummyItem1; + delete _dummyItem2; + delete _dummyItem3; + + delete [] _fcs_list; + + delete _sound; + delete _debugger; +} + +void SimonEngine::errorString(const char *buf1, char *buf2) { + strcpy(buf2, buf1); + +#ifdef _WIN32_WCE + if (isSmartphone()) + return; +#endif + + // Unless an error -originated- within the debugger, spawn the + // debugger. Otherwise exit out normally. + if (_debugger && !_debugger->isAttached()) { + // (Print it again in case debugger segfaults) + printf("%s\n", buf2); + _debugger->attach(buf2); + _debugger->onFrame(); + } +} + +void SimonEngine::palette_fadeout(uint32 *pal_values, uint num) { + byte *p = (byte *)pal_values; + + do { + if (p[0] >= 8) + p[0] -= 8; + else + p[0] = 0; + if (p[1] >= 8) + p[1] -= 8; + else + p[1] = 0; + if (p[2] >= 8) + p[2] -= 8; + else + p[2] = 0; + p += 4; + } while (--num); +} + +byte *SimonEngine::allocateItem(uint size) { + byte *org = _itemHeapPtr; + size = (size + 3) & ~3; + + _itemHeapPtr += size; + _itemHeapCurPos += size; + + if (_itemHeapCurPos > _itemHeapSize) + error("Itemheap overflow"); + + return org; +} + +void SimonEngine::alignTableMem() { + if ((unsigned long)_tablesHeapPtr & 3) { + _tablesHeapPtr += 2; + _tablesHeapCurPos += 2; + } +} + +byte *SimonEngine::allocateTable(uint size) { + byte *org = _tablesHeapPtr; + + size = (size + 1) & ~1; + + _tablesHeapPtr += size; + _tablesHeapCurPos += size; + + if (_tablesHeapCurPos > _tablesHeapSize) + error("Tablesheap overflow"); + + return org; +} + +int SimonEngine::allocGamePcVars(File *in) { + uint item_array_size, item_array_inited, stringtable_num; + uint32 version; + uint i; + + item_array_size = in->readUint32BE(); + version = in->readUint32BE(); + item_array_inited = in->readUint32BE(); + stringtable_num = in->readUint32BE(); + + item_array_inited += 2; // first two items are predefined + item_array_size += 2; + + if (version != 0x80) + error("Not a runtime database"); + + _itemArrayPtr = (Item **)calloc(item_array_size, sizeof(Item *)); + if (_itemArrayPtr == NULL) + error("Out of memory for Item array"); + + _itemArraySize = item_array_size; + _itemArrayInited = item_array_inited; + + for (i = 1; i < item_array_inited; i++) { + _itemArrayPtr[i] = (Item *)allocateItem(sizeof(Item)); + } + + // The rest is cleared automatically by calloc + allocateStringTable(stringtable_num + 10); + _stringTabNum = stringtable_num; + + return item_array_inited; +} + +void SimonEngine::loginPlayerHelper(Item *item, int a, int b) { + Child9 *child; + + child = (Child9 *) findChildOfType(item, 9); + if (child == NULL) { + child = (Child9 *) allocateChildBlock(item, 9, sizeof(Child9)); + } + + if (a >= 0 && a <= 3) + child->array[a] = b; +} + +void SimonEngine::loginPlayer() { + Child *child; + + _item1 = _itemArrayPtr[1]; + _item1->adjective = -1; + _item1->noun = 10000; + + child = (Child *)allocateChildBlock(_item1, 3, sizeof(Child)); + if (child == NULL) + error("player create failure"); + + loginPlayerHelper(_item1, 0, 0); +} + +void SimonEngine::allocateStringTable(int num) { + _stringTabPtr = (byte **)calloc(num, sizeof(byte *)); + _stringTabPos = 0; + _stringtab_numalloc = num; +} + +void SimonEngine::setupStringTable(byte *mem, int num) { + int i = 0; + for (;;) { + _stringTabPtr[i++] = mem; + if (--num == 0) + break; + for (; *mem; mem++); + mem++; + } + + _stringTabPos = i; +} + +void SimonEngine::setupLocalStringTable(byte *mem, int num) { + int i = 0; + for (;;) { + _localStringtable[i++] = mem; + if (--num == 0) + break; + for (; *mem; mem++); + mem++; + } +} + +void SimonEngine::readSubroutineLine(File *in, SubroutineLine *sl, Subroutine *sub) { + byte line_buffer[1024], *q = line_buffer; + int size; + + if (sub->id == 0) { + sl->verb = in->readUint16BE(); + sl->noun1 = in->readUint16BE(); + sl->noun2 = in->readUint16BE(); + } + + while ((*q = in->readByte()) != 0xFF) { + if (*q == 87) { + in->readUint16BE(); + } else { + q = readSingleOpcode(in, q); + } + } + + size = q - line_buffer + 1; + + memcpy(allocateTable(size), line_buffer, size); +} + +SubroutineLine *SimonEngine::createSubroutineLine(Subroutine *sub, int where) { + SubroutineLine *sl, *cur_sl = NULL, *last_sl = NULL; + + if (sub->id == 0) + sl = (SubroutineLine *)allocateTable(SUBROUTINE_LINE_BIG_SIZE); + else + sl = (SubroutineLine *)allocateTable(SUBROUTINE_LINE_SMALL_SIZE); + + // where is what offset to insert the line at, locate the proper beginning line + if (sub->first != 0) { + cur_sl = (SubroutineLine *)((byte *)sub + sub->first); + while (where) { + last_sl = cur_sl; + cur_sl = (SubroutineLine *)((byte *)sub + cur_sl->next); + if ((byte *)cur_sl == (byte *)sub) + break; + where--; + } + } + + if (last_sl != NULL) { + // Insert the subroutine line in the middle of the link + last_sl->next = (byte *)sl - (byte *)sub; + sl->next = (byte *)cur_sl - (byte *)sub; + } else { + // Insert the subroutine line at the head of the link + sl->next = sub->first; + sub->first = (byte *)sl - (byte *)sub; + } + + return sl; +} + +void SimonEngine::readSubroutine(File *in, Subroutine *sub) { + while (in->readUint16BE() == 0) { + readSubroutineLine(in, createSubroutineLine(sub, 0xFFFF), sub); + } +} + +Subroutine *SimonEngine::createSubroutine(uint id) { + Subroutine *sub; + + alignTableMem(); + + sub = (Subroutine *)allocateTable(sizeof(Subroutine)); + sub->id = id; + sub->first = 0; + sub->next = _subroutineList; + _subroutineList = sub; + return sub; +} + +void SimonEngine::readSubroutineBlock(File *in) { + while (in->readUint16BE() == 0) { + readSubroutine(in, createSubroutine(in->readUint16BE())); + } +} + +Child *SimonEngine::findChildOfType(Item *i, uint type) { + Child *child = i->children; + for (; child; child = child->next) + if (child->type == type) + return child; + return NULL; +} + +bool SimonEngine::isRoom(Item *item) { + return findChildOfType(item, 1) != NULL; +} + +bool SimonEngine::isObject(Item *item) { + return findChildOfType(item, 2) != NULL; +} + +uint SimonEngine::getOffsetOfChild2Param(Child2 *child, uint prop) { + uint m = 1; + uint offset = 0; + while (m != prop) { + if (child->avail_props & m) + offset++; + m *= 2; + } + return offset; +} + +Child *SimonEngine::allocateChildBlock(Item *i, uint type, uint size) { + Child *child = (Child *)allocateItem(size); + child->next = i->children; + i->children = child; + child->type = type; + return child; +} + +void SimonEngine::allocItemHeap() { + _itemHeapSize = 10000; + _itemHeapCurPos = 0; + _itemHeapPtr = (byte *)calloc(10000, 1); +} + +void SimonEngine::allocTablesHeap() { + _tablesHeapSize = TABLES_MEM_SIZE; + _tablesHeapCurPos = 0; + _tablesHeapPtr = (byte *)calloc(TABLES_MEM_SIZE, 1); +} + +void SimonEngine::setItemState(Item *item, int value) { + item->state = value; +} + +int SimonEngine::getNextWord() { + int16 a = (int16)READ_BE_UINT16(_codePtr); + _codePtr += 2; + return a; +} + +uint SimonEngine::getNextStringID() { + return (uint16)getNextWord(); +} + +uint SimonEngine::getVarOrByte() { + uint a = *_codePtr++; + if (a != 255) + return a; + return readVariable(*_codePtr++); +} + +uint SimonEngine::getVarOrWord() { + uint a = READ_BE_UINT16(_codePtr); + _codePtr += 2; + if (a >= 30000 && a < 30512) + return readVariable(a - 30000); + return a; +} + +Item *SimonEngine::getNextItemPtr() { + int a = getNextWord(); + switch (a) { + case -1: + return _subjectItem; + case -3: + return _objectItem; + case -5: + return getItem1Ptr(); + case -7: + return getItemPtrB(); + case -9: + return derefItem(getItem1Ptr()->parent); + default: + return derefItem(a); + } +} + +Item *SimonEngine::getNextItemPtrStrange() { + int a = getNextWord(); + switch (a) { + case -1: + return _subjectItem; + case -3: + return _objectItem; + case -5: + return _dummyItem2; + case -7: + return NULL; + case -9: + return _dummyItem3; + default: + return derefItem(a); + } +} + +uint SimonEngine::getNextItemID() { + int a = getNextWord(); + switch (a) { + case -1: + return itemPtrToID(_subjectItem); + case -3: + return itemPtrToID(_objectItem); + case -5: + return getItem1ID(); + case -7: + return 0; + case -9: + return getItem1Ptr()->parent; + default: + return a; + } +} + +Item *SimonEngine::getItem1Ptr() { + if (_item1) + return _item1; + return _dummyItem1; +} + +Item *SimonEngine::getItemPtrB() { + error("getItemPtrB: is this code ever used?"); + return _dummyItem1; +} + +uint SimonEngine::getNextVarContents() { + return (uint16)readVariable(getVarOrByte()); +} + +uint SimonEngine::readVariable(uint variable) { + if (variable >= 255) + error("Variable %d out of range in read", variable); + return _variableArray[variable]; +} + +void SimonEngine::writeNextVarContents(uint16 contents) { + writeVariable(getVarOrByte(), contents); +} + +void SimonEngine::writeVariable(uint variable, uint16 contents) { + if (variable >= 256) + error("Variable %d out of range in write", variable); + _variableArray[variable] = contents; +} + +void SimonEngine::setItemParent(Item *item, Item *parent) { + Item *old_parent = derefItem(item->parent); + + if (item == parent) + error("Trying to set item as its own parent"); + + // unlink it if it has a parent + if (old_parent) + unlinkItem(item); + itemChildrenChanged(old_parent); + linkItem(item, parent); + itemChildrenChanged(parent); +} + +void SimonEngine::itemChildrenChanged(Item *item) { + int i; + FillOrCopyStruct *fcs; + + if (_noParentNotify) + return; + + mouseOff(); + + for (i = 0; i != 8; i++) { + fcs = _windowArray[i]; + if (fcs && fcs->fcs_data && fcs->fcs_data->item_ptr == item) { + if (_fcsData1[i]) { + _fcsData2[i] = true; + } else { + _fcsData2[i] = false; + drawIconArray(i, item, fcs->fcs_data->unk1, fcs->fcs_data->unk2); + } + } + } + + mouseOn(); +} + +void SimonEngine::unlinkItem(Item *item) { + Item *first, *parent, *next; + + // can't unlink item without parent + if (item->parent == 0) + return; + + // get parent and first child of parent + parent = derefItem(item->parent); + first = derefItem(parent->child); + + // the node to remove is first in the parent's children? + if (first == item) { + parent->child = item->sibling; + item->parent = 0; + item->sibling = 0; + return; + } + + for (;;) { + if (!first) + error("unlinkItem: parent empty"); + if (first->sibling == 0) + error("unlinkItem: parent does not contain child"); + + next = derefItem(first->sibling); + if (next == item) { + first->sibling = next->sibling; + item->parent = 0; + item->sibling = 0; + return; + } + first = next; + } +} + +void SimonEngine::linkItem(Item *item, Item *parent) { + uint id; + // Don't allow that an item that is already linked is relinked + if (item->parent) + return; + + id = itemPtrToID(parent); + item->parent = id; + + if (parent != 0) { + item->sibling = parent->child; + parent->child = itemPtrToID(item); + } else { + item->sibling = 0; + } +} + +const byte *SimonEngine::getStringPtrByID(uint string_id) { + const byte *string_ptr; + byte *dst; + + _freeStringSlot ^= 1; + + if (string_id < 0x8000) { + string_ptr = _stringTabPtr[string_id]; + } else { + string_ptr = getLocalStringByID(string_id); + } + + dst = _stringReturnBuffer[_freeStringSlot]; + strcpy((char *)dst, (const char *)string_ptr); + return dst; +} + +const byte *SimonEngine::getLocalStringByID(uint string_id) { + if (string_id < _stringIdLocalMin || string_id >= _stringIdLocalMax) { + loadTextIntoMem(string_id); + } + return _localStringtable[string_id - _stringIdLocalMin]; +} + +void SimonEngine::loadTextIntoMem(uint string_id) { + byte *p; + char filename[30]; + int i; + uint base_min = 0x8000, base_max, size; + + _tablesHeapPtr = _tablesheapPtrNew; + _tablesHeapCurPos = _tablesHeapCurPosNew; + + p = _strippedTxtMem; + + // get filename + while (*p) { + for (i = 0; *p; p++, i++) + filename[i] = *p; + filename[i] = 0; + p++; + + base_max = (p[0] * 256) | p[1]; + p += 2; + + if (string_id < base_max) { + _stringIdLocalMin = base_min; + _stringIdLocalMax = base_max; + + _localStringtable = (byte **)_tablesHeapPtr; + + size = (base_max - base_min + 1) * sizeof(byte *); + _tablesHeapPtr += size; + _tablesHeapCurPos += size; + + size = loadTextFile(filename, _tablesHeapPtr); + + setupLocalStringTable(_tablesHeapPtr, base_max - base_min + 1); + + _tablesHeapPtr += size; + _tablesHeapCurPos += size; + + if (_tablesHeapCurPos > _tablesHeapSize) { + error("loadTextIntoMem: Out of table memory"); + } + return; + } + + base_min = base_max; + } + + error("loadTextIntoMem: didn't find %d", string_id); +} + +void SimonEngine::loadTablesIntoMem(uint subr_id) { + byte *p; + int i; + uint min_num, max_num; + char filename[30]; + File *in; + + p = _tblList; + if (p == NULL) + return; + + while (*p) { + for (i = 0; *p; p++, i++) + filename[i] = *p; + filename[i] = 0; + p++; + + for (;;) { + min_num = (p[0] * 256) | p[1]; + p += 2; + + if (min_num == 0) + break; + + max_num = (p[0] * 256) | p[1]; + p += 2; + + if (subr_id >= min_num && subr_id <= max_num) { + _subroutineList = _subroutineListOrg; + _tablesHeapPtr = _tablesHeapPtrOrg; + _tablesHeapCurPos = _tablesHeapCurPosOrg; + _stringIdLocalMin = 1; + _stringIdLocalMax = 0; + + in = openTablesFile(filename); + readSubroutineBlock(in); + closeTablesFile(in); + if (getGameType() == GType_FF) { + // TODO + } else if (getGameType() == GType_SIMON2) { + _sound->loadSfxTable(_gameFile, _gameOffsetsPtr[atoi(filename + 6) - 1 + SOUND_INDEX_BASE]); + } else if (getPlatform() == Common::kPlatformWindows) { + memcpy(filename, "SFXXXX", 6); + _sound->readSfxFile(filename); + } + + alignTableMem(); + + _tablesheapPtrNew = _tablesHeapPtr; + _tablesHeapCurPosNew = _tablesHeapCurPos; + + if (_tablesHeapCurPos > _tablesHeapSize) + error("loadTablesIntoMem: Out of table memory"); + return; + } + } + } + + debug(1,"loadTablesIntoMem: didn't find %d", subr_id); +} + +void SimonEngine::playSting(uint a) { + if (!midi._enable_sfx) + return; + + char filename[15]; + + File mus_file; + uint16 mus_offset; + + sprintf(filename, "STINGS%i.MUS", _soundFileId); + mus_file.open(filename); + if (!mus_file.isOpen()) { + warning("Can't load sound effect from '%s'", filename); + return; + } + + mus_file.seek(a * 2, SEEK_SET); + mus_offset = mus_file.readUint16LE(); + if (mus_file.ioFailed()) + error("Can't read sting %d offset", a); + + mus_file.seek(mus_offset, SEEK_SET); + midi.loadSMF(&mus_file, a, true); + midi.startTrack(0); +} + +Subroutine *SimonEngine::getSubroutineByID(uint subroutine_id) { + Subroutine *cur; + + _subroutine = subroutine_id; + + for (cur = _subroutineList; cur; cur = cur->next) { + if (cur->id == subroutine_id) + return cur; + } + + loadTablesIntoMem(subroutine_id); + + for (cur = _subroutineList; cur; cur = cur->next) { + if (cur->id == subroutine_id) + return cur; + } + + debug(1,"getSubroutineByID: subroutine %d not found", subroutine_id); + return NULL; +} + +uint SimonEngine::loadTextFile_gme(const char *filename, byte *dst) { + uint res; + uint32 offs; + uint32 size; + + res = atoi(filename + 4) + TEXT_INDEX_BASE - 1; + offs = _gameOffsetsPtr[res]; + size = _gameOffsetsPtr[res + 1] - offs; + + resfile_read(dst, offs, size); + + return size; +} + +File *SimonEngine::openTablesFile_gme(const char *filename) { + uint res; + uint32 offs; + + res = atoi(filename + 6) + TABLE_INDEX_BASE - 1; + offs = _gameOffsetsPtr[res]; + + _gameFile->seek(offs, SEEK_SET); + return _gameFile; +} + +uint SimonEngine::loadTextFile_simon1(const char *filename, byte *dst) { + File fo; + fo.open(filename); + uint32 size; + + if (fo.isOpen() == false) + error("loadTextFile: Can't open '%s'", filename); + + size = fo.size(); + + if (fo.read(dst, size) != size) + error("loadTextFile: fread failed"); + fo.close(); + + return size; +} + +File *SimonEngine::openTablesFile_simon1(const char *filename) { + File *fo = new File(); + fo->open(filename); + if (fo->isOpen() == false) + error("openTablesFile: Can't open '%s'", filename); + return fo; +} + +uint SimonEngine::loadTextFile(const char *filename, byte *dst) { + if (getFeatures() & GF_OLD_BUNDLE) + return loadTextFile_simon1(filename, dst); + else + return loadTextFile_gme(filename, dst); +} + +File *SimonEngine::openTablesFile(const char *filename) { + if (getFeatures() & GF_OLD_BUNDLE) + return openTablesFile_simon1(filename); + else + return openTablesFile_gme(filename); +} + +void SimonEngine::closeTablesFile(File *in) { + if (getFeatures() & GF_OLD_BUNDLE) { + in->close(); + delete in; + } +} + +void SimonEngine::addTimeEvent(uint timeout, uint subroutine_id) { + TimeEvent *te = (TimeEvent *)malloc(sizeof(TimeEvent)), *first, *last = NULL; + time_t cur_time; + + time(&cur_time); + + te->time = cur_time + timeout - _base_time; + te->subroutine_id = subroutine_id; + + first = _firstTimeStruct; + while (first) { + if (te->time <= first->time) { + if (last) { + last->next = te; + te->next = first; + return; + } + te->next = _firstTimeStruct; + _firstTimeStruct = te; + return; + } + + last = first; + first = first->next; + } + + if (last) { + last->next = te; + te->next = NULL; + } else { + _firstTimeStruct = te; + te->next = NULL; + } +} + +void SimonEngine::delTimeEvent(TimeEvent *te) { + TimeEvent *cur; + + if (te == _pendingDeleteTimeEvent) + _pendingDeleteTimeEvent = NULL; + + if (te == _firstTimeStruct) { + _firstTimeStruct = te->next; + free(te); + return; + } + + cur = _firstTimeStruct; + if (cur == NULL) + error("delTimeEvent: none available"); + + for (;;) { + if (cur->next == NULL) + error("delTimeEvent: no such te"); + if (te == cur->next) { + cur->next = te->next; + free(te); + return; + } + cur = cur->next; + } +} + +void SimonEngine::killAllTimers() { + TimeEvent *cur, *next; + + for (cur = _firstTimeStruct; cur; cur = next) { + next = cur->next; + delTimeEvent(cur); + } +} + +bool SimonEngine::kickoffTimeEvents() { + time_t cur_time; + TimeEvent *te; + bool result = false; + + time(&cur_time); + cur_time -= _base_time; + + while ((te = _firstTimeStruct) != NULL && te->time <= (uint32)cur_time) { + result = true; + _pendingDeleteTimeEvent = te; + invokeTimeEvent(te); + if (_pendingDeleteTimeEvent) { + _pendingDeleteTimeEvent = NULL; + delTimeEvent(te); + } + } + + return result; +} + +void SimonEngine::invokeTimeEvent(TimeEvent *te) { + Subroutine *sub; + + _scriptVerb = 0; + if (_runScriptReturn1) + return; + sub = getSubroutineByID(te->subroutine_id); + if (sub != NULL) + startSubroutineEx(sub); + _runScriptReturn1 = false; +} + +void SimonEngine::o_setup_cond_c() { + + setup_cond_c_helper(); + + _objectItem = _hitAreaObjectItem; + + if (_objectItem == _dummyItem2) + _objectItem = getItem1Ptr(); + + if (_objectItem == _dummyItem3) + _objectItem = derefItem(getItem1Ptr()->parent); + + if (_objectItem != NULL) { + _scriptNoun2 = _objectItem->noun; + _scriptAdj2 = _objectItem->adjective; + } else { + _scriptNoun2 = -1; + _scriptAdj2 = -1; + } +} + +void SimonEngine::setup_cond_c_helper() { + HitArea *last; + + if (getGameType() == GType_SIMON2) { + _mouseCursor = 0; + if (_hitAreaUnk4 != 999) { + _mouseCursor = 9; + _needHitAreaRecalc++; + _hitAreaUnk4 = 0; + } + } + + _lastHitArea = 0; + _hitAreaObjectItem = NULL; + + last = _lastHitArea2Ptr; + defocusHitarea(); + _lastHitArea2Ptr = last; + + for (;;) { + _lastHitArea = NULL; + _lastHitArea3 = 0; + _leftButtonDown = 0; + + do { + if (_exitCutscene && (_bitArray[0] & 0x200)) { + endCutscene(); + goto out_of_here; + } + + if (getGameType() == GType_FF) { + if (_variableArray[254] == 63) { + hitarea_stuff_helper(); + } else if (_variableArray[254] == 75) { + hitarea_stuff_helper(); + _variableArray[60] = 9999; + goto out_of_here; + } + } + + delay(100); + } while (_lastHitArea3 == (HitArea *) 0xFFFFFFFF || _lastHitArea3 == 0); + + if (_lastHitArea == NULL) { + } else if (_lastHitArea->id == 0x7FFB) { + handle_uparrow_hitarea(_lastHitArea->fcs); + } else if (_lastHitArea->id == 0x7FFC) { + handle_downarrow_hitarea(_lastHitArea->fcs); + } else if (_lastHitArea->item_ptr != NULL) { + _hitAreaObjectItem = _lastHitArea->item_ptr; + _variableArray[60] = (_lastHitArea->flags & 1) ? (_lastHitArea->flags / 256) : 0xFFFF; + break; + } + } + +out_of_here: + _lastHitArea3 = 0; + _lastHitArea = 0; + _lastHitArea2Ptr = NULL; +} + +void SimonEngine::endCutscene() { + Subroutine *sub; + + _sound->stopVoice(); + + sub = getSubroutineByID(170); + if (sub != NULL) + startSubroutineEx(sub); + + _runScriptReturn1 = true; +} + +uint SimonEngine::get_fcs_ptr_3_index(FillOrCopyStruct *fcs) { + uint i; + + for (i = 0; i != ARRAYSIZE(_windowArray); i++) + if (_windowArray[i] == fcs) + return i; + + error("get_fcs_ptr_3_index: not found"); + return 0; +} + +void SimonEngine::mouseOff() { + _mouseHideCount++; +} + +void SimonEngine::mouseOn() { + _lockWord |= 1; + + if (_mouseHideCount != 0) + _mouseHideCount--; + + _lockWord &= ~1; +} + +void SimonEngine::handle_mouse_moved() { + uint x; + + if (_mouseHideCount) { + _system->showMouse(false); + return; + } + + _system->showMouse(true); + pollMouseXY(); + + if (_mouseX >= 32768) + _mouseX = 0; + if (_mouseX >= _screenWidth - 1) + _mouseX = _screenWidth - 1; + + if (_mouseY >= 32768) + _mouseY = 0; + if (_mouseY >= _screenHeight - 1) + _mouseY = _screenHeight - 1; + + if (_hitAreaUnk4) { + uint id = 101; + if (_mouseY >= 136) + id = 102; + if (_hitAreaUnk4 != id) + hitarea_proc_1(); + } + + if (getGameType() == GType_FF) { + if (_bitArray[6] & 0x8) { // Oracle + if (_mouseX >= 10 && _mouseX <= 635 && _mouseY >= 5 && _mouseX <= 475) { + _bitArray[6] |= 0x4; + } else { + if (_bitArray[6] & 0x4) { + _variableArray[254] = 63; + } + } + } else if (_bitArray[5] & 0x0100) { // Close Up + if (_mouseX >= 10 && _mouseX <= 635 && _mouseY >= 5 && _mouseX <= 475) { + _bitArray[5] |= 0x80; + } else { + if (_bitArray[5] & 0x80) { + _variableArray[254] = 75; + } + } + } + } + + if (getGameType() == GType_SIMON2) { + if (_bitArray[4] & 0x8000) { + if (!_vgaVar9) { + if (_mouseX >= 630 / 2 || _mouseX < 9) + goto get_out2; + _vgaVar9 = 1; + } + if (_scrollCount == 0) { + if (_mouseX >= 631 / 2) { + if (_scrollX != _scrollXMax) + _scrollFlag = 1; + } else if (_mouseX < 8) { + if (_scrollX != 0) + _scrollFlag = -1; + } + } + } else { + get_out2:; + _vgaVar9 = 0; + } + } + + if (_mouseX != _mouseXOld || _mouseY != _mouseYOld) + _needHitAreaRecalc++; + + x = 0; + if (_lastHitArea3 == 0 && _leftButtonDown != 0) { + _leftButtonDown = 0; + x = 1; + } else { + if (_hitarea_unk_3 == 0 && _needHitAreaRecalc == 0) + goto get_out; + } + + setup_hitarea_from_pos(_mouseX, _mouseY, x); + _lastHitArea3 = _lastHitArea; + if (x == 1 && _lastHitArea == NULL) + _lastHitArea3 = (HitArea *) - 1; + +get_out: + drawMousePointer(); + _needHitAreaRecalc = 0; +} + +void SimonEngine::drawIconArray(uint fcs_index, Item *item_ptr, int unk1, int unk2) { + Item *item_ptr_org = item_ptr; + FillOrCopyStruct *fcs_ptr; + uint width_div_3, height_div_3; + uint j, k, i, num_sibs_with_flag; + bool item_again; + uint x_pos, y_pos; + + fcs_ptr = _windowArray[fcs_index & 7]; + + if (getGameType() == GType_SIMON1) { + width_div_3 = fcs_ptr->width / 3; + height_div_3 = fcs_ptr->height / 3; + } else { + width_div_3 = 100; + height_div_3 = 40; + } + + i = 0; + + if (fcs_ptr == NULL) + return; + + if (fcs_ptr->fcs_data) + removeIconArray(fcs_index); + + fcs_ptr->fcs_data = (FillOrCopyData *) malloc(sizeof(FillOrCopyData)); + fcs_ptr->fcs_data->item_ptr = item_ptr; + fcs_ptr->fcs_data->upArrow = -1; + fcs_ptr->fcs_data->downArrow = -1; + fcs_ptr->fcs_data->unk1 = unk1; + fcs_ptr->fcs_data->unk2 = unk2; + + item_ptr = derefItem(item_ptr->child); + + while (item_ptr && unk1-- != 0) { + num_sibs_with_flag = 0; + while (item_ptr && width_div_3 > num_sibs_with_flag) { + if ((unk2 == 0 || item_ptr->classFlags & unk2) && has_item_childflag_0x10(item_ptr)) + if (getGameType() == GType_SIMON1) { + num_sibs_with_flag++; + } else { + num_sibs_with_flag += 20; + } + item_ptr = derefItem(item_ptr->sibling); + } + } + + if (item_ptr == NULL) { + fcs_ptr->fcs_data->unk1 = 0; + item_ptr = derefItem(item_ptr_org->child); + } + + x_pos = 0; + y_pos = 0; + item_again = false; + k = 0; + j = 0; + + while (item_ptr) { + if ((unk2 == 0 || item_ptr->classFlags & unk2) && has_item_childflag_0x10(item_ptr)) { + if (item_again == false) { + fcs_ptr->fcs_data->e[k].item = item_ptr; + if (getGameType() == GType_SIMON1) { + draw_icon_c(fcs_ptr, item_get_icon_number(item_ptr), x_pos * 3, y_pos); + fcs_ptr->fcs_data->e[k].hit_area = + setup_icon_hit_area(fcs_ptr, x_pos * 3, y_pos, + item_get_icon_number(item_ptr), item_ptr); + } else { + draw_icon_c(fcs_ptr, item_get_icon_number(item_ptr), x_pos, y_pos); + fcs_ptr->fcs_data->e[k].hit_area = + setup_icon_hit_area(fcs_ptr, x_pos, y_pos, item_get_icon_number(item_ptr), item_ptr); + } + k++; + } else { + fcs_ptr->fcs_data->e[k].item = NULL; + j = 1; + } + x_pos += (getGameType() == GType_SIMON1) ? 1 : 20; + + if (x_pos >= width_div_3) { + x_pos = 0; + + y_pos += (getGameType() == GType_SIMON1) ? 1 : 20; + if (y_pos >= height_div_3) + item_again = true; + } + } + item_ptr = derefItem(item_ptr->sibling); + } + + fcs_ptr->fcs_data->e[k].item = NULL; + + if (j != 0 || fcs_ptr->fcs_data->unk1 != 0) { + addArrows(fcs_ptr, fcs_index); + } +} + +void SimonEngine::addArrows(FillOrCopyStruct *fcs, uint fcs_index) { + setArrowHitAreas(fcs, fcs_index); + + fcs->fcs_data->upArrow = _scrollUpHitArea; + fcs->fcs_data->downArrow = _scrollDownHitArea; +} + +void SimonEngine::setArrowHitAreas(FillOrCopyStruct *fcs, uint fcs_index) { + HitArea *ha; + + ha = findEmptyHitArea(); + _scrollUpHitArea = ha - _hitAreas; + if (getGameType() == GType_SIMON1) { + ha->x = 308; + ha->y = 149; + ha->width = 12; + ha->height = 17; + ha->flags = 0x24; + ha->id = 0x7FFB; + ha->layer = 100; + ha->fcs = fcs; + ha->unk3 = 1; + } else { + ha->x = 81; + ha->y = 158; + ha->width = 12; + ha->height = 26; + ha->flags = 36; + ha->id = 0x7FFB; + ha->layer = 100; + ha->fcs = fcs; + ha->unk3 = 1; + } + + ha = findEmptyHitArea(); + _scrollDownHitArea = ha - _hitAreas; + + if (getGameType() == GType_SIMON1) { + ha->x = 308; + ha->y = 176; + ha->width = 12; + ha->height = 17; + ha->flags = 0x24; + ha->id = 0x7FFC; + ha->layer = 100; + ha->fcs = fcs; + ha->unk3 = 1; + + // Simon1 specific + o_kill_sprite_simon1(128); + loadSprite(0, 1, 128, 0, 0, 14); + } else { + ha->x = 227; + ha->y = 162; + ha->width = 12; + ha->height = 26; + ha->flags = 36; + ha->id = 0x7FFC; + ha->layer = 100; + ha->fcs = fcs; + ha->unk3 = 1; + } +} + + +bool SimonEngine::has_item_childflag_0x10(Item *item) { + Child2 *child = (Child2 *)findChildOfType(item, 2); + return child && (child->avail_props & 0x10) != 0; +} + +uint SimonEngine::item_get_icon_number(Item *item) { + Child2 *child = (Child2 *)findChildOfType(item, 2); + uint offs; + + if (child == NULL || !(child->avail_props & 0x10)) + return 0; + + offs = getOffsetOfChild2Param(child, 0x10); + return child->array[offs]; +} + +void SimonEngine::f10_key() { + HitArea *ha, *dha; + uint count; + uint y_, x_; + byte *dst; + uint b, color; + + _lockWord |= 0x8000; + + if (getGameType() == GType_SIMON2) + color = 236; + else + color = 225; + + uint limit = (getGameType() == GType_SIMON2) ? 200 : 134; + + for (int i = 0; i < 5; i++) { + ha = _hitAreas; + count = ARRAYSIZE(_hitAreas); + + timer_vga_sprites(); + + do { + if (ha->id != 0 && ha->flags & 0x20 && !(ha->flags & 0x40)) { + + dha = _hitAreas; + if (ha->flags & 1) { + while (dha != ha && dha->flags != ha->flags) + ++dha; + if (dha != ha && dha->flags == ha->flags) + continue; + } else { + dha = _hitAreas; + while (dha != ha && dha->item_ptr != ha->item_ptr) + ++dha; + if (dha != ha && dha->item_ptr == ha->item_ptr) + continue; + } + + if (ha->y >= limit || ((getGameType() == GType_SIMON2) && ha->y >= _vgaVar8)) + continue; + + y_ = (ha->height / 2) - 4 + ha->y; + + x_ = (ha->width / 2) - 4 + ha->x - (_scrollX * 8); + + if (x_ >= 311) + continue; + + dst = dx_lock_attached(); + + dst += (((_dxSurfacePitch / 4) * y_) * 4) + x_; + + b = _dxSurfacePitch; + dst[4] = color; + dst[b+1] = color; + dst[b+4] = color; + dst[b+7] = color; + b += _dxSurfacePitch; + dst[b+2] = color; + dst[b+4] = color; + dst[b+6] = color; + b += _dxSurfacePitch; + dst[b+3] = color; + dst[b+5] = color; + b += _dxSurfacePitch; + dst[b] = color; + dst[b+1] = color; + dst[b+2] = color; + dst[b+6] = color; + dst[b+7] = color; + dst[b+8] = color; + b += _dxSurfacePitch; + dst[b+3] = color; + dst[b+5] = color; + b += _dxSurfacePitch; + dst[b+2] = color; + dst[b+4] = color; + dst[b+6] = color; + b += _dxSurfacePitch; + dst[b+1] = color; + dst[b+4] = color; + dst[b+7] = color; + b += _dxSurfacePitch; + dst[b+4] = color; + + dx_unlock_attached(); + } + } while (ha++, --count); + + dx_update_screen_and_palette(); + delay(100); + timer_vga_sprites(); + dx_update_screen_and_palette(); + delay(100); + } + + _lockWord &= ~0x8000; +} + +void SimonEngine::hitarea_stuff() { + HitArea *ha; + uint id; + + _leftButtonDown = 0; + _lastHitArea = 0; + _verbHitArea = 0; + _hitAreaSubjectItem = NULL; + _hitAreaObjectItem = NULL; + + hitarea_proc_1(); + +startOver: + for (;;) { + _lastHitArea = NULL; + _lastHitArea3 = NULL; + + for (;;) { + if (_keyPressed == 35) + f10_key(); + processSpecialKeys(); + if (_lastHitArea3 == (HitArea *) 0xFFFFFFFF) + goto startOver; + if (_lastHitArea3 != 0) + break; + hitarea_stuff_helper(); + delay(100); + } + + ha = _lastHitArea; + + if (ha == NULL) { + } else if (ha->id == 0x7FFB) { + handle_uparrow_hitarea(ha->fcs); + } else if (ha->id == 0x7FFC) { + handle_downarrow_hitarea(ha->fcs); + } else if (ha->id >= 101 && ha->id < 113) { + _verbHitArea = ha->unk3; + handle_verb_hitarea(ha); + _hitAreaUnk4 = 0; + } else { + if ((_verbHitArea != 0 || _hitAreaSubjectItem != ha->item_ptr && ha->flags & 0x80) && + ha->item_ptr) { + if_1:; + _hitAreaSubjectItem = ha->item_ptr; + id = 0xFFFF; + if (ha->flags & 1) + id = ha->flags / 256; + _variableArray[60] = id; + new_current_hitarea(ha); + if (_verbHitArea != 0) + break; + } else { + // else 1 + if (ha->unk3 == 0) { + if (ha->item_ptr) + goto if_1; + } else { + _verbHitArea = ha->unk3 & 0xBFFF; + if (ha->unk3 & 0x4000) { + _hitAreaSubjectItem = ha->item_ptr; + break; + } + if (_hitAreaSubjectItem != NULL) + break; + } + } + } + } + + _needHitAreaRecalc++; +} + +void SimonEngine::hitarea_stuff_helper() { + time_t cur_time; + + if (getGameType() == GType_SIMON1) { + uint subr_id = _variableArray[254]; + if (subr_id != 0) { + Subroutine *sub = getSubroutineByID(subr_id); + if (sub != NULL) { + startSubroutineEx(sub); + startUp_helper_2(); + } + _variableArray[254] = 0; + _runScriptReturn1 = false; + } + } else { + if (_variableArray[254] || _variableArray[249]) { + hitarea_stuff_helper_2(); + } + } + + time(&cur_time); + if ((uint) cur_time != _lastTime) { + _lastTime = cur_time; + if (kickoffTimeEvents()) + startUp_helper_2(); + } +} + +// Simon 2 specific +void SimonEngine::hitarea_stuff_helper_2() { + uint subr_id; + Subroutine *sub; + + subr_id = _variableArray[249]; + if (subr_id != 0) { + sub = getSubroutineByID(subr_id); + if (sub != NULL) { + _variableArray[249] = 0; + startSubroutineEx(sub); + startUp_helper_2(); + } + _variableArray[249] = 0; + } + + subr_id = _variableArray[254]; + if (subr_id != 0) { + sub = getSubroutineByID(subr_id); + if (sub != NULL) { + _variableArray[254] = 0; + startSubroutineEx(sub); + startUp_helper_2(); + } + _variableArray[254] = 0; + } + + _runScriptReturn1 = false; +} + +void SimonEngine::startUp_helper_2() { + if (!_mortalFlag) { + _mortalFlag = true; + showmessage_print_char(0); + _curWindow = 0; + if (_windowArray[0] != 0) { + _textWindow = _windowArray[0]; + showmessage_helper_3(_textWindow->textLength, _textWindow->textMaxLength); + } + _mortalFlag = false; + } +} + +void SimonEngine::pollMouseXY() { + _mouseX = _sdlMouseX; + _mouseY = _sdlMouseY; +} + +void SimonEngine::handle_verb_clicked(uint verb) { + Subroutine *sub; + int result; + + _objectItem = _hitAreaObjectItem; + if (_objectItem == _dummyItem2) { + _objectItem = getItem1Ptr(); + } + if (_objectItem == _dummyItem3) { + _objectItem = derefItem(getItem1Ptr()->parent); + } + + _subjectItem = _hitAreaSubjectItem; + if (_subjectItem == _dummyItem2) { + _subjectItem = getItem1Ptr(); + } + if (_subjectItem == _dummyItem3) { + _subjectItem = derefItem(getItem1Ptr()->parent); + } + + if (_subjectItem) { + _scriptNoun1 = _subjectItem->noun; + _scriptAdj1 = _subjectItem->adjective; + } else { + _scriptNoun1 = -1; + _scriptAdj1 = -1; + } + + if (_objectItem) { + _scriptNoun2 = _objectItem->noun; + _scriptAdj2 = _objectItem->adjective; + } else { + _scriptNoun2 = -1; + _scriptAdj2 = -1; + } + + _scriptVerb = _verbHitArea; + + sub = getSubroutineByID(0); + if (sub == NULL) + return; + + result = startSubroutine(sub); + if (result == -1) + showMessageFormat("I don't understand"); + + _runScriptReturn1 = false; + + sub = getSubroutineByID(100); + if (sub) + startSubroutine(sub); + + if (getGameType() == GType_SIMON2 || getGameType() == GType_FF) + _runScriptReturn1 = false; + + startUp_helper_2(); +} + +TextLocation *SimonEngine::getTextLocation(uint a) { + switch (a) { + case 1: + return &_textLocation1; + case 2: + return &_textLocation2; + case 101: + return &_textLocation3; + case 102: + return &_textLocation4; + default: + error("text, invalid value %d", a); + } + return NULL; +} + +void SimonEngine::o_printStr() { + uint vgaSpriteId = getVarOrByte(); + uint color = getVarOrByte(); + uint string_id = getNextStringID(); + const byte *string_ptr = NULL; + uint speech_id = 0; + TextLocation *tl; + + if (string_id != 0xFFFF) + string_ptr = getStringPtrByID(string_id); + + if (getFeatures() & GF_TALKIE) + speech_id = (uint16)getNextWord(); + + tl = getTextLocation(vgaSpriteId); + + if (_speech && speech_id != 0) + playSpeech(speech_id, vgaSpriteId); + if ((getGameType() == GType_SIMON2) && (getFeatures() & GF_TALKIE) && speech_id == 0) + o_kill_sprite_simon2(2, vgaSpriteId + 2); + + if (string_ptr != NULL && (speech_id == 0 || _subtitles)) + printText(vgaSpriteId, color, (const char *)string_ptr, tl->x, tl->y, tl->width); + +} + +void SimonEngine::o_loadZone(uint vga_res) { + _lockWord |= 0x80; + loadZone(vga_res); + _lockWord &= ~0x80; +} + +void SimonEngine::loadZone(uint vga_res) { + VgaPointersEntry *vpe; + + CHECK_BOUNDS(vga_res, _vgaBufferPointers); + + vpe = _vgaBufferPointers + vga_res; + if (vpe->vgaFile1 != NULL) + return; + + vpe->vgaFile1 = read_vga_from_datfile_2(vga_res * 2, 1); + vpe->vgaFile2 = read_vga_from_datfile_2(vga_res * 2 + 1, 2); + vpe->sfxFile = read_vga_from_datfile_2(vga_res * 2, 3); + +} + +byte *SimonEngine::setup_vga_destination(uint32 size) { + byte *dest, *end; + + _videoVar4 = 0; + + for (;;) { + dest = _vgaBufFreeStart; + + end = dest + size; + + if (end >= _vgaBufEnd) { + _vgaBufFreeStart = _vgaBufStart; + } else { + _videoVar5 = false; + vga_buf_unk_proc3(end); + if (_videoVar5) + continue; + vga_buf_unk_proc1(end); + if (_videoVar5) + continue; + delete_memptr_range(end); + _vgaBufFreeStart = end; + return dest; + } + } +} + +void SimonEngine::setup_vga_file_buf_pointers() { + byte *alloced; + + alloced = (byte *)malloc(VGA_MEM_SIZE); + + _vgaBufFreeStart = alloced; + _vgaBufStart = alloced; + _vgaFileBufOrg = alloced; + _vgaFileBufOrg2 = alloced; + _vgaBufEnd = alloced + VGA_MEM_SIZE; +} + +void SimonEngine::vga_buf_unk_proc3(byte *end) { + VgaPointersEntry *vpe; + + if (_videoVar7 == 0xFFFF) + return; + + if (_videoVar4 == 2) + error("vga_buf_unk_proc3: _videoVar4 == 2"); + + vpe = &_vgaBufferPointers[_videoVar7]; + + if (_vgaBufFreeStart <= vpe->vgaFile1 && end >= vpe->vgaFile1 || + _vgaBufFreeStart <= vpe->vgaFile2 && end >= vpe->vgaFile2) { + _videoVar5 = 1; + _videoVar4++; + _vgaBufFreeStart = vpe->vgaFile1 + 0x5000; + } else { + _videoVar5 = 0; + } +} + +void SimonEngine::vga_buf_unk_proc1(byte *end) { + VgaSprite *vsp; + if (_lockWord & 0x20) + return; + + for (vsp = _vgaSprites; vsp->id; vsp++) { + vga_buf_unk_proc2(vsp->fileId, end); + if (_videoVar5 == true) + return; + } +} + +void SimonEngine::delete_memptr_range(byte *end) { + uint count = ARRAYSIZE(_vgaBufferPointers); + VgaPointersEntry *vpe = _vgaBufferPointers; + do { + if (_vgaBufFreeStart <= vpe->vgaFile1 && end >= vpe->vgaFile1 || + _vgaBufFreeStart <= vpe->vgaFile2 && end >= vpe->vgaFile2) { + vpe->sfxFile = NULL; + vpe->vgaFile1 = NULL; + vpe->vgaFile2 = NULL; + } + + } while (++vpe, --count); +} + +void SimonEngine::vga_buf_unk_proc2(uint a, byte *end) { + VgaPointersEntry *vpe; + + vpe = &_vgaBufferPointers[a]; + + if (_vgaBufFreeStart <= vpe->vgaFile1 && end >= vpe->vgaFile1 || + _vgaBufFreeStart <= vpe->vgaFile2 && end >= vpe->vgaFile2) { + _videoVar5 = true; + _videoVar4++; + _vgaBufFreeStart = vpe->vgaFile1 + 0x5000; + } else { + _videoVar5 = false; + } +} + +void SimonEngine::o_unloadZone(uint a) { + VgaPointersEntry *vpe; + + vpe = &_vgaBufferPointers[a]; + + vpe->sfxFile = NULL; + vpe->vgaFile1 = NULL; + vpe->vgaFile2 = NULL; +} + +void SimonEngine::o_set_video_mode(uint mode, uint vga_res) { + if (mode == 4) + vc29_stopAllSounds(); + + if (_lockWord & 0x10) + error("o_set_video_mode_ex: _lockWord & 0x10"); + + set_video_mode_internal(mode, vga_res); +} + +void SimonEngine::set_video_mode_internal(uint mode, uint vga_res_id) { + uint num, num_lines; + VgaPointersEntry *vpe; + byte *bb, *b; + // uint16 count; + const byte *vc_ptr_org; + + _windowNum = mode; + _lockWord |= 0x20; + + if (vga_res_id == 0) { + + if (getGameType() == GType_SIMON1) { + _unkPalFlag = true; + } else { + _dxUse3Or4ForLock = true; + _vgaVar6 = true; + } + } + + _vgaCurFile2 = num = vga_res_id / 100; + + for (;;) { + vpe = &_vgaBufferPointers[num]; + + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + _curSfxFile = vpe->sfxFile; + + if (vpe->vgaFile1 != NULL) + break; + + loadZone(num); + } + + // ensure flipping complete + + bb = _curVgaFile1; + + if (getGameType() == GType_FF) { + b = bb + READ_LE_UINT16(&((VgaFileHeader_Feeble *) bb)->hdr2_start); + //count = READ_LE_UINT16(&((VgaFileHeader2_Feeble *) b)->imageCount); + b = bb + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) b)->imageTable); + + while (READ_LE_UINT16(&((ImageHeader_Feeble *) b)->id) != vga_res_id) + b += sizeof(ImageHeader_Feeble); + } else { + b = bb + READ_BE_UINT16(&((VgaFileHeader_Simon *) bb)->hdr2_start); + //count = READ_BE_UINT16(&((VgaFileHeader2_Simon *) b)->imageCount); + b = bb + READ_BE_UINT16(&((VgaFileHeader2_Simon *) b)->imageTable); + + while (READ_BE_UINT16(&((ImageHeader_Simon *) b)->id) != vga_res_id) + b += sizeof(ImageHeader_Simon); + } + + if ((getGameType() == GType_SIMON1) && vga_res_id == 16300) { + dx_clear_attached_from_top(134); + _usePaletteDelay = true; + } else { + _scrollX = 0; + _scrollXMax = 0; + _scrollCount = 0; + _scrollFlag = 0; + _scrollHeight = 134; + if (_variableArray[34] != -1) + _variableArray[251] = 0; + } + + vc_ptr_org = _vcPtr; + + if (getGameType() == GType_FF) { + _vcPtr = _curVgaFile1 + READ_LE_UINT16(&((ImageHeader_Feeble *) b)->scriptOffs); + } else { + _vcPtr = _curVgaFile1 + READ_BE_UINT16(&((ImageHeader_Simon *) b)->scriptOffs); + } + + //dump_vga_script(_vcPtr, num, vga_res_id); + run_vga_script(); + _vcPtr = vc_ptr_org; + + + if (getGameType() == GType_SIMON1) { + // Allow one section of Simon the Sorcerer 1 introduction to be displayed + // in lower half of screen + if (_subroutine == 2923 || _subroutine == 2926) + num_lines = 200; + else + num_lines = _windowNum == 4 ? 134 : 200; + + dx_copy_from_attached_to_2(0, 0, _screenWidth, num_lines); + dx_copy_from_attached_to_3(num_lines); + + _syncFlag2 = 1; + _timer5 = 0; + } else { + if (!_dxUse3Or4ForLock) { + if (getGameType() == GType_FF) + num_lines = 480; + else + num_lines = _windowNum == 4 ? 134 : 200; + + _vgaVar8 = num_lines; + dx_copy_from_attached_to_2(0, 0, _screenWidth, num_lines); + dx_copy_from_attached_to_3(num_lines); + _syncFlag2 = 1; + } + _dxUse3Or4ForLock = false; + } + + _lockWord &= ~0x20; + + if (getGameType() == GType_SIMON1) { + if (_unkPalFlag) { + _unkPalFlag = false; + while (_paletteColorCount != 0) { + delay(10); + } + } + } +} + +void SimonEngine::o_fadeToBlack() { + uint i; + + memcpy(_videoBuf1, _paletteBackup, 1024); + + i = NUM_PALETTE_FADEOUT; + do { + palette_fadeout((uint32 *)_videoBuf1, 32); + palette_fadeout((uint32 *)_videoBuf1 + 32 + 16, 144); + palette_fadeout((uint32 *)_videoBuf1 + 32 + 16 + 144 + 16, 48); + + _system->setPalette(_videoBuf1, 0, 256); + if (_fade) + _system->updateScreen(); + delay(5); + } while (--i); + + memcpy(_paletteBackup, _videoBuf1, 1024); + memcpy(_palette, _videoBuf1, 1024); +} + +void SimonEngine::delete_vga_timer(VgaTimerEntry * vte) { + _lockWord |= 1; + + if (vte + 1 <= _nextVgaTimerToProcess) { + _nextVgaTimerToProcess--; + } + + do { + memcpy(vte, vte + 1, sizeof(VgaTimerEntry)); + vte++; + } while (vte->delay); + + _lockWord &= ~1; +} + +void SimonEngine::expire_vga_timers() { + VgaTimerEntry *vte = _vgaTimerList; + + _vgaTickCounter++; + + while (vte->delay) { + if (!--vte->delay) { + uint16 cur_file = vte->cur_vga_file; + uint16 cur_unk = vte->sprite_id; + const byte *script_ptr = vte->script_pointer; + + _nextVgaTimerToProcess = vte + 1; + delete_vga_timer(vte); + + if ((getGameType() == GType_SIMON2) && script_ptr == NULL) { + // special scroll timer + scroll_timeout(); + } else { + vcResumeSprite(script_ptr, cur_file, cur_unk); + } + vte = _nextVgaTimerToProcess; + } else { + vte++; + } + } +} + +// Simon2 specific +void SimonEngine::scroll_timeout() { + if (_scrollCount == 0) + return; + + if (_scrollCount < 0) { + if (_scrollFlag != -1) { + _scrollFlag = -1; + if (++_scrollCount == 0) + return; + } + } else { + if (_scrollFlag != 1) { + _scrollFlag = 1; + if (--_scrollCount == 0) + return; + } + } + + add_vga_timer(6, NULL, 0, 0); +} + +void SimonEngine::vcResumeSprite(const byte *code_ptr, uint16 cur_file, uint16 cur_sprite) { + VgaPointersEntry *vpe; + + _vgaCurSpriteId = cur_sprite; + + _vgaCurFileId = cur_file; + _vgaCurFile2 = cur_file; + vpe = &_vgaBufferPointers[cur_file]; + + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + _curSfxFile = vpe->sfxFile; + + _vcPtr = code_ptr; + + run_vga_script(); +} + +void SimonEngine::add_vga_timer(uint num, const byte *code_ptr, uint cur_sprite, uint cur_file) { + VgaTimerEntry *vte; + + // When Simon talks to the Golum about stew in French version of + // Simon the Sorcerer 1 the code_ptr is at wrong location for + // sprite 200. This was a bug in the original game, which + // caused several glitches in this scene. + // We work around the problem by correcting the code_ptr for sprite + // 200 in this scene, if it is wrong. + if (getGameType() == GType_SIMON1 && _language == Common::FR_FRA && + (code_ptr - _vgaBufferPointers[cur_file].vgaFile1 == 4) && (cur_sprite == 200) && (cur_file == 2)) + code_ptr += 0x66; + + _lockWord |= 1; + + for (vte = _vgaTimerList; vte->delay; vte++) { + } + + vte->delay = num; + vte->script_pointer = code_ptr; + vte->sprite_id = cur_sprite; + vte->cur_vga_file = cur_file; + + _lockWord &= ~1; +} + +void SimonEngine::o_mouseOn() { + if (getGameType() == GType_SIMON2 && _bitArray[4] & 0x8000) + _mouseCursor = 0; + _mouseHideCount = 0; +} + +void SimonEngine::o_mouseOff() { + _lockWord |= 0x8000; + vc34_setMouseOff(); + _lockWord &= ~0x8000; +} + +void SimonEngine::o_waitForSync(uint a) { + _vgaWaitFor = a; + _timer1 = 0; + _exitCutscene = false; + _skipSpeech = false; + while (_vgaWaitFor != 0) { + if (_skipSpeech && (getGameType() == GType_SIMON2 || getGameType() == GType_FF)) { + if (_vgaWaitFor == 200 && !vcGetBit(14)) { + skipSpeech(); + break; + } + } else if (_exitCutscene) { + if (vcGetBit(9)) { + endCutscene(); + break; + } + } else { + processSpecialKeys(); + } + + delay(10); + + if (getGameType() == GType_SIMON2) { + if (_timer1 >= 1000) { + warning("wait timed out"); + break; + } + } else if (_timer1 >= 500) { + warning("wait timed out"); + break; + } + + } +} + +void SimonEngine::skipSpeech() { + _sound->stopVoice(); + if (!(_bitArray[1] & 0x1000)) { + _bitArray[0] |= 0x4000; + _variableArray[100] = 5; + loadSprite(4, 1, 30, 0, 0, 0); + o_waitForSync(130); + o_kill_sprite_simon2(2, 1); + } +} + +void SimonEngine::timer_vga_sprites() { + VgaSprite *vsp; + VgaPointersEntry *vpe; + const byte *vc_ptr_org = _vcPtr; + uint16 params[5]; // parameters to vc10 + + if (_paletteFlag == 2) + _paletteFlag = 1; + + if (getGameType() == GType_SIMON2 && _scrollFlag) { + timer_vga_sprites_helper(); + } + + vsp = _vgaSprites; + + while (vsp->id != 0) { + vsp->windowNum &= 0x7FFF; + + vpe = &_vgaBufferPointers[vsp->fileId]; + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + _curSfxFile = vpe->sfxFile; + _windowNum = vsp->windowNum; + _vgaCurSpriteId = vsp->id; + + params[0] = readUint16Wrapper(&vsp->image); + params[1] = readUint16Wrapper(&vsp->palette); + params[2] = readUint16Wrapper(&vsp->x); + params[3] = readUint16Wrapper(&vsp->y); + + if (getGameType() == GType_SIMON1) { + params[4] = READ_BE_UINT16(&vsp->flags); + } else { + *(byte *)(¶ms[4]) = (byte)vsp->flags; + } + + _vcPtr = (const byte *)params; + vc10_draw(); + + vsp++; + } + + if (_drawImagesDebug) + memset(_sdl_buf_attached, 0, _screenWidth * _screenHeight); + + _updateScreen++; + _vcPtr = vc_ptr_org; +} + +void SimonEngine::timer_vga_sprites_helper() { + byte *dst = dx_lock_2(); + const byte *src; + uint x; + + if (_scrollFlag < 0) { + memmove(dst + 8, dst, _screenWidth * _scrollHeight - 8); + } else { + memmove(dst, dst + 8, _screenWidth * _scrollHeight - 8); + } + + x = _scrollX - 1; + + if (_scrollFlag > 0) { + dst += _screenWidth - 8; + x += 41; + } + + src = _scrollImage + x * 4; + decodeStripA(dst, src + READ_BE_UINT32(src), _scrollHeight); + + dx_unlock_2(); + + + memcpy(_sdl_buf_attached, _sdl_buf, _screenWidth * _screenHeight); + dx_copy_from_attached_to_3(_scrollHeight); + + + _scrollX += _scrollFlag; + + vcWriteVar(251, _scrollX); + + _scrollFlag = 0; +} + +void SimonEngine::timer_vga_sprites_2() { + VgaSprite *vsp; + VgaPointersEntry *vpe; + const byte *vc_ptr_org = _vcPtr; + uint16 params[5]; // parameters to vc10_draw + + if (_paletteFlag == 2) + _paletteFlag = 1; + + vsp = _vgaSprites; + while (vsp->id != 0) { + vsp->windowNum &= 0x7FFF; + + vpe = &_vgaBufferPointers[vsp->fileId]; + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + _curSfxFile = vpe->sfxFile; + _windowNum = vsp->windowNum; + _vgaCurSpriteId = vsp->id; + + if (vsp->image) + fprintf(_dumpFile, "id:%5d image:%3d base-color:%3d x:%3d y:%3d flags:%x\n", + vsp->id, vsp->image, vsp->palette, vsp->x, vsp->y, vsp->flags); + params[0] = readUint16Wrapper(&vsp->image); + params[1] = readUint16Wrapper(&vsp->palette); + params[2] = readUint16Wrapper(&vsp->x); + params[3] = readUint16Wrapper(&vsp->y); + params[4] = readUint16Wrapper(&vsp->flags); + _vcPtr = (const byte *)params; + vc10_draw(); + + vsp++; + } + + _updateScreen++; + _vcPtr = vc_ptr_org; +} + +void SimonEngine::timer_proc1() { + _timer4++; + + if (_lockWord & 0x80E9 || _lockWord & 2) + return; + + _timer1++; + + _lockWord |= 2; + + if (!(_lockWord & 0x10)) { + expire_vga_timers(); + expire_vga_timers(); + _syncFlag2 ^= 1; + _cepeFlag ^= 1; + if (!_cepeFlag) + expire_vga_timers(); + + if (_mouseHideCount != 0 && _syncFlag2) { + _lockWord &= ~2; + return; + } + } + + timer_vga_sprites(); + if (_drawImagesDebug) + timer_vga_sprites_2(); + + if (_copyPartialMode == 1) { + dx_copy_from_2_to_attached(80, 46, 208 - 80, 94 - 46); + } + + if (_copyPartialMode == 2) { + // copy partial from attached to 2 + dx_copy_from_attached_to_2(176, 61, _screenWidth - 176, 134 - 61); + _copyPartialMode = 0; + } + + if (_updateScreen) { + handle_mouse_moved(); + dx_update_screen_and_palette(); + _updateScreen = false; + } + + _lockWord &= ~2; +} + +void SimonEngine::timer_callback() { + if (_timer5 != 0) { + _syncFlag2 = true; + _timer5--; + } else { + timer_proc1(); + } +} + +void SimonEngine::fcs_setTextColor(FillOrCopyStruct *fcs, uint value) { + fcs->text_color = value; +} + +void SimonEngine::o_vga_reset() { + _lockWord |= 0x8000; + vc27_resetSprite(); + _lockWord &= ~0x8000; +} + +bool SimonEngine::itemIsSiblingOf(uint16 a) { + Item *item; + + CHECK_BOUNDS(a, _vcItemArray); + + item = _vcItemArray[a]; + if (item == NULL) + return true; + + return getItem1Ptr()->parent == item->parent; +} + +bool SimonEngine::itemIsParentOf(uint16 a, uint16 b) { + Item *item_a, *item_b; + + CHECK_BOUNDS(a, _vcItemArray); + CHECK_BOUNDS(b, _vcItemArray); + + item_a = _vcItemArray[a]; + item_b = _vcItemArray[b]; + + if (item_a == NULL || item_b == NULL) + return true; + + return derefItem(item_a->parent) == item_b; +} + +bool SimonEngine::vc_maybe_skip_proc_1(uint16 a, int16 b) { + Item *item; + + CHECK_BOUNDS(a, _vcItemArray); + + item = _vcItemArray[a]; + if (item == NULL) + return true; + return item->state == b; +} + +// OK +void SimonEngine::closeWindow(uint a) { + if (_windowArray[a] == NULL) + return; + removeIconArray(a); + video_copy_if_flag_0x8_c(_windowArray[a]); + _windowArray[a] = NULL; + if (_curWindow == a) { + _textWindow = NULL; + changeWindow(0); + } +} + +// OK +void SimonEngine::changeWindow(uint a) { + a &= 7; + + if (_windowArray[a] == NULL || _curWindow == a) + return; + + _curWindow = a; + showmessage_print_char(0); + _textWindow = _windowArray[a]; + + showmessage_helper_3(_textWindow->textLength, _textWindow->textMaxLength); +} + +// OK +FillOrCopyStruct *SimonEngine::openWindow(uint x, uint y, uint w, uint h, uint flags, uint fill_color, uint text_color) { + FillOrCopyStruct *fcs; + + fcs = _fcs_list; + while (fcs->mode != 0) + fcs++; + + fcs->mode = 2; + fcs->x = x; + fcs->y = y; + fcs->width = w; + fcs->height = h; + fcs->flags = flags; + fcs->fill_color = fill_color; + fcs->text_color = text_color; + fcs->textColumn = 0; + fcs->textRow = 0; + fcs->textColumnOffset = 0; + fcs->textMaxLength = fcs->width * 8 / 6; // characters are 6 pixels + return fcs; +} + +Item *SimonEngine::derefItem(uint item) { + if (item >= _itemArraySize) + error("derefItem: invalid item %d", item); + return _itemArrayPtr[item]; +} + +uint SimonEngine::itemPtrToID(Item *id) { + uint i; + for (i = 0; i != _itemArraySize; i++) + if (_itemArrayPtr[i] == id) + return i; + error("itemPtrToID: not found"); + return 0; +} + +void SimonEngine::o_pathfind(int x, int y, uint var_1, uint var_2) { + const uint16 *p; + uint i, j; + uint prev_i; + uint x_diff, y_diff; + uint best_i = 0, best_j = 0, best_dist = 0xFFFFFFFF; + + if (getGameType() == GType_SIMON2) { + x += _scrollX * 8; + } + + int end = (getGameType() == GType_FF) ? 9999 : 999; + prev_i = 21 - _variableArray[12]; + for (i = 20; i != 0; --i) { + p = (const uint16 *)_pathFindArray[20 - i]; + if (!p) + continue; + for (j = 0; readUint16Wrapper(&p[0]) != end; j++, p += 2) { + x_diff = abs((int)(readUint16Wrapper(&p[0]) - x)); + y_diff = abs((int)(readUint16Wrapper(&p[1]) - 12 - y)); + + if (x_diff < y_diff) { + x_diff /= 4; + y_diff *= 4; + } + x_diff += y_diff /= 4; + + if (x_diff < best_dist || x_diff == best_dist && prev_i == i) { + best_dist = x_diff; + best_i = 21 - i; + best_j = j; + } + } + } + + _variableArray[var_1] = best_i; + _variableArray[var_2] = best_j; +} + +// ok +void SimonEngine::removeIconArray(uint fcs_index) { + FillOrCopyStruct *fcs; + uint16 fcsunk1; + uint16 i; + + fcs = _windowArray[fcs_index & 7]; + fcsunk1 = _curWindow; + + if (fcs == NULL || fcs->fcs_data == NULL) + return; + + changeWindow(fcs_index); + fcs_putchar(12); + changeWindow(fcsunk1); + + for (i = 0; fcs->fcs_data->e[i].item != NULL; i++) { + delete_hitarea_by_index(fcs->fcs_data->e[i].hit_area); + } + + if (fcs->fcs_data->upArrow != -1) { + delete_hitarea_by_index(fcs->fcs_data->upArrow); + } + + if (fcs->fcs_data->downArrow != -1) { + delete_hitarea_by_index(fcs->fcs_data->downArrow); + if (getGameType() == GType_SIMON1) + removeArrows(fcs, fcs_index); + } + + free(fcs->fcs_data); + fcs->fcs_data = NULL; + + _fcsData1[fcs_index] = 0; + _fcsData2[fcs_index] = 0; +} + +// ok +void SimonEngine::removeArrows(FillOrCopyStruct *fcs, uint fcs_index) { + o_kill_sprite_simon1(128); +} + +void SimonEngine::delete_hitarea_by_index(uint index) { + CHECK_BOUNDS(index, _hitAreas); + _hitAreas[index].flags = 0; +} + +// ok +void SimonEngine::fcs_putchar(uint a) { + if (_textWindow != _windowArray[0]) + video_putchar(_textWindow, a); +} + +// ok +void SimonEngine::video_fill_or_copy_from_3_to_2(FillOrCopyStruct *fcs) { + if (fcs->flags & 0x10) + copy_img_from_3_to_2(fcs); + else + video_erase(fcs); + + fcs->textColumn = 0; + fcs->textRow = 0; + fcs->textColumnOffset = 0; + fcs->textLength = 0; +} + +// ok +void SimonEngine::copy_img_from_3_to_2(FillOrCopyStruct *fcs) { + _lockWord |= 0x8000; + + if (getGameType() == GType_SIMON1) { + dx_copy_rgn_from_3_to_2(fcs->y + fcs->height * 8 + ((fcs == _windowArray[2]) ? 1 : 0), (fcs->x + fcs->width) * 8, fcs->y, fcs->x * 8); + } else { + if (_vgaVar6 && _windowArray[2] == fcs) { + fcs = _windowArray[6]; + _vgaVar6 = 0; + } + + dx_copy_rgn_from_3_to_2(fcs->y + fcs->height * 8, (fcs->x + fcs->width) * 8, fcs->y, fcs->x * 8); + } + + _lockWord &= ~0x8000; +} + +void SimonEngine::video_erase(FillOrCopyStruct *fcs) { + byte *dst; + uint h; + + _lockWord |= 0x8000; + + dst = dx_lock_2(); + dst += _dxSurfacePitch * fcs->y + fcs->x * 8; + + h = fcs->height * 8; + do { + memset(dst, fcs->fill_color, fcs->width * 8); + dst += _dxSurfacePitch; + } while (--h); + + dx_unlock_2(); + _lockWord &= ~0x8000; +} + +VgaSprite *SimonEngine::findCurSprite() { + VgaSprite *vsp = _vgaSprites; + while (vsp->id) { + if (getGameType() == GType_SIMON1) { + if (vsp->id == _vgaCurSpriteId) + break; + } else { + if (vsp->id == _vgaCurSpriteId && vsp->fileId == _vgaCurFileId) + break; + } + vsp++; + } + return vsp; +} + +bool SimonEngine::isSpriteLoaded(uint16 id, uint16 fileId) { + VgaSprite *vsp = _vgaSprites; + while (vsp->id) { + if (getGameType() == GType_SIMON1) { + if (vsp->id == id) + return true; + } else { + if (vsp->id == id && vsp->fileId == fileId) + return true; + } + vsp++; + } + return false; +} + +void SimonEngine::processSpecialKeys() { + switch (_keyPressed) { + case 27: // escape + _exitCutscene = true; + break; + case 59: // F1 + if (getGameType() == GType_SIMON1) { + vcWriteVar(5, 40); + } else { + vcWriteVar(5, 50); + } + vcWriteVar(86, 0); + break; + case 60: // F2 + if (getGameType() == GType_SIMON1) { + vcWriteVar(5, 60); + } else { + vcWriteVar(5, 75); + } + vcWriteVar(86, 1); + break; + case 61: // F3 + if (getGameType() == GType_SIMON1) { + vcWriteVar(5, 100); + } else { + vcWriteVar(5, 125); + } + vcWriteVar(86, 2); + break; + case 63: // F5 + if (getGameType() == GType_SIMON2 || getGameType() == GType_FF) + _exitCutscene = true; + break; + case 'p': + pause(); + break; + case 't': + if ((getGameType() == GType_SIMON2 && getFeatures() & GF_TALKIE) || ( getFeatures() & GF_TALKIE && _language > 1)) + if (_speech) + _subtitles ^= 1; + break; + case 'v': + if ((getGameType() == GType_SIMON2) && (getFeatures() & GF_TALKIE)) + if (_subtitles) + _speech ^= 1; + case '+': + midi.set_volume(midi.get_volume() + 16); + break; + case '-': + midi.set_volume(midi.get_volume() - 16); + break; + case 'm': + midi.pause(_musicPaused ^= 1); + break; + case 's': + if (getGameId() == GID_SIMON1DOS) + midi._enable_sfx ^= 1; + else + _sound->effectsPause(_effectsPaused ^= 1); + break; + case 'b': + _sound->ambientPause(_ambientPaused ^= 1); + break; + case 'r': + if (_debugMode) + _startMainScript ^= 1; + break; + case 'o': + if (_debugMode) + _continousMainScript ^= 1; + break; + case 'a': + if (_debugMode) + _startVgaScript ^= 1; + break; + case 'g': + if (_debugMode) + _continousVgaScript ^= 1; + break; + case 'i': + if (_debugMode) + _drawImagesDebug ^= 1; + break; + case 'd': + if (_debugMode) + _dumpImages ^=1; + break; + } + + _keyPressed = 0; +} + +void SimonEngine::pause() { + _keyPressed = 1; + _pause = 1; + bool ambient_status = _ambientPaused; + bool music_status = _musicPaused; + + midi.pause(true); + _sound->ambientPause(true); + while (_pause) { + delay(1); + if (_keyPressed == 'p') + _pause = 0; + } + midi.pause(music_status); + _sound->ambientPause(ambient_status); + +} + +void SimonEngine::video_toggle_colors(HitArea * ha, byte a, byte b, byte c, byte d) { + byte *src, color; + uint w, h, i; + + _lockWord |= 0x8000; + src = dx_lock_2() + ha->y * _dxSurfacePitch + ha->x; + + w = ha->width; + h = ha->height; + + // Works around bug in original Simon the Sorcerer 2 + // Animations continue in background when load/save dialog is open + // often causing the savegame name highlighter to be cut short + if (!(h > 0 && w > 0 && ha->x + w <= _screenWidth && ha->y + h <= _screenHeight)) { + debug(1,"Invalid coordinates in video_toggle_colors (%d,%d,%d,%d)", ha->x, ha->y, ha->width, ha->height); + _lockWord &= ~0x8000; + return; + } + + do { + for (i = 0; i != w; ++i) { + color = src[i]; + if (a >= color && b < color) { + if (c >= color) + color += d; + else + color -= d; + src[i] = color; + } + } + src += _dxSurfacePitch; + } while (--h); + + + dx_unlock_2(); + _lockWord &= ~0x8000; +} + +void SimonEngine::video_copy_if_flag_0x8_c(FillOrCopyStruct *fcs) { + if (fcs->flags & 8) + copy_img_from_3_to_2(fcs); + fcs->mode = 0; +} + +void SimonEngine::loadSprite(uint windowNum, uint fileId, uint vgaSpriteId, uint x, uint y, uint palette) { + VgaSprite *vsp; + VgaPointersEntry *vpe; + byte *p, *pp; + uint count; + + _lockWord |= 0x40; + + if (isSpriteLoaded(vgaSpriteId, fileId)) { + _lockWord &= ~0x40; + return; + } + + vsp = _vgaSprites; + while (vsp->id != 0) + vsp++; + + vsp->windowNum = windowNum; + vsp->priority = 0; + vsp->flags = 0; + + vsp->y = y; + vsp->x = x; + vsp->image = 0; + vsp->palette = palette; + vsp->id = vgaSpriteId; + if (getGameType() == GType_SIMON1) + vsp->fileId = fileId = vgaSpriteId / 100; + else + vsp->fileId = fileId; + + + for (;;) { + vpe = &_vgaBufferPointers[fileId]; + _vgaCurFile2 = fileId; + _curVgaFile1 = vpe->vgaFile1; + if (vpe->vgaFile1 != NULL) + break; + loadZone(fileId); + } + + pp = _curVgaFile1; + if (getGameType() == GType_FF) { + p = pp + READ_LE_UINT16(&((VgaFileHeader_Feeble *) pp)->hdr2_start); + count = READ_LE_UINT16(&((VgaFileHeader2_Feeble *) p)->animationCount); + p = pp + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) p)->animationTable); + } else { + p = pp + READ_BE_UINT16(&((VgaFileHeader_Simon *) pp)->hdr2_start); + count = READ_BE_UINT16(&((VgaFileHeader2_Simon *) p)->animationCount); + p = pp + READ_BE_UINT16(&((VgaFileHeader2_Simon *) p)->animationTable); + } + + for (;;) { + if (getGameType() == GType_FF) { + if (READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->id) == vgaSpriteId) { + if (_startVgaScript) + dump_vga_script(pp + READ_LE_UINT16(&((AnimationHeader_Feeble*)p)->scriptOffs), fileId, vgaSpriteId); + + add_vga_timer(VGA_DELAY_BASE, pp + READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->scriptOffs), vgaSpriteId, fileId); + break; + } + p += sizeof(AnimationHeader_Feeble); + } else { + if (READ_BE_UINT16(&((AnimationHeader_Simon *) p)->id) == vgaSpriteId) { + if (_startVgaScript) + dump_vga_script(pp + READ_BE_UINT16(&((AnimationHeader_Simon*)p)->scriptOffs), fileId, vgaSpriteId); + + add_vga_timer(VGA_DELAY_BASE, pp + READ_BE_UINT16(&((AnimationHeader_Simon *) p)->scriptOffs), vgaSpriteId, fileId); + break; + } + p += sizeof(AnimationHeader_Simon); + } + + if (!--count) { + vsp->id = 0; + break; + } + } + + _lockWord &= ~0x40; +} + +void SimonEngine::playSpeech(uint speech_id, uint vgaSpriteId) { + if (getGameType() == GType_SIMON1) { + if (speech_id == 9999) { + if (_subtitles) + return; + if (!(_bitArray[0] & 0x4000) && !(_bitArray[1] & 0x1000)) { + _bitArray[0] |= 0x4000; + _variableArray[100] = 15; + loadSprite(4, 1, 130, 0, 0, 0); + o_waitForSync(130); + } + _skipVgaWait = true; + } else { + if (_subtitles && _scriptVar2) { + loadSprite(4, 2, 204, 0, 0, 0); + o_waitForSync(204); + o_kill_sprite_simon1(204); + } + o_kill_sprite_simon1(vgaSpriteId + 201); + _sound->playVoice(speech_id); + loadSprite(4, 2, vgaSpriteId + 201, 0, 0, 0); + } + } else { + if (speech_id == 0xFFFF) { + if (_subtitles) + return; + if (!(_bitArray[0] & 0x4000) && !(_bitArray[1] & 0x1000)) { + _bitArray[0] |= 0x4000; + _variableArray[100] = 5; + loadSprite(4, 1, 30, 0, 0, 0); + o_waitForSync(130); + } + _skipVgaWait = true; + } else { + if (_subtitles && _language != Common::HB_ISR) { + _sound->playVoice(speech_id); + return; + } else if (_subtitles && _scriptVar2) { + loadSprite(4, 2, 5, 0, 0, 0); + o_waitForSync(205); + o_kill_sprite_simon2(2,5); + } + + o_kill_sprite_simon2(2, vgaSpriteId + 2); + _sound->playVoice(speech_id); + loadSprite(4, 2, vgaSpriteId + 2, 0, 0, 0); + } + } +} + +void SimonEngine::printText(uint vgaSpriteId, uint color, const char *string, int16 x, int16 y, int16 width) { + char convertedString[320]; + char *convertedString2 = convertedString; + int16 height, talkDelay; + int stringLength = strlen(string); + int padding, lettersPerRow, lettersPerRowJustified; + const int textHeight = 10; + + height = textHeight; + lettersPerRow = width / 6; + lettersPerRowJustified = stringLength / (stringLength / lettersPerRow + 1) + 1; + + talkDelay = (stringLength + 3) / 3; + if ((getGameType() == GType_SIMON1) && (getFeatures() & GF_TALKIE)) { + if (_variableArray[141] == 0) + _variableArray[141] = 9; + _variableArray[85] = _variableArray[141] * talkDelay; + } else { + if (_variableArray[86] == 0) + talkDelay /= 2; + if (_variableArray[86] == 2) + talkDelay *= 2; + _variableArray[85] = talkDelay * 5; + } + + assert(stringLength > 0); + while (stringLength > 0) { + int pos = 0; + if (stringLength > lettersPerRow) { + int removeLastWord = 0; + if (lettersPerRow > lettersPerRowJustified) { + pos = lettersPerRowJustified; + while (string[pos] != ' ') + pos++; + if (pos > lettersPerRow) + removeLastWord = 1; + } + if (lettersPerRow <= lettersPerRowJustified || removeLastWord) { + pos = lettersPerRow; + while (string[pos] != ' ' && pos > 0) + pos--; + } + height += textHeight; + y -= textHeight; + } else + pos = stringLength; + padding = (lettersPerRow - pos) % 2 ? + (lettersPerRow - pos) / 2 + 1 : (lettersPerRow - pos) / 2; + while (padding--) + *convertedString2++ = ' '; + stringLength -= pos; + while (pos--) + *convertedString2++ = *string++; + *convertedString2++ = '\n'; + string++; // skip space + stringLength--; // skip space + } + *(convertedString2 - 1) = '\0'; + + if (getGameType() == GType_SIMON1) + o_kill_sprite_simon1(vgaSpriteId + 199); + else + o_kill_sprite_simon2(2, vgaSpriteId); + + color = color * 3 + 192; + if (getPlatform() == Common::kPlatformAmiga) + render_string_amiga(vgaSpriteId, color, width, height, convertedString); + else + render_string(vgaSpriteId, color, width, height, convertedString); + + int b = 4; + if (!(_bitArray[8] & 0x20)) + b = 3; + + x /= 8; + if (y < 2) + y = 2; + + if (getGameType() == GType_SIMON1) + loadSprite(b, 2, vgaSpriteId + 199, x, y, 12); + else + loadSprite(b, 2, vgaSpriteId, x, y, 12); +} + +// Thanks to Stuart Caie for providing the original +// C conversion upon which this decruncher is based. + +#define SD_GETBIT(var) do { \ + if (!bits--) { \ + s -= 4; \ + if (s < src) \ + return false; \ + bb = READ_BE_UINT32(s); \ + bits = 31; \ + } \ + (var) = bb & 1; \ + bb >>= 1; \ +}while (0) + +#define SD_GETBITS(var, nbits) do { \ + bc = (nbits); \ + (var) = 0; \ + while (bc--) { \ + (var) <<= 1; \ + SD_GETBIT(bit); \ + (var) |= bit; \ + } \ +}while (0) + +#define SD_TYPE_LITERAL (0) +#define SD_TYPE_MATCH (1) + +static bool decrunchFile(byte *src, byte *dst, uint32 size) { + byte *s = src + size - 4; + uint32 destlen = READ_BE_UINT32 (s); + uint32 bb, x, y; + byte *d = dst + destlen; + byte bc, bit, bits, type; + + // Initialize bit buffer. + s -= 4; + bb = x = READ_BE_UINT32 (s); + bits = 0; + do { + x >>= 1; + bits++; + } while (x); + bits--; + + while (d > dst) { + SD_GETBIT(x); + if (x) { + SD_GETBITS(x, 2); + switch (x) { + case 0: + type = SD_TYPE_MATCH; + x = 9; + y = 2; + break; + + case 1: + type = SD_TYPE_MATCH; + x = 10; + y = 3; + break; + + case 2: + type = SD_TYPE_MATCH; + x = 12; + SD_GETBITS(y, 8); + break; + + default: + type = SD_TYPE_LITERAL; + x = 8; + y = 8; + } + } else { + SD_GETBIT(x); + if (x) { + type = SD_TYPE_MATCH; + x = 8; + y = 1; + } else { + type = SD_TYPE_LITERAL; + x = 3; + y = 0; + } + } + + if (type == SD_TYPE_LITERAL) { + SD_GETBITS(x, x); + y += x; + if ((int)(y + 1) > (d - dst)) + return false; // Overflow? + do { + SD_GETBITS(x, 8); + *--d = x; + } while (y-- > 0); + } else { + if ((int)(y + 1) > (d - dst)) + return false; // Overflow? + SD_GETBITS(x, x); + if ((d + x) > (dst + destlen)) + return false; // Offset overflow? + do { + d--; + *d = d[x]; + } while (y-- > 0); + } + } + + // Successful decrunch. + return true; +} + +#undef SD_GETBIT +#undef SD_GETBITS +#undef SD_TYPE_LITERAL +#undef SD_TYPE_MATCH + +void SimonEngine::read_vga_from_datfile_1(uint vga_id) { + if (getFeatures() & GF_OLD_BUNDLE) { + File in; + char buf[15]; + uint32 size; + if (vga_id == 23) + vga_id = 112; + if (vga_id == 328) + vga_id = 119; + + if (getPlatform() == Common::kPlatformAmiga) { + if (getFeatures() & GF_TALKIE) + sprintf(buf, "0%d.out", vga_id); + else + sprintf(buf, "0%d.pkd", vga_id); + } else { + sprintf(buf, "0%d.VGA", vga_id); + } + + in.open(buf); + if (in.isOpen() == false) + error("read_vga_from_datfile_1: can't open %s", buf); + size = in.size(); + + if (getFeatures() & GF_CRUNCHED) { + byte *buffer = new byte[size]; + if (in.read(buffer, size) != size) + error("read_vga_from_datfile_1: read failed"); + decrunchFile(buffer, _vgaBufferPointers[11].vgaFile2, size); + delete [] buffer; + } else { + if (in.read(_vgaBufferPointers[11].vgaFile2, size) != size) + error("read_vga_from_datfile_1: read failed"); + } + in.close(); + } else { + uint32 offs_a = _gameOffsetsPtr[vga_id]; + uint32 size = _gameOffsetsPtr[vga_id + 1] - offs_a; + + resfile_read(_vgaBufferPointers[11].vgaFile2, offs_a, size); + } +} + +byte *SimonEngine::read_vga_from_datfile_2(uint id, uint type) { + // !!! HACK !!! + // allocate more space for text to cope with foreign languages that use + // up more space than english. I hope 6400 bytes are enough. This number + // is base on: 2 (lines) * 320 (screen width) * 10 (textheight) -- olki + int extraBuffer = (id == 5 ? 6400 : 0); + + if (getFeatures() & GF_OLD_BUNDLE) { + File in; + char buf[15]; + uint32 size; + byte *dst = NULL; + + if (getPlatform() == Common::kPlatformAmiga) { + if (getFeatures() & GF_TALKIE) + sprintf(buf, "%.3d%d.out", id / 2, type); + else + sprintf(buf, "%.3d%d.pkd", id / 2, type); + } else { + sprintf(buf, "%.3d%d.VGA", id / 2, type); + } + + in.open(buf); + if (in.isOpen() == false) { + if (type == 3) + return NULL; + else + error("read_vga_from_datfile_2: can't open %s", buf); + } + size = in.size(); + + if (getFeatures() & GF_CRUNCHED) { + byte *buffer = new byte[size]; + if (in.read(buffer, size) != size) + error("read_vga_from_datfile_2: read failed"); + dst = setup_vga_destination (READ_BE_UINT32(buffer + size - 4) + extraBuffer); + decrunchFile(buffer, dst, size); + delete[] buffer; + } else { + dst = setup_vga_destination(size + extraBuffer); + if (in.read(dst, size) != size) + error("read_vga_from_datfile_2: read failed"); + } + in.close(); + + return dst; + } else { + uint32 offs_a = _gameOffsetsPtr[id]; + uint32 size = _gameOffsetsPtr[id + 1] - offs_a; + byte *dst; + + dst = setup_vga_destination(size + extraBuffer); + resfile_read(dst, offs_a, size); + + return dst; + } +} + +void SimonEngine::resfile_read(void *dst, uint32 offs, uint32 size) { + _gameFile->seek(offs, SEEK_SET); + if (_gameFile->read(dst, size) != size) + error("resfile_read(%d,%d) read failed", offs, size); +} + +void SimonEngine::openGameFile() { + if (!(getFeatures() & GF_OLD_BUNDLE)) { + _gameFile = new File(); + _gameFile->open(gss->gme_filename); + + if (_gameFile->isOpen() == false) + error("Can't open game file '%s'", gss->gme_filename); + + uint32 size = _gameFile->readUint32LE(); + + _gameOffsetsPtr = (uint32 *)malloc(size); + if (_gameOffsetsPtr == NULL) + error("out of memory, game offsets"); + + resfile_read(_gameOffsetsPtr, 0, size); +#if defined(SCUMM_BIG_ENDIAN) + for (uint r = 0; r < size / 4; r++) + _gameOffsetsPtr[r] = FROM_LE_32(_gameOffsetsPtr[r]); +#endif + } + + if (getGameType() != GType_FF) + loadIconFile(); + + vc34_setMouseOff(); + + runSubroutine101(); + startUp_helper_2(); +} + +void SimonEngine::runSubroutine101() { + Subroutine *sub; + + sub = getSubroutineByID(101); + if (sub != NULL) + startSubroutineEx(sub); + + startUp_helper_2(); +} + +void SimonEngine::dx_copy_rgn_from_3_to_2(uint b, uint r, uint y, uint x) { + byte *dst, *src; + uint i; + + dst = dx_lock_2(); + src = _sdl_buf_3; + + dst += y * _dxSurfacePitch; + src += y * _dxSurfacePitch; + + while (y < b) { + for (i = x; i < r; i++) + dst[i] = src[i]; + y++; + dst += _dxSurfacePitch; + src += _dxSurfacePitch; + } + + dx_unlock_2(); +} + +void SimonEngine::dx_clear_surfaces(uint num_lines) { + memset(_sdl_buf_attached, 0, num_lines * _screenWidth); + + _system->copyRectToScreen(_sdl_buf_attached, _screenWidth, 0, 0, _screenWidth, num_lines); + + if (_dxUse3Or4ForLock) { + memset(_sdl_buf, 0, num_lines * _screenWidth); + memset(_sdl_buf_3, 0, num_lines * _screenWidth); + } +} + +void SimonEngine::dx_clear_attached_from_top(uint lines) { + memset(_sdl_buf_attached, 0, lines * _screenWidth); +} + +void SimonEngine::dx_copy_from_attached_to_2(uint x, uint y, uint w, uint h) { + uint offs = x + y * _screenWidth; + byte *s = _sdl_buf_attached + offs; + byte *d = _sdl_buf + offs; + + do { + memcpy(d, s, w); + d += _screenWidth; + s += _screenWidth; + } while (--h); +} + +void SimonEngine::dx_copy_from_2_to_attached(uint x, uint y, uint w, uint h) { + uint offs = x + y * _screenWidth; + byte *s = _sdl_buf + offs; + byte *d = _sdl_buf_attached + offs; + + do { + memcpy(d, s, w); + d += _screenWidth; + s += _screenWidth; + } while (--h); +} + +void SimonEngine::dx_copy_from_attached_to_3(uint lines) { + memcpy(_sdl_buf_3, _sdl_buf_attached, lines * _screenWidth); +} + +void SimonEngine::dx_update_screen_and_palette() { + _numScreenUpdates++; + + if (_paletteColorCount == 0 && _paletteFlag == 1) { + _paletteFlag = 0; + if (memcmp(_palette, _paletteBackup, 1024) != 0) { + memcpy(_paletteBackup, _palette, 1024); + _system->setPalette(_palette, 0, 256); + } + } + + _system->copyRectToScreen(_sdl_buf_attached, _screenWidth, 0, 0, _screenWidth, _screenHeight); + _system->updateScreen(); + + memcpy(_sdl_buf_attached, _sdl_buf, _screenWidth * _screenHeight); + + if (_paletteColorCount != 0) { + if (getGameType() == GType_SIMON1 && _usePaletteDelay) { + delay(100); + _usePaletteDelay = false; + } + fastFadeIn(); + } +} + +void SimonEngine::fastFadeIn() { + if (_paletteColorCount & 0x8000) { + slowFadeIn(); + } else { + _paletteFlag = false; + memcpy(_paletteBackup, _palette, 1024); + _system->setPalette(_palette, 0, _paletteColorCount); + _paletteColorCount = 0; + } +} + +void SimonEngine::slowFadeIn() { + uint8 paletteTmp[768]; + uint8 *src, *dst; + int c, p; + + _paletteColorCount &= 0x7fff; + _paletteFlag = false; + + memcpy(_videoBuf1, _palette, 1024); // Difference + memset(_videoBuf1, 0, 768); + + memcpy(_paletteBackup, _palette, 768); + memcpy(paletteTmp, _palette, 768); + + for (c = 255; c > 0; c -= 4) { + src = paletteTmp; + dst = _videoBuf1; + + for (p = _paletteColorCount; p !=0 ; p--) { + if (*src >= c) + *dst = *dst + 4; + + src++; + dst++; + } + _system->setPalette(_videoBuf1, 0, _videoNumPalColors); + if (_fade) + _system->updateScreen(); + delay(5); + } + _paletteColorCount = 0; +} + +int SimonEngine::go() { + if (!_dumpFile) + _dumpFile = stdout; + + // allocate buffers + _sdl_buf_3 = (byte *)calloc(_screenWidth * _screenHeight, 1); + _sdl_buf = (byte *)calloc(_screenWidth * _screenHeight, 1); + _sdl_buf_attached = (byte *)calloc(_screenWidth * _screenHeight, 1); + + allocItemHeap(); + allocTablesHeap(); + + setup_vga_file_buf_pointers(); + + _sound = new Sound(this, gss, _mixer); + _debugger = new Debugger(this); + + if (ConfMan.hasKey("sfx_mute") && ConfMan.getBool("sfx_mute") == 1) { + if (getGameId() == GID_SIMON1DOS) + midi._enable_sfx ^= 1; + else + _sound->effectsPause(_effectsPaused ^= 1); + } + + loadGamePcFile(gss->gamepc_filename); + + addTimeEvent(0, 1); + openGameFile(); + + _lastMusicPlayed = -1; + _frameRate = 1; + + _startMainScript = false; + _continousMainScript = false; + _startVgaScript = false; + _continousVgaScript = false; + _drawImagesDebug = false; + + if (gDebugLevel == 2) + _continousMainScript = true; + if (gDebugLevel == 3) + _continousVgaScript = true; + if (gDebugLevel == 4) + _startMainScript = true; + if (gDebugLevel == 5) + _startVgaScript = true; + + if (getFeatures() & GF_TALKIE) { + // English and German versions of Simon the Sorcerer 1 don't have full subtitles + if (getGameType() == GType_SIMON1 && (_language == Common::EN_USA || _language == Common::DE_DEU)) + _subtitles = false; + } else { + _subtitles = true; + } + + while (1) { + hitarea_stuff(); + handle_verb_clicked(_verbHitArea); + delay(100); + } + + return 0; +} + +void SimonEngine::shutdown() { + delete _gameFile; + + midi.close(); + + free(_stringTabPtr); + free(_itemArrayPtr); + free(_itemHeapPtr - _itemHeapCurPos); + free(_tablesHeapPtr - _tablesHeapCurPos); + free(_tblList); + free(_iconFilePtr); + free(_gameOffsetsPtr); + + _system->quit(); +} + +void SimonEngine::delay(uint amount) { + OSystem::Event event; + + uint32 start = _system->getMillis(); + uint32 cur = start; + uint this_delay, vga_period; + + if (_debugger->isAttached()) + _debugger->onFrame(); + + if (_fastMode) + vga_period = 10; + else if (getGameType() == GType_SIMON2) + vga_period = 45 * _speed; + else + vga_period = 50 * _speed; + + _rnd.getRandomNumber(2); + + do { + while (!_inCallBack && cur >= _lastVgaTick + vga_period && !_pause) { + _lastVgaTick += vga_period; + + // don't get too many frames behind + if (cur >= _lastVgaTick + vga_period * 2) + _lastVgaTick = cur; + + _inCallBack = true; + timer_callback(); + _inCallBack = false; + } + + while (_system->pollEvent(event)) { + switch (event.type) { + case OSystem::EVENT_KEYDOWN: + if (event.kbd.keycode >= '0' && event.kbd.keycode <='9' + && (event.kbd.flags == OSystem::KBD_ALT || + event.kbd.flags == OSystem::KBD_CTRL)) { + _saveLoadSlot = event.kbd.keycode - '0'; + + // There is no save slot 0 + if (_saveLoadSlot == 0) + _saveLoadSlot = 10; + + sprintf(_saveLoadName, "Quicksave %d", _saveLoadSlot); + _saveLoadType = (event.kbd.flags == OSystem::KBD_ALT) ? 1 : 2; + + // We should only allow a load or save when it was possible in original + // This stops load/save during copy protection, conversations and cut scenes + if (!_mouseHideCount && !_showPreposition) + quickLoadOrSave(); + } else if (event.kbd.flags == OSystem::KBD_CTRL) { + if (event.kbd.keycode == 'a') { + GUI::Dialog *_aboutDialog; + _aboutDialog = new GUI::AboutDialog(); + _aboutDialog->runModal(); + } else if (event.kbd.keycode == 'f') + _fastMode ^= 1; + else if (event.kbd.keycode == 'd') + _debugger->attach(); + } + // Make sure backspace works right (this fixes a small issue on OS X) + if (event.kbd.keycode == 8) + _keyPressed = 8; + else + _keyPressed = (byte)event.kbd.ascii; + break; + case OSystem::EVENT_MOUSEMOVE: + _sdlMouseX = event.mouse.x; + _sdlMouseY = event.mouse.y; + break; + case OSystem::EVENT_LBUTTONDOWN: + _leftButtonDown++; +#if defined (_WIN32_WCE) || defined(PALMOS_MODE) + _sdlMouseX = event.mouse.x; + _sdlMouseY = event.mouse.y; +#endif + break; + case OSystem::EVENT_RBUTTONDOWN: + if (getGameType() == GType_SIMON2 || getGameType() == GType_FF) + _skipSpeech = true; + else + _exitCutscene = true; + break; + case OSystem::EVENT_QUIT: + shutdown(); + return; + break; + default: + break; + } + } + + if (amount == 0) + break; + + { + this_delay = _fastMode ? 1 : 20 * _speed; + if (this_delay > amount) + this_delay = amount; + _system->delayMillis(this_delay); + } + cur = _system->getMillis(); + } while (cur < start + amount); +} + +void SimonEngine::loadMusic(uint music) { + char buf[4]; + + if (getPlatform() == Common::kPlatformAmiga) { + if (getFeatures() & GF_CRUNCHED) { + // TODO Add support for decruncher + debug(5,"loadMusic - Decrunch %dtune attempt", music); + } + // TODO Add Protracker support for simon1amiga/cd32 + debug(5,"playMusic - Load %dtune attempt", music); + } else if (getGameType() == GType_SIMON2) { // Simon 2 music + midi.stop(); + _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music - 1], SEEK_SET); + _gameFile->read(buf, 4); + if (!memcmp(buf, "FORM", 4)) { + _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music - 1], SEEK_SET); + midi.loadXMIDI (_gameFile); + } else { + _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music - 1], SEEK_SET); + midi.loadMultipleSMF (_gameFile); + } + + _lastMusicPlayed = music; + _nextMusicToPlay = -1; + } else if (getGameType() == GType_SIMON1) { // Simon 1 music + midi.stop(); + midi.setLoop (true); // Must do this BEFORE loading music. (GMF may have its own override.) + + if (getFeatures() & GF_TALKIE) { + // FIXME: The very last music resource, a cymbal crash for when the + // two demons crash into each other, should NOT be looped like the + // other music tracks. In simon1dos/talkie the GMF resource includes + // a loop override that acomplishes this, but there seems to be nothing + // for this in the SMF resources. + if (music == 35) + midi.setLoop (false); + + _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music], SEEK_SET); + _gameFile->read(buf, 4); + if (!memcmp(buf, "GMF\x1", 4)) { + _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music], SEEK_SET); + midi.loadSMF (_gameFile, music); + } else { + _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music], SEEK_SET); + midi.loadMultipleSMF (_gameFile); + } + + } else { + char filename[15]; + File f; + sprintf(filename, "MOD%d.MUS", music); + f.open(filename); + if (f.isOpen() == false) { + warning("Can't load music from '%s'", filename); + return; + } + if (getGameId() == GID_SIMON1DEMO) + midi.loadS1D (&f); + else + midi.loadSMF (&f, music); + } + + midi.startTrack (0); + } +} + +byte *SimonEngine::dx_lock_2() { + _dxSurfacePitch = _screenWidth; + return _sdl_buf; +} + +void SimonEngine::dx_unlock_2() { +} + +byte *SimonEngine::dx_lock_attached() { + _dxSurfacePitch = _screenWidth; + return _dxUse3Or4ForLock ? _sdl_buf_3 : _sdl_buf_attached; +} + +void SimonEngine::dx_unlock_attached() { +} + +void SimonEngine::set_volume(int volume) { + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volume); +} + +byte SimonEngine::getByte() { + return *_codePtr++; +} + +} // End of namespace Simon + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(Simon_Simon) +_GSETPTR(Simon::simon1_settings, GBVARS_SIMON1SETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON) +_GSETPTR(Simon::simon1acorn_settings, GBVARS_SIMON1ACORNSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON) +_GSETPTR(Simon::simon1amiga_settings, GBVARS_SIMON1AMIGASETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON) +_GSETPTR(Simon::simon1demo_settings, GBVARS_SIMON1DEMOSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON) +_GSETPTR(Simon::simon2win_settings, GBVARS_SIMON2WINSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON) +_GSETPTR(Simon::simon2dos_settings, GBVARS_SIMON2DOSSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON) +_GSETPTR(Simon::feeblefiles_settings, GBVARS_FEEBLEFILESSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON) +_GEND + +_GRELEASE(Simon_Simon) +_GRELEASEPTR(GBVARS_SIMON1SETTINGS_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_SIMON1ACORNSETTINGS_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_SIMON1AMIGASETTINGS_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_SIMON1DEMOSETTINGS_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_SIMON2WINSETTINGS_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_SIMON2DOSSETTINGS_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_FEEBLEFILESSETTINGS_INDEX, GBVARS_SIMON) +_GEND + +#endif |