scummvm/simon/simon.cpp
Lars Persson 1c69696a9a Patches needed to build for SYMBIAN32 WINS/GCC added.
Test built for Symbian and run on P910i without any major problems.
Test built for MSVC6. Changed parts seems to compile ok but there are some problems with MSVC6 and some of the targets which the EPOC build does n't support (KYRA,SAGA).

svn-id: r18430
2005-06-21 22:08:21 +00:00

4290 lines
96 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001-2005 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
#include "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 "simon/simon-md5.h"
#include "sound/mididrv.h"
#ifdef __PALM_OS__
#include "globals.h"
#endif
using Common::File;
struct SimonGameSettings {
const char *name;
const char *description;
uint32 features;
const char *detectname;
GameSettings toGameSettings() const {
GameSettings dummy = { name, description, features };
return dummy;
}
};
static const SimonGameSettings simon_settings[] = {
// Simon the Sorcerer 1 & 2 (not SCUMM games)
{"simon1acorn", "Simon the Sorcerer 1 (Acorn)", GAME_SIMON1ACORN, "DATA"},
{"simon1dos", "Simon the Sorcerer 1 (DOS)", GAME_SIMON1DOS, "GAMEPC"},
{"simon1amiga", "Simon the Sorcerer 1 (Amiga)", GAME_SIMON1AMIGA, "gameamiga"},
{"simon2dos", "Simon the Sorcerer 2 (DOS)", GAME_SIMON2DOS, "GAME32"},
{"simon1talkie", "Simon the Sorcerer 1 Talkie", GAME_SIMON1TALKIE, "GAMEPC"},
{"simon1win", "Simon the Sorcerer 1 Talkie (Windows)", GAME_SIMON1TALKIE, 0},
{"simon2talkie", "Simon the Sorcerer 2 Talkie", GAME_SIMON2TALKIE, "GSPTR30"},
{"simon2win", "Simon the Sorcerer 2 Talkie (Windows)", GAME_SIMON2WIN, 0},
{"simon2mac", "Simon the Sorcerer 2 Talkie (Amiga or Mac)", GAME_SIMON2WIN, 0},
{"simon1cd32", "Simon the Sorcerer 1 Talkie (Amiga CD32)", GAME_SIMON1CD32, "gameamiga"},
{"simon1demo", "Simon the Sorcerer 1 (DOS Demo)", GAME_SIMON1DEMO, "GDEMO"},
{NULL, NULL, 0, NULL}
};
static int compareMD5Table(const void *a, const void *b) {
const char *key = (const char *)a;
const MD5Table *elem = (const MD5Table *)b;
return strcmp(key, elem->md5);
}
GameList Engine_SIMON_gameList() {
const SimonGameSettings *g = simon_settings;
GameList games;
while (g->name) {
games.push_back(g->toGameSettings());
g++;
}
return games;
}
DetectedGameList Engine_SIMON_detectGames(const FSList &fslist) {
DetectedGameList detectedGames;
const SimonGameSettings *g;
char detectName[128];
char detectName2[128];
typedef Common::Map<Common::String, bool> StringSet;
StringSet fileSet;
for (g = simon_settings; g->name; ++g) {
if (g->detectname == NULL)
continue;
strcpy(detectName, g->detectname);
strcpy(detectName2, g->detectname);
strcat(detectName2, ".");
// Iterate over all files in the given directory
for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
if (!file->isDirectory()) {
const char *name = file->displayName().c_str();
if ((!scumm_stricmp(detectName, name)) || (!scumm_stricmp(detectName2, name))) {
// Match found, add to list of candidates, then abort inner loop.
detectedGames.push_back(g->toGameSettings());
fileSet.addKey(file->path());
break;
}
}
}
}
// Now, we check the MD5 sums of the 'candidate' files. If we have an exact match,
// only return that.
bool exactMatch = false;
for (StringSet::const_iterator iter = fileSet.begin(); iter != fileSet.end(); ++iter) {
uint8 md5sum[16];
const char *name = iter->_key.c_str();
if (Common::md5_file(name, md5sum)) {
char md5str[32+1];
for (int j = 0; j < 16; j++) {
sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
}
const MD5Table *elem;
elem = (const MD5Table *)bsearch(md5str, md5table, ARRAYSIZE(md5table)-1, sizeof(MD5Table), compareMD5Table);
if (elem) {
if (!exactMatch)
detectedGames.clear(); // Clear all the non-exact candidates
// Find the GameSettings for that target
for (g = simon_settings; g->name; ++g) {
if (0 == scumm_stricmp(g->name, elem->target))
break;
}
assert(g->name);
// Insert the 'enhanced' game data into the candidate list
detectedGames.push_back(DetectedGame(g->toGameSettings(), elem->language, elem->platform));
exactMatch = true;
}
}
}
return detectedGames;
}
Engine *Engine_SIMON_create(GameDetector *detector, OSystem *syst) {
return new Simon::SimonEngine(detector, syst);
}
REGISTER_PLUGIN(SIMON, "Simon the Sorcerer")
namespace Simon {
#ifdef __PALM_OS__
#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;
#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
};
#endif
static const char* bad_versions[3] = {
"465eed710cc242b2de7dc77edd467c4c", // simon1dos (English)
"bed9134804d96f72afa152b8ec5628c3", // simon1dos (French)
"27c8e7feada80c75b70b9c2f6088d519", // simon2dos (English)
};
SimonEngine::SimonEngine(GameDetector *detector, OSystem *syst)
: Engine(syst), midi(syst) {
int j =0;
_vcPtr = 0;
_vc_get_out_of_code = 0;
_gameOffsetsPtr = 0;
const SimonGameSettings *g = simon_settings;
while (g->name) {
if (!scumm_stricmp(detector->_game.name, g->name))
break;
g++;
}
if (!g->name)
error("Invalid game '%s'\n", detector->_game.name);
SimonGameSettings game = *g;
switch (Common::parsePlatform(ConfMan.get("platform"))) {
case Common::kPlatformAmiga:
case Common::kPlatformMacintosh:
if (game.features & GF_SIMON2)
game.features |= GF_WIN;
break;
case Common::kPlatformWindows:
game.features |= GF_WIN;
break;
default:
break;
}
_game = game.features;
// Convert older targets
if (g->detectname == NULL) {
if (!strcmp("simon1win", g->name)) {
ConfMan.set("gameid", "simon1talkie");
ConfMan.set("platform", "Windows");
} else if (!strcmp("simon2win", g->name) || !strcmp("simon2mac", g->name)) {
ConfMan.set("gameid", "simon2talkie");
ConfMan.set("platform", "Windows");
}
ConfMan.flushToDisk();
} else {
char buf[100];
uint8 md5sum[16];
File f;
sprintf(buf, g->detectname);
f.open(buf);
if (f.isOpen() == false)
strcat(buf, ".");
if (Common::md5_file(buf, md5sum)) {
char md5str[32+1];
for (j = 0; j < 16; j++) {
sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
}
for (j = 0; j < 3; j++) {
if (!strcmp(md5str, bad_versions[j]))
error("Cracked versions aren't supported");
}
printf("%s %s\n", md5str, buf);
const MD5Table *elem;
elem = (const MD5Table *)bsearch(md5str, md5table, ARRAYSIZE(md5table)-1, sizeof(MD5Table), compareMD5Table);
if (elem)
printf("Match found in database: target %s, language %s, platform %s\n",
elem->target, Common::getLanguageDescription(elem->language), Common::getPlatformDescription(elem->platform));
else
printf("Unknown MD5! Please report the details (language, platform, etc.) of this game to the ScummVM team\n");
}
}
VGA_DELAY_BASE = 1;
if (_game & GF_SIMON2) {
TABLE_INDEX_BASE = 1580 / 4;
TEXT_INDEX_BASE = 1500 / 4;
NUM_VIDEO_OP_CODES = 75;
#ifndef __PALM_OS__
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 ((_game & GF_SIMON2) && (ConfMan.getBool("native_mt32") || (_midiDriver == MD_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 __PALM_OS__
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;
}
_language = Common::parseLanguage(ConfMan.get("language"));
if (_game & GF_SIMON2) {
if (_game & 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 (_game & GF_SIMON1) {
if (_game & GF_ACORN) {
gss = PTR(simon1acorn_settings);
// Add default file directories
File::addDefaultDirectory(_gameDataPath + "execute/");
File::addDefaultDirectory(_gameDataPath + "EXECUTE/");
} else if (_game & GF_AMIGA) {
gss = PTR(simon1amiga_settings);
} else if (_game & GF_DEMO) {
gss = PTR(simon1demo_settings);
} else {
gss = PTR(simon1_settings);
}
}
if ((_game & GF_SIMON1) && (_game & GF_TALKIE)) {
// Add default file directories
switch (_language) {
case 20:
File::addDefaultDirectory(_gameDataPath + "hebrew/");
File::addDefaultDirectory(_gameDataPath + "HEBREW/");
break;
case 5:
File::addDefaultDirectory(_gameDataPath + "spanish/");
File::addDefaultDirectory(_gameDataPath + "SPANISH/");
break;
case 3:
File::addDefaultDirectory(_gameDataPath + "italian/");
File::addDefaultDirectory(_gameDataPath + "ITALIAN/");
break;
case 2:
File::addDefaultDirectory(_gameDataPath + "french/");
File::addDefaultDirectory(_gameDataPath + "FRENCH/");
break;
}
}
_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;
_op189Flags = 0;
_scriptVar2 = 0;
_runScriptReturn1 = 0;
_skipVgaWait = 0;
_noParentNotify = 0;
_vgaRes328Loaded = 0;
_hitarea_unk_3 = 0;
_mortalFlag = 0;
_videoVar8 = 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;
_xScroll = 0;
_vgaVar1 = 0;
_vgaVar2 = 0;
_xScrollStep = 0;
_spriteHeight = 0;
_vgaVar7 = 0;
_vgaVar8 = 0;
_scriptCondA = 0;
_scriptCondB = 0;
_scriptCondC = 0;
_fcsUnk1 = 0;
_fcsPtr1 = 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;
_lockCounter = 0;
_videoPaletteMode = 0;
_printCharUnk1 = 0;
_printCharUnk2 = 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;
_videoVar3 = 0;
_unkPalFlag = 0;
_exitCutscene = 0;
_skipSpeech = 0;
_videoVar9 = 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;
_timer1 = 0;
_timer5 = 0;
_timer4 = 0;
_vgaBaseDelay = 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(_fcsPtrArray3, 0, sizeof(_fcsPtrArray3));
memset(_fcsData1, 0, sizeof(_fcsData1));
memset(_fcsData2, 0, sizeof(_fcsData2));
_freeStringSlot = 0;
memset(_stringReturnBuffer, 0, sizeof(_stringReturnBuffer));
memset(_pathFindArray, 0, sizeof(_pathFindArray));
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) {
// 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(320, 200);
_system->endGFXTransaction();
// Setup midi driver
MidiDriver *driver = 0;
_midiDriver = MD_NULL;
if (_game == GAME_SIMON1AMIGA || _game == GAME_SIMON1CD32)
driver = MidiDriver::createMidi(MD_NULL); // Create fake MIDI driver for Simon1Amiga and Simon2CD32 for now
else {
_midiDriver = MidiDriver::detectMusicDriver(MDT_ADLIB | MDT_NATIVE);
driver = MidiDriver::createMidi(_midiDriver);
}
if (!driver)
driver = MidiDriver_ADLIB_create(_mixer);
else if (ConfMan.getBool("native_mt32") || (_midiDriver == MD_MT32))
driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
midi.mapMT32toGM (!(_game & GF_SIMON2) && !(ConfMan.getBool("native_mt32") || (_midiDriver == MD_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);
if (ConfMan.hasKey("music_mute") && ConfMan.getBool("music_mute") == 1)
midi.pause(_musicPaused ^= 1);
if ((_game & GF_SIMON2) && ConfMan.hasKey("speech_mute") && ConfMan.getBool("speech_mute") == 1)
_speech = 0;
if ((!(_game & GF_SIMON2) && _language > 1) || ((_game & GF_SIMON2) && _language == 20)) {
if (ConfMan.hasKey("subtitles") && ConfMan.getBool("subtitles") == 0)
_subtitles = 0;
} else
_subtitles = ConfMan.getBool("subtitles");
// Make sure either speech or subtitles is enabled
if ((_game & 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);
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 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 += sizeof(uint32);
} 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->unk2 = -1;
_item1->unk1 = 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->cond_a = in->readUint16BE();
sl->cond_b = in->readUint16BE();
sl->cond_c = 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::hasChildOfType1(Item *item) {
return findChildOfType(item, 1) != NULL;
}
bool SimonEngine::hasChildOfType2(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 <<= 1;
}
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::setItemUnk3(Item *item, int value) {
item->unk3 = 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;
lock();
for (i = 0; i != 8; i++) {
fcs = _fcsPtrArray3[i];
if (fcs && fcs->fcs_data && fcs->fcs_data->item_ptr == item) {
if (_fcsData1[i]) {
_fcsData2[i] = true;
} else {
_fcsData2[i] = false;
fcs_unk_proc_1(i, item, fcs->fcs_data->unk1, fcs->fcs_data->unk2);
}
}
}
unlock();
}
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] << 8) | 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] << 8) | p[1];
p += 2;
if (min_num == 0)
break;
max_num = (p[0] << 8) | 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 (_game & GF_SIMON2) {
_sound->loadSfxTable(_gameFile, _gameOffsetsPtr[atoi(filename + 6) - 1 + SOUND_INDEX_BASE]);
} else if (_game & GF_TALKIE) {
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 (_game & GF_OLD_BUNDLE)
return loadTextFile_simon1(filename, dst);
else
return loadTextFile_gme(filename, dst);
}
File *SimonEngine::openTablesFile(const char *filename) {
if (_game & GF_OLD_BUNDLE)
return openTablesFile_simon1(filename);
else
return openTablesFile_gme(filename);
}
void SimonEngine::closeTablesFile(File *in) {
if (_game & 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;
_scriptCondA = 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) {
_scriptCondC = _objectItem->unk1;
} else {
_scriptCondC = -1;
}
}
void SimonEngine::setup_cond_c_helper() {
HitArea *last;
if (_game & GF_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)) {
startSubroutine170();
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 >> 8) : 0xFFFF;
break;
}
}
out_of_here:
_lastHitArea3 = 0;
_lastHitArea = 0;
_lastHitArea2Ptr = NULL;
}
void SimonEngine::startSubroutine170() {
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(_fcsPtrArray3); i++)
if (_fcsPtrArray3[i] == fcs)
return i;
error("get_fcs_ptr_3_index: not found");
return 0;
}
void SimonEngine::lock() {
_lockCounter++;
}
void SimonEngine::unlock() {
_lockWord |= 1;
if (_lockCounter != 0)
_lockCounter--;
_lockWord &= ~1;
}
void SimonEngine::handle_mouse_moved() {
uint x;
if (_lockCounter) {
_system->showMouse(false);
return;
}
_system->showMouse(true);
pollMouseXY();
if (_mouseX >= 32768)
_mouseX = 0;
if (_mouseX >= 638 / 2)
_mouseX = 638 / 2;
if (_mouseY >= 32768)
_mouseY = 0;
if (_mouseY >= 199)
_mouseY = 199;
if (_hitAreaUnk4) {
uint id = 101;
if (_mouseY >= 136)
id = 102;
if (_hitAreaUnk4 != id)
hitarea_proc_1();
}
if (_game & GF_SIMON2) {
if (_bitArray[4] & 0x8000) {
if (!_vgaVar9) {
if (_mouseX >= 630 / 2 || _mouseX < 9)
goto get_out2;
_vgaVar9 = 1;
}
if (_vgaVar2 == 0) {
if (_mouseX >= 631 / 2) {
if (_xScroll != _vgaVar1)
_xScrollStep = 1;
} else if (_mouseX < 8) {
if (_xScroll != 0)
_xScrollStep = -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:
draw_mouse_pointer();
_needHitAreaRecalc = 0;
}
void SimonEngine::fcs_unk_proc_1(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 = _fcsPtrArray3[fcs_index & 7];
if (!(_game & GF_SIMON2)) {
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)
fcs_unk1(fcs_index);
fcs_ptr->fcs_data = (FillOrCopyData *) malloc(sizeof(FillOrCopyData));
fcs_ptr->fcs_data->item_ptr = item_ptr;
fcs_ptr->fcs_data->unk3 = -1;
fcs_ptr->fcs_data->unk4 = -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->unk4 & unk2) && has_item_childflag_0x10(item_ptr))
if (!(_game & GF_SIMON2)) {
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->unk4 & unk2) && has_item_childflag_0x10(item_ptr)) {
if (item_again == false) {
fcs_ptr->fcs_data->e[k].item = item_ptr;
if (!(_game & GF_SIMON2)) {
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 += (_game & GF_SIMON2) ? 20 : 1;
if (x_pos >= width_div_3) {
x_pos = 0;
y_pos += (_game & GF_SIMON2) ? 20 : 1;
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) {
fcs_unk_proc_2(fcs_ptr, fcs_index);
}
}
void SimonEngine::fcs_unk_proc_2(FillOrCopyStruct *fcs, uint fcs_index) {
setup_hit_areas(fcs, fcs_index);
fcs->fcs_data->unk3 = _scrollUpHitArea;
fcs->fcs_data->unk4 = _scrollDownHitArea;
}
void SimonEngine::setup_hit_areas(FillOrCopyStruct *fcs, uint fcs_index) {
HitArea *ha;
ha = findEmptyHitArea();
_scrollUpHitArea = ha - _hitAreas;
if (!(_game & GF_SIMON2)) {
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 (!(_game & GF_SIMON2)) {
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(0x80);
loadSprite(0, 1, 0x80, 0, 0, 0xE);
} 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 (_game & GF_SIMON2)
color = 0xec;
else
color = 0xe1;
uint limit = (_game & GF_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 || ((_game & GF_SIMON2) && ha->y >= _vgaVar8))
continue;
y_ = (ha->height >> 1) - 4 + ha->y;
x_ = (ha->width >> 1) - 4 + ha->x - (_xScroll << 3);
if (x_ >= 0x137)
continue;
dst = dx_lock_attached();
dst += (((_dxSurfacePitch >> 2) * y_) << 2) + 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 >> 8;
_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 (!(_game & GF_SIMON2)) {
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);
_fcsUnk1 = 0;
if (_fcsPtrArray3[0] != 0) {
_fcsPtr1 = _fcsPtrArray3[0];
showmessage_helper_3(_fcsPtr1->textLength, _fcsPtr1->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) {
_scriptCondB = _subjectItem->unk1;
} else {
_scriptCondB = -1;
}
if (_objectItem) {
_scriptCondC = _objectItem->unk1;
} else {
_scriptCondC = -1;
}
_scriptCondA = _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 (_game & GF_SIMON2)
_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_print_str() {
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 (_game & GF_TALKIE)
speech_id = (uint16)getNextWord();
tl = getTextLocation(vgaSpriteId);
if (_speech && speech_id != 0)
talk_with_speech(speech_id, vgaSpriteId);
if ((_game & GF_SIMON2) && (_game & GF_TALKIE) && speech_id == 0)
o_kill_sprite_simon2(2, vgaSpriteId + 2);
if (string_ptr != NULL && (speech_id == 0 || _subtitles))
talk_with_text(vgaSpriteId, color, (const char *)string_ptr, tl->x, tl->y, tl->width);
}
void SimonEngine::ensureVgaResLoadedC(uint vga_res) {
_lockWord |= 0x80;
ensureVgaResLoaded(vga_res);
_lockWord &= ~0x80;
}
void SimonEngine::ensureVgaResLoaded(uint vga_res) {
VgaPointersEntry *vpe;
CHECK_BOUNDS(vga_res, _vgaBufferPointers);
vpe = _vgaBufferPointers + vga_res;
if (vpe->vgaFile1 != NULL)
return;
vpe->vgaFile2 = read_vga_from_datfile_2(vga_res * 2 + 1);
vpe->vgaFile1 = read_vga_from_datfile_2(vga_res * 2);
}
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->dd = 0;
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_clear_vgapointer_entry(uint a) {
VgaPointersEntry *vpe;
vpe = &_vgaBufferPointers[a];
vpe->dd = 0;
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 c;
const byte *vc_ptr_org;
_videoPaletteMode = mode;
_lockWord |= 0x20;
if (vga_res_id == 0) {
if (!(_game & GF_SIMON2)) {
_unkPalFlag = true;
} else {
_dxUse3Or4ForLock = true;
_vgaVar6 = true;
}
}
_vgaCurFile2 = num = vga_res_id / 100;
for (;;) {
vpe = &_vgaBufferPointers[num];
_curVgaFile1 = vpe->vgaFile1;
_curVgaFile2 = vpe->vgaFile2;
if (vpe->vgaFile1 != NULL)
break;
ensureVgaResLoaded(num);
}
// ensure flipping complete
bb = _curVgaFile1;
b = bb + READ_BE_UINT16(&((VgaFile1Header *) bb)->hdr2_start);
c = READ_BE_UINT16(&((VgaFile1Header2 *) b)->unk1);
b = bb + READ_BE_UINT16(&((VgaFile1Header2 *) b)->unk2_offs);
while (READ_BE_UINT16(&((VgaFile1Struct0x8 *) b)->id) != vga_res_id)
b += sizeof(VgaFile1Struct0x8);
if (!(_game & GF_SIMON2)) {
if (num == 16300) {
dx_clear_attached_from_top(134);
_usePaletteDelay = true;
}
} else {
_xScroll = 0;
_vgaVar1 = 0;
_vgaVar2 = 0;
_xScrollStep = 0;
_spriteHeight = 134;
if (_variableArray[34] != -1)
_variableArray[251] = 0;
}
vc_ptr_org = _vcPtr;
_vcPtr = _curVgaFile1 + READ_BE_UINT16(&((VgaFile1Struct0x8 *) b)->script_offs);
//dump_vga_script(_vcPtr, num, vga_res_id);
run_vga_script();
_vcPtr = vc_ptr_org;
if (_game & GF_SIMON2) {
if (!_dxUse3Or4ForLock) {
num_lines = _videoPaletteMode == 4 ? 134 : 200;
_vgaVar8 = num_lines;
dx_copy_from_attached_to_2(0, 0, 320, num_lines);
dx_copy_from_attached_to_3(num_lines);
_syncFlag2 = 1;
}
_dxUse3Or4ForLock = false;
} else {
// 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 = _videoPaletteMode == 4 ? 134 : 200;
dx_copy_from_attached_to_2(0, 0, 320, num_lines);
dx_copy_from_attached_to_3(num_lines);
_syncFlag2 = 1;
_timer5 = 0;
}
_lockWord &= ~0x20;
if (!(_game & GF_SIMON2)) {
if (_unkPalFlag) {
_unkPalFlag = false;
while (_paletteColorCount != 0) {
delay(10);
}
}
}
}
void SimonEngine::o_fade_to_black() {
uint i;
memcpy(_videoBuf1, _paletteBackup, 256 * sizeof(uint32));
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, 256 * sizeof(uint32));
memcpy(_palette, _videoBuf1, 256 * sizeof(uint32));
}
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 ((_game & GF_SIMON2) && script_ptr == NULL) {
// special scroll timer
scroll_timeout();
} else {
vc_resume_sprite(script_ptr, cur_file, cur_unk);
}
vte = _nextVgaTimerToProcess;
} else {
vte++;
}
}
}
// Simon2 specific
void SimonEngine::scroll_timeout() {
if (_vgaVar2 == 0)
return;
if (_vgaVar2 < 0) {
if (_xScrollStep != -1) {
_xScrollStep = -1;
if (++_vgaVar2 == 0)
return;
}
} else {
if (_xScrollStep != 1) {
_xScrollStep = 1;
if (--_vgaVar2 == 0)
return;
}
}
add_vga_timer(6, NULL, 0, 0);
}
void SimonEngine::vc_resume_sprite(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;
_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 (!(_game & GF_SIMON2) && (_language == 2) &&
(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_force_unlock() {
if (_game & GF_SIMON2 && _bitArray[4] & 0x8000)
_mouseCursor = 0;
_lockCounter = 0;
}
void SimonEngine::o_force_lock() {
if (_game & GF_SIMON2) {
_lockWord |= 0x8000;
vc34_forceLock();
_lockWord &= ~0x8000;
} else {
_lockWord |= 0x4000;
vc34_forceLock();
_lockWord &= ~0x4000;
}
}
void SimonEngine::o_wait_for_vga(uint a) {
_vgaWaitFor = a;
_timer1 = 0;
_exitCutscene = false;
_skipSpeech = false;
while (_vgaWaitFor != 0) {
if (_skipSpeech && _game & GF_SIMON2) {
if (_vgaWaitFor == 200 && !vc_get_bit(14)) {
skip_speech();
break;
}
} else if (_exitCutscene) {
if (vc_get_bit(9)) {
startSubroutine170();
break;
}
} else {
processSpecialKeys();
}
delay(10);
if (_game & GF_SIMON2) {
if (_timer1 >= 1000) {
warning("wait timed out");
break;
}
} else if (_timer1 >= 500) {
warning("wait timed out");
break;
}
}
}
void SimonEngine::skip_speech() {
_sound->stopVoice();
if (!(_bitArray[1] & 0x1000)) {
_bitArray[0] |= 0x4000;
_variableArray[100] = 5;
loadSprite(4, 1, 0x1e, 0, 0, 0);
o_wait_for_vga(0x82);
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 (_videoVar9 == 2)
_videoVar9 = 1;
if (_game & GF_SIMON2 && _xScrollStep) {
timer_vga_sprites_helper();
}
vsp = _vgaSprites;
while (vsp->id != 0) {
vsp->paletteMode &= 0x7FFF;
vpe = &_vgaBufferPointers[vsp->fileId];
_curVgaFile1 = vpe->vgaFile1;
_curVgaFile2 = vpe->vgaFile2;
_videoPaletteMode = vsp->paletteMode;
_vgaCurSpriteId = vsp->id;
params[0] = READ_BE_UINT16(&vsp->image);
params[1] = READ_BE_UINT16(&vsp->base_color);
params[2] = READ_BE_UINT16(&vsp->x);
params[3] = READ_BE_UINT16(&vsp->y);
if (_game & GF_SIMON2) {
*(byte *)(&params[4]) = (byte)vsp->flags;
} else {
params[4] = READ_BE_UINT16(&vsp->flags);
}
_vcPtr = (const byte *)params;
vc10_draw();
vsp++;
}
if (_drawImagesDebug)
memset(_sdl_buf_attached, 0, 320 * 200);
_videoVar8++;
_vcPtr = vc_ptr_org;
}
void SimonEngine::timer_vga_sprites_helper() {
byte *dst = dx_lock_2();
const byte *src;
uint x;
if (_xScrollStep < 0) {
memmove(dst + 8, dst, 320 * _spriteHeight - 8);
} else {
memmove(dst, dst + 8, 320 * _spriteHeight - 8);
}
x = _xScroll - 1;
if (_xScrollStep > 0) {
dst += 320 - 8;
x += 41;
}
src = _vgaVar7 + x * 4;
decodeStripA(dst, src + READ_BE_UINT32(src), _spriteHeight);
dx_unlock_2();
memcpy(_sdl_buf_attached, _sdl_buf, 320 * 200);
dx_copy_from_attached_to_3(_spriteHeight);
_xScroll += _xScrollStep;
vc_write_var(0xfB, _xScroll);
_xScrollStep = 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 (_videoVar9 == 2)
_videoVar9 = 1;
vsp = _vgaSprites;
while (vsp->id != 0) {
vsp->paletteMode &= 0x7FFF;
vpe = &_vgaBufferPointers[vsp->fileId];
_curVgaFile1 = vpe->vgaFile1;
_curVgaFile2 = vpe->vgaFile2;
_videoPaletteMode = vsp->paletteMode;
_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->base_color, vsp->x, vsp->y, vsp->flags);
params[0] = READ_BE_UINT16(&vsp->image);
params[1] = READ_BE_UINT16(&vsp->base_color);
params[2] = READ_BE_UINT16(&vsp->x);
params[3] = READ_BE_UINT16(&vsp->y);
params[4] = READ_BE_UINT16(&vsp->flags);
_vcPtr = (const byte *)params;
vc10_draw();
vsp++;
}
_videoVar8++;
_vcPtr = vc_ptr_org;
}
void SimonEngine::timer_proc1() {
_timer4++;
if (_game & GF_SIMON2) {
if (_lockWord & 0x80E9 || _lockWord & 2)
return;
} else {
if (_lockWord & 0xC0E9 || _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 (_lockCounter != 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, 320 - 176, 134 - 61);
_copyPartialMode = 0;
}
if (_videoVar8) {
handle_mouse_moved();
dx_update_screen_and_palette();
_videoVar8 = 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() {
if (_game & GF_SIMON2) {
_lockWord |= 0x8000;
vc27_resetSprite();
_lockWord &= ~0x8000;
} else {
_lockWord |= 0x4000;
vc27_resetSprite();
_lockWord &= ~0x4000;
}
}
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->unk3 == b;
}
// OK
void SimonEngine::fcs_delete(uint a) {
if (_fcsPtrArray3[a] == NULL)
return;
fcs_unk1(a);
video_copy_if_flag_0x8_c(_fcsPtrArray3[a]);
_fcsPtrArray3[a] = NULL;
if (_fcsUnk1 == a) {
_fcsPtr1 = NULL;
fcs_unk_2(0);
}
}
// OK
void SimonEngine::fcs_unk_2(uint a) {
a &= 7;
if (_fcsPtrArray3[a] == NULL || _fcsUnk1 == a)
return;
_fcsUnk1 = a;
showmessage_print_char(0);
_fcsPtr1 = _fcsPtrArray3[a];
showmessage_helper_3(_fcsPtr1->textLength, _fcsPtr1->textMaxLength);
}
// OK
FillOrCopyStruct *SimonEngine::fcs_alloc(uint x, uint y, uint w, uint h, uint flags, uint fill_color,
uint unk4) {
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 = unk4;
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 (_game & GF_SIMON2) {
x += _xScroll * 8;
}
prev_i = 21 - _variableArray[12];
for (i = 20; i != 0; --i) {
p = (const uint16 *)_pathFindArray[20 - i];
if (!p)
continue;
for (j = 0; READ_BE_UINT16(&p[0]) != 999; j++, p += 2) { // 0xE703 = byteswapped 999
x_diff = abs((int)(READ_BE_UINT16(&p[0]) - x));
y_diff = abs((int)(READ_BE_UINT16(&p[1]) - 12 - y));
if (x_diff < y_diff) {
x_diff >>= 2;
y_diff <<= 2;
}
x_diff += y_diff >> 2;
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::fcs_unk1(uint fcs_index) {
FillOrCopyStruct *fcs;
uint16 fcsunk1;
uint16 i;
fcs = _fcsPtrArray3[fcs_index & 7];
fcsunk1 = _fcsUnk1;
if (fcs == NULL || fcs->fcs_data == NULL)
return;
fcs_unk_2(fcs_index);
fcs_putchar(12);
fcs_unk_2(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->unk3 != -1) {
delete_hitarea_by_index(fcs->fcs_data->unk3);
}
if (fcs->fcs_data->unk4 != -1) {
delete_hitarea_by_index(fcs->fcs_data->unk4);
if (!(_game & GF_SIMON2))
fcs_unk_5(fcs, fcs_index);
}
free(fcs->fcs_data);
fcs->fcs_data = NULL;
_fcsData1[fcs_index] = 0;
_fcsData2[fcs_index] = 0;
}
// ok
void SimonEngine::fcs_unk_5(FillOrCopyStruct *fcs, uint fcs_index) {
o_kill_sprite_simon1(0x80);
}
void SimonEngine::delete_hitarea_by_index(uint index) {
CHECK_BOUNDS(index, _hitAreas);
_hitAreas[index].flags = 0;
}
// ok
void SimonEngine::fcs_putchar(uint a) {
if (_fcsPtr1 != _fcsPtrArray3[0])
video_putchar(_fcsPtr1, 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 (!(_game & GF_SIMON2)) {
dx_copy_rgn_from_3_to_2(fcs->y + fcs->height * 8 + ((fcs == _fcsPtrArray3[2]) ? 1 : 0), (fcs->x + fcs->width) * 8, fcs->y, fcs->x * 8);
} else {
if (_vgaVar6 && _fcsPtrArray3[2] == fcs) {
fcs = _fcsPtrArray3[0x18 / 4];
_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::find_cur_sprite() {
VgaSprite *vsp = _vgaSprites;
while (vsp->id) {
if (_game & GF_SIMON2) {
if (vsp->id == _vgaCurSpriteId && vsp->fileId == _vgaCurFileId)
break;
} else {
if (vsp->id == _vgaCurSpriteId)
break;
}
vsp++;
}
return vsp;
}
bool SimonEngine::isSpriteLoaded(uint16 id, uint16 fileId) {
VgaSprite *vsp = _vgaSprites;
while (vsp->id) {
if (_game & GF_SIMON2) {
if (vsp->id == id && vsp->fileId == fileId)
return true;
} else {
if (vsp->id == id)
return true;
}
vsp++;
}
return false;
}
void SimonEngine::processSpecialKeys() {
switch (_keyPressed) {
case 27: // escape
_exitCutscene = true;
break;
case 59: // F1
if (_game & GF_SIMON2) {
vc_write_var(5, 50);
} else {
vc_write_var(5, 40);
}
vc_write_var(86, 0);
break;
case 60: // F2
if (_game & GF_SIMON2) {
vc_write_var(5, 75);
} else {
vc_write_var(5, 60);
}
vc_write_var(86, 1);
break;
case 61: // F3
if (_game & GF_SIMON2) {
vc_write_var(5, 125);
} else {
vc_write_var(5, 100);
}
vc_write_var(86, 2);
break;
case 63: // F5
if (_game & GF_SIMON2)
_exitCutscene = true;
break;
case 'p':
pause();
break;
case 't':
if ((_game & GF_SIMON2 && _game & GF_TALKIE) || ( _game & GF_TALKIE && _language > 1))
if (_speech)
_subtitles ^= 1;
break;
case 'v':
if ((_game & GF_SIMON2) && (_game & 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 (_game == GAME_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 <= 320 && ha->y + h <= 200)) {
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 paletteMode, uint fileId, uint vgaSpriteId, uint x, uint y, uint base_color) {
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->paletteMode = paletteMode;
vsp->priority = 0;
vsp->flags = 0;
vsp->y = y;
vsp->x = x;
vsp->image = 0;
vsp->base_color = base_color;
vsp->id = vgaSpriteId;
if (_game & GF_SIMON1)
vsp->fileId = fileId = vgaSpriteId / 100;
else
vsp->fileId = fileId;
for (;;) {
vpe = &_vgaBufferPointers[fileId];
_vgaCurFile2 = fileId;
_curVgaFile1 = vpe->vgaFile1;
if (vpe->vgaFile1 != NULL)
break;
ensureVgaResLoaded(fileId);
}
pp = _curVgaFile1;
p = pp + READ_BE_UINT16(&((VgaFile1Header *) pp)->hdr2_start);
count = READ_BE_UINT16(&((VgaFile1Header2 *) p)->id_count);
p = pp + READ_BE_UINT16(&((VgaFile1Header2 *) p)->id_table);
for (;;) {
if (READ_BE_UINT16(&((VgaFile1Struct0x6 *) p)->id) == vgaSpriteId) {
if (_startVgaScript)
dump_vga_script(pp + READ_BE_UINT16(&((VgaFile1Struct0x6*)p)->script_offs), fileId, vgaSpriteId);
add_vga_timer(VGA_DELAY_BASE, pp + READ_BE_UINT16(&((VgaFile1Struct0x6 *) p)->script_offs), vgaSpriteId, fileId);
break;
}
p += sizeof(VgaFile1Struct0x6);
if (!--count) {
vsp->id = 0;
break;
}
}
_lockWord &= ~0x40;
}
void SimonEngine::talk_with_speech(uint speech_id, uint vgaSpriteId) {
if (!(_game & GF_SIMON2)) {
if (speech_id == 9999) {
if (_subtitles)
return;
if (!(_bitArray[0] & 0x4000) && !(_bitArray[1] & 0x1000)) {
_bitArray[0] |= 0x4000;
_variableArray[100] = 0xF;
loadSprite(4, 1, 0x82, 0, 0, 0);
o_wait_for_vga(0x82);
}
_skipVgaWait = true;
} else {
if (_subtitles && _scriptVar2) {
loadSprite(4, 2, 204, 0, 0, 0);
o_wait_for_vga(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, 0x1e, 0, 0, 0);
o_wait_for_vga(0x82);
}
_skipVgaWait = true;
} else {
if (_subtitles && _language != 20) {
_sound->playVoice(speech_id);
return;
} else if (_subtitles && _scriptVar2) {
loadSprite(4, 2, 5, 0, 0, 0);
o_wait_for_vga(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::talk_with_text(uint vgaSpriteId, uint color, const char *string, int16 x, int16 y, int16 width) {
char convertedString[320];
char *convertedString2 = convertedString;
int16 height, len_div_3;
int stringLength = strlen(string);
int padding, lettersPerRow, lettersPerRowJustified;
const int textHeight = 10;
height = textHeight;
lettersPerRow = width / 6;
lettersPerRowJustified = stringLength / (stringLength / lettersPerRow + 1) + 1;
len_div_3 = (stringLength + 3) / 3;
if (!(_game & GF_SIMON2) && (_game & GF_TALKIE)) {
if (_variableArray[141] == 0)
_variableArray[141] = 9;
_variableArray[85] = _variableArray[141] * len_div_3;
} else {
if (_variableArray[86] == 0)
len_div_3 >>= 1;
if (_variableArray[86] == 2)
len_div_3 <<= 1;
_variableArray[85] = len_div_3 * 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 (_game & GF_SIMON2)
o_kill_sprite_simon2(2, vgaSpriteId);
else
o_kill_sprite_simon1(vgaSpriteId + 199);
color = color * 3 + 192;
if (_game & GF_AMIGA)
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 >>= 3;
if (y < 2)
y = 2;
if (_game & GF_SIMON2)
loadSprite(b, 2, vgaSpriteId, x, y, 12);
else
loadSprite(b, 2, vgaSpriteId + 199, 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 decrunch_file_amiga (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 (_game & 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 (_game == GAME_SIMON1CD32) {
sprintf(buf, "0%d.out", vga_id);
} else if (_game == GAME_SIMON1AMIGA) {
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 (_game == GAME_SIMON1AMIGA) {
byte *buffer = new byte[size];
if (in.read(buffer, size) != size)
error("read_vga_from_datfile_1: read failed");
decrunch_file_amiga (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) {
// !!! 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 (_game & GF_OLD_BUNDLE) {
File in;
char buf[15];
uint32 size;
byte *dst;
if (_game == GAME_SIMON1CD32) {
sprintf(buf, "%.3d%d.out", id >> 1, (id & 1) + 1);
} else if (_game == GAME_SIMON1AMIGA) {
sprintf(buf, "%.3d%d.pkd", id >> 1, (id & 1) + 1);
} else {
sprintf(buf, "%.3d%d.VGA", id >> 1, (id & 1) + 1);
}
in.open(buf);
if (in.isOpen() == false)
error("read_vga_from_datfile_2: can't open %s", buf);
size = in.size();
if (_game == GAME_SIMON1AMIGA) {
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);
decrunch_file_amiga (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 (!(_game & 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 / sizeof(uint32); r++)
_gameOffsetsPtr[r] = FROM_LE_32(_gameOffsetsPtr[r]);
#endif
}
loadIconFile();
vc34_forceLock();
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 * 320);
_system->copyRectToScreen(_sdl_buf_attached, 320, 0, 0, 320, 200);
if (_dxUse3Or4ForLock) {
memset(_sdl_buf, 0, num_lines * 320);
memset(_sdl_buf_3, 0, num_lines * 320);
}
}
void SimonEngine::dx_clear_attached_from_top(uint lines) {
memset(_sdl_buf_attached, 0, lines * 320);
}
void SimonEngine::dx_copy_from_attached_to_2(uint x, uint y, uint w, uint h) {
uint offs = x + y * 320;
byte *s = _sdl_buf_attached + offs;
byte *d = _sdl_buf + offs;
do {
memcpy(d, s, w);
d += 320;
s += 320;
} while (--h);
}
void SimonEngine::dx_copy_from_2_to_attached(uint x, uint y, uint w, uint h) {
uint offs = x + y * 320;
byte *s = _sdl_buf + offs;
byte *d = _sdl_buf_attached + offs;
do {
memcpy(d, s, w);
d += 320;
s += 320;
} while (--h);
}
void SimonEngine::dx_copy_from_attached_to_3(uint lines) {
memcpy(_sdl_buf_3, _sdl_buf_attached, lines * 320);
}
void SimonEngine::dx_update_screen_and_palette() {
_numScreenUpdates++;
if (_paletteColorCount == 0 && _videoVar9 == 1) {
_videoVar9 = 0;
if (memcmp(_palette, _paletteBackup, 256 * 4) != 0) {
memcpy(_paletteBackup, _palette, 256 * 4);
_system->setPalette(_palette, 0, 256);
}
}
_system->copyRectToScreen(_sdl_buf_attached, 320, 0, 0, 320, 200);
_system->updateScreen();
memcpy(_sdl_buf_attached, _sdl_buf, 320 * 200);
if (_paletteColorCount != 0) {
if (!(_game & GF_SIMON2) && _usePaletteDelay) {
delay(100);
_usePaletteDelay = false;
}
realizePalette();
}
}
void SimonEngine::realizePalette() {
_videoVar9 = false;
memcpy(_paletteBackup, _palette, 256 * 4);
if (_paletteColorCount & 0x8000) {
fadeUpPalette();
} else {
_system->setPalette(_palette, 0, _paletteColorCount);
}
_paletteColorCount = 0;
}
void SimonEngine::fadeUpPalette() {
bool done;
_paletteColorCount = (_paletteColorCount & 0x7fff) / 4;
memset(_videoBuf1, 0, _paletteColorCount * sizeof(uint32));
// This function is used by Simon 2 when riding the lion to the goblin
// camp. Note that _paletteColorCount is not 1024 in this scene, so
// only part of the palette is faded up. But apparently that's enough,
// as long as we make sure that the remaining palette colours aren't
// completely ignored.
if (_paletteColorCount < _videoNumPalColors)
memcpy(_videoBuf1 + _paletteColorCount * sizeof(uint32),
_palette + _paletteColorCount * sizeof(uint32),
(_videoNumPalColors - _paletteColorCount) * sizeof(uint32));
do {
uint8 *src;
byte *dst;
int i;
done = true;
src = _palette;
dst = _videoBuf1;
for (i = 0; i < _paletteColorCount; i++) {
if (src[0] > dst[0]) {
if (dst[0] > src[0] - 4)
dst[0] = src[0];
else
dst[0] += 4;
done = false;
}
if (src[1] > dst[1]) {
if (dst[1] > src[1] - 4)
dst[1] = src[1];
else
dst[1] += 4;
done = false;
}
if (src[2] > dst[2]) {
if (dst[2] > src[2] - 4)
dst[2] = src[2];
else
dst[2] += 4;
done = false;
}
dst += 4;
src += 4;
}
_system->setPalette(_videoBuf1, 0, _videoNumPalColors);
delay(5);
} while (!done);
}
int SimonEngine::go() {
if (!_dumpFile)
_dumpFile = stdout;
// allocate buffers
_sdl_buf_3 = (byte *)calloc(320 * 200, 1);
_sdl_buf = (byte *)calloc(320 * 200, 1);
_sdl_buf_attached = (byte *)calloc(320 * 200, 1);
allocItemHeap();
allocTablesHeap();
setup_vga_file_buf_pointers();
_sound = new Sound(_game, gss, _mixer);
_debugger = new Debugger(this);
if (ConfMan.hasKey("sfx_mute") && ConfMan.getBool("sfx_mute") == 1) {
if (_game == GAME_SIMON1DOS)
midi._enable_sfx ^= 1;
else
_sound->effectsPause(_effectsPaused ^= 1);
}
loadGamePcFile(gss->gamepc_filename);
addTimeEvent(0, 1);
openGameFile();
_lastMusicPlayed = -1;
_vgaBaseDelay = 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 (_game & GF_TALKIE) {
// English and German versions of Simon the Sorcerer 1 don't have full subtitles
if (!(_game & GF_SIMON2) && _language < 2)
_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 (_game & GF_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 (!_lockCounter && !_showPreposition)
quick_load_or_save();
} 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(__PALM_OS__)
_sdlMouseX = event.mouse.x;
_sdlMouseY = event.mouse.y;
#endif
break;
case OSystem::EVENT_RBUTTONDOWN:
if (_game & GF_SIMON2)
_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 (_game & GF_AMIGA) {
if (_game != GAME_SIMON1CD32) {
// 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 (_game & GF_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 (_game & GF_SIMON1) { // Simon 1 music
midi.stop();
midi.setLoop (true); // Must do this BEFORE loading music. (GMF may have its own override.)
if (_game & 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 (_game & GF_DEMO)
midi.loadS1D (&f);
else
midi.loadSMF (&f, music);
}
midi.startTrack (0);
}
}
byte *SimonEngine::dx_lock_2() {
_dxSurfacePitch = 320;
return _sdl_buf;
}
void SimonEngine::dx_unlock_2() {
}
byte *SimonEngine::dx_lock_attached() {
_dxSurfacePitch = 320;
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 __PALM_OS__
#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)
_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)
_GEND
#endif