diff options
author | Alejandro Marzini | 2010-07-30 05:28:09 +0000 |
---|---|---|
committer | Alejandro Marzini | 2010-07-30 05:28:09 +0000 |
commit | fb4086cadb8ce3e473dae40558d713e7a31b3858 (patch) | |
tree | 95c19d544da914c43a43f0538a1977f43e17cb39 /engines | |
parent | 7b070bbef8275ff25dfc2cbc3106acfdc8de74a5 (diff) | |
parent | a17e3c444917ca90dfd537c2102a6150e7ffe977 (diff) | |
download | scummvm-rg350-fb4086cadb8ce3e473dae40558d713e7a31b3858.tar.gz scummvm-rg350-fb4086cadb8ce3e473dae40558d713e7a31b3858.tar.bz2 scummvm-rg350-fb4086cadb8ce3e473dae40558d713e7a31b3858.zip |
Merged from trunk, from Rev 50841 to HEAD
svn-id: r51495
Diffstat (limited to 'engines')
204 files changed, 8949 insertions, 8130 deletions
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index 4a994b731a..e83ef4ead9 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -279,8 +279,7 @@ void AgiEngine::pollTimer() { while ((dm = _tickTimer - _lastTickTimer) < 5) { processEvents(); - if (_console->isAttached()) - _console->onFrame(); + _console->onFrame(); _system->delayMillis(10); _system->updateScreen(); } @@ -344,7 +343,7 @@ int AgiEngine::agiInit() { // clear view table for (i = 0; i < MAX_VIEWTABLE; i++) - memset(&_game.viewTable[i], 0, sizeof(VtEntry)); + memset(&_game.viewTable[i], 0, sizeof(struct VtEntry)); initWords(); @@ -580,7 +579,7 @@ void AgiEngine::initialize() { } else if (getPlatform() == Common::kPlatformCoCo3) { _soundemu = SOUND_EMU_COCO3; } else { - switch (MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK))) { + switch (MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK|MDT_ADLIB|MDT_PCJR|MDT_MIDI))) { case MT_PCSPK: _soundemu = SOUND_EMU_PC; break; @@ -610,6 +609,8 @@ void AgiEngine::initialize() { _renderMode = Common::kRenderEGA; break; } + } else { + _renderMode = Common::kRenderDefault; } _buttonStyle = AgiButtonStyle(_renderMode); diff --git a/engines/agi/console.cpp b/engines/agi/console.cpp index e881b092e3..e5942455e2 100644 --- a/engines/agi/console.cpp +++ b/engines/agi/console.cpp @@ -53,15 +53,6 @@ Console::Console(AgiEngine *vm) : GUI::Debugger() { DCmd_Register("bt", WRAP_METHOD(Console, Cmd_BT)); } -Console::~Console() { -} - -void Console::preEnter() { -} - -void Console::postEnter() { -} - bool Console::Cmd_SetVar(int argc, const char **argv) { if (argc != 3) { DebugPrintf("Usage: setvar <varnum> <value>\n"); diff --git a/engines/agi/console.h b/engines/agi/console.h index e8eccbe50a..e79db42054 100644 --- a/engines/agi/console.h +++ b/engines/agi/console.h @@ -46,11 +46,6 @@ struct AgiDebug { class Console : public GUI::Debugger { public: Console(AgiEngine *vm); - virtual ~Console(); - -protected: - virtual void preEnter(); - virtual void postEnter(); private: bool Cmd_SetVar(int argc, const char **argv); @@ -80,10 +75,6 @@ public: PreAGI_Console(PreAgiEngine *vm); virtual ~PreAGI_Console() {} -protected: - virtual void preEnter() {} - virtual void postEnter() {} - private: PreAgiEngine *_vm; }; @@ -94,10 +85,6 @@ public: Mickey_Console(PreAgiEngine *vm, Mickey *mickey); virtual ~Mickey_Console() {} -protected: - virtual void preEnter() {} - virtual void postEnter() {} - private: Mickey *_mickey; @@ -112,10 +99,6 @@ public: Winnie_Console(PreAgiEngine *vm, Winnie *winnie); virtual ~Winnie_Console() {} -protected: - virtual void preEnter() {} - virtual void postEnter() {} - private: Winnie *_winnie; diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp index c185c3efb3..b7eba22298 100644 --- a/engines/agi/cycle.cpp +++ b/engines/agi/cycle.cpp @@ -203,7 +203,7 @@ int AgiEngine::mainCycle() { // vars in every interpreter cycle. // // We run AGIMOUSE always as a side effect - if (getFeatures() & GF_AGIMOUSE || 1) { + if (getFeatures() & GF_AGIMOUSE || true) { _game.vars[28] = _mouse.x / 2; _game.vars[29] = _mouse.y; } @@ -318,7 +318,7 @@ int AgiEngine::playGame() { _game.lineUserInput = 22; // We run AGIMOUSE always as a side effect - if (getFeatures() & GF_AGIMOUSE || 1) + if (getFeatures() & GF_AGIMOUSE || true) report("Using AGI Mouse 1.0 protocol\n"); if (getFeatures() & GF_AGIPAL) diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp index 072ab0114f..a60080186c 100644 --- a/engines/agi/op_cmd.cpp +++ b/engines/agi/op_cmd.cpp @@ -1228,8 +1228,7 @@ void AgiEngine::cmd_quit(uint8 *p) { if (p0) { quitGame(); } else { - if (selectionBox - (" Quit the _game, or continue? \n\n\n", buttons) == 0) { + if (selectionBox(" Quit the game, or continue? \n\n\n", buttons) == 0) { quitGame(); } } @@ -1504,7 +1503,7 @@ void AgiEngine::cmd_print_at_v(uint8 *p) { void AgiEngine::cmd_push_script(uint8 *p) { // We run AGIMOUSE always as a side effect - if (getFeatures() & GF_AGIMOUSE || 1) { + if (getFeatures() & GF_AGIMOUSE || true) { _game.vars[27] = _mouse.button; _game.vars[28] = _mouse.x / 2; _game.vars[29] = _mouse.y; diff --git a/engines/agi/sprite.cpp b/engines/agi/sprite.cpp index 25118b69a6..569481d772 100644 --- a/engines/agi/sprite.cpp +++ b/engines/agi/sprite.cpp @@ -245,7 +245,7 @@ void SpritesMgr::objsRestoreArea(Sprite *s) { // WORKAROUND (see ScummVM bug #1945716) // When set.view command is called, current code cannot detect this situation while updating // Thus we force removal of the old sprite - if (s->v->viewReplaced) { + if (s->v && s->v->viewReplaced) { commitBlock(xPos, yPos, xPos + xSize, yPos + ySize); s->v->viewReplaced = false; } @@ -458,11 +458,11 @@ void SpritesMgr::blitSprites(SpriteList& l) { */ void SpritesMgr::commitUpdSprites() { - commitSprites(_sprUpd, true); + commitSprites(_sprUpd); } void SpritesMgr::commitNonupdSprites() { - commitSprites(_sprNonupd, true); + commitSprites(_sprNonupd); } // check moves in both lists @@ -679,6 +679,7 @@ void SpritesMgr::showObj(int n) { s.xSize = c->width; s.ySize = c->height; s.buffer = (uint8 *)malloc(s.xSize * s.ySize); + s.v = 0; objsSaveArea(&s); blitCel(x1, y1, 15, c, _vm->_game.views[n].agi256_2); diff --git a/engines/agi/view.cpp b/engines/agi/view.cpp index 45244bb292..fcca1e2a79 100644 --- a/engines/agi/view.cpp +++ b/engines/agi/view.cpp @@ -157,8 +157,7 @@ int AgiEngine::decodeView(int n) { return errNoLoopsInView; // allocate memory for all views - _game.views[n].loop = (ViewLoop *) - calloc(_game.views[n].numLoops, sizeof(ViewLoop)); + _game.views[n].loop = (ViewLoop *)calloc(_game.views[n].numLoops, sizeof(ViewLoop)); if (_game.views[n].loop == NULL) return errNotEnoughMemory; diff --git a/engines/agi/wagparser.cpp b/engines/agi/wagparser.cpp index 1d60524070..22de66712d 100644 --- a/engines/agi/wagparser.cpp +++ b/engines/agi/wagparser.cpp @@ -100,10 +100,8 @@ void WagProperty::setDefaults() { } void WagProperty::deleteData() { - if (_propData != NULL) { - delete _propData; - _propData = NULL; - } + delete _propData; + _propData = NULL; } WagFileParser::WagFileParser() : diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp index c8b22956f4..464c218ae8 100644 --- a/engines/agi/words.cpp +++ b/engines/agi/words.cpp @@ -76,10 +76,8 @@ int AgiEngine::loadWords(const char *fname) { } void AgiEngine::unloadWords() { - if (words != NULL) { - free(words); - words = NULL; - } + free(words); + words = NULL; } /** diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp index 1b7802acb6..c5841ff05e 100644 --- a/engines/agos/agos.cpp +++ b/engines/agos/agos.cpp @@ -1038,12 +1038,21 @@ uint32 AGOSEngine::getTime() const { } void AGOSEngine::syncSoundSettings() { - _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); - _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); - _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume")); + // Sync the engine with the config manager + int soundVolumeMusic = ConfMan.getInt("music_volume"); + int soundVolumeSFX = ConfMan.getInt("sfx_volume"); + int soundVolumeSpeech = ConfMan.getInt("speech_volume"); + + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); + + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, (mute ? 0 : (_musicPaused ? 0 : soundVolumeMusic))); + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, (mute ? 0 : soundVolumeSFX)); + _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, (mute ? 0 : soundVolumeSpeech)); if (_midiEnabled) - _midi.setVolume(ConfMan.getInt("music_volume"), ConfMan.getInt("sfx_volume")); + _midi.setVolume((mute ? 0 : soundVolumeMusic), (mute ? 0 : soundVolumeSFX)); } } // End of namespace AGOS diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp index f3ec948f0f..32c26752a2 100644 --- a/engines/agos/event.cpp +++ b/engines/agos/event.cpp @@ -429,8 +429,7 @@ void AGOSEngine::delay(uint amount) { _system->getAudioCDManager()->updateCD(); - if (_debugger->isAttached()) - _debugger->onFrame(); + _debugger->onFrame(); vgaPeriod = (_fastMode) ? 10 : _vgaPeriod; if (getGameType() == GType_PP && getGameId() != GID_DIMP) { diff --git a/engines/agos/input.cpp b/engines/agos/input.cpp index 5fc2a64416..1246149aa5 100644 --- a/engines/agos/input.cpp +++ b/engines/agos/input.cpp @@ -602,7 +602,7 @@ bool AGOSEngine::processSpecialKeys() { if (_midiEnabled) { _midi.pause(_musicPaused); } - _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, (_musicPaused) ? 0 : ConfMan.getInt("music_volume")); + syncSoundSettings(); break; case 's': if (getGameId() == GID_SIMON1DOS) { diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp index ed3e3d801b..ab5bfc4c94 100644 --- a/engines/agos/midi.cpp +++ b/engines/agos/midi.cpp @@ -346,15 +346,11 @@ void MidiPlayer::clearConstructs(MusicInfo &info) { info.num_songs = 0; } - if (info.data) { - free(info.data); - info.data = 0; - } // end if - - if (info.parser) { - delete info.parser; - info.parser = 0; - } + free(info.data); + info.data = 0; + + delete info.parser; + info.parser = 0; if (_driver) { for (i = 0; i < 16; ++i) { diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp index c50af52901..e6ac2859cf 100644 --- a/engines/cine/cine.cpp +++ b/engines/cine/cine.cpp @@ -48,10 +48,9 @@ namespace Cine { -Sound *g_sound; -Common::SaveFileManager *g_saveFileMan; +Sound *g_sound = 0; -CineEngine *g_cine; +CineEngine *g_cine = 0; CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { DebugMan.addDebugChannel(kCineDebugScript, "Script", "Script debug level"); @@ -72,7 +71,7 @@ CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Eng } CineEngine::~CineEngine() { - if (g_cine->getGameType() == Cine::GType_OS) { + if (getGameType() == Cine::GType_OS) { freeErrmessDat(); } DebugMan.clearAllDebugChannels(); @@ -82,13 +81,12 @@ Common::Error CineEngine::run() { // Initialize backend initGraphics(320, 200, false); - if (g_cine->getPlatform() == Common::kPlatformPC) { + if (getPlatform() == Common::kPlatformPC) { g_sound = new PCSound(_mixer, this); } else { // Paula chipset for Amiga and Atari versions g_sound = new PaulaSound(_mixer, this); } - g_saveFileMan = _saveFileMan; _restartRequested = false; @@ -147,9 +145,9 @@ void CineEngine::initialize() { _timerDelayMultiplier = 12; // Set default speed setupOpcodes(); - initLanguage(g_cine->getLanguage()); + initLanguage(getLanguage()); - if (g_cine->getGameType() == Cine::GType_OS) { + if (getGameType() == Cine::GType_OS) { renderer = new OSRenderer; } else { renderer = new FWRenderer; @@ -163,13 +161,13 @@ void CineEngine::initialize() { // Its size will change when loading data into it with the loadPart function. partBuffer.clear(); - if (g_cine->getGameType() == Cine::GType_OS) { + if (getGameType() == Cine::GType_OS) { readVolCnf(); } loadTextData("texte.dat"); - if (g_cine->getGameType() == Cine::GType_OS && !(g_cine->getFeatures() & GF_DEMO)) { + if (getGameType() == Cine::GType_OS && !(getFeatures() & GF_DEMO)) { loadPoldatDat("poldat.dat"); loadErrmessDat("errmess.dat"); } diff --git a/engines/cine/cine.h b/engines/cine/cine.h index 6f7b409ad7..6f2c2243e2 100644 --- a/engines/cine/cine.h +++ b/engines/cine/cine.h @@ -190,8 +190,6 @@ enum { }; -extern Common::SaveFileManager *g_saveFileMan; // TEMP - } // End of namespace Cine #endif diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp index b92ad8a0a2..9dfa2f71ea 100644 --- a/engines/cine/detection.cpp +++ b/engines/cine/detection.cpp @@ -251,7 +251,7 @@ Common::Error CineEngine::saveGameState(int slot, const char *desc) { char indexFile[80]; snprintf(indexFile, 80, "%s.dir", _targetName.c_str()); - Common::OutSaveFile *fHandle = g_saveFileMan->openForSaving(indexFile); + Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(indexFile); if (!fHandle) { warning("Unable to open file %s for saving", indexFile); return Common::kUnknownError; diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp index b5adebcd0b..f9dfcc7737 100644 --- a/engines/cine/saveload.cpp +++ b/engines/cine/saveload.cpp @@ -465,7 +465,7 @@ bool CineEngine::loadSaveDirectory() { char tmp[80]; snprintf(tmp, 80, "%s.dir", _targetName.c_str()); - fHandle = g_saveFileMan->openForLoading(tmp); + fHandle = _saveFileMan->openForLoading(tmp); if (!fHandle) { return false; @@ -771,7 +771,7 @@ bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFor } bool CineEngine::makeLoad(char *saveName) { - Common::SharedPtr<Common::InSaveFile> saveFile(g_saveFileMan->openForLoading(saveName)); + Common::SharedPtr<Common::InSaveFile> saveFile(_saveFileMan->openForLoading(saveName)); if (!saveFile) { renderer->drawString(otherMessages[0], 0); @@ -966,7 +966,7 @@ void CineEngine::makeSaveOS(Common::OutSaveFile &out) { } void CineEngine::makeSave(char *saveFileName) { - Common::SharedPtr<Common::OutSaveFile> fHandle(g_saveFileMan->openForSaving(saveFileName)); + Common::SharedPtr<Common::OutSaveFile> fHandle(_saveFileMan->openForSaving(saveFileName)); setMouseCursor(MOUSE_CURSOR_DISK); @@ -976,7 +976,7 @@ void CineEngine::makeSave(char *saveFileName) { // restoreScreen(); checkDataDisk(-1); } else { - if (g_cine->getGameType() == GType_FW) { + if (getGameType() == GType_FW) { makeSaveFW(*fHandle); } else { makeSaveOS(*fHandle); diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp index 9a10c2b5d7..82c40a2f50 100644 --- a/engines/cine/various.cpp +++ b/engines/cine/various.cpp @@ -450,7 +450,7 @@ void CineEngine::makeSystemMenu() { snprintf(tmp, 80, "%s.dir", _targetName.c_str()); - Common::OutSaveFile *fHandle = g_saveFileMan->openForSaving(tmp); + Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(tmp); if (!fHandle) { warning("Unable to open file %s for saving", tmp); break; diff --git a/engines/cruise/cruise.cpp b/engines/cruise/cruise.cpp index e6d0359059..2f38aa98ba 100644 --- a/engines/cruise/cruise.cpp +++ b/engines/cruise/cruise.cpp @@ -52,10 +52,6 @@ CruiseEngine *_vm; CruiseEngine::CruiseEngine(OSystem * syst, const CRUISEGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { -#ifdef PALMOS_MODE - _currentVolumeFile = new Common::File(); -#endif - DebugMan.addDebugChannel(kCruiseDebugScript, "scripts", "Scripts debug level"); DebugMan.addDebugChannel(kCruiseDebugSound, "sound", "Sound debug level"); diff --git a/engines/cruise/cruise_main.cpp b/engines/cruise/cruise_main.cpp index 14de916a84..65d8b57366 100644 --- a/engines/cruise/cruise_main.cpp +++ b/engines/cruise/cruise_main.cpp @@ -1833,19 +1833,17 @@ void CruiseEngine::mainLoop() { if (!skipEvents) skipEvents = manageEvents(); - if (playerDontAskQuit) break; + if (playerDontAskQuit) + break; - if (_vm->getDebugger()->isAttached()) - _vm->getDebugger()->onFrame(); + _vm->getDebugger()->onFrame(); } while (currentTick < lastTick + _gameSpeed); } else { manageEvents(); if (currentTick >= (lastTickDebug + 10)) { lastTickDebug = currentTick; - - if (_vm->getDebugger()->isAttached()) - _vm->getDebugger()->onFrame(); + _vm->getDebugger()->onFrame(); } } if (playerDontAskQuit) diff --git a/engines/cruise/vars.cpp b/engines/cruise/vars.cpp index 94fd00cbfd..bab5d171fd 100644 --- a/engines/cruise/vars.cpp +++ b/engines/cruise/vars.cpp @@ -64,11 +64,7 @@ int16 autoTrack; int16 currentDiskNumber = 1; -#ifdef PALMOS_MODE -Common::File *_currentVolumeFile; -#else Common::File currentVolumeFile; -#endif int16 volumeNumEntry; fileEntry *volumePtrToFileDescriptor = NULL; diff --git a/engines/cruise/vars.h b/engines/cruise/vars.h index 1e19794f3a..3cb09602cc 100644 --- a/engines/cruise/vars.h +++ b/engines/cruise/vars.h @@ -167,12 +167,7 @@ extern int16 autoTrack; extern int16 currentDiskNumber; -#ifdef PALMOS_MODE -extern Common::File *_currentVolumeFile; -#define currentVolumeFile (*_currentVolumeFile) -#else extern Common::File currentVolumeFile; -#endif extern int16 volumeNumEntry; extern fileEntry *volumePtrToFileDescriptor; diff --git a/engines/draci/module.mk b/engines/draci/module.mk index a172e4b939..1f80737135 100644 --- a/engines/draci/module.mk +++ b/engines/draci/module.mk @@ -17,9 +17,6 @@ MODULE_OBJS := \ surface.o \ walking.o -MODULE_DIRS += \ - engines/draci - # This module can be built as a plugin ifeq ($(ENABLE_DRACI), DYNAMIC_PLUGIN) PLUGIN := 1 diff --git a/engines/draci/walking.cpp b/engines/draci/walking.cpp index e57972fbc5..02612832d2 100644 --- a/engines/draci/walking.cpp +++ b/engines/draci/walking.cpp @@ -324,7 +324,7 @@ void WalkingMap::drawOverlayRectangle(const Common::Point &p, byte colour, byte } int WalkingMap::pointsBetween(const Common::Point &p1, const Common::Point &p2) { - return MAX(abs(p2.x - p1.x), abs(p2.y - p1.y)); + return MAX(ABS(p2.x - p1.x), ABS(p2.y - p1.y)); } Common::Point WalkingMap::interpolate(const Common::Point &p1, const Common::Point &p2, int i, int n) { @@ -636,7 +636,7 @@ bool WalkingState::walkOnNextEdge() { Movement WalkingState::animationForDirection(const Common::Point &here, const Common::Point &there) { const int dx = there.x - here.x; const int dy = there.y - here.y; - if (abs(dx) >= abs(dy)) { + if (ABS(dx) >= ABS(dy)) { return dx >= 0 ? kMoveRight : kMoveLeft; } else { return dy >= 0 ? kMoveDown : kMoveUp; diff --git a/engines/drascula/actors.cpp b/engines/drascula/actors.cpp index 33cb7fd478..8523b5b158 100644 --- a/engines/drascula/actors.cpp +++ b/engines/drascula/actors.cpp @@ -312,9 +312,9 @@ void DrasculaEngine::quadrant_2() { float distanceX, distanceY; if (currentChapter == 2) - distanceX = abs(curX + curWidth - roomX); + distanceX = ABS(curX + curWidth - roomX); else - distanceX = abs(curX + curWidth / 2 - roomX); + distanceX = ABS(curX + curWidth / 2 - roomX); distanceY = (curY + curHeight) - roomY; @@ -354,9 +354,9 @@ void DrasculaEngine::quadrant_4() { float distanceX, distanceY; if (currentChapter == 2) - distanceX = abs(curX + curWidth - roomX); + distanceX = ABS(curX + curWidth - roomX); else - distanceX = abs(curX + curWidth / 2 - roomX); + distanceX = ABS(curX + curWidth / 2 - roomX); distanceY = roomY - (curY + curHeight); diff --git a/engines/engine.cpp b/engines/engine.cpp index 3c85220931..627de87723 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -78,7 +78,7 @@ static void defaultErrorHandler(const char *msg) { if (isSmartphone()) debugger = 0; #endif - if (debugger && !debugger->isAttached()) { + if (debugger && !debugger->isActive()) { debugger->attach(msg); debugger->onFrame(); } diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index 9c61bc1b75..03c0b1d991 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -126,8 +126,13 @@ GobEngine::GobEngine(OSystem *syst) : Engine(syst) { _pauseStart = 0; // Setup mixer - _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); - _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + bool muteSFX = ConfMan.getBool("mute") || ConfMan.getBool("sfx_mute"); + bool muteMusic = ConfMan.getBool("mute") || ConfMan.getBool("music_mute"); + + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, + muteSFX ? 0 : ConfMan.getInt("sfx_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, + muteMusic ? 0 : ConfMan.getInt("music_volume")); _copyProtection = ConfMan.getBool("copy_protection"); diff --git a/engines/gob/inter_playtoons.cpp b/engines/gob/inter_playtoons.cpp index c9b962579b..142467b47f 100644 --- a/engines/gob/inter_playtoons.cpp +++ b/engines/gob/inter_playtoons.cpp @@ -362,7 +362,6 @@ void Inter_Playtoons::oPlaytoons_getObjAnimSize() { int16 objIndex; uint16 readVar[4]; uint8 i; - bool break_fl; Mult::Mult_AnimData animData; _vm->_game->_script->evalExpr(&objIndex); @@ -375,7 +374,6 @@ void Inter_Playtoons::oPlaytoons_getObjAnimSize() { return; } if (objIndex == -2) { - break_fl = false; warning("oPlaytoons_getObjAnimSize case -2 not implemented"); return; } diff --git a/engines/gob/mult_v2.cpp b/engines/gob/mult_v2.cpp index 88b604023c..6acd096e58 100644 --- a/engines/gob/mult_v2.cpp +++ b/engines/gob/mult_v2.cpp @@ -1149,7 +1149,6 @@ void Mult_v2::playImd(const char *imdFile, Mult::Mult_ImdKey &key, int16 dir, void Mult_v2::advanceObjects(int16 index) { int16 frame; bool stop = false; - bool hasImds = false; frame = _multData->animKeysFrames[index]; if (frame == -1) @@ -1254,7 +1253,6 @@ void Mult_v2::advanceObjects(int16 index) { if ((dir != 1) && (--startFrame < 0)) startFrame = 0; - hasImds = true; playImd(imdFile, key, dir, startFrame); } } diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp index 276b26ffe7..82eda1efe2 100644 --- a/engines/groovie/groovie.cpp +++ b/engines/groovie/groovie.cpp @@ -220,10 +220,8 @@ Common::Error GroovieEngine::run() { _system->getAudioCDManager()->openCD(cd_num); while (!shouldQuit()) { - // Show the debugger if required - if (_debugger->isAttached()) { - _debugger->onFrame(); - } + // Give the debugger a chance to act + _debugger->onFrame(); // Handle input Common::Event ev; diff --git a/engines/groovie/vdx.cpp b/engines/groovie/vdx.cpp index 1d3108a2cc..61756cb218 100644 --- a/engines/groovie/vdx.cpp +++ b/engines/groovie/vdx.cpp @@ -209,17 +209,14 @@ static const uint16 vdxBlockMapLookup[] = { }; void VDXPlayer::getDelta(Common::ReadStream *in) { - uint16 j, k, l; - uint32 offset; - uint8 currOpCode, param1, param2, param3; + uint16 k, l; // Get the size of the local palette - j = in->readUint16LE(); + uint16 palSize = in->readUint16LE(); // Load the palette if it isn't empty - if (j) { + if (palSize) { uint16 palBitField[16]; - int flag = 1, palIndex; // Load the bit field for (l = 0; l < 16; l++) { @@ -228,9 +225,9 @@ void VDXPlayer::getDelta(Common::ReadStream *in) { // Load the actual palette for (l = 0; l < 16; l++) { - flag = 1 << 15; - for (j = 0; j < 16; j++) { - palIndex = (l * 16) + j; + int flag = 1 << 15; + for (uint16 j = 0; j < 16; j++) { + int palIndex = (l * 16) + j; if (flag & palBitField[l]) { for (k = 0; k < 3; k++) { @@ -247,11 +244,12 @@ void VDXPlayer::getDelta(Common::ReadStream *in) { setPalette(_palBuf); } } - currOpCode = in->readByte(); - /* j now becomes the current block line we're dealing with */ - j = 0; - offset = 0; + uint8 currOpCode = in->readByte(); + uint8 param1, param2, param3; + + uint16 currentLine = 0; + uint32 offset = 0; while (!in->eos()) { byte colours[16]; if (currOpCode < 0x60) { @@ -277,8 +275,8 @@ void VDXPlayer::getDelta(Common::ReadStream *in) { break; case 0x61: /* Skip to the end of this line, next block is start of next */ /* Note this is used at the end of EVERY line */ - j++; - offset = j * TILE_SIZE * 640; + currentLine++; + offset = currentLine * TILE_SIZE * 640; break; case 0x62: case 0x63: @@ -382,12 +380,15 @@ void VDXPlayer::getStill(Common::ReadStream *in) { byte colours[16]; for (uint16 j = 0; j < numYTiles; j++) { - for (uint16 i = 0; i < numXTiles; i++) { /* Tile number */ + byte *currentTile = buf + j * TILE_SIZE * imageWidth; + for (uint16 i = numXTiles; i; i--) { uint8 colour1 = in->readByte(); uint8 colour0 = in->readByte(); uint16 colourMap = in->readUint16LE(); expandColourMap(colours, colourMap, colour1, colour0); - decodeBlockStill(buf + j * TILE_SIZE * imageWidth + i * TILE_SIZE, colours, 640, mask); + decodeBlockStill(currentTile, colours, 640, mask); + + currentTile += TILE_SIZE; } } @@ -425,20 +426,27 @@ void VDXPlayer::getStill(Common::ReadStream *in) { } void VDXPlayer::expandColourMap(byte *out, uint16 colourMap, uint8 colour1, uint8 colour0) { - int flag = 1 << 15; - for (int i = 0; i < 16; i++) { + // It's a bit faster to start from the end + out += 16; + for (int i = 16; i; i--) { // Set the corresponding colour - out[i] = (colourMap & flag) ? colour1 : colour0; + // The following is an optimized version of: + // *--out = (colourMap & 1) ? colour1 : colour0; + uint8 selector = -(colourMap & 1); + *--out = (selector & colour1) | (~selector & colour0); - // Update the flag to test the next colour - flag >>= 1; + // Update the flag map to test the next colour + colourMap >>= 1; } } void VDXPlayer::decodeBlockStill(byte *buf, byte *colours, uint16 imageWidth, uint8 mask) { - for (int y = 0; y < TILE_SIZE; y++) { - for (int x = 0; x < TILE_SIZE; x++) { - if (_flagOne) { + assert(TILE_SIZE == 4); + + for (int y = TILE_SIZE; y; y--) { + if (_flagOne) { + // TODO: optimize with bit logic? + for (int x = 0; x < TILE_SIZE; x++) { // 0xff pixels don't modify the buffer if (*colours != 0xff) { // Write the colour @@ -446,25 +454,28 @@ void VDXPlayer::decodeBlockStill(byte *buf, byte *colours, uint16 imageWidth, ui // Note: if the mask is 0, it paints the image // else, it paints the image's mask using 0xff } - } else { - *buf = *colours; + + // Point to the next colour + colours++; + + // Point to the next pixel + buf++; } - // Point to the next colour - colours++; + // Point to the start of the next line + buf += imageWidth - TILE_SIZE; + } else { + *((uint32 *)buf) = *((uint32 *)colours); + colours += 4; - // Point to the next pixel - buf++; + // Point to the start of the next line + buf += imageWidth; } - - // Point to the start of the next line - buf += imageWidth - TILE_SIZE; } } void VDXPlayer::decodeBlockDelta(uint32 offset, byte *colours, uint16 imageWidth) { - byte *fgBuf = (byte *)_fg->getBasePtr(0, 0) + offset; - //byte *bgBuf = (byte *)_bg->getBasePtr(0, 0) + offset; + assert(TILE_SIZE == 4); byte *dest; // TODO: Verify just the else block is required @@ -475,27 +486,38 @@ void VDXPlayer::decodeBlockDelta(uint32 offset, byte *colours, uint16 imageWidth dest = (byte *)_bg->getBasePtr(0, 0) + offset; //} - int32 off = _origX + _origY * imageWidth; - for (int y = 0; y < TILE_SIZE; y++) { - for (int x = 0; x < TILE_SIZE; x++) { - if (_flagSeven) { - if (fgBuf[off] != 0xff) { + // Move the pointers to the beginning of the current block + int32 blockOff = _origX + _origY * imageWidth; + dest += blockOff; + byte *fgBuf = 0; + if (_flagSeven) { + fgBuf = (byte *)_fg->getBasePtr(0, 0) + offset + blockOff; + //byte *bgBuf = (byte *)_bg->getBasePtr(0, 0) + offset + blockOff; + } + + for (int y = TILE_SIZE; y; y--) { + if (_flagSeven) { + // Paint mask + for (int x = 0; x < TILE_SIZE; x++) { + // TODO: this can probably be optimized with bit logic + if (fgBuf[x] != 0xff) { if (*colours == 0xff) { - dest[off] = fgBuf[off]; + dest[x] = fgBuf[x]; } else { - dest[off] = *colours; + dest[x] = *colours; } } - } else { - // Paint directly - dest[off] = *colours; + colours++; } - colours++; - off++; + fgBuf += imageWidth; + } else { + // Paint directly + *((uint32 *)dest) = *((uint32 *)colours); + colours += 4; } - // Prepare the offset of the next line - off += imageWidth - TILE_SIZE; + // Move to the next line + dest += imageWidth; } } diff --git a/engines/kyra/debugger.cpp b/engines/kyra/debugger.cpp index d71f7b8b25..225b44b3f4 100644 --- a/engines/kyra/debugger.cpp +++ b/engines/kyra/debugger.cpp @@ -240,7 +240,7 @@ bool Debugger_LoK::cmd_enterRoom(int argc, const char **argv) { while (!_vm->_screen->isMouseVisible()) _vm->_screen->showMouse(); - _detach_now = true; + detach(); return false; } @@ -327,7 +327,7 @@ bool Debugger_v2::cmd_enterScene(int argc, const char **argv) { while (!_vm->screen_v2()->isMouseVisible()) _vm->screen_v2()->showMouse(); - _detach_now = true; + detach(); return false; } diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h index 390281c356..fe4cc7298f 100644 --- a/engines/kyra/detection_tables.h +++ b/engines/kyra/detection_tables.h @@ -228,7 +228,7 @@ const KYRAGameDescription adGameDescs[] = { Common::EN_ANY, Common::kPlatformMacintosh, ADGF_NO_FLAGS, - Common::GUIO_NOSPEECH + Common::GUIO_NOSPEECH | Common::GUIO_MIDIGM }, KYRA1_FLOPPY_FLAGS }, diff --git a/engines/kyra/gui_lok.cpp b/engines/kyra/gui_lok.cpp index c8b7e8ec4f..9a1d750391 100644 --- a/engines/kyra/gui_lok.cpp +++ b/engines/kyra/gui_lok.cpp @@ -579,7 +579,7 @@ void GUI_LoK::setupSavegames(Menu &menu, int num) { for (int i = startSlot; i < num; ++i) menu.item[i].enabled = 0; - KyraEngine_v1::SaveHeader header; + KyraEngine_LoK::SaveHeader header; for (int i = startSlot; i < num && uint(_savegameOffset + i) < _saveSlots.size(); i++) { if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header))) { Common::strlcpy(savenames[i], header.description.c_str(), ARRAYSIZE(savenames[0])); diff --git a/engines/kyra/gui_lol.cpp b/engines/kyra/gui_lol.cpp index ded1326110..2c86073892 100644 --- a/engines/kyra/gui_lol.cpp +++ b/engines/kyra/gui_lol.cpp @@ -2611,7 +2611,7 @@ void GUI_LoL::updateSavegameList() { if (_savegameListSize) { Common::sort(_saveSlots.begin(), _saveSlots.end(), Common::Greater<int>()); - KyraEngine_v1::SaveHeader header; + LoLEngine::SaveHeader header; Common::InSaveFile *in; _savegameList = new char *[_savegameListSize]; @@ -2658,7 +2658,7 @@ int GUI_LoL::getInput() { if (_currentMenu == &_savenameMenu) { _vm->updateInput(); - for (Common::List<KyraEngine_v1::Event>::const_iterator evt = _vm->_eventList.begin(); evt != _vm->_eventList.end(); ++evt) { + for (Common::List<LoLEngine::Event>::const_iterator evt = _vm->_eventList.begin(); evt != _vm->_eventList.end(); ++evt) { if (evt->event.type == Common::EVENT_KEYDOWN) _keyPressed = evt->event.kbd; } diff --git a/engines/kyra/gui_v2.cpp b/engines/kyra/gui_v2.cpp index de19228d16..fe4b54d09b 100644 --- a/engines/kyra/gui_v2.cpp +++ b/engines/kyra/gui_v2.cpp @@ -452,7 +452,7 @@ void GUI_v2::setupSavegameNames(Menu &menu, int num) { if (_isSaveMenu && _savegameOffset == 0) startSlot = 1; - KyraEngine_v1::SaveHeader header; + KyraEngine_v2::SaveHeader header; Common::InSaveFile *in; for (int i = startSlot; i < num && uint(_savegameOffset + i) < _saveSlots.size(); ++i) { if ((in = _vm->openSaveForReading(_vm->getSavegameFilename(_saveSlots[i + _savegameOffset]), header)) != 0) { diff --git a/engines/kyra/kyra_lok.cpp b/engines/kyra/kyra_lok.cpp index cf61b58326..159230e928 100644 --- a/engines/kyra/kyra_lok.cpp +++ b/engines/kyra/kyra_lok.cpp @@ -234,6 +234,7 @@ Common::Error KyraEngine_LoK::init() { _talkingCharNum = -1; _charSayUnk3 = -1; + _disabledTalkAnimObject = _enabledTalkAnimObject = 0; memset(_currSentenceColor, 0, 3); _startSentencePalIndex = -1; _fadeText = false; diff --git a/engines/kyra/kyra_lok.h b/engines/kyra/kyra_lok.h index c0c9bf06c4..b37a14bad4 100644 --- a/engines/kyra/kyra_lok.h +++ b/engines/kyra/kyra_lok.h @@ -132,7 +132,7 @@ public: int _paletteChanged; int16 _northExitHeight; - typedef void (KyraEngine_LoK::*IntroProc)(); + typedef bool (KyraEngine_LoK::*IntroProc)(); // static data access const char * const *seqWSATable() { return _seq_WSATable; } @@ -157,11 +157,12 @@ protected: // -> intro void seq_intro(); - void seq_introLogos(); - void seq_introStory(); - void seq_introMalcolmTree(); - void seq_introKallakWriting(); - void seq_introKallakMalcolm(); + bool seq_introPublisherLogos(); + bool seq_introLogos(); + bool seq_introStory(); + bool seq_introMalcolmTree(); + bool seq_introKallakWriting(); + bool seq_introKallakMalcolm(); // -> ingame animations void seq_createAmuletJewel(int jewel, int page, int noSound, int drawOnly); @@ -373,20 +374,24 @@ protected: //void setTimer19(); void setupTimers(); void timerUpdateHeadAnims(int timerNum); - void timerSetFlags1(int timerNum); - void timerSetFlags2(int timerNum); - void timerSetFlags3(int timerNum); - void timerCheckAnimFlag1(int timerNum); - void timerCheckAnimFlag2(int timerNum); + void timerTulipCreator(int timerNum); + void timerRubyCreator(int timerNum); + void timerAsInvisibleTimeout(int timerNum); + void timerAsWillowispTimeout(int timerNum); void checkAmuletAnimFlags(); void timerRedrawAmulet(int timerNum); + void timerLavenderRoseCreator(int timerNum); + void timerAcornCreator(int timerNum); + void timerBlueberryCreator(int timerNum); void timerFadeText(int timerNum); - void updateAnimFlag1(int timerNum); - void updateAnimFlag2(int timerNum); + void timerWillowispFrameTimer(int timerNum); + void timerInvisibleFrameTimer(int timerNum); void drawAmulet(); void setTextFadeTimerCountdown(int16 countdown); void setWalkspeed(uint8 newSpeed); + void setItemCreationFlags(int offset, int count); + int buttonInventoryCallback(Button *caller); int buttonAmuletCallback(Button *caller); @@ -472,6 +477,8 @@ protected: int8 _charSayUnk2; int8 _charSayUnk3; int8 _currHeadShape; + int8 _disabledTalkAnimObject; + int8 _enabledTalkAnimObject; uint8 _currSentenceColor[3]; int8 _startSentencePalIndex; bool _fadeText; diff --git a/engines/kyra/kyra_mr.cpp b/engines/kyra/kyra_mr.cpp index cf90c73dbd..61bc3708c3 100644 --- a/engines/kyra/kyra_mr.cpp +++ b/engines/kyra/kyra_mr.cpp @@ -369,9 +369,7 @@ void KyraEngine_MR::playVQA(const char *name) { VQAMovie vqa(this, _system); char filename[20]; - int size = 0; // TODO: Movie size is 0, 1 or 2. - - snprintf(filename, sizeof(filename), "%s%d.VQA", name, size); + snprintf(filename, sizeof(filename), "%s%d.VQA", name, _configVQAQuality); if (vqa.open(filename)) { for (int i = 0; i < 4; ++i) { @@ -1460,6 +1458,8 @@ void KyraEngine_MR::registerDefaultSettings() { ConfMan.registerDefault("studio_audience", true); ConfMan.registerDefault("skip_support", true); ConfMan.registerDefault("helium_mode", false); + // 0 - best, 1 - mid, 2 - low + ConfMan.registerDefault("video_quality", 0); } void KyraEngine_MR::writeSettings() { @@ -1495,6 +1495,7 @@ void KyraEngine_MR::readSettings() { _configStudio = ConfMan.getBool("studio_audience"); _configSkip = ConfMan.getBool("skip_support"); _configHelium = ConfMan.getBool("helium_mode"); + _configVQAQuality = CLIP(ConfMan.getInt("video_quality"), 0, 2); } } // End of namespace Kyra diff --git a/engines/kyra/kyra_mr.h b/engines/kyra/kyra_mr.h index 773b0a1699..36b937f2a8 100644 --- a/engines/kyra/kyra_mr.h +++ b/engines/kyra/kyra_mr.h @@ -73,6 +73,7 @@ private: bool _configStudio; bool _configSkip; bool _configHelium; + int _configVQAQuality; void registerDefaultSettings(); void writeSettings(); diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp index abe13cec2b..5db2c360d6 100644 --- a/engines/kyra/kyra_v1.cpp +++ b/engines/kyra/kyra_v1.cpp @@ -100,9 +100,7 @@ void KyraEngine_v1::pauseEngineIntern(bool pause) { Common::Error KyraEngine_v1::init() { // Setup mixer - _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); - _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); - _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume")); + syncSoundSettings(); if (!_flags.useDigSound) { // We prefer AdLib over MIDI in Kyra 1, since it offers MT-32 support only, most users don't have a real @@ -259,7 +257,7 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag) int keys = 0; int8 mouseWheel = 0; - while (_eventList.size()) { + while (!_eventList.empty()) { Common::Event event = *_eventList.begin(); bool breakLoop = false; @@ -283,6 +281,7 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag) if (event.kbd.keycode == Common::KEYCODE_d) { if (_debugger) _debugger->attach(); + breakLoop = true; } else if (event.kbd.keycode == Common::KEYCODE_q) { quitGame(); } @@ -336,7 +335,7 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag) break; } - if (_debugger && _debugger->isAttached()) + if (_debugger) _debugger->onFrame(); if (breakLoop) diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk index b9006431d7..4708041cf7 100644 --- a/engines/kyra/module.mk +++ b/engines/kyra/module.mk @@ -84,6 +84,7 @@ MODULE_OBJS += \ sequences_lol.o \ sound_lol.o \ sprites_lol.o \ + staticres_lol.o \ text_lol.o \ timer_lol.o endif diff --git a/engines/kyra/scene_mr.cpp b/engines/kyra/scene_mr.cpp index 875200895a..bd0a1fe544 100644 --- a/engines/kyra/scene_mr.cpp +++ b/engines/kyra/scene_mr.cpp @@ -79,11 +79,9 @@ void KyraEngine_MR::enterNewScene(uint16 sceneId, int facing, int unk1, int unk2 musicUpdate(0); uint32 waitUntilTimer = 0; - bool newSoundFile = false; if (_lastMusicCommand != _sceneList[sceneId].sound) { fadeOutMusic(60); waitUntilTimer = _system->getMillis() + 60 * _tickLength; - newSoundFile = true; } _chatAltFlag = false; diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp index 7f3959d5fe..ade9886c2e 100644 --- a/engines/kyra/screen.cpp +++ b/engines/kyra/screen.cpp @@ -611,14 +611,6 @@ void Screen::fadePalette(const Palette &pal, int delay, const UpdateFunctor *upF _vm->delay((delayAcc >> 8) * 1000 / 60); delayAcc &= 0xFF; } - - if (_vm->shouldQuit()) { - setScreenPalette(pal); - if (upFunc && upFunc->isValid()) - (*upFunc)(); - else - _system->updateScreen(); - } } void Screen::getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff) { diff --git a/engines/kyra/script_tim.cpp b/engines/kyra/script_tim.cpp index 4ec6e7f349..20bc8abec5 100644 --- a/engines/kyra/script_tim.cpp +++ b/engines/kyra/script_tim.cpp @@ -159,7 +159,7 @@ TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpc _tim->opcodes = opcodes; IFFParser iff(*stream); - Common::Functor1Mem< Common::IFFChunk &, bool, TIMInterpreter > c(this, &TIMInterpreter::callback); + Common::Functor1Mem<Common::IFFChunk &, bool, TIMInterpreter> c(this, &TIMInterpreter::callback); iff.parse(c); if (!_tim->avtl) @@ -170,7 +170,7 @@ TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpc delete stream; - int num = (_avtlChunkSize < TIM::kCountFuncs) ? _avtlChunkSize : (int)TIM::kCountFuncs; + const int num = (_avtlChunkSize < TIM::kCountFuncs) ? _avtlChunkSize : (int)TIM::kCountFuncs; for (int i = 0; i < num; ++i) _tim->func[i].avtl = _tim->avtl + _tim->avtl[i]; diff --git a/engines/kyra/sequences_lok.cpp b/engines/kyra/sequences_lok.cpp index 78e8ded9f7..c4bdc29f57 100644 --- a/engines/kyra/sequences_lok.cpp +++ b/engines/kyra/sequences_lok.cpp @@ -93,6 +93,7 @@ void KyraEngine_LoK::seq_intro() { _res->loadPakFile("INTRO.VRM"); static const IntroProc introProcTable[] = { + &KyraEngine_LoK::seq_introPublisherLogos, &KyraEngine_LoK::seq_introLogos, &KyraEngine_LoK::seq_introStory, &KyraEngine_LoK::seq_introMalcolmTree, @@ -114,8 +115,13 @@ void KyraEngine_LoK::seq_intro() { snd_playTheme(0, 2); _text->setTalkCoords(144); - for (int i = 0; i < ARRAYSIZE(introProcTable) && !seq_skipSequence(); ++i) - (this->*introProcTable[i])(); + for (int i = 0; i < ARRAYSIZE(introProcTable) && !seq_skipSequence(); ++i) { + if ((this->*introProcTable[i])() && !shouldQuit()) { + resetSkipFlag(); + _screen->fadeToBlack(); + _screen->clearPage(0); + } + } _text->setTalkCoords(136); delay(30 * _tickLength); @@ -127,18 +133,32 @@ void KyraEngine_LoK::seq_intro() { _res->unloadPakFile("INTRO.VRM"); } -void KyraEngine_LoK::seq_introLogos() { +bool KyraEngine_LoK::seq_introPublisherLogos() { if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) { _screen->loadBitmap("LOGO.CPS", 3, 3, &_screen->getPalette(0)); _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); _screen->updateScreen(); _screen->fadeFromBlack(); delay(90 * _tickLength); - _screen->fadeToBlack(); - if (!_abortIntroFlag) + if (!_abortIntroFlag) { + _screen->fadeToBlack(); snd_playWanderScoreViaMap(_flags.platform == Common::kPlatformFMTowns ? 57 : 2, 0); + } + } else if (_flags.platform == Common::kPlatformMacintosh && _res->exists("MP_GOLD.CPS")) { + _screen->loadPalette("MP_GOLD.COL", _screen->getPalette(0)); + _screen->loadBitmap("MP_GOLD.CPS", 3, 3, 0); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + _screen->fadeFromBlack(); + delay(120 * _tickLength); + if (!_abortIntroFlag) + _screen->fadeToBlack(); } + return _abortIntroFlag; +} + +bool KyraEngine_LoK::seq_introLogos() { _screen->clearPage(0); if (_flags.platform == Common::kPlatformAmiga) { @@ -159,11 +179,8 @@ void KyraEngine_LoK::seq_introLogos() { _screen->updateScreen(); _screen->fadeFromBlack(); - if (_seq->playSequence(_seq_WestwoodLogo, skipFlag()) || shouldQuit()) { - _screen->fadeToBlack(); - _screen->clearPage(0); - return; - } + if (_seq->playSequence(_seq_WestwoodLogo, skipFlag()) || shouldQuit()) + return true; delay(60 * _tickLength); @@ -174,16 +191,14 @@ void KyraEngine_LoK::seq_introLogos() { Screen::FontId of = _screen->setFont(Screen::FID_8_FNT); - if ((_seq->playSequence(_seq_KyrandiaLogo, skipFlag()) && !seq_skipSequence()) || shouldQuit()) { - _screen->fadeToBlack(); - _screen->clearPage(0); - return; - } + if (_seq->playSequence(_seq_KyrandiaLogo, skipFlag()) || shouldQuit()) + return true; + _screen->setFont(of); _screen->fillRect(0, 179, 319, 199, 0); if (shouldQuit()) - return; + return false; if (_flags.platform == Common::kPlatformAmiga) { _screen->copyPalette(0, 2); @@ -225,20 +240,20 @@ void KyraEngine_LoK::seq_introLogos() { } while (!doneFlag && !shouldQuit() && !_abortIntroFlag); } - if (shouldQuit()) - return; + if (_abortIntroFlag || shouldQuit()) + return true; - _seq->playSequence(_seq_Forest, true); + return _seq->playSequence(_seq_Forest, true); } -void KyraEngine_LoK::seq_introStory() { +bool KyraEngine_LoK::seq_introStory() { _screen->clearPage(3); _screen->clearPage(0); // HACK: The Italian fan translation uses an special text screen here // so we show it even when text is disabled if (!textEnabled() && speechEnabled() && _flags.lang != Common::IT_ITA) - return; + return false; if ((_flags.lang == Common::EN_ANY && !_flags.isTalkie && _flags.platform == Common::kPlatformPC) || _flags.platform == Common::kPlatformAmiga) _screen->loadBitmap("TEXT.CPS", 3, 3, &_screen->getPalette(0)); @@ -292,25 +307,30 @@ void KyraEngine_LoK::seq_introStory() { _screen->updateScreen(); delay(360 * _tickLength); + + return _abortIntroFlag; } -void KyraEngine_LoK::seq_introMalcolmTree() { +bool KyraEngine_LoK::seq_introMalcolmTree() { _screen->_curPage = 0; _screen->clearPage(3); - _seq->playSequence(_seq_MalcolmTree, true); + return _seq->playSequence(_seq_MalcolmTree, true); } -void KyraEngine_LoK::seq_introKallakWriting() { +bool KyraEngine_LoK::seq_introKallakWriting() { _seq->makeHandShapes(); _screen->setAnimBlockPtr(5060); _screen->_charWidth = -2; _screen->clearPage(3); - _seq->playSequence(_seq_KallakWriting, true); + const bool skipped = _seq->playSequence(_seq_KallakWriting, true); + _seq->freeHandShapes(); + + return skipped; } -void KyraEngine_LoK::seq_introKallakMalcolm() { +bool KyraEngine_LoK::seq_introKallakMalcolm() { _screen->clearPage(3); - _seq->playSequence(_seq_KallakMalcolm, true); + return _seq->playSequence(_seq_KallakMalcolm, true); } void KyraEngine_LoK::seq_createAmuletJewel(int jewel, int page, int noSound, int drawOnly) { diff --git a/engines/kyra/sound_midi.cpp b/engines/kyra/sound_midi.cpp index 7eb151a64d..026c72de26 100644 --- a/engines/kyra/sound_midi.cpp +++ b/engines/kyra/sound_midi.cpp @@ -573,8 +573,12 @@ void SoundMidiPC::updateVolumeSettings() { if (!_output) return; - int newMusVol = ConfMan.getInt("music_volume"); - _sfxVolume = ConfMan.getInt("sfx_volume"); + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); + + const int newMusVol = (mute ? 0 : ConfMan.getInt("music_volume")); + _sfxVolume = (mute ? 0 : ConfMan.getInt("sfx_volume")); _output->setSourceVolume(0, newMusVol, newMusVol != _musicVolume); _musicVolume = newMusVol; diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp index 7a570b3100..32fbbdf1e7 100644 --- a/engines/kyra/sound_towns.cpp +++ b/engines/kyra/sound_towns.cpp @@ -596,21 +596,15 @@ Towns_EuphonyDriver::~Towns_EuphonyDriver() { MidiDriver_YM2612::removeLookupTables(); - if (_fmInstruments) { - delete[] _fmInstruments; - _fmInstruments = 0; - } + delete[] _fmInstruments; + _fmInstruments = 0; - if (_waveInstruments) { - delete[] _waveInstruments; - _waveInstruments = 0; - } + delete[] _waveInstruments; + _waveInstruments = 0; for (int i = 0; i < 10; i++) { - if (_waveSounds[i]) { - delete[] _waveSounds[i]; - _waveSounds[i] = 0; - } + delete[] _waveSounds[i]; + _waveSounds[i] = 0; } if (_queue) { @@ -1859,7 +1853,7 @@ void TownsPC98_OpnChannel::loadData(uint8 *data) { _dataPtr = data; _totalLevel = 0x7F; - uint8 *tmp = _dataPtr; + uint8 *tmp = _dataPtr; for (bool loop = true; loop; ) { uint8 cmd = *tmp++; if (cmd < 0xf0) { @@ -2507,7 +2501,7 @@ void TownsPC98_OpnSfxChannel::loadData(uint8 *data) { _ssgTl = 0xff; _algorithm = 0x80; - uint8 *tmp = _dataPtr; + uint8 *tmp = _dataPtr; for (bool loop = true; loop; ) { uint8 cmd = *tmp++; if (cmd < 0xf0) { @@ -2798,7 +2792,7 @@ void TownsPC98_OpnSquareSineSource::nextTick(int32 *buffer, uint32 bufferSize) { finOut += finOutTemp; } - finOut /= 3; + finOut /= 3; buffer[i << 1] += finOut; buffer[(i << 1) + 1] += finOut; @@ -3335,7 +3329,7 @@ void TownsPC98_OpnCore::setVolumeChannelMasks(int channelMaskA, int channelMaskB if (_ssg) _ssg->setVolumeChannelMasks(_volMaskA >> _numChan, _volMaskB >> _numChan); if (_prc) - _prc->setVolumeChannelMasks(_volMaskA >> (_numChan + _numSSG), _volMaskB >> (_numChan + _numSSG)); + _prc->setVolumeChannelMasks(_volMaskA >> (_numChan + _numSSG), _volMaskB >> (_numChan + _numSSG)); } void TownsPC98_OpnCore::generateTables() { @@ -3805,7 +3799,7 @@ void TownsPC98_OpnDriver::setSfxTempo(uint16 tempo) { void TownsPC98_OpnDriver::startSoundEffect() { int volFlags = 0; - + for (int i = 0; i < 2; i++) { if (_sfxOffsets[i]) { _ssgChannels[i + 1]->protect(); @@ -3818,7 +3812,7 @@ void TownsPC98_OpnDriver::startSoundEffect() { _updateSfxFlag &= ~_sfxChannels[i]->_idFlag; } } - + setVolumeChannelMasks(~volFlags, volFlags); _sfxData = 0; } @@ -4259,8 +4253,12 @@ void SoundPC98::updateVolumeSettings() { if (!_driver) return; - _driver->setMusicVolume(ConfMan.getInt("music_volume")); - _driver->setSoundEffectVolume(ConfMan.getInt("sfx_volume")); + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); + + _driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume"))); + _driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume"))); } // KYRA 2 @@ -4461,14 +4459,18 @@ void SoundTownsPC98_v2::updateVolumeSettings() { if (!_driver) return; - _driver->setMusicVolume(ConfMan.getInt("music_volume")); - _driver->setSoundEffectVolume(ConfMan.getInt("sfx_volume")); + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); + + _driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume"))); + _driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume"))); } // static resources const uint32 TownsPC98_OpnCore::_adtStat[] = { - 0x00010001, 0x00010001, 0x00010001, 0x01010001, + 0x00010001, 0x00010001, 0x00010001, 0x01010001, 0x00010101, 0x00010101, 0x00010101, 0x01010101, 0x01010101, 0x01010101, 0x01010102, 0x01010102, 0x01020102, 0x01020102, 0x01020202, 0x01020202, @@ -4482,14 +4484,14 @@ const uint32 TownsPC98_OpnCore::_adtStat[] = { const uint8 TownsPC98_OpnCore::_detSrc[] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, - 0x08, 0x08, 0x08, 0x08, 0x01, 0x01, 0x01, 0x01, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, + 0x08, 0x08, 0x08, 0x08, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, - 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x10, 0x10, 0x10, 0x10, 0x02, 0x02, 0x02, 0x02, + 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x10, 0x10, 0x10, 0x10, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, - 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, - 0x0b, 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14, + 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14, 0x16, 0x16, 0x16, 0x16 }; diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp index 2f2acd78d1..9ad2f50619 100644 --- a/engines/kyra/staticres.cpp +++ b/engines/kyra/staticres.cpp @@ -27,18 +27,15 @@ #include "common/md5.h" #include "kyra/kyra_v1.h" #include "kyra/kyra_lok.h" -#include "kyra/lol.h" #include "kyra/kyra_v2.h" #include "kyra/kyra_hof.h" #include "kyra/kyra_mr.h" #include "kyra/screen.h" #include "kyra/screen_lok.h" -#include "kyra/screen_lol.h" #include "kyra/screen_hof.h" #include "kyra/screen_mr.h" #include "kyra/resource.h" #include "kyra/gui_lok.h" -#include "kyra/gui_lol.h" #include "kyra/gui_hof.h" #include "kyra/gui_mr.h" #include "kyra/sound_intern.h" @@ -305,36 +302,6 @@ const ItemAnimData_v2 *StaticResource::loadShapeAnimData_v2(int id, int &entries return (const ItemAnimData_v2 *)getData(id, k2ShpAnimDataV2, entries); } -#ifdef ENABLE_LOL -const LoLCharacter *StaticResource::loadCharData(int id, int &entries) { - return (const LoLCharacter *)getData(id, kLolCharData, entries); -} - -const SpellProperty *StaticResource::loadSpellData(int id, int &entries) { - return (const SpellProperty *)getData(id, kLolSpellData, entries); -} - -const CompassDef *StaticResource::loadCompassData(int id, int &entries) { - return (const CompassDef *)getData(id, kLolCompassData, entries); -} - -const FlyingObjectShape *StaticResource::loadFlyingObjectData(int id, int &entries) { - return (const FlyingObjectShape *)getData(id, kLolFlightShpData, entries); -} - -const uint16 *StaticResource::loadRawDataBe16(int id, int &entries) { - return (const uint16 *)getData(id, kLolRawDataBe16, entries); -} - -const uint32 *StaticResource::loadRawDataBe32(int id, int &entries) { - return (const uint32 *)getData(id, kLolRawDataBe32, entries); -} - -const ButtonDef *StaticResource::loadButtonDefs(int id, int &entries) { - return (const ButtonDef *)getData(id, kLolButtonData, entries); -} -#endif // ENABLE_LOL - bool StaticResource::prefetchId(int id) { if (id == -1) { for (DataMap::const_iterator i = _dataTable.begin(); i != _dataTable.end(); ++i) { @@ -641,160 +608,6 @@ bool StaticResource::loadShapeAnimData_v2(Common::SeekableReadStream &stream, vo return true; } -#ifdef ENABLE_LOL -bool StaticResource::loadCharData(Common::SeekableReadStream &stream, void *&ptr, int &size) { - size = stream.size() / 130; - LoLCharacter *charData = new LoLCharacter[size]; - - for (int i = 0; i < size; i++) { - LoLCharacter *t = &charData[i]; - - t->flags = stream.readUint16LE(); - stream.read(t->name, 11); - t->raceClassSex = stream.readByte(); - t->id = stream.readSint16LE(); - t->curFaceFrame = stream.readByte(); - t->tempFaceFrame = stream.readByte(); - t->screamSfx = stream.readByte(); - stream.readUint32LE(); - for (int ii = 0; ii < 8; ii++) - t->itemsMight[ii] = stream.readUint16LE(); - for (int ii = 0; ii < 8; ii++) - t->protectionAgainstItems[ii] = stream.readUint16LE(); - t->itemProtection = stream.readUint16LE(); - t->hitPointsCur = stream.readSint16LE(); - t->hitPointsMax = stream.readUint16LE(); - t->magicPointsCur = stream.readSint16LE(); - t->magicPointsMax = stream.readUint16LE(); - t->field_41 = stream.readByte(); - t->damageSuffered = stream.readUint16LE(); - t->weaponHit = stream.readUint16LE(); - t->totalMightModifier = stream.readUint16LE(); - t->totalProtectionModifier = stream.readUint16LE(); - t->might = stream.readUint16LE(); - t->protection = stream.readUint16LE(); - t->nextAnimUpdateCountdown = stream.readSint16LE(); - for (int ii = 0; ii < 11; ii++) - t->items[ii] = stream.readUint16LE(); - for (int ii = 0; ii < 3; ii++) - t->skillLevels[ii] = stream.readByte(); - for (int ii = 0; ii < 3; ii++) - t->skillModifiers[ii] = stream.readByte(); - for (int ii = 0; ii < 3; ii++) - t->experiencePts[ii] = stream.readUint32LE(); - for (int ii = 0; ii < 5; ii++) - t->characterUpdateEvents[ii] = stream.readByte(); - for (int ii = 0; ii < 5; ii++) - t->characterUpdateDelay[ii] = stream.readByte(); - }; - - ptr = charData; - return true; -} - -bool StaticResource::loadSpellData(Common::SeekableReadStream &stream, void *&ptr, int &size) { - size = stream.size() / 28; - SpellProperty *spellData = new SpellProperty[size]; - - for (int i = 0; i < size; i++) { - SpellProperty *t = &spellData[i]; - - t->spellNameCode = stream.readUint16LE(); - for (int ii = 0; ii < 4; ii++) - t->mpRequired[ii] = stream.readUint16LE(); - t->field_a = stream.readUint16LE(); - t->field_c = stream.readUint16LE(); - for (int ii = 0; ii < 4; ii++) - t->hpRequired[ii] = stream.readUint16LE(); - t->field_16 = stream.readUint16LE(); - t->field_18 = stream.readUint16LE(); - t->flags = stream.readUint16LE(); - }; - - ptr = spellData; - return true; -} - -bool StaticResource::loadCompassData(Common::SeekableReadStream &stream, void *&ptr, int &size) { - size = stream.size() / 4; - CompassDef *defs = new CompassDef[size]; - - for (int i = 0; i < size; i++) { - CompassDef *t = &defs[i]; - t->shapeIndex = stream.readByte(); - t->x = stream.readByte(); - t->y = stream.readByte(); - t->flags = stream.readByte(); - }; - - - ptr = defs; - return true; -} - -bool StaticResource::loadFlyingObjectData(Common::SeekableReadStream &stream, void *&ptr, int &size) { - size = stream.size() / 5; - FlyingObjectShape *defs = new FlyingObjectShape[size]; - - for (int i = 0; i < size; i++) { - FlyingObjectShape *t = &defs[i]; - t->shapeFront = stream.readByte(); - t->shapeBack = stream.readByte(); - t->shapeLeft = stream.readByte(); - t->drawFlags = stream.readByte(); - t->flipFlags = stream.readByte(); - }; - - ptr = defs; - return true; -} - -bool StaticResource::loadRawDataBe16(Common::SeekableReadStream &stream, void *&ptr, int &size) { - size = stream.size() >> 1; - - uint16 *r = new uint16[size]; - - for (int i = 0; i < size; i++) - r[i] = stream.readUint16BE(); - - ptr = r; - return true; -} - -bool StaticResource::loadRawDataBe32(Common::SeekableReadStream &stream, void *&ptr, int &size) { - size = stream.size() >> 2; - - uint32 *r = new uint32[size]; - - for (int i = 0; i < size; i++) - r[i] = stream.readUint32BE(); - - ptr = r; - return true; -} - -bool StaticResource::loadButtonDefs(Common::SeekableReadStream &stream, void *&ptr, int &size) { - size = stream.size() / 18; - - ButtonDef *r = new ButtonDef[size]; - - for (int i = 0; i < size; i++) { - r[i].buttonflags = stream.readUint16BE(); - r[i].keyCode = stream.readUint16BE(); - r[i].keyCode2 = stream.readUint16BE(); - r[i].x = stream.readSint16BE(); - r[i].y = stream.readSint16BE(); - r[i].w = stream.readUint16BE(); - r[i].h = stream.readUint16BE(); - r[i].index = stream.readUint16BE(); - r[i].screenDim = stream.readUint16BE(); - } - - ptr = r; - return true; -} -#endif // ENABLE_LOL - void StaticResource::freeRawData(void *&ptr, int &size) { uint8 *data = (uint8 *)ptr; delete[] data; @@ -870,58 +683,6 @@ void StaticResource::freeHofShapeAnimDataV2(void *&ptr, int &size) { size = 0; } -#ifdef ENABLE_LOL -void StaticResource::freeCharData(void *&ptr, int &size) { - LoLCharacter *d = (LoLCharacter *)ptr; - delete[] d; - ptr = 0; - size = 0; -} - -void StaticResource::freeSpellData(void *&ptr, int &size) { - SpellProperty *d = (SpellProperty *)ptr; - delete[] d; - ptr = 0; - size = 0; -} - -void StaticResource::freeCompassData(void *&ptr, int &size) { - CompassDef *d = (CompassDef *)ptr; - delete[] d; - ptr = 0; - size = 0; -} - -void StaticResource::freeFlyingObjectData(void *&ptr, int &size) { - FlyingObjectShape *d = (FlyingObjectShape *)ptr; - delete[] d; - ptr = 0; - size = 0; -} - - -void StaticResource::freeRawDataBe16(void *&ptr, int &size) { - uint16 *data = (uint16 *)ptr; - delete[] data; - ptr = 0; - size = 0; -} - -void StaticResource::freeRawDataBe32(void *&ptr, int &size) { - uint32 *data = (uint32 *)ptr; - delete[] data; - ptr = 0; - size = 0; -} - -void StaticResource::freeButtonDefs(void *&ptr, int &size) { - ButtonDef *d = (ButtonDef *)ptr; - delete[] d; - ptr = 0; - size = 0; -} -#endif // ENABLE_LOL - #pragma mark - void KyraEngine_LoK::initStaticResource() { @@ -1360,371 +1121,6 @@ void KyraEngine_MR::initStaticResource() { _itemStringMap = _staticres->loadRawData(k3ItemStringMap, _itemStringMapSize); } -#ifdef ENABLE_LOL -// TODO: move this to lol.cpp maybe? -void LoLEngine::initStaticResource() { - // assign music data - static const char *pcMusicFileListIntro[] = { "LOREINTR" }; - static const char *pcMusicFileListFinale[] = { "LOREFINL" }; - static const char *pcMusicFileListIngame[] = { "LORE%02d%c" }; - - static const char *pc98MusicFileListIntro[] = { 0, "lore84.86", "lore82.86", 0, 0, 0, "lore83.86", "lore81.86" }; - static const char *pc98MusicFileListFinale[] = { 0, 0, "lore85.86", "lore86.86", "lore87.86" }; - static const char *pc98MusicFileListIngame[] = { "lore%02d.86" }; - - memset(_soundData, 0, sizeof(_soundData)); - if (_flags.platform == Common::kPlatformPC) { - _soundData[0].fileList = pcMusicFileListIntro; - _soundData[0].fileListLen = ARRAYSIZE(pcMusicFileListIntro); - _soundData[1].fileList = pcMusicFileListIngame; - _soundData[1].fileListLen = ARRAYSIZE(pcMusicFileListIngame); - _soundData[2].fileList = pcMusicFileListFinale; - _soundData[2].fileListLen = ARRAYSIZE(pcMusicFileListFinale); - } else if (_flags.platform == Common::kPlatformPC98) { - _soundData[0].fileList = pc98MusicFileListIntro; - _soundData[0].fileListLen = ARRAYSIZE(pc98MusicFileListIntro); - _soundData[1].fileList = pc98MusicFileListIngame; - _soundData[1].fileListLen = ARRAYSIZE(pc98MusicFileListIngame); - _soundData[2].fileList = pc98MusicFileListFinale; - _soundData[2].fileListLen = ARRAYSIZE(pc98MusicFileListFinale); - } - - if (_flags.isDemo) - return; - - _pakFileList = _staticres->loadStrings(kLolIngamePakFiles, _pakFileListSize); - _charDefaults = _staticres->loadCharData(kLolCharacterDefs, _charDefaultsSize); - _ingameSoundIndex = (const uint16 *)_staticres->loadRawData(kLolIngameSfxIndex, _ingameSoundIndexSize); - _musicTrackMap = _staticres->loadRawData(kLolMusicTrackMap, _musicTrackMapSize); - _ingameGMSoundIndex = _staticres->loadRawData(kLolIngameGMSfxIndex, _ingameGMSoundIndexSize); - _ingameMT32SoundIndex = _staticres->loadRawData(kLolIngameMT32SfxIndex, _ingameMT32SoundIndexSize); - _ingamePCSpeakerSoundIndex = _staticres->loadRawData(kLolIngamePcSpkSfxIndex, _ingamePCSpeakerSoundIndexSize); - _spellProperties = _staticres->loadSpellData(kLolSpellProperties, _spellPropertiesSize); - _gameShapeMap = (const int8 *)_staticres->loadRawData(kLolGameShapeMap, _gameShapeMapSize); - _sceneItemOffs = (const int8 *)_staticres->loadRawData(kLolSceneItemOffs, _sceneItemOffsSize); - _charInvIndex = _staticres->loadRawData(kLolCharInvIndex, _charInvIndexSize); - _charInvDefs = _staticres->loadRawData(kLolCharInvDefs, _charInvDefsSize); - _charDefsMan = _staticres->loadRawDataBe16(kLolCharDefsMan, _charDefsManSize); - _charDefsWoman = _staticres->loadRawDataBe16(kLolCharDefsWoman, _charDefsWomanSize); - _charDefsKieran = _staticres->loadRawDataBe16(kLolCharDefsKieran, _charDefsKieranSize); - _charDefsAkshel = _staticres->loadRawDataBe16(kLolCharDefsAkshel, _charDefsAkshelSize); - _expRequirements = (const int32 *)_staticres->loadRawDataBe32(kLolExpRequirements, _expRequirementsSize); - _monsterModifiers = _staticres->loadRawDataBe16(kLolMonsterModifiers, _monsterModifiersSize); - _monsterShiftOffs = (const int8 *)_staticres->loadRawData(kLolMonsterShiftOffsets, _monsterShiftOffsSize); - _monsterDirFlags = _staticres->loadRawData(kLolMonsterDirFlags, _monsterDirFlagsSize); - _monsterScaleX = _staticres->loadRawData(kLolMonsterScaleX, _monsterScaleXSize); - _monsterScaleY = _staticres->loadRawData(kLolMonsterScaleY, _monsterScaleYSize); - _monsterScaleWH = _staticres->loadRawDataBe16(kLolMonsterScaleWH, _monsterScaleWHSize); - _inventorySlotDesc = _staticres->loadRawDataBe16(kLolInventoryDesc, _inventorySlotDescSize); - _levelShpList = _staticres->loadStrings(kLolLevelShpList, _levelShpListSize); - _levelDatList = _staticres->loadStrings(kLolLevelDatList, _levelDatListSize); - _compassDefs = _staticres->loadCompassData(kLolCompassDefs, _compassDefsSize); - _flyingItemShapes = _staticres->loadFlyingObjectData(kLolFlyingObjectShp, _flyingItemShapesSize); - _itemCost = _staticres->loadRawDataBe16(kLolItemPrices, _itemCostSize); - _stashSetupData = _staticres->loadRawData(kLolStashSetup, _stashSetupDataSize); - - _dscUnk1 = (const int8 *)_staticres->loadRawData(kLolDscUnk1, _dscUnk1Size); - _dscShapeIndex = (const int8 *)_staticres->loadRawData(kLolDscShapeIndex, _dscShapeIndexSize); - _dscOvlMap = _staticres->loadRawData(kLolDscOvlMap, _dscOvlMapSize); - _dscShapeScaleW = _staticres->loadRawDataBe16(kLolDscScaleWidthData, _dscShapeScaleWSize); - _dscShapeScaleH = _staticres->loadRawDataBe16(kLolDscScaleHeightData, _dscShapeScaleHSize); - _dscShapeX = (const int16 *)_staticres->loadRawDataBe16(kLolDscX, _dscShapeXSize); - _dscShapeY = (const int8 *)_staticres->loadRawData(kLolDscY, _dscShapeYSize); - _dscTileIndex = _staticres->loadRawData(kLolDscTileIndex, _dscTileIndexSize); - _dscUnk2 = _staticres->loadRawData(kLolDscUnk2, _dscUnk2Size); - _dscDoorShpIndex = _staticres->loadRawData(kLolDscDoorShapeIndex, _dscDoorShpIndexSize); - _dscDim1 = (const int8 *)_staticres->loadRawData(kLolDscDimData1, _dscDim1Size); - _dscDim2 = (const int8 *)_staticres->loadRawData(kLolDscDimData2, _dscDim2Size); - _dscBlockMap = _staticres->loadRawData(kLolDscBlockMap, _dscBlockMapSize); - _dscDimMap = _staticres->loadRawData(kLolDscDimMap, _dscDimMapSize); - _dscDoorMonsterScaleTable = _staticres->loadRawDataBe16(kLolDscDoorScale, _dscDoorMonsterScaleTableSize); - _dscShapeOvlIndex = _staticres->loadRawData(kLolDscOvlIndex, _dscShapeOvlIndexSize); - _dscDoor4 = _staticres->loadRawDataBe16(kLolDscDoor4, _dscDoor4Size); - _dscBlockIndex = (const int8 *)_staticres->loadRawData(kLolDscBlockIndex, _dscBlockIndexSize); - _dscDoor1 = _staticres->loadRawData(kLolDscDoor1, _dscDoor1Size); - _dscDoorMonsterX = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorX, _dscDoorMonsterXSize); - _dscDoorMonsterY = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorY, _dscDoorMonsterYSize); - - _scrollXTop = _staticres->loadRawData(kLolScrollXTop, _scrollXTopSize); - _scrollYTop = _staticres->loadRawData(kLolScrollYTop, _scrollYTopSize); - _scrollXBottom = _staticres->loadRawData(kLolScrollXBottom, _scrollXBottomSize); - _scrollYBottom = _staticres->loadRawData(kLolScrollYBottom, _scrollYBottomSize); - - const char *const *tmpSndList = _staticres->loadStrings(kLolIngameSfxFiles, _ingameSoundListSize); - if (tmpSndList) { - _ingameSoundList = new char *[_ingameSoundListSize]; - for (int i = 0; i < _ingameSoundListSize; i++) { - _ingameSoundList[i] = new char[strlen(tmpSndList[i]) + 1]; - strcpy(_ingameSoundList[i], tmpSndList[i]); - } - _staticres->unloadId(kLolIngameSfxFiles); - } - - _buttonData = _staticres->loadButtonDefs(kLolButtonDefs, _buttonDataSize); - _buttonList1 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList1, _buttonList1Size); - _buttonList2 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList2, _buttonList2Size); - _buttonList3 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList3, _buttonList3Size); - _buttonList4 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList4, _buttonList4Size); - _buttonList5 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList5, _buttonList5Size); - _buttonList6 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList6, _buttonList6Size); - _buttonList7 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList7, _buttonList7Size); - _buttonList8 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList8, _buttonList8Size); - - _autoMapStrings = _staticres->loadRawDataBe16(kLolMapStringId, _autoMapStringsSize); - - int tmpSize = 0; - const uint8 *tmp = _staticres->loadRawData(kLolLegendData, tmpSize); - tmpSize /= 5; - if (tmp) { - _defaultLegendData = new MapLegendData[tmpSize]; - for (int i = 0; i < tmpSize; i++) { - _defaultLegendData[i].shapeIndex = *tmp++; - _defaultLegendData[i].enable = *tmp++ ? true : false; - _defaultLegendData[i].x = (int8)*tmp++; - _defaultLegendData[i].stringId = READ_LE_UINT16(tmp); - tmp += 2; - } - _staticres->unloadId(kLolLegendData); - } - - tmp = _staticres->loadRawData(kLolMapCursorOvl, tmpSize); - _mapCursorOverlay = new uint8[tmpSize]; - memcpy(_mapCursorOverlay, tmp, tmpSize); - _staticres->unloadId(kLolMapCursorOvl); - - _updateSpellBookCoords = _staticres->loadRawData(kLolSpellbookCoords, _updateSpellBookCoordsSize); - _updateSpellBookAnimData = _staticres->loadRawData(kLolSpellbookAnim, _updateSpellBookAnimDataSize); - _healShapeFrames = _staticres->loadRawData(kLolHealShapeFrames, _healShapeFramesSize); - - tmp = _staticres->loadRawData(kLolLightningDefs, tmpSize); - if (tmp) { - _lightningProps = new LightningProperty[5]; - for (int i = 0; i < 5; i++) { - _lightningProps[i].lastFrame = tmp[i << 2]; - _lightningProps[i].frameDiv = tmp[(i << 2) + 1]; - _lightningProps[i].sfxId = READ_LE_UINT16(&tmp[(i << 2) + 2]); - } - _staticres->unloadId(kLolLightningDefs); - } - - _fireBallCoords = (const int16 *)_staticres->loadRawDataBe16(kLolFireballCoords, _fireBallCoordsSize); - - _buttonCallbacks.clear(); - _buttonCallbacks.reserve(95); -#define cb(x) _buttonCallbacks.push_back(BUTTON_FUNCTOR(LoLEngine, this, &LoLEngine::x)) - // 0x00 - cb(clickedUpArrow); - cb(clickedDownArrow); - _buttonCallbacks.push_back(_buttonCallbacks[1]); - cb(clickedLeftArrow); - - // 0x04 - cb(clickedRightArrow); - cb(clickedTurnLeftArrow); - cb(clickedTurnRightArrow); - cb(clickedAttackButton); - - // 0x08 - for (int i = 0; i < 3; ++i) - _buttonCallbacks.push_back(_buttonCallbacks[7]); - cb(clickedMagicButton); - - // 0x0C - for (int i = 0; i < 3; ++i) - _buttonCallbacks.push_back(_buttonCallbacks[11]); - cb(clickedMagicSubmenu); - - // 0x10 - cb(clickedScreen); - cb(clickedPortraitLeft); - for (int i = 0; i < 7; ++i) - _buttonCallbacks.push_back(_buttonCallbacks[17]); - - // 0x19 - cb(clickedLiveMagicBarsLeft); - for (int i = 0; i < 3; ++i) - _buttonCallbacks.push_back(_buttonCallbacks[25]); - - // 0x1D - cb(clickedPortraitEtcRight); - for (int i = 0; i < 3; ++i) - _buttonCallbacks.push_back(_buttonCallbacks[29]); - - // 0x21 - cb(clickedCharInventorySlot); - for (int i = 0; i < 10; ++i) - _buttonCallbacks.push_back(_buttonCallbacks[33]); - - // 0x2C - cb(clickedExitCharInventory); - cb(clickedSceneDropItem); - for (int i = 0; i < 3; ++i) - _buttonCallbacks.push_back(_buttonCallbacks[45]); - - // 0x31 - cb(clickedScenePickupItem); - cb(clickedInventorySlot); - for (int i = 0; i < 9; ++i) - _buttonCallbacks.push_back(_buttonCallbacks[50]); - - // 0x3C - cb(clickedInventoryScroll); - cb(clickedInventoryScroll); - cb(clickedWall); - _buttonCallbacks.push_back(_buttonCallbacks[62]); - - // 0x40 - cb(clickedSequenceWindow); - _buttonCallbacks.push_back(_buttonCallbacks[0]); - _buttonCallbacks.push_back(_buttonCallbacks[1]); - _buttonCallbacks.push_back(_buttonCallbacks[3]); - - // 0x44 - _buttonCallbacks.push_back(_buttonCallbacks[4]); - _buttonCallbacks.push_back(_buttonCallbacks[5]); - _buttonCallbacks.push_back(_buttonCallbacks[6]); - cb(clickedScroll); - - // 0x48 - for (int i = 0; i < 9; ++i) - _buttonCallbacks.push_back(_buttonCallbacks[71]); - - // 0x51 - cb(clickedSpellTargetCharacter); - for (int i = 0; i < 3; ++i) - _buttonCallbacks.push_back(_buttonCallbacks[81]); - - // 0x55 - cb(clickedSpellTargetScene); - cb(clickedSceneThrowItem); - _buttonCallbacks.push_back(_buttonCallbacks[86]); - - // 0x58 - cb(clickedOptions); - cb(clickedRestParty); - cb(clickedMoneyBox); - cb(clickedCompass); - - // 0x5C - cb(clickedAutomap); - cb(clickedLamp); - cb(clickedStatusIcon); -#undef cb -} - -void GUI_LoL::initStaticData() { - GUI_V2_BUTTON(_scrollUpButton, 20, 96, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0); - GUI_V2_BUTTON(_scrollDownButton, 21, 98, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0); - - for (uint i = 0; i < ARRAYSIZE(_menuButtons); ++i) - GUI_V2_BUTTON(_menuButtons[i], i, 0, 0, 0, 0, 0, 0x4487, 0, 0, 0, 0, 0, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0); - - if (_vm->gameFlags().isTalkie) - GUI_LOL_MENU(_mainMenu, 9, 0x4000, 0, 7, -1, -1, -1, -1); - else - GUI_LOL_MENU(_mainMenu, 17, 0x4000, 0, 6, -1, -1, -1, -1); - - GUI_LOL_MENU_ITEM(_mainMenu.item[0], 0x4001, 16, 23, 176, 15, 0, 0); - GUI_LOL_MENU_ITEM(_mainMenu.item[1], 0x4002, 16, 40, 176, 15, 0, 0); - GUI_LOL_MENU_ITEM(_mainMenu.item[2], 0x4003, 16, 57, 176, 15, 0, 0); - GUI_LOL_MENU_ITEM(_mainMenu.item[3], 0x4004, 16, 74, 176, 15, 0, 0); - - if (_vm->gameFlags().isTalkie) { - GUI_LOL_MENU_ITEM(_mainMenu.item[4], 0x42D9, 16, 91, 176, 15, 0, 0); - GUI_LOL_MENU_ITEM(_mainMenu.item[5], 0x4006, 16, 108, 176, 15, 0, 0); - GUI_LOL_MENU_ITEM(_mainMenu.item[6], 0x4005, 88, 127, 104, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); - } else { - GUI_LOL_MENU_ITEM(_mainMenu.item[4], 0x4006, 16, 91, 176, 15, 0, 0); - GUI_LOL_MENU_ITEM(_mainMenu.item[5], 0x4005, 88, 110, 104, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); - } - - Button::Callback mainMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedMainMenu); - for (int i = 0; i < _mainMenu.numberOfItems; ++i) - _mainMenu.item[i].callback = mainMenuFunctor; - - GUI_LOL_MENU(_loadMenu, 10, 0x400e, 1, 5, 128, 20, 128, 118); - GUI_LOL_MENU_ITEM(_loadMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_loadMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_loadMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_loadMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_loadMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); - Button::Callback loadMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedLoadMenu); - for (int i = 0; i < 5; ++i) - _loadMenu.item[i].callback = loadMenuFunctor; - - GUI_LOL_MENU(_saveMenu, 10, 0x400d, 1, 5, 128, 20, 128, 118); - GUI_LOL_MENU_ITEM(_saveMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_saveMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_saveMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_saveMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_saveMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); - Button::Callback saveMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedSaveMenu); - for (int i = 0; i < 5; ++i) - _saveMenu.item[i].callback = saveMenuFunctor; - - GUI_LOL_MENU(_deleteMenu, 10, 0x400f, 1, 5, 128, 20, 128, 118); - GUI_LOL_MENU_ITEM(_deleteMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_deleteMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_deleteMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_deleteMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0); - GUI_LOL_MENU_ITEM(_deleteMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); - Button::Callback deleteMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedDeleteMenu); - for (int i = 0; i < 5; ++i) - _deleteMenu.item[i].callback = deleteMenuFunctor; - - GUI_LOL_MENU(_gameOptions, 17, 0x400c, 0, 6, -1, -1, -1, -1); - if (_vm->gameFlags().isTalkie) { - GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xfff7, 120, 22, 80, 15, 0x406e, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xfff6, 120, 39, 80, 15, 0x406c, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xfff5, 120, 56, 80, 15, 0x406d, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xfff4, 120, 73, 80, 15, 0x42d5, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xfff3, 120, 90, 80, 15, 0x42d2, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[5], 0x4072, 104, 110, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); - } else { - GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xfff9, 120, 22, 80, 15, 0x406a, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xfff8, 120, 39, 80, 15, 0x406b, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xfff7, 120, 56, 80, 15, 0x406e, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xfff6, 120, 73, 80, 15, 0x406c, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xfff5, 120, 90, 80, 15, 0x406d, 0); - GUI_LOL_MENU_ITEM(_gameOptions.item[5], 0x4072, 104, 110, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); - } - Button::Callback optionsMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedOptionsMenu); - for (int i = 0; i < _gameOptions.numberOfItems; ++i) - _gameOptions.item[i].callback = optionsMenuFunctor; - - GUI_LOL_MENU(_audioOptions, 18, 0x42d9, 2, 1, -1, -1, -1, -1); - GUI_LOL_MENU_ITEM(_audioOptions.item[0], 0x4072, 152, 76, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); - GUI_LOL_MENU_ITEM(_audioOptions.item[1], 3, 128, 22, 114, 14, 0x42db, 0); - GUI_LOL_MENU_ITEM(_audioOptions.item[2], 4, 128, 39, 114, 14, 0x42da, 0); - GUI_LOL_MENU_ITEM(_audioOptions.item[3], 5, 128, 56, 114, 14, 0x42dc, 0); - Button::Callback audioMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedAudioMenu); - for (int i = 0; i < 4; ++i) - _audioOptions.item[i].callback = audioMenuFunctor; - - GUI_LOL_MENU(_deathMenu, 11, 0x4013, 0, 2, -1, -1, -1, -1); - GUI_LOL_MENU_ITEM(_deathMenu.item[0], 0x4006, 8, 30, 104, 15, 0, 0); - GUI_LOL_MENU_ITEM(_deathMenu.item[1], 0x4001, 176, 30, 104, 15, 0, 0); - Button::Callback deathMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedDeathMenu); - for (int i = 0; i < 2; ++i) - _deathMenu.item[i].callback = deathMenuFunctor; - - GUI_LOL_MENU(_savenameMenu, 7, 0x4053, 0, 2, -1, -1, -1, -1); - GUI_LOL_MENU_ITEM(_savenameMenu.item[0], 0x4012, 8, 38, 72, 15, 0, _vm->_keyMap[Common::KEYCODE_RETURN]); - GUI_LOL_MENU_ITEM(_savenameMenu.item[1], 0x4011, 176, 38, 72, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); - Button::Callback savenameMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedSavenameMenu); - for (int i = 0; i < 2; ++i) - _savenameMenu.item[i].callback = savenameMenuFunctor; - - GUI_LOL_MENU(_choiceMenu, 11, 0, 0, 2, -1, -1, -1, -1); - GUI_LOL_MENU_ITEM(_choiceMenu.item[0], 0x4007, 8, 30, 72, 15, 0, 0); - GUI_LOL_MENU_ITEM(_choiceMenu.item[1], 0x4008, 208, 30, 72, 15, 0, 0); - Button::Callback choiceMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedChoiceMenu); - for (int i = 0; i < 2; ++i) - _choiceMenu.item[i].callback = choiceMenuFunctor; -} - -#endif // ENABLE_LOL - const uint8 Screen_LoK_16::_palette16[48] = { 0x00, 0x00, 0x00, 0x02, 0x07, 0x0B, 0x0C, 0x06, 0x04, 0x0E, 0x09, 0x07, 0x00, 0x06, 0x03, 0x00, 0x0C, 0x07, @@ -2745,263 +2141,5 @@ const int8 KyraEngine_MR::_albumWSAY[] = { -1, -2, 2, 2, -6, -6, -6, 0 }; -// lands of lore static res - -#ifdef ENABLE_LOL -const ScreenDim Screen_LoL::_screenDimTable256C[] = { - { 0x00, 0x00, 0x28, 0xC8, 0xC7, 0xCF, 0x00, 0x00 }, // Taken from Intro - { 0x08, 0x48, 0x18, 0x38, 0xFE, 0x01, 0x00, 0x00 }, - { 0x0E, 0x00, 0x16, 0x78, 0xFE, 0x01, 0x00, 0x00 }, - { 0x0B, 0x7B, 0x1C, 0x12, 0xFE, 0xFC, 0x00, 0x00 }, - { 0x0B, 0x7B, 0x1C, 0x2D, 0xFE, 0xFC, 0x00, 0x00 }, - { 0x55, 0x7B, 0xE9, 0x37, 0xFE, 0xFC, 0x00, 0x00 }, - { 0x0B, 0x8C, 0x10, 0x2B, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (4 entries) - { 0x04, 0x59, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x00 }, - { 0x05, 0x6E, 0x1E, 0x0C, 0xFE, 0x01, 0x00, 0x00 }, - { 0x07, 0x19, 0x1A, 0x97, 0x00, 0x00, 0x00, 0x00 }, // Ingame main menu box CD version - { 0x03, 0x1E, 0x22, 0x8C, 0x00, 0x00, 0x00, 0x00 }, - { 0x02, 0x48, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x0E, 0x00, 0x16, 0x78, 0xFE, 0x01, 0x00, 0x00 }, - { 0x0D, 0xA2, 0x18, 0x0C, 0xFE, 0x01, 0x00, 0x00 }, - { 0x0F, 0x06, 0x14, 0x6E, 0x01, 0x00, 0x00, 0x00 }, - { 0x1A, 0xBE, 0x0A, 0x07, 0xFE, 0x01, 0x00, 0x00 }, - { 0x07, 0x21, 0x1A, 0x85, 0x00, 0x00, 0x00, 0x00 }, - { 0x03, 0x32, 0x22, 0x62, 0x00, 0x00, 0x00, 0x00 }, - { 0x0B, 0x8C, 0x10, 0x33, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (5 entries, CD version only) - { 0x0B, 0x8C, 0x10, 0x23, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (3 entries, floppy version only) - - { 0x01, 0x20, 0x26, 0x80, 0xDC, 0xFD, 0x00, 0x00 }, // Credits - { 0x09, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 }, - { 0x19, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 }, - { 0x01, 0x02, 0x26, 0x14, 0x00, 0x0F, 0x0E, 0x00 }, -}; - -const ScreenDim Screen_LoL::_screenDimTable16C[] = { - { 0x00, 0x00, 0x28, 0xC8, 0x33, 0x44, 0x00, 0x00 }, // Taken from Intro - { 0x08, 0x48, 0x18, 0x38, 0x33, 0x44, 0x00, 0x00 }, - { 0x0E, 0x00, 0x16, 0x78, 0x33, 0x44, 0x00, 0x00 }, - { 0x0B, 0x7B, 0x1C, 0x11, 0x33, 0x11, 0x00, 0x00 }, - { 0x0B, 0x7B, 0x1C, 0x2D, 0x33, 0x11, 0x00, 0x00 }, - { 0x55, 0x7B, 0xE9, 0x37, 0x33, 0x11, 0x00, 0x00 }, - { 0x0B, 0x92, 0x10, 0x2A, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (4 entries) - { 0x04, 0x58, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x00 }, - { 0x05, 0x6C, 0x1E, 0x0D, 0x33, 0x44, 0x00, 0x00 }, - { 0x07, 0x20, 0x1A, 0x86, 0x00, 0x00, 0x00, 0x00 }, - { 0x03, 0x20, 0x22, 0x8C, 0x00, 0x00, 0x00, 0x00 }, - { 0x02, 0x48, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x0E, 0x00, 0x16, 0x78, 0x33, 0x44, 0x00, 0x00 }, - { 0x0D, 0xA2, 0x18, 0x0C, 0x33, 0x44, 0x00, 0x00 }, - { 0x0F, 0x06, 0x14, 0x6E, 0x44, 0x00, 0x00, 0x00 }, - { 0x1A, 0xBE, 0x0A, 0x07, 0x33, 0x44, 0x00, 0x00 }, - { 0x07, 0x21, 0x1A, 0x85, 0x00, 0x00, 0x00, 0x00 }, - { 0x03, 0x32, 0x22, 0x62, 0x00, 0x00, 0x00, 0x00 }, - { 0x0B, 0x8C, 0x10, 0x33, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (5 entries, not used here) - { 0x0B, 0x8C, 0x10, 0x23, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (3 entries) - - { 0x01, 0x20, 0x26, 0x80, 0xDC, 0xFD, 0x00, 0x00 }, // Credits (TODO: Check this!) - { 0x09, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 }, - { 0x19, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 }, - { 0x01, 0x02, 0x26, 0x14, 0x00, 0x0F, 0x0E, 0x00 }, -}; - -const int Screen_LoL::_screenDimTableCount = ARRAYSIZE(Screen_LoL::_screenDimTable256C); - -const char * const LoLEngine::_languageExt[] = { - "ENG", - "FRE", - "GER" -}; - -const LoLEngine::CharacterPrev LoLEngine::_charPreviews[] = { - { "Ak\'shel", 0x060, 0x7F, { 0x0F, 0x08, 0x05 } }, - { "Michael", 0x09A, 0x7F, { 0x06, 0x0A, 0x0F } }, - { "Kieran", 0x0D4, 0x7F, { 0x08, 0x06, 0x08 } }, - { "Conrad", 0x10F, 0x7F, { 0x0A, 0x0C, 0x0A } } -}; - -const uint16 LoLEngine::_charPosXPC98[] = { - 92, 152, 212, 268 -}; - -const uint8 LoLEngine::_charNamesPC98[][11] = { - { 0x83, 0x41, 0x83, 0x4E, 0x83, 0x56, 0x83, 0x46, 0x83, 0x8B, 0x00 }, - { 0x83, 0x7D, 0x83, 0x43, 0x83, 0x50, 0x83, 0x8B, 0x00, 0x00, 0x00 }, - { 0x83, 0x4C, 0x81, 0x5B, 0x83, 0x89, 0x83, 0x93, 0x00, 0x00, 0x00 }, - { 0x83, 0x52, 0x83, 0x93, 0x83, 0x89, 0x83, 0x62, 0x83, 0x68, 0x00 } -}; - -const uint8 LoLEngine::_chargenFrameTableTalkie[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, - 0x05, 0x04, 0x03, 0x02, 0x01, - 0x00, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0A, 0x0B, 0x0C, 0x0D, - 0x0E, 0x0F, 0x10, 0x11, 0x12 -}; - -const uint8 LoLEngine::_chargenFrameTableFloppy[] = { - 0, 1, 2, 3, 4, 5, 4, 3, 2, - 1, 0, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15 -}; - -const uint16 LoLEngine::_selectionPosTable[] = { - 0x6F, 0x00, 0x8F, 0x00, 0xAF, 0x00, 0xCF, 0x00, - 0xEF, 0x00, 0x6F, 0x20, 0x8F, 0x20, 0xAF, 0x20, - 0xCF, 0x20, 0xEF, 0x20, 0x6F, 0x40, 0x8F, 0x40, - 0xAF, 0x40, 0xCF, 0x40, 0xEF, 0x40, 0x10F, 0x00 -}; - -const uint8 LoLEngine::_selectionChar1IdxTable[] = { - 0, 0, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 0, 0, 5, 5, 5, - 5, 5, 5, 5, 0, 0, 5, 5, - 5, 5, 5 -}; - -const uint8 LoLEngine::_selectionChar2IdxTable[] = { - 1, 1, 6, 6, 1, 1, 6, 6, - 6, 6, 6, 6, 6, 1, 1, 6, - 6, 6, 1, 1, 6, 6, 6, 6, - 6, 6, 6 -}; - -const uint8 LoLEngine::_selectionChar3IdxTable[] = { - 2, 2, 7, 7, 7, 7, 2, 2, - 7, 7, 7, 7, 7, 7, 7, 2, - 2, 7, 7, 7, 7, 2, 2, 7, - 7, 7, 7 -}; - -const uint8 LoLEngine::_selectionChar4IdxTable[] = { - 3, 3, 8, 8, 8, 8, 3, 3, - 8, 8, 3, 3, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 3, 3, 8, - 8, 8, 8 -}; - -const uint8 LoLEngine::_reminderChar1IdxTable[] = { - 4, 4, 4, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 5 -}; - -const uint8 LoLEngine::_reminderChar2IdxTable[] = { - 9, 9, 9, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, - 6 -}; - -const uint8 LoLEngine::_reminderChar3IdxTable[] = { - 0xE, 0xE, 0xE, 0x7, 0x7, 0x7, 0x7, 0x7, - 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, - 0x7 -}; - -const uint8 LoLEngine::_reminderChar4IdxTable[] = { - 0xF, 0xF, 0xF, 0x8, 0x8, 0x8, 0x8, 0x8, - 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, - 0x8 -}; - -const uint8 LoLEngine::_selectionAnimIndexTable[] = { - 0, 5, 1, 6, 2, 7, 3, 8 -}; - -const uint8 LoLEngine::_charInfoFrameTable[] = { - 0x0, 0x7, 0x8, 0x9, 0xA, 0xB, 0xA, 0x9, - 0x8, 0x7, 0x0, 0x0, 0x7, 0x8, 0x9, 0xA, - 0xB, 0xA, 0x9, 0x8, 0x7, 0x0, 0x0, 0x7, - 0x8, 0x9, 0xA, 0xB, 0xA, 0x9, 0x8, 0x7 -}; - -const uint8 LoLEngine::_clock2Timers[] = { - 0x00, 0x10, 0x11, 0x03, 0x04, 0x50, - 0x51, 0x52, 0x08, 0x09, 0x0A -}; - -const uint8 LoLEngine::_numClock2Timers = ARRAYSIZE(LoLEngine::_clock2Timers); - -const int8 LoLEngine::_mapCoords[12][4] = { - { 0, 7, 0, -5 }, { -5, 0, 6, 0 }, { 7, 5, 7, 1 }, - { 5, 6, 4, 6 }, { 0, 7, 0, -1 }, { -3, 0, 6, 0 }, - { 6, 7, 6, -3 }, { -3, 5, 6, 5 }, { 1, 5, 1, 1 }, - { 3, 1, 3, 1 }, { -1, 6, -1, -8 }, { -7, -1, 5, -1 } -}; - -const MistOfDoomAnimData LoLEngine::_mistAnimData[] = { - { 0, 7, 7, 13, 155 }, - { 0, 16, 16, 17, 155 }, - { 0, 24, 24, 24, 174 }, - { 0, 19, 19, 19, 174 }, - { 0, 16, 16, 17, 175 }, -}; - -const char * const LoLEngine::_outroShapeFileTable[] = { - "AMAZON.SHP", "ARCHRSLG.SHP", "AVIANWRM.SHP", "BANDIT.SHP", "BOAR.SHP", "CABAL.SHP", - "GUARD.SHP", "HAG.SHP", "HORNET.SHP", "HURZELL.SHP", "IRONGRZR.SHP", "KNOWLES.SHP", - "LIZARD.SHP", "MANTHA.SHP", "MINOTAUR.SHP", "MORIBUND.SHP", "ORC.SHP", "ORCLDR.SHP", - "PENTROG.SHP", "RATMAN.SHP", "ROCKLING.SHP", "SCAVNGR.SHP", "STARK.SHP", - "SWAMPCIT.SHP", "SWAMPMON.SHP", "THUG.SHP", "VIPER.SHP", "XEOB.SHP" -}; - -const uint8 LoLEngine::_outroFrameTable[] = { - 0, 0, 0, 0, 0, 1, 2, 3, - 0, 1, 2, 3, 8, 9, 10, 11, - 8, 9, 10, 11, 4, 5, 6, 7 -}; - -const int16 LoLEngine::_outroRightMonsterPos[] = { - 205, 55, 205, 55, 205, 55, 205, 55, - 205, 56, 207, 57, 208, 58, 210, 59, - 213, 60, 216, 61, 220, 61, 225, 61, - 230, 61, 235, 61, 240, 61, 240, 61, - 240, 61, 240, 61, 240, 61, 240, 61, - 240, 61, 265, 61, 290, 61, 315, 61 -}; - -const int16 LoLEngine::_outroLeftMonsterPos[] = { - 92, 55, 92, 55, 92, 55, 92, 55, - 92, 56, 90, 57, 85, 58, 77, 59, - 67, 60, 57, 61, 47, 61, 35, 61, - 35, 61, 35, 61, 35, 61, 35, 61, - 35, 61, 35, 61, 35, 61, 35, 61, - 35, 61, 10, 61, -20, 61, -45, 61 -}; - -const int16 LoLEngine::_outroRightDoorPos[] = { - 200, 41, 200, 29, 200, 17, 200, 5, - 200, -7, 200, -7, 200, -7, 200, -7, - 200, 5, 200, 17, 200, 29, 200, 41, - 200, 41, 200, 41, 200, 41, 200, 41, - 200, 41, 200, 41, 200, 41, 200, 41, - 200, 41, 200, 41, 200, 41, 200, 41 -}; - -const int16 LoLEngine::_outroLeftDoorPos[] = { - 72, 41, 72, 29, 72, 17, 72, 5, - 72, -7, 72, -7, 72, -7, 72, -7, - 72, 5, 72, 17, 72, 29, 72, 41, - 72, 41, 72, 41, 72, 41, 72, 41, - 72, 41, 72, 41, 72, 41, 72, 41, - 72, 41, 72, 41, 72, 41, 72, 41 -}; - -const int LoLEngine::_outroMonsterScaleTableX[] = { - 0x050, 0x050, 0x050, 0x050, 0x050, 0x05D, 0x070, 0x085, - 0x0A0, 0x0C0, 0x0E2, 0x100, 0x100, 0x100, 0x100, 0x100, - 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100 -}; - -const int LoLEngine::_outroMonsterScaleTableY[] = { - 0x04C, 0x04C, 0x04C, 0x04C, 0x04C, 0x059, 0x06B, 0x080, - 0x099, 0x0B8, 0x0D9, 0x100, 0x100, 0x100, 0x100, 0x100, - 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100 -}; - -#endif // ENABLE_LOL - } // End of namespace Kyra diff --git a/engines/kyra/staticres_lol.cpp b/engines/kyra/staticres_lol.cpp new file mode 100644 index 0000000000..dbf6808e37 --- /dev/null +++ b/engines/kyra/staticres_lol.cpp @@ -0,0 +1,882 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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 "kyra/resource.h" +#include "kyra/lol.h" +#include "kyra/screen_lol.h" +#include "kyra/gui_lol.h" + +#ifdef ENABLE_LOL + +namespace Kyra { + +const LoLCharacter *StaticResource::loadCharData(int id, int &entries) { + return (const LoLCharacter *)getData(id, kLolCharData, entries); +} + +const SpellProperty *StaticResource::loadSpellData(int id, int &entries) { + return (const SpellProperty *)getData(id, kLolSpellData, entries); +} + +const CompassDef *StaticResource::loadCompassData(int id, int &entries) { + return (const CompassDef *)getData(id, kLolCompassData, entries); +} + +const FlyingObjectShape *StaticResource::loadFlyingObjectData(int id, int &entries) { + return (const FlyingObjectShape *)getData(id, kLolFlightShpData, entries); +} + +const uint16 *StaticResource::loadRawDataBe16(int id, int &entries) { + return (const uint16 *)getData(id, kLolRawDataBe16, entries); +} + +const uint32 *StaticResource::loadRawDataBe32(int id, int &entries) { + return (const uint32 *)getData(id, kLolRawDataBe32, entries); +} + +const ButtonDef *StaticResource::loadButtonDefs(int id, int &entries) { + return (const ButtonDef *)getData(id, kLolButtonData, entries); +} + +bool StaticResource::loadCharData(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() / 130; + LoLCharacter *charData = new LoLCharacter[size]; + + for (int i = 0; i < size; i++) { + LoLCharacter *t = &charData[i]; + + t->flags = stream.readUint16LE(); + stream.read(t->name, 11); + t->raceClassSex = stream.readByte(); + t->id = stream.readSint16LE(); + t->curFaceFrame = stream.readByte(); + t->tempFaceFrame = stream.readByte(); + t->screamSfx = stream.readByte(); + stream.readUint32LE(); + for (int ii = 0; ii < 8; ii++) + t->itemsMight[ii] = stream.readUint16LE(); + for (int ii = 0; ii < 8; ii++) + t->protectionAgainstItems[ii] = stream.readUint16LE(); + t->itemProtection = stream.readUint16LE(); + t->hitPointsCur = stream.readSint16LE(); + t->hitPointsMax = stream.readUint16LE(); + t->magicPointsCur = stream.readSint16LE(); + t->magicPointsMax = stream.readUint16LE(); + t->field_41 = stream.readByte(); + t->damageSuffered = stream.readUint16LE(); + t->weaponHit = stream.readUint16LE(); + t->totalMightModifier = stream.readUint16LE(); + t->totalProtectionModifier = stream.readUint16LE(); + t->might = stream.readUint16LE(); + t->protection = stream.readUint16LE(); + t->nextAnimUpdateCountdown = stream.readSint16LE(); + for (int ii = 0; ii < 11; ii++) + t->items[ii] = stream.readUint16LE(); + for (int ii = 0; ii < 3; ii++) + t->skillLevels[ii] = stream.readByte(); + for (int ii = 0; ii < 3; ii++) + t->skillModifiers[ii] = stream.readByte(); + for (int ii = 0; ii < 3; ii++) + t->experiencePts[ii] = stream.readUint32LE(); + for (int ii = 0; ii < 5; ii++) + t->characterUpdateEvents[ii] = stream.readByte(); + for (int ii = 0; ii < 5; ii++) + t->characterUpdateDelay[ii] = stream.readByte(); + }; + + ptr = charData; + return true; +} + +bool StaticResource::loadSpellData(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() / 28; + SpellProperty *spellData = new SpellProperty[size]; + + for (int i = 0; i < size; i++) { + SpellProperty *t = &spellData[i]; + + t->spellNameCode = stream.readUint16LE(); + for (int ii = 0; ii < 4; ii++) + t->mpRequired[ii] = stream.readUint16LE(); + t->field_a = stream.readUint16LE(); + t->field_c = stream.readUint16LE(); + for (int ii = 0; ii < 4; ii++) + t->hpRequired[ii] = stream.readUint16LE(); + t->field_16 = stream.readUint16LE(); + t->field_18 = stream.readUint16LE(); + t->flags = stream.readUint16LE(); + }; + + ptr = spellData; + return true; +} + +bool StaticResource::loadCompassData(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() / 4; + CompassDef *defs = new CompassDef[size]; + + for (int i = 0; i < size; i++) { + CompassDef *t = &defs[i]; + t->shapeIndex = stream.readByte(); + t->x = stream.readByte(); + t->y = stream.readByte(); + t->flags = stream.readByte(); + }; + + + ptr = defs; + return true; +} + +bool StaticResource::loadFlyingObjectData(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() / 5; + FlyingObjectShape *defs = new FlyingObjectShape[size]; + + for (int i = 0; i < size; i++) { + FlyingObjectShape *t = &defs[i]; + t->shapeFront = stream.readByte(); + t->shapeBack = stream.readByte(); + t->shapeLeft = stream.readByte(); + t->drawFlags = stream.readByte(); + t->flipFlags = stream.readByte(); + }; + + ptr = defs; + return true; +} + +bool StaticResource::loadRawDataBe16(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() >> 1; + + uint16 *r = new uint16[size]; + + for (int i = 0; i < size; i++) + r[i] = stream.readUint16BE(); + + ptr = r; + return true; +} + +bool StaticResource::loadRawDataBe32(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() >> 2; + + uint32 *r = new uint32[size]; + + for (int i = 0; i < size; i++) + r[i] = stream.readUint32BE(); + + ptr = r; + return true; +} + +bool StaticResource::loadButtonDefs(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() / 18; + + ButtonDef *r = new ButtonDef[size]; + + for (int i = 0; i < size; i++) { + r[i].buttonflags = stream.readUint16BE(); + r[i].keyCode = stream.readUint16BE(); + r[i].keyCode2 = stream.readUint16BE(); + r[i].x = stream.readSint16BE(); + r[i].y = stream.readSint16BE(); + r[i].w = stream.readUint16BE(); + r[i].h = stream.readUint16BE(); + r[i].index = stream.readUint16BE(); + r[i].screenDim = stream.readUint16BE(); + } + + ptr = r; + return true; +} + +void StaticResource::freeCharData(void *&ptr, int &size) { + LoLCharacter *d = (LoLCharacter *)ptr; + delete[] d; + ptr = 0; + size = 0; +} + +void StaticResource::freeSpellData(void *&ptr, int &size) { + SpellProperty *d = (SpellProperty *)ptr; + delete[] d; + ptr = 0; + size = 0; +} + +void StaticResource::freeCompassData(void *&ptr, int &size) { + CompassDef *d = (CompassDef *)ptr; + delete[] d; + ptr = 0; + size = 0; +} + +void StaticResource::freeFlyingObjectData(void *&ptr, int &size) { + FlyingObjectShape *d = (FlyingObjectShape *)ptr; + delete[] d; + ptr = 0; + size = 0; +} + + +void StaticResource::freeRawDataBe16(void *&ptr, int &size) { + uint16 *data = (uint16 *)ptr; + delete[] data; + ptr = 0; + size = 0; +} + +void StaticResource::freeRawDataBe32(void *&ptr, int &size) { + uint32 *data = (uint32 *)ptr; + delete[] data; + ptr = 0; + size = 0; +} + +void StaticResource::freeButtonDefs(void *&ptr, int &size) { + ButtonDef *d = (ButtonDef *)ptr; + delete[] d; + ptr = 0; + size = 0; +} + +void LoLEngine::initStaticResource() { + // assign music data + static const char *pcMusicFileListIntro[] = { "LOREINTR" }; + static const char *pcMusicFileListFinale[] = { "LOREFINL" }; + static const char *pcMusicFileListIngame[] = { "LORE%02d%c" }; + + static const char *pc98MusicFileListIntro[] = { 0, "lore84.86", "lore82.86", 0, 0, 0, "lore83.86", "lore81.86" }; + static const char *pc98MusicFileListFinale[] = { 0, 0, "lore85.86", "lore86.86", "lore87.86" }; + static const char *pc98MusicFileListIngame[] = { "lore%02d.86" }; + + memset(_soundData, 0, sizeof(_soundData)); + if (_flags.platform == Common::kPlatformPC) { + _soundData[0].fileList = pcMusicFileListIntro; + _soundData[0].fileListLen = ARRAYSIZE(pcMusicFileListIntro); + _soundData[1].fileList = pcMusicFileListIngame; + _soundData[1].fileListLen = ARRAYSIZE(pcMusicFileListIngame); + _soundData[2].fileList = pcMusicFileListFinale; + _soundData[2].fileListLen = ARRAYSIZE(pcMusicFileListFinale); + } else if (_flags.platform == Common::kPlatformPC98) { + _soundData[0].fileList = pc98MusicFileListIntro; + _soundData[0].fileListLen = ARRAYSIZE(pc98MusicFileListIntro); + _soundData[1].fileList = pc98MusicFileListIngame; + _soundData[1].fileListLen = ARRAYSIZE(pc98MusicFileListIngame); + _soundData[2].fileList = pc98MusicFileListFinale; + _soundData[2].fileListLen = ARRAYSIZE(pc98MusicFileListFinale); + } + + if (_flags.isDemo) + return; + + _pakFileList = _staticres->loadStrings(kLolIngamePakFiles, _pakFileListSize); + _charDefaults = _staticres->loadCharData(kLolCharacterDefs, _charDefaultsSize); + _ingameSoundIndex = (const uint16 *)_staticres->loadRawData(kLolIngameSfxIndex, _ingameSoundIndexSize); + _musicTrackMap = _staticres->loadRawData(kLolMusicTrackMap, _musicTrackMapSize); + _ingameGMSoundIndex = _staticres->loadRawData(kLolIngameGMSfxIndex, _ingameGMSoundIndexSize); + _ingameMT32SoundIndex = _staticres->loadRawData(kLolIngameMT32SfxIndex, _ingameMT32SoundIndexSize); + _ingamePCSpeakerSoundIndex = _staticres->loadRawData(kLolIngamePcSpkSfxIndex, _ingamePCSpeakerSoundIndexSize); + _spellProperties = _staticres->loadSpellData(kLolSpellProperties, _spellPropertiesSize); + _gameShapeMap = (const int8 *)_staticres->loadRawData(kLolGameShapeMap, _gameShapeMapSize); + _sceneItemOffs = (const int8 *)_staticres->loadRawData(kLolSceneItemOffs, _sceneItemOffsSize); + _charInvIndex = _staticres->loadRawData(kLolCharInvIndex, _charInvIndexSize); + _charInvDefs = _staticres->loadRawData(kLolCharInvDefs, _charInvDefsSize); + _charDefsMan = _staticres->loadRawDataBe16(kLolCharDefsMan, _charDefsManSize); + _charDefsWoman = _staticres->loadRawDataBe16(kLolCharDefsWoman, _charDefsWomanSize); + _charDefsKieran = _staticres->loadRawDataBe16(kLolCharDefsKieran, _charDefsKieranSize); + _charDefsAkshel = _staticres->loadRawDataBe16(kLolCharDefsAkshel, _charDefsAkshelSize); + _expRequirements = (const int32 *)_staticres->loadRawDataBe32(kLolExpRequirements, _expRequirementsSize); + _monsterModifiers = _staticres->loadRawDataBe16(kLolMonsterModifiers, _monsterModifiersSize); + _monsterShiftOffs = (const int8 *)_staticres->loadRawData(kLolMonsterShiftOffsets, _monsterShiftOffsSize); + _monsterDirFlags = _staticres->loadRawData(kLolMonsterDirFlags, _monsterDirFlagsSize); + _monsterScaleX = _staticres->loadRawData(kLolMonsterScaleX, _monsterScaleXSize); + _monsterScaleY = _staticres->loadRawData(kLolMonsterScaleY, _monsterScaleYSize); + _monsterScaleWH = _staticres->loadRawDataBe16(kLolMonsterScaleWH, _monsterScaleWHSize); + _inventorySlotDesc = _staticres->loadRawDataBe16(kLolInventoryDesc, _inventorySlotDescSize); + _levelShpList = _staticres->loadStrings(kLolLevelShpList, _levelShpListSize); + _levelDatList = _staticres->loadStrings(kLolLevelDatList, _levelDatListSize); + _compassDefs = _staticres->loadCompassData(kLolCompassDefs, _compassDefsSize); + _flyingItemShapes = _staticres->loadFlyingObjectData(kLolFlyingObjectShp, _flyingItemShapesSize); + _itemCost = _staticres->loadRawDataBe16(kLolItemPrices, _itemCostSize); + _stashSetupData = _staticres->loadRawData(kLolStashSetup, _stashSetupDataSize); + + _dscUnk1 = (const int8 *)_staticres->loadRawData(kLolDscUnk1, _dscUnk1Size); + _dscShapeIndex = (const int8 *)_staticres->loadRawData(kLolDscShapeIndex, _dscShapeIndexSize); + _dscOvlMap = _staticres->loadRawData(kLolDscOvlMap, _dscOvlMapSize); + _dscShapeScaleW = _staticres->loadRawDataBe16(kLolDscScaleWidthData, _dscShapeScaleWSize); + _dscShapeScaleH = _staticres->loadRawDataBe16(kLolDscScaleHeightData, _dscShapeScaleHSize); + _dscShapeX = (const int16 *)_staticres->loadRawDataBe16(kLolDscX, _dscShapeXSize); + _dscShapeY = (const int8 *)_staticres->loadRawData(kLolDscY, _dscShapeYSize); + _dscTileIndex = _staticres->loadRawData(kLolDscTileIndex, _dscTileIndexSize); + _dscUnk2 = _staticres->loadRawData(kLolDscUnk2, _dscUnk2Size); + _dscDoorShpIndex = _staticres->loadRawData(kLolDscDoorShapeIndex, _dscDoorShpIndexSize); + _dscDim1 = (const int8 *)_staticres->loadRawData(kLolDscDimData1, _dscDim1Size); + _dscDim2 = (const int8 *)_staticres->loadRawData(kLolDscDimData2, _dscDim2Size); + _dscBlockMap = _staticres->loadRawData(kLolDscBlockMap, _dscBlockMapSize); + _dscDimMap = _staticres->loadRawData(kLolDscDimMap, _dscDimMapSize); + _dscDoorMonsterScaleTable = _staticres->loadRawDataBe16(kLolDscDoorScale, _dscDoorMonsterScaleTableSize); + _dscShapeOvlIndex = _staticres->loadRawData(kLolDscOvlIndex, _dscShapeOvlIndexSize); + _dscDoor4 = _staticres->loadRawDataBe16(kLolDscDoor4, _dscDoor4Size); + _dscBlockIndex = (const int8 *)_staticres->loadRawData(kLolDscBlockIndex, _dscBlockIndexSize); + _dscDoor1 = _staticres->loadRawData(kLolDscDoor1, _dscDoor1Size); + _dscDoorMonsterX = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorX, _dscDoorMonsterXSize); + _dscDoorMonsterY = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorY, _dscDoorMonsterYSize); + + _scrollXTop = _staticres->loadRawData(kLolScrollXTop, _scrollXTopSize); + _scrollYTop = _staticres->loadRawData(kLolScrollYTop, _scrollYTopSize); + _scrollXBottom = _staticres->loadRawData(kLolScrollXBottom, _scrollXBottomSize); + _scrollYBottom = _staticres->loadRawData(kLolScrollYBottom, _scrollYBottomSize); + + const char *const *tmpSndList = _staticres->loadStrings(kLolIngameSfxFiles, _ingameSoundListSize); + if (tmpSndList) { + _ingameSoundList = new char *[_ingameSoundListSize]; + for (int i = 0; i < _ingameSoundListSize; i++) { + _ingameSoundList[i] = new char[strlen(tmpSndList[i]) + 1]; + strcpy(_ingameSoundList[i], tmpSndList[i]); + } + _staticres->unloadId(kLolIngameSfxFiles); + } + + _buttonData = _staticres->loadButtonDefs(kLolButtonDefs, _buttonDataSize); + _buttonList1 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList1, _buttonList1Size); + _buttonList2 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList2, _buttonList2Size); + _buttonList3 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList3, _buttonList3Size); + _buttonList4 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList4, _buttonList4Size); + _buttonList5 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList5, _buttonList5Size); + _buttonList6 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList6, _buttonList6Size); + _buttonList7 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList7, _buttonList7Size); + _buttonList8 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList8, _buttonList8Size); + + _autoMapStrings = _staticres->loadRawDataBe16(kLolMapStringId, _autoMapStringsSize); + + int tmpSize = 0; + const uint8 *tmp = _staticres->loadRawData(kLolLegendData, tmpSize); + tmpSize /= 5; + if (tmp) { + _defaultLegendData = new MapLegendData[tmpSize]; + for (int i = 0; i < tmpSize; i++) { + _defaultLegendData[i].shapeIndex = *tmp++; + _defaultLegendData[i].enable = *tmp++ ? true : false; + _defaultLegendData[i].x = (int8)*tmp++; + _defaultLegendData[i].stringId = READ_LE_UINT16(tmp); + tmp += 2; + } + _staticres->unloadId(kLolLegendData); + } + + tmp = _staticres->loadRawData(kLolMapCursorOvl, tmpSize); + _mapCursorOverlay = new uint8[tmpSize]; + memcpy(_mapCursorOverlay, tmp, tmpSize); + _staticres->unloadId(kLolMapCursorOvl); + + _updateSpellBookCoords = _staticres->loadRawData(kLolSpellbookCoords, _updateSpellBookCoordsSize); + _updateSpellBookAnimData = _staticres->loadRawData(kLolSpellbookAnim, _updateSpellBookAnimDataSize); + _healShapeFrames = _staticres->loadRawData(kLolHealShapeFrames, _healShapeFramesSize); + + tmp = _staticres->loadRawData(kLolLightningDefs, tmpSize); + if (tmp) { + _lightningProps = new LightningProperty[5]; + for (int i = 0; i < 5; i++) { + _lightningProps[i].lastFrame = tmp[i << 2]; + _lightningProps[i].frameDiv = tmp[(i << 2) + 1]; + _lightningProps[i].sfxId = READ_LE_UINT16(&tmp[(i << 2) + 2]); + } + _staticres->unloadId(kLolLightningDefs); + } + + _fireBallCoords = (const int16 *)_staticres->loadRawDataBe16(kLolFireballCoords, _fireBallCoordsSize); + + _buttonCallbacks.clear(); + _buttonCallbacks.reserve(95); +#define cb(x) _buttonCallbacks.push_back(BUTTON_FUNCTOR(LoLEngine, this, &LoLEngine::x)) + // 0x00 + cb(clickedUpArrow); + cb(clickedDownArrow); + _buttonCallbacks.push_back(_buttonCallbacks[1]); + cb(clickedLeftArrow); + + // 0x04 + cb(clickedRightArrow); + cb(clickedTurnLeftArrow); + cb(clickedTurnRightArrow); + cb(clickedAttackButton); + + // 0x08 + for (int i = 0; i < 3; ++i) + _buttonCallbacks.push_back(_buttonCallbacks[7]); + cb(clickedMagicButton); + + // 0x0C + for (int i = 0; i < 3; ++i) + _buttonCallbacks.push_back(_buttonCallbacks[11]); + cb(clickedMagicSubmenu); + + // 0x10 + cb(clickedScreen); + cb(clickedPortraitLeft); + for (int i = 0; i < 7; ++i) + _buttonCallbacks.push_back(_buttonCallbacks[17]); + + // 0x19 + cb(clickedLiveMagicBarsLeft); + for (int i = 0; i < 3; ++i) + _buttonCallbacks.push_back(_buttonCallbacks[25]); + + // 0x1D + cb(clickedPortraitEtcRight); + for (int i = 0; i < 3; ++i) + _buttonCallbacks.push_back(_buttonCallbacks[29]); + + // 0x21 + cb(clickedCharInventorySlot); + for (int i = 0; i < 10; ++i) + _buttonCallbacks.push_back(_buttonCallbacks[33]); + + // 0x2C + cb(clickedExitCharInventory); + cb(clickedSceneDropItem); + for (int i = 0; i < 3; ++i) + _buttonCallbacks.push_back(_buttonCallbacks[45]); + + // 0x31 + cb(clickedScenePickupItem); + cb(clickedInventorySlot); + for (int i = 0; i < 9; ++i) + _buttonCallbacks.push_back(_buttonCallbacks[50]); + + // 0x3C + cb(clickedInventoryScroll); + cb(clickedInventoryScroll); + cb(clickedWall); + _buttonCallbacks.push_back(_buttonCallbacks[62]); + + // 0x40 + cb(clickedSequenceWindow); + _buttonCallbacks.push_back(_buttonCallbacks[0]); + _buttonCallbacks.push_back(_buttonCallbacks[1]); + _buttonCallbacks.push_back(_buttonCallbacks[3]); + + // 0x44 + _buttonCallbacks.push_back(_buttonCallbacks[4]); + _buttonCallbacks.push_back(_buttonCallbacks[5]); + _buttonCallbacks.push_back(_buttonCallbacks[6]); + cb(clickedScroll); + + // 0x48 + for (int i = 0; i < 9; ++i) + _buttonCallbacks.push_back(_buttonCallbacks[71]); + + // 0x51 + cb(clickedSpellTargetCharacter); + for (int i = 0; i < 3; ++i) + _buttonCallbacks.push_back(_buttonCallbacks[81]); + + // 0x55 + cb(clickedSpellTargetScene); + cb(clickedSceneThrowItem); + _buttonCallbacks.push_back(_buttonCallbacks[86]); + + // 0x58 + cb(clickedOptions); + cb(clickedRestParty); + cb(clickedMoneyBox); + cb(clickedCompass); + + // 0x5C + cb(clickedAutomap); + cb(clickedLamp); + cb(clickedStatusIcon); +#undef cb +} + +void GUI_LoL::initStaticData() { + GUI_V2_BUTTON(_scrollUpButton, 20, 96, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0); + GUI_V2_BUTTON(_scrollDownButton, 21, 98, 0, 1, 1, 1, 0x4487, 0, 0, 0, 25, 16, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0); + + for (uint i = 0; i < ARRAYSIZE(_menuButtons); ++i) + GUI_V2_BUTTON(_menuButtons[i], i, 0, 0, 0, 0, 0, 0x4487, 0, 0, 0, 0, 0, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0); + + if (_vm->gameFlags().isTalkie) + GUI_LOL_MENU(_mainMenu, 9, 0x4000, 0, 7, -1, -1, -1, -1); + else + GUI_LOL_MENU(_mainMenu, 17, 0x4000, 0, 6, -1, -1, -1, -1); + + GUI_LOL_MENU_ITEM(_mainMenu.item[0], 0x4001, 16, 23, 176, 15, 0, 0); + GUI_LOL_MENU_ITEM(_mainMenu.item[1], 0x4002, 16, 40, 176, 15, 0, 0); + GUI_LOL_MENU_ITEM(_mainMenu.item[2], 0x4003, 16, 57, 176, 15, 0, 0); + GUI_LOL_MENU_ITEM(_mainMenu.item[3], 0x4004, 16, 74, 176, 15, 0, 0); + + if (_vm->gameFlags().isTalkie) { + GUI_LOL_MENU_ITEM(_mainMenu.item[4], 0x42D9, 16, 91, 176, 15, 0, 0); + GUI_LOL_MENU_ITEM(_mainMenu.item[5], 0x4006, 16, 108, 176, 15, 0, 0); + GUI_LOL_MENU_ITEM(_mainMenu.item[6], 0x4005, 88, 127, 104, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); + } else { + GUI_LOL_MENU_ITEM(_mainMenu.item[4], 0x4006, 16, 91, 176, 15, 0, 0); + GUI_LOL_MENU_ITEM(_mainMenu.item[5], 0x4005, 88, 110, 104, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); + } + + Button::Callback mainMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedMainMenu); + for (int i = 0; i < _mainMenu.numberOfItems; ++i) + _mainMenu.item[i].callback = mainMenuFunctor; + + GUI_LOL_MENU(_loadMenu, 10, 0x400e, 1, 5, 128, 20, 128, 118); + GUI_LOL_MENU_ITEM(_loadMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_loadMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_loadMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_loadMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_loadMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); + Button::Callback loadMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedLoadMenu); + for (int i = 0; i < 5; ++i) + _loadMenu.item[i].callback = loadMenuFunctor; + + GUI_LOL_MENU(_saveMenu, 10, 0x400d, 1, 5, 128, 20, 128, 118); + GUI_LOL_MENU_ITEM(_saveMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_saveMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_saveMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_saveMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_saveMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); + Button::Callback saveMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedSaveMenu); + for (int i = 0; i < 5; ++i) + _saveMenu.item[i].callback = saveMenuFunctor; + + GUI_LOL_MENU(_deleteMenu, 10, 0x400f, 1, 5, 128, 20, 128, 118); + GUI_LOL_MENU_ITEM(_deleteMenu.item[0], 0xfffe, 8, 39, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_deleteMenu.item[1], 0xfffd, 8, 56, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_deleteMenu.item[2], 0xfffc, 8, 73, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_deleteMenu.item[3], 0xfffb, 8, 90, 256, 15, 0, 0); + GUI_LOL_MENU_ITEM(_deleteMenu.item[4], 0x4011, 168, 118, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); + Button::Callback deleteMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedDeleteMenu); + for (int i = 0; i < 5; ++i) + _deleteMenu.item[i].callback = deleteMenuFunctor; + + GUI_LOL_MENU(_gameOptions, 17, 0x400c, 0, 6, -1, -1, -1, -1); + if (_vm->gameFlags().isTalkie) { + GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xfff7, 120, 22, 80, 15, 0x406e, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xfff6, 120, 39, 80, 15, 0x406c, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xfff5, 120, 56, 80, 15, 0x406d, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xfff4, 120, 73, 80, 15, 0x42d5, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xfff3, 120, 90, 80, 15, 0x42d2, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[5], 0x4072, 104, 110, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); + } else { + GUI_LOL_MENU_ITEM(_gameOptions.item[0], 0xfff9, 120, 22, 80, 15, 0x406a, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[1], 0xfff8, 120, 39, 80, 15, 0x406b, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[2], 0xfff7, 120, 56, 80, 15, 0x406e, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[3], 0xfff6, 120, 73, 80, 15, 0x406c, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[4], 0xfff5, 120, 90, 80, 15, 0x406d, 0); + GUI_LOL_MENU_ITEM(_gameOptions.item[5], 0x4072, 104, 110, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); + } + Button::Callback optionsMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedOptionsMenu); + for (int i = 0; i < _gameOptions.numberOfItems; ++i) + _gameOptions.item[i].callback = optionsMenuFunctor; + + GUI_LOL_MENU(_audioOptions, 18, 0x42d9, 2, 1, -1, -1, -1, -1); + GUI_LOL_MENU_ITEM(_audioOptions.item[0], 0x4072, 152, 76, 96, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); + GUI_LOL_MENU_ITEM(_audioOptions.item[1], 3, 128, 22, 114, 14, 0x42db, 0); + GUI_LOL_MENU_ITEM(_audioOptions.item[2], 4, 128, 39, 114, 14, 0x42da, 0); + GUI_LOL_MENU_ITEM(_audioOptions.item[3], 5, 128, 56, 114, 14, 0x42dc, 0); + Button::Callback audioMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedAudioMenu); + for (int i = 0; i < 4; ++i) + _audioOptions.item[i].callback = audioMenuFunctor; + + GUI_LOL_MENU(_deathMenu, 11, 0x4013, 0, 2, -1, -1, -1, -1); + GUI_LOL_MENU_ITEM(_deathMenu.item[0], 0x4006, 8, 30, 104, 15, 0, 0); + GUI_LOL_MENU_ITEM(_deathMenu.item[1], 0x4001, 176, 30, 104, 15, 0, 0); + Button::Callback deathMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedDeathMenu); + for (int i = 0; i < 2; ++i) + _deathMenu.item[i].callback = deathMenuFunctor; + + GUI_LOL_MENU(_savenameMenu, 7, 0x4053, 0, 2, -1, -1, -1, -1); + GUI_LOL_MENU_ITEM(_savenameMenu.item[0], 0x4012, 8, 38, 72, 15, 0, _vm->_keyMap[Common::KEYCODE_RETURN]); + GUI_LOL_MENU_ITEM(_savenameMenu.item[1], 0x4011, 176, 38, 72, 15, 0, _vm->_keyMap[Common::KEYCODE_ESCAPE]); + Button::Callback savenameMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedSavenameMenu); + for (int i = 0; i < 2; ++i) + _savenameMenu.item[i].callback = savenameMenuFunctor; + + GUI_LOL_MENU(_choiceMenu, 11, 0, 0, 2, -1, -1, -1, -1); + GUI_LOL_MENU_ITEM(_choiceMenu.item[0], 0x4007, 8, 30, 72, 15, 0, 0); + GUI_LOL_MENU_ITEM(_choiceMenu.item[1], 0x4008, 208, 30, 72, 15, 0, 0); + Button::Callback choiceMenuFunctor = BUTTON_FUNCTOR(GUI_LoL, this, &GUI_LoL::clickedChoiceMenu); + for (int i = 0; i < 2; ++i) + _choiceMenu.item[i].callback = choiceMenuFunctor; +} + +const ScreenDim Screen_LoL::_screenDimTable256C[] = { + { 0x00, 0x00, 0x28, 0xC8, 0xC7, 0xCF, 0x00, 0x00 }, // Taken from Intro + { 0x08, 0x48, 0x18, 0x38, 0xFE, 0x01, 0x00, 0x00 }, + { 0x0E, 0x00, 0x16, 0x78, 0xFE, 0x01, 0x00, 0x00 }, + { 0x0B, 0x7B, 0x1C, 0x12, 0xFE, 0xFC, 0x00, 0x00 }, + { 0x0B, 0x7B, 0x1C, 0x2D, 0xFE, 0xFC, 0x00, 0x00 }, + { 0x55, 0x7B, 0xE9, 0x37, 0xFE, 0xFC, 0x00, 0x00 }, + { 0x0B, 0x8C, 0x10, 0x2B, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (4 entries) + { 0x04, 0x59, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x00 }, + { 0x05, 0x6E, 0x1E, 0x0C, 0xFE, 0x01, 0x00, 0x00 }, + { 0x07, 0x19, 0x1A, 0x97, 0x00, 0x00, 0x00, 0x00 }, // Ingame main menu box CD version + { 0x03, 0x1E, 0x22, 0x8C, 0x00, 0x00, 0x00, 0x00 }, + { 0x02, 0x48, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x0E, 0x00, 0x16, 0x78, 0xFE, 0x01, 0x00, 0x00 }, + { 0x0D, 0xA2, 0x18, 0x0C, 0xFE, 0x01, 0x00, 0x00 }, + { 0x0F, 0x06, 0x14, 0x6E, 0x01, 0x00, 0x00, 0x00 }, + { 0x1A, 0xBE, 0x0A, 0x07, 0xFE, 0x01, 0x00, 0x00 }, + { 0x07, 0x21, 0x1A, 0x85, 0x00, 0x00, 0x00, 0x00 }, + { 0x03, 0x32, 0x22, 0x62, 0x00, 0x00, 0x00, 0x00 }, + { 0x0B, 0x8C, 0x10, 0x33, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (5 entries, CD version only) + { 0x0B, 0x8C, 0x10, 0x23, 0x3D, 0x01, 0x00, 0x00 }, // Main menu box (3 entries, floppy version only) + + { 0x01, 0x20, 0x26, 0x80, 0xDC, 0xFD, 0x00, 0x00 }, // Credits + { 0x09, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 }, + { 0x19, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 }, + { 0x01, 0x02, 0x26, 0x14, 0x00, 0x0F, 0x0E, 0x00 }, +}; + +const ScreenDim Screen_LoL::_screenDimTable16C[] = { + { 0x00, 0x00, 0x28, 0xC8, 0x33, 0x44, 0x00, 0x00 }, // Taken from Intro + { 0x08, 0x48, 0x18, 0x38, 0x33, 0x44, 0x00, 0x00 }, + { 0x0E, 0x00, 0x16, 0x78, 0x33, 0x44, 0x00, 0x00 }, + { 0x0B, 0x7B, 0x1C, 0x11, 0x33, 0x11, 0x00, 0x00 }, + { 0x0B, 0x7B, 0x1C, 0x2D, 0x33, 0x11, 0x00, 0x00 }, + { 0x55, 0x7B, 0xE9, 0x37, 0x33, 0x11, 0x00, 0x00 }, + { 0x0B, 0x92, 0x10, 0x2A, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (4 entries) + { 0x04, 0x58, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x00 }, + { 0x05, 0x6C, 0x1E, 0x0D, 0x33, 0x44, 0x00, 0x00 }, + { 0x07, 0x20, 0x1A, 0x86, 0x00, 0x00, 0x00, 0x00 }, + { 0x03, 0x20, 0x22, 0x8C, 0x00, 0x00, 0x00, 0x00 }, + { 0x02, 0x48, 0x24, 0x34, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x0E, 0x00, 0x16, 0x78, 0x33, 0x44, 0x00, 0x00 }, + { 0x0D, 0xA2, 0x18, 0x0C, 0x33, 0x44, 0x00, 0x00 }, + { 0x0F, 0x06, 0x14, 0x6E, 0x44, 0x00, 0x00, 0x00 }, + { 0x1A, 0xBE, 0x0A, 0x07, 0x33, 0x44, 0x00, 0x00 }, + { 0x07, 0x21, 0x1A, 0x85, 0x00, 0x00, 0x00, 0x00 }, + { 0x03, 0x32, 0x22, 0x62, 0x00, 0x00, 0x00, 0x00 }, + { 0x0B, 0x8C, 0x10, 0x33, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (5 entries, not used here) + { 0x0B, 0x8C, 0x10, 0x23, 0x33, 0x44, 0x00, 0x00 }, // Main menu box (3 entries) + + { 0x01, 0x20, 0x26, 0x80, 0xDC, 0xFD, 0x00, 0x00 }, // Credits (TODO: Check this!) + { 0x09, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 }, + { 0x19, 0x29, 0x08, 0x2C, 0x00, 0x00, 0x00, 0x00 }, + { 0x01, 0x02, 0x26, 0x14, 0x00, 0x0F, 0x0E, 0x00 }, +}; + +const int Screen_LoL::_screenDimTableCount = ARRAYSIZE(Screen_LoL::_screenDimTable256C); + +const char * const LoLEngine::_languageExt[] = { + "ENG", + "FRE", + "GER" +}; + +const LoLEngine::CharacterPrev LoLEngine::_charPreviews[] = { + { "Ak\'shel", 0x060, 0x7F, { 0x0F, 0x08, 0x05 } }, + { "Michael", 0x09A, 0x7F, { 0x06, 0x0A, 0x0F } }, + { "Kieran", 0x0D4, 0x7F, { 0x08, 0x06, 0x08 } }, + { "Conrad", 0x10F, 0x7F, { 0x0A, 0x0C, 0x0A } } +}; + +const uint16 LoLEngine::_charPosXPC98[] = { + 92, 152, 212, 268 +}; + +const uint8 LoLEngine::_charNamesPC98[][11] = { + { 0x83, 0x41, 0x83, 0x4E, 0x83, 0x56, 0x83, 0x46, 0x83, 0x8B, 0x00 }, + { 0x83, 0x7D, 0x83, 0x43, 0x83, 0x50, 0x83, 0x8B, 0x00, 0x00, 0x00 }, + { 0x83, 0x4C, 0x81, 0x5B, 0x83, 0x89, 0x83, 0x93, 0x00, 0x00, 0x00 }, + { 0x83, 0x52, 0x83, 0x93, 0x83, 0x89, 0x83, 0x62, 0x83, 0x68, 0x00 } +}; + +const uint8 LoLEngine::_chargenFrameTableTalkie[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x04, 0x03, 0x02, 0x01, + 0x00, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12 +}; + +const uint8 LoLEngine::_chargenFrameTableFloppy[] = { + 0, 1, 2, 3, 4, 5, 4, 3, 2, + 1, 0, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +const uint16 LoLEngine::_selectionPosTable[] = { + 0x6F, 0x00, 0x8F, 0x00, 0xAF, 0x00, 0xCF, 0x00, + 0xEF, 0x00, 0x6F, 0x20, 0x8F, 0x20, 0xAF, 0x20, + 0xCF, 0x20, 0xEF, 0x20, 0x6F, 0x40, 0x8F, 0x40, + 0xAF, 0x40, 0xCF, 0x40, 0xEF, 0x40, 0x10F, 0x00 +}; + +const uint8 LoLEngine::_selectionChar1IdxTable[] = { + 0, 0, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 0, 0, 5, 5, 5, + 5, 5, 5, 5, 0, 0, 5, 5, + 5, 5, 5 +}; + +const uint8 LoLEngine::_selectionChar2IdxTable[] = { + 1, 1, 6, 6, 1, 1, 6, 6, + 6, 6, 6, 6, 6, 1, 1, 6, + 6, 6, 1, 1, 6, 6, 6, 6, + 6, 6, 6 +}; + +const uint8 LoLEngine::_selectionChar3IdxTable[] = { + 2, 2, 7, 7, 7, 7, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, + 2, 7, 7, 7, 7, 2, 2, 7, + 7, 7, 7 +}; + +const uint8 LoLEngine::_selectionChar4IdxTable[] = { + 3, 3, 8, 8, 8, 8, 3, 3, + 8, 8, 3, 3, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 3, 3, 8, + 8, 8, 8 +}; + +const uint8 LoLEngine::_reminderChar1IdxTable[] = { + 4, 4, 4, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5 +}; + +const uint8 LoLEngine::_reminderChar2IdxTable[] = { + 9, 9, 9, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6 +}; + +const uint8 LoLEngine::_reminderChar3IdxTable[] = { + 0xE, 0xE, 0xE, 0x7, 0x7, 0x7, 0x7, 0x7, + 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, + 0x7 +}; + +const uint8 LoLEngine::_reminderChar4IdxTable[] = { + 0xF, 0xF, 0xF, 0x8, 0x8, 0x8, 0x8, 0x8, + 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, + 0x8 +}; + +const uint8 LoLEngine::_selectionAnimIndexTable[] = { + 0, 5, 1, 6, 2, 7, 3, 8 +}; + +const uint8 LoLEngine::_charInfoFrameTable[] = { + 0x0, 0x7, 0x8, 0x9, 0xA, 0xB, 0xA, 0x9, + 0x8, 0x7, 0x0, 0x0, 0x7, 0x8, 0x9, 0xA, + 0xB, 0xA, 0x9, 0x8, 0x7, 0x0, 0x0, 0x7, + 0x8, 0x9, 0xA, 0xB, 0xA, 0x9, 0x8, 0x7 +}; + +const uint8 LoLEngine::_clock2Timers[] = { + 0x00, 0x10, 0x11, 0x03, 0x04, 0x50, + 0x51, 0x52, 0x08, 0x09, 0x0A +}; + +const uint8 LoLEngine::_numClock2Timers = ARRAYSIZE(LoLEngine::_clock2Timers); + +const int8 LoLEngine::_mapCoords[12][4] = { + { 0, 7, 0, -5 }, { -5, 0, 6, 0 }, { 7, 5, 7, 1 }, + { 5, 6, 4, 6 }, { 0, 7, 0, -1 }, { -3, 0, 6, 0 }, + { 6, 7, 6, -3 }, { -3, 5, 6, 5 }, { 1, 5, 1, 1 }, + { 3, 1, 3, 1 }, { -1, 6, -1, -8 }, { -7, -1, 5, -1 } +}; + +const MistOfDoomAnimData LoLEngine::_mistAnimData[] = { + { 0, 7, 7, 13, 155 }, + { 0, 16, 16, 17, 155 }, + { 0, 24, 24, 24, 174 }, + { 0, 19, 19, 19, 174 }, + { 0, 16, 16, 17, 175 }, +}; + +const char * const LoLEngine::_outroShapeFileTable[] = { + "AMAZON.SHP", "ARCHRSLG.SHP", "AVIANWRM.SHP", "BANDIT.SHP", "BOAR.SHP", "CABAL.SHP", + "GUARD.SHP", "HAG.SHP", "HORNET.SHP", "HURZELL.SHP", "IRONGRZR.SHP", "KNOWLES.SHP", + "LIZARD.SHP", "MANTHA.SHP", "MINOTAUR.SHP", "MORIBUND.SHP", "ORC.SHP", "ORCLDR.SHP", + "PENTROG.SHP", "RATMAN.SHP", "ROCKLING.SHP", "SCAVNGR.SHP", "STARK.SHP", + "SWAMPCIT.SHP", "SWAMPMON.SHP", "THUG.SHP", "VIPER.SHP", "XEOB.SHP" +}; + +const uint8 LoLEngine::_outroFrameTable[] = { + 0, 0, 0, 0, 0, 1, 2, 3, + 0, 1, 2, 3, 8, 9, 10, 11, + 8, 9, 10, 11, 4, 5, 6, 7 +}; + +const int16 LoLEngine::_outroRightMonsterPos[] = { + 205, 55, 205, 55, 205, 55, 205, 55, + 205, 56, 207, 57, 208, 58, 210, 59, + 213, 60, 216, 61, 220, 61, 225, 61, + 230, 61, 235, 61, 240, 61, 240, 61, + 240, 61, 240, 61, 240, 61, 240, 61, + 240, 61, 265, 61, 290, 61, 315, 61 +}; + +const int16 LoLEngine::_outroLeftMonsterPos[] = { + 92, 55, 92, 55, 92, 55, 92, 55, + 92, 56, 90, 57, 85, 58, 77, 59, + 67, 60, 57, 61, 47, 61, 35, 61, + 35, 61, 35, 61, 35, 61, 35, 61, + 35, 61, 35, 61, 35, 61, 35, 61, + 35, 61, 10, 61, -20, 61, -45, 61 +}; + +const int16 LoLEngine::_outroRightDoorPos[] = { + 200, 41, 200, 29, 200, 17, 200, 5, + 200, -7, 200, -7, 200, -7, 200, -7, + 200, 5, 200, 17, 200, 29, 200, 41, + 200, 41, 200, 41, 200, 41, 200, 41, + 200, 41, 200, 41, 200, 41, 200, 41, + 200, 41, 200, 41, 200, 41, 200, 41 +}; + +const int16 LoLEngine::_outroLeftDoorPos[] = { + 72, 41, 72, 29, 72, 17, 72, 5, + 72, -7, 72, -7, 72, -7, 72, -7, + 72, 5, 72, 17, 72, 29, 72, 41, + 72, 41, 72, 41, 72, 41, 72, 41, + 72, 41, 72, 41, 72, 41, 72, 41, + 72, 41, 72, 41, 72, 41, 72, 41 +}; + +const int LoLEngine::_outroMonsterScaleTableX[] = { + 0x050, 0x050, 0x050, 0x050, 0x050, 0x05D, 0x070, 0x085, + 0x0A0, 0x0C0, 0x0E2, 0x100, 0x100, 0x100, 0x100, 0x100, + 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100 +}; + +const int LoLEngine::_outroMonsterScaleTableY[] = { + 0x04C, 0x04C, 0x04C, 0x04C, 0x04C, 0x059, 0x06B, 0x080, + 0x099, 0x0B8, 0x0D9, 0x100, 0x100, 0x100, 0x100, 0x100, + 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100 +}; + +} // End of namespace Kyra + +#endif + diff --git a/engines/kyra/text_lok.cpp b/engines/kyra/text_lok.cpp index 3e45c0f286..79b16bc1be 100644 --- a/engines/kyra/text_lok.cpp +++ b/engines/kyra/text_lok.cpp @@ -137,8 +137,11 @@ void KyraEngine_LoK::endCharacterChat(int8 charNum, int16 convoInitialized) { _charSayUnk3 = -1; if (charNum > 4 && charNum < 11) { - //TODO: weird _game_inventory stuff here - //warning("STUB: endCharacterChat() for high charnums"); + _animator->sprites()[_disabledTalkAnimObject].active = 1; + _sprites->_anims[_disabledTalkAnimObject].play = true; + + _animator->sprites()[_enabledTalkAnimObject].active = 0; + _sprites->_anims[_enabledTalkAnimObject].play = false; } if (convoInitialized != 0) { @@ -225,8 +228,19 @@ int KyraEngine_LoK::initCharacterChat(int8 charNum) { _animator->restoreAllObjectBackgrounds(); if (charNum > 4 && charNum < 11) { - // TODO: Fill in weird _game_inventory stuff here - //warning("STUB: initCharacterChat() for high charnums"); + const uint8 animDisableTable[] = { 3, 1, 1, 5, 0, 6 }; + const uint8 animEnableTable[] = { 4, 2, 5, 6, 1, 7 }; + + _disabledTalkAnimObject = animDisableTable[charNum - 5]; + _enabledTalkAnimObject = animEnableTable[charNum - 5]; + + _animator->sprites()[_disabledTalkAnimObject].active = 0; + _sprites->_anims[_disabledTalkAnimObject].play = false; + + _animator->sprites()[_enabledTalkAnimObject].active = 1; + _sprites->_anims[_enabledTalkAnimObject].play = true; + + _charSayUnk2 = _enabledTalkAnimObject; } _animator->flagAllObjectsForRefresh(); diff --git a/engines/kyra/timer_lok.cpp b/engines/kyra/timer_lok.cpp index 3a7d1ed0de..40426a3dfe 100644 --- a/engines/kyra/timer_lok.cpp +++ b/engines/kyra/timer_lok.cpp @@ -48,33 +48,39 @@ void KyraEngine_LoK::setupTimers() { for (int i = 10; i <= 13; ++i) _timer->addTimer(i, 0, 420, 1); - _timer->addTimer(14, TimerV1(timerCheckAnimFlag2), 600, 1); + _timer->addTimer(14, TimerV1(timerAsWillowispTimeout), 600, 1); _timer->addTimer(15, TimerV1(timerUpdateHeadAnims), 11, 1); - _timer->addTimer(16, TimerV1(timerSetFlags1), 7200, 1); - _timer->addTimer(17, 0 /*sub_15120*/, 7200, 1); - _timer->addTimer(18, TimerV1(timerCheckAnimFlag1), 600, 1); + _timer->addTimer(16, TimerV1(timerTulipCreator), 7200, 1); + _timer->addTimer(17, TimerV1(timerRubyCreator), 7200, 1); + _timer->addTimer(18, TimerV1(timerAsInvisibleTimeout), 600, 1); _timer->addTimer(19, TimerV1(timerRedrawAmulet), 600, 1); _timer->addTimer(20, 0, 7200, 1); - _timer->addTimer(21, 0/*sub_1517C*/, 18000, 1); + _timer->addTimer(21, TimerV1(timerLavenderRoseCreator), 18000, 1); _timer->addTimer(22, 0, 7200, 1); - for (int i = 23; i <= 27; ++i) - _timer->addTimer(i, 0, 10800, 1); + _timer->addTimer(23, 0, 10800, 1); + _timer->addTimer(24, TimerV1(timerAcornCreator), 10800, 1); + _timer->addTimer(25, 0, 10800, 1); + _timer->addTimer(26, TimerV1(timerBlueberryCreator), 10800, 1); + _timer->addTimer(27, 0, 10800, 1); _timer->addTimer(28, 0, 21600, 1); _timer->addTimer(29, 0, 7200, 1); _timer->addTimer(30, 0, 10800, 1); _timer->addTimer(31, TimerV1(timerFadeText), -1, 1); - _timer->addTimer(32, TimerV1(updateAnimFlag1), 9, 1); - _timer->addTimer(33, TimerV1(updateAnimFlag2), 3, 1); + _timer->addTimer(32, TimerV1(timerWillowispFrameTimer), 9, 1); + _timer->addTimer(33, TimerV1(timerInvisibleFrameTimer), 3, 1); } void KyraEngine_LoK::timerUpdateHeadAnims(int timerNum) { static int8 currentFrame = 0; - static const int8 frameTable[] = {4, 5, 4, 5, 4, 5, 0, 1, 4, 5, - 4, 4, 6, 4, 8, 1, 9, 4, -1}; + static const int8 frameTable[] = { + 4, 5, 4, 5, 4, 5, 0, 1, + 4, 5, 4, 4, 6, 4, 8, 1, + 9, 4, -1 + }; if (_talkingCharNum < 0) return; @@ -89,19 +95,51 @@ void KyraEngine_LoK::timerUpdateHeadAnims(int timerNum) { _animator->animRefreshNPC(_talkingCharNum); } -void KyraEngine_LoK::timerSetFlags1(int timerNum) { +void KyraEngine_LoK::timerTulipCreator(int timerNum) { if (_currentCharacter->sceneId == 0x1C) return; - int rndNr = _rnd.getRandomNumberRng(0, 3); + setItemCreationFlags(17, 3); +} + +void KyraEngine_LoK::timerRubyCreator(int timerNum) { + if (_currentCharacter->sceneId == 0x23) + return; + + setItemCreationFlags(22, 4); +} + +void KyraEngine_LoK::timerLavenderRoseCreator(int timerNum) { + if (_currentCharacter->sceneId == 0x06) + return; + + setItemCreationFlags(0, 4); +} - for (int i = 0; i < 4; i++) { - if (!queryGameFlag(rndNr + 17)) { - setGameFlag(rndNr + 17); +void KyraEngine_LoK::timerAcornCreator(int timerNum) { + if (_currentCharacter->sceneId == 0x1F) + return; + + setItemCreationFlags(72, 5); +} + +void KyraEngine_LoK::timerBlueberryCreator(int timerNum) { + if (_currentCharacter->sceneId == 0x28) + return; + + setItemCreationFlags(26, 7); +} + +void KyraEngine_LoK::setItemCreationFlags(int offset, int count) { + int rndNr = _rnd.getRandomNumberRng(0, count); + + for (int i = 0; i <= count; i++) { + if (!queryGameFlag(rndNr + offset)) { + setGameFlag(rndNr + offset); break; } else { rndNr++; - if (rndNr > 3) + if (rndNr > count) rndNr = 0; } } @@ -111,16 +149,14 @@ void KyraEngine_LoK::timerFadeText(int timerNum) { _fadeText = true; } -void KyraEngine_LoK::updateAnimFlag1(int timerNum) { - if (_brandonStatusBit & 2) { +void KyraEngine_LoK::timerWillowispFrameTimer(int timerNum) { + if (_brandonStatusBit & 2) _brandonStatusBit0x02Flag = 1; - } } -void KyraEngine_LoK::updateAnimFlag2(int timerNum) { - if (_brandonStatusBit & 0x20) { +void KyraEngine_LoK::timerInvisibleFrameTimer(int timerNum) { + if (_brandonStatusBit & 0x20) _brandonStatusBit0x20Flag = 1; - } } void KyraEngine_LoK::setTextFadeTimerCountdown(int16 countdown) { @@ -130,19 +166,14 @@ void KyraEngine_LoK::setTextFadeTimerCountdown(int16 countdown) { _timer->setCountdown(31, countdown*60); } -void KyraEngine_LoK::timerSetFlags2(int timerNum) { - if (!((uint32 *)(_flagsTable+0x2D))[timerNum]) - ((uint32 *)(_flagsTable+0x2D))[timerNum] = 1; -} - -void KyraEngine_LoK::timerCheckAnimFlag1(int timerNum) { +void KyraEngine_LoK::timerAsInvisibleTimeout(int timerNum) { if (_brandonStatusBit & 0x20) { checkAmuletAnimFlags(); _timer->setCountdown(18, -1); } } -void KyraEngine_LoK::timerCheckAnimFlag2(int timerNum) { +void KyraEngine_LoK::timerAsWillowispTimeout(int timerNum) { if (_brandonStatusBit & 0x2) { checkAmuletAnimFlags(); _timer->setCountdown(14, -1); diff --git a/engines/kyra/vqa.cpp b/engines/kyra/vqa.cpp index 8a598591bb..d0e54f996b 100644 --- a/engines/kyra/vqa.cpp +++ b/engines/kyra/vqa.cpp @@ -260,8 +260,8 @@ bool VQAMovie::open(const char *filename) { _header.bits = 8; } - setX((Screen::SCREEN_W - _header.width) / 2); - setY((Screen::SCREEN_H - _header.height) / 2); + _x = (Screen::SCREEN_W - _header.width) / 2; + _y = (Screen::SCREEN_H - _header.height) / 2; _frameInfo = new uint32[_header.numFrames]; _frame = new byte[_header.width * _header.height]; diff --git a/engines/kyra/vqa.h b/engines/kyra/vqa.h index c1448a4865..129d526e98 100644 --- a/engines/kyra/vqa.h +++ b/engines/kyra/vqa.h @@ -56,9 +56,6 @@ public: // It's unlikely that we ever want to change the movie position from // its default. - void setX(int x) { _x = x; } - void setY(int y) { _y = y; } - void setDrawPage(int page) { _drawPage = page; } bool open(const char *filename); diff --git a/engines/lure/debugger.cpp b/engines/lure/debugger.cpp index 3abc079a05..1cfe0804e4 100644 --- a/engines/lure/debugger.cpp +++ b/engines/lure/debugger.cpp @@ -105,7 +105,7 @@ bool Debugger::cmd_enterRoom(int argc, const char **argv) { if (!remoteFlag) res.getActiveHotspot(PLAYER_ID)->setRoomNumber(roomNumber); - _detach_now = true; + detach(); return false; } diff --git a/engines/lure/fights.cpp b/engines/lure/fights.cpp index 53539677c8..789c9d924e 100644 --- a/engines/lure/fights.cpp +++ b/engines/lure/fights.cpp @@ -132,8 +132,7 @@ void FightsManager::fightLoop() { } Screen::getReference().update(); - if (game.debugger().isAttached()) - game.debugger().onFrame(); + game.debugger().onFrame(); g_system->delayMillis(10); } diff --git a/engines/lure/game.cpp b/engines/lure/game.cpp index d0f98b9c34..e77ac25716 100644 --- a/engines/lure/game.cpp +++ b/engines/lure/game.cpp @@ -56,8 +56,6 @@ Game::Game() { _debugFlag = gDebugLevel >= ERROR_BASIC; _soundFlag = true; - _musicVolume = ConfMan.getBool("music_mute") ? 0 : MIN(255, ConfMan.getInt("music_volume")); - _sfxVolume = ConfMan.getBool("sfx_mute") ? 0 : MIN(255, ConfMan.getInt("sfx_volume")); } Game::~Game() { @@ -281,8 +279,7 @@ void Game::execute() { system.updateScreen(); system.delayMillis(10); - if (_debugger->isAttached()) - _debugger->onFrame(); + _debugger->onFrame(); } room.leaveRoom(); diff --git a/engines/lure/game.h b/engines/lure/game.h index 3864e9c205..123ac0dca7 100644 --- a/engines/lure/game.h +++ b/engines/lure/game.h @@ -48,8 +48,6 @@ class Game { private: Debugger *_debugger; bool _fastTextFlag, _soundFlag; - uint8 _sfxVolume; - uint8 _musicVolume; uint8 _state; uint16 _tellCommands[MAX_TELL_COMMANDS * 3 + 1]; int _numTellCommands; @@ -87,8 +85,6 @@ public: bool &debugFlag() { return _debugFlag; } bool fastTextFlag() { return _fastTextFlag; } bool soundFlag() { return _soundFlag; } - uint8 sfxVolume() { return ConfMan.getInt("sfx_volume"); } - uint8 musicVolume() { return ConfMan.getInt("music_volume"); } Debugger &debugger() { return *_debugger; } // Menu item support methods diff --git a/engines/lure/sound.cpp b/engines/lure/sound.cpp index cd539dfab4..a75545c330 100644 --- a/engines/lure/sound.cpp +++ b/engines/lure/sound.cpp @@ -72,6 +72,8 @@ SoundManager::SoundManager() { _channelsInner[index].volume = 90; } } + + syncSounds(); } SoundManager::~SoundManager() { @@ -288,16 +290,21 @@ uint8 SoundManager::descIndexOf(uint8 soundNumber) { // Used to sync the volume for all channels with the Config Manager // void SoundManager::syncSounds() { - Game &game = Game::getReference(); musicInterface_TidySounds(); + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); + _musicVolume = mute ? 0 : MIN(255, ConfMan.getInt("music_volume")); + _sfxVolume = mute ? 0 : MIN(255, ConfMan.getInt("sfx_volume")); + g_system->lockMutex(_soundMutex); MusicListIterator i; for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) { if ((*i)->isMusic()) - (*i)->setVolume(game.musicVolume()); + (*i)->setVolume(_musicVolume); else - (*i)->setVolume(game.sfxVolume()); + (*i)->setVolume(_sfxVolume); } g_system->unlockMutex(_soundMutex); } @@ -599,9 +606,9 @@ MidiMusic::MidiMusic(MidiDriver *driver, ChannelEntry channels[NUM_CHANNELS], } if (_isMusic) - setVolume(ConfMan.getInt("music_volume")); + setVolume(Sound.musicVolume()); else - setVolume(ConfMan.getInt("sfx_volume")); + setVolume(Sound.sfxVolume()); _passThrough = false; @@ -658,8 +665,7 @@ void MidiMusic::setVolume(int volume) { _volume = volume; - Game &game = Game::getReference(); - volume *= _isMusic ? game.musicVolume() : game.sfxVolume(); + volume *= _isMusic ? Sound.musicVolume() : Sound.sfxVolume(); for (int i = 0; i < _numChannels; ++i) { if (_channels[_channelNumber + i].midiChannel != NULL) @@ -707,8 +713,7 @@ void MidiMusic::send(uint32 b) { // Adjust volume changes by song and master volume byte volume = (byte)((b >> 16) & 0x7F); _channels[channel].volume = volume; - Game &game = Game::getReference(); - int master_volume = _isMusic ? game.musicVolume() : game.sfxVolume(); + int master_volume = _isMusic ? Sound.musicVolume() : Sound.sfxVolume(); volume = volume * _volume * master_volume / 65025; b = (b & 0xFF00FFFF) | (volume << 16); } else if ((b & 0xF0) == 0xC0) { diff --git a/engines/lure/sound.h b/engines/lure/sound.h index c41cec48fe..6d248fbd20 100644 --- a/engines/lure/sound.h +++ b/engines/lure/sound.h @@ -105,7 +105,7 @@ public: bool isMusic() {return _isMusic; } }; -class SoundManager: public Common::Singleton<SoundManager> { +class SoundManager : public Common::Singleton<SoundManager> { private: // Outer sound interface properties MemoryBlock *_descs; @@ -128,11 +128,15 @@ private: Common::MutexRef _soundMutex; bool _paused; + uint _musicVolume; + uint _sfxVolume; + // Internal support methods void bellsBodge(); void musicInterface_TidySounds(); static void onTimer(void *data); void doTimer(); + public: SoundManager(); ~SoundManager(); @@ -156,9 +160,11 @@ public: void fadeOut(); void pause() { _paused = true; } void resume() { _paused = false; } - bool getPaused() { return _paused; } - bool hasNativeMT32() { return _nativeMT32; } - bool isRoland() { return _isRoland; } + bool getPaused() const { return _paused; } + bool hasNativeMT32() const { return _nativeMT32; } + bool isRoland() const { return _isRoland; } + uint musicVolume() const { return _musicVolume; } + uint sfxVolume() const { return _sfxVolume; } // The following methods implement the external sound player module void musicInterface_Initialise(); diff --git a/engines/m4/animation.cpp b/engines/m4/animation.cpp index 0ead57aac9..37314eff44 100644 --- a/engines/m4/animation.cpp +++ b/engines/m4/animation.cpp @@ -287,8 +287,8 @@ void MadsAnimation::load(const Common::String &filename, int abortTimers) { _abortTimers = abortTimers; _abortMode = _madsVm->scene()->_abortTimersMode2; - for (int i = 0; i < 3; ++i) - _actionNouns[i] = _madsVm->globals()->actionNouns[i]; + if (_madsVm->_scene) + _actionNouns = _madsVm->scene()->_action._action; // Initialise kernel message list for (uint i = 0; i < _messages.size(); ++i) @@ -464,8 +464,8 @@ void MadsAnimation::update() { if (_abortMode != ABORTMODE_1) { // Copy the noun list - for (int i = 0; i < 3; ++i) - _madsVm->globals()->actionNouns[i] = _actionNouns[i]; + if (_madsVm->_scene) + _madsVm->scene()->_action._action = _actionNouns; } } } diff --git a/engines/m4/animation.h b/engines/m4/animation.h index 583d829066..a7a6b57c32 100644 --- a/engines/m4/animation.h +++ b/engines/m4/animation.h @@ -104,8 +104,7 @@ private: int _messageCtr; int _abortTimers; AbortTimerMode _abortMode; - uint16 _actionNouns[3]; - + ActionDetails _actionNouns; void load1(int frameNumber); bool proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber); diff --git a/engines/m4/console.cpp b/engines/m4/console.cpp index 19fbf6e852..71c70e3e1b 100644 --- a/engines/m4/console.cpp +++ b/engines/m4/console.cpp @@ -103,8 +103,8 @@ bool Console::cmdListHotSpots(int argc, const char **argv) { if (_vm->isM4()) { DebugPrintf("Scene parallax\n"); _m4Vm->scene()->getSceneResources().parallax->dump(); - DebugPrintf("Scene props\n"); - _vm->_scene->getSceneResources().props->dump(); + DebugPrintf("Scene dynamic hotspots\n"); + _vm->_scene->getSceneResources().dynamicHotspots->dump(); } return true; } @@ -400,9 +400,9 @@ bool M4Console::cmdSceneInfo(int argc, const char **argv) { DebugPrintf("Scene resources:\n"); DebugPrintf("artBase: %s\n", _m4Vm->scene()->getSceneResources().artBase); DebugPrintf("pictureBase: %s\n", _m4Vm->scene()->getSceneResources().pictureBase); - DebugPrintf("hotspotCount: %i\n", _m4Vm->scene()->getSceneResources().hotspotCount); + DebugPrintf("hotspotCount: %i\n", _m4Vm->scene()->getSceneResources().hotspots->size()); DebugPrintf("parallaxCount: %i\n", _m4Vm->scene()->getSceneResources().parallaxCount); - DebugPrintf("propsCount: %i\n", _m4Vm->scene()->getSceneResources().propsCount); + DebugPrintf("dynHotspotCount: %i\n", _m4Vm->scene()->getSceneResources().dynamicHotspots->size()); DebugPrintf("frontY: %i\n", _m4Vm->scene()->getSceneResources().frontY); DebugPrintf("backY: %i\n", _m4Vm->scene()->getSceneResources().backY); DebugPrintf("frontScale: %i\n", _m4Vm->scene()->getSceneResources().frontScale); diff --git a/engines/m4/converse.cpp b/engines/m4/converse.cpp index 11bc165811..af26a86313 100644 --- a/engines/m4/converse.cpp +++ b/engines/m4/converse.cpp @@ -858,8 +858,8 @@ void Converse::loadConversationMads(const char *convName) { if (buffer[curPos - 1] == '\0') { // end of string //printf("%s\n", buffer); - buf = new char[strlen(buffer)]; - sprintf(buf, "%s", buffer); + buf = new char[strlen(buffer) + 1]; + strcpy(buf, buffer); _convStrings.push_back(buf); curPos = 0; *buffer = 0; diff --git a/engines/m4/graphics.cpp b/engines/m4/graphics.cpp index c10ea6c9f6..423dda5e7e 100644 --- a/engines/m4/graphics.cpp +++ b/engines/m4/graphics.cpp @@ -443,7 +443,7 @@ void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth, for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) { // Copy each byte one at a time checking against the depth for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr) { - if ((depthsPtr[xCtr] > depth) && (srcPtr[xCtr] != transparentColour)) + if ((depth <= (depthsPtr[xCtr] & 0x7f)) && (srcPtr[xCtr] != transparentColour)) destPtr[xCtr] = srcPtr[xCtr]; } @@ -557,7 +557,7 @@ void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth, // Not a display pixel continue; - if ((*srcP != transparentColour) && (depth <= *depthP)) + if ((*srcP != transparentColour) && (depth <= (*depthP & 0x7f))) *destP = *srcP; ++destP; diff --git a/engines/m4/hotspot.cpp b/engines/m4/hotspot.cpp index 9849cc7416..27180c5eb8 100644 --- a/engines/m4/hotspot.cpp +++ b/engines/m4/hotspot.cpp @@ -185,9 +185,9 @@ void HotSpotList::dump() { uint32 HotSpotList::readHotSpotInteger(Common::SeekableReadStream* hotspotStream) { if (_vm->isM4()) - return hotspotStream->readUint32LE(); + return hotspotStream->readSint32LE(); else - return hotspotStream->readUint16LE(); + return hotspotStream->readSint16LE(); } void HotSpotList::loadHotSpots(Common::SeekableReadStream* hotspotStream, int hotspotCount) { @@ -196,7 +196,7 @@ void HotSpotList::loadHotSpots(Common::SeekableReadStream* hotspotStream, int ho char buffer[256]; uint32 strLength = 0; uint32 index = 0; - uint32 feetX, feetY; + int feetX, feetY; int cursorOffset = (_vm ->isM4()) ? 0 : 1; for (int i = 0; i < hotspotCount; i++) { @@ -206,6 +206,7 @@ void HotSpotList::loadHotSpots(Common::SeekableReadStream* hotspotStream, int ho y2 = readHotSpotInteger(hotspotStream); index = add(new HotSpot(x1, y1, x2, y2), i == 0); currentHotSpot = get(index); + currentHotSpot->setIndex(index); feetX = readHotSpotInteger(hotspotStream); feetY = readHotSpotInteger(hotspotStream); currentHotSpot->setFeet(feetX, feetY); diff --git a/engines/m4/hotspot.h b/engines/m4/hotspot.h index 5bb4f5888a..f650d5ff54 100644 --- a/engines/m4/hotspot.h +++ b/engines/m4/hotspot.h @@ -73,6 +73,8 @@ public: int getFeetY() { return _feetY; } int8 getArticle() const { return _articleNumber; } Common::Rect getRect() const; + int getIndex() const { return _index; } + void setIndex(int index) { _index = index; } int32 area() const { return (_rect.width() - 1) * (_rect.height() - 1); } bool pointInside(int x, int y) { return _rect.contains(x, y); } @@ -83,6 +85,7 @@ private: bool _active; int _feetX, _feetY; uint8 _facing, _cursor; + int _index; // Unused in Orion Burger, used in MADS games uint8 _syntax; @@ -101,6 +104,7 @@ public: int add(HotSpot *hotspot, bool head = false); HotSpot *get(int index) { return _hotspots[index]; } HotSpot &operator[](int idx) { return *get(idx); } + int size() const { return _hotspots.size(); } void remove(HotSpot *hotspot); void unlinkItem(HotSpot *hotspot); void clear(); diff --git a/engines/m4/m4_scene.cpp b/engines/m4/m4_scene.cpp index 79122a9564..475fdba653 100644 --- a/engines/m4/m4_scene.cpp +++ b/engines/m4/m4_scene.cpp @@ -45,7 +45,7 @@ M4Scene::M4Scene(M4Engine *vm): _sceneResources(), Scene(vm, &_sceneResources) { _sceneResources.hotspots = new HotSpotList(); _sceneResources.parallax = new HotSpotList(); - _sceneResources.props = new HotSpotList(); + _sceneResources.dynamicHotspots = new HotSpotList(); _interfaceSurface = new M4InterfaceView(vm); } @@ -74,9 +74,9 @@ void M4Scene::loadSceneResources(int sceneNumber) { if (sceneS != NULL) { sceneS->read(_sceneResources.artBase, MAX_CHK_FILENAME_SIZE); sceneS->read(_sceneResources.pictureBase, MAX_CHK_FILENAME_SIZE); - _sceneResources.hotspotCount = sceneS->readUint32LE(); + int hotspotCount = sceneS->readUint32LE(); _sceneResources.parallaxCount = sceneS->readUint32LE(); - _sceneResources.propsCount = sceneS->readUint32LE(); + int dynHotspotCount = sceneS->readUint32LE(); _sceneResources.frontY = sceneS->readUint32LE(); _sceneResources.backY = sceneS->readUint32LE(); _sceneResources.frontScale = sceneS->readUint32LE(); @@ -99,11 +99,11 @@ void M4Scene::loadSceneResources(int sceneNumber) { // Clear current hotspot lists _sceneResources.hotspots->clear(); _sceneResources.parallax->clear(); - _sceneResources.props->clear(); + _sceneResources.dynamicHotspots->clear(); - _sceneResources.hotspots->loadHotSpots(sceneS, _sceneResources.hotspotCount); + _sceneResources.hotspots->loadHotSpots(sceneS, hotspotCount); _sceneResources.parallax->loadHotSpots(sceneS, _sceneResources.parallaxCount); - _sceneResources.props->loadHotSpots(sceneS, _sceneResources.propsCount); + _sceneResources.dynamicHotspots->loadHotSpots(sceneS, dynHotspotCount); // Note that toss() deletes the MemoryReadStream _vm->res()->toss(filename); @@ -207,7 +207,7 @@ void M4Scene::leaveScene() { Scene::leaveScene(); } -void M4Scene::checkHotspotAtMousePos(int x, int y) { +void M4Scene::mouseMove(int x, int y) { if (_vm->getGameType() == GType_Riddle) return; diff --git a/engines/m4/m4_scene.h b/engines/m4/m4_scene.h index 329582caf4..2216779a3e 100644 --- a/engines/m4/m4_scene.h +++ b/engines/m4/m4_scene.h @@ -69,7 +69,7 @@ public: virtual void leaveScene(); virtual void loadSceneCodes(int sceneNumber, int index = 0); virtual void show(); - virtual void checkHotspotAtMousePos(int x, int y); + virtual void mouseMove(int x, int y); virtual void leftClick(int x, int y); virtual void rightClick(int x, int y); virtual void update(); diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp index e451a306ef..e7c20b237d 100644 --- a/engines/m4/mads_logic.cpp +++ b/engines/m4/mads_logic.cpp @@ -189,30 +189,30 @@ uint16 MadsSceneLogic::loadSpriteSet(uint16 suffixNum, uint16 sepChar) { return _madsVm->scene()->loadSceneSpriteSet(resName); } -uint16 MadsSceneLogic::startReversibleSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { +uint16 MadsSceneLogic::startReversibleSpriteSequence(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0); uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2), spriteFrame->y + (spriteFrame->height() / 2))); - return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, + return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, flipped, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, true, 100, depth - 1, 1, ANIMTYPE_REVERSIBLE, 0, 0); } -uint16 MadsSceneLogic::startCycledSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { +uint16 MadsSceneLogic::startCycledSpriteSequence(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0); uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2), spriteFrame->y + (spriteFrame->height() / 2))); - return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, + return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, flipped, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, true, 100, depth - 1, 1, ANIMTYPE_CYCLED, 0, 0); } -uint16 MadsSceneLogic::startSpriteSequence3(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { +uint16 MadsSceneLogic::startSpriteSequence3(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0); uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2), spriteFrame->y + (spriteFrame->height() / 2))); - return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, + return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, flipped, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, true, 100, depth - 1, -1, ANIMTYPE_CYCLED, 0, 0); } @@ -354,20 +354,18 @@ void MadsSceneLogic::enterScene() { _spriteIndexes[12] = loadSpriteSet(8, 'x'); _spriteIndexes[13] = loadSpriteSet(0, 'x'); - _spriteIndexes[15] = startCycledSpriteSequence(_spriteIndexes[0], 0, 5, 0, 0, 25); - - _spriteIndexes[16] = startCycledSpriteSequence(_spriteIndexes[1], 0, 4, 0, 1, 0); - _spriteIndexes[17] = startCycledSpriteSequence(_spriteIndexes[2], 0, 4, 0, 1, 0); + _spriteIndexes[15] = startCycledSpriteSequence(_spriteIndexes[0], false, 5, 0, 0, 25); + _spriteIndexes[16] = startCycledSpriteSequence(_spriteIndexes[1], false, 4, 0, 1, 0); + _spriteIndexes[17] = startCycledSpriteSequence(_spriteIndexes[2], false, 4, 0, 1, 0); _madsVm->scene()->_sequenceList.addSubEntry(_spriteIndexes[17], SM_FRAME_INDEX, 7, 70); - _spriteIndexes[18] = startReversibleSpriteSequence(_spriteIndexes[3], 0, 10, 0, 0, 60); - _spriteIndexes[19] = startCycledSpriteSequence(_spriteIndexes[4], 0, 5, 0, 1, 0); - _spriteIndexes[20] = startCycledSpriteSequence(_spriteIndexes[5], 0, 10, 0, 2, 0); - _spriteIndexes[21] = startCycledSpriteSequence(_spriteIndexes[6], 0, 6, 0, 0, 0); - - _spriteIndexes[23] = startCycledSpriteSequence(_spriteIndexes[8], 0, 6, 0, 10, 4); - _spriteIndexes[24] = startCycledSpriteSequence(_spriteIndexes[9], 0, 6, 0, 32, 47); + _spriteIndexes[18] = startReversibleSpriteSequence(_spriteIndexes[3], false, 10, 0, 0, 60); + _spriteIndexes[19] = startCycledSpriteSequence(_spriteIndexes[4], false, 5, 0, 1, 0); + _spriteIndexes[20] = startCycledSpriteSequence(_spriteIndexes[5], false, 10, 0, 2, 0); + _spriteIndexes[21] = startCycledSpriteSequence(_spriteIndexes[6], false, 6, 0, 0, 0); + _spriteIndexes[23] = startCycledSpriteSequence(_spriteIndexes[8], false, 6, 0, 10, 4); + _spriteIndexes[24] = startCycledSpriteSequence(_spriteIndexes[9], false, 6, 0, 32, 47); activateHotspot(0x137, false); // SHIELD MODULATOR // shield_panel_opened = 0; @@ -386,7 +384,7 @@ void MadsSceneLogic::enterScene() { _madsVm->_player._direction = 9; // TODO: Extra flags setting - _spriteIndexes[25] = startCycledSpriteSequence(_spriteIndexes[10], 0, 3, 0, 0, 0); + _spriteIndexes[25] = startCycledSpriteSequence(_spriteIndexes[10], false, 3, 0, 0, 0); _madsVm->scene()->_sequenceList.setAnimRange(_spriteIndexes[25], 17, 17); activateHotspot(0x47, false); // CHAIR /*timer_unk1 = */_madsVm->scene()->_dynamicHotspots.add(0x47, 0x13F /*SIT_IN*/, -1, @@ -395,7 +393,8 @@ void MadsSceneLogic::enterScene() { //if (_madsVm->globals()->previousScene == 112) // room101Check(); } else { - _spriteIndexes[26] = startCycledSpriteSequence(_spriteIndexes[11], 0, 6, 0, 0, 0); + _spriteIndexes[26] = startCycledSpriteSequence(_spriteIndexes[11], false, 6, 0, 0, 0); + _madsVm->scene()->_sequenceList.setDepth(_spriteIndexes[26], 4); } _madsVm->globals()->loadQuoteSet(0x31, 0x32, 0x37, 0x38, 0x39, -1); @@ -418,11 +417,15 @@ void MadsSceneLogic::enterScene() { lowRoomsEntrySound(); } -void MadsSceneLogic::doAction() { +void MadsSceneLogic::doPreactions() { + warning("Still to do preactions logic"); +} +void MadsSceneLogic::doAction() { + warning("Still to do actions logic"); } -void MadsSceneLogic::sceneStep() { +void MadsSceneLogic::doSceneStep() { // TODO: Sound handling switch (_madsVm->scene()->_abortTimers) { diff --git a/engines/m4/mads_logic.h b/engines/m4/mads_logic.h index 98d6df6163..ec6eff368b 100644 --- a/engines/m4/mads_logic.h +++ b/engines/m4/mads_logic.h @@ -37,9 +37,9 @@ class MadsSceneLogic { private: // Library interface methods uint16 loadSpriteSet(uint16 suffixNum, uint16 sepChar); - uint16 startReversibleSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks); - uint16 startCycledSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks); - uint16 startSpriteSequence3(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks); + uint16 startReversibleSpriteSequence(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks); + uint16 startCycledSpriteSequence(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks); + uint16 startSpriteSequence3(uint16 srcSpriteIdx, bool flipped, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks); void activateHotspot(int idx, bool active); void lowRoomsEntrySound(); void getPlayerSpritesPrefix(); @@ -59,8 +59,9 @@ public: void setupScene(); void enterScene(); + void doPreactions(); void doAction(); - void sceneStep(); + void doSceneStep(); }; class MadsGameLogic { diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp index d7d9cf4150..810acb04fb 100644 --- a/engines/m4/mads_menus.cpp +++ b/engines/m4/mads_menus.cpp @@ -809,7 +809,7 @@ void RexDialogView::setFrame(int frameNumber, int depth) { } void RexDialogView::initVars() { - _word_8502C = -1; + _v8502C = -1; _selectedLine = -1; _lineIndex = 0; _enterFlag = false; diff --git a/engines/m4/mads_menus.h b/engines/m4/mads_menus.h index e964c5866d..a0fc6fb3bc 100644 --- a/engines/m4/mads_menus.h +++ b/engines/m4/mads_menus.h @@ -136,7 +136,7 @@ protected: int _dialogSelectedLine; Common::StringArray _saveList; - int _word_8502C; + int _v8502C; int _selectedLine; int _lineIndex; bool _enterFlag; diff --git a/engines/m4/mads_player.cpp b/engines/m4/mads_player.cpp index 8531a3ed44..de09e97640 100644 --- a/engines/m4/mads_player.cpp +++ b/engines/m4/mads_player.cpp @@ -35,20 +35,27 @@ const int MadsPlayer::_directionListIndexes[32] = { MadsPlayer::MadsPlayer() { _playerPos = Common::Point(160, 78); - _direction = 0; - _newDirection = 0; + _ticksAmount = 3; _forceRefresh = true; _stepEnabled = true; - _ticksAmount = 3; - _priorTimer = 0; _visible = true; - _priorVisible = false; - _visible3 = false; _yScale = 0; _moving = false; + _spriteListStart = 0; - _spriteListIdx = 0; + //TODO:unknown vars + _special = 0; + _next = 0; + _unk4 = false; + _spritesChanged = true; + + _direction = 0; + _newDirection = 0; + _priorTimer = 0; + _priorVisible = false; + _visible3 = false; + _spriteListIdx = 0; _currentScale = 0; strcpy(_spritesPrefix, ""); for (int idx = 0; idx < 8; ++idx) @@ -59,6 +66,8 @@ MadsPlayer::MadsPlayer() { _frameCount = 0; _frameListIndex = 0; _actionIndex = 0; + _routeCount = 0; + resetActionList(); } @@ -127,7 +136,7 @@ void MadsPlayer::update() { int yp = MIN(_playerPos.y, (int16)155); for (int idx = 1; idx < 15; ++idx) { - if (_madsVm->scene()->getSceneResources().depthTable[newDepth] >= yp) + if (_madsVm->scene()->getSceneResources()._depthBands[newDepth] >= yp) newDepth = idx + 1; } _currentDepth = newDepth; @@ -168,7 +177,7 @@ void MadsPlayer::update() { _madsVm->scene()->_spriteSlots[slotIndex] = slot; } - // TODO: Meaning of word_844c0 block + // TODO: Meaning of _v844c0 block } } @@ -325,6 +334,34 @@ void MadsPlayer::nextFrame() { } } +void MadsPlayer::setDest(int destX, int destY, int facing) { + resetActionList(); + setTicksAmount(); + _moving = true; + _destFacing = facing; + + _madsVm->scene()->getSceneResources().setRouteNode(_madsVm->scene()->getSceneResources()._nodes.size() - 2, + _playerPos, _madsVm->scene()->_depthSurface); + _madsVm->scene()->getSceneResources().setRouteNode(_madsVm->scene()->getSceneResources()._nodes.size() - 1, + Common::Point(destX, destY), _madsVm->scene()->_depthSurface); + + bool v = _madsVm->scene()->getDepthHighBit(Common::Point(destX, destY)); + setupRoute(v); + _next = 0; + + if (_routeCount > 0) { + Common::Point srcPos = _playerPos; + for (int routeCtr = _routeCount - 1; (routeCtr >= 0) && (_next == 0); --routeCtr) { + int idx = _routeIndexes[routeCtr]; + const Common::Point &pt = _madsVm->scene()->getSceneResources()._nodes[idx].pt; + + _next = scanPath(_madsVm->scene()->_depthSurface, srcPos, pt); + srcPos = pt; + } + } +} + + int MadsPlayer::getScale(int yp) { MadsSceneResources &r = _madsVm->scene()->getSceneResources(); @@ -411,7 +448,100 @@ void MadsPlayer::idle() { } void MadsPlayer::move() { - // TODO: Handle player movement + bool routeFlag = false; + + if (_moving) { + int idx = _routeCount; + while (!_v844C0 && (_destPos.x == _playerPos.x) && (_destPos.y == _playerPos.y)) { + if (idx != 0) { + --idx; + SceneNode &node = _madsVm->scene()->getSceneResources()._nodes[_routeIndexes[idx]]; + _destPos = node.pt; + routeFlag = true; + } else if (_v844BE == idx) { + // End of walking path + _routeCount = 0; + _moving = false; + turnToDestFacing(); + routeFlag = true; + idx = _routeCount; + } else { + _v844C0 = _v844BE; + _v844BC = true; + _v844BE = 0; + _stepEnabled = true; + routeFlag = false; + } + + if (!_moving) + break; + } + _routeCount = idx; + } + + if (routeFlag && _moving) + startMovement(); + + if (_newDirection != _direction) + dirChanged(); + else if (!_moving) + updateFrame(); + + int var1 = _unk1; + if (_unk4 && (_hypotenuse > 0)) { + int v1 = -(_currentScale - 100) * (_posDiff.x - 1) / _hypotenuse + _currentScale; + var1 = MAX(1, 10000 / (v1 * _currentScale * var1)); + } + + if (!_moving || (_direction != _newDirection)) + return; + + Common::Point newPos = _playerPos; + + if (_v8452E < var1) { + do { + if (_v8452C < _posDiff.x) + _v8452C += _posDiff.y; + if (_v8452C >= _posDiff.x) { + if ((_posChange.y > 0) || (_v844C0 != 0)) + newPos.y += _yDirection; + --_posChange.y; + _v8452C -= _posDiff.x; + } + + if (_v8452C < _posDiff.x) { + if ((_posChange.x > 0) || (_v844C0 != 0)) + newPos.x += _xDirection; + --_posChange.x; + } + + if ((_v844BC == 0) && (_v844C0 == 0) && (_v844BE == 0)) { + routeFlag = _madsVm->scene()->getDepthHighBit(newPos); + + if (_special == 0) + _special = _madsVm->scene()->getDepthHighBits(newPos); + } + + _v8452E += _v84530; + + } while ((_v8452E < var1) && !routeFlag && ((_posChange.x > 0) || (_posChange.y > 0) || (_v844C0 != 0))); + } + + _v8452E -= var1; + + if (routeFlag) + moveComplete(); + else { + if (!_v844C0) { + // If the move is complete, make sure the position is exactly on the given destination + if (_posChange.x == 0) + newPos.x = _destPos.x; + if (_posChange.y == 0) + newPos.y = _destPos.y; + } + + _playerPos = newPos; + } } void MadsPlayer::dirChanged() { @@ -451,4 +581,212 @@ void MadsPlayer::dirChanged() { _priorTimer += 1; } +void MadsPlayer::moveComplete() { + reset(); + //todo: Unknown flag +} + +void MadsPlayer::reset() { + _destPos = _playerPos; + _destFacing = 5; + _newDirection = _direction; + + _madsVm->scene()->_action._startWalkFlag = false; + _madsVm->scene()->_action._walkFlag = false; + _moving = false; + _v844BC = false; + _v844C0 = false; + _v844BE = 0; + _next = 0; + _routeCount = 0; +} + +/** + * Scans along an edge connecting two points within the depths/walk surface, and returns the information of the first + * pixel high nibble encountered with a non-zero value + */ +int MadsPlayer::scanPath(M4Surface *depthSurface, const Common::Point &srcPos, const Common::Point &destPos) { + // For compressed depth surfaces, always return 0 + if (_madsVm->scene()->getSceneResources()._depthStyle != 2) + return 0; + + int yDiff = destPos.y - srcPos.y; + int yAmount = MADS_SURFACE_WIDTH; + + if (yDiff < 0) { + yDiff = -yDiff; + yAmount = -yAmount; + } + + int xDiff = destPos.x - srcPos.y; + int xDirection = 1; + int xAmount = 0; + if (xDiff < 0) { + xDiff = -xDiff; + xDirection = -xDirection; + xAmount = MIN(yDiff, xDiff); + } + + ++xDiff; + ++yDiff; + + const byte *srcP = depthSurface->getBasePtr(srcPos.x, srcPos.y); + int index = xAmount; + + // Outer horizontal movement loop + for (int yIndex = 0; yIndex < yDiff; ++yIndex) { + index += yDiff; + int v = (*srcP & 0x7F) >> 4; + if (v) + return v; + + // Inner loop for handling vertical movement + while (index >= xDiff) { + index -= xDiff; + + v = (*srcP & 0x7F) >> 4; + if (v) + return v; + + srcP += yAmount; + } + + srcP += xDirection; + } + + return 0; +} + +/** + * Starts a player moving to a given destination + */ +void MadsPlayer::startMovement() { + int xDiff = _destPos.x - _playerPos.x; + int yDiff = _destPos.y - _playerPos.y; + int srcScale = getScale(_playerPos.y); + int destScale = getScale(_destPos.y); + + // Sets the X direction + if (xDiff > 0) + _xDirection = 1; + else if (xDiff < 0) + _xDirection = -1; + else + _xDirection = 0; + + // Sets the Y direction + if (yDiff > 0) + _yDirection = 1; + else if (yDiff < 0) + _yDirection = -1; + else + _yDirection = 0; + + xDiff = ABS(xDiff); + yDiff = ABS(yDiff); + int scaleDiff = ABS(srcScale - destScale); + + int xAmt100 = xDiff * 100; + int yAmt100 = yDiff * 100; + int xAmt33 = xDiff * 33; + + int scaleAmount = (_unk4 ? scaleDiff * 3 : 0) + 100 * yDiff / 100; + int scaleAmount100 = scaleAmount * 100; + + // Figure out direction that will need to be moved in + int majorDir; + if (xDiff == 0) + majorDir = 1; + else if (yDiff == 0) + majorDir = 3; + else { + if ((scaleAmount < xDiff) && ((xAmt33 / scaleAmount) >= 141)) + majorDir = 3; + else if (yDiff <= xDiff) + majorDir = 2; + else if ((scaleAmount100 / xDiff) >= 141) + majorDir = 1; + else + majorDir = 2; + } + + switch (majorDir) { + case 1: + _newDirection = (_yDirection <= 0) ? 8 : 2; + break; + case 2: { + _newDirection = ((_yDirection <= 0) ? 9 : 3) - ((_xDirection <= 0) ? 2 : 0); + break; + } + case 3: + _newDirection = (_xDirection <= 0) ? 4 : 6; + break; + default: + break; + } + + _hypotenuse = SqrtF16(xAmt100 * xAmt100 + yAmt100 * yAmt100); + _posDiff.x = xDiff + 1; + _posDiff.y = yDiff + 1; + _posChange.x = xDiff; + _posChange.y = yDiff; + + int majorChange = MAX(xDiff, yDiff); + _v84530 = (majorChange == 0) ? 0 : _hypotenuse / majorChange; + + if (_playerPos.x > _destPos.x) + _v8452C = MAX(_posChange.x, _posChange.y); + else + _v8452C = 0; + + _hypotenuse /= 100; + _v8452E = -_v84530; +} + +void MadsPlayer::turnToDestFacing() { + if (_destFacing != 5) + _newDirection = _destFacing; +} + +void MadsPlayer::setupRoute(bool bitFlag) { + // Reset the flag set of nodes in use + SceneNodeList &nodes = _madsVm->scene()->getSceneResources()._nodes; + for (uint i = 0; i < nodes.size(); ++i) + nodes[i].active = false; + + // Start constructing route node list + _routeLength = 0x3FFF; + _routeCount = 0; + + setupRouteNode(_tempRoute, nodes.size() - 1, bitFlag ? 0xC000 : 0x8000, 0); +} + +void MadsPlayer::setupRouteNode(int *routeIndexP, int nodeIndex, int flags, int routeLength) { + SceneNodeList &nodes = _madsVm->scene()->getSceneResources()._nodes; + SceneNode ¤tNode = nodes[nodeIndex]; + currentNode.active = true; + + *routeIndexP++ = nodeIndex; + + int subIndex = nodes.size() - 2; + int indexVal = nodes[nodeIndex].indexes[subIndex]; + if (indexVal & flags) { + routeLength += indexVal & 0x3FFF; + if (routeLength < _routeLength) { + // Found a new shorter route to destination, so set up the route with the found one + Common::copy(_tempRoute, routeIndexP, _routeIndexes); + _routeCount = routeIndexP - _tempRoute; + _routeLength = indexVal & 0x3FFF; + } + } else { + for (int idx = nodes.size() - 2; idx > 0; --idx) { + int nodePos = idx - 1; + if (!nodes[nodePos].active && ((currentNode.indexes[nodePos] & flags) != 0)) + setupRouteNode(routeIndexP, nodePos, 0x8000, indexVal & 0x3fff); + } + } + + currentNode.active = false; +} + } // End of namespace M4 diff --git a/engines/m4/mads_player.h b/engines/m4/mads_player.h index 65ed9ef89c..6a9b7b4ca1 100644 --- a/engines/m4/mads_player.h +++ b/engines/m4/mads_player.h @@ -27,6 +27,7 @@ #define M4_MADS_PLAYER_H #include "common/scummsys.h" +#include "m4/mads_scene.h" namespace M4 { @@ -42,12 +43,19 @@ private: void idle(); void move(); void dirChanged(); + void reset(); + int scanPath(M4Surface *depthSurface, const Common::Point &srcPos, const Common::Point &destPos); + void startMovement(); + void setupRouteNode(int *routeIndexP, int nodeIndex, int flags, int routeLength); public: char _spritesPrefix[16]; int _spriteSetCount; bool _spriteSetsPresent[8]; Common::Point _playerPos; Common::Point _destPos; + Common::Point _posChange; + Common::Point _posDiff; + int _hypotenuse; uint32 _priorTimer; uint _ticksAmount; int16 _direction, _newDirection; @@ -70,6 +78,22 @@ public: int _actionList2[12]; int _unk2; int _unk3; + int _xDirection, _yDirection; + int _destFacing; + int _special; + int _next; + int _routeCount; + int _routeOffset; + int _tempRoute[MAX_ROUTE_NODES]; + int _routeIndexes[MAX_ROUTE_NODES]; + bool _unk4; + bool _v844BC; + int _v844BE; + bool _v844C0; + int _v8452E; + int _v8452C; + int _v84530; + int _routeLength; static const int _directionListIndexes[32]; public: @@ -81,6 +105,10 @@ public: void setupFrame(); void step(); void nextFrame(); + void setDest(int destX, int destY, int facing); + void turnToDestFacing(); + void setupRoute(bool bitFlag); + void moveComplete(); }; } // End of namespace M4 diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp index e4f84aeb5a..d44fa2a753 100644 --- a/engines/m4/mads_scene.cpp +++ b/engines/m4/mads_scene.cpp @@ -48,6 +48,17 @@ static const int SCROLLER_DELAY = 200; //-------------------------------------------------------------------------- +void SceneNode::load(Common::SeekableReadStream *stream) { + // Get the next data block + pt.x = stream->readUint16LE(); + pt.y = stream->readUint16LE(); + + for (int i = 0; i < MAX_ROUTE_NODES; ++i) + indexes[i] = stream->readUint16LE(); +} + +//-------------------------------------------------------------------------- + MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResources), MadsView(this) { _vm = vm; _activeAnimation = NULL; @@ -55,6 +66,8 @@ MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResour MadsView::_bgSurface = Scene::_backgroundSurface; MadsView::_depthSurface = Scene::_walkSurface; _interfaceSurface = new MadsInterfaceView(vm); + _showMousePos = false; + _mouseMsgIndex = -1; } MadsScene::~MadsScene() { @@ -182,24 +195,21 @@ void MadsScene::loadSceneHotspots(int sceneNumber) { int hotspotCount = hotspotStream->readUint16LE(); delete hotspotStream; - _sceneResources.hotspotCount = hotspotCount; - hotspotStream = hotSpotData.getItemStream(1); // Clear current hotspot lists _sceneResources.hotspots->clear(); - - _sceneResources.hotspots->loadHotSpots(hotspotStream, _sceneResources.hotspotCount); + _sceneResources.hotspots->loadHotSpots(hotspotStream, hotspotCount); delete hotspotStream; } void MadsScene::leaveScene() { _sceneResources.hotspots->clear(); - _sceneResources.props->clear(); + _sceneResources.dynamicHotspots->clear(); delete _sceneResources.hotspots; - delete _sceneResources.props; + delete _sceneResources.dynamicHotspots; delete _walkSurface; if (_activeAnimation) { @@ -228,43 +238,28 @@ void MadsScene::loadSceneCodes(int sceneNumber, int index) { } } -void MadsScene::checkHotspotAtMousePos(int x, int y) { +void MadsScene::mouseMove(int x, int y) { HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y); if (currentHotSpot != NULL) { _vm->_mouse->setCursorNum(currentHotSpot->getCursor()); - // This is the "easy" interface, which updates the status text when the mouse is moved - // TODO: toggle this code for easy/normal interface mode - char statusText[50]; - int verbId = 0;//***DEBUG****_currentAction; - if (verbId == kVerbNone) - verbId = currentHotSpot->getVerbID(); - if (verbId == kVerbNone) - verbId = kVerbWalkTo; - - sprintf(statusText, "%s %s\n", _madsVm->globals()->getVocab(verbId), currentHotSpot->getVocab()); + _action._selectedRow = -1; + _action._actionMode = ACTMODE_NONE; + _action._actionMode2 = ACTMODE2_4; + _action._hotspotId = currentHotSpot->getIndex(); - statusText[0] = toupper(statusText[0]); // capitalize first letter - setStatusText(statusText); } else { _vm->_mouse->setCursorNum(0); - setStatusText(""); } } void MadsScene::leftClick(int x, int y) { - HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y); - if (currentHotSpot != NULL) { - char statusText[50]; - if (currentHotSpot->getVerbID() != 0) { - sprintf(statusText, "%s %s\n", currentHotSpot->getVerb(), currentHotSpot->getVocab()); - } else { - sprintf(statusText, "%s %s\n", _madsVm->globals()->getVocab(kVerbWalkTo), currentHotSpot->getVocab()); - } - - statusText[0] = toupper(statusText[0]); // capitalize first letter - setStatusText(statusText); - } + // TODO: figure out the rest of Scene_leftClick, and implements relevant parts in the interface class + _action._v86F4C = -1; + _action._v86F4E = 0; + _customDest = _madsVm->_mouse->currentPos(); + _action._selectedAction = -1; + _action._v86F4A = true; } void MadsScene::rightClick(int x, int y) { @@ -280,7 +275,6 @@ void MadsScene::rightClick(int x, int y) { void MadsScene::setAction(int action, int objectId) { VALIDATE_MADS; - char statusText[50]; error("todo"); // TODO: Actually executing actions directly for objects. Also, some object actions are special in that @@ -297,7 +291,7 @@ void MadsScene::setAction(int action, int objectId) { _currentAction = action; } */ - setStatusText(statusText); +// setStatusText(statusText); } /** @@ -316,39 +310,77 @@ void MadsScene::update() { drawElements(); _action.set(); - const char *sStatusText = _action.statusText(); - - // Handle display of any status text - if (sStatusText[0]) { - // Text colors are inverted in Dragonsphere - if (_vm->getGameType() == GType_DragonSphere) - _vm->_font->current()->setColours(_vm->_palette->BLACK, _vm->_palette->WHITE, _vm->_palette->BLACK); - else - _vm->_font->current()->setColours(_vm->_palette->WHITE, _vm->_palette->BLACK, _vm->_palette->BLACK); - - _vm->_font->setFont(FONT_MAIN_MADS); - _vm->_font->current()->writeString(this, sStatusText, (width() - _vm->_font->current()->getWidth(sStatusText)) / 2, 142, 0); - } } void MadsScene::updateState() { - _madsVm->_player.update(); + if (!_abortTimers && !_madsVm->_player._unk3) { + if (_dynamicHotspots._changed) + _dynamicHotspots.refresh(); - // Step through the scene - _sceneLogic.sceneStep(); +// int v = (_madsVm->_player._stepEnabled && !_action._startWalkFlag && !_abortTimers2) ? 1 : 0; +// _screenObjects.check(v, false); + } - _madsVm->_player.step(); - _madsVm->_player._unk3 = 0; + // Handle starting off any selected action + bool doPreAction = false; + if ((_action._selectedAction != 0) && _madsVm->_player._stepEnabled && + !_action._startWalkFlag && !_abortTimers && !_madsVm->_player._unk3) { + // Start the action + _action.startAction(); - if (_abortTimersMode == ABORTMODE_1) - _abortTimers = 0; + if (_action._action.verbId == kVerbLookAt) { + _action._action.verbId = kVerbLook; + _action._savedFields.selectedRow = 0; + } + doPreAction = true; + } + if (doPreAction || ((_abortTimers != 0) && (_abortTimersMode == ABORTMODE_2))) + doPreactions(); + + checkStartWalk(); + + if (_action._inProgress && !_madsVm->_player._moving && !_action._startWalkFlag && + (_madsVm->_player._newDirection == _madsVm->_player._direction)) { + // Reached the end of action movement, so ready to actually do action + doAction(); + } else if ((_abortTimers != 0) && (_abortTimersMode == ABORTMODE_0)) + // Do an action designated by scripts + doAction(); + + bool freeFlag = false; + if (_currentScene != _nextScene) + freeFlag = true; + else { + doSceneStep(); + + if (_currentScene != _nextScene) + freeFlag = true; + else { + // Update the player + _madsVm->_player.nextFrame(); + + // Handle updating the animation + if (!_abortTimers && (_activeAnimation)) + _activeAnimation->update(); + + // Handle refreshing the mouse position display + if (_mouseMsgIndex != -1) + _madsVm->scene()->_kernelMessages.remove(_mouseMsgIndex); + if (_showMousePos) { + char buffer[20]; + sprintf(buffer, "(%d,%d)", _madsVm->_mouse->currentPos().x, _madsVm->_mouse->currentPos().y); + + _mouseMsgIndex = _madsVm->scene()->_kernelMessages.add(Common::Point(5, 5), 0x203, 0, 0, 1, buffer); + } + } + } - // Handle updating the player frame - _madsVm->_player.nextFrame(); + if (_madsVm->globals()->_config.easyMouse) + _action.refresh(); if ((_activeAnimation) && !_abortTimers) { _activeAnimation->update(); - if (((MadsAnimation *) _activeAnimation)->freeFlag()) { + if (((MadsAnimation *) _activeAnimation)->freeFlag() || freeFlag) { delete _activeAnimation; _activeAnimation = NULL; } @@ -359,8 +391,48 @@ void MadsScene::updateState() { // Remove the animation if it's been completed if ((_activeAnimation) && ((MadsAnimation *)_activeAnimation)->freeFlag()) freeAnimation(); + + if ((_action._selectedAction != 0) || !_madsVm->_player._stepEnabled) { + _action.clear(); + _action._selectedAction = 0; + } } +void MadsScene::checkStartWalk() { + if (_action._startWalkFlag && _action._walkFlag) { + _madsVm->_player.setDest(_destPos.x, _destPos.y, _destFacing); + _action._startWalkFlag = false; + } +} + +void MadsScene::doPreactions() { + if ((_screenObjects._v832EC == 0) || (_screenObjects._v832EC == 2)) { + _abortTimersMode2 = ABORTMODE_2; + _action.checkAction(); + + _sceneLogic.doPreactions(); + + if (_abortTimersMode == ABORTMODE_2) + _abortTimers = 0; + } +} + +void MadsScene::doSceneStep() { + // Step through the scene + _sceneLogic.doSceneStep(); + + _madsVm->_player.step(); + _madsVm->_player._unk3 = 0; + + if (_abortTimersMode == ABORTMODE_1) + _abortTimers = 0; +} + +void MadsScene::doAction() { + warning("TODO MadsScene::doAction"); +} + + /** * Does extra work at cleaning up the animation, and then deletes it */ @@ -503,179 +575,20 @@ void MadsScene::loadAnimation(const Common::String &animName, int abortTimers) { _activeAnimation = anim; } -/*--------------------------------------------------------------------------*/ - -MadsAction::MadsAction() { - clear(); -} - -void MadsAction::clear() { - _actionMode = ACTMODE_NONE; - _actionMode2 = ACTMODE2_0; - _word_86F42 = 0; - _word_86F4E = 0; - _articleNumber = 0; - _lookFlag = false; - _word_86F4A = 0; - _statusText[0] = '\0'; - _selectedRow = -1; - _currentHotspot = -1; - _word_86F3A = -1; - _word_86F4C = -1; - //word_86F3A/word_86F4C - _currentAction = kVerbNone; - _objectNameId = -1; - _objectDescId = -1; - _word_83334 = -1; -} - -void MadsAction::appendVocab(int vocabId, bool capitalise) { - char *s = _statusText + strlen(_statusText); - const char *vocabStr = _madsVm->globals()->getVocab(vocabId); - strcpy(s, vocabStr); - if (capitalise) - *s = toupper(*s); +bool MadsScene::getDepthHighBit(const Common::Point &pt) { + const byte *p = _depthSurface->getBasePtr(pt.x, pt.y); + if (_sceneResources._depthStyle == 2) + return ((*p << 4) & 0x80) != 0; - strcat(s, " "); + return (*p & 0x80) != 0; } -void MadsAction::set() { - int hotspotCount = _madsVm->scene()->getSceneResources().hotspotCount; - bool flag = false; - _currentAction = -1; - _objectNameId = -1; - _objectDescId = -1; - - if (_actionMode == ACTMODE_TALK) { - // Handle showing the conversation selection. Rex at least doesn't actually seem to use this - if (_selectedRow >= 0) { - const char *desc = _madsVm->_converse[_selectedRow].desc; - if (desc) - strcpy(_statusText, desc); - } - } else if (_lookFlag && (_selectedRow == 0)) { - // Two 'look' actions in succession, so the action becomes 'Look around' - strcpy(_statusText, lookAroundStr); - } else { - if ((_actionMode == ACTMODE_OBJECT) && (_selectedRow >= 0) && (_flags1 == 2) && (_flags2 == 0)) { - // Use/to action - int selectedObject = _madsVm->scene()->getInterface()->getSelectedObject(); - MadsObject *objEntry = _madsVm->globals()->getObject(selectedObject); - - _objectNameId = objEntry->descId; - _currentAction = objEntry->vocabList[_selectedRow].vocabId; - - // Set up the status text stirng - strcpy(_statusText, useStr); - appendVocab(_objectNameId); - strcpy(_statusText, toStr); - appendVocab(_currentAction); - } else { - // Handling for if an action has been selected - if (_selectedRow >= 0) { - if (_actionMode == ACTMODE_VERB) { - // Standard verb action - _currentAction = verbList[_selectedRow].verb; - } else { - // Selected action on an inventory object - int selectedObject = _madsVm->scene()->getInterface()->getSelectedObject(); - MadsObject *objEntry = _madsVm->globals()->getObject(selectedObject); - - _currentAction = objEntry->vocabList[_selectedRow].vocabId; - } - - appendVocab(_currentAction, true); - - if (_currentAction == kVerbLook) { - // Add in the word 'add' - strcat(_statusText, atStr); - strcat(_statusText, " "); - } - } - - // Handling for if a hotspot has been selected/highlighted - if ((_currentHotspot >= 0) && (_selectedRow >= 0) && (_articleNumber > 0) && (_flags1 == 2)) { - flag = true; - - strcat(_statusText, englishMADSArticleList[_articleNumber]); - strcat(_statusText, " "); - } - - if (_currentHotspot >= 0) { - if (_selectedRow < 0) { - int verbId; - - if (_currentHotspot < hotspotCount) { - // Get the verb Id from the hotspot - verbId = (*_madsVm->scene()->getSceneResources().hotspots)[_currentHotspot].getVerbID(); - } else { - // Get the verb Id from the scene object - verbId = (*_madsVm->scene()->getSceneResources().props)[_currentHotspot - hotspotCount].getVerbID(); - } - - if (verbId > 0) { - // Set the specified action - _currentAction = verbId; - appendVocab(_currentAction, true); - } else { - // Default to a standard 'walk to' - _currentAction = kVerbWalkTo; - strcat(_statusText, walkToStr); - } - } - - if ((_actionMode2 == ACTMODE2_2) || (_actionMode2 == ACTMODE2_5)) { - // Get name from given inventory object - int objectId = _madsVm->scene()->getInterface()->getInventoryObject(_currentHotspot); - _objectNameId = _madsVm->globals()->getObject(objectId)->descId; - } else if (_currentHotspot < hotspotCount) { - // Get name from scene hotspot - _objectNameId = (*_madsVm->scene()->getSceneResources().hotspots)[_currentHotspot].getVocabID(); - } else { - // Get name from temporary scene hotspot - _objectNameId = (*_madsVm->scene()->getSceneResources().props)[_currentHotspot].getVocabID(); - } - } - } - - if ((_currentHotspot >= 0) && (_articleNumber > 0) && !flag) { - if (_articleNumber == -1) { - if (_word_86F3A >= 0) { - int articleNum = 0; - - if ((_word_86F42 == 2) || (_word_86F42 == 5)) { - int objectId = _madsVm->scene()->getInterface()->getInventoryObject(_currentHotspot); - articleNum = _madsVm->globals()->getObject(objectId)->article; - } else if (_word_86F3A < hotspotCount) { - articleNum = (*_madsVm->scene()->getSceneResources().hotspots)[_currentHotspot].getArticle(); - } else { - - } - } - - } else if ((_articleNumber == kVerbLook) || (_vm->getGameType() != GType_RexNebular) || - (strcmp(_madsVm->globals()->getVocab(_objectDescId), fenceStr) != 0)) { - // Write out the article - strcat(_statusText, englishMADSArticleList[_articleNumber]); - } else { - // Special case for a 'fence' entry in Rex Nebular - strcat(_statusText, overStr); - } - - strcat(_statusText, " "); - } - - // Append object description if necessary - if (_word_86F3A >= 0) - appendVocab(_objectDescId); - - // Remove any trailing space character - int statusLen = strlen(_statusText); - if ((statusLen > 0) && (_statusText[statusLen - 1] == ' ')) - _statusText[statusLen - 1] = '\0'; - } +bool MadsScene::getDepthHighBits(const Common::Point &pt) { + if (_sceneResources._depthStyle == 2) + return 0; - _word_83334 = -1; + const byte *p = _depthSurface->getBasePtr(pt.x, pt.y); + return (*p & 0x70) >> 4; } /*--------------------------------------------------------------------------*/ @@ -722,7 +635,7 @@ void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Su stream->skip(24); - int objectCount = stream->readUint16LE(); + int nodeCount = stream->readUint16LE(); _yBandsEnd = stream->readUint16LE(); _yBandsStart = stream->readUint16LE(); _maxScale = stream->readSint16LE(); @@ -732,14 +645,20 @@ void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Su stream->skip(2); // Load in any scene objects - for (int i = 0; i < objectCount; ++i) { - MadsObject rec; + for (int i = 0; i < nodeCount; ++i) { + SceneNode rec; rec.load(stream); - _objects.push_back(rec); + _nodes.push_back(rec); } - for (int i = 0; i < 20 - objectCount; ++i) + for (int i = 0; i < 20 - nodeCount; ++i) stream->skip(48); + // Add two extra nodes in that will be used for player movement + for (int i = 0; i < 2; ++i) { + SceneNode rec; + _nodes.push_back(rec); + } + int setCount = stream->readUint16LE(); stream->readUint16LE(); for (int i = 0; i < setCount; ++i) { @@ -810,6 +729,84 @@ void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Su delete depthSurface; } +void MadsSceneResources::setRouteNode(int nodeIndex, const Common::Point &pt, M4Surface *depthSurface) { + int flags, hypotenuse; + + _nodes[nodeIndex].pt = pt; + + // Recalculate inter-node lengths + for (uint idx = 0; idx < _nodes.size(); ++idx) { + int entry; + if (idx == (uint)nodeIndex) { + entry = 0x3FFF; + } else { + // Process the node + flags = getRouteFlags(pt, _nodes[idx].pt, depthSurface); + + int xDiff = ABS(_nodes[idx].pt.x - pt.x); + int yDiff = ABS(_nodes[idx].pt.y - pt.y); + hypotenuse = SqrtF16(xDiff * xDiff + yDiff * yDiff); + + if (hypotenuse >= 0x3FFF) + // Shouldn't ever be this large + hypotenuse = 0x3FFF; + + entry = hypotenuse | flags; + _nodes[idx].indexes[nodeIndex] = entry; + _nodes[nodeIndex].indexes[idx] = entry; + } + } +} + +int MadsSceneResources::getRouteFlags(const Common::Point &src, const Common::Point &dest, M4Surface *depthSurface) { + int result = 0x8000; + bool flag = false; + + int xDiff = ABS(dest.x - src.x); + int yDiff = ABS(dest.y - src.y); + int xDirection = dest.x >= src.x ? 1 : -1; + int yDirection = dest.y >= src.y ? depthSurface->width() : -depthSurface->width(); + int majorDiff = 0; + if (dest.x < src.x) + majorDiff = MAX(xDiff, yDiff); + ++xDiff; + ++yDiff; + + byte *srcP = depthSurface->getBasePtr(src.x, src.y); + + int totalCtr = majorDiff; + for (int xCtr = 0; xCtr < xDiff; ++xCtr, srcP += xDirection) { + totalCtr += yDiff; + + if ((*srcP & 0x80) == 0) + flag = false; + else if (!flag) { + flag = true; + result -= 0x4000; + if (result == 0) + break; + } + + while (totalCtr >= xDiff) { + totalCtr -= xDiff; + + if ((*srcP & 0x80) == 0) + flag = false; + else if (!flag) { + flag = true; + result -= 0x4000; + if (result == 0) + break; + } + + srcP += yDirection; + } + if (result == 0) + break; + } + + return result; +} /*--------------------------------------------------------------------------*/ @@ -1024,7 +1021,7 @@ void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) { } bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) { - MadsAction &act = _madsVm->scene()->getAction(); + MadsAction &act = _madsVm->scene()->_action; // If the mouse isn't being held down, then reset the repeated scroll timer if (eventType != MEVENT_LEFT_HOLD) @@ -1071,7 +1068,7 @@ bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int act._flags1 = obj->vocabList[1].flags1; act._flags2 = obj->vocabList[1].flags2; - act._currentHotspot = _selectedObject; + act._action.verbId = _selectedObject; act._articleNumber = act._flags2; } } @@ -1124,8 +1121,13 @@ bool MadsInterfaceView::handleCheatKey(int32 keycode) { // TODO: Move player to current destination return true; - case Common::KEYCODE_t | (Common::KEYCODE_LALT): - case Common::KEYCODE_t | (Common::KEYCODE_RALT): + case Common::KEYCODE_c | (Common::KBD_CTRL << 24): + // Toggle display of mouse position + _madsVm->scene()->_showMousePos = !_madsVm->scene()->_showMousePos; + break; + + case Common::KEYCODE_t | (Common::KEYCODE_LALT << 24): + case Common::KEYCODE_t | (Common::KEYCODE_RALT << 24): { // Teleport to room //Scene *sceneView = (Scene *)vm->_viewManager->getView(VIEWID_SCENE); diff --git a/engines/m4/mads_scene.h b/engines/m4/mads_scene.h index e671dfb194..7723058cc7 100644 --- a/engines/m4/mads_scene.h +++ b/engines/m4/mads_scene.h @@ -36,15 +36,37 @@ namespace M4 { class MadsInterfaceView; #define DEPTH_BANDS_SIZE 15 +#define MAX_ROUTE_NODES 22 + +enum ScreenCategory {CAT_NONE = 0, CAT_ACTION = 1, CAT_INV_LIST = 2, CAT_INV_VOCAB, CAT_HOTSPOT = 4, + CAT_INV_ANIM = 6, CAT_6, CAT_INV_SCROLLER = 7, CAT_12 = 12}; + +class SceneNode { +public: + Common::Point pt; + int indexes[MAX_ROUTE_NODES]; + + bool active; + + SceneNode() { + active = false; + } + + void load(Common::SeekableReadStream *stream); +}; + +typedef Common::Array<SceneNode> SceneNodeList; class MadsSceneResources: public SceneResources { +private: + int getRouteFlags(const Common::Point &src, const Common::Point &dest, M4Surface *depthSurface); public: int _sceneId; int _artFileNum; int _depthStyle; int _width; int _height; - Common::Array<MadsObject> _objects; + SceneNodeList _nodes; Common::Array<Common::String> _setNames; int _yBandsStart, _yBandsEnd; int _maxScale, _minScale; @@ -55,52 +77,19 @@ public: void load(int sceneId, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface); int bandsRange() const { return _yBandsEnd - _yBandsStart; } int scaleRange() const { return _maxScale - _minScale; } -}; - -enum MadsActionMode {ACTMODE_NONE = 0, ACTMODE_VERB = 1, ACTMODE_OBJECT = 3, ACTMODE_TALK = 6}; -enum MAdsActionMode2 {ACTMODE2_0 = 0, ACTMODE2_2 = 2, ACTMODE2_5 = 5}; - -class MadsAction { -private: - char _statusText[100]; - - void appendVocab(int vocabId, bool capitalise = false); -public: - int _currentHotspot; - int _objectNameId; - int _objectDescId; - int _currentAction; - int8 _flags1, _flags2; - MadsActionMode _actionMode; - MAdsActionMode2 _actionMode2; - int _articleNumber; - bool _lookFlag; - int _selectedRow; - // Unknown fields - int16 _word_86F3A; - int16 _word_86F42; - int16 _word_86F4E; - int16 _word_86F4A; - int16 _word_83334; - int16 _word_86F4C; - -public: - MadsAction(); - - void clear(); - void set(); - const char *statusText() const { return _statusText; } + void setRouteNode(int nodeIndex, const Common::Point &pt, M4Surface *depthSurface); }; class MadsScene : public Scene, public MadsView { private: MadsEngine *_vm; MadsSceneResources _sceneResources; - MadsAction _action; Animation *_activeAnimation; MadsSceneLogic _sceneLogic; SpriteAsset *_playerSprites; + int _mouseMsgIndex; + int _highlightedHotspot; void drawElements(); void loadScene2(const char *aaName, int sceneNumber); @@ -109,8 +98,16 @@ private: void clearAction(); void appendActionVocab(int vocabId, bool capitalise); void setAction(); + void checkStartWalk(); + void doPreactions(); + void doSceneStep(); + void doAction(); public: char _aaName[100]; + bool _showMousePos; + Common::Point _destPos; + int _destFacing; + Common::Point _customDest; public: MadsScene(MadsEngine *vm); virtual ~MadsScene(); @@ -120,7 +117,7 @@ public: virtual void leaveScene(); virtual void loadSceneCodes(int sceneNumber, int index = 0); virtual void show(); - virtual void checkHotspotAtMousePos(int x, int y); + virtual void mouseMove(int x, int y); virtual void leftClick(int x, int y); virtual void rightClick(int x, int y); virtual void setAction(int action, int objectId = -1); @@ -136,8 +133,8 @@ public: MadsInterfaceView *getInterface() { return (MadsInterfaceView *)_interfaceSurface; } MadsSceneResources &getSceneResources() { return _sceneResources; } - MadsAction &getAction() { return _action; } - void setStatusText(const char *text) {}//***DEPRECATED*** + bool getDepthHighBit(const Common::Point &pt); + bool getDepthHighBits(const Common::Point &pt); }; #define CHEAT_SEQUENCE_MAX 8 diff --git a/engines/m4/mads_views.cpp b/engines/m4/mads_views.cpp index d7e6435b14..3e5f0c2ac9 100644 --- a/engines/m4/mads_views.cpp +++ b/engines/m4/mads_views.cpp @@ -37,6 +37,307 @@ namespace M4 { +MadsAction::MadsAction(MadsView &owner): _owner(owner) { + clear(); + _currentAction = kVerbNone; + _startWalkFlag = false; + _statusTextIndex = -1; + _selectedAction = 0; + _inProgress = false; +} + +void MadsAction::clear() { + _v83338 = 1; + _actionMode = ACTMODE_NONE; + _actionMode2 = ACTMODE2_0; + _v86F42 = 0; + _v86F4E = 0; + _articleNumber = 0; + _lookFlag = false; + _v86F4A = 0; + _statusText[0] = '\0'; + _selectedRow = -1; + _hotspotId = -1; + _v86F3A = -1; + _v86F4C = -1; + _action.verbId = -1; + _action.objectNameId = -1; + _action.indirectObjectId = -1; + _textChanged = true; + _walkFlag = false; +} + +void MadsAction::appendVocab(int vocabId, bool capitalise) { + char *s = _statusText + strlen(_statusText); + const char *vocabStr = _madsVm->globals()->getVocab(vocabId); + strcpy(s, vocabStr); + if (capitalise) + *s = toupper(*s); + + strcat(s, " "); +} + +void MadsAction::set() { + int hotspotCount = _madsVm->scene()->getSceneResources().hotspots->size(); + bool flag = false; + strcpy(_statusText, ""); + + _currentAction = -1; + _action.objectNameId = -1; + _action.indirectObjectId = -1; + + if (_actionMode == ACTMODE_TALK) { + // Handle showing the conversation selection. Rex at least doesn't actually seem to use this + if (_selectedRow >= 0) { + const char *desc = _madsVm->_converse[_selectedRow].desc; + if (desc) + strcpy(_statusText, desc); + } + } else if (_lookFlag && (_selectedRow == 0)) { + // Two 'look' actions in succession, so the action becomes 'Look around' + strcpy(_statusText, lookAroundStr); + } else { + if ((_actionMode == ACTMODE_OBJECT) && (_selectedRow >= 0) && (_flags1 == 2) && (_flags2 == 0)) { + // Use/to action + int selectedObject = _madsVm->scene()->getInterface()->getSelectedObject(); + MadsObject *objEntry = _madsVm->globals()->getObject(selectedObject); + + _action.objectNameId = objEntry->descId; + _currentAction = objEntry->vocabList[_selectedRow].vocabId; + + // Set up the status text stirng + strcpy(_statusText, useStr); + appendVocab(_action.objectNameId); + strcpy(_statusText, toStr); + appendVocab(_currentAction); + } else { + // Handling for if an action has been selected + if (_selectedRow >= 0) { + if (_actionMode == ACTMODE_VERB) { + // Standard verb action + _currentAction = verbList[_selectedRow].verb; + } else { + // Selected action on an inventory object + int selectedObject = _madsVm->scene()->getInterface()->getSelectedObject(); + MadsObject *objEntry = _madsVm->globals()->getObject(selectedObject); + + _currentAction = objEntry->vocabList[_selectedRow].vocabId; + } + + appendVocab(_currentAction, true); + + if (_currentAction == kVerbLook) { + // Add in the word 'add' + strcat(_statusText, atStr); + strcat(_statusText, " "); + } + } + + // Handling for if a hotspot has been selected/highlighted + if ((_hotspotId >= 0) && (_selectedRow >= 0) && (_articleNumber > 0) && (_flags1 == 2)) { + flag = true; + + strcat(_statusText, englishMADSArticleList[_articleNumber]); + strcat(_statusText, " "); + } + + if (_hotspotId >= 0) { + if (_selectedRow < 0) { + int verbId; + + if (_hotspotId < hotspotCount) { + // Get the verb Id from the hotspot + verbId = (*_madsVm->scene()->getSceneResources().hotspots)[_hotspotId].getVerbID(); + } else { + // Get the verb Id from the scene object + verbId = (*_madsVm->scene()->getSceneResources().dynamicHotspots)[_hotspotId - hotspotCount].getVerbID(); + } + + if (verbId > 0) { + // Set the specified action + _currentAction = verbId; + appendVocab(_currentAction, true); + } else { + // Default to a standard 'walk to' + _currentAction = kVerbWalkTo; + strcat(_statusText, walkToStr); + } + } + + if ((_actionMode2 == ACTMODE2_2) || (_actionMode2 == ACTMODE2_5)) { + // Get name from given inventory object + int objectId = _madsVm->scene()->getInterface()->getInventoryObject(_hotspotId); + _action.objectNameId = _madsVm->globals()->getObject(objectId)->descId; + } else if (_hotspotId < hotspotCount) { + // Get name from scene hotspot + _action.objectNameId = (*_madsVm->scene()->getSceneResources().hotspots)[_hotspotId].getVocabID(); + } else { + // Get name from temporary scene hotspot + _action.objectNameId = (*_madsVm->scene()->getSceneResources().dynamicHotspots)[_hotspotId].getVocabID(); + } + appendVocab(_action.objectNameId); + } + } + + if ((_hotspotId >= 0) && (_articleNumber > 0) && !flag) { + if (_articleNumber == -1) { + if (_v86F3A >= 0) { + int articleNum = 0; + + if ((_v86F42 == 2) || (_v86F42 == 5)) { + int objectId = _madsVm->scene()->getInterface()->getInventoryObject(_hotspotId); + articleNum = _madsVm->globals()->getObject(objectId)->article; + } else if (_v86F3A < hotspotCount) { + articleNum = (*_madsVm->scene()->getSceneResources().hotspots)[_hotspotId].getArticle(); + } else { + + } + } + + } else if ((_articleNumber == kVerbLook) || (_vm->getGameType() != GType_RexNebular) || + (strcmp(_madsVm->globals()->getVocab(_action.indirectObjectId), fenceStr) != 0)) { + // Write out the article + strcat(_statusText, englishMADSArticleList[_articleNumber]); + } else { + // Special case for a 'fence' entry in Rex Nebular + strcat(_statusText, overStr); + } + + strcat(_statusText, " "); + } + + // Append object description if necessary + if (_v86F3A >= 0) + appendVocab(_action.indirectObjectId); + + // Remove any trailing space character + int statusLen = strlen(_statusText); + if ((statusLen > 0) && (_statusText[statusLen - 1] == ' ')) + _statusText[statusLen - 1] = '\0'; + } + + _textChanged = true; +} + +void MadsAction::refresh() { + // Exit immediately if nothing has changed + if (!_textChanged) + return; + + // Remove any old copy of the status text + if (_statusTextIndex >= 0) { + _owner._textDisplay.expire(_statusTextIndex); + _statusTextIndex = -1; + } + + if (strlen(_statusText) != 0) { + if ((_owner._screenObjects._v832EC == 0) || (_owner._screenObjects._v832EC == 2)) { + Font *font = _madsVm->_font->getFont(FONT_MAIN_MADS); + int textSpacing = -1; + + int strWidth = font->getWidth(_statusText); + if (strWidth > 320) { + // Too large to fit, so fall back on interface font + font = _madsVm->_font->getFont(FONT_INTERFACE_MADS); + strWidth = font->getWidth(_statusText, 0); + textSpacing = 0; + } + + // Add a new text display entry to display the status text at the bottom of the screen area + uint colours = (_vm->getGameType() == GType_DragonSphere) ? 0x0300 : 0x0003; + + _statusTextIndex = _owner._textDisplay.add(160 - (strWidth / 2), + MADS_SURFACE_HEIGHT + _owner._posAdjust.y - 13, colours, textSpacing, _statusText, font); + } + } + + _textChanged = false; +} + +void MadsAction::startAction() { + _madsVm->_player.moveComplete(); + + _inProgress = true; + _v8453A = 0; + _savedFields.selectedRow = _selectedRow; + _savedFields.articleNumber = _articleNumber; + _savedFields.actionMode = _actionMode; + _savedFields.actionMode2 = _actionMode2; + _savedFields.lookFlag = _lookFlag; + int savedHotspotId = _hotspotId; + int savedV86F3A = _v86F3A; + int savedV86F42 = _v86F42; + + // Copy the action to be active + _activeAction = _action; + strcpy(_dialogTitle, _statusText); + + if ((_savedFields.actionMode2 == ACTMODE2_4) && (savedV86F42 == 0)) + _v8453A = true; + + _startWalkFlag = false; + int hotspotId = -1; + HotSpotList &dynHotspots = *_madsVm->scene()->getSceneResources().dynamicHotspots; + HotSpotList &hotspots = *_madsVm->scene()->getSceneResources().hotspots; + + if (!_savedFields.lookFlag && (_madsVm->scene()->_screenObjects._v832EC != 1)) { + if (_savedFields.actionMode2 == ACTMODE2_4) + hotspotId = savedHotspotId; + else if (savedV86F42 == 4) + hotspotId = savedV86F3A; + + if (hotspotId >= hotspots.size()) { + HotSpot &hs = dynHotspots[hotspotId - hotspots.size()]; + if ((hs.getFeetX() == -1) || (hs.getFeetX() == -3)) { + if (_v86F4A && ((hs.getFeetX() == -3) || (_savedFields.selectedRow < 0))) { + _startWalkFlag = true; + _madsVm->scene()->_destPos = _madsVm->scene()->_customDest; + } + } else if ((hs.getFeetX() >= 0) && ((_savedFields.actionMode == ACTMODE_NONE) || (hs.getCursor() < 2))) { + _startWalkFlag = true; + _madsVm->scene()->_destPos.x = hs.getFeetX(); + _madsVm->scene()->_destPos.y = hs.getFeetY(); + } + _madsVm->scene()->_destFacing = hs.getFacing(); + hotspotId = -1; + } + } + + if (hotspotId >= 0) { + HotSpot &hs = hotspots[hotspotId]; + if ((hs.getFeetX() == -1) || (hs.getFeetX() == -3)) { + if (_v86F4A && ((hs.getFeetX() == -3) || (_savedFields.selectedRow < 0))) { + _startWalkFlag = true; + _madsVm->scene()->_destPos = _madsVm->scene()->_customDest; + } + } else if ((hs.getFeetX() >= 0) && ((_savedFields.actionMode == ACTMODE_NONE) || (hs.getCursor() < 2))) { + _startWalkFlag = true; + _madsVm->scene()->_destPos.x = hs.getFeetX(); + _madsVm->scene()->_destPos.y = hs.getFeetY(); + } + _madsVm->scene()->_destFacing = hs.getFacing(); + } + + _walkFlag = _startWalkFlag; +} + +void MadsAction::checkAction() { + if (isAction(kVerbLookAt) || isAction(kVerbThrow)) + _startWalkFlag = 0; +} + +bool MadsAction::isAction(int verbId, int objectNameId, int indirectObjectId) { + if (_activeAction.verbId != verbId) + return false; + if ((objectNameId != 0) && (_activeAction.objectNameId != objectNameId)) + return false; + if ((indirectObjectId != 0) && (_activeAction.indirectObjectId != indirectObjectId)) + return false; + return true; +} + +//-------------------------------------------------------------------------- + bool MadsSpriteSlot::operator==(const SpriteSlotSubset &other) const { return (spriteListIndex == other.spriteListIndex) && (frameNumber == other.frameNumber) && (xp == other.xp) && (yp == other.yp) && (depth == other.depth) && (scale == other.scale); @@ -190,8 +491,8 @@ void MadsSpriteSlots::drawForeground(M4Surface *viewport) { // Get a list of sprite object depths for active objects for (int i = 0; i < startIndex; ++i) { - if (_entries[i].spriteType >= 0) { - DepthEntry rec(_entries[i].depth, i); + if (_entries[i].spriteType >= SPRITE_ZERO) { + DepthEntry rec(16 - _entries[i].depth, i); depthList.push_back(rec); } } @@ -618,6 +919,18 @@ void MadsKernelMessageList::processText(int msgIndex) { //-------------------------------------------------------------------------- +ScreenObjects::ScreenObjects(MadsView &owner): _owner(owner) { + _v832EC = 0; + _v7FECA = 0; + _v7FED6 = 0; + _v8332A = 0; + _yp = 0; + _v8333C = 0; + _selectedObject = 0; + _category = 0; + _objectIndex = 0; +} + /** * Clears the entries list */ @@ -670,6 +983,29 @@ void ScreenObjects::setActive(int category, int idx, bool active) { } } +void ScreenObjects::check(bool scanFlag, bool mouseClick) { + if (!mouseClick || _v832EC) + _v7FECA = 0; + + if (!_v7FED6 && !_v8332A && !_yp && (_v8333C != 0)) { + if (scanFlag) { + _category = CAT_NONE; + _selectedObject = scanBackwards(_madsVm->_mouse->currentPos().x, _madsVm->_mouse->currentPos().y, + LAYER_GUI); + + if (_selectedObject > 0) { + ScreenObjectEntry &obj = _entries[_selectedObject]; + _category = obj.category & 7; + _objectIndex = obj.index; + } + + // TODO: Other stuff related to the user interface + } + } + + _owner._action.refresh(); +} + /*--------------------------------------------------------------------------*/ MadsDynamicHotspots::MadsDynamicHotspots(MadsView &owner): _owner(owner) { @@ -678,7 +1014,7 @@ MadsDynamicHotspots::MadsDynamicHotspots(MadsView &owner): _owner(owner) { rec.active = false; _entries.push_back(rec); } - _flag = true; + _changed = true; _count = 0; } @@ -702,7 +1038,7 @@ int MadsDynamicHotspots::add(int descId, int field14, int seqIndex, const Common _entries[idx].field_17 = 0; ++_count; - _flag = true; + _changed = true; if (seqIndex >= 0) _owner._sequenceList[seqIndex].dynamicHotspotIndex = idx; @@ -734,7 +1070,7 @@ void MadsDynamicHotspots::remove(int index) { _entries[index].active = false; --_count; - _flag = true; + _changed = true; } } @@ -743,7 +1079,7 @@ void MadsDynamicHotspots::reset() { _entries[i].active = false; _count = 0; - _flag = false; + _changed = false; } /*--------------------------------------------------------------------------*/ @@ -929,7 +1265,7 @@ bool MadsSequenceList::addSubEntry(int index, SequenceSubEntryMode mode, int fra return false; } -int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int triggerCountdown, int delayTicks, int extraTicks, int numTicks, +int MadsSequenceList::add(int spriteListIndex, bool flipped, int frameIndex, int triggerCountdown, int delayTicks, int extraTicks, int numTicks, int msgX, int msgY, bool nonFixed, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites, int frameStart) { @@ -950,7 +1286,7 @@ int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int trigg // Set the list entry fields _entries[seqIndex].active = true; _entries[seqIndex].spriteListIndex = spriteListIndex; - _entries[seqIndex].field_2 = v0; + _entries[seqIndex].flipped = flipped; _entries[seqIndex].frameIndex = frameIndex; _entries[seqIndex].frameStart = frameStart; _entries[seqIndex].numSprites = numSprites; @@ -996,7 +1332,7 @@ void MadsSequenceList::setSpriteSlot(int seqIndex, MadsSpriteSlot &spriteSlot) { spriteSlot.spriteType = spriteSet.isBackground() ? BACKGROUND_SPRITE : FOREGROUND_SPRITE; spriteSlot.seqIndex = seqIndex; spriteSlot.spriteListIndex = timerEntry.spriteListIndex; - spriteSlot.frameNumber = ((timerEntry.field_2 == 1) ? 0x8000 : 0) | timerEntry.frameIndex; + spriteSlot.frameNumber = (timerEntry.flipped ? 0x8000 : 0) | timerEntry.frameIndex; spriteSlot.depth = timerEntry.depth; spriteSlot.scale = timerEntry.scale; @@ -1048,7 +1384,7 @@ bool MadsSequenceList::loadSprites(int seqIndex) { dynHotspot.bounds.top = MAX(y2 - height, 0); dynHotspot.bounds.bottom = MIN(y2, 155) - dynHotspot.bounds.top; - _owner._dynamicHotspots._flag = true; + _owner._dynamicHotspots._changed = true; } } @@ -1197,6 +1533,13 @@ void MadsSequenceList::scan() { } } +/** + * Sets the depth of the specified entry in the sequence list + */ +void MadsSequenceList::setDepth(int seqIndex, int depth) { + _entries[seqIndex].depth = depth; +} + //-------------------------------------------------------------------------- Animation::Animation(MadsM4Engine *vm): _vm(vm) { @@ -1208,7 +1551,8 @@ Animation::~Animation() { //-------------------------------------------------------------------------- MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceList(*this), - _kernelMessages(*this), _spriteSlots(*this), _dirtyAreas(*this), _textDisplay(*this) { + _kernelMessages(*this), _spriteSlots(*this), _dirtyAreas(*this), _textDisplay(*this), + _screenObjects(*this), _action(*this) { _textSpacing = -1; _newTimeout = 0; diff --git a/engines/m4/mads_views.h b/engines/m4/mads_views.h index 0604ae1ee1..c93d0beda3 100644 --- a/engines/m4/mads_views.h +++ b/engines/m4/mads_views.h @@ -36,6 +36,69 @@ namespace M4 { class MadsView; +enum MadsActionMode {ACTMODE_NONE = 0, ACTMODE_VERB = 1, ACTMODE_OBJECT = 3, ACTMODE_TALK = 6}; +enum MAdsActionMode2 {ACTMODE2_0 = 0, ACTMODE2_2 = 2, ACTMODE2_4 = 4, ACTMODE2_5 = 5}; + +struct ActionDetails { + int verbId; + int objectNameId; + int indirectObjectId; +}; + +struct MadsActionSavedFields { + int articleNumber; + int actionMode; + int actionMode2; + bool lookFlag; + int selectedRow; +}; + +class MadsAction { +private: + MadsView &_owner; + char _statusText[100]; + char _dialogTitle[100]; + + void appendVocab(int vocabId, bool capitalise = false); +public: + ActionDetails _action, _activeAction; + int _currentAction; + int8 _flags1, _flags2; + MadsActionMode _actionMode; + MAdsActionMode2 _actionMode2; + int _articleNumber; + bool _lookFlag; + int _selectedRow; + bool _textChanged; + int _selectedAction; + bool _startWalkFlag; + int _statusTextIndex; + int _hotspotId; + MadsActionSavedFields _savedFields; + bool _walkFlag; + + // Unknown fields + int16 _v86F3A; + int16 _v86F42; + int16 _v86F4E; + bool _v86F4A; + int16 _v86F4C; + int _v83338; + bool _inProgress; + bool _v8453A; + +public: + MadsAction(MadsView &owner); + + void clear(); + void set(); + const char *statusText() const { return _statusText; } + void refresh(); + void startAction(); + void checkAction(); + bool isAction(int verbId, int objectNameId = 0, int indirectObjectId = 0); +}; + enum AbortTimerMode {ABORTMODE_0 = 0, ABORTMODE_1 = 1, ABORTMODE_2 = 2}; class SpriteSlotSubset { @@ -211,10 +274,20 @@ public: class ScreenObjects { private: + MadsView &_owner; Common::Array<ScreenObjectEntry> _entries; public: - ScreenObjects() {} - + int _v832EC; + int _v7FECA; + int _v7FED6; + int _v8332A; + int _yp; + int _v8333C; + int _selectedObject; + int _category; + int _objectIndex; + + ScreenObjects(MadsView &owner); ScreenObjectEntry &operator[](uint idx) { assert(idx <= _entries.size()); return _entries[idx - 1]; @@ -226,6 +299,7 @@ public: int scan(int xp, int yp, int layer); int scanBackwards(int xp, int yp, int layer); void setActive(int category, int idx, bool active); + void check(bool scanFlag, bool mouseClick); }; class DynamicHotspot { @@ -251,7 +325,7 @@ private: Common::Array<DynamicHotspot> _entries; int _count; public: - bool _flag; + bool _changed; public: MadsDynamicHotspots(MadsView &owner); @@ -261,6 +335,9 @@ public: int set17(int index, int v); void remove(int index); void reset(); + void refresh() { + // TODO + } }; class MadsDirtyArea { @@ -314,8 +391,7 @@ struct MadsSequenceSubEntries { struct MadsSequenceEntry { int8 active; int8 spriteListIndex; - - int field_2; + bool flipped; int frameIndex; int frameStart; @@ -355,9 +431,9 @@ public: MadsSequenceEntry &operator[](int index) { return _entries[index]; } void clear(); bool addSubEntry(int index, SequenceSubEntryMode mode, int frameIndex, int abortVal); - int add(int spriteListIndex, int v0, int v1, int triggerCountdown, int delayTicks, int extraTicks, int numTicks, - int msgX, int msgY, bool nonFixed, char scale, uint8 depth, int frameInc, SpriteAnimType animType, - int numSprites, int frameStart); + int add(int spriteListIndex, bool flipped, int frameIndex, int triggerCountdown, int delayTicks, + int extraTicks, int numTicks, int msgX, int msgY, bool nonFixed, char scale, uint8 depth, + int frameInc, SpriteAnimType animType, int numSprites, int frameStart); void remove(int seqIndex); void setSpriteSlot(int seqIndex, MadsSpriteSlot &spriteSlot); bool loadSprites(int seqIndex); @@ -365,6 +441,7 @@ public: void delay(uint32 v1, uint32 v2); void setAnimRange(int seqIndex, int startVal, int endVal); void scan(); + void setDepth(int seqIndex, int depth); }; class Animation { @@ -393,6 +470,7 @@ public: MadsDynamicHotspots _dynamicHotspots; MadsSequenceList _sequenceList; MadsDirtyAreas _dirtyAreas; + MadsAction _action; int _textSpacing; uint32 _newTimeout; diff --git a/engines/m4/rails.cpp b/engines/m4/rails.cpp index fbad6995eb..11b9bcdbb9 100644 --- a/engines/m4/rails.cpp +++ b/engines/m4/rails.cpp @@ -179,7 +179,7 @@ long SqrtF16(long n) { uint32 r = 0, s; uint32 v = (uint32)n; - for (int i = 15; i <= 0; i--) { + for (int i = 15; i >= 0; --i) { s = r + (1L << i * 2); r >>= 1; if (s <= v) { diff --git a/engines/m4/rails.h b/engines/m4/rails.h index a7add5a8eb..e3183c243f 100644 --- a/engines/m4/rails.h +++ b/engines/m4/rails.h @@ -93,6 +93,8 @@ private: bool isLineWalkable(int x0, int y0, int x1, int y1); }; +long SqrtF16(long n); + } // End of namespace M4 #endif diff --git a/engines/m4/scene.cpp b/engines/m4/scene.cpp index e78d7f865e..8457f2087a 100644 --- a/engines/m4/scene.cpp +++ b/engines/m4/scene.cpp @@ -44,7 +44,7 @@ Scene::Scene(MadsM4Engine *vm, SceneResources *res): View(vm, Common::Rect(0, 0, _screenType = VIEWID_SCENE; _sceneResources->hotspots = new HotSpotList(); - _sceneResources->props = new HotSpotList(); + _sceneResources->dynamicHotspots = new HotSpotList(); _backgroundSurface = new M4Surface(); _walkSurface = new M4Surface(); _palData = NULL; @@ -61,6 +61,7 @@ Scene::~Scene() { void Scene::loadScene(int sceneNumber) { _previousScene = _currentScene; _currentScene = sceneNumber; + _nextScene = sceneNumber; } void Scene::leaveScene() { @@ -123,14 +124,14 @@ void Scene::showHotSpots() { HotSpot *currentHotSpot; // hotspots (green) - for (i = 0; i < _sceneResources->hotspotCount; i++) { + for (i = 0; i < _sceneResources->hotspots->size(); i++) { currentHotSpot = _sceneResources->hotspots->get(i); _backgroundSurface->frameRect(currentHotSpot->getRect(), _vm->_palette->GREEN); } - // props (red) - for (i = 0; i < _sceneResources->propsCount; i++) { - currentHotSpot = _sceneResources->props->get(i); + // Dynamic hotspots (red) + for (i = 0; i < _sceneResources->dynamicHotspots->size(); i++) { + currentHotSpot = _sceneResources->dynamicHotspots->get(i); _backgroundSurface->frameRect(currentHotSpot->getRect(), _vm->_palette->RED); } } @@ -154,8 +155,21 @@ void Scene::showCodes() { colors[255 * 4 + 2] = 255; _vm->_palette->setPalette(colors, 0, 256); } else { - // For MADS, simply copy the walk data to the background, in whatever current palette is active + // MADS handling + + // copy the walk data to the background, in whatever current palette is active _walkSurface->copyTo(_backgroundSurface); + + // Show all the scene's walk nodes + SceneNodeList &nodeList = _madsVm->scene()->getSceneResources()._nodes; + _backgroundSurface->setColour(_madsVm->_palette->WHITE); + for (uint i = 0; i < nodeList.size() - 2; ++i) { + // Draw a little cross at the node's position + _backgroundSurface->hLine(nodeList[i].pt.x - 2, nodeList[i].pt.x + 2, nodeList[i].pt.y); + _backgroundSurface->vLine(nodeList[i].pt.x, nodeList[i].pt.y - 2, nodeList[i].pt.y + 2); + } + + ((MadsScene *)this)->_spriteSlots.fullRefresh(); } } @@ -184,7 +198,7 @@ bool Scene::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &cap rightClick(x, y); break; case MEVENT_MOVE: - checkHotspotAtMousePos(x, y); + mouseMove(x, y); break; default: return false; diff --git a/engines/m4/scene.h b/engines/m4/scene.h index 633a34b549..9262a7c828 100644 --- a/engines/m4/scene.h +++ b/engines/m4/scene.h @@ -53,17 +53,16 @@ enum MADSVerbs { kVerbPull = 10, kVerbClose = 11, kVerbThrow = 12, - kVerbWalkTo = 13 + kVerbWalkTo = 13, + kVerbLookAt = 209 }; class SceneResources { public: char artBase[MAX_CHK_FILENAME_SIZE]; char pictureBase[MAX_CHK_FILENAME_SIZE]; - int32 hotspotCount; HotSpotList *hotspots; - int32 propsCount; - HotSpotList *props; + HotSpotList *dynamicHotspots; int32 frontY, backY; int32 frontScale, backScale; int16 depthTable[16]; @@ -80,6 +79,7 @@ private: protected: int _currentScene; int _previousScene; + int _nextScene; GameInterfaceView *_interfaceSurface; M4Surface *_backgroundSurface; M4Surface *_walkSurface; @@ -95,7 +95,7 @@ public: virtual void leaveScene(); virtual void loadSceneCodes(int sceneNumber, int index = 0) = 0; virtual void show(); - virtual void checkHotspotAtMousePos(int x, int y) = 0; + virtual void mouseMove(int x, int y) = 0; virtual void leftClick(int x, int y) = 0; virtual void rightClick(int x, int y) = 0; virtual void update() = 0; diff --git a/engines/made/database.cpp b/engines/made/database.cpp index ae1a08e1b5..51308cb7e5 100644 --- a/engines/made/database.cpp +++ b/engines/made/database.cpp @@ -515,6 +515,8 @@ int16 GameDatabaseV2::loadgame(const char *filename, int16 version) { _objects[i]->load(*in); } delete in; + + _objectPropertyCache.clear(); // make sure to clear cache return result; } @@ -644,6 +646,8 @@ void GameDatabaseV3::load(Common::SeekableReadStream &sourceS) { void GameDatabaseV3::reloadFromStream(Common::SeekableReadStream &sourceS) { sourceS.seek(_gameStateOffs); sourceS.read(_gameState, _gameStateSize); + + _objectPropertyCache.clear(); // make sure to clear cache } bool GameDatabaseV3::getSavegameDescription(const char *filename, Common::String &description, int16 version) { @@ -734,6 +738,9 @@ int16 GameDatabaseV3::loadgame(const char *filename, int16 version) { in->skip(64); // skip savegame description in->read(_gameState, _gameStateSize); delete in; + + _objectPropertyCache.clear(); // make sure to clear cache + return 0; } diff --git a/engines/made/detection.cpp b/engines/made/detection.cpp index dd2becd3b8..6a6a70cb30 100644 --- a/engines/made/detection.cpp +++ b/engines/made/detection.cpp @@ -76,7 +76,6 @@ using Common::GUIO_NONE; using Common::GUIO_NOSPEECH; static const MadeGameDescription gameDescriptions[] = { - { // NOTE: Return to Zork entries with *.dat are used to detect the game via rtzcd.dat, // which is packed inside rtzcd.red. Entries with *.red refer to the packed file @@ -329,6 +328,60 @@ static const MadeGameDescription gameDescriptions[] = { }, { + // Return to Zork - Japanese DOS + // This is the RTZCD.DAT in the base directory of the FM-Towns CD + { + "rtz", + "", + AD_ENTRY1("rtzcd.dat", "c4fccf67ad247f09b94c3c808b138576"), + Common::JA_JPN, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO_NONE + }, + GID_RTZ, + 0, + GF_CD, + 3, + }, + + { + // Return to Zork - Japanese FM-Towns + // This is in the RTZFM folder of the FM-Towns CD + { + "rtz", + "", + AD_ENTRY1("rtzcd.dat", "e949a6a42d82daabfa7d4dc0a87a9843"), + Common::JA_JPN, + Common::kPlatformFMTowns, + ADGF_NO_FLAGS, + GUIO_NONE + }, + GID_RTZ, + 0, + GF_CD, + 3, + }, + + { + // Return to Zork - Japanese PC-98 + // This is in the RTZ9821 folder of the FM-Towns CD + { + "rtz", + "", + AD_ENTRY1("rtzcd.dat", "0c0117e98530c736a141c2aad6834dc5"), + Common::JA_JPN, + Common::kPlatformPC98, + ADGF_NO_FLAGS, + GUIO_NONE + }, + GID_RTZ, + 0, + GF_CD, + 3, + }, + + { // The Manhole: New and Enhanced { "manhole", diff --git a/engines/made/resource.cpp b/engines/made/resource.cpp index 28d46cf4ec..cdcb49f9f9 100644 --- a/engines/made/resource.cpp +++ b/engines/made/resource.cpp @@ -50,10 +50,9 @@ PictureResource::~PictureResource() { delete _picture; _picture = 0; } - if (_picturePalette) { - delete[] _picturePalette; - _picturePalette = 0; - } + + delete[] _picturePalette; + _picturePalette = 0; } void PictureResource::load(byte *source, int size) { diff --git a/engines/made/scriptfuncs.cpp b/engines/made/scriptfuncs.cpp index b1a8b0ff84..2f069e882e 100644 --- a/engines/made/scriptfuncs.cpp +++ b/engines/made/scriptfuncs.cpp @@ -27,6 +27,7 @@ #include "common/util.h" #include "common/events.h" #include "graphics/cursorman.h" +#include "sound/softsynth/pcspk.h" #include "made/made.h" #include "made/resource.h" @@ -40,6 +41,22 @@ namespace Made { +ScriptFunctions::ScriptFunctions(MadeEngine *vm) : _vm(vm), _soundStarted(false) { + // Initialize the two tone generators + _pcSpeaker1 = new Audio::PCSpeaker(); + _pcSpeaker2 = new Audio::PCSpeaker(); + _vm->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_pcSpeakerHandle1, _pcSpeaker1); + _vm->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_pcSpeakerHandle2, _pcSpeaker2); +} + +ScriptFunctions::~ScriptFunctions() { + for (uint i = 0; i < _externalFuncs.size(); ++i) + delete _externalFuncs[i]; + + _vm->_system->getMixer()->stopHandle(_pcSpeakerHandle1); + _vm->_system->getMixer()->stopHandle(_pcSpeakerHandle2); +} + typedef Common::Functor2Mem<int16, int16*, int16, ScriptFunctions> ExternalScriptFunc; #define External(x) \ _externalFuncs.push_back(new ExternalScriptFunc(this, &ScriptFunctions::x)); \ @@ -307,36 +324,78 @@ int16 ScriptFunctions::sfFlashScreen(int16 argc, int16 *argv) { } int16 ScriptFunctions::sfPlayNote(int16 argc, int16 *argv) { - // TODO: Used in Manhole:NE, Manhole EGA - // This is used when using the piano in the desk screen inside the ship. + // This is used when using the piano in the desk screen inside the ship + // in The Manhole (EGA/NE). + // It takes 2 parameters: - // The first parameter is the key pressed + // The first parameter is the note number of the key pressed + 1 // The second parameter is some sort of modifier (volume, perhaps?), - // depending on which of the 3 keys on the right has been pressed (12 - 14) - warning("Unimplemented opcode: sfPlayNote"); + // depending on which of the 3 keys on the right has been pressed. + // This value seems to be [12, 14] in NE and [1, 3] in EGA. + + // Note frequencies based on http://www.phy.mtu.edu/~suits/notefreqs.html + static const int freqTable[] = { + 16, 17, 18, 19, 21, 22, 23, 24, 26, 28, 29, + 30, 32, 35, 37, 39, 41, 44, 46, 49, 52, 55, + 58, 62, 65, 69, 73, 77, 82, 87, 93, 98, 104, + 110, 117, 123, 131, 139, 147, 156, 165, 175, 195, + 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, + 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, + 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, + 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, + 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, + 3136, 3322, 3529, 3729, 3951, 4186, 4435, 4697, 4978 + }; + + debug(4, "sfPlayNote: Note = %d, Volume(?) = %d", argv[0] - 1, argv[1]); + + _pcSpeaker1->play(Audio::PCSpeaker::kWaveFormSine, freqTable[argv[0] - 1], -1); + + // TODO: Figure out what to do with the second parameter + //_pcSpeaker1->setVolume(argv[1]); + return 0; } int16 ScriptFunctions::sfStopNote(int16 argc, int16 *argv) { - // TODO: Used in Manhole:NE, Manhole EGA // Used in the same place as sfPlayNote, with the same parameters - warning("Unimplemented opcode: sfStopNote"); + // We just stop the wave generator here + _pcSpeaker1->stop(); return 0; } int16 ScriptFunctions::sfPlayTele(int16 argc, int16 *argv) { - // TODO: Used in Manhole:NE, Manhole EGA // This is used when pressing the phone keys while using the phone in - // the desk screen inside the ship. + // the desk screen inside the ship in The Manhole (EGA/NE). // It takes 1 parameter, the key pressed (0-9, 10 for asterisk, 11 for hash) - warning("Unimplemented opcode: sfPlayTele"); + + // A telephone keypad uses a two tones for each key. + // See http://en.wikipedia.org/wiki/Telephone_keypad for more info + + static const int freqTable1[] = { + 1336, 1209, 1336, 1477, + 1209, 1336, 1477, 1209, + 1336, 1477, 1209, 1477 + }; + + static const int freqTable2[] = { + 941, 697, 697, 697, + 770, 770, 770, 852, + 852, 852, 941, 941 + }; + + debug(4, "sfPlayTele: Button = %d", argv[0]); + + _pcSpeaker1->play(Audio::PCSpeaker::kWaveFormSine, freqTable1[argv[0]], -1); + _pcSpeaker2->play(Audio::PCSpeaker::kWaveFormSine, freqTable2[argv[0]], -1); return 0; } int16 ScriptFunctions::sfStopTele(int16 argc, int16 *argv) { - // TODO: Used in Manhole:NE, Manhole EGA // Used in the same place as sfPlayTele, with the same parameters - warning("Unimplemented opcode: sfStopTele"); + // We just stop both wave generators here + _pcSpeaker1->stop(); + _pcSpeaker2->stop(); return 0; } diff --git a/engines/made/scriptfuncs.h b/engines/made/scriptfuncs.h index 5fdfb77f45..3bed27c5c8 100644 --- a/engines/made/scriptfuncs.h +++ b/engines/made/scriptfuncs.h @@ -33,6 +33,10 @@ #include "made/resource.h" +namespace Audio { + class PCSpeaker; +} + namespace Made { class MadeEngine; @@ -41,17 +45,16 @@ typedef Common::Functor2<int16, int16*, int16> ExternalFunc; class ScriptFunctions { public: - ScriptFunctions(MadeEngine *vm) : _vm(vm), _soundStarted(false) {} - virtual ~ScriptFunctions() { - for (uint i = 0; i < _externalFuncs.size(); ++i) - delete _externalFuncs[i]; - } + ScriptFunctions(MadeEngine *vm); + virtual ~ScriptFunctions(); + int16 callFunction(uint16 index, int16 argc, int16 *argv) { if (index >= _externalFuncs.size()) error("ScriptFunctions::callFunction() Invalid function index %d", index); debug(4, "%s", _externalFuncNames[index]); return (*_externalFuncs[index])(argc, argv); } + void setupExternalsTable(); const char* getFuncName(int index) { return _externalFuncNames[index]; } int getCount() const { return _externalFuncs.size(); } @@ -64,6 +67,10 @@ protected: SoundResource* _soundResource; bool _soundStarted; + // PlayNote/StopNote and PlayTele/StopTele wave generators + Audio::SoundHandle _pcSpeakerHandle1, _pcSpeakerHandle2; + Audio::PCSpeaker *_pcSpeaker1, *_pcSpeaker2; + Common::Array<const ExternalFunc*> _externalFuncs; Common::Array<const char *> _externalFuncNames; GenericResource *_musicRes; diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp index 6d3f6f0b04..ca8f358158 100644 --- a/engines/parallaction/input.cpp +++ b/engines/parallaction/input.cpp @@ -148,8 +148,7 @@ void Input::readInput() { setCursorPos(e.mouse); } - if (_vm->_debugger->isAttached()) - _vm->_debugger->onFrame(); + _vm->_debugger->onFrame(); return; diff --git a/engines/queen/queen.cpp b/engines/queen/queen.cpp index 10c3d56cb4..5cda4e3208 100644 --- a/engines/queen/queen.cpp +++ b/engines/queen/queen.cpp @@ -264,9 +264,7 @@ void QueenEngine::writeOptionSettings() { } void QueenEngine::update(bool checkPlayerInput) { - if (_debugger->isAttached()) { - _debugger->onFrame(); - } + _debugger->onFrame(); _graphics->update(_logic->currentRoom()); _logic->update(); diff --git a/engines/queen/resource.cpp b/engines/queen/resource.cpp index a43141ef56..1e2eff8458 100644 --- a/engines/queen/resource.cpp +++ b/engines/queen/resource.cpp @@ -96,18 +96,7 @@ ResourceEntry *Resource::resourceEntry(const char *filename) const { entryName.toUppercase(); ResourceEntry *re = NULL; -#ifndef PALMOS_MODE re = (ResourceEntry *)bsearch(entryName.c_str(), _resourceTable, _resourceEntries, sizeof(ResourceEntry), compareResourceEntry); -#else - // PALMOS FIXME (?) : still doesn't work for me (????) use this instead - uint32 cur = 0; - do { - if (!strcmp(entryName.c_str(), _resourceTable[cur].filename)) { - re = &_resourceTable[cur]; - break; - } - } while (++cur < _resourceEntries); -#endif return re; } diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp index 8b7654d689..e4a16e27da 100644 --- a/engines/saga/music.cpp +++ b/engines/saga/music.cpp @@ -158,6 +158,8 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) { _driver->setGM(_vm->getGameId() != GID_ITE); } else { _parser = MidiParser::createParser_SMF(); + // ITE with standalone MIDI files is General MIDI + _driver->setGM(_vm->getGameId() == GID_ITE); } free(resourceData); } @@ -177,7 +179,6 @@ Music::~Music() { _vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback); _mixer->stopHandle(_musicHandle); _driver->setTimerCallback(NULL, NULL); - _driver->close(); delete _driver; _parser->setMidiDriver(NULL); delete _parser; diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index d1ab3bc9d7..1b7fa97f8d 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -352,8 +352,7 @@ Common::Error SagaEngine::run() { uint32 currentTicks; while (!shouldQuit()) { - if (_console->isAttached()) - _console->onFrame(); + _console->onFrame(); if (_render->getFlags() & RF_RENDERPAUSE) { // Freeze time while paused diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp index 18bbca2425..5fd120ac33 100644 --- a/engines/saga/script.cpp +++ b/engines/saga/script.cpp @@ -571,9 +571,7 @@ void Script::opCall(SCRIPTOP_PARAMS) { if (iparam1 != kAddressModule) { error("Script::runThread iparam1 != kAddressModule"); } - byte *addr = thread->baseAddress(iparam1); iparam1 = scriptS->readSint16LE(); - addr += iparam1; thread->push(argumentsCount); // NOTE: The original pushes the program diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 33aa5514f2..20acbed450 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -93,6 +93,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger(), DCmd_Register("sentence_fragments", WRAP_METHOD(Console, cmdSentenceFragments)); DCmd_Register("parse", WRAP_METHOD(Console, cmdParse)); DCmd_Register("set_parse_nodes", WRAP_METHOD(Console, cmdSetParseNodes)); + DCmd_Register("said", WRAP_METHOD(Console, cmdSaid)); // Resources DCmd_Register("diskdump", WRAP_METHOD(Console, cmdDiskDump)); DCmd_Register("hexdump", WRAP_METHOD(Console, cmdHexDump)); @@ -167,6 +168,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger(), DCmd_Register("disasm_addr", WRAP_METHOD(Console, cmdDisassembleAddress)); DCmd_Register("send", WRAP_METHOD(Console, cmdSend)); DCmd_Register("go", WRAP_METHOD(Console, cmdGo)); + DCmd_Register("logkernel", WRAP_METHOD(Console, cmdLogKernel)); // Breakpoints DCmd_Register("bp_list", WRAP_METHOD(Console, cmdBreakpointList)); DCmd_Register("bplist", WRAP_METHOD(Console, cmdBreakpointList)); // alias @@ -306,6 +308,7 @@ bool Console::cmdHelp(int argc, const char **argv) { DebugPrintf(" sentence_fragments - Shows the sentence fragments (used to build Parse trees)\n"); DebugPrintf(" parse - Parses a sequence of words and prints the resulting parse tree\n"); DebugPrintf(" set_parse_nodes - Sets the contents of all parse nodes\n"); + DebugPrintf(" said - Match a string against a said spec\n"); DebugPrintf("\n"); DebugPrintf("Resources:\n"); DebugPrintf(" diskdump - Dumps the specified resource to disk as a patch file\n"); @@ -372,6 +375,7 @@ bool Console::cmdHelp(int argc, const char **argv) { DebugPrintf(" disasm_addr - Disassembles one or more commands\n"); DebugPrintf(" send - Sends a message to an object\n"); DebugPrintf(" go - Executes the script\n"); + DebugPrintf(" logkernel - Logs kernel calls\n"); DebugPrintf("\n"); DebugPrintf("Breakpoints:\n"); DebugPrintf(" bp_list / bplist / bl - Lists the current breakpoints\n"); @@ -409,6 +413,13 @@ bool Console::cmdGetVersion(int argc, const char **argv) { const char *viewTypeDesc[] = { "Unknown", "EGA", "VGA", "VGA SCI1.1", "Amiga" }; bool hasVocab997 = g_sci->getResMan()->testResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SELECTORS)) ? true : false; + Common::String gameVersion = "N/A"; + + Common::File versionFile; + if (versionFile.open("VERSION")) { + gameVersion = versionFile.readLine(); + versionFile.close(); + } DebugPrintf("Game ID: %s\n", _engine->getGameIdStr()); DebugPrintf("Emulated interpreter version: %s\n", getSciVersionDesc(getSciVersion())); @@ -425,6 +436,7 @@ bool Console::cmdGetVersion(int argc, const char **argv) { DebugPrintf("Resource volume version: %s\n", g_sci->getResMan()->getVolVersionDesc()); DebugPrintf("Resource map version: %s\n", g_sci->getResMan()->getMapVersionDesc()); DebugPrintf("Contains selector vocabulary (vocab.997): %s\n", hasVocab997 ? "yes" : "no"); + DebugPrintf("Game version (VERSION file): %s\n", gameVersion.c_str()); DebugPrintf("\n"); return true; @@ -1099,13 +1111,13 @@ bool Console::cmdRestoreGame(int argc, const char **argv) { return true; } - return false; + return Cmd_Exit(0, 0); } bool Console::cmdRestartGame(int argc, const char **argv) { _engine->_gamestate->abortScriptProcessing = kAbortRestartGame;; - return false; + return Cmd_Exit(0, 0); } bool Console::cmdClassTable(int argc, const char **argv) { @@ -1176,8 +1188,9 @@ bool Console::cmdParse(int argc, const char **argv) { char string[1000]; // Construct the string - strcpy(string, argv[2]); + strcpy(string, argv[1]); for (int i = 2; i < argc; i++) { + strcat(string, " "); strcat(string, argv[i]); } @@ -1209,6 +1222,122 @@ bool Console::cmdParse(int argc, const char **argv) { return true; } +bool Console::cmdSaid(int argc, const char **argv) { + if (argc < 2) { + DebugPrintf("Matches a string against a said spec\n"); + DebugPrintf("Usage: %s <string> > & <said spec>\n", argv[0]); + DebugPrintf("<string> is a sequence of actual words.\n"); + DebugPrintf("<said spec> is a sequence of hex tokens.\n"); + return true; + } + + ResultWordList words; + char *error; + char string[1000]; + byte spec[1000]; + + int p; + // Construct the string + strcpy(string, argv[1]); + for (p = 2; p < argc && strcmp(argv[p],"&") != 0; p++) { + strcat(string, " "); + strcat(string, argv[p]); + } + + if (p >= argc-1) { + DebugPrintf("Matches a string against a said spec\n"); + DebugPrintf("Usage: %s <string> > & <said spec>\n", argv[0]); + DebugPrintf("<string> is a sequence of actual words.\n"); + DebugPrintf("<said spec> is a sequence of hex tokens.\n"); + return true; + } + + // TODO: Maybe turn this into a proper said spec compiler + unsigned int len = 0; + for (p++; p < argc; p++) { + if (strcmp(argv[p], ",") == 0) { + spec[len++] = 0xf0; + } else if (strcmp(argv[p], "&") == 0) { + spec[len++] = 0xf1; + } else if (strcmp(argv[p], "/") == 0) { + spec[len++] = 0xf2; + } else if (strcmp(argv[p], "(") == 0) { + spec[len++] = 0xf3; + } else if (strcmp(argv[p], ")") == 0) { + spec[len++] = 0xf4; + } else if (strcmp(argv[p], "[") == 0) { + spec[len++] = 0xf5; + } else if (strcmp(argv[p], "]") == 0) { + spec[len++] = 0xf6; + } else if (strcmp(argv[p], "#") == 0) { + spec[len++] = 0xf7; + } else if (strcmp(argv[p], "<") == 0) { + spec[len++] = 0xf8; + } else if (strcmp(argv[p], ">") == 0) { + spec[len++] = 0xf9; + } else if (strcmp(argv[p], "[<") == 0) { + spec[len++] = 0xf5; + spec[len++] = 0xf8; + } else if (strcmp(argv[p], "[/") == 0) { + spec[len++] = 0xf5; + spec[len++] = 0xf2; + } else if (strcmp(argv[p], "!*") == 0) { + spec[len++] = 0x0f; + spec[len++] = 0xfe; + } else if (strcmp(argv[p], "[!*]") == 0) { + spec[len++] = 0xf5; + spec[len++] = 0x0f; + spec[len++] = 0xfe; + spec[len++] = 0xf6; + } else { + unsigned int s = strtol(argv[p], 0, 16); + if (s >= 0xf0 && s <= 0xff) { + spec[len++] = s; + } else { + spec[len++] = s >> 8; + spec[len++] = s & 0xFF; + } + } + } + spec[len++] = 0xFF; + + printf("Matching '%s' against:", string); + _engine->getVocabulary()->debugDecipherSaidBlock(spec); + printf("\n"); + + bool res = _engine->getVocabulary()->tokenizeString(words, string, &error); + if (res && !words.empty()) { + int syntax_fail = 0; + + _engine->getVocabulary()->synonymizeTokens(words); + + DebugPrintf("Parsed to the following blocks:\n"); + + for (ResultWordList::const_iterator i = words.begin(); i != words.end(); ++i) + DebugPrintf(" Type[%04x] Group[%04x]\n", i->_class, i->_group); + + if (_engine->getVocabulary()->parseGNF(words, true)) + syntax_fail = 1; // Building a tree failed + + if (syntax_fail) + DebugPrintf("Building a tree failed.\n"); + else { + _engine->getVocabulary()->dumpParseTree(); + _engine->getVocabulary()->parserIsValid = true; + + int ret = said(_engine->_gamestate, (byte*)spec, true); + DebugPrintf("kSaid: %s\n", (ret == SAID_NO_MATCH ? "No match" : "Match")); + } + + } else { + DebugPrintf("Unknown word: '%s'\n", error); + free(error); + } + + return true; +} + + bool Console::cmdParserNodes(int argc, const char **argv) { if (argc != 2) { DebugPrintf("Shows the specified number of nodes from the parse node tree\n"); @@ -1341,7 +1470,7 @@ bool Console::cmdPlayVideo(int argc, const char **argv) { if (filename.hasSuffix(".seq") || filename.hasSuffix(".avi") || filename.hasSuffix(".vmd")) { _videoFile = filename; _videoFrameDelay = (argc == 2) ? 10 : atoi(argv[2]); - return false; + return Cmd_Exit(0, 0); } else { DebugPrintf("Unknown video file type\n"); return true; @@ -1631,7 +1760,7 @@ bool Console::cmdShowMap(int argc, const char **argv) { DebugPrintf("Map %d is not available.\n", map); return true; } - return false; + return Cmd_Exit(0, 0); } bool Console::cmdSongLib(int argc, const char **argv) { @@ -1676,8 +1805,7 @@ bool Console::cmdStartSound(int argc, const char **argv) { } g_sci->_soundCmd->startNewSound(number); - - return false; + return Cmd_Exit(0, 0); } bool Console::cmdToggleSound(int argc, const char **argv) { @@ -2310,7 +2438,7 @@ bool Console::cmdTrace(int argc, const char **argv) { _debugState.runningStep = atoi(argv[1]) - 1; _debugState.debugging = true; - return false; + return Cmd_Exit(0, 0); } bool Console::cmdStepOver(int argc, const char **argv) { @@ -2318,14 +2446,14 @@ bool Console::cmdStepOver(int argc, const char **argv) { _debugState.seekLevel = _engine->_gamestate->_executionStack.size(); _debugState.debugging = true; - return false; + return Cmd_Exit(0, 0); } bool Console::cmdStepEvent(int argc, const char **argv) { _debugState.stopOnEvent = true; _debugState.debugging = true; - return false; + return Cmd_Exit(0, 0); } bool Console::cmdStepRet(int argc, const char **argv) { @@ -2333,7 +2461,7 @@ bool Console::cmdStepRet(int argc, const char **argv) { _debugState.seekLevel = _engine->_gamestate->_executionStack.size() - 1; _debugState.debugging = true; - return false; + return Cmd_Exit(0, 0); } bool Console::cmdStepGlobal(int argc, const char **argv) { @@ -2347,7 +2475,7 @@ bool Console::cmdStepGlobal(int argc, const char **argv) { _debugState.seekSpecial = atoi(argv[1]); _debugState.debugging = true; - return false; + return Cmd_Exit(0, 0); } bool Console::cmdStepCallk(int argc, const char **argv) { @@ -2380,7 +2508,7 @@ bool Console::cmdStepCallk(int argc, const char **argv) { } _debugState.debugging = true; - return false; + return Cmd_Exit(0, 0); } bool Console::cmdDisassemble(int argc, const char **argv) { @@ -2545,7 +2673,7 @@ bool Console::cmdSend(int argc, const char **argv) { // We call run_engine explictly so we can restore the value of r_acc // after execution. - run_vm(_engine->_gamestate, 0); + run_vm(_engine->_gamestate); } @@ -2565,6 +2693,31 @@ bool Console::cmdGo(int argc, const char **argv) { return Cmd_Exit(argc, argv); } +bool Console::cmdLogKernel(int argc, const char **argv) { + if (argc < 3) { + DebugPrintf("Logs calls to specified kernel function.\n"); + DebugPrintf("Usage: %s <kernel-function/*> <on/off>\n", argv[0]); + DebugPrintf("Example: %s StrCpy on\n", argv[0]); + return true; + } + + bool logging; + if (strcmp(argv[2], "on") == 0) + logging = true; + else if (strcmp(argv[2], "off") == 0) + logging = false; + else { + DebugPrintf("2nd parameter must be either on or off\n"); + return true; + } + + if (g_sci->getKernel()->debugSetFunctionLogging(argv[1], logging)) + DebugPrintf("Logging %s for k%s\n", logging ? "enabled" : "disabled", argv[1]); + else + DebugPrintf("Unknown kernel function %s\n", argv[1]); + return true; +} + bool Console::cmdBreakpointList(int argc, const char **argv) { int i = 0; int bpdata; @@ -2884,7 +3037,7 @@ bool Console::cmdQuit(int argc, const char **argv) { exit(0); } - return false; + return Cmd_Exit(0, 0); } bool Console::cmdAddresses(int argc, const char **argv) { @@ -3323,11 +3476,11 @@ void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startO } printf(" |"); for (i = 0; i < regsPerLine; i++) { - c = data[i].toUint16() >> 8; + c = data[i].toUint16() & 0xff; if (c < 32 || c >= 127) c = '.'; printf("%c", c); - c = data[i].toUint16() & 0xff; + c = data[i].toUint16() >> 8; if (c < 32 || c >= 127) c = '.'; printf("%c", c); @@ -3350,11 +3503,11 @@ void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startO } printf(" |"); for (i = 0; i < len; i++) { - c = data[i].toUint16() >> 8; + c = data[i].toUint16() & 0xff; if (c < 32 || c >= 127) c = '.'; printf("%c", c); - c = data[i].toUint16() & 0xff; + c = data[i].toUint16() >> 8; if (c < 32 || c >= 127) c = '.'; printf("%c", c); diff --git a/engines/sci/console.h b/engines/sci/console.h index 51f02d7168..234272bbab 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -65,6 +65,7 @@ private: bool cmdSentenceFragments(int argc, const char **argv); bool cmdParse(int argc, const char **argv); bool cmdSetParseNodes(int argc, const char **argv); + bool cmdSaid(int argc, const char **argv); // Resources bool cmdDiskDump(int argc, const char **argv); bool cmdHexDump(int argc, const char **argv); @@ -129,6 +130,7 @@ private: bool cmdDisassembleAddress(int argc, const char **argv); bool cmdSend(int argc, const char **argv); bool cmdGo(int argc, const char **argv); + bool cmdLogKernel(int argc, const char **argv); // Breakpoints bool cmdBreakpointList(int argc, const char **argv); bool cmdBreakpointDelete(int argc, const char **argv); diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index eb2c989e0d..3698964de5 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -56,7 +56,7 @@ static const PlainGameDescriptor s_sciGameTitles[] = { {"lsl2", "Leisure Suit Larry 2: Goes Looking for Love (in Several Wrong Places)"}, {"lsl3", "Leisure Suit Larry 3: Passionate Patti in Pursuit of the Pulsating Pectorals"}, {"pq2", "Police Quest II: The Vengeance"}, - {"qfg1", "Quest for Glory I: So You Want to Be a Hero"}, // EGA is SCI0, VGA SCI1.1 + {"qfg1", "Quest for Glory I: So You Want to Be a Hero"}, {"sq3", "Space Quest III: The Pirates of Pestulon"}, // === SCI01 games ======================================================== {"qfg2", "Quest for Glory II: Trial by Fire"}, @@ -89,11 +89,12 @@ static const PlainGameDescriptor s_sciGameTitles[] = { {"hoyle4", "Hoyle Classic Card Games"}, {"kq6", "King's Quest VI: Heir Today, Gone Tomorrow"}, {"laurabow2", "Laura Bow 2: The Dagger of Amon Ra"}, + {"qfg1vga", "Quest for Glory I: So You Want to Be a Hero"}, {"qfg3", "Quest for Glory III: Wages of War"}, {"sq5", "Space Quest V: The Next Mutation"}, {"islandbrain", "The Island of Dr. Brain"}, {"lsl6", "Leisure Suit Larry 6: Shape Up or Slip Out!"}, - {"mothergoose", "Mixed-Up Mother Goose"}, // floppy is SCI1.1, CD SCI2.1 + {"mothergoose", "Mixed-Up Mother Goose"}, {"pepper", "Pepper's Adventure in Time"}, {"slater", "Slater & Charlie Go Camping"}, // === SCI2 games ========================================================= @@ -106,6 +107,7 @@ static const PlainGameDescriptor s_sciGameTitles[] = { {"kq7", "King's Quest VII: The Princeless Bride"}, // TODO: King's Questions {"lsl6hires", "Leisure Suit Larry 6: Shape Up or Slip Out!"}, + {"mothergoosehires","Mixed-Up Mother Goose"}, {"phantasmagoria", "Phantasmagoria"}, {"pqswat", "Police Quest: SWAT"}, {"shivers", "Shivers"}, @@ -168,6 +170,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = { { "lsl6hires", GID_LSL6HIRES }, { "lsl7", GID_LSL7 }, { "mothergoose", GID_MOTHERGOOSE }, + { "mothergoosehires",GID_MOTHERGOOSEHIRES }, { "msastrochicken", GID_MSASTROCHICKEN }, { "pepper", GID_PEPPER }, { "phantasmagoria", GID_PHANTASMAGORIA }, @@ -178,6 +181,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = { { "pq4", GID_PQ4 }, { "pqswat", GID_PQSWAT }, { "qfg1", GID_QFG1 }, + { "qfg1vga", GID_QFG1VGA }, { "qfg2", GID_QFG2 }, { "qfg3", GID_QFG3 }, { "qfg4", GID_QFG4 }, @@ -332,7 +336,7 @@ Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, R // or qfg4 full (SCI2) // qfg1 VGA doesn't have view 1 if (!resMan->testResource(ResourceId(kResourceTypeView, 1))) - return "qfg1"; + return "qfg1vga"; // qfg4 full is SCI2 if (getSciVersion() == SCI_VERSION_2) @@ -587,7 +591,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl !strcmp(s_fallbackDesc.gameid, "sq1sci")) s_fallbackDesc.extra = "VGA Remake"; - if (!strcmp(s_fallbackDesc.gameid, "qfg1") && getSciVersion() == SCI_VERSION_1_1) + if (!strcmp(s_fallbackDesc.gameid, "qfg1vga") && getSciVersion() == SCI_VERSION_1_1) s_fallbackDesc.extra = "VGA Remake"; // Add "demo" to the description for demos @@ -624,8 +628,15 @@ bool SciMetaEngine::hasFeature(MetaEngineFeature f) const { bool SciEngine::hasFeature(EngineFeature f) const { return //(f == kSupportsRTL) || - (f == kSupportsLoadingDuringRuntime) || - (f == kSupportsSavingDuringRuntime); + (f == kSupportsLoadingDuringRuntime); // || + //(f == kSupportsSavingDuringRuntime); + // We can't allow saving through ScummVM menu, because + // a) lots of games don't like saving everywhere (e.g. castle of dr. brain) + // b) some games even dont allow saving in certain rooms (e.g. lsl6) + // c) somehow some games even get mad when doing this (execstackbase was 1 all of a sudden in lsl3) + // d) for sci0/sci01 games we should at least wait till status bar got drawn, although this may not be enough + // we can't make sure that the scripts are fine with us saving at a specific location, doing so may work sometimes + // and some other times it won't work. } SaveStateList SciMetaEngine::listSaves(const char *target) const { diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index aa1e26e0f4..da3763b236 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -83,6 +83,19 @@ static const struct ADGameDescription SciGameDescriptions[] = { {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH }, + // Castle of Dr. Brain - English DOS Floppy EGA (from omer_mor, bug report #3035349) + {"castlebrain", "EGA", { + {"resource.map", 0, "88d106f945f7fd9d1aeda961cfec38a9", 2646}, + {"resource.000", 0, "6e125f4ce3f4f5c35f2617c7b66c6e21", 25325}, + {"resource.001", 0, "1d806162f6d3cfbe3c0135414efe6f88", 99931}, + {"resource.002", 0, "6a41a0eb5237778427dddf92ae07cf9b", 294772}, + {"resource.003", 0, "0c6ab4efb3be4d991ae9762e19f17c92", 306378}, + {"resource.004", 0, "5e7b90949422de005f80285979972e43", 292423}, + {"resource.005", 0, "8a5ed3ba96e2eaf18e36fedfaab89419", 297838}, + {"resource.006", 0, "dceed92e709cad1bd9582809a235b0a0", 266682}, + {NULL, 0, NULL, 0}}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, + // Castle of Dr. Brain - English DOS Floppy (from jvprat) // Executable scanning reports "1.000.044", Floppy label reports "1.0, 10.30.91", VERSION file reports "1.000" // SCI interpreter version 1.000.510 @@ -960,6 +973,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { // King's Quest 5 EGA (supplied by markcoolio in bug report #2829470) // SCI interpreter version 1.000.060 + // VERSION file reports "0.000.055" {"kq5", "EGA", { {"resource.map", 0, "baf888a4e4797ce0de0b19d4e183583c", 7662}, {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 394242}, @@ -973,6 +987,21 @@ static const struct ADGameDescription SciGameDescriptions[] = { {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, + // King's Quest 5 EGA (supplied by omer_mor in bug report #3035421) + // VERSION file reports "0.000.062" + {"kq5", "EGA", { + {"resource.map", 0, "e17cfb38175382b9188da75c53bbab64", 7656}, + {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 394072}, + {"resource.001", 0, "c1eef048fa9fe76298c2d4705ef9549f", 561444}, + {"resource.002", 0, "076aa0bf1d8d2c147d64aeffbe2928e5", 597580}, + {"resource.003", 0, "ecb47cd04d06b2ab2f9f883667db6e81", 487633}, + {"resource.004", 0, "4d74e8094ff57cea6ee92faf63dbd0af", 620749}, + {"resource.005", 0, "3cca5b2dae8afe94532edfdc98d7edbe", 669961}, + {"resource.006", 0, "698c698570cde9015e4d51eb8d2e9db1", 666541}, + {"resource.007", 0, "703d8df30e89541af337d7706540d5c4", 541762}, + {NULL, 0, NULL, 0}}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, + // King's Quest 5 - German DOS Floppy (supplied by markcoolio in bug report #2727101) // SCI interpreter version 1.000.060 {"kq5", "", { @@ -1551,7 +1580,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, // Larry 5 - German DOS (from Tobis87) - // SCI interpreter version 1.000.510 (just a guess) + // SCI interpreter version T.A00.196 {"lsl5", "", { {"resource.map", 0, "c97297aa76d4dd2ed144c7b7769e2caf", 6867}, {"resource.000", 0, "4c00c14b8181ad47076a51d86097d97e", 759095}, @@ -1706,6 +1735,14 @@ static const struct ADGameDescription SciGameDescriptions[] = { {NULL, 0, NULL, 0}}, Common::FR_FRA, Common::kPlatformPC, 0, GUIO_NONE }, + // Larry 7 - English DOS Demo (provided by richiefs in bug report #2670691) + // SCI interpreter version 2.100.002 + {"lsl7", "Demo", { + {"ressci.000", 0, "5cc6159688b2dc03790a67c90ccc67f9", 10195878}, + {"resmap.000", 0, "6a2b2811eef82e87cde91cf1de845af8", 2695}, + {NULL, 0, NULL, 0}}, + Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH }, + #ifdef ENABLE_SCI3_GAMES // Larry 7 - English DOS CD (from spookypeanut) // SCI interpreter version 3.000.000 @@ -1747,14 +1784,6 @@ static const struct ADGameDescription SciGameDescriptions[] = { {NULL, 0, NULL, 0}}, Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH }, - // Larry 7 - English DOS Demo (provided by richiefs in bug report #2670691) - // SCI interpreter version 2.100.002 - {"lsl7", "Demo", { - {"ressci.000", 0, "5cc6159688b2dc03790a67c90ccc67f9", 10195878}, - {"resmap.000", 0, "6a2b2811eef82e87cde91cf1de845af8", 2695}, - {NULL, 0, NULL, 0}}, - Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH }, - // Lighthouse - English Windows Demo (from jvprat) // Executable scanning reports "2.100.002", VERSION file reports "1.00" {"lighthouse", "Demo", { @@ -1802,6 +1831,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH }, + // Mixed-Up Fairy Tales - English DOS Floppy EGA (from omer_mor, bug report #3035350) + {"fairytales", "EGA", { + {"resource.map", 0, "daa94e9f327be6657eb97a51b490dbb1", 3219}, + {"resource.000", 0, "6dc287611e510793b72e73110bbdd45d", 17819}, + {"resource.001", 0, "5ad26e7af4d4c3a3185c66a44abd5220", 478401}, + {"resource.002", 0, "4db83250f821607b634c99d663cae74a", 663713}, + {"resource.003", 0, "509b2467ba779100d5933ed51a9ae32f", 560255}, + {"resource.004", 0, "93afc85d5ffa60ea555d6cc336d22c03", 651109}, + {NULL, 0, NULL, 0}}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, + // Mixed-Up Fairy Tales v1.000 - English DOS (supplied by markcoolio in bug report #2723791) // Executable scanning reports "1.000.145" {"fairytales", "", { @@ -1835,6 +1875,14 @@ static const struct ADGameDescription SciGameDescriptions[] = { {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH }, + // Mixed-Up Mother Goose - English DOS Floppy EGA (from omer_mor, bug report #3035354) + {"mothergoose", "EGA", { + {"resource.map", 0, "3490f85dab47e504c41b7eb3312e285e", 2598}, + {"resource.001", 0, "d893892d62b3f061357291d66775e360", 239906}, + {"resource.002", 0, "d893892d62b3f061357291d66775e360", 719398}, + {NULL, 0, NULL, 0}}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, + // Mixed-Up Mother Goose v2.000 - English DOS Floppy (supplied by markcoolio in bug report #2723795) // Executable scanning reports "1.001.031" {"mothergoose", "", { @@ -1858,17 +1906,25 @@ static const struct ADGameDescription SciGameDescriptions[] = { {"resource.map", 0, "87f9dc1cafc4d4fa835fb2f00cf3a6ef", 4560}, {"resource.001", 0, "5a0ed1d745855148364de1b3be099bac", 2070072}, {NULL, 0, NULL, 0}}, - Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH + Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO_NOSPEECH }, #ifdef ENABLE_SCI32 // Mixed-Up Mother Goose Deluxe - English Windows/DOS CD (supplied by markcoolio in bug report #2723810) // Executable scanning reports "2.100.002" - {"mothergoose", "", { + {"mothergoosehires", "", { {"resource.map", 0, "5159a1578c4306bfe070a3e4d8c2e1d3", 4741}, {"resource.000", 0, "1926925c95d82f0999590e93b02887c5", 15150768}, {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE }, + + // Mixed-Up Mother Goose Deluxe - Multilingual Windows CD (English/French/German/Spanish) + // Executable scanning reports "2.100.002" + {"mothergoosehires", "", { + {"resmap.000", 0, "ef611af561898dcfea87846919ebf3eb", 4969}, + {"ressci.000", 0, "227685bc59d90821978d330713e44a7a", 17205800}, + {NULL, 0, NULL, 0}}, + Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NONE }, #endif // ENABLE_SCI32 // Ms. Astro Chicken - English DOS @@ -2020,6 +2076,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { {NULL, 0, NULL, 0}}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH }, + // Police Quest 2 - Japanese PC-98 + // SCI interpreter version unknown + {"pq2", "", { + {"resource.map", 0, "883804c616dca1d82373bf9fda3a71d2", 4656}, + {"resource.001", 0, "05fdee43a228dd6ea4d1a92ccae3f788", 669319}, + {"resource.002", 0, "05fdee43a228dd6ea4d1a92ccae3f788", 637662}, + {"resource.003", 0, "05fdee43a228dd6ea4d1a92ccae3f788", 684395}, + {NULL, 0, NULL, 0}}, + Common::JA_JPN, Common::kPlatformPC98, 0, GUIO_NOSPEECH + }, // also includes english language + // Police Quest 3 - English Amiga // Executable scanning reports "1.004.024" // SCI interpreter version 1.000.784 @@ -2252,7 +2319,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { // Quest for Glory 1 VGA Remake - English DOS // Executable scanning reports "2.000.411" - {"qfg1", "VGA Remake", { + {"qfg1vga", "VGA Remake", { {"resource.map", 0, "a731fb6c9c0b282443f7027bc8694d4c", 8469}, {"resource.000", 0, "ecace1a2771846b1a8aa1afdd44111a0", 6570147}, {NULL, 0, NULL, 0}}, @@ -2261,7 +2328,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { // Quest for Glory 1 VGA Remake - English DOS Non-Interactive Demo (from FRG) // SCI interpreter version 1.001.029 - {"qfg1", "VGA Remake, Demo", { + {"qfg1vga", "VGA Remake, Demo", { {"resource.map", 0, "ac0257051c95a59c0cdc0be24d9b11fa", 729}, {"resource.000", 0, "ec6f5cf369054dd3e5392995e9975b9e", 768218}, {NULL, 0, NULL, 0}}, @@ -2270,7 +2337,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { // Quest for Glory 1 VGA Remake - English Macintosh Floppy // VERSION file reports "2.0" - {"qfg1", "VGA Remake", { + {"qfg1vga", "VGA Remake", { {"Data1", 0, "14f26bc75f24bb1ecc94532df17b5371", 1768155}, {"Data2", 0, "a7aee8bd46fc9cef7fd3bea93ef173e0", 6586422}, {NULL, 0, NULL, 0}}, @@ -3078,26 +3145,37 @@ static const struct ADGameDescription SciGameDescriptions[] = { #endif // ENABLE_SCI32 // SCI Fanmade Games - FANMADE("Al Pond 2: Island Quest", "9625372e710d1a95d2027b48f9e325af", 1506, "a0f9aa65b9bf3d8703adff5a621f243c", 889843), FANMADE("Al Pond: Island Quest 2", "4cba6a5a4c8f66f21935ed78b0511a92", 870, "876587dc9a5ec569287a3dc4b29139d8", 613769), + FANMADE("Al Pond 2: Island Quest", "9625372e710d1a95d2027b48f9e325af", 1506, "a0f9aa65b9bf3d8703adff5a621f243c", 889843), + FANMADE("Al Pond 2: Island Quest (Updated)", "64be277cdcc6aafce7d9f26e88ad31a8", 1500, "571547228a212d63315f0c114cf48d54", 885241), FANMADE("Another DG Game: I Want My C64 Back", "4a8ca7ca2abd18899ef856f47665e2e9", 588, "12ff558d20c72e42cc6adb408f34d6d8", 150513), FANMADE_L("Another DG Game: I Want My C64 Back", "13dc1d9ebc57daf8895412eee5e39fea", 576, "e2ad60b3a280171429db5c85f158f84a", 141697, Common::FR_FRA), + FANMADE("Aquarius: An Aquatic Experience", "2e23bc3b82f22a454be202ea593fb478", 480, "01555c8de683d25405bda270aa1ff014", 272372), FANMADE("Bluntman and Chronic (Politically Correct Version)", "c3ef9fa6c7c5fb840078bf28d87c7f8b", 1362, "441636a9f6f86710844868fded868ee7", 596688), FANMADE("Cascade Quest", "c94efc10d18c040b6e22a1dc6d3adfe1", 3468, "8ada33dfa945f81531e5508240b573de", 1432195), - FANMADE("Curt Quest 1.0", "b0e555370380d218968a40a68eaaaffc", 1146, "c851182cdf6fc6a81b840f4d4875f1a0", 307165), - FANMADE("Curt Quest 1.1", "54084c29346683296e45ef32d7ae74f3", 1128, "c851182cdf6fc6a81b840f4d4875f1a0", 302000), + FANMADE("Circus Quest", "35871f6b4e1df56af4113c0203a0b223", 630, "7d6f97d7935d8733f488d4cb74315e5b", 279627), + FANMADE("Curt's Quest 1.0", "b0e555370380d218968a40a68eaaaffc", 1146, "c851182cdf6fc6a81b840f4d4875f1a0", 307165), + FANMADE("Curt's Quest 1.1", "54084c29346683296e45ef32d7ae74f3", 1128, "c851182cdf6fc6a81b840f4d4875f1a0", 302000), FANMADE("Demo Quest", "c89a0c9e0a4e4af0ecedb300a3b31dbf", 384, "a32f3495ba24764cba091119cc3f1e13", 160098), FANMADE("Dr. Jummybummy's Space Adventure 2", "6ae6cb7de423f51736d9487b4ca0c6da", 810, "26e5b563f578e104d79689f36568b7cf", 394670), FANMADE_L("Grostesteing: Plus Mechant que Jamais", "ec9a97ccb134f69249f6ea8b16c13d8e", 1500, "b869f5f11bfe2ab5f67f4f0c618f2ce1", 464657, Common::FR_FRA), // FIXME: Accent - FANMADE("Jim Quest", "0af50be1d3f0cb77a09137709a76ef4f", 960, "9c042c136548b20d9183495668e03526", 496446), + FANMADE("Humanoid Demo", "97d8331293a6d57e8bad58c1efc89a63", 624, "fb354b9abe64011b12159e45d724633f", 452320), + FANMADE("Jim’s Quest 1: The Phantom Thesis", "0af50be1d3f0cb77a09137709a76ef4f", 960, "9c042c136548b20d9183495668e03526", 496446), FANMADE("Knight's Quest Demo 1.0", "5e816edf993956752ed06fccfeeae6d9", 1260, "959321f88a22905fa1f8c6d897874744", 703836), FANMADE("LockerGnome Quest", "3eeff9130206cad0c4e1551e2b9dd2c5", 420, "ae05ca90806fd90cc43f147c82d3547c", 158906), - FANMADE("New Year's Mystery", "efd1beb5120293725065c95959144f81", 714, "b3bd3c2372ed6efa28adb12403c4c31a", 305027), + FANMADE("New Year's Mystery", "e4dcab1b1d3cb4a2c070a07a9c9589e0", 708, "e00ca5e44fd4e98d8174b467b31b0f21", 295425), + FANMADE("New Year's Mystery (Updated)", "efd1beb5120293725065c95959144f81", 714, "b3bd3c2372ed6efa28adb12403c4c31a", 305027), + FANMADE("Ocean Battle", "c2304a0568e0eb84f8e9a0915f01170a", 408, "46c520c1ac9b63528854d0f58c7e1b74", 142234), FANMADE("Osama", "db8f1710453cfbecf4214b2946970043", 390, "7afd75d4620dedf97a84ae8f0a7523cf", 123827), FANMADE("Quest for the Cheat", "a359d4cf27f98264b42b55c017671214", 882, "8a943029f73c4bc85d454b7f473455ba", 455209), + FANMADE("SCI Capture the Flag", "4cd679a51d93b8b27c6b38d81be24b8b", 432, "98ae1f6ed7b4c21f88addbf643dd1d2f", 147878), FANMADE("SCI Companion Template", "ad54d4f504086cd597aa2348d0aa3b09", 354, "6798b7b601ce8154c1d1bc0f0edcdd18", 113061), + FANMADE("SCI Programming April 2010 Competition Template", "36e5c4011dd7c92e1ae4c6fede7d698d", 456, "20c87fbb7f73e2a3eb2c5dfab4d76b5a", 142221), FANMADE("SCI Studio Template 3.0", "ca0dc8d586e0a8670b7621cde090b532", 354, "58a48ee692a86c0575e6bd0b00a92b9a", 113097), FANMADE("SCI Quest", "9067e1f1e54436d2dbfce855524bc84a", 552, "ffa7d355cd9223f245289108a696bcd2", 149634), + FANMADE("SCI-Man", "3ab85bd39a86c11f85781764f9db09bb", 468, "bb8f9992f504a242bf0860e3588e150b", 131810), + FANMADE("The Farm Nightmare", "fb6cbfddaa7c055e2c3d8cf4c683a7db", 906, "50655e8b8925f717e698e08f006f40be", 338303), + FANMADE("The Gem Scenario", "ef5f61f4d2c6d31122d3e2baf89ad976", 642, "2f16be390dd90c3d7ca1c8a594ac0bfa", 244794), FANMADE("The Legend of the Lost Jewel", "ba1bca315e3818c5626eda51bcfbcccf", 636, "9b0736d69924af0cff32a0f78db96855", 300398), // FIXME: The vga demo does not have a resource.000/001 file. diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index fee6e69da7..315c86c56c 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -38,6 +38,7 @@ GameFeatures::GameFeatures(SegManager *segMan, Kernel *kernel) : _segMan(segMan) _doSoundType = SCI_VERSION_NONE; _lofsType = SCI_VERSION_NONE; _gfxFunctionsType = SCI_VERSION_NONE; + _messageFunctionType = SCI_VERSION_NONE; _moveCountType = kMoveCountUninitialized; #ifdef ENABLE_SCI32 @@ -140,6 +141,10 @@ SciVersion GameFeatures::detectDoSoundType() { // This game is using early SCI0 sound code (different headers than // SCI0 late) _doSoundType = SCI_VERSION_0_EARLY; +#ifdef ENABLE_SCI32 + } else if (getSciVersion() >= SCI_VERSION_2_1) { + _doSoundType = SCI_VERSION_2_1; +#endif } else if (SELECTOR(nodePtr) == -1) { // No nodePtr selector, so this game is definitely using newer // SCI0 sound code (i.e. SCI_VERSION_0_LATE) @@ -403,6 +408,41 @@ SciVersion GameFeatures::detectGfxFunctionsType() { return _gfxFunctionsType; } +SciVersion GameFeatures::detectMessageFunctionType() { + if (_messageFunctionType != SCI_VERSION_NONE) + return _messageFunctionType; + + if (getSciVersion() > SCI_VERSION_1_1) { + _messageFunctionType = SCI_VERSION_1_1; + return _messageFunctionType; + } else if (getSciVersion() < SCI_VERSION_1_1) { + _messageFunctionType = SCI_VERSION_1_LATE; + return _messageFunctionType; + } + + Common::List<ResourceId> *resources = g_sci->getResMan()->listResources(kResourceTypeMessage, -1); + + if (resources->empty()) { + // No messages found, so this doesn't really matter anyway... + _messageFunctionType = SCI_VERSION_1_1; + return _messageFunctionType; + } + + Resource *res = g_sci->getResMan()->findResource(*resources->begin(), false); + assert(res); + + // Only v2 Message resources use the kGetMessage kernel function. + // v3-v5 use the kMessage kernel function. + + if (READ_SCI11ENDIAN_UINT32(res->data) / 1000 == 2) + _messageFunctionType = SCI_VERSION_1_LATE; + else + _messageFunctionType = SCI_VERSION_1_1; + + debugC(1, kDebugLevelVM, "Detected message function type: %s", getSciVersionDesc(_messageFunctionType)); + return _messageFunctionType; +} + #ifdef ENABLE_SCI32 bool GameFeatures::autoDetectSci21KernelType() { // First, check if the Sound object is loaded diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h index 5b383746d8..167c207437 100644 --- a/engines/sci/engine/features.h +++ b/engines/sci/engine/features.h @@ -66,6 +66,12 @@ public: * @return Graphics functions type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE */ SciVersion detectGfxFunctionsType(); + + /** + * Autodetects the message function used + * @return Message function type, SCI_VERSION_1_LATE / SCI_VERSION_1_1 + */ + SciVersion detectMessageFunctionType(); #ifdef ENABLE_SCI32 /** @@ -105,7 +111,7 @@ private: bool autoDetectSci21KernelType(); #endif - SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType; + SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType, _messageFunctionType; #ifdef ENABLE_SCI32 SciVersion _sci21KernelType; #endif diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index beb1d3ce35..d76199c794 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -27,619 +27,15 @@ #include "sci/engine/kernel.h" #include "sci/event.h" #include "sci/resource.h" +#include "sci/engine/features.h" +#include "sci/engine/kernel_tables.h" #include "sci/engine/state.h" +#include "sci/engine/workarounds.h" #include "common/system.h" namespace Sci { -// Uncompiled kernel signatures are formed from a string of letters. -// each corresponding to a type of a parameter (see below). -// Use small letters to indicate end of sum type. -// Use capital letters for sum types, e.g. -// "LNoLr" for a function which takes two arguments: -// (1) list, node or object -// (2) list or ref -#define KSIG_SPEC_LIST 'l' -#define KSIG_SPEC_NODE 'n' -#define KSIG_SPEC_OBJECT 'o' -#define KSIG_SPEC_REF 'r' // Said Specs and strings -#define KSIG_SPEC_ARITHMETIC 'i' -#define KSIG_SPEC_NULL 'z' -#define KSIG_SPEC_ANY '.' -#define KSIG_SPEC_ELLIPSIS '*' // Arbitrarily more TYPED arguments - -#define KSIG_SPEC_SUM_DONE ('a' - 'A') - - - -/** Default kernel name table. */ -static const char *s_defaultKernelNames[] = { - /*0x00*/ "Load", - /*0x01*/ "UnLoad", - /*0x02*/ "ScriptID", - /*0x03*/ "DisposeScript", - /*0x04*/ "Clone", - /*0x05*/ "DisposeClone", - /*0x06*/ "IsObject", - /*0x07*/ "RespondsTo", - /*0x08*/ "DrawPic", - /*0x09*/ "Dummy", // Show - /*0x0a*/ "PicNotValid", - /*0x0b*/ "Animate", - /*0x0c*/ "SetNowSeen", - /*0x0d*/ "NumLoops", - /*0x0e*/ "NumCels", - /*0x0f*/ "CelWide", - /*0x10*/ "CelHigh", - /*0x11*/ "DrawCel", - /*0x12*/ "AddToPic", - /*0x13*/ "NewWindow", - /*0x14*/ "GetPort", - /*0x15*/ "SetPort", - /*0x16*/ "DisposeWindow", - /*0x17*/ "DrawControl", - /*0x18*/ "HiliteControl", - /*0x19*/ "EditControl", - /*0x1a*/ "TextSize", - /*0x1b*/ "Display", - /*0x1c*/ "GetEvent", - /*0x1d*/ "GlobalToLocal", - /*0x1e*/ "LocalToGlobal", - /*0x1f*/ "MapKeyToDir", - /*0x20*/ "DrawMenuBar", - /*0x21*/ "MenuSelect", - /*0x22*/ "AddMenu", - /*0x23*/ "DrawStatus", - /*0x24*/ "Parse", - /*0x25*/ "Said", - /*0x26*/ "SetSynonyms", // Portrait (KQ6 hires) - /*0x27*/ "HaveMouse", - /*0x28*/ "SetCursor", - // FOpen (SCI0) - // FPuts (SCI0) - // FGets (SCI0) - // FClose (SCI0) - /*0x29*/ "SaveGame", - /*0x2a*/ "RestoreGame", - /*0x2b*/ "RestartGame", - /*0x2c*/ "GameIsRestarting", - /*0x2d*/ "DoSound", - /*0x2e*/ "NewList", - /*0x2f*/ "DisposeList", - /*0x30*/ "NewNode", - /*0x31*/ "FirstNode", - /*0x32*/ "LastNode", - /*0x33*/ "EmptyList", - /*0x34*/ "NextNode", - /*0x35*/ "PrevNode", - /*0x36*/ "NodeValue", - /*0x37*/ "AddAfter", - /*0x38*/ "AddToFront", - /*0x39*/ "AddToEnd", - /*0x3a*/ "FindKey", - /*0x3b*/ "DeleteKey", - /*0x3c*/ "Random", - /*0x3d*/ "Abs", - /*0x3e*/ "Sqrt", - /*0x3f*/ "GetAngle", - /*0x40*/ "GetDistance", - /*0x41*/ "Wait", - /*0x42*/ "GetTime", - /*0x43*/ "StrEnd", - /*0x44*/ "StrCat", - /*0x45*/ "StrCmp", - /*0x46*/ "StrLen", - /*0x47*/ "StrCpy", - /*0x48*/ "Format", - /*0x49*/ "GetFarText", - /*0x4a*/ "ReadNumber", - /*0x4b*/ "BaseSetter", - /*0x4c*/ "DirLoop", - /*0x4d*/ "CanBeHere", // CantBeHere in newer SCI versions - /*0x4e*/ "OnControl", - /*0x4f*/ "InitBresen", - /*0x50*/ "DoBresen", - /*0x51*/ "Platform", // DoAvoider (SCI0) - /*0x52*/ "SetJump", - /*0x53*/ "SetDebug", - /*0x54*/ "Dummy", // InspectObj - /*0x55*/ "Dummy", // ShowSends - /*0x56*/ "Dummy", // ShowObjs - /*0x57*/ "Dummy", // ShowFree - /*0x58*/ "MemoryInfo", - /*0x59*/ "Dummy", // StackUsage - /*0x5a*/ "Dummy", // Profiler - /*0x5b*/ "GetMenu", - /*0x5c*/ "SetMenu", - /*0x5d*/ "GetSaveFiles", - /*0x5e*/ "GetCWD", - /*0x5f*/ "CheckFreeSpace", - /*0x60*/ "ValidPath", - /*0x61*/ "CoordPri", - /*0x62*/ "StrAt", - /*0x63*/ "DeviceInfo", - /*0x64*/ "GetSaveDir", - /*0x65*/ "CheckSaveGame", - /*0x66*/ "ShakeScreen", - /*0x67*/ "FlushResources", - /*0x68*/ "SinMult", - /*0x69*/ "CosMult", - /*0x6a*/ "SinDiv", - /*0x6b*/ "CosDiv", - /*0x6c*/ "Graph", - /*0x6d*/ "Joystick", - // End of kernel function table for SCI0 - /*0x6e*/ "Dummy", // ShiftScreen - /*0x6f*/ "Palette", - /*0x70*/ "MemorySegment", - /*0x71*/ "Intersections", // MoveCursor (SCI1 late), PalVary (SCI1.1) - /*0x72*/ "Memory", - /*0x73*/ "Dummy", // ListOps - /*0x74*/ "FileIO", - /*0x75*/ "DoAudio", - /*0x76*/ "DoSync", - /*0x77*/ "AvoidPath", - /*0x78*/ "Sort", // StrSplit (SCI01) - /*0x79*/ "Dummy", // ATan - /*0x7a*/ "Lock", - /*0x7b*/ "StrSplit", - /*0x7c*/ "GetMessage", // Message (SCI1.1) - /*0x7d*/ "IsItSkip", - /*0x7e*/ "MergePoly", - /*0x7f*/ "ResCheck", - /*0x80*/ "AssertPalette", - /*0x81*/ "TextColors", - /*0x82*/ "TextFonts", - /*0x83*/ "Dummy", // Record - /*0x84*/ "Dummy", // PlayBack - /*0x85*/ "ShowMovie", - /*0x86*/ "SetVideoMode", - /*0x87*/ "SetQuitStr", - /*0x88*/ "Dummy" // DbugStr -}; - -reg_t kStub(EngineState *s, int argc, reg_t *argv) { - Kernel *kernel = g_sci->getKernel(); - int kernelCallNr = -1; - - Common::List<ExecStack>::iterator callIterator = s->_executionStack.end(); - if (callIterator != s->_executionStack.begin()) { - callIterator--; - ExecStack lastCall = *callIterator; - kernelCallNr = lastCall.debugSelector; - } - - Common::String warningMsg = "Dummy function k" + kernel->getKernelName(kernelCallNr) + - Common::String::printf("[%x]", kernelCallNr) + - " invoked. Params: " + - Common::String::printf("%d", argc) + " ("; - - for (int i = 0; i < argc; i++) { - warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i])); - warningMsg += (i == argc - 1 ? ")" : ", "); - } - - warning("%s", warningMsg.c_str()); - return s->r_acc; -} - -reg_t kStubNull(EngineState *s, int argc, reg_t *argv) { - kStub(s, argc, argv); - return NULL_REG; -} - -reg_t kDummy(EngineState *s, int argc, reg_t *argv) { - kStub(s, argc, argv); - error("Kernel function was called, which was considered to be unused - see log for details"); -} - -// [io] -> either integer or object -// (io) -> optionally integer AND an object -// (i) -> optional integer -// . -> any type -// i* -> optional multiple integers -// .* -> any parameters afterwards (or none) - -// gameID, scriptNr,lvl, object-name, method-name, call, index, replace -static const SciWorkaroundEntry kAbs_workarounds[] = { - { GID_HOYLE1, 1, 0, "room1", "doit", -1, 0, { 2, 0x3e9 } }, // crazy eights - called with objects instead of integers - { GID_HOYLE1, 2, 0, "room2", "doit", -1, 0, { 2, 0x3e9 } }, // old maid - called with objects instead of integers - { GID_HOYLE1, 3, 0, "room3", "doit", -1, 0, { 2, 0x3e9 } }, // hearts - called with objects instead of integers - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, scriptNr,lvl, object-name, method-name, call, index, replace -static const SciWorkaroundEntry kDisposeScript_workarounds[] = { - { GID_QFG1, 64, 0, "rm64", "dispose", -1, 0, { 1, 0 } }, // when leaving graveyard, parameter 0 is an object - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, scriptNr,lvl, object-name, method-name, call, index, replace -static const SciWorkaroundEntry kDoSoundFade_workarounds[] = { - { GID_KQ6, 989, 0, "globalSound", "fade", -1, 0, { 0, 0 } }, // during intro, parameter 4 is an object - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, scriptNr,lvl, object-name, method-name, call, index, replace -static const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = { - { GID_LSL6, 85, 0, "rScroller", "hide", -1, 0, { 0, 0 } }, // happens when restoring (sometimes), same as the one below - { GID_LSL6, 85, 0, "lScroller", "hide", -1, 0, { 0, 0 } }, // happens when restoring (sometimes), same as the one below - { GID_LSL6, 86, 0, "LL6Inv", "show", -1, 0, { 0, 0 } }, // happens when restoring, is called with hunk segment, but hunk is not allocated at that time - // ^^ TODO: check, if this is really a script error or an issue with our restore code - { GID_LSL6, 86, 0, "LL6Inv", "hide", -1, 0, { 0, 0 } }, // happens during the game, gets called with 1 extra parameter - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, scriptNr,lvl, object-name, method-name, call, index, replace -static const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[] = { - { GID_LSL6, 0, 0, "LSL6", "hideControls", -1, 0, { 0, 0 } }, // happens when giving the bungee key to merrily - gets called with additional 5th parameter - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, scriptNr,lvl, object-name, method-name, call, index, replace -static const SciWorkaroundEntry kGraphFillBoxAny_workarounds[] = { - { GID_SQ4, 818, 0, "iconTextSwitch", "show", -1, 0, { 0, 0 } }, // game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -// gameID, scriptNr,lvl, object-name, method-name, call, index, replace -static const SciWorkaroundEntry kUnLoad_workarounds[] = { - { GID_LSL6, 130, 0, "recruitLarryScr", "changeState", -1, 0, { 1, 0 } }, // during intro, a 3rd parameter is passed by accident - { GID_LSL6, 740, 0, "showCartoon", "changeState", -1, 0, { 1, 0 } }, // during ending, 4 additional parameters are passed by accident - { GID_SQ1, 303, 0, "slotGuy", "dispose", -1, 0, { 1, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error - SCI_WORKAROUNDENTRY_TERMINATOR -}; - -struct SciKernelMapSubEntry { - SciVersion fromVersion; - SciVersion toVersion; - - uint16 id; - - const char *name; - KernelFunctionCall *function; - - const char *signature; - const SciWorkaroundEntry *workarounds; -}; - -#define SCI_SUBOPENTRY_TERMINATOR { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, NULL, NULL, NULL, NULL } - - -#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE -#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01 -#define SIG_SCI1 SCI_VERSION_1_EGA, SCI_VERSION_1_LATE -#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1 -#define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1 -#define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE - -// SCI-Sound-Version -#define SIG_SOUNDSCI0 SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE -#define SIG_SOUNDSCI1EARLY SCI_VERSION_1_EARLY, SCI_VERSION_1_EARLY -#define SIG_SOUNDSCI1LATE SCI_VERSION_1_LATE, SCI_VERSION_1_LATE - -#define SIGFOR_ALL 0x3f -#define SIGFOR_DOS 1 << 0 -#define SIGFOR_PC98 1 << 1 -#define SIGFOR_WIN 1 << 2 -#define SIGFOR_MAC 1 << 3 -#define SIGFOR_AMIGA 1 << 4 -#define SIGFOR_ATARI 1 << 5 -#define SIGFOR_PC SIGFOR_DOS|SIGFOR_WIN - -#define SIG_EVERYWHERE SIG_SCIALL, SIGFOR_ALL - -#define MAP_CALL(_name_) #_name_, k##_name_ - -// version, subId, function-mapping, signature, workarounds -static const SciKernelMapSubEntry kDoSound_subops[] = { - { SIG_SOUNDSCI0, 0, MAP_CALL(DoSoundInit), "o", NULL }, - { SIG_SOUNDSCI0, 1, MAP_CALL(DoSoundPlay), "o", NULL }, - { SIG_SOUNDSCI0, 2, MAP_CALL(DoSoundDummy), "o", NULL }, - { SIG_SOUNDSCI0, 3, MAP_CALL(DoSoundDispose), "o", NULL }, - { SIG_SOUNDSCI0, 4, MAP_CALL(DoSoundMute), "(i)", NULL }, - { SIG_SOUNDSCI0, 5, MAP_CALL(DoSoundStop), "o", NULL }, - { SIG_SOUNDSCI0, 6, MAP_CALL(DoSoundPause), "i", NULL }, - { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResume), "", NULL }, - { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL }, - { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL }, - { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", NULL }, - { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL }, - { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL }, - { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL }, - { SIG_SOUNDSCI1EARLY, 1, MAP_CALL(DoSoundMute), NULL, NULL }, - { SIG_SOUNDSCI1EARLY, 2, MAP_CALL(DoSoundDummy), NULL, NULL }, - { SIG_SOUNDSCI1EARLY, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL }, - { SIG_SOUNDSCI1EARLY, 4, MAP_CALL(DoSoundUpdate), NULL, NULL }, - { SIG_SOUNDSCI1EARLY, 5, MAP_CALL(DoSoundInit), NULL, NULL }, - { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL }, - { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL }, - { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL }, - { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL }, - { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", NULL }, - { SIG_SOUNDSCI1EARLY, 11, MAP_CALL(DoSoundUpdateCues), "o", NULL }, - { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiiii", NULL }, - { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundReverb), "oi", NULL }, - { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL }, - { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(DoSoundDummy), "", NULL }, - // ^^ Longbow demo - { SIG_SOUNDSCI1LATE, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 1, MAP_CALL(DoSoundMute), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 2, MAP_CALL(DoSoundDummy), "", NULL }, - { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL }, - { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL }, - { SIG_SOUNDSCI1LATE, 6, MAP_CALL(DoSoundInit), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 7, MAP_CALL(DoSoundDispose), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 8, MAP_CALL(DoSoundPlay), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 9, MAP_CALL(DoSoundStop), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 10, MAP_CALL(DoSoundPause), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 11, MAP_CALL(DoSoundFade), "oiiii(i)", kDoSoundFade_workarounds }, - { SIG_SOUNDSCI1LATE, 12, MAP_CALL(DoSoundSetHold), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 13, MAP_CALL(DoSoundDummy), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL }, - { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL }, - { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL }, - { SIG_SOUNDSCI1LATE, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 18, MAP_CALL(DoSoundSendMidi), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundReverb), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 20, MAP_CALL(DoSoundUpdate), NULL, NULL }, - SCI_SUBOPENTRY_TERMINATOR -}; - -// version, subId, function-mapping, signature, workarounds -static const SciKernelMapSubEntry kGraph_subops[] = { - { SIG_SCI32, 1, MAP_CALL(StubNull), "", NULL }, // called by gk1 sci32 right at the start - { SIG_SCIALL, 2, MAP_CALL(GraphGetColorCount), "", NULL }, - // 3 - set palette via resource - { SIG_SCIALL, 4, MAP_CALL(GraphDrawLine), "iiiii(i)(i)", NULL }, - // 5 - nop - // 6 - draw pattern - { SIG_SCIALL, 7, MAP_CALL(GraphSaveBox), "iiiii", NULL }, - { SIG_SCIALL, 8, MAP_CALL(GraphRestoreBox), "[r0!]", kGraphRestoreBox_workarounds }, - // ^ this may get called with invalid references, we check them within restoreBits() and sierra sci behaves the same - { SIG_SCIALL, 9, MAP_CALL(GraphFillBoxBackground), "iiii", NULL }, - { SIG_SCIALL, 10, MAP_CALL(GraphFillBoxForeground), "iiii", kGraphFillBoxForeground_workarounds }, - { SIG_SCIALL, 11, MAP_CALL(GraphFillBoxAny), "iiiiii(i)(i)", kGraphFillBoxAny_workarounds }, - { SIG_SCI11, 12, MAP_CALL(GraphUpdateBox), "iiii(i)(r0)", NULL }, // kq6 hires - { SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", NULL }, - { SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", NULL }, - { SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL }, - { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires - SCI_SUBOPENTRY_TERMINATOR -}; - -// version, subId, function-mapping, signature, workarounds -static const SciKernelMapSubEntry kPalVary_subops[] = { - { SIG_SCIALL, 0, MAP_CALL(PalVaryInit), "ii(i)(i)", NULL }, - { SIG_SCIALL, 1, MAP_CALL(PalVaryReverse), "(i)(i)(i)", NULL }, - { SIG_SCIALL, 2, MAP_CALL(PalVaryGetCurrentStep), "", NULL }, - { SIG_SCIALL, 3, MAP_CALL(PalVaryDeinit), "", NULL }, - { SIG_SCIALL, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL }, - { SIG_SCIALL, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL }, - { SIG_SCIALL, 6, MAP_CALL(PalVaryPauseResume), "i", NULL }, - { SIG_SCI32, 8, MAP_CALL(PalVaryUnknown), "", NULL }, - SCI_SUBOPENTRY_TERMINATOR -}; - -// version, subId, function-mapping, signature, workarounds -static const SciKernelMapSubEntry kPalette_subops[] = { - { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL }, - { SIG_SCIALL, 2, MAP_CALL(PaletteSetFlag), "iii", NULL }, - { SIG_SCIALL, 3, MAP_CALL(PaletteUnsetFlag), "iii", NULL }, - { SIG_SCIALL, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL }, - { SIG_SCIALL, 5, MAP_CALL(PaletteFindColor), "iii", NULL }, - { SIG_SCIALL, 6, MAP_CALL(PaletteAnimate), "i*", NULL }, - { SIG_SCIALL, 7, MAP_CALL(PaletteSave), "", NULL }, - { SIG_SCIALL, 8, MAP_CALL(PaletteRestore), "i", NULL }, - SCI_SUBOPENTRY_TERMINATOR -}; - -struct SciKernelMapEntry { - const char *name; - KernelFunctionCall *function; - - SciVersion fromVersion; - SciVersion toVersion; - byte forPlatform; - - const char *signature; - const SciKernelMapSubEntry *subFunctions; - const SciWorkaroundEntry *workarounds; -}; - -// name, version/platform, signature, sub-signatures, workarounds -static SciKernelMapEntry s_kernelMap[] = { - { MAP_CALL(Abs), SIG_EVERYWHERE, "i", NULL, kAbs_workarounds }, - { MAP_CALL(AddAfter), SIG_EVERYWHERE, "lnn", NULL, NULL }, - { MAP_CALL(AddMenu), SIG_EVERYWHERE, "rr", NULL, NULL }, - { MAP_CALL(AddToEnd), SIG_EVERYWHERE, "ln", NULL, NULL }, - { MAP_CALL(AddToFront), SIG_EVERYWHERE, "ln", NULL, NULL }, - { MAP_CALL(AddToPic), SIG_EVERYWHERE, "[il](iiiiii)", NULL, NULL }, - { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL }, - { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL }, - { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL }, - { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL }, - { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, NULL }, - { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, NULL }, - { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL }, - { MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL }, - { MAP_CALL(CheckSaveGame), SIG_EVERYWHERE, ".*", NULL, NULL }, - { MAP_CALL(Clone), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(CoordPri), SIG_EVERYWHERE, "i(i)", NULL, NULL }, - { MAP_CALL(CosDiv), SIG_EVERYWHERE, "ii", NULL, NULL }, - { MAP_CALL(DeleteKey), SIG_EVERYWHERE, "l.", NULL, NULL }, - { MAP_CALL(DeviceInfo), SIG_EVERYWHERE, "i(r)(r)(i)", NULL, NULL }, // subop - { MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, NULL }, - // ^ we allow invalid references here, because kDisplay gets called with those in e.g. pq3 during intro - // restoreBits() checks and skips invalid handles, so that's fine. Sierra SCI behaved the same - { MAP_CALL(DirLoop), SIG_EVERYWHERE, "oi", NULL, NULL }, - { MAP_CALL(DisposeClone), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(DisposeList), SIG_EVERYWHERE, "l", NULL, NULL }, - { MAP_CALL(DisposeScript), SIG_EVERYWHERE, "i(i*)", NULL, kDisposeScript_workarounds }, - { MAP_CALL(DisposeWindow), SIG_EVERYWHERE, "i(i)", NULL, NULL }, - { MAP_CALL(DoAudio), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop - { MAP_CALL(DoAvoider), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(DoBresen), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(DoSound), SIG_EVERYWHERE, "i([io])(i)(ii[io])(i)", kDoSound_subops, NULL }, - { MAP_CALL(DoSync), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop - { MAP_CALL(DrawCel), SIG_SCI11, SIGFOR_PC, "iiiii(i)(i)(r0)", NULL, NULL }, // for kq6 hires - { MAP_CALL(DrawCel), SIG_EVERYWHERE, "iiiii(i)(i)", NULL, NULL }, - { MAP_CALL(DrawControl), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(DrawMenuBar), SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(DrawPic), SIG_EVERYWHERE, "i(i)(i)(i)", NULL, NULL }, - { MAP_CALL(DrawStatus), SIG_EVERYWHERE, "[r0](i)(i)", NULL, NULL }, - { MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL }, - { MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL }, - { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL }, - { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL }, - { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL }, - { MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop - { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, NULL }, - { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL }, - { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL }, - { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL }, - { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, NULL }, - // ^^ FIXME - occasionally KQ6 passes a 5th argument by mistake - { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL }, - { MAP_CALL(GetDistance), SIG_EVERYWHERE, "ii(i)(i)(i)(i)", NULL, NULL }, - { MAP_CALL(GetEvent), SIG_SCIALL, SIGFOR_MAC, "io(i*)", NULL, NULL }, - { MAP_CALL(GetEvent), SIG_EVERYWHERE, "io", NULL, NULL }, - { MAP_CALL(GetFarText), SIG_EVERYWHERE, "ii[r0]", NULL, NULL }, - { MAP_CALL(GetMenu), SIG_EVERYWHERE, "i.", NULL, NULL }, - { MAP_CALL(GetMessage), SIG_EVERYWHERE, "iiir", NULL, NULL }, - { MAP_CALL(GetPort), SIG_EVERYWHERE, "", NULL, NULL }, - { MAP_CALL(GetSaveDir), SIG_SCI32, SIGFOR_ALL, "(r*)", NULL, NULL }, - { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL }, - { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL }, - { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL }, - { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL }, - { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL }, - { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL }, - { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(InitBresen), SIG_EVERYWHERE, "o(i)", NULL, NULL }, - { MAP_CALL(Intersections), SIG_EVERYWHERE, "iiiiriiiri", NULL, NULL }, - { MAP_CALL(IsItSkip), SIG_EVERYWHERE, "iiiii", NULL, NULL }, - { MAP_CALL(IsObject), SIG_EVERYWHERE, ".", NULL, NULL }, - { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop - { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL }, - { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL }, - { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL }, - { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL }, - { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop - { MAP_CALL(MemoryInfo), SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(MemorySegment), SIG_EVERYWHERE, "ir(i)", NULL, NULL }, // subop - { MAP_CALL(MenuSelect), SIG_EVERYWHERE, "o(i)", NULL, NULL }, - { MAP_CALL(MergePoly), SIG_EVERYWHERE, "rli", NULL, NULL }, - { MAP_CALL(Message), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop - { MAP_CALL(MoveCursor), SIG_EVERYWHERE, "ii", NULL, NULL }, - { MAP_CALL(NewList), SIG_EVERYWHERE, "", NULL, NULL }, - { MAP_CALL(NewNode), SIG_EVERYWHERE, "..", NULL, NULL }, - { MAP_CALL(NewWindow), SIG_SCIALL, SIGFOR_MAC, ".*", NULL, NULL }, - { MAP_CALL(NewWindow), SIG_SCI0, SIGFOR_ALL, "iiii[r0]i(i)(i)(i)", NULL, NULL }, - { MAP_CALL(NewWindow), SIG_SCI1, SIGFOR_ALL, "iiii[ir]i(i)(i)([ir])(i)(i)(i)(i)", NULL, NULL }, - { MAP_CALL(NewWindow), SIG_SCI11, SIGFOR_ALL, "iiiiiiii[r0]i(i)(i)(i)", NULL, NULL }, - { MAP_CALL(NextNode), SIG_EVERYWHERE, "n", NULL, NULL }, - { MAP_CALL(NodeValue), SIG_EVERYWHERE, "[n0]", NULL, NULL }, - { MAP_CALL(NumCels), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(NumLoops), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(OnControl), SIG_EVERYWHERE, "ii(i)(i)(i)", NULL, NULL }, - { MAP_CALL(PalVary), SIG_EVERYWHERE, "i(i*)", kPalVary_subops, NULL }, - { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL }, - { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL }, - { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL }, - { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop - { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL }, - { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(Random), SIG_EVERYWHERE, "ii", NULL, NULL }, - { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL }, - { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL }, - { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL }, - { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL }, - { MAP_CALL(RestoreGame), SIG_EVERYWHERE, "rir", NULL, NULL }, - { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL }, - { MAP_CALL(SaveGame), SIG_EVERYWHERE, "rir(r)", NULL, NULL }, - { MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL }, - { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i*)", NULL, NULL }, - { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL }, - { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL }, - { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, - { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL }, - { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iii)(i)(i)(i)", NULL, NULL }, - { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL }, - { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL }, - { MAP_CALL(ShowMovie), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_CALL(SinDiv), SIG_EVERYWHERE, "ii", NULL, NULL }, - { MAP_CALL(Sort), SIG_EVERYWHERE, "ooo", NULL, NULL }, - { MAP_CALL(Sqrt), SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(StrAt), SIG_EVERYWHERE, "ri(i)", NULL, NULL }, - { MAP_CALL(StrCat), SIG_EVERYWHERE, "rr", NULL, NULL }, - { MAP_CALL(StrCmp), SIG_EVERYWHERE, "rr(i)", NULL, NULL }, - { MAP_CALL(StrCpy), SIG_EVERYWHERE, "[r0]r(i)", NULL, NULL }, - { MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL }, - { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, NULL }, - { MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL }, - { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL }, - { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL }, - { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL }, - { MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL }, - { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL }, - { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL }, - { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL }, - { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL }, - { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL }, - { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds }, - { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL }, - { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL }, - -#ifdef ENABLE_SCI32 - // SCI2 Kernel Functions - { MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, - { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL }, - { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL }, - { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL }, - { MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL }, - { MAP_CALL(ListAllTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL }, - { MAP_CALL(ListAt), SIG_EVERYWHERE, "li", NULL, NULL }, - { MAP_CALL(ListEachElementDo), SIG_EVERYWHERE, "li(.*)", NULL, NULL }, - { MAP_CALL(ListFirstTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL }, - { MAP_CALL(ListIndexOf), SIG_EVERYWHERE, "l[o0]", NULL, NULL }, - { MAP_CALL(OnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL }, - { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, - - // SCI2.1 Kernel Functions - { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL }, - { MAP_CALL(List), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL }, - { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL } -#endif -}; - Kernel::Kernel(ResourceManager *resMan, SegManager *segMan) : _resMan(resMan), _segMan(segMan), _invalid("<invalid>") { loadSelectorNames(); @@ -648,16 +44,24 @@ Kernel::Kernel(ResourceManager *resMan, SegManager *segMan) Kernel::~Kernel() { for (KernelFunctionArray::iterator i = _kernelFuncs.begin(); i != _kernelFuncs.end(); ++i) - free(i->signature); + delete[] i->signature; } uint Kernel::getSelectorNamesSize() const { return _selectorNames.size(); } -const Common::String &Kernel::getSelectorName(uint selector) const { - if (selector >= _selectorNames.size()) - return _invalid; +const Common::String &Kernel::getSelectorName(uint selector) { + if (selector >= _selectorNames.size()) { + // This should only occur in games w/o a selector-table + // We need this for proper workaround tables + // TODO: maybe check, if there is a fixed selector-table and error() out in that case + for (uint loopSelector = _selectorNames.size(); loopSelector <= selector; loopSelector++) { + Common::String newSelectorName; + newSelectorName = newSelectorName.printf("<noname %d>", loopSelector); + _selectorNames.push_back(newSelectorName); + } + } return _selectorNames[selector]; } @@ -1140,7 +544,7 @@ void Kernel::mapFunctions() { _kernelFuncs[id].workarounds = NULL; _kernelFuncs[id].subFunctions = NULL; _kernelFuncs[id].subFunctionCount = 0; - _kernelFuncs[id].debugCalls = false; + _kernelFuncs[id].debugLogging = false; if (kernelName.empty()) { // No name was given -> must be an unknown opcode warning("Kernel function %x unknown", id); @@ -1246,12 +650,77 @@ void Kernel::mapFunctions() { return; } -void Kernel::setDefaultKernelNames() { +bool Kernel::debugSetFunctionLogging(const char *kernelName, bool logging) { + if (strcmp(kernelName, "*")) { + for (uint id = 0; id < _kernelFuncs.size(); id++) { + if (_kernelFuncs[id].name) { + if (strcmp(kernelName, _kernelFuncs[id].name) == 0) { + if (_kernelFuncs[id].subFunctions) { + // sub-functions available and main name matched, in that case set logging of all sub-functions + KernelSubFunction *kernelSubCall = _kernelFuncs[id].subFunctions; + uint kernelSubCallCount = _kernelFuncs[id].subFunctionCount; + for (uint subId = 0; subId < kernelSubCallCount; subId++) { + if (kernelSubCall->function) + kernelSubCall->debugLogging = logging; + kernelSubCall++; + } + return true; + } + // function name matched, set for this one and exit + _kernelFuncs[id].debugLogging = logging; + return true; + } else { + // main name was not matched + if (_kernelFuncs[id].subFunctions) { + // Sub-Functions available + KernelSubFunction *kernelSubCall = _kernelFuncs[id].subFunctions; + uint kernelSubCallCount = _kernelFuncs[id].subFunctionCount; + for (uint subId = 0; subId < kernelSubCallCount; subId++) { + if (kernelSubCall->function) { + if (strcmp(kernelName, kernelSubCall->name) == 0) { + // sub-function name matched, set for this one and exit + kernelSubCall->debugLogging = logging; + return true; + } + } + kernelSubCall++; + } + } + } + } + } + return false; + } + // Set debugLogging for all calls + for (uint id = 0; id < _kernelFuncs.size(); id++) { + if (_kernelFuncs[id].name) { + if (!_kernelFuncs[id].subFunctions) { + // No sub-functions, enable actual kernel function + _kernelFuncs[id].debugLogging = logging; + } else { + // Sub-Functions available, enable those too + KernelSubFunction *kernelSubCall = _kernelFuncs[id].subFunctions; + uint kernelSubCallCount = _kernelFuncs[id].subFunctionCount; + for (uint subId = 0; subId < kernelSubCallCount; subId++) { + if (kernelSubCall->function) + kernelSubCall->debugLogging = logging; + kernelSubCall++; + } + } + } + } + return true; +} + +void Kernel::setDefaultKernelNames(GameFeatures *features) { _kernelNames = Common::StringArray(s_defaultKernelNames, ARRAYSIZE(s_defaultKernelNames)); // Some (later) SCI versions replaced CanBeHere by CantBeHere - if (_selectorCache.cantBeHere != -1) - _kernelNames[0x4d] = "CantBeHere"; + if (_selectorCache.cantBeHere != -1) { + // hoyle 3 has cantBeHere selector but is assuming to call kCanBeHere + if (g_sci->getGameId() != GID_HOYLE3) + _kernelNames[0x4d] = "CantBeHere"; + } switch (getSciVersion()) { case SCI_VERSION_0_EARLY: @@ -1298,7 +767,11 @@ void Kernel::setDefaultKernelNames() { } _kernelNames[0x71] = "PalVary"; - _kernelNames[0x7c] = "Message"; + + // At least EcoQuest 1 demo uses kGetMessage instead of kMessage. + // Detect which function to use. + if (features->detectMessageFunctionType() == SCI_VERSION_1_1) + _kernelNames[0x7c] = "Message"; break; default: @@ -1307,6 +780,39 @@ void Kernel::setDefaultKernelNames() { } } +#ifdef ENABLE_SCI32 + +enum { + kKernelEntriesSci2 = 0x8b, + kKernelEntriesGk2Demo = 0xa0, + kKernelEntriesSci21 = 0x9d +}; + +void Kernel::setKernelNamesSci2() { + _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2); +} + +void Kernel::setKernelNamesSci21(GameFeatures *features) { + // Some SCI games use a modified SCI2 kernel table instead of the + // SCI2.1 kernel table. The GK2 demo does this as well as at least + // one version of KQ7 (1.4). We detect which version to use based on + // how kDoSound is called from Sound::play(). + + // This is interesting because they all have the same interpreter + // version (2.100.002), yet they would not be compatible with other + // games of the same interpreter. + + if (features->detectSci21KernelType() == SCI_VERSION_2) { + _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo); + // OnMe is IsOnMe here, but they should be compatible + _kernelNames[0x23] = "Robot"; // Graph in SCI2 + _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2 + } else + _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci21); +} + +#endif + void Kernel::loadKernelNames(GameFeatures *features) { _kernelNames.clear(); @@ -1317,7 +823,7 @@ void Kernel::loadKernelNames(GameFeatures *features) { setKernelNamesSci2(); else #endif - setDefaultKernelNames(); + setDefaultKernelNames(features); mapFunctions(); } @@ -1352,4 +858,26 @@ Common::String Kernel::lookupText(reg_t address, int index) { return NULL; } +// TODO: script_adjust_opcode_formats should probably be part of the +// constructor (?) of a VirtualMachine or a ScriptManager class. +void script_adjust_opcode_formats() { + if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) { + g_opcode_formats[op_lofsa][0] = Script_Offset; + g_opcode_formats[op_lofss][0] = Script_Offset; + } + +#ifdef ENABLE_SCI32 + // In SCI32, some arguments are now words instead of bytes + if (getSciVersion() >= SCI_VERSION_2) { + g_opcode_formats[op_calle][2] = Script_Word; + g_opcode_formats[op_callk][1] = Script_Word; + g_opcode_formats[op_super][1] = Script_Word; + g_opcode_formats[op_send][0] = Script_Word; + g_opcode_formats[op_self][0] = Script_Word; + g_opcode_formats[op_call][1] = Script_Word; + g_opcode_formats[op_callb][1] = Script_Word; + } +#endif +} + } // End of namespace Sci diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index fa206e8053..285e746349 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -40,6 +40,7 @@ namespace Sci { struct Node; // from segment.h struct List; // from segment.h struct SelectorCache; // from selector.h +struct SciWorkaroundEntry; // from workarounds.h /** * @defgroup VocabularyResources Vocabulary resources in SCI @@ -100,12 +101,12 @@ struct SelectorCache; // from selector.h enum { SIG_TYPE_NULL = 0x01, // may be 0:0 [0] SIG_TYPE_INTEGER = 0x02, // may be 0:* [i], automatically also allows null - SIG_TYPE_UNINITIALIZED = 0x04, // may be FFFF:* -> not allowable, only used for comparsion + SIG_TYPE_UNINITIALIZED = 0x04, // may be FFFF:* -> not allowable, only used for comparison SIG_TYPE_OBJECT = 0x08, // may be object [o] SIG_TYPE_REFERENCE = 0x10, // may be reference [r] SIG_TYPE_LIST = 0x20, // may be list [l] SIG_TYPE_NODE = 0x40, // may be node [n] - SIG_TYPE_ERROR = 0x80, // happens, when there is a identification error - only used for comparsion + SIG_TYPE_ERROR = 0x80, // happens, when there is a identification error - only used for comparison SIG_IS_INVALID = 0x100, // ptr is invalid [!] -> invalid offset SIG_IS_OPTIONAL = 0x200, // is optional SIG_NEEDS_MORE = 0x400, // needs at least one additional parameter following @@ -120,24 +121,12 @@ enum { /* Generic description: */ typedef reg_t KernelFunctionCall(EngineState *s, int argc, reg_t *argv); -struct SciWorkaroundEntry { - SciGameId gameId; - int scriptNr; - int16 inheritanceLevel; - const char *objectName; - const char *methodName; - int localCallOffset; - int index; - reg_t newValue; -}; - -#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, 0, NULL, NULL, -1, 0, { 0, 0 } } - struct KernelSubFunction { KernelFunctionCall *function; const char *name; uint16 *signature; const SciWorkaroundEntry *workarounds; + bool debugLogging; }; struct KernelFunction { @@ -145,9 +134,9 @@ struct KernelFunction { const char *name; uint16 *signature; const SciWorkaroundEntry *workarounds; - const KernelSubFunction *subFunctions; + KernelSubFunction *subFunctions; uint16 subFunctionCount; - bool debugCalls; + bool debugLogging; }; class Kernel { @@ -159,7 +148,7 @@ public: ~Kernel(); uint getSelectorNamesSize() const; - const Common::String &getSelectorName(uint selector) const; + const Common::String &getSelectorName(uint selector); uint getKernelNamesSize() const; const Common::String &getKernelName(uint number) const; @@ -228,11 +217,16 @@ public: */ void loadKernelNames(GameFeatures *features); + /** + * Sets debugCalls flag for a kernel function + */ + bool debugSetFunctionLogging(const char *kernelName, bool debugCalls); + private: /** * Sets the default kernel function names, based on the SCI version used. */ - void setDefaultKernelNames(); + void setDefaultKernelNames(GameFeatures *features); #ifdef ENABLE_SCI32 /** @@ -423,7 +417,10 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv); reg_t kPlatform(EngineState *s, int argc, reg_t *argv); reg_t kTextColors(EngineState *s, int argc, reg_t *argv); reg_t kTextFonts(EngineState *s, int argc, reg_t *argv); +reg_t kDummy(EngineState *s, int argc, reg_t *argv); reg_t kEmpty(EngineState *s, int argc, reg_t *argv); +reg_t kStub(EngineState *s, int argc, reg_t *argv); +reg_t kStubNull(EngineState *s, int argc, reg_t *argv); #ifdef ENABLE_SCI32 // SCI2 Kernel Functions @@ -432,6 +429,7 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv); reg_t kListAt(EngineState *s, int argc, reg_t *argv); reg_t kString(EngineState *s, int argc, reg_t *argv); reg_t kMulDiv(EngineState *s, int argc, reg_t *argv); +reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv); // "Screen items" in SCI32 are views reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv); @@ -460,7 +458,11 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv); reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv); reg_t kCD(EngineState *s, int argc, reg_t *argv); +reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv); +reg_t kAddBefore(EngineState *s, int argc, reg_t *argv); +reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv); +reg_t kMoveToEnd(EngineState *s, int argc, reg_t *argv); #endif reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv); @@ -471,7 +473,7 @@ reg_t kDoSoundMute(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundStop(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundStopAll(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundPause(EngineState *s, int argc, reg_t *argv); -reg_t kDoSoundResume(EngineState *s, int argc, reg_t *argv); +reg_t kDoSoundResumeAfterRestore(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundMasterVolume(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundUpdate(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundFade(EngineState *s, int argc, reg_t *argv); @@ -516,6 +518,25 @@ reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv); reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv); reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOFindFirst(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv); +reg_t kFileIORename(EngineState *s, int argc, reg_t *argv); +#ifdef ENABLE_SCI32 +reg_t kFileIOReadByte(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv); +reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv); +#endif + //@} } // End of namespace Sci diff --git a/engines/sci/engine/kernel32.cpp b/engines/sci/engine/kernel32.cpp deleted file mode 100644 index eab1b90139..0000000000 --- a/engines/sci/engine/kernel32.cpp +++ /dev/null @@ -1,932 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * 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$ - * - */ - -#ifdef ENABLE_SCI32 - -#include "sci/engine/features.h" -#include "sci/engine/kernel.h" -#include "sci/engine/segment.h" -#include "sci/engine/state.h" -#include "sci/engine/selector.h" -#include "sci/graphics/frameout.h" -#include "sci/graphics/screen.h" - -#include "common/system.h" - -namespace Sci { - -// NOTE: 0x72-0x79, 0x85-0x86, 0x88 are from the GK2 demo (which has debug support) and are -// just Dummy in other SCI2 games. -static const char *sci2_default_knames[] = { - /*0x00*/ "Load", - /*0x01*/ "UnLoad", - /*0x02*/ "ScriptID", - /*0x03*/ "DisposeScript", - /*0x04*/ "Lock", - /*0x05*/ "ResCheck", - /*0x06*/ "Purge", - /*0x07*/ "Clone", - /*0x08*/ "DisposeClone", - /*0x09*/ "RespondsTo", - /*0x0a*/ "SetNowSeen", - /*0x0b*/ "NumLoops", - /*0x0c*/ "NumCels", - /*0x0d*/ "CelWide", - /*0x0e*/ "CelHigh", - /*0x0f*/ "GetHighPlanePri", - /*0x10*/ "GetHighItemPri", - /*0x11*/ "ShakeScreen", - /*0x12*/ "OnMe", - /*0x13*/ "ShowMovie", - /*0x14*/ "SetVideoMode", - /*0x15*/ "AddScreenItem", - /*0x16*/ "DeleteScreenItem", - /*0x17*/ "UpdateScreenItem", - /*0x18*/ "FrameOut", - /*0x19*/ "AddPlane", - /*0x1a*/ "DeletePlane", - /*0x1b*/ "UpdatePlane", - /*0x1c*/ "RepaintPlane", - /*0x1d*/ "SetShowStyle", - /*0x1e*/ "ShowStylePercent", - /*0x1f*/ "SetScroll", - /*0x20*/ "AddMagnify", - /*0x21*/ "DeleteMagnify", - /*0x22*/ "IsHiRes", - /*0x23*/ "Graph", - /*0x24*/ "InvertRect", - /*0x25*/ "TextSize", - /*0x26*/ "Message", - /*0x27*/ "TextColors", - /*0x28*/ "TextFonts", - /*0x29*/ "Dummy", - /*0x2a*/ "SetQuitStr", - /*0x2b*/ "EditText", - /*0x2c*/ "InputText", - /*0x2d*/ "CreateTextBitmap", - /*0x2e*/ "DisposeTextBitmap", - /*0x2f*/ "GetEvent", - /*0x30*/ "GlobalToLocal", - /*0x31*/ "LocalToGlobal", - /*0x32*/ "MapKeyToDir", - /*0x33*/ "HaveMouse", - /*0x34*/ "SetCursor", - /*0x35*/ "VibrateMouse", - /*0x36*/ "SaveGame", - /*0x37*/ "RestoreGame", - /*0x38*/ "RestartGame", - /*0x39*/ "GameIsRestarting", - /*0x3a*/ "MakeSaveCatName", - /*0x3b*/ "MakeSaveFileName", - /*0x3c*/ "GetSaveFiles", - /*0x3d*/ "GetSaveDir", - /*0x3e*/ "CheckSaveGame", - /*0x3f*/ "CheckFreeSpace", - /*0x40*/ "DoSound", - /*0x41*/ "DoAudio", - /*0x42*/ "DoSync", - /*0x43*/ "NewList", - /*0x44*/ "DisposeList", - /*0x45*/ "NewNode", - /*0x46*/ "FirstNode", - /*0x47*/ "LastNode", - /*0x48*/ "EmptyList", - /*0x49*/ "NextNode", - /*0x4a*/ "PrevNode", - /*0x4b*/ "NodeValue", - /*0x4c*/ "AddAfter", - /*0x4d*/ "AddToFront", - /*0x4e*/ "AddToEnd", - /*0x4f*/ "Dummy", - /*0x50*/ "Dummy", - /*0x51*/ "FindKey", - /*0x52*/ "Dummy", - /*0x53*/ "Dummy", - /*0x54*/ "Dummy", - /*0x55*/ "DeleteKey", - /*0x56*/ "Dummy", - /*0x57*/ "Dummy", - /*0x58*/ "ListAt", - /*0x59*/ "ListIndexOf", - /*0x5a*/ "ListEachElementDo", - /*0x5b*/ "ListFirstTrue", - /*0x5c*/ "ListAllTrue", - /*0x5d*/ "Random", - /*0x5e*/ "Abs", - /*0x5f*/ "Sqrt", - /*0x60*/ "GetAngle", - /*0x61*/ "GetDistance", - /*0x62*/ "ATan", - /*0x63*/ "SinMult", - /*0x64*/ "CosMult", - /*0x65*/ "SinDiv", - /*0x66*/ "CosDiv", - /*0x67*/ "GetTime", - /*0x68*/ "Platform", - /*0x69*/ "BaseSetter", - /*0x6a*/ "DirLoop", - /*0x6b*/ "CantBeHere", - /*0x6c*/ "InitBresen", - /*0x6d*/ "DoBresen", - /*0x6e*/ "SetJump", - /*0x6f*/ "AvoidPath", - /*0x70*/ "InPolygon", - /*0x71*/ "MergePoly", - /*0x72*/ "SetDebug", - /*0x73*/ "InspectObject", - /*0x74*/ "MemoryInfo", - /*0x75*/ "Profiler", - /*0x76*/ "Record", - /*0x77*/ "PlayBack", - /*0x78*/ "MonoOut", - /*0x79*/ "SetFatalStr", - /*0x7a*/ "GetCWD", - /*0x7b*/ "ValidPath", - /*0x7c*/ "FileIO", - /*0x7d*/ "Dummy", - /*0x7e*/ "DeviceInfo", - /*0x7f*/ "Palette", - /*0x80*/ "PalVary", - /*0x81*/ "PalCycle", - /*0x82*/ "Array", - /*0x83*/ "String", - /*0x84*/ "RemapColors", - /*0x85*/ "IntegrityChecking", - /*0x86*/ "CheckIntegrity", - /*0x87*/ "ObjectIntersect", - /*0x88*/ "MarkMemory", - /*0x89*/ "TextWidth", - /*0x8a*/ "PointSize", - - // GK2 Demo only kernel functions - /*0x8b*/ "AddLine", - /*0x8c*/ "DeleteLine", - /*0x8d*/ "UpdateLine", - /*0x8e*/ "AddPolygon", - /*0x8f*/ "DeletePolygon", - /*0x90*/ "UpdatePolygon", - /*0x91*/ "Bitmap", - /*0x92*/ "ScrollWindow", - /*0x93*/ "SetFontRes", - /*0x94*/ "MovePlaneItems", - /*0x95*/ "PreloadResource", - /*0x96*/ "Dummy", - /*0x97*/ "ResourceTrack", - /*0x98*/ "CheckCDisc", - /*0x99*/ "GetSaveCDisc", - /*0x9a*/ "TestPoly", - /*0x9b*/ "WinHelp", - /*0x9c*/ "LoadChunk", - /*0x9d*/ "SetPalStyleRange", - /*0x9e*/ "AddPicAt", - /*0x9f*/ "MessageBox" -}; - -static const char *sci21_default_knames[] = { - /*0x00*/ "Load", - /*0x01*/ "UnLoad", - /*0x02*/ "ScriptID", - /*0x03*/ "DisposeScript", - /*0x04*/ "Lock", - /*0x05*/ "ResCheck", - /*0x06*/ "Purge", - /*0x07*/ "SetLanguage", - /*0x08*/ "Dummy", - /*0x09*/ "Dummy", - /*0x0a*/ "Clone", - /*0x0b*/ "DisposeClone", - /*0x0c*/ "RespondsTo", - /*0x0d*/ "FindSelector", - /*0x0e*/ "FindClass", - /*0x0f*/ "Dummy", - /*0x10*/ "Dummy", - /*0x11*/ "Dummy", - /*0x12*/ "Dummy", - /*0x13*/ "Dummy", - /*0x14*/ "SetNowSeen", - /*0x15*/ "NumLoops", - /*0x16*/ "NumCels", - /*0x17*/ "IsOnMe", - /*0x18*/ "AddMagnify", - /*0x19*/ "DeleteMagnify", - /*0x1a*/ "CelRect", - /*0x1b*/ "BaseLineSpan", - /*0x1c*/ "CelWide", - /*0x1d*/ "CelHigh", - /*0x1e*/ "AddScreenItem", - /*0x1f*/ "DeleteScreenItem", - /*0x20*/ "UpdateScreenItem", - /*0x21*/ "FrameOut", - /*0x22*/ "CelInfo", - /*0x23*/ "Bitmap", - /*0x24*/ "CelLink", - /*0x25*/ "Dummy", - /*0x26*/ "Dummy", - /*0x27*/ "Dummy", - /*0x28*/ "AddPlane", - /*0x29*/ "DeletePlane", - /*0x2a*/ "UpdatePlane", - /*0x2b*/ "RepaintPlane", - /*0x2c*/ "GetHighPlanePri", - /*0x2d*/ "GetHighItemPri", - /*0x2e*/ "SetShowStyle", - /*0x2f*/ "ShowStylePercent", - /*0x30*/ "SetScroll", - /*0x31*/ "MovePlaneItems", - /*0x32*/ "ShakeScreen", - /*0x33*/ "Dummy", - /*0x34*/ "Dummy", - /*0x35*/ "Dummy", - /*0x36*/ "Dummy", - /*0x37*/ "IsHiRes", - /*0x38*/ "SetVideoMode", - /*0x39*/ "ShowMovie", - /*0x3a*/ "Robot", - /*0x3b*/ "CreateTextBitmap", - /*0x3c*/ "Random", - /*0x3d*/ "Abs", - /*0x3e*/ "Sqrt", - /*0x3f*/ "GetAngle", - /*0x40*/ "GetDistance", - /*0x41*/ "ATan", - /*0x42*/ "SinMult", - /*0x43*/ "CosMult", - /*0x44*/ "SinDiv", - /*0x45*/ "CosDiv", - /*0x46*/ "Text", - /*0x47*/ "Dummy", - /*0x48*/ "Message", - /*0x49*/ "Font", - /*0x4a*/ "EditText", - /*0x4b*/ "InputText", - /*0x4c*/ "ScrollWindow", - /*0x4d*/ "Dummy", - /*0x4e*/ "Dummy", - /*0x4f*/ "Dummy", - /*0x50*/ "GetEvent", - /*0x51*/ "GlobalToLocal", - /*0x52*/ "LocalToGlobal", - /*0x53*/ "MapKeyToDir", - /*0x54*/ "HaveMouse", - /*0x55*/ "SetCursor", - /*0x56*/ "VibrateMouse", // NOTE: Not in SCI3, instead replaced by Dummy. - /*0x57*/ "Dummy", - /*0x58*/ "Dummy", - /*0x59*/ "Dummy", - /*0x5a*/ "List", - /*0x5b*/ "Array", - /*0x5c*/ "String", - /*0x5d*/ "FileIO", - /*0x5e*/ "BaseSetter", - /*0x5f*/ "DirLoop", - /*0x60*/ "CantBeHere", - /*0x61*/ "InitBresen", - /*0x62*/ "DoBresen", - /*0x63*/ "SetJump", - /*0x64*/ "AvoidPath", - /*0x65*/ "InPolygon", - /*0x66*/ "MergePoly", - /*0x67*/ "ObjectIntersect", - /*0x68*/ "Dummy", - /*0x69*/ "MemoryInfo", - /*0x6a*/ "DeviceInfo", - /*0x6b*/ "Palette", - /*0x6c*/ "PalVary", - /*0x6d*/ "PalCycle", - /*0x6e*/ "RemapColors", - /*0x6f*/ "AddLine", - /*0x70*/ "DeleteLine", - /*0x71*/ "UpdateLine", - /*0x72*/ "AddPolygon", - /*0x73*/ "DeletePolygon", - /*0x74*/ "UpdatePolygon", - /*0x75*/ "DoSound", - /*0x76*/ "DoAudio", - /*0x77*/ "DoSync", - /*0x78*/ "Save", - /*0x79*/ "GetTime", - /*0x7a*/ "Platform", - /*0x7b*/ "CD", - /*0x7c*/ "SetQuitStr", - /*0x7d*/ "GetConfig", - /*0x7e*/ "Table", - /*0x7f*/ "WinHelp", // Windows only - /*0x80*/ "Dummy", - /*0x81*/ "Dummy", - /*0x82*/ "Dummy", - /*0x83*/ "PrintDebug", // used by the Shivers 2 demo - /*0x84*/ "Dummy", - /*0x85*/ "Dummy", - /*0x86*/ "Dummy", - /*0x87*/ "Dummy", - /*0x88*/ "Dummy", - /*0x89*/ "Dummy", - /*0x8a*/ "LoadChunk", - /*0x8b*/ "SetPalStyleRange", - /*0x8c*/ "AddPicAt", - /*0x8d*/ "Dummy", - /*0x8e*/ "NewRoom", - /*0x8f*/ "Dummy", - /*0x90*/ "Priority", - /*0x91*/ "MorphOn", - /*0x92*/ "PlayVMD", - /*0x93*/ "SetHotRectangles", - /*0x94*/ "MulDiv", - /*0x95*/ "GetSierraProfileInt", // Windows only - /*0x96*/ "GetSierraProfileString", // Windows only - /*0x97*/ "SetWindowsOption", // Windows only - /*0x98*/ "GetWindowsOption", // Windows only - /*0x99*/ "WinDLL", // Windows only - - // SCI3 - /*0x9a*/ "Dummy", - /*0x9b*/ "Dummy", - /*0x9c*/ "DeletePic" -}; - -enum { - kKernelEntriesSci2 = 0x8b, - kKernelEntriesGk2Demo = 0xa0, - kKernelEntriesSci21 = 0x9a, - kKernelEntriesSci3 = 0x9d -}; - -void Kernel::setKernelNamesSci2() { - _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2); -} - -void Kernel::setKernelNamesSci21(GameFeatures *features) { - // Some SCI games use a modified SCI2 kernel table instead of the - // SCI2.1/SCI3 kernel table. The GK2 demo does this as well as at - // least one version of KQ7. We detect which version to use based on - // where kDoSound is called from Sound::play(). - - // This is interesting because they all have the same interpreter - // version (2.100.002), yet they would not be compatible with other - // games of the same interpreter. - - if (features->detectSci21KernelType() == SCI_VERSION_2) { - _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo); - // OnMe is IsOnMe here, but they should be compatible - _kernelNames[0x23] = "Robot"; // Graph in SCI2 - _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2 - } else { - // TODO: Differentiate between SCI2.1/3 - _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci3); - } -} - -// SCI2 Kernel Functions - -reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) { - // Returns 0 if the screen width or height is less than 640 or 400, - // respectively. - if (g_system->getWidth() < 640 || g_system->getHeight() < 400) - return make_reg(0, 0); - - return make_reg(0, 1); -} - -reg_t kArray(EngineState *s, int argc, reg_t *argv) { - switch (argv[0].toUint16()) { - case 0: { // New - reg_t arrayHandle; - SciArray<reg_t> *array = s->_segMan->allocateArray(&arrayHandle); - array->setType(argv[2].toUint16()); - array->setSize(argv[1].toUint16()); - return arrayHandle; - } - case 1: { // Size - SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); - return make_reg(0, array->getSize()); - } - case 2: { // At (return value at an index) - SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); - return array->getValue(argv[2].toUint16()); - } - case 3: { // Atput (put value at an index) - SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); - - uint32 index = argv[2].toUint16(); - uint32 count = argc - 3; - - if (index + count > 65535) - break; - - if (array->getSize() < index + count) - array->setSize(index + count); - - for (uint16 i = 0; i < count; i++) - array->setValue(i + index, argv[i + 3]); - - return argv[1]; // We also have to return the handle - } - case 4: // Free - // Freeing of arrays is handled by the garbage collector - return s->r_acc; - case 5: { // Fill - SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); - uint16 index = argv[2].toUint16(); - - // A count of -1 means fill the rest of the array - uint16 count = argv[3].toSint16() == -1 ? array->getSize() - index : argv[3].toUint16(); - uint16 arraySize = array->getSize(); - - if (arraySize < index + count) - array->setSize(index + count); - - for (uint16 i = 0; i < count; i++) - array->setValue(i + index, argv[4]); - - return argv[1]; - } - case 6: { // Cpy - if (s->_segMan->getSegmentObj(argv[1].segment)->getType() != SEG_TYPE_ARRAY || - s->_segMan->getSegmentObj(argv[3].segment)->getType() != SEG_TYPE_ARRAY) { - // Happens in the RAMA demo - warning("kArray(Cpy): Request to copy a segment which isn't an array, ignoring"); - return NULL_REG; - } - - SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]); - SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]); - uint32 index1 = argv[2].toUint16(); - uint32 index2 = argv[4].toUint16(); - - // The original engine ignores bad copies too - if (index2 > array2->getSize()) - break; - - // A count of -1 means fill the rest of the array - uint32 count = argv[5].toSint16() == -1 ? array2->getSize() - index2 : argv[5].toUint16(); - - if (array1->getSize() < index1 + count) - array1->setSize(index1 + count); - - for (uint16 i = 0; i < count; i++) - array1->setValue(i + index1, array2->getValue(i + index2)); - - return argv[1]; - } - case 7: // Cmp - // Not implemented in SSCI - return s->r_acc; - case 8: { // Dup - SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); - reg_t arrayHandle; - SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle); - - dupArray->setType(array->getType()); - dupArray->setSize(array->getSize()); - - for (uint32 i = 0; i < array->getSize(); i++) - dupArray->setValue(i, array->getValue(i)); - - return arrayHandle; - } - case 9: // Getdata - if (!s->_segMan->isHeapObject(argv[1])) - return argv[1]; - - return readSelector(s->_segMan, argv[1], SELECTOR(data)); - default: - error("Unknown kArray subop %d", argv[0].toUint16()); - } - - return NULL_REG; -} - -reg_t kText(EngineState *s, int argc, reg_t *argv) { - switch (argv[0].toUint16()) { - case 0: - return kTextSize(s, argc - 1, argv + 1); - default: - // TODO: Other subops here too, perhaps kTextColors and kTextFonts - warning("kText(%d)", argv[0].toUint16()); - break; - } - - return s->r_acc; -} - -reg_t kString(EngineState *s, int argc, reg_t *argv) { - switch (argv[0].toUint16()) { - case 0: { // New - reg_t stringHandle; - SciString *string = s->_segMan->allocateString(&stringHandle); - string->setSize(argv[1].toUint16()); - - // Make sure the first character is a null character - if (string->getSize() > 0) - string->setValue(0, 0); - - return stringHandle; - } - case 1: // Size - return make_reg(0, s->_segMan->getString(argv[1]).size()); - case 2: { // At (return value at an index) - if (argv[1].segment == s->_segMan->getStringSegmentId()) - return make_reg(0, s->_segMan->lookupString(argv[1])->getRawData()[argv[2].toUint16()]); - - return make_reg(0, s->_segMan->getString(argv[1])[argv[2].toUint16()]); - } - case 3: { // Atput (put value at an index) - SciString *string = s->_segMan->lookupString(argv[1]); - - uint32 index = argv[2].toUint16(); - uint32 count = argc - 3; - - if (index + count > 65535) - break; - - if (string->getSize() < index + count) - string->setSize(index + count); - - for (uint16 i = 0; i < count; i++) - string->setValue(i + index, argv[i + 3].toUint16()); - - return argv[1]; // We also have to return the handle - } - case 4: // Free - // Freeing of strings is handled by the garbage collector - return s->r_acc; - case 5: { // Fill - SciString *string = s->_segMan->lookupString(argv[1]); - uint16 index = argv[2].toUint16(); - - // A count of -1 means fill the rest of the array - uint16 count = argv[3].toSint16() == -1 ? string->getSize() - index : argv[3].toUint16(); - uint16 stringSize = string->getSize(); - - if (stringSize < index + count) - string->setSize(index + count); - - for (uint16 i = 0; i < count; i++) - string->setValue(i + index, argv[4].toUint16()); - - return argv[1]; - } - case 6: { // Cpy - const char *string2 = 0; - uint32 string2Size = 0; - - if (argv[3].segment == s->_segMan->getStringSegmentId()) { - SciString *string = s->_segMan->lookupString(argv[3]); - string2 = string->getRawData(); - string2Size = string->getSize(); - } else { - Common::String string = s->_segMan->getString(argv[3]); - string2 = string.c_str(); - string2Size = string.size() + 1; - } - - uint32 index1 = argv[2].toUint16(); - uint32 index2 = argv[4].toUint16(); - - // The original engine ignores bad copies too - if (index2 > string2Size) - break; - - // A count of -1 means fill the rest of the array - uint32 count = argv[5].toSint16() == -1 ? string2Size - index2 + 1 : argv[5].toUint16(); - - // We have a special case here for argv[1] being a system string - if (argv[1].segment == s->_segMan->getSysStringsSegment()) { - // Resize if necessary - const uint16 sysStringId = argv[1].toUint16(); - SystemString *sysString = s->_segMan->getSystemString(sysStringId); - assert(sysString); - if ((uint32)sysString->_maxSize < index1 + count) { - free(sysString->_value); - sysString->_maxSize = index1 + count; - sysString->_value = (char *)calloc(index1 + count, sizeof(char)); - } - - strncpy(sysString->_value + index1, string2 + index2, count); - } else { - SciString *string1 = s->_segMan->lookupString(argv[1]); - - if (string1->getSize() < index1 + count) - string1->setSize(index1 + count); - - // Note: We're accessing from c_str() here because the - // string's size ignores the trailing 0 and therefore - // triggers an assert when doing string2[i + index2]. - for (uint16 i = 0; i < count; i++) - string1->setValue(i + index1, string2[i + index2]); - } - - } return argv[1]; - case 7: { // Cmp - Common::String string1 = argv[1].isNull() ? "" : s->_segMan->getString(argv[1]); - Common::String string2 = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]); - - if (argc == 4) // Strncmp - return make_reg(0, strncmp(string1.c_str(), string2.c_str(), argv[3].toUint16())); - else // Strcmp - return make_reg(0, strcmp(string1.c_str(), string2.c_str())); - } - case 8: { // Dup - const char *rawString = 0; - uint32 size = 0; - - if (argv[1].segment == s->_segMan->getStringSegmentId()) { - SciString *string = s->_segMan->lookupString(argv[1]); - rawString = string->getRawData(); - size = string->getSize(); - } else { - Common::String string = s->_segMan->getString(argv[1]); - rawString = string.c_str(); - size = string.size() + 1; - } - - reg_t stringHandle; - SciString *dupString = s->_segMan->allocateString(&stringHandle); - dupString->setSize(size); - - for (uint32 i = 0; i < size; i++) - dupString->setValue(i, rawString[i]); - - return stringHandle; - } - case 9: // Getdata - if (!s->_segMan->isHeapObject(argv[1])) - return argv[1]; - - return readSelector(s->_segMan, argv[1], SELECTOR(data)); - case 10: // Stringlen - return make_reg(0, s->_segMan->strlen(argv[1])); - case 11: { // Printf - reg_t stringHandle; - s->_segMan->allocateString(&stringHandle); - - reg_t *adjustedArgs = new reg_t[argc]; - adjustedArgs[0] = stringHandle; - memcpy(&adjustedArgs[1], argv + 1, (argc - 1) * sizeof(reg_t)); - - kFormat(s, argc, adjustedArgs); - delete[] adjustedArgs; - return stringHandle; - } - case 12: // Printf Buf - return kFormat(s, argc - 1, argv + 1); - case 13: { // atoi - Common::String string = s->_segMan->getString(argv[1]); - return make_reg(0, (uint16)atoi(string.c_str())); - } - default: - error("Unknown kString subop %d", argv[0].toUint16()); - } - - return NULL_REG; -} - -reg_t kSave(EngineState *s, int argc, reg_t *argv) { - switch (argv[0].toUint16()) { - case 2: // GetSaveDir - // Yay! Reusing the old kernel function! - return kGetSaveDir(s, argc - 1, argv + 1); - case 8: - // TODO - // This function has to return something other than 0 to proceed - return s->r_acc; - default: - warning("Unknown/unhandled kSave subop %d", argv[0].toUint16()); - } - - return NULL_REG; -} - -reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) { - reg_t viewObj = argv[0]; - - g_sci->_gfxFrameout->kernelAddScreenItem(viewObj); - return NULL_REG; -} - -reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) { - //reg_t viewObj = argv[0]; - - //warning("kUpdateScreenItem, object %04x:%04x, view %d, loop %d, cel %d, pri %d", PRINT_REG(viewObj), viewId, loopNo, celNo, priority); - return NULL_REG; -} - -reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) { - reg_t viewObj = argv[0]; - - g_sci->_gfxFrameout->kernelDeleteScreenItem(viewObj); - - /* - reg_t viewObj = argv[0]; - uint16 viewId = readSelectorValue(s->_segMan, viewObj, SELECTOR(view)); - int16 loopNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(loop)); - int16 celNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(cel)); - //int16 leftPos = 0; - //int16 topPos = 0; - int16 priority = readSelectorValue(s->_segMan, viewObj, SELECTOR(priority)); - //int16 control = 0; - */ - - // TODO - - //warning("kDeleteScreenItem, view %d, loop %d, cel %d, pri %d", viewId, loopNo, celNo, priority); - return NULL_REG; -} - -reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) { - reg_t planeObj = argv[0]; - - g_sci->_gfxFrameout->kernelAddPlane(planeObj); - warning("kAddPlane object %04x:%04x", PRINT_REG(planeObj)); - return NULL_REG; -} - -reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) { - reg_t planeObj = argv[0]; - - g_sci->_gfxFrameout->kernelDeletePlane(planeObj); - warning("kDeletePlane object %04x:%04x", PRINT_REG(planeObj)); - return NULL_REG; -} - -reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) { - reg_t planeObj = argv[0]; - - g_sci->_gfxFrameout->kernelUpdatePlane(planeObj); - return s->r_acc; -} - -reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv) { - reg_t picObj = argv[0]; - - // TODO - - warning("kRepaintPlane object %04x:%04x", PRINT_REG(picObj)); - return NULL_REG; -} - -reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) { - warning("kGetHighPlanePri: %d", g_sci->_gfxFrameout->kernelGetHighPlanePri()); - return make_reg(0, g_sci->_gfxFrameout->kernelGetHighPlanePri()); -} - -reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) { - // This kernel call likely seems to be doing the screen updates, - // as its called right after a view is updated - - // TODO - g_sci->_gfxFrameout->kernelFrameout(); - - return NULL_REG; -} - -reg_t kOnMe(EngineState *s, int argc, reg_t *argv) { - // Tests if the cursor is on the passed object - - uint16 x = argv[0].toUint16(); - uint16 y = argv[1].toUint16(); - reg_t targetObject = argv[2]; - // TODO: argv[3] - it's usually 0 - Common::Rect nsRect; - - // Get the bounding rectangle of the object - nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft)); - nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop)); - nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight)); - nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom)); - uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x)); - uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y)); - - // If top and left are negative, we need to adjust coordinates by - // the item's x and y (e.g. happens in GK1, day 1, with detective - // Mosely's hotspot in his office) - - if (nsRect.left < 0) - nsRect.translate(itemX, 0); - - if (nsRect.top < 0) - nsRect.translate(0, itemY); - - // HACK: nsLeft and nsTop can be invalid, so try and fix them here - // using x and y (e.g. with the inventory screen in GK1) - if (nsRect.left == itemY && nsRect.top == itemX) { - // Swap the values, as they're inversed (eh???) - nsRect.left = itemX; - nsRect.top = itemY; - } - - /* - warning("kOnMe: (%d, %d) on object %04x:%04x (%s), rect (%d, %d, %d, %d), parameter %d", - argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), s->_segMan->getObjectName(argv[2]), - nsRect.left, nsRect.top, nsRect.right, nsRect.bottom, - argv[3].toUint16()); - */ - - return make_reg(0, nsRect.contains(x, y)); -} - -reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) { - // Tests if the cursor is on the passed object, after adjusting the - // coordinates of the object according to the object's plane - - uint16 x = argv[0].toUint16(); - uint16 y = argv[1].toUint16(); - reg_t targetObject = argv[2]; - // TODO: argv[3] - it's usually 0 - Common::Rect nsRect; - - // Get the bounding rectangle of the object - nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft)); - nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop)); - nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight)); - nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom)); - - // Get the object's plane - reg_t planeObject = readSelector(s->_segMan, targetObject, SELECTOR(plane)); - if (!planeObject.isNull()) { - uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x)); - uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y)); - uint16 planeResY = readSelectorValue(s->_segMan, planeObject, SELECTOR(resY)); - uint16 planeResX = readSelectorValue(s->_segMan, planeObject, SELECTOR(resX)); - uint16 planeTop = readSelectorValue(s->_segMan, planeObject, SELECTOR(top)); - uint16 planeLeft = readSelectorValue(s->_segMan, planeObject, SELECTOR(left)); - planeTop = (planeTop * g_sci->_gfxScreen->getHeight()) / planeResY; - planeLeft = (planeLeft * g_sci->_gfxScreen->getWidth()) / planeResX; - - // Adjust the bounding rectangle of the object by the object's - // actual X, Y coordinates - itemY = ((itemY * g_sci->_gfxScreen->getHeight()) / planeResY); - itemX = ((itemX * g_sci->_gfxScreen->getWidth()) / planeResX); - itemY += planeTop; - itemX += planeLeft; - - nsRect.translate(itemX, itemY); - } - - //warning("kIsOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16()); - - return make_reg(0, nsRect.contains(x, y)); -} - -reg_t kInPolygon(EngineState *s, int argc, reg_t *argv) { - // kAvoidPath already implements this - return kAvoidPath(s, argc, argv); -} - -reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { - // TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1 - switch (argv[0].toUint16()) { - case 0: { - if (argc != 4) { - warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc); - return NULL_REG; - } - reg_t object = argv[3]; - Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text))); - debug("kCreateTextBitmap: %s", text.c_str()); - } - default: - warning("CreateTextBitmap(%d)", argv[0].toUint16()); - } - - return NULL_REG; -} - -reg_t kCD(EngineState *s, int argc, reg_t *argv) { - // TODO: Stub - switch (argv[0].toUint16()) { - case 0: - // Return whether the contents of disc argv[1] is available. - return TRUE_REG; - default: - warning("CD(%d)", argv[0].toUint16()); - } - - return NULL_REG; -} - -} // End of namespace Sci - -#endif // ENABLE_SCI32 diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h new file mode 100644 index 0000000000..886e918fd8 --- /dev/null +++ b/engines/sci/engine/kernel_tables.h @@ -0,0 +1,1030 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SCI_ENGINE_KERNEL_TABLES_H +#define SCI_ENGINE_KERNEL_TABLES_H + +#include "sci/engine/workarounds.h" + +namespace Sci { + +// [io] -> either integer or object +// (io) -> optionally integer AND an object +// (i) -> optional integer +// . -> any type +// i* -> optional multiple integers +// .* -> any parameters afterwards (or none) + +struct SciKernelMapSubEntry { + SciVersion fromVersion; + SciVersion toVersion; + + uint16 id; + + const char *name; + KernelFunctionCall *function; + + const char *signature; + const SciWorkaroundEntry *workarounds; +}; + +#define SCI_SUBOPENTRY_TERMINATOR { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, NULL, NULL, NULL, NULL } + + +#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE +#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01 +#define SIG_SCI1 SCI_VERSION_1_EGA, SCI_VERSION_1_LATE +#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1 +#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE +#define SIG_SCI21 SCI_VERSION_2_1, SCI_VERSION_2_1 + +#define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1 +#define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE + +// SCI-Sound-Version +#define SIG_SOUNDSCI0 SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE +#define SIG_SOUNDSCI1EARLY SCI_VERSION_1_EARLY, SCI_VERSION_1_EARLY +#define SIG_SOUNDSCI1LATE SCI_VERSION_1_LATE, SCI_VERSION_1_LATE +#define SIG_SOUNDSCI21 SCI_VERSION_2_1, SCI_VERSION_2_1 + +#define SIGFOR_ALL 0x3f +#define SIGFOR_DOS 1 << 0 +#define SIGFOR_PC98 1 << 1 +#define SIGFOR_WIN 1 << 2 +#define SIGFOR_MAC 1 << 3 +#define SIGFOR_AMIGA 1 << 4 +#define SIGFOR_ATARI 1 << 5 +#define SIGFOR_PC SIGFOR_DOS|SIGFOR_WIN + +#define SIG_EVERYWHERE SIG_SCIALL, SIGFOR_ALL + +#define MAP_CALL(_name_) #_name_, k##_name_ + +// version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kDoSound_subops[] = { + { SIG_SOUNDSCI0, 0, MAP_CALL(DoSoundInit), "o", NULL }, + { SIG_SOUNDSCI0, 1, MAP_CALL(DoSoundPlay), "o", NULL }, + { SIG_SOUNDSCI0, 2, MAP_CALL(DoSoundDummy), "(o)", NULL }, + { SIG_SOUNDSCI0, 3, MAP_CALL(DoSoundDispose), "o", NULL }, + { SIG_SOUNDSCI0, 4, MAP_CALL(DoSoundMute), "(i)", NULL }, + { SIG_SOUNDSCI0, 5, MAP_CALL(DoSoundStop), "o", NULL }, + { SIG_SOUNDSCI0, 6, MAP_CALL(DoSoundPause), "i", NULL }, + { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResumeAfterRestore), "", NULL }, + { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL }, + { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL }, + { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", kDoSoundFade_workarounds }, + { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL }, + { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL }, + { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL }, + { SIG_SOUNDSCI1EARLY, 1, MAP_CALL(DoSoundMute), NULL, NULL }, + { SIG_SOUNDSCI1EARLY, 2, MAP_CALL(DoSoundDummy), NULL, NULL }, + { SIG_SOUNDSCI1EARLY, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL }, + { SIG_SOUNDSCI1EARLY, 4, MAP_CALL(DoSoundUpdate), NULL, NULL }, + { SIG_SOUNDSCI1EARLY, 5, MAP_CALL(DoSoundInit), NULL, NULL }, + { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL }, + { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL }, + // ^^ TODO: In SCI1-SCI1.1 DoSound (play) is called by 2 methods of the Sound object: play and + // playBed. The methods are the same, apart from the second integer parameter: it's 0 in + // play and 1 in playBed, to distinguish the caller. It's passed on, we should find out what + // it actually does internally + { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL }, + { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL }, + { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", NULL }, + { SIG_SOUNDSCI1EARLY, 11, MAP_CALL(DoSoundUpdateCues), "o", NULL }, + { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiii", NULL }, + { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundReverb), "i", NULL }, + { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL }, + { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(DoSoundDummy), "", NULL }, + // ^^ Longbow demo + { SIG_SOUNDSCI1LATE, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 1, MAP_CALL(DoSoundMute), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 2, MAP_CALL(DoSoundDummy), "", NULL }, + { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL }, + { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL }, + { SIG_SOUNDSCI1LATE, 6, MAP_CALL(DoSoundInit), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 7, MAP_CALL(DoSoundDispose), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 8, MAP_CALL(DoSoundPlay), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 9, MAP_CALL(DoSoundStop), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 10, MAP_CALL(DoSoundPause), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 11, MAP_CALL(DoSoundFade), "oiiii(i)", kDoSoundFade_workarounds }, + { SIG_SOUNDSCI1LATE, 12, MAP_CALL(DoSoundSetHold), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 13, MAP_CALL(DoSoundDummy), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL }, + { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL }, + { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL }, + { SIG_SOUNDSCI1LATE, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 18, MAP_CALL(DoSoundSendMidi), "oiii(i)", NULL }, + { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundReverb), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 20, MAP_CALL(DoSoundUpdate), NULL, NULL }, +#ifdef ENABLE_SCI32 + { SIG_SOUNDSCI21, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL }, + { SIG_SOUNDSCI21, 1, MAP_CALL(DoSoundMute), NULL, NULL }, + { SIG_SOUNDSCI21, 2, MAP_CALL(DoSoundDummy), NULL, NULL }, + { SIG_SOUNDSCI21, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL }, + { SIG_SOUNDSCI21, 4, MAP_CALL(DoSoundGetAudioCapability), NULL, NULL }, + { SIG_SOUNDSCI21, 5, MAP_CALL(DoSoundSuspend), NULL, NULL }, + { SIG_SOUNDSCI21, 6, MAP_CALL(DoSoundInit), NULL, NULL }, + { SIG_SOUNDSCI21, 7, MAP_CALL(DoSoundDispose), NULL, NULL }, + { SIG_SOUNDSCI21, 8, MAP_CALL(DoSoundPlay), "o(i)", NULL }, + // ^^ TODO: if this is really the only change between SCI1LATE AND SCI21, we could rename the + // SIG_SOUNDSCI1LATE #define to SIG_SINCE_SOUNDSCI1LATE and make it being SCI1LATE+. Although + // I guess there are many more changes somewhere + // TODO: Quest for Glory 4 (SCI2.1) uses the old scheme, we need to detect it accordingly + // signature for SCI21 should be "o" + { SIG_SOUNDSCI21, 9, MAP_CALL(DoSoundStop), NULL, NULL }, + { SIG_SOUNDSCI21, 10, MAP_CALL(DoSoundPause), NULL, NULL }, + { SIG_SOUNDSCI21, 11, MAP_CALL(DoSoundFade), NULL, NULL }, + { SIG_SOUNDSCI21, 12, MAP_CALL(DoSoundSetHold), NULL, NULL }, + { SIG_SOUNDSCI21, 13, MAP_CALL(DoSoundDummy), NULL, NULL }, + { SIG_SOUNDSCI21, 14, MAP_CALL(DoSoundSetVolume), NULL, NULL }, + { SIG_SOUNDSCI21, 15, MAP_CALL(DoSoundSetPriority), NULL, NULL }, + { SIG_SOUNDSCI21, 16, MAP_CALL(DoSoundSetLoop), NULL, NULL }, + { SIG_SOUNDSCI21, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL }, + { SIG_SOUNDSCI21, 18, MAP_CALL(DoSoundSendMidi), NULL, NULL }, + { SIG_SOUNDSCI21, 19, MAP_CALL(DoSoundReverb), NULL, NULL }, + { SIG_SOUNDSCI21, 20, MAP_CALL(DoSoundUpdate), NULL, NULL }, +#endif + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kGraph_subops[] = { + { SIG_SCI32, 1, MAP_CALL(StubNull), "", NULL }, // called by gk1 sci32 right at the start + { SIG_SCIALL, 2, MAP_CALL(GraphGetColorCount), "", NULL }, + // 3 - set palette via resource + { SIG_SCIALL, 4, MAP_CALL(GraphDrawLine), "iiiii(i)(i)", kGraphDrawLine_workarounds }, + // 5 - nop + // 6 - draw pattern + { SIG_SCIALL, 7, MAP_CALL(GraphSaveBox), "iiiii", kGraphSaveBox_workarounds }, + { SIG_SCIALL, 8, MAP_CALL(GraphRestoreBox), "[r0!]", kGraphRestoreBox_workarounds }, + // ^ this may get called with invalid references, we check them within restoreBits() and sierra sci behaves the same + { SIG_SCIALL, 9, MAP_CALL(GraphFillBoxBackground), "iiii", NULL }, + { SIG_SCIALL, 10, MAP_CALL(GraphFillBoxForeground), "iiii", kGraphFillBoxForeground_workarounds }, + { SIG_SCIALL, 11, MAP_CALL(GraphFillBoxAny), "iiiiii(i)(i)", kGraphFillBoxAny_workarounds }, + { SIG_SCI11, 12, MAP_CALL(GraphUpdateBox), "iiii(i)(r0)", NULL }, // kq6 hires + { SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", NULL }, + { SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", kGraphRedrawBox_workarounds }, + { SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL }, + { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kPalVary_subops[] = { + { SIG_SCI21, 0, MAP_CALL(PalVaryInit), "ii(i)(i)(i)", NULL }, + { SIG_SCIALL, 0, MAP_CALL(PalVaryInit), "ii(i)(i)", NULL }, + { SIG_SCIALL, 1, MAP_CALL(PalVaryReverse), "(i)(i)(i)", NULL }, + { SIG_SCIALL, 2, MAP_CALL(PalVaryGetCurrentStep), "", NULL }, + { SIG_SCIALL, 3, MAP_CALL(PalVaryDeinit), "", NULL }, + { SIG_SCIALL, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL }, + { SIG_SCIALL, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL }, + { SIG_SCIALL, 6, MAP_CALL(PalVaryPauseResume), "i", NULL }, + { SIG_SCI32, 8, MAP_CALL(PalVaryUnknown), "i", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kPalette_subops[] = { + { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL }, + { SIG_SCIALL, 2, MAP_CALL(PaletteSetFlag), "iii", NULL }, + { SIG_SCIALL, 3, MAP_CALL(PaletteUnsetFlag), "iii", kPaletteUnsetFlag_workarounds }, + { SIG_SCIALL, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL }, + { SIG_SCIALL, 5, MAP_CALL(PaletteFindColor), "iii", NULL }, + { SIG_SCIALL, 6, MAP_CALL(PaletteAnimate), "i*", NULL }, + { SIG_SCIALL, 7, MAP_CALL(PaletteSave), "", NULL }, + { SIG_SCIALL, 8, MAP_CALL(PaletteRestore), "[r0]", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +static const SciKernelMapSubEntry kFileIO_subops[] = { + { SIG_SCI32, 0, MAP_CALL(FileIOOpen), "r(i)", NULL }, + { SIG_SCIALL, 0, MAP_CALL(FileIOOpen), "ri", NULL }, + { SIG_SCIALL, 1, MAP_CALL(FileIOClose), "i", NULL }, + { SIG_SCIALL, 2, MAP_CALL(FileIOReadRaw), "iri", NULL }, + { SIG_SCIALL, 3, MAP_CALL(FileIOWriteRaw), "iri", NULL }, + { SIG_SCIALL, 4, MAP_CALL(FileIOUnlink), "r", NULL }, + { SIG_SCIALL, 5, MAP_CALL(FileIOReadString), "rii", NULL }, + { SIG_SCIALL, 6, MAP_CALL(FileIOWriteString), "ir", NULL }, + { SIG_SCIALL, 7, MAP_CALL(FileIOSeek), "iii", NULL }, + { SIG_SCIALL, 8, MAP_CALL(FileIOFindFirst), "rri", NULL }, + { SIG_SCIALL, 9, MAP_CALL(FileIOFindNext), "r", NULL }, + { SIG_SCIALL, 10, MAP_CALL(FileIOExists), "r", NULL }, + { SIG_SINCE_SCI11, 11, MAP_CALL(FileIORename), "rr", NULL }, +#ifdef ENABLE_SCI32 + { SIG_SCI32, 13, MAP_CALL(FileIOReadByte), "i", NULL }, + { SIG_SCI32, 14, MAP_CALL(FileIOWriteByte), "ii", NULL }, + { SIG_SCI32, 15, MAP_CALL(FileIOReadWord), "i", NULL }, + { SIG_SCI32, 16, MAP_CALL(FileIOWriteWord), "ii", NULL }, + { SIG_SCI32, 19, MAP_CALL(Stub), "r", NULL }, // for Torin / Torin demo +#endif + SCI_SUBOPENTRY_TERMINATOR +}; + +#ifdef ENABLE_SCI32 +// version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kList_subops[] = { + { SIG_SCI21, 0, MAP_CALL(NewList), "", NULL }, + { SIG_SCI21, 1, MAP_CALL(DisposeList), "l", NULL }, + { SIG_SCI21, 2, MAP_CALL(NewNode), ".", NULL }, + { SIG_SCI21, 3, MAP_CALL(FirstNode), "[l0]", NULL }, + { SIG_SCI21, 4, MAP_CALL(LastNode), "l", NULL }, + { SIG_SCI21, 5, MAP_CALL(EmptyList), "l", NULL }, + { SIG_SCI21, 6, MAP_CALL(NextNode), "n", NULL }, + { SIG_SCI21, 7, MAP_CALL(PrevNode), "n", NULL }, + { SIG_SCI21, 8, MAP_CALL(NodeValue), "[n0]", NULL }, + { SIG_SCI21, 9, MAP_CALL(AddAfter), "lnn.", NULL }, + { SIG_SCI21, 10, MAP_CALL(AddToFront), "ln.", NULL }, + { SIG_SCI21, 11, MAP_CALL(AddToEnd), "ln.", NULL }, + { SIG_SCI21, 12, MAP_CALL(AddBefore), "ln.", NULL }, + { SIG_SCI21, 13, MAP_CALL(MoveToFront), "ln", NULL }, + { SIG_SCI21, 14, MAP_CALL(MoveToEnd), "ln", NULL }, + { SIG_SCI21, 15, MAP_CALL(FindKey), "l.", NULL }, + { SIG_SCI21, 16, MAP_CALL(DeleteKey), "l.", NULL }, + { SIG_SCI21, 17, MAP_CALL(ListAt), "li", NULL }, + // FIXME: This doesn't seem to be ListIndexOf. In Torin demo, an index is + // passed as a second parameter instead of an object. Thus, it seems to + // be something like ListAt instead... If we swap the two subops though, + // Torin demo crashes complaining that it tried to send to a non-object, + // therefore the semantics might be different here (signature was l[o0]) + // In SQ6 object is passed right when skipping the intro + { SIG_SCI21, 18, MAP_CALL(StubNull), "l[io]", NULL }, + { SIG_SCI21, 19, MAP_CALL(ListEachElementDo), "li(.*)", NULL }, + { SIG_SCI21, 20, MAP_CALL(ListFirstTrue), "li(.*)", NULL }, + { SIG_SCI21, 21, MAP_CALL(ListAllTrue), "li(.*)", NULL }, + { SIG_SCI21, 22, MAP_CALL(Sort), "ooo", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; +#endif + +struct SciKernelMapEntry { + const char *name; + KernelFunctionCall *function; + + SciVersion fromVersion; + SciVersion toVersion; + byte forPlatform; + + const char *signature; + const SciKernelMapSubEntry *subFunctions; + const SciWorkaroundEntry *workarounds; +}; + +// name, version/platform, signature, sub-signatures, workarounds +static SciKernelMapEntry s_kernelMap[] = { + { MAP_CALL(Abs), SIG_EVERYWHERE, "i", NULL, kAbs_workarounds }, + { MAP_CALL(AddAfter), SIG_EVERYWHERE, "lnn", NULL, NULL }, + { MAP_CALL(AddMenu), SIG_EVERYWHERE, "rr", NULL, NULL }, + { MAP_CALL(AddToEnd), SIG_EVERYWHERE, "ln", NULL, NULL }, + { MAP_CALL(AddToFront), SIG_EVERYWHERE, "ln", NULL, NULL }, + { MAP_CALL(AddToPic), SIG_EVERYWHERE, "[il](iiiiii)", NULL, NULL }, + { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL }, + { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL }, + { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL }, + { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL }, +#ifdef ENABLE_SCI32 + { "CantBeHere", kCantBeHere32, SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL }, +#endif + { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL }, + { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, kCelHigh_workarounds }, + { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, kCelWide_workarounds }, + { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL }, + { MAP_CALL(CheckFreeSpace), SIG_SCI11, SIGFOR_ALL, "r(i)", NULL, NULL }, + { MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL }, + { MAP_CALL(CheckSaveGame), SIG_EVERYWHERE, ".*", NULL, NULL }, + { MAP_CALL(Clone), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(CoordPri), SIG_EVERYWHERE, "i(i)", NULL, NULL }, + { MAP_CALL(CosDiv), SIG_EVERYWHERE, "ii", NULL, NULL }, + { MAP_CALL(DeleteKey), SIG_EVERYWHERE, "l.", NULL, NULL }, + { MAP_CALL(DeviceInfo), SIG_EVERYWHERE, "i(r)(r)(i)", NULL, NULL }, // subop + { MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, NULL }, + // ^ we allow invalid references here, because kDisplay gets called with those in e.g. pq3 during intro + // restoreBits() checks and skips invalid handles, so that's fine. Sierra SCI behaved the same + { MAP_CALL(DirLoop), SIG_EVERYWHERE, "oi", NULL, NULL }, + { MAP_CALL(DisposeClone), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(DisposeList), SIG_EVERYWHERE, "l", NULL, NULL }, + { MAP_CALL(DisposeScript), SIG_EVERYWHERE, "i(i*)", NULL, kDisposeScript_workarounds }, + { MAP_CALL(DisposeWindow), SIG_EVERYWHERE, "i(i)", NULL, NULL }, + { MAP_CALL(DoAudio), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop + { MAP_CALL(DoAvoider), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(DoBresen), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(DoSound), SIG_EVERYWHERE, "i(.*)", kDoSound_subops, NULL }, + { MAP_CALL(DoSync), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop + { MAP_CALL(DrawCel), SIG_SCI11, SIGFOR_PC, "iiiii(i)(i)([ri])", NULL, NULL }, // reference for kq6 hires + { MAP_CALL(DrawCel), SIG_EVERYWHERE, "iiiii(i)(i)", NULL, NULL }, + { MAP_CALL(DrawControl), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(DrawMenuBar), SIG_EVERYWHERE, "i", NULL, NULL }, + { MAP_CALL(DrawPic), SIG_EVERYWHERE, "i(i)(i)(i)", NULL, NULL }, + { MAP_CALL(DrawStatus), SIG_EVERYWHERE, "[r0](i)(i)", NULL, NULL }, + { MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL }, + { MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL }, + { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL }, + { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL }, + { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL }, + { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL }, + { MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", kFileIO_subops, NULL }, + { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, kFindKey_workarounds }, + { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL }, + { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL }, + { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL }, + { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL }, + { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, kGetAngle_workarounds }, + { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL }, + { MAP_CALL(GetDistance), SIG_EVERYWHERE, "ii(i)(i)(i)(i)", NULL, NULL }, + { MAP_CALL(GetEvent), SIG_SCIALL, SIGFOR_MAC, "io(i*)", NULL, NULL }, + { MAP_CALL(GetEvent), SIG_EVERYWHERE, "io", NULL, NULL }, + { MAP_CALL(GetFarText), SIG_EVERYWHERE, "ii[r0]", NULL, NULL }, + { MAP_CALL(GetMenu), SIG_EVERYWHERE, "i.", NULL, NULL }, + { MAP_CALL(GetMessage), SIG_EVERYWHERE, "iiir", NULL, NULL }, + { MAP_CALL(GetPort), SIG_EVERYWHERE, "", NULL, NULL }, + { MAP_CALL(GetSaveDir), SIG_SCI32, SIGFOR_ALL, "(r*)", NULL, NULL }, + { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL }, + { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL }, + { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL }, + { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL }, + { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL }, + { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL }, + { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(InitBresen), SIG_EVERYWHERE, "o(i)", NULL, NULL }, + { MAP_CALL(Intersections), SIG_EVERYWHERE, "iiiiriiiri", NULL, NULL }, + { MAP_CALL(IsItSkip), SIG_EVERYWHERE, "iiiii", NULL, NULL }, + { MAP_CALL(IsObject), SIG_EVERYWHERE, ".", NULL, kIsObject_workarounds }, + { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop + { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL }, + { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL }, + { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL }, + { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL }, + { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, kMemory_workarounds }, // subop + { MAP_CALL(MemoryInfo), SIG_EVERYWHERE, "i", NULL, NULL }, + { MAP_CALL(MemorySegment), SIG_EVERYWHERE, "ir(i)", NULL, NULL }, // subop + { MAP_CALL(MenuSelect), SIG_EVERYWHERE, "o(i)", NULL, NULL }, + { MAP_CALL(MergePoly), SIG_EVERYWHERE, "rli", NULL, NULL }, + { MAP_CALL(Message), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop + { MAP_CALL(MoveCursor), SIG_EVERYWHERE, "ii", NULL, NULL }, + { MAP_CALL(NewList), SIG_EVERYWHERE, "", NULL, NULL }, + { MAP_CALL(NewNode), SIG_EVERYWHERE, "..", NULL, NULL }, + { MAP_CALL(NewWindow), SIG_SCIALL, SIGFOR_MAC, ".*", NULL, NULL }, + { MAP_CALL(NewWindow), SIG_SCI0, SIGFOR_ALL, "iiii[r0]i(i)(i)(i)", NULL, NULL }, + { MAP_CALL(NewWindow), SIG_SCI1, SIGFOR_ALL, "iiii[ir]i(i)(i)([ir])(i)(i)(i)(i)", NULL, NULL }, + { MAP_CALL(NewWindow), SIG_SCI11, SIGFOR_ALL, "iiiiiiii[r0]i(i)(i)(i)", NULL, kNewWindow_workarounds }, + { MAP_CALL(NextNode), SIG_EVERYWHERE, "n", NULL, NULL }, + { MAP_CALL(NodeValue), SIG_EVERYWHERE, "[n0]", NULL, NULL }, + { MAP_CALL(NumCels), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(NumLoops), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(OnControl), SIG_EVERYWHERE, "ii(i)(i)(i)", NULL, NULL }, + { MAP_CALL(PalVary), SIG_EVERYWHERE, "i(i*)", kPalVary_subops, NULL }, + { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL }, + { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL }, + { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL }, + { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop + { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL }, + { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL }, + { MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL }, + { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL }, + { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL }, + { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL }, + { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL }, + { MAP_CALL(RestoreGame), SIG_EVERYWHERE, "rir", NULL, NULL }, + { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL }, + { MAP_CALL(SaveGame), SIG_EVERYWHERE, "rir(r)", NULL, NULL }, + { MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL }, + { MAP_CALL(SetCursor), SIG_SCI21, SIGFOR_ALL, "i(i)([io])(i*)", NULL, NULL }, + // TODO: SCI2.1 may supply an object optionally (mother goose sci21 right on startup) - find out why + { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i*)", NULL, NULL }, + { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL }, + { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL }, + { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, + { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL }, + { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds }, + { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL }, + { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL }, + { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL }, + { MAP_CALL(ShowMovie), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(SinDiv), SIG_EVERYWHERE, "ii", NULL, NULL }, + { MAP_CALL(Sort), SIG_EVERYWHERE, "ooo", NULL, NULL }, + { MAP_CALL(Sqrt), SIG_EVERYWHERE, "i", NULL, NULL }, + { MAP_CALL(StrAt), SIG_EVERYWHERE, "ri(i)", NULL, NULL }, + { MAP_CALL(StrCat), SIG_EVERYWHERE, "rr", NULL, NULL }, + { MAP_CALL(StrCmp), SIG_EVERYWHERE, "rr(i)", NULL, NULL }, + { MAP_CALL(StrCpy), SIG_EVERYWHERE, "r[r0](i)", NULL, NULL }, + { MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL }, + { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, NULL }, + { MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL }, + { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL }, + { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL }, + { MAP_CALL(TextSize), SIG_SCIALL, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL }, + { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL }, + { MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL }, + { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL }, + { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL }, + { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL }, + { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL }, + { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL }, + { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds }, + { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL }, + { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL }, + +#ifdef ENABLE_SCI32 + // SCI2 Kernel Functions + { MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, + { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL }, + { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL }, + { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL }, + { MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL }, + { MAP_CALL(ListAllTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL }, + { MAP_CALL(ListAt), SIG_EVERYWHERE, "li", NULL, NULL }, + { MAP_CALL(ListEachElementDo), SIG_EVERYWHERE, "li(.*)", NULL, NULL }, + { MAP_CALL(ListFirstTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL }, + { MAP_CALL(ListIndexOf), SIG_EVERYWHERE, "l[o0]", NULL, NULL }, + { MAP_CALL(OnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL }, + { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, + + // SCI2.1 Kernel Functions + { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL }, + { MAP_CALL(List), SIG_SCI21, SIGFOR_ALL, "(.*)", kList_subops, NULL }, + { MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL }, + { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL }, + { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL } +#endif +}; + +/** Default kernel name table. */ +static const char *s_defaultKernelNames[] = { + /*0x00*/ "Load", + /*0x01*/ "UnLoad", + /*0x02*/ "ScriptID", + /*0x03*/ "DisposeScript", + /*0x04*/ "Clone", + /*0x05*/ "DisposeClone", + /*0x06*/ "IsObject", + /*0x07*/ "RespondsTo", + /*0x08*/ "DrawPic", + /*0x09*/ "Dummy", // Show + /*0x0a*/ "PicNotValid", + /*0x0b*/ "Animate", + /*0x0c*/ "SetNowSeen", + /*0x0d*/ "NumLoops", + /*0x0e*/ "NumCels", + /*0x0f*/ "CelWide", + /*0x10*/ "CelHigh", + /*0x11*/ "DrawCel", + /*0x12*/ "AddToPic", + /*0x13*/ "NewWindow", + /*0x14*/ "GetPort", + /*0x15*/ "SetPort", + /*0x16*/ "DisposeWindow", + /*0x17*/ "DrawControl", + /*0x18*/ "HiliteControl", + /*0x19*/ "EditControl", + /*0x1a*/ "TextSize", + /*0x1b*/ "Display", + /*0x1c*/ "GetEvent", + /*0x1d*/ "GlobalToLocal", + /*0x1e*/ "LocalToGlobal", + /*0x1f*/ "MapKeyToDir", + /*0x20*/ "DrawMenuBar", + /*0x21*/ "MenuSelect", + /*0x22*/ "AddMenu", + /*0x23*/ "DrawStatus", + /*0x24*/ "Parse", + /*0x25*/ "Said", + /*0x26*/ "SetSynonyms", // Portrait (KQ6 hires) + /*0x27*/ "HaveMouse", + /*0x28*/ "SetCursor", + // FOpen (SCI0) + // FPuts (SCI0) + // FGets (SCI0) + // FClose (SCI0) + /*0x29*/ "SaveGame", + /*0x2a*/ "RestoreGame", + /*0x2b*/ "RestartGame", + /*0x2c*/ "GameIsRestarting", + /*0x2d*/ "DoSound", + /*0x2e*/ "NewList", + /*0x2f*/ "DisposeList", + /*0x30*/ "NewNode", + /*0x31*/ "FirstNode", + /*0x32*/ "LastNode", + /*0x33*/ "EmptyList", + /*0x34*/ "NextNode", + /*0x35*/ "PrevNode", + /*0x36*/ "NodeValue", + /*0x37*/ "AddAfter", + /*0x38*/ "AddToFront", + /*0x39*/ "AddToEnd", + /*0x3a*/ "FindKey", + /*0x3b*/ "DeleteKey", + /*0x3c*/ "Random", + /*0x3d*/ "Abs", + /*0x3e*/ "Sqrt", + /*0x3f*/ "GetAngle", + /*0x40*/ "GetDistance", + /*0x41*/ "Wait", + /*0x42*/ "GetTime", + /*0x43*/ "StrEnd", + /*0x44*/ "StrCat", + /*0x45*/ "StrCmp", + /*0x46*/ "StrLen", + /*0x47*/ "StrCpy", + /*0x48*/ "Format", + /*0x49*/ "GetFarText", + /*0x4a*/ "ReadNumber", + /*0x4b*/ "BaseSetter", + /*0x4c*/ "DirLoop", + /*0x4d*/ "CanBeHere", // CantBeHere in newer SCI versions + /*0x4e*/ "OnControl", + /*0x4f*/ "InitBresen", + /*0x50*/ "DoBresen", + /*0x51*/ "Platform", // DoAvoider (SCI0) + /*0x52*/ "SetJump", + /*0x53*/ "SetDebug", + /*0x54*/ "Dummy", // InspectObj + /*0x55*/ "Dummy", // ShowSends + /*0x56*/ "Dummy", // ShowObjs + /*0x57*/ "Dummy", // ShowFree + /*0x58*/ "MemoryInfo", + /*0x59*/ "Dummy", // StackUsage + /*0x5a*/ "Dummy", // Profiler + /*0x5b*/ "GetMenu", + /*0x5c*/ "SetMenu", + /*0x5d*/ "GetSaveFiles", + /*0x5e*/ "GetCWD", + /*0x5f*/ "CheckFreeSpace", + /*0x60*/ "ValidPath", + /*0x61*/ "CoordPri", + /*0x62*/ "StrAt", + /*0x63*/ "DeviceInfo", + /*0x64*/ "GetSaveDir", + /*0x65*/ "CheckSaveGame", + /*0x66*/ "ShakeScreen", + /*0x67*/ "FlushResources", + /*0x68*/ "SinMult", + /*0x69*/ "CosMult", + /*0x6a*/ "SinDiv", + /*0x6b*/ "CosDiv", + /*0x6c*/ "Graph", + /*0x6d*/ "Joystick", + // End of kernel function table for SCI0 + /*0x6e*/ "Dummy", // ShiftScreen + /*0x6f*/ "Palette", + /*0x70*/ "MemorySegment", + /*0x71*/ "Intersections", // MoveCursor (SCI1 late), PalVary (SCI1.1) + /*0x72*/ "Memory", + /*0x73*/ "Dummy", // ListOps + /*0x74*/ "FileIO", + /*0x75*/ "DoAudio", + /*0x76*/ "DoSync", + /*0x77*/ "AvoidPath", + /*0x78*/ "Sort", // StrSplit (SCI01) + /*0x79*/ "Dummy", // ATan + /*0x7a*/ "Lock", + /*0x7b*/ "StrSplit", + /*0x7c*/ "GetMessage", // Message (SCI1.1) + /*0x7d*/ "IsItSkip", + /*0x7e*/ "MergePoly", + /*0x7f*/ "ResCheck", + /*0x80*/ "AssertPalette", + /*0x81*/ "TextColors", + /*0x82*/ "TextFonts", + /*0x83*/ "Dummy", // Record + /*0x84*/ "Dummy", // PlayBack + /*0x85*/ "ShowMovie", + /*0x86*/ "SetVideoMode", + /*0x87*/ "SetQuitStr", + /*0x88*/ "Dummy" // DbugStr +}; + +#ifdef ENABLE_SCI32 + +// NOTE: 0x72-0x79, 0x85-0x86, 0x88 are from the GK2 demo (which has debug support) and are +// just Dummy in other SCI2 games. +static const char *sci2_default_knames[] = { + /*0x00*/ "Load", + /*0x01*/ "UnLoad", + /*0x02*/ "ScriptID", + /*0x03*/ "DisposeScript", + /*0x04*/ "Lock", + /*0x05*/ "ResCheck", + /*0x06*/ "Purge", + /*0x07*/ "Clone", + /*0x08*/ "DisposeClone", + /*0x09*/ "RespondsTo", + /*0x0a*/ "SetNowSeen", + /*0x0b*/ "NumLoops", + /*0x0c*/ "NumCels", + /*0x0d*/ "CelWide", + /*0x0e*/ "CelHigh", + /*0x0f*/ "GetHighPlanePri", + /*0x10*/ "GetHighItemPri", + /*0x11*/ "ShakeScreen", + /*0x12*/ "OnMe", + /*0x13*/ "ShowMovie", + /*0x14*/ "SetVideoMode", + /*0x15*/ "AddScreenItem", + /*0x16*/ "DeleteScreenItem", + /*0x17*/ "UpdateScreenItem", + /*0x18*/ "FrameOut", + /*0x19*/ "AddPlane", + /*0x1a*/ "DeletePlane", + /*0x1b*/ "UpdatePlane", + /*0x1c*/ "RepaintPlane", + /*0x1d*/ "SetShowStyle", + /*0x1e*/ "ShowStylePercent", + /*0x1f*/ "SetScroll", + /*0x20*/ "AddMagnify", + /*0x21*/ "DeleteMagnify", + /*0x22*/ "IsHiRes", + /*0x23*/ "Graph", + /*0x24*/ "InvertRect", + /*0x25*/ "TextSize", + /*0x26*/ "Message", + /*0x27*/ "TextColors", + /*0x28*/ "TextFonts", + /*0x29*/ "Dummy", + /*0x2a*/ "SetQuitStr", + /*0x2b*/ "EditText", + /*0x2c*/ "InputText", + /*0x2d*/ "CreateTextBitmap", + /*0x2e*/ "DisposeTextBitmap", + /*0x2f*/ "GetEvent", + /*0x30*/ "GlobalToLocal", + /*0x31*/ "LocalToGlobal", + /*0x32*/ "MapKeyToDir", + /*0x33*/ "HaveMouse", + /*0x34*/ "SetCursor", + /*0x35*/ "VibrateMouse", + /*0x36*/ "SaveGame", + /*0x37*/ "RestoreGame", + /*0x38*/ "RestartGame", + /*0x39*/ "GameIsRestarting", + /*0x3a*/ "MakeSaveCatName", + /*0x3b*/ "MakeSaveFileName", + /*0x3c*/ "GetSaveFiles", + /*0x3d*/ "GetSaveDir", + /*0x3e*/ "CheckSaveGame", + /*0x3f*/ "CheckFreeSpace", + /*0x40*/ "DoSound", + /*0x41*/ "DoAudio", + /*0x42*/ "DoSync", + /*0x43*/ "NewList", + /*0x44*/ "DisposeList", + /*0x45*/ "NewNode", + /*0x46*/ "FirstNode", + /*0x47*/ "LastNode", + /*0x48*/ "EmptyList", + /*0x49*/ "NextNode", + /*0x4a*/ "PrevNode", + /*0x4b*/ "NodeValue", + /*0x4c*/ "AddAfter", + /*0x4d*/ "AddToFront", + /*0x4e*/ "AddToEnd", + /*0x4f*/ "Dummy", + /*0x50*/ "Dummy", + /*0x51*/ "FindKey", + /*0x52*/ "Dummy", + /*0x53*/ "Dummy", + /*0x54*/ "Dummy", + /*0x55*/ "DeleteKey", + /*0x56*/ "Dummy", + /*0x57*/ "Dummy", + /*0x58*/ "ListAt", + /*0x59*/ "ListIndexOf", + /*0x5a*/ "ListEachElementDo", + /*0x5b*/ "ListFirstTrue", + /*0x5c*/ "ListAllTrue", + /*0x5d*/ "Random", + /*0x5e*/ "Abs", + /*0x5f*/ "Sqrt", + /*0x60*/ "GetAngle", + /*0x61*/ "GetDistance", + /*0x62*/ "ATan", + /*0x63*/ "SinMult", + /*0x64*/ "CosMult", + /*0x65*/ "SinDiv", + /*0x66*/ "CosDiv", + /*0x67*/ "GetTime", + /*0x68*/ "Platform", + /*0x69*/ "BaseSetter", + /*0x6a*/ "DirLoop", + /*0x6b*/ "CantBeHere", + /*0x6c*/ "InitBresen", + /*0x6d*/ "DoBresen", + /*0x6e*/ "SetJump", + /*0x6f*/ "AvoidPath", + /*0x70*/ "InPolygon", + /*0x71*/ "MergePoly", + /*0x72*/ "SetDebug", + /*0x73*/ "InspectObject", + /*0x74*/ "MemoryInfo", + /*0x75*/ "Profiler", + /*0x76*/ "Record", + /*0x77*/ "PlayBack", + /*0x78*/ "MonoOut", + /*0x79*/ "SetFatalStr", + /*0x7a*/ "GetCWD", + /*0x7b*/ "ValidPath", + /*0x7c*/ "FileIO", + /*0x7d*/ "Dummy", + /*0x7e*/ "DeviceInfo", + /*0x7f*/ "Palette", + /*0x80*/ "PalVary", + /*0x81*/ "PalCycle", + /*0x82*/ "Array", + /*0x83*/ "String", + /*0x84*/ "RemapColors", + /*0x85*/ "IntegrityChecking", + /*0x86*/ "CheckIntegrity", + /*0x87*/ "ObjectIntersect", + /*0x88*/ "MarkMemory", + /*0x89*/ "TextWidth", + /*0x8a*/ "PointSize", + + // GK2 Demo (and similar) only kernel functions + /*0x8b*/ "AddLine", + /*0x8c*/ "DeleteLine", + /*0x8d*/ "UpdateLine", + /*0x8e*/ "AddPolygon", + /*0x8f*/ "DeletePolygon", + /*0x90*/ "UpdatePolygon", + /*0x91*/ "Bitmap", + /*0x92*/ "ScrollWindow", + /*0x93*/ "SetFontRes", + /*0x94*/ "MovePlaneItems", + /*0x95*/ "PreloadResource", + /*0x96*/ "Dummy", + /*0x97*/ "ResourceTrack", + /*0x98*/ "CheckCDisc", + /*0x99*/ "GetSaveCDisc", + /*0x9a*/ "TestPoly", + /*0x9b*/ "WinHelp", + /*0x9c*/ "LoadChunk", + /*0x9d*/ "SetPalStyleRange", + /*0x9e*/ "AddPicAt", + /*0x9f*/ "MessageBox" +}; + +static const char *sci21_default_knames[] = { + /*0x00*/ "Load", + /*0x01*/ "UnLoad", + /*0x02*/ "ScriptID", + /*0x03*/ "DisposeScript", + /*0x04*/ "Lock", + /*0x05*/ "ResCheck", + /*0x06*/ "Purge", + /*0x07*/ "SetLanguage", + /*0x08*/ "Dummy", + /*0x09*/ "Dummy", + /*0x0a*/ "Clone", + /*0x0b*/ "DisposeClone", + /*0x0c*/ "RespondsTo", + /*0x0d*/ "FindSelector", + /*0x0e*/ "FindClass", + /*0x0f*/ "Dummy", + /*0x10*/ "Dummy", + /*0x11*/ "Dummy", + /*0x12*/ "Dummy", + /*0x13*/ "Dummy", + /*0x14*/ "SetNowSeen", + /*0x15*/ "NumLoops", + /*0x16*/ "NumCels", + /*0x17*/ "IsOnMe", + /*0x18*/ "AddMagnify", + /*0x19*/ "DeleteMagnify", + /*0x1a*/ "CelRect", + /*0x1b*/ "BaseLineSpan", + /*0x1c*/ "CelWide", + /*0x1d*/ "CelHigh", + /*0x1e*/ "AddScreenItem", + /*0x1f*/ "DeleteScreenItem", + /*0x20*/ "UpdateScreenItem", + /*0x21*/ "FrameOut", + /*0x22*/ "CelInfo", + /*0x23*/ "Bitmap", + /*0x24*/ "CelLink", + /*0x25*/ "Dummy", + /*0x26*/ "Dummy", + /*0x27*/ "Dummy", + /*0x28*/ "AddPlane", + /*0x29*/ "DeletePlane", + /*0x2a*/ "UpdatePlane", + /*0x2b*/ "RepaintPlane", + /*0x2c*/ "GetHighPlanePri", + /*0x2d*/ "GetHighItemPri", + /*0x2e*/ "SetShowStyle", + /*0x2f*/ "ShowStylePercent", + /*0x30*/ "SetScroll", + /*0x31*/ "MovePlaneItems", + /*0x32*/ "ShakeScreen", + /*0x33*/ "Dummy", + /*0x34*/ "Dummy", + /*0x35*/ "Dummy", + /*0x36*/ "Dummy", + /*0x37*/ "IsHiRes", + /*0x38*/ "SetVideoMode", + /*0x39*/ "ShowMovie", + /*0x3a*/ "Robot", + /*0x3b*/ "CreateTextBitmap", + /*0x3c*/ "Random", + /*0x3d*/ "Abs", + /*0x3e*/ "Sqrt", + /*0x3f*/ "GetAngle", + /*0x40*/ "GetDistance", + /*0x41*/ "ATan", + /*0x42*/ "SinMult", + /*0x43*/ "CosMult", + /*0x44*/ "SinDiv", + /*0x45*/ "CosDiv", + /*0x46*/ "Text", + /*0x47*/ "Dummy", + /*0x48*/ "Message", + /*0x49*/ "Font", + /*0x4a*/ "EditText", + /*0x4b*/ "InputText", + /*0x4c*/ "ScrollWindow", + /*0x4d*/ "Dummy", + /*0x4e*/ "Dummy", + /*0x4f*/ "Dummy", + /*0x50*/ "GetEvent", + /*0x51*/ "GlobalToLocal", + /*0x52*/ "LocalToGlobal", + /*0x53*/ "MapKeyToDir", + /*0x54*/ "HaveMouse", + /*0x55*/ "SetCursor", + /*0x56*/ "VibrateMouse", + /*0x57*/ "Dummy", + /*0x58*/ "Dummy", + /*0x59*/ "Dummy", + /*0x5a*/ "List", + /*0x5b*/ "Array", + /*0x5c*/ "String", + /*0x5d*/ "FileIO", + /*0x5e*/ "BaseSetter", + /*0x5f*/ "DirLoop", + /*0x60*/ "CantBeHere", + /*0x61*/ "InitBresen", + /*0x62*/ "DoBresen", + /*0x63*/ "SetJump", + /*0x64*/ "AvoidPath", + /*0x65*/ "InPolygon", + /*0x66*/ "MergePoly", + /*0x67*/ "ObjectIntersect", + /*0x68*/ "Dummy", + /*0x69*/ "MemoryInfo", + /*0x6a*/ "DeviceInfo", + /*0x6b*/ "Palette", + /*0x6c*/ "PalVary", + /*0x6d*/ "PalCycle", + /*0x6e*/ "RemapColors", + /*0x6f*/ "AddLine", + /*0x70*/ "DeleteLine", + /*0x71*/ "UpdateLine", + /*0x72*/ "AddPolygon", + /*0x73*/ "DeletePolygon", + /*0x74*/ "UpdatePolygon", + /*0x75*/ "DoSound", + /*0x76*/ "DoAudio", + /*0x77*/ "DoSync", + /*0x78*/ "Save", + /*0x79*/ "GetTime", + /*0x7a*/ "Platform", + /*0x7b*/ "CD", + /*0x7c*/ "SetQuitStr", + /*0x7d*/ "GetConfig", + /*0x7e*/ "Table", + /*0x7f*/ "WinHelp", // Windows only + /*0x80*/ "Dummy", + /*0x81*/ "Dummy", + /*0x82*/ "Dummy", + /*0x83*/ "PrintDebug", // used by Shivers 2 (demo and full) + /*0x84*/ "Dummy", + /*0x85*/ "Dummy", + /*0x86*/ "Dummy", + /*0x87*/ "Dummy", + /*0x88*/ "Dummy", + /*0x89*/ "Dummy", + /*0x8a*/ "LoadChunk", + /*0x8b*/ "SetPalStyleRange", + /*0x8c*/ "AddPicAt", + /*0x8d*/ "Dummy", + /*0x8e*/ "NewRoom", + /*0x8f*/ "Dummy", + /*0x90*/ "Priority", + /*0x91*/ "MorphOn", + /*0x92*/ "PlayVMD", + /*0x93*/ "SetHotRectangles", + /*0x94*/ "MulDiv", + /*0x95*/ "GetSierraProfileInt", // Windows only + /*0x96*/ "GetSierraProfileString", // Windows only + /*0x97*/ "SetWindowsOption", // Windows only + /*0x98*/ "GetWindowsOption", // Windows only + /*0x99*/ "WinDLL", // Windows only + /*0x9a*/ "Dummy", + /*0x9b*/ "Dummy", + /*0x9c*/ "DeletePic" +}; + +#endif + +#define END Script_None + +opcode_format g_opcode_formats[128][4] = { + /*00*/ + {Script_None}, {Script_None}, {Script_None}, {Script_None}, + /*04*/ + {Script_None}, {Script_None}, {Script_None}, {Script_None}, + /*08*/ + {Script_None}, {Script_None}, {Script_None}, {Script_None}, + /*0C*/ + {Script_None}, {Script_None}, {Script_None}, {Script_None}, + /*10*/ + {Script_None}, {Script_None}, {Script_None}, {Script_None}, + /*14*/ + {Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END}, + /*18*/ + {Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None}, + /*1C*/ + {Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END}, + /*20*/ + {Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END}, + /*24 (24=ret)*/ + {Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid}, + /*28*/ + {Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END}, + /*2C*/ + {Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid}, + /*30*/ + {Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, + /*34*/ + {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, + /*38*/ + {Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None}, + /*3C*/ + {Script_None}, {Script_None}, {Script_None}, {Script_Word}, + /*40-4F*/ + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + /*50-5F*/ + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + /*60-6F*/ + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + /*70-7F*/ + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, + {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END} +}; +#undef END + +} // End of namespace Sci + +#endif // SCI_ENGINE_KERNEL_TABLES_H diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index 7547ad5ab6..3395811700 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -50,6 +50,10 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { // Limit the mouse cursor position, if necessary g_sci->_gfxCursor->refreshPosition(); mousePos = g_sci->_gfxCursor->getPosition(); +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2_1) + g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); +#endif // If there's a simkey pending, and the game wants a keyboard event, use the // simkey instead of a normal event @@ -150,11 +154,11 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { } if (g_sci->_features->detectDoSoundType() <= SCI_VERSION_0_LATE) { - // If we're running a SCI0 game, update the sound cues, to compensate - // for the fact that SCI0 does not poll to update the sound cues itself, - // like SCI01 and later do with cmdUpdateSoundCues. kGetEvent is called - // quite often, so emulate the SCI01 behavior of cmdUpdateSoundCues with - // this call + // If we're running a sound-SCI0 game, update the sound cues, to + // compensate for the fact that sound-SCI0 does not poll to update + // the sound cues itself, like sound-SCI1 and later do with + // cmdUpdateSoundCues. kGetEvent is called quite often, so emulate + // the sound-SCI1 behavior of cmdUpdateSoundCues with this call g_sci->_soundCmd->updateSci0Cues(); } @@ -215,7 +219,7 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) { } reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) { - reg_t obj = argc ? argv[0] : NULL_REG; // Can this really happen? Lars + reg_t obj = argv[0]; reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32 SegManager *segMan = s->_segMan; @@ -234,7 +238,7 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) { } reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) { - reg_t obj = argc ? argv[0] : NULL_REG; // Can this really happen? Lars + reg_t obj = argv[0]; reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32 SegManager *segMan = s->_segMan; diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index 807edc63a5..d4ba467b25 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -98,7 +98,7 @@ enum { -void file_open(EngineState *s, const char *filename, int mode) { +reg_t file_open(EngineState *s, const char *filename, int mode) { // QfG3 character import prepends /\ to the filenames. if (filename[0] == '/' && filename[1] == '\\') filename += 2; @@ -132,17 +132,17 @@ void file_open(EngineState *s, const char *filename, int mode) { } if (!inFile) - warning(" -> file_open(_K_FILE_MODE_OPEN_OR_FAIL): failed to open file '%s'", englishName.c_str()); + debugC(2, kDebugLevelFile, " -> file_open(_K_FILE_MODE_OPEN_OR_FAIL): failed to open file '%s'", englishName.c_str()); } else if (mode == _K_FILE_MODE_CREATE) { // Create the file, destroying any content it might have had outFile = saveFileMan->openForSaving(wrappedName); if (!outFile) - warning(" -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str()); + debugC(2, kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str()); } else if (mode == _K_FILE_MODE_OPEN_OR_CREATE) { // Try to open file, create it if it doesn't exist outFile = saveFileMan->openForSaving(wrappedName); if (!outFile) - warning(" -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str()); + debugC(2, kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str()); // QfG1 opens the character export file with _K_FILE_MODE_CREATE first, // closes it immediately and opens it again with this here. Perhaps // other games use this for read access as well. I guess changing this @@ -154,8 +154,7 @@ void file_open(EngineState *s, const char *filename, int mode) { if (!inFile && !outFile) { // Failed debugC(2, kDebugLevelFile, " -> file_open() failed"); - s->r_acc = SIGNAL_REG; - return; + return SIGNAL_REG; } // Find a free file handle @@ -172,9 +171,8 @@ void file_open(EngineState *s, const char *filename, int mode) { s->_fileHandles[handle]._out = outFile; s->_fileHandles[handle]._name = englishName; - s->r_acc = make_reg(0, handle); - debugC(2, kDebugLevelFile, " -> opened file '%s' with handle %d", englishName.c_str(), handle); + return make_reg(0, handle); } reg_t kFOpen(EngineState *s, int argc, reg_t *argv) { @@ -182,8 +180,7 @@ reg_t kFOpen(EngineState *s, int argc, reg_t *argv) { int mode = argv[1].toUint16(); debugC(2, kDebugLevelFile, "kFOpen(%s,0x%x)", name.c_str(), mode); - file_open(s, name.c_str(), mode); - return s->r_acc; + return file_open(s, name.c_str(), mode); } static FileHandle *getFileFromHandle(EngineState *s, uint handle) { @@ -368,11 +365,25 @@ reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) { } reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) { -#ifdef ENABLE_SCI32 - // TODO: SCI32 uses a parameter here. - if (argc > 1) - warning("kCheckFreeSpace called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[1])); -#endif + if (argc > 1) { + // SCI1.1/SCI32 + // TODO: don't know if those are right for SCI32 as well + // Please note that sierra sci supported both calls either w/ or w/o opcode in SCI1.1 + switch (argv[1].toUint16()) { + case 0: // return saved game size + return make_reg(0, 0); // we return 0 + + case 1: // return free harddisc space (shifted right somehow) + return make_reg(0, 0x7fff); // we return maximum + + case 2: // same as call w/o opcode + break; + return make_reg(0, 1); + + default: + error("kCheckFreeSpace: called with unknown sub-op %d", argv[1].toUint16()); + } + } Common::String path = s->_segMan->getString(argv[0]); @@ -534,6 +545,12 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) { debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), virtualId, game_description.c_str(), version.c_str()); + // We check here, we don't want to delete a users save in case we are within a kernel function + if (s->executionStackBase) { + warning("kSaveGame - won't save from within kernel function"); + return NULL_REG; + } + Common::Array<SavegameDesc> saves; listSavegames(saves); @@ -541,7 +558,7 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) { if ((virtualId >= SAVEGAMEID_OFFICIALRANGE_START) && (virtualId <= SAVEGAMEID_OFFICIALRANGE_END)) { // savegameId is an actual Id, so search for it just to make sure savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START; - if (findSavegame(saves, savegameId) != -1) + if (findSavegame(saves, savegameId) == -1) return NULL_REG; } else if (virtualId < SAVEGAMEID_OFFICIALRANGE_START) { // virtualId is low, we assume that scripts expect us to create new slot @@ -603,11 +620,15 @@ reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) { debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savegameId); - if ((savegameId < 1000) || (savegameId > 1999)) { - warning("Savegame ID %d is not allowed", savegameId); - return TRUE_REG; + if (argv[0].isNull()) { + // Loading from the launcher, don't adjust the ID of the saved game + } else { + if ((savegameId < 1000) || (savegameId > 1999)) { + warning("Savegame ID %d is not allowed", savegameId); + return TRUE_REG; + } + savegameId -= 1000; } - savegameId -= 1000; Common::Array<SavegameDesc> saves; listSavegames(saves); @@ -643,28 +664,6 @@ reg_t kValidPath(EngineState *s, int argc, reg_t *argv) { return make_reg(0, 1); } -enum { - K_FILEIO_OPEN = 0, - K_FILEIO_CLOSE = 1, - K_FILEIO_READ_RAW = 2, - K_FILEIO_WRITE_RAW = 3, - K_FILEIO_UNLINK = 4, - K_FILEIO_READ_STRING = 5, - K_FILEIO_WRITE_STRING = 6, - K_FILEIO_SEEK = 7, - K_FILEIO_FIND_FIRST = 8, - K_FILEIO_FIND_NEXT = 9, - K_FILEIO_FILE_EXISTS = 10, - // SCI1.1 - K_FILEIO_RENAME = 11, - // SCI32 - // 12? - K_FILEIO_READ_BYTE = 13, - K_FILEIO_WRITE_BYTE = 14, - K_FILEIO_READ_WORD = 15, - K_FILEIO_WRITE_WORD = 16 -}; - reg_t DirSeeker::firstFile(const Common::String &mask, reg_t buffer, SegManager *segMan) { // Verify that we are given a valid buffer if (!buffer.segment) { @@ -705,263 +704,298 @@ reg_t DirSeeker::nextFile(SegManager *segMan) { } reg_t kFileIO(EngineState *s, int argc, reg_t *argv) { - int func_nr = argv[0].toUint16(); + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} - switch (func_nr) { - case K_FILEIO_OPEN : { - Common::String name = s->_segMan->getString(argv[1]); +reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) { + Common::String name = s->_segMan->getString(argv[0]); - // SCI32 can call K_FILEIO_OPEN with only two arguments. It seems to - // just be checking if it exists. - int mode = (argc < 3) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[2].toUint16(); + // SCI32 can call K_FILEIO_OPEN with only one argument. It seems to + // just be checking if it exists. + int mode = (argc < 2) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[1].toUint16(); - // SQ4 floppy prepends /\ to the filenames - if (name.hasPrefix("/\\")) { - name.deleteChar(0); - name.deleteChar(0); - } + // SQ4 floppy prepends /\ to the filenames + if (name.hasPrefix("/\\")) { + name.deleteChar(0); + name.deleteChar(0); + } - // SQ4 floppy attempts to update the savegame index file sq4sg.dir when - // deleting saved games. We don't use an index file for saving or - // loading, so just stop the game from modifying the file here in order - // to avoid having it saved in the ScummVM save directory. - if (name == "sq4sg.dir") { - debugC(2, kDebugLevelFile, "Not opening unused file sq4sg.dir"); - return SIGNAL_REG; - } + // SQ4 floppy attempts to update the savegame index file sq4sg.dir when + // deleting saved games. We don't use an index file for saving or + // loading, so just stop the game from modifying the file here in order + // to avoid having it saved in the ScummVM save directory. + if (name == "sq4sg.dir") { + debugC(2, kDebugLevelFile, "Not opening unused file sq4sg.dir"); + return SIGNAL_REG; + } - if (name.empty()) { - warning("Attempted to open a file with an empty filename"); - return SIGNAL_REG; - } - debugC(2, kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode); - file_open(s, name.c_str(), mode); - break; + if (name.empty()) { + warning("Attempted to open a file with an empty filename"); + return SIGNAL_REG; } - case K_FILEIO_CLOSE : { - debugC(2, kDebugLevelFile, "kFileIO(close): %d", argv[1].toUint16()); + debugC(2, kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode); + return file_open(s, name.c_str(), mode); +} - FileHandle *f = getFileFromHandle(s, argv[1].toUint16()); - if (f) - f->close(); - break; +reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) { + debugC(2, kDebugLevelFile, "kFileIO(close): %d", argv[0].toUint16()); + + FileHandle *f = getFileFromHandle(s, argv[0].toUint16()); + if (f) { + f->close(); + return SIGNAL_REG; } - case K_FILEIO_READ_RAW : { - int handle = argv[1].toUint16(); - int size = argv[3].toUint16(); - char *buf = new char[size]; - debugC(2, kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size); + return NULL_REG; +} + +reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv) { + int handle = argv[0].toUint16(); + int size = argv[2].toUint16(); + int bytesRead = 0; + char *buf = new char[size]; + debugC(2, kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size); - FileHandle *f = getFileFromHandle(s, handle); - if (f) { - s->r_acc = make_reg(0, f->_in->read(buf, size)); - s->_segMan->memcpy(argv[2], (const byte*)buf, size); - } + FileHandle *f = getFileFromHandle(s, handle); + if (f) { + bytesRead = f->_in->read(buf, size); + s->_segMan->memcpy(argv[1], (const byte*)buf, size); + } - delete[] buf; - break; + delete[] buf; + return make_reg(0, bytesRead); +} + +reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) { + int handle = argv[0].toUint16(); + int size = argv[2].toUint16(); + char *buf = new char[size]; + bool success = false; + s->_segMan->memcpy((byte*)buf, argv[1], size); + debugC(2, kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size); + + FileHandle *f = getFileFromHandle(s, handle); + if (f) { + f->_out->write(buf, size); + success = true; } - case K_FILEIO_WRITE_RAW : { - int handle = argv[1].toUint16(); - int size = argv[3].toUint16(); - char *buf = new char[size]; - s->_segMan->memcpy((byte*)buf, argv[2], size); - debugC(2, kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size); - FileHandle *f = getFileFromHandle(s, handle); - if (f) - f->_out->write(buf, size); + delete[] buf; + if (success) + return NULL_REG; + return make_reg(0, 6); // DOS - invalid handle +} - delete[] buf; - break; +reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) { + Common::String name = s->_segMan->getString(argv[0]); + Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); + bool result; + + // SQ4 floppy prepends /\ to the filenames + if (name.hasPrefix("/\\")) { + name.deleteChar(0); + name.deleteChar(0); + } + + // Special case for SQ4 floppy: This game has hardcoded names for all of + // its savegames, and they are all named "sq4sg.xxx", where xxx is the + // slot. We just take the slot number here, and delete the appropriate + // save game. + if (name.hasPrefix("sq4sg.")) { + // Special handling for SQ4... get the slot number and construct the + // save game name. + int slotNum = atoi(name.c_str() + name.size() - 3); + Common::Array<SavegameDesc> saves; + listSavegames(saves); + int savedir_nr = saves[slotNum].id; + name = g_sci->getSavegameName(savedir_nr); + result = saveFileMan->removeSavefile(name); + } else { + const Common::String wrappedName = g_sci->wrapFilename(name); + result = saveFileMan->removeSavefile(wrappedName); } - case K_FILEIO_UNLINK : { - Common::String name = s->_segMan->getString(argv[1]); - Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); - // SQ4 floppy prepends /\ to the filenames - if (name.hasPrefix("/\\")) { - name.deleteChar(0); - name.deleteChar(0); - } - // Special case for SQ4 floppy: This game has hardcoded names for all of - // its savegames, and they are all named "sq4sg.xxx", where xxx is the - // slot. We just take the slot number here, and delete the appropriate - // save game. - if (name.hasPrefix("sq4sg.")) { - // Special handling for SQ4... get the slot number and construct the - // save game name. - int slotNum = atoi(name.c_str() + name.size() - 3); - Common::Array<SavegameDesc> saves; - listSavegames(saves); - int savedir_nr = saves[slotNum].id; - name = g_sci->getSavegameName(savedir_nr); - saveFileMan->removeSavefile(name); - } else { - const Common::String wrappedName = g_sci->wrapFilename(name); - saveFileMan->removeSavefile(wrappedName); - } + debugC(2, kDebugLevelFile, "kFileIO(unlink): %s", name.c_str()); + if (result) + return NULL_REG; + return make_reg(0, 2); // DOS - file not found error code +} - debugC(2, kDebugLevelFile, "kFileIO(unlink): %s", name.c_str()); +reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv) { + int size = argv[1].toUint16(); + char *buf = new char[size]; + int handle = argv[2].toUint16(); + debugC(2, kDebugLevelFile, "kFileIO(readString): %d, %d", handle, size); - // TODO/FIXME: Should we return something (like, a bool indicating - // whether deleting the save succeeded or failed)? - break; - } - case K_FILEIO_READ_STRING : { - int size = argv[2].toUint16(); - char *buf = new char[size]; - int handle = argv[3].toUint16(); - debugC(2, kDebugLevelFile, "kFileIO(readString): %d, %d", handle, size); + fgets_wrapper(s, buf, size, handle); + s->_segMan->memcpy(argv[0], (const byte*)buf, size); + delete[] buf; + return argv[0]; +} - fgets_wrapper(s, buf, size, handle); - s->_segMan->memcpy(argv[1], (const byte*)buf, size); - delete[] buf; - return argv[1]; - } - case K_FILEIO_WRITE_STRING : { - int handle = argv[1].toUint16(); - int size = argv[3].toUint16(); - Common::String str = s->_segMan->getString(argv[2]); - debugC(2, kDebugLevelFile, "kFileIO(writeString): %d, %d", handle, size); - - // CHECKME: Is the size parameter used at all? - // In the LSL5 password protection it is zero, and we should - // then write a full string. (Not sure if it should write the - // terminating zero.) - - FileHandle *f = getFileFromHandle(s, handle); - if (f) - f->_out->write(str.c_str(), str.size()); - break; - } - case K_FILEIO_SEEK : { - int handle = argv[1].toUint16(); - int offset = argv[2].toUint16(); - int whence = argv[3].toUint16(); - debugC(2, kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence); +reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) { + int handle = argv[0].toUint16(); + Common::String str = s->_segMan->getString(argv[1]); + debugC(2, kDebugLevelFile, "kFileIO(writeString): %d", handle); + + FileHandle *f = getFileFromHandle(s, handle); + if (f) + f->_out->write(str.c_str(), str.size()); + return NULL_REG; + return make_reg(0, 6); // DOS - invalid handle +} + +reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv) { + int handle = argv[0].toUint16(); + int offset = argv[1].toUint16(); + int whence = argv[2].toUint16(); + debugC(2, kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence); - FileHandle *f = getFileFromHandle(s, handle); - if (f) - s->r_acc = make_reg(0, f->_in->seek(offset, whence)); - break; - } - case K_FILEIO_FIND_FIRST : { - Common::String mask = s->_segMan->getString(argv[1]); - reg_t buf = argv[2]; - int attr = argv[3].toUint16(); // We won't use this, Win32 might, though... - debugC(2, kDebugLevelFile, "kFileIO(findFirst): %s, 0x%x", mask.c_str(), attr); - - // We remove ".*". mask will get prefixed, so we will return all additional files for that gameid - if (mask == "*.*") - mask = "*"; - - // QfG3 uses this mask for the character import - if (mask == "/\\*.*") - mask = "*"; -//#ifndef WIN32 -// if (mask == "*.*") -// mask = "*"; // For UNIX -//#endif - s->r_acc = s->_dirseeker.firstFile(mask, buf, s->_segMan); + FileHandle *f = getFileFromHandle(s, handle); + if (f) + s->r_acc = make_reg(0, f->_in->seek(offset, whence)); + return SIGNAL_REG; +} - break; - } - case K_FILEIO_FIND_NEXT : { - debugC(2, kDebugLevelFile, "kFileIO(findNext)"); - s->r_acc = s->_dirseeker.nextFile(s->_segMan); - break; +reg_t kFileIOFindFirst(EngineState *s, int argc, reg_t *argv) { + Common::String mask = s->_segMan->getString(argv[0]); + reg_t buf = argv[1]; + int attr = argv[2].toUint16(); // We won't use this, Win32 might, though... + debugC(2, kDebugLevelFile, "kFileIO(findFirst): %s, 0x%x", mask.c_str(), attr); + + // QfG3 uses "/\*.*" for the character import, QfG4 uses "/\*" + if (mask.hasPrefix("/\\")) { + mask.deleteChar(0); + mask.deleteChar(0); } - case K_FILEIO_FILE_EXISTS : { - Common::String name = s->_segMan->getString(argv[1]); - // Check for regular file - bool exists = Common::File::exists(name); - Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); - const Common::String wrappedName = g_sci->wrapFilename(name); + // We remove ".*". mask will get prefixed, so we will return all additional files for that gameid + if (mask == "*.*") + mask = "*"; + return s->_dirseeker.firstFile(mask, buf, s->_segMan); +} - if (!exists) - exists = !saveFileMan->listSavefiles(name).empty(); +reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv) { + debugC(2, kDebugLevelFile, "kFileIO(findNext)"); + return s->_dirseeker.nextFile(s->_segMan); +} - if (!exists) { - // Try searching for the file prepending target- - Common::SeekableReadStream *inFile = saveFileMan->openForLoading(wrappedName); - exists = (inFile != 0); - delete inFile; - } +reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) { + Common::String name = s->_segMan->getString(argv[0]); - // Special case for non-English versions of LSL5: The English version of - // LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if - // memory.drv exists (which is where the game's password is stored). If - // it's not found, it calls kFileIO() again, case K_FILEIO_OPEN for - // writing and creates a new file. Non-English versions call kFileIO(), - // case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be - // found. We create a default memory.drv file with no password, so that - // the game can continue. - if (!exists && name == "memory.drv") { - // Create a new file, and write the bytes for the empty password - // string inside - byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 }; - Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName); - for (int i = 0; i < 10; i++) - outFile->writeByte(defaultContent[i]); - outFile->finalize(); - delete outFile; - exists = true; - } + // Check for regular file + bool exists = Common::File::exists(name); + Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); + const Common::String wrappedName = g_sci->wrapFilename(name); + + if (!exists) + exists = !saveFileMan->listSavefiles(name).empty(); + + if (!exists) { + // Try searching for the file prepending target- + Common::SeekableReadStream *inFile = saveFileMan->openForLoading(wrappedName); + exists = (inFile != 0); + delete inFile; + } + + // Special case for non-English versions of LSL5: The English version of + // LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if + // memory.drv exists (which is where the game's password is stored). If + // it's not found, it calls kFileIO() again, case K_FILEIO_OPEN for + // writing and creates a new file. Non-English versions call kFileIO(), + // case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be + // found. We create a default memory.drv file with no password, so that + // the game can continue. + if (!exists && name == "memory.drv") { + // Create a new file, and write the bytes for the empty password + // string inside + byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 }; + Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName); + for (int i = 0; i < 10; i++) + outFile->writeByte(defaultContent[i]); + outFile->finalize(); + delete outFile; + exists = true; + } + + debugC(2, kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists); + return make_reg(0, exists); +} - debugC(2, kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists); - return make_reg(0, exists); - } - case K_FILEIO_RENAME: { - Common::String oldName = s->_segMan->getString(argv[1]); - Common::String newName = s->_segMan->getString(argv[2]); +reg_t kFileIORename(EngineState *s, int argc, reg_t *argv) { + Common::String oldName = s->_segMan->getString(argv[0]); + Common::String newName = s->_segMan->getString(argv[1]); + + // SCI1.1 returns 0 on success and a DOS error code on fail. SCI32 + // returns -1 on fail. We just return -1 for all versions. + if (g_engine->getSaveFileManager()->renameSavefile(oldName, newName)) + return NULL_REG; + else + return SIGNAL_REG; +} - // SCI1.1 returns 0 on success and a DOS error code on fail. SCI32 - // returns -1 on fail. We just return -1 for all versions. - if (g_engine->getSaveFileManager()->renameSavefile(oldName, newName)) - return NULL_REG; - else - return SIGNAL_REG; - } #ifdef ENABLE_SCI32 - case K_FILEIO_READ_BYTE: { - // Read the byte into the low byte of the accumulator - FileHandle *f = getFileFromHandle(s, argv[1].toUint16()); - if (!f) - return NULL_REG; - - return make_reg(0, (s->r_acc.toUint16() & 0xff00) | f->_in->readByte()); - } - case K_FILEIO_WRITE_BYTE: { - FileHandle *f = getFileFromHandle(s, argv[1].toUint16()); - if (f) - f->_out->writeByte(argv[2].toUint16() & 0xff); - break; - } - case K_FILEIO_READ_WORD: { - FileHandle *f = getFileFromHandle(s, argv[1].toUint16()); - if (!f) - return NULL_REG; - - return make_reg(0, f->_in->readUint16LE()); - } - case K_FILEIO_WRITE_WORD: { - FileHandle *f = getFileFromHandle(s, argv[1].toUint16()); - if (f) - f->_out->writeUint16LE(argv[2].toUint16()); - break; +reg_t kFileIOReadByte(EngineState *s, int argc, reg_t *argv) { + // Read the byte into the low byte of the accumulator + FileHandle *f = getFileFromHandle(s, argv[0].toUint16()); + if (!f) + return NULL_REG; + return make_reg(0, (s->r_acc.toUint16() & 0xff00) | f->_in->readByte()); +} + +reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv) { + FileHandle *f = getFileFromHandle(s, argv[0].toUint16()); + if (f) + f->_out->writeByte(argv[1].toUint16() & 0xff); + return s->r_acc; // FIXME: does this really doesn't return anything? +} + +reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv) { + FileHandle *f = getFileFromHandle(s, argv[0].toUint16()); + if (!f) + return NULL_REG; + return make_reg(0, f->_in->readUint16LE()); +} + +reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv) { + FileHandle *f = getFileFromHandle(s, argv[0].toUint16()); + if (f) + f->_out->writeUint16LE(argv[1].toUint16()); + return s->r_acc; // FIXME: does this really doesn't return anything? +} + +reg_t kCD(EngineState *s, int argc, reg_t *argv) { + // TODO: Stub + switch (argv[0].toUint16()) { + case 0: + // Return whether the contents of disc argv[1] is available. + return TRUE_REG; + default: + warning("CD(%d)", argv[0].toUint16()); } - case 19: - // TODO: Torin's Passage uses this early on in the Sierra logo - warning("kFileIO(19)"); - break; -#endif + + return NULL_REG; +} + +reg_t kSave(EngineState *s, int argc, reg_t *argv) { + switch (argv[0].toUint16()) { + case 0: // Called by kq7 when starting chapters + return SIGNAL_REG; + case 2: // GetSaveDir + // Yay! Reusing the old kernel function! + return kGetSaveDir(s, argc - 1, argv + 1); + case 8: + // TODO + // This function has to return something other than 0 to proceed + return s->r_acc; default: - error("kFileIO(): unknown sub-command: %d", func_nr); + warning("Unknown/unhandled kSave subop %d", argv[0].toUint16()); } - return s->r_acc; + return NULL_REG; } +#endif + } // End of namespace Sci diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 29f7565ef9..56518f10bf 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -23,16 +23,15 @@ * */ +#include "common/system.h" + #include "engines/util.h" #include "graphics/cursorman.h" -#include "graphics/video/avi_decoder.h" -#include "graphics/video/qt_decoder.h" #include "graphics/surface.h" #include "sci/sci.h" #include "sci/debug.h" // for g_debug_sleeptime_factor #include "sci/resource.h" -#include "sci/video/seq_decoder.h" #include "sci/engine/features.h" #include "sci/engine/state.h" #include "sci/engine/selector.h" @@ -44,12 +43,14 @@ #include "sci/graphics/cursor.h" #include "sci/graphics/palette.h" #include "sci/graphics/paint16.h" +#include "sci/graphics/picture.h" #include "sci/graphics/ports.h" +#include "sci/graphics/robot.h" #include "sci/graphics/screen.h" #include "sci/graphics/text16.h" #include "sci/graphics/view.h" #ifdef ENABLE_SCI32 -#include "sci/video/vmd_decoder.h" +#include "sci/graphics/frameout.h" #endif namespace Sci { @@ -129,9 +130,10 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) { break; case -1: // TODO: Special case at least in kq6, check disassembly + // Does something with magCursor, which is set on argc = 10, which we don't support break; case -2: - // TODO: Special case at least in kq6, check disassembly + g_sci->_gfxCursor->kernelResetMoveZone(); break; default: g_sci->_gfxCursor->kernelShow(); @@ -145,14 +147,22 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) { g_sci->_gfxCursor->kernelSetPos(pos); break; case 4: { - int16 top = argv[0].toSint16(); - int16 left = argv[1].toSint16(); - int16 bottom = argv[2].toSint16(); - int16 right = argv[3].toSint16(); + int16 top, left, bottom, right; - // In SCI32, the right parameter seems to be divided by 2 - if (getSciVersion() >= SCI_VERSION_2) - right *= 2; + if (getSciVersion() >= SCI_VERSION_2) { + top = argv[1].toSint16(); + left = argv[0].toSint16(); + bottom = argv[3].toSint16(); + right = argv[2].toSint16(); + } else { + top = argv[0].toSint16(); + left = argv[1].toSint16(); + bottom = argv[2].toSint16(); + right = argv[3].toSint16(); + } + // bottom/right needs to be included into our movezone, because we compare it like any regular Common::Rect + bottom++; + right++; if ((right >= left) && (bottom >= top)) { Common::Rect rect = Common::Rect(left, top, right, bottom); @@ -250,7 +260,7 @@ reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv) { reg_t kGraphSaveBox(EngineState *s, int argc, reg_t *argv) { Common::Rect rect = getGraphRect(argv); - uint16 screenMask = (argc > 4) ? argv[4].toUint16() : 0; + uint16 screenMask = argv[4].toUint16() & GFX_SCREEN_MASK_ALL; return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask); } @@ -382,17 +392,16 @@ reg_t kCanBeHere(EngineState *s, int argc, reg_t *argv) { reg_t curObject = argv[0]; reg_t listReference = (argc > 1) ? argv[1] : NULL_REG; - bool canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference); - return make_reg(0, canBeHere); + reg_t canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference); + return make_reg(0, canBeHere.isNull() ? 1 : 0); } -// kCantBeHere does the same thing as kCanBeHere, except that it returns the opposite result. reg_t kCantBeHere(EngineState *s, int argc, reg_t *argv) { reg_t curObject = argv[0]; reg_t listReference = (argc > 1) ? argv[1] : NULL_REG; - bool canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference); - return make_reg(0, !canBeHere); + reg_t canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference); + return canBeHere; } reg_t kIsItSkip(EngineState *s, int argc, reg_t *argv) { @@ -518,14 +527,6 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) { reg_t object = argv[0]; g_sci->_gfxCompare->kernelBaseSetter(object); - - // WORKAROUND for a problem in LSL1VGA. This allows the casino door to be opened, - // till the actual problem is found - if (s->currentRoomNumber() == 300 && g_sci->getGameId() == GID_LSL1) { - int top = readSelectorValue(s->_segMan, object, SELECTOR(brTop)); - writeSelectorValue(s->_segMan, object, SELECTOR(brTop), top + 2); - } - return s->r_acc; } @@ -615,16 +616,16 @@ reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv) { reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv) { if (g_sci->getResMan()->isVGA()) { - warning("kPalette(7), save palette to heap STUB"); + return g_sci->_gfxPalette->kernelSave(); } return NULL_REG; } reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv) { if (g_sci->getResMan()->isVGA()) { - warning("kPalette(8), restore palette from heap STUB"); + g_sci->_gfxPalette->kernelRestore(argv[0]); } - return s->r_acc; + return argv[0]; } reg_t kPalVary(EngineState *s, int argc, reg_t *argv) { @@ -889,8 +890,8 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) { reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text)); if (!textReference.isNull()) { Common::String text = s->_segMan->getString(textReference); - if (text == "a:hq1_hero.sav") { - // Remove "a:" from hero quest export default filename + if ((text == "a:hq1_hero.sav") || (text == "a:glory1.sav") || (text == "a:glory2.sav") || (text == "a:glory3.sav")) { + // Remove "a:" from hero quest / quest for glory export default filenames text.deleteChar(0); text.deleteChar(0); s->_segMan->strcpy(textReference, text.c_str()); @@ -974,14 +975,13 @@ reg_t kSetPort(EngineState *s, int argc, reg_t *argv) { case 7: initPriorityBandsFlag = true; - case 4: case 6: picRect.top = argv[0].toSint16(); picRect.left = argv[1].toSint16(); picRect.bottom = argv[2].toSint16(); picRect.right = argv[3].toSint16(); - picTop = (argc >= 6) ? argv[4].toSint16() : 0; - picLeft = (argc >= 6) ? argv[5].toSint16() : 0; + picTop = argv[4].toSint16(); + picLeft = argv[5].toSint16(); g_sci->_gfxPorts->kernelSetPicWindow(picRect, picTop, picLeft, initPriorityBandsFlag); break; @@ -1000,10 +1000,26 @@ reg_t kDrawCel(EngineState *s, int argc, reg_t *argv) { uint16 y = argv[4].toUint16(); int16 priority = (argc > 5) ? argv[5].toSint16() : -1; uint16 paletteNo = (argc > 6) ? argv[6].toUint16() : 0; - bool hiresMode = (argc > 7) ? true : false; - reg_t upscaledHiresHandle = (argc > 7) ? argv[7] : NULL_REG; + bool hiresMode = false; + reg_t upscaledHiresHandle = NULL_REG; + uint16 scaleX = 128; + uint16 scaleY = 128; + + if (argc > 7) { + // this is either kq6 hires or scaling + if (paletteNo > 0) { + // it's scaling + scaleX = argv[6].toUint16(); + scaleY = argv[7].toUint16(); + paletteNo = 0; + } else { + // KQ6 hires + hiresMode = true; + upscaledHiresHandle = argv[7]; + } + } - g_sci->_gfxPaint16->kernelDrawCel(viewId, loopNo, celNo, x, y, priority, paletteNo, hiresMode, upscaledHiresHandle); + g_sci->_gfxPaint16->kernelDrawCel(viewId, loopNo, celNo, x, y, priority, paletteNo, scaleX, scaleY, hiresMode, upscaledHiresHandle); return s->r_acc; } @@ -1075,153 +1091,264 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) { return g_sci->_gfxPaint16->kernelDisplay(g_sci->strSplit(text.c_str()).c_str(), argc, argv); } -void playVideo(Graphics::VideoDecoder *videoDecoder) { - if (!videoDecoder) - return; +reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) { + // This call is used for KQ6's intro. It has one parameter, which is 1 when + // the intro begins, and 0 when it ends. It is suspected that this is + // actually a flag to enable video planar memory access, as the video + // decoder in KQ6 is specifically written for the planar memory model. + // Planar memory mode access was used for VGA "Mode X" (320x240 resolution, + // although the intro in KQ6 is 320x200). + // Refer to http://en.wikipedia.org/wiki/Mode_X - byte *scaleBuffer = 0; - uint16 width = videoDecoder->getWidth(); - uint16 height = videoDecoder->getHeight(); - uint16 screenWidth = g_system->getWidth(); - uint16 screenHeight = g_system->getHeight(); - - if (screenWidth == 640 && width <= 320 && height <= 240) { - assert(videoDecoder->getPixelFormat().bytesPerPixel == 1); - width *= 2; - height *= 2; - scaleBuffer = new byte[width * height]; - } + //warning("STUB: SetVideoMode %d", argv[0].toUint16()); + return s->r_acc; +} - uint16 x = (screenWidth - width) / 2; - uint16 y = (screenHeight - height) / 2; - bool skipVideo = false; - - while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { - if (videoDecoder->needsUpdate()) { - Graphics::Surface *frame = videoDecoder->decodeNextFrame(); - if (frame) { - if (scaleBuffer) { - // TODO: Probably should do aspect ratio correction in e.g. GK1 Windows - g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight()); - g_system->copyRectToScreen(scaleBuffer, width, x, y, width, height); - } else - g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, width, height); - - if (videoDecoder->hasDirtyPalette()) - videoDecoder->setSystemPalette(); - - g_system->updateScreen(); - } - } +// New calls for SCI11. Using those is only needed when using text-codes so that +// one is able to change font and/or color multiple times during kDisplay and +// kDrawControl +reg_t kTextFonts(EngineState *s, int argc, reg_t *argv) { + g_sci->_gfxText16->kernelTextFonts(argc, argv); + return s->r_acc; +} - Common::Event event; - while (g_system->getEventManager()->pollEvent(event)) { - if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) - skipVideo = true; - } +reg_t kTextColors(EngineState *s, int argc, reg_t *argv) { + g_sci->_gfxText16->kernelTextColors(argc, argv); + return s->r_acc; +} - g_system->delayMillis(10); - } +#ifdef ENABLE_SCI32 - delete[] scaleBuffer; - delete videoDecoder; +reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) { + // Returns 0 if the screen width or height is less than 640 or 400, + // respectively. + if (g_system->getWidth() < 640 || g_system->getHeight() < 400) + return make_reg(0, 0); + + return make_reg(0, 1); } -reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { - // Hide the cursor if it's showing and then show it again if it was - // previously visible. - bool reshowCursor = g_sci->_gfxCursor->isVisible(); - if (reshowCursor) - g_sci->_gfxCursor->kernelHide(); +// SCI32 variant, can't work like sci16 variants +reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) { +// reg_t curObject = argv[0]; +// reg_t listReference = (argc > 1) ? argv[1] : NULL_REG; + + return NULL_REG; +} - uint16 screenWidth = g_system->getWidth(); - uint16 screenHeight = g_system->getHeight(); - - Graphics::VideoDecoder *videoDecoder = 0; +reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) { + reg_t viewObj = argv[0]; - if (argv[0].segment != 0) { - Common::String filename = s->_segMan->getString(argv[0]); + g_sci->_gfxFrameout->kernelAddScreenItem(viewObj); + return NULL_REG; +} - if (g_sci->getPlatform() == Common::kPlatformMacintosh) { - // Mac QuickTime - // The only argument is the string for the video +reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) { + //reg_t viewObj = argv[0]; - // HACK: Switch to 16bpp graphics for Cinepak. - initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL); + //warning("kUpdateScreenItem, object %04x:%04x, view %d, loop %d, cel %d, pri %d", PRINT_REG(viewObj), viewId, loopNo, celNo, priority); + return NULL_REG; +} - if (g_system->getScreenFormat().bytesPerPixel == 1) { - error("This video requires >8bpp color to be displayed, but could not switch to RGB color mode."); - return NULL_REG; - } +reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) { + reg_t viewObj = argv[0]; - videoDecoder = new Graphics::QuickTimeDecoder(); - if (!videoDecoder->loadFile(filename)) - error("Could not open '%s'", filename.c_str()); - } else { - // DOS SEQ - // SEQ's are called with no subops, just the string and delay - SeqDecoder *seqDecoder = new SeqDecoder(); - seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks - videoDecoder = seqDecoder; - - if (!videoDecoder->loadFile(filename)) { - warning("Failed to open movie file %s", filename.c_str()); - delete videoDecoder; - videoDecoder = 0; - } - } - } else { - // Windows AVI - // TODO: This appears to be some sort of subop. case 0 contains the string - // for the video, so we'll just play it from there for now. + g_sci->_gfxFrameout->kernelDeleteScreenItem(viewObj); -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2_1) { - // SCI2.1 always has argv[0] as 1, the rest of the arguments seem to - // follow SCI1.1/2. - if (argv[0].toUint16() != 1) - error("SCI2.1 kShowMovie argv[0] not 1"); - argv++; - argc--; - } -#endif - switch (argv[0].toUint16()) { - case 0: { - Common::String filename = s->_segMan->getString(argv[1]); - videoDecoder = new Graphics::AviDecoder(g_system->getMixer()); - - if (!videoDecoder->loadFile(filename.c_str())) { - warning("Failed to open movie file %s", filename.c_str()); - delete videoDecoder; - videoDecoder = 0; - } - break; - } - default: - warning("Unhandled SCI kShowMovie subop %d", argv[1].toUint16()); - } + /* + reg_t viewObj = argv[0]; + uint16 viewId = readSelectorValue(s->_segMan, viewObj, SELECTOR(view)); + int16 loopNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(loop)); + int16 celNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(cel)); + //int16 leftPos = 0; + //int16 topPos = 0; + int16 priority = readSelectorValue(s->_segMan, viewObj, SELECTOR(priority)); + //int16 control = 0; + */ + + // TODO + + //warning("kDeleteScreenItem, view %d, loop %d, cel %d, pri %d", viewId, loopNo, celNo, priority); + return NULL_REG; +} + +reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) { + reg_t planeObj = argv[0]; + + g_sci->_gfxFrameout->kernelAddPlane(planeObj); + return NULL_REG; +} + +reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) { + reg_t planeObj = argv[0]; + + g_sci->_gfxFrameout->kernelDeletePlane(planeObj); + return NULL_REG; +} + +reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) { + reg_t planeObj = argv[0]; + + g_sci->_gfxFrameout->kernelUpdatePlane(planeObj); + return s->r_acc; +} + +reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv) { + reg_t picObj = argv[0]; + + // TODO + + warning("kRepaintPlane object %04x:%04x", PRINT_REG(picObj)); + return NULL_REG; +} + +reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) { + reg_t planeObj = argv[0]; + GuiResourceId pictureId = argv[1].toUint16(); + int16 forWidth = argv[2].toSint16(); + // argv[3] seems to be 0 most of the time + + g_sci->_gfxFrameout->kernelAddPicAt(planeObj, forWidth, pictureId); + return s->r_acc; +} + +reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, g_sci->_gfxFrameout->kernelGetHighPlanePri()); +} + +reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) { + // This kernel call likely seems to be doing the screen updates, + // as its called right after a view is updated + + // TODO + g_sci->_gfxFrameout->kernelFrameout(); + + return NULL_REG; +} + +reg_t kOnMe(EngineState *s, int argc, reg_t *argv) { + // Tests if the cursor is on the passed object + + uint16 x = argv[0].toUint16(); + uint16 y = argv[1].toUint16(); + reg_t targetObject = argv[2]; + uint16 illegalBits = argv[3].offset; + Common::Rect nsRect; + + // we assume that x, y are local coordinates + + // Get the bounding rectangle of the object + nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft)); + nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop)); + nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight)); + nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom)); + + // nsRect top/left may be negative, adjust accordingly + Common::Rect checkRect = nsRect; + if (checkRect.top < 0) + checkRect.top = 0; + if (checkRect.left < 0) + checkRect.left = 0; + + bool contained = checkRect.contains(x, y); + if (contained && illegalBits) { + // If illegalbits are set, we check the color of the pixel that got clicked on + // for now, we return false if the pixel is transparent + // although illegalBits may get differently set, don't know yet how this really works out + uint16 viewId = readSelectorValue(s->_segMan, targetObject, SELECTOR(view)); + int16 loopNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(loop)); + int16 celNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(cel)); + if (g_sci->_gfxCompare->kernelIsItSkip(viewId, loopNo, celNo, Common::Point(x - nsRect.left, y - nsRect.top))) + contained = false; + } +// these hacks shouldn't be needed anymore +// uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x)); +// uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y)); + + // If top and left are negative, we need to adjust coordinates by + // the item's x and y (e.g. happens in GK1, day 1, with detective + // Mosely's hotspot in his office) + +// if (nsRect.left < 0) +// nsRect.translate(itemX, 0); +// +// if (nsRect.top < 0) +// nsRect.translate(0, itemY); + +// // HACK: nsLeft and nsTop can be invalid, so try and fix them here +// // using x and y (e.g. with the inventory screen in GK1) +// if (nsRect.left == itemY && nsRect.top == itemX) { +// // Swap the values, as they're inversed (eh???) +// nsRect.left = itemX; +// nsRect.top = itemY; +// } + + return make_reg(0, contained); +} + +reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) { + // Tests if the cursor is on the passed object, after adjusting the + // coordinates of the object according to the object's plane + + uint16 x = argv[0].toUint16(); + uint16 y = argv[1].toUint16(); + reg_t targetObject = argv[2]; + // TODO: argv[3] - it's usually 0 + Common::Rect nsRect; + + // Get the bounding rectangle of the object + nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft)); + nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop)); + nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight)); + nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom)); + + // Get the object's plane +#if 0 + reg_t planeObject = readSelector(s->_segMan, targetObject, SELECTOR(plane)); + if (!planeObject.isNull()) { + //uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x)); + //uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y)); + uint16 planeResY = readSelectorValue(s->_segMan, planeObject, SELECTOR(resY)); + uint16 planeResX = readSelectorValue(s->_segMan, planeObject, SELECTOR(resX)); + uint16 planeTop = readSelectorValue(s->_segMan, planeObject, SELECTOR(top)); + uint16 planeLeft = readSelectorValue(s->_segMan, planeObject, SELECTOR(left)); + planeTop = (planeTop * g_sci->_gfxScreen->getHeight()) / planeResY; + planeLeft = (planeLeft * g_sci->_gfxScreen->getWidth()) / planeResX; + + // Adjust the bounding rectangle of the object by the object's + // actual X, Y coordinates + nsRect.top = ((nsRect.top * g_sci->_gfxScreen->getHeight()) / planeResY); + nsRect.left = ((nsRect.left * g_sci->_gfxScreen->getWidth()) / planeResX); + nsRect.bottom = ((nsRect.bottom * g_sci->_gfxScreen->getHeight()) / planeResY); + nsRect.right = ((nsRect.right * g_sci->_gfxScreen->getWidth()) / planeResX); + + nsRect.translate(planeLeft, planeTop); } +#endif + //warning("kIsOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16()); - if (videoDecoder) { - playVideo(videoDecoder); + return make_reg(0, nsRect.contains(x, y)); +} - // HACK: Switch back to 8bpp if we played a QuickTime video. - // We also won't be copying the screen to the SCI screen... - if (g_system->getScreenFormat().bytesPerPixel != 1) - initGraphics(screenWidth, screenHeight, screenWidth > 320); - else { - g_sci->_gfxScreen->kernelSyncWithFramebuffer(); - g_sci->_gfxPalette->kernelSyncScreenPalette(); +reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { + // TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1 + switch (argv[0].toUint16()) { + case 0: { + if (argc != 4) { + warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc); + return NULL_REG; } + reg_t object = argv[3]; + Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text))); + break; + } + default: + warning("CreateTextBitmap(%d)", argv[0].toUint16()); } - if (reshowCursor) - g_sci->_gfxCursor->kernelShow(); - - return s->r_acc; + return NULL_REG; } -#ifdef ENABLE_SCI32 reg_t kRobot(EngineState *s, int argc, reg_t *argv) { int16 subop = argv[0].toUint16(); @@ -1234,6 +1361,10 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { int16 x = argv[4].toUint16(); int16 y = argv[5].toUint16(); warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y); + GfxRobot *test = new GfxRobot(g_sci->getResMan(), g_sci->_gfxScreen, id); + test->draw(); + delete test; + } break; case 1: // LSL6 hires (startup) @@ -1255,140 +1386,6 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) { - uint16 operation = argv[0].toUint16(); - Graphics::VideoDecoder *videoDecoder = 0; - bool reshowCursor = g_sci->_gfxCursor->isVisible(); - Common::String fileName, warningMsg; - - switch (operation) { - case 0: // init - // This is actually meant to init the video file, but we play it instead - fileName = s->_segMan->derefString(argv[1]); - // TODO: argv[2] (usually null). When it exists, it points to an "Event" object, - // that holds no data initially (e.g. in the intro of Phantasmagoria 1 demo). - // Perhaps it's meant for syncing - if (argv[2] != NULL_REG) - warning("kPlayVMD: third parameter isn't 0 (it's %04x:%04x - %s)", PRINT_REG(argv[2]), s->_segMan->getObjectName(argv[2])); - - videoDecoder = new VMDDecoder(g_system->getMixer()); - - if (reshowCursor) - g_sci->_gfxCursor->kernelHide(); - - if (videoDecoder && videoDecoder->loadFile(fileName)) - playVideo(videoDecoder); - - if (reshowCursor) - g_sci->_gfxCursor->kernelShow(); - break; - case 1: - { - // Set VMD parameters. Called with a maximum of 6 parameters: - // - // x, y, flags, gammaBoost, gammaFirst, gammaLast - // - // Flags are as follows: - // bit 0 doubled - // bit 1 "drop frames"? - // bit 2 insert black lines - // bit 3 unknown - // bit 4 gamma correction - // bit 5 hold black frame - // bit 6 hold last frame - // bit 7 unknown - // bit 8 stretch - - // gammaBoost boosts palette colors in the range gammaFirst to - // gammaLast, but only if bit 4 in flags is set. Percent value such that - // 0% = no amplification These three parameters are optional if bit 4 is - // clear. Also note that the x, y parameters play subtle games if used - // with subfx 21. The subtleness has to do with creation of temporary - // planes and positioning relative to such planes. - - int flags = argv[3].offset; - Common::String flagspec; - - if (argc > 3) { - if (flags & 1) - flagspec += "doubled "; - if (flags & 2) - flagspec += "dropframes "; - if (flags & 4) - flagspec += "blacklines "; - if (flags & 8) - flagspec += "bit3 "; - if (flags & 16) - flagspec += "gammaboost "; - if (flags & 32) - flagspec += "holdblack "; - if (flags & 64) - flagspec += "holdlast "; - if (flags & 128) - flagspec += "bit7 "; - if (flags & 256) - flagspec += "stretch"; - - warning("VMDFlags: %s", flagspec.c_str()); - } - - warning("x, y: %d, %d", argv[1].offset, argv[2].offset); - - if (argc > 4 && flags & 16) - warning("gammaBoost: %d%% between palette entries %d and %d", argv[4].offset, argv[5].offset, argv[6].offset); - break; - } - case 6: - // Play, perhaps? Or stop? This is the last call made, and takes no extra parameters - case 14: - // Takes an additional integer parameter (e.g. 3) - case 16: - // Takes an additional parameter, usually 0 - case 21: - // Looks to be setting the video size and position. Called with 4 extra integer - // parameters (e.g. 86, 41, 235, 106) - default: - warningMsg = "PlayVMD - unsupported subop. Params: " + - Common::String::printf("%d", argc) + " ("; - - for (int i = 0; i < argc; i++) { - warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i])); - warningMsg += (i == argc - 1 ? ")" : ", "); - } - - warning("%s", warningMsg.c_str()); - break; - } - - return s->r_acc; -} - #endif -reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) { - // This call is used for KQ6's intro. It has one parameter, which is 1 when - // the intro begins, and 0 when it ends. It is suspected that this is - // actually a flag to enable video planar memory access, as the video - // decoder in KQ6 is specifically written for the planar memory model. - // Planar memory mode access was used for VGA "Mode X" (320x240 resolution, - // although the intro in KQ6 is 320x200). - // Refer to http://en.wikipedia.org/wiki/Mode_X - - //warning("STUB: SetVideoMode %d", argv[0].toUint16()); - return s->r_acc; -} - -// New calls for SCI11. Using those is only needed when using text-codes so that -// one is able to change font and/or color multiple times during kDisplay and -// kDrawControl -reg_t kTextFonts(EngineState *s, int argc, reg_t *argv) { - g_sci->_gfxText16->kernelTextFonts(argc, argv); - return s->r_acc; -} - -reg_t kTextColors(EngineState *s, int argc, reg_t *argv) { - g_sci->_gfxText16->kernelTextColors(argc, argv); - return s->r_acc; -} - } // End of namespace Sci diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp index 0701883a9b..93e95099f5 100644 --- a/engines/sci/engine/klists.cpp +++ b/engines/sci/engine/klists.cpp @@ -34,10 +34,15 @@ static bool isSaneNodePointer(SegManager *segMan, reg_t addr) { reg_t prev = addr; do { - Node *node = segMan->lookupNode(addr); + Node *node = segMan->lookupNode(addr, false); if (!node) { - error("isSaneNodePointer: Node at %04x:%04x wasn't found", PRINT_REG(addr)); + if ((g_sci->getGameId() == GID_ICEMAN) && (g_sci->getEngineState()->currentRoomNumber() == 40)) { + // ICEMAN: when plotting course, unDrawLast is called by startPlot::changeState + // there is no previous entry so we get 0 in here + } else { + error("isSaneNodePointer: Node at %04x:%04x wasn't found", PRINT_REG(addr)); + } return false; } @@ -70,8 +75,8 @@ static void checkListPointer(SegManager *segMan, reg_t addr) { // Empty list is fine } else if (!list->first.isNull() && !list->last.isNull()) { // Normal list - Node *node_a = segMan->lookupNode(list->first); - Node *node_z = segMan->lookupNode(list->last); + Node *node_a = segMan->lookupNode(list->first, false); + Node *node_z = segMan->lookupNode(list->last, false); if (!node_a) { error("checkListPointer (list %04x:%04x): missing first node", PRINT_REG(addr)); @@ -251,6 +256,19 @@ reg_t kNodeValue(EngineState *s, int argc, reg_t *argv) { reg_t kAddToFront(EngineState *s, int argc, reg_t *argv) { addToFront(s, argv[0], argv[1]); + + if (argc == 3) + s->_segMan->lookupNode(argv[1])->key = argv[2]; + + return s->r_acc; +} + +reg_t kAddToEnd(EngineState *s, int argc, reg_t *argv) { + addToEnd(s, argv[0], argv[1]); + + if (argc == 3) + s->_segMan->lookupNode(argv[1])->key = argv[2]; + return s->r_acc; } @@ -294,11 +312,6 @@ reg_t kAddAfter(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -reg_t kAddToEnd(EngineState *s, int argc, reg_t *argv) { - addToEnd(s, argv[0], argv[1]); - return s->r_acc; -} - reg_t kFindKey(EngineState *s, int argc, reg_t *argv) { reg_t node_pos; reg_t key = argv[1]; @@ -580,65 +593,134 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -// In SCI2.1, all the list functions were merged in one reg_t kList(EngineState *s, int argc, reg_t *argv) { + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} + +reg_t kAddBefore(EngineState *s, int argc, reg_t *argv) { + error("Unimplemented function kAddBefore called"); + return s->r_acc; +} + +reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv) { + error("Unimplemented function kMoveToFront called"); + return s->r_acc; +} + +reg_t kMoveToEnd(EngineState *s, int argc, reg_t *argv) { + error("Unimplemented function kMoveToEnd called"); + return s->r_acc; +} + +reg_t kArray(EngineState *s, int argc, reg_t *argv) { switch (argv[0].toUint16()) { - case 0: - return kNewList(s, argc - 1, argv + 1); - case 1: - return kDisposeList(s, argc - 1, argv + 1); - case 2: - return kNewNode(s, argc - 1, argv + 1); - case 3: - return kFirstNode(s, argc - 1, argv + 1); - case 4: - return kLastNode(s, argc - 1, argv + 1); - case 5: - return kEmptyList(s, argc - 1, argv + 1); - case 6: - return kNextNode(s, argc - 1, argv + 1); - case 7: - return kPrevNode(s, argc - 1, argv + 1); - case 8: - return kNodeValue(s, argc - 1, argv + 1); - case 9: - return kAddAfter(s, argc - 1, argv + 1); - case 10: - return kAddToFront(s, argc - 1, argv + 1); - case 11: - return kAddToEnd(s, argc - 1, argv + 1); - case 12: - error("kList: unimplemented subfunction kAddBefore"); - //return kAddBefore(s, argc - 1, argv + 1); - return NULL_REG; - case 13: - error("kList: unimplemented subfunction kMoveToFront"); - //return kMoveToFront(s, argc - 1, argv + 1); - return NULL_REG; - case 14: - error("kList: unimplemented subfunction kMoveToEnd"); - //return kMoveToEnd(s, argc - 1, argv + 1); - return NULL_REG; - case 15: - return kFindKey(s, argc - 1, argv + 1); - case 16: - return kDeleteKey(s, argc - 1, argv + 1); - case 17: - return kListAt(s, argc - 1, argv + 1); - case 18: - return kListIndexOf(s, argc - 1, argv + 1); - case 19: - return kListEachElementDo(s, argc - 1, argv + 1); - case 20: - return kListFirstTrue(s, argc - 1, argv + 1); - case 21: - return kListAllTrue(s, argc - 1, argv + 1); - case 22: - return kSort(s, argc - 1, argv + 1); + case 0: { // New + reg_t arrayHandle; + SciArray<reg_t> *array = s->_segMan->allocateArray(&arrayHandle); + array->setType(argv[2].toUint16()); + array->setSize(argv[1].toUint16()); + return arrayHandle; + } + case 1: { // Size + SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); + return make_reg(0, array->getSize()); + } + case 2: { // At (return value at an index) + SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); + return array->getValue(argv[2].toUint16()); + } + case 3: { // Atput (put value at an index) + SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); + + uint32 index = argv[2].toUint16(); + uint32 count = argc - 3; + + if (index + count > 65535) + break; + + if (array->getSize() < index + count) + array->setSize(index + count); + + for (uint16 i = 0; i < count; i++) + array->setValue(i + index, argv[i + 3]); + + return argv[1]; // We also have to return the handle + } + case 4: // Free + // Freeing of arrays is handled by the garbage collector + return s->r_acc; + case 5: { // Fill + SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); + uint16 index = argv[2].toUint16(); + + // A count of -1 means fill the rest of the array + uint16 count = argv[3].toSint16() == -1 ? array->getSize() - index : argv[3].toUint16(); + uint16 arraySize = array->getSize(); + + if (arraySize < index + count) + array->setSize(index + count); + + for (uint16 i = 0; i < count; i++) + array->setValue(i + index, argv[4]); + + return argv[1]; + } + case 6: { // Cpy + if (s->_segMan->getSegmentObj(argv[1].segment)->getType() != SEG_TYPE_ARRAY || + s->_segMan->getSegmentObj(argv[3].segment)->getType() != SEG_TYPE_ARRAY) { + // Happens in the RAMA demo + warning("kArray(Cpy): Request to copy a segment which isn't an array, ignoring"); + return NULL_REG; + } + + SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]); + SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]); + uint32 index1 = argv[2].toUint16(); + uint32 index2 = argv[4].toUint16(); + + // The original engine ignores bad copies too + if (index2 > array2->getSize()) + break; + + // A count of -1 means fill the rest of the array + uint32 count = argv[5].toSint16() == -1 ? array2->getSize() - index2 : argv[5].toUint16(); + + if (array1->getSize() < index1 + count) + array1->setSize(index1 + count); + + for (uint16 i = 0; i < count; i++) + array1->setValue(i + index1, array2->getValue(i + index2)); + + return argv[1]; + } + case 7: // Cmp + // Not implemented in SSCI + return s->r_acc; + case 8: { // Dup + SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); + reg_t arrayHandle; + SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle); + + dupArray->setType(array->getType()); + dupArray->setSize(array->getSize()); + + for (uint32 i = 0; i < array->getSize(); i++) + dupArray->setValue(i, array->getValue(i)); + + return arrayHandle; + } + case 9: // Getdata + if (!s->_segMan->isHeapObject(argv[1])) + return argv[1]; + + return readSelector(s->_segMan, argv[1], SELECTOR(data)); default: - error("kList: Unhandled case %d", argv[0].toUint16()); - return NULL_REG; + error("Unknown kArray subop %d", argv[0].toUint16()); } + + return NULL_REG; } #endif diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp index eab964d624..bdc705cae3 100644 --- a/engines/sci/engine/kmath.cpp +++ b/engines/sci/engine/kmath.cpp @@ -29,10 +29,27 @@ namespace Sci { reg_t kRandom(EngineState *s, int argc, reg_t *argv) { - int fromNumber = argv[0].toUint16(); - int toNumber = argv[1].toUint16(); - double randomNumber = fromNumber + ((toNumber + 1.0 - fromNumber) * (rand() / (RAND_MAX + 1.0))); - return make_reg(0, (int)randomNumber); + switch (argc) { + case 1: // set seed to argv[0] + // SCI0/SCI01 just reset the seed to 0 instead of using argv[0] at all + return NULL_REG; + + case 2: { // get random number + int fromNumber = argv[0].toUint16(); + int toNumber = argv[1].toUint16(); + double randomNumber = fromNumber + ((toNumber + 1.0 - fromNumber) * (rand() / (RAND_MAX + 1.0))); + return make_reg(0, (int)randomNumber); + } + + case 3: // get seed + // SCI0/01 did not support this at all + // Actually we would have to return the previous seed + error("kRandom: scripts asked for previous seed"); + break; + + default: + error("kRandom: unsupported argc"); + } } reg_t kAbs(EngineState *s, int argc, reg_t *argv) { diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp index b8c62210f9..305e202ae9 100644 --- a/engines/sci/engine/kmisc.cpp +++ b/engines/sci/engine/kmisc.cpp @@ -368,4 +368,39 @@ reg_t kEmpty(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +reg_t kStub(EngineState *s, int argc, reg_t *argv) { + Kernel *kernel = g_sci->getKernel(); + int kernelCallNr = -1; + + Common::List<ExecStack>::iterator callIterator = s->_executionStack.end(); + if (callIterator != s->_executionStack.begin()) { + callIterator--; + ExecStack lastCall = *callIterator; + kernelCallNr = lastCall.debugSelector; + } + + Common::String warningMsg = "Dummy function k" + kernel->getKernelName(kernelCallNr) + + Common::String::printf("[%x]", kernelCallNr) + + " invoked. Params: " + + Common::String::printf("%d", argc) + " ("; + + for (int i = 0; i < argc; i++) { + warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i])); + warningMsg += (i == argc - 1 ? ")" : ", "); + } + + warning("%s", warningMsg.c_str()); + return s->r_acc; +} + +reg_t kStubNull(EngineState *s, int argc, reg_t *argv) { + kStub(s, argc, argv); + return NULL_REG; +} + +reg_t kDummy(EngineState *s, int argc, reg_t *argv) { + kStub(s, argc, argv); + error("Kernel function was called, which was considered to be unused - see log for details"); +} + } // End of namespace Sci diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp index ccef3d862a..114b6eb755 100644 --- a/engines/sci/engine/kmovement.cpp +++ b/engines/sci/engine/kmovement.cpp @@ -267,6 +267,15 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { bdelta = (int16)readSelectorValue(segMan, mover, SELECTOR(b_incr)); axis = (int16)readSelectorValue(segMan, mover, SELECTOR(b_xAxis)); + if ((getSciVersion() >= SCI_VERSION_1_LATE)) { + // Mixed-Up Fairy Tales has no xLast/yLast selectors + if (SELECTOR(xLast) != -1) { + // save last position into mover + writeSelectorValue(segMan, mover, SELECTOR(xLast), x); + writeSelectorValue(segMan, mover, SELECTOR(yLast), y); + } + } + //printf("movecnt %d, move speed %d\n", movcnt, max_movcnt); if (g_sci->_features->handleMoveCount()) { @@ -316,14 +325,24 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { debugC(2, kDebugLevelBresen, "New data: (x,y)=(%d,%d), di=%d", x, y, bdi); + bool collision = false; + reg_t cantBeHere = NULL_REG; + if (SELECTOR(cantBeHere) != -1) { + // adding this here for hoyle 3 to get happy. CantBeHere is a dummy in hoyle 3 and acc is != 0 so we would + // get a collision otherwise + s->r_acc = NULL_REG; invokeSelector(s, client, SELECTOR(cantBeHere), argc, argv); - s->r_acc = make_reg(0, !s->r_acc.offset); + if (!s->r_acc.isNull()) + collision = true; + cantBeHere = s->r_acc; } else { invokeSelector(s, client, SELECTOR(canBeHere), argc, argv); + if (s->r_acc.isNull()) + collision = true; } - if (!s->r_acc.offset) { // Contains the return value + if (collision) { signal = readSelectorValue(segMan, client, SELECTOR(signal)); writeSelectorValue(segMan, client, SELECTOR(x), oldx); @@ -331,13 +350,16 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { writeSelectorValue(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle)); debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x by collision", PRINT_REG(mover)); - completed = 1; + // We shall not set completed in this case, sierra sci also doesn't do it + // if we set call .moveDone in those cases qfg1 vga gate at the castle and lsl1 casino door will not work } if ((getSciVersion() >= SCI_VERSION_1_EGA)) if (completed) invokeSelector(s, mover, SELECTOR(moveDone), argc, argv); + if (SELECTOR(cantBeHere) != -1) + return cantBeHere; return make_reg(0, completed); } diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp index 45493a95d2..552e425906 100644 --- a/engines/sci/engine/kparse.cpp +++ b/engines/sci/engine/kparse.cpp @@ -31,6 +31,8 @@ #include "sci/engine/message.h" #include "sci/engine/kernel.h" +//#define DEBUG_PARSER + namespace Sci { /*************************************************************/ @@ -60,8 +62,8 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { } #ifdef DEBUG_PARSER - debugC(2, kDebugLevelParser, "Said block:", 0); - s->_voc->decipherSaidBlock(said_block); + printf("Said block: "); + g_sci->getVocabulary()->debugDecipherSaidBlock(said_block); #endif if (voc->parser_event.isNull() || (readSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed)))) { @@ -93,6 +95,7 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) { char *error; ResultWordList words; reg_t event = argv[1]; + g_sci->checkVocabularySwitch(); Vocabulary *voc = g_sci->getVocabulary(); voc->parser_event = event; reg_t params[2] = { voc->parser_base, stringpos }; @@ -106,7 +109,7 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) { s->r_acc = make_reg(0, 1); #ifdef DEBUG_PARSER - debugC(2, kDebugLevelParser, "Parsed to the following blocks:", 0); + debugC(2, kDebugLevelParser, "Parsed to the following blocks:"); for (ResultWordList::const_iterator i = words.begin(); i != words.end(); ++i) debugC(2, kDebugLevelParser, " Type[%04x] Group[%04x]", i->_class, i->_group); diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index fdaae3e121..fdebc0599c 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -933,9 +933,11 @@ static Common::Point *fixup_start_point(PathfindingState *s, const Common::Point case POLY_NEAREST_ACCESS: if (cont == CONT_INSIDE) { if (s->_prependPoint != NULL) { - // We shouldn't get here twice + // We shouldn't get here twice. + // We need to break in this case, otherwise we'll end in an infinite + // loop. warning("AvoidPath: start point is contained in multiple polygons"); - continue; + break; } if (s->findNearPoint(start, (*it), new_start) != PF_OK) { @@ -944,7 +946,7 @@ static Common::Point *fixup_start_point(PathfindingState *s, const Common::Point } if ((type == POLY_BARRED_ACCESS) || (type == POLY_CONTAINED_ACCESS)) - warning("AvoidPath: start position at unreachable location"); + debugC(2, kDebugLevelAvoidPath, "AvoidPath: start position at unreachable location"); // The original start position is in an invalid location, so we // use the moved point and add the original one to the final path @@ -987,9 +989,14 @@ static Common::Point *fixup_end_point(PathfindingState *s, const Common::Point & case POLY_NEAREST_ACCESS: if (cont != CONT_OUTSIDE) { if (s->_appendPoint != NULL) { - // We shouldn't get here twice + // We shouldn't get here twice. + // Happens in LB2CD, inside the speakeasy when walking from the + // speakeasy (room 310) into the bathroom (room 320), after having + // consulted the notebook (bug #3036299). + // We need to break in this case, otherwise we'll end in an infinite + // loop. warning("AvoidPath: end point is contained in multiple polygons"); - continue; + break; } // The original end position is in an invalid location, so we move the point @@ -1315,7 +1322,7 @@ static void AStar(PathfindingState *s) { } if (openSet.empty()) - warning("[avoidpath] End point (%i, %i) is unreachable", s->vertex_end->v.x, s->vertex_end->v.y); + debugC(2, kDebugLevelAvoidPath, "AvoidPath: End point (%i, %i) is unreachable", s->vertex_end->v.x, s->vertex_end->v.y); } static reg_t allocateOutputArray(SegManager *segMan, int size) { @@ -1770,4 +1777,13 @@ reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) { return output; } +#ifdef ENABLE_SCI32 + +reg_t kInPolygon(EngineState *s, int argc, reg_t *argv) { + // kAvoidPath already implements this + return kAvoidPath(s, argc, argv); +} + +#endif + } // End of namespace Sci diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index 029943b070..e211867ef9 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -38,7 +38,7 @@ namespace Sci { // Loads arbitrary resources of type 'restype' with resource numbers 'resnrs' // This implementation ignores all resource numbers except the first one. reg_t kLoad(EngineState *s, int argc, reg_t *argv) { - ResourceType restype = (ResourceType)(argv[0].toUint16() & 0x7f); + ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16()); int resnr = argv[1].toUint16(); // Request to dynamically allocate hunk memory for later use @@ -53,7 +53,7 @@ reg_t kLoad(EngineState *s, int argc, reg_t *argv) { // 1 or 3+ parameters is not right according to sierra sci reg_t kUnLoad(EngineState *s, int argc, reg_t *argv) { if (argc >= 2) { - ResourceType restype = (ResourceType)(argv[0].toUint16() & 0x7f); + ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16()); reg_t resnr = argv[1]; // WORKAROUND for a broken script in room 320 in Castle of Dr. Brain. @@ -73,7 +73,7 @@ reg_t kUnLoad(EngineState *s, int argc, reg_t *argv) { reg_t kLock(EngineState *s, int argc, reg_t *argv) { int state = argc > 2 ? argv[2].toUint16() : 1; - ResourceType type = (ResourceType)(argv[0].toUint16() & 0x7f); + ResourceType type = g_sci->getResMan()->convertResType(argv[0].toUint16()); ResourceId id = ResourceId(type, argv[1].toUint16()); Resource *which; @@ -104,8 +104,10 @@ reg_t kLock(EngineState *s, int argc, reg_t *argv) { if (id.getType() == kResourceTypeInvalid) warning("[resMan] Attempt to unlock resource %i of invalid type %i", id.getNumber(), type); else - // Happens in CD games (e.g. LSL6CD) with the message resource - warning("[resMan] Attempt to unlock non-existant resource %s", id.toString().c_str()); + // Happens in CD games (e.g. LSL6CD) with the message + // resource. It isn't fatal, and it's usually caused + // by leftover scripts. + debugC(2, kDebugLevelResMan, "[resMan] Attempt to unlock non-existant resource %s", id.toString().c_str()); } } break; @@ -115,7 +117,7 @@ reg_t kLock(EngineState *s, int argc, reg_t *argv) { reg_t kResCheck(EngineState *s, int argc, reg_t *argv) { Resource *res = NULL; - ResourceType restype = (ResourceType)(argv[0].toUint16() & 0x7f); + ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16()); if (restype == kResourceTypeVMD) { char fileName[10]; diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp index 69ae68674b..4e5ddc5e96 100644 --- a/engines/sci/engine/ksound.cpp +++ b/engines/sci/engine/ksound.cpp @@ -54,7 +54,7 @@ CREATE_DOSOUND_FORWARD(DoSoundMute) CREATE_DOSOUND_FORWARD(DoSoundStop) CREATE_DOSOUND_FORWARD(DoSoundStopAll) CREATE_DOSOUND_FORWARD(DoSoundPause) -CREATE_DOSOUND_FORWARD(DoSoundResume) +CREATE_DOSOUND_FORWARD(DoSoundResumeAfterRestore) CREATE_DOSOUND_FORWARD(DoSoundMasterVolume) CREATE_DOSOUND_FORWARD(DoSoundUpdate) CREATE_DOSOUND_FORWARD(DoSoundFade) diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index f2f9543ad2..9254bce9c1 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -566,7 +566,7 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) { } reg_t kSetQuitStr(EngineState *s, int argc, reg_t *argv) { - Common::String quitStr = s->_segMan->getString(argv[0]); + //Common::String quitStr = s->_segMan->getString(argv[0]); //debug("Setting quit string to '%s'", quitStr.c_str()); return s->r_acc; } @@ -592,4 +592,193 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv) { return argv[0]; } +#ifdef ENABLE_SCI32 + +reg_t kText(EngineState *s, int argc, reg_t *argv) { + switch (argv[0].toUint16()) { + case 0: + return kTextSize(s, argc - 1, argv + 1); + default: + // TODO: Other subops here too, perhaps kTextColors and kTextFonts + warning("kText(%d)", argv[0].toUint16()); + break; + } + + return s->r_acc; +} + +reg_t kString(EngineState *s, int argc, reg_t *argv) { + switch (argv[0].toUint16()) { + case 0: { // New + reg_t stringHandle; + SciString *string = s->_segMan->allocateString(&stringHandle); + string->setSize(argv[1].toUint16()); + + // Make sure the first character is a null character + if (string->getSize() > 0) + string->setValue(0, 0); + + return stringHandle; + } + case 1: // Size + return make_reg(0, s->_segMan->getString(argv[1]).size()); + case 2: { // At (return value at an index) + if (argv[1].segment == s->_segMan->getStringSegmentId()) + return make_reg(0, s->_segMan->lookupString(argv[1])->getRawData()[argv[2].toUint16()]); + + return make_reg(0, s->_segMan->getString(argv[1])[argv[2].toUint16()]); + } + case 3: { // Atput (put value at an index) + SciString *string = s->_segMan->lookupString(argv[1]); + + uint32 index = argv[2].toUint16(); + uint32 count = argc - 3; + + if (index + count > 65535) + break; + + if (string->getSize() < index + count) + string->setSize(index + count); + + for (uint16 i = 0; i < count; i++) + string->setValue(i + index, argv[i + 3].toUint16()); + + return argv[1]; // We also have to return the handle + } + case 4: // Free + // Freeing of strings is handled by the garbage collector + return s->r_acc; + case 5: { // Fill + SciString *string = s->_segMan->lookupString(argv[1]); + uint16 index = argv[2].toUint16(); + + // A count of -1 means fill the rest of the array + uint16 count = argv[3].toSint16() == -1 ? string->getSize() - index : argv[3].toUint16(); + uint16 stringSize = string->getSize(); + + if (stringSize < index + count) + string->setSize(index + count); + + for (uint16 i = 0; i < count; i++) + string->setValue(i + index, argv[4].toUint16()); + + return argv[1]; + } + case 6: { // Cpy + const char *string2 = 0; + uint32 string2Size = 0; + + if (argv[3].segment == s->_segMan->getStringSegmentId()) { + SciString *string = s->_segMan->lookupString(argv[3]); + string2 = string->getRawData(); + string2Size = string->getSize(); + } else { + Common::String string = s->_segMan->getString(argv[3]); + string2 = string.c_str(); + string2Size = string.size() + 1; + } + + uint32 index1 = argv[2].toUint16(); + uint32 index2 = argv[4].toUint16(); + + // The original engine ignores bad copies too + if (index2 > string2Size) + break; + + // A count of -1 means fill the rest of the array + uint32 count = argv[5].toSint16() == -1 ? string2Size - index2 + 1 : argv[5].toUint16(); + + // We have a special case here for argv[1] being a system string + if (argv[1].segment == s->_segMan->getSysStringsSegment()) { + // Resize if necessary + const uint16 sysStringId = argv[1].toUint16(); + SystemString *sysString = s->_segMan->getSystemString(sysStringId); + assert(sysString); + if ((uint32)sysString->_maxSize < index1 + count) { + free(sysString->_value); + sysString->_maxSize = index1 + count; + sysString->_value = (char *)calloc(index1 + count, sizeof(char)); + } + + strncpy(sysString->_value + index1, string2 + index2, count); + } else { + SciString *string1 = s->_segMan->lookupString(argv[1]); + + if (string1->getSize() < index1 + count) + string1->setSize(index1 + count); + + // Note: We're accessing from c_str() here because the + // string's size ignores the trailing 0 and therefore + // triggers an assert when doing string2[i + index2]. + for (uint16 i = 0; i < count; i++) + string1->setValue(i + index1, string2[i + index2]); + } + + } return argv[1]; + case 7: { // Cmp + Common::String string1 = argv[1].isNull() ? "" : s->_segMan->getString(argv[1]); + Common::String string2 = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]); + + if (argc == 4) // Strncmp + return make_reg(0, strncmp(string1.c_str(), string2.c_str(), argv[3].toUint16())); + else // Strcmp + return make_reg(0, strcmp(string1.c_str(), string2.c_str())); + } + case 8: { // Dup + const char *rawString = 0; + uint32 size = 0; + + if (argv[1].segment == s->_segMan->getStringSegmentId()) { + SciString *string = s->_segMan->lookupString(argv[1]); + rawString = string->getRawData(); + size = string->getSize(); + } else { + Common::String string = s->_segMan->getString(argv[1]); + rawString = string.c_str(); + size = string.size() + 1; + } + + reg_t stringHandle; + SciString *dupString = s->_segMan->allocateString(&stringHandle); + dupString->setSize(size); + + for (uint32 i = 0; i < size; i++) + dupString->setValue(i, rawString[i]); + + return stringHandle; + } + case 9: // Getdata + if (!s->_segMan->isHeapObject(argv[1])) + return argv[1]; + + return readSelector(s->_segMan, argv[1], SELECTOR(data)); + case 10: // Stringlen + return make_reg(0, s->_segMan->strlen(argv[1])); + case 11: { // Printf + reg_t stringHandle; + s->_segMan->allocateString(&stringHandle); + + reg_t *adjustedArgs = new reg_t[argc]; + adjustedArgs[0] = stringHandle; + memcpy(&adjustedArgs[1], argv + 1, (argc - 1) * sizeof(reg_t)); + + kFormat(s, argc, adjustedArgs); + delete[] adjustedArgs; + return stringHandle; + } + case 12: // Printf Buf + return kFormat(s, argc - 1, argv + 1); + case 13: { // atoi + Common::String string = s->_segMan->getString(argv[1]); + return make_reg(0, (uint16)atoi(string.c_str())); + } + default: + error("Unknown kString subop %d", argv[0].toUint16()); + } + + return NULL_REG; +} + +#endif + } // End of namespace Sci diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp new file mode 100644 index 0000000000..cd103dade7 --- /dev/null +++ b/engines/sci/engine/kvideo.cpp @@ -0,0 +1,300 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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 "engines/util.h" +#include "sci/engine/state.h" +#include "sci/graphics/helpers.h" +#include "sci/graphics/cursor.h" +#include "sci/graphics/palette.h" +#include "sci/graphics/screen.h" +#include "graphics/cursorman.h" +#include "graphics/video/avi_decoder.h" +#include "graphics/video/qt_decoder.h" +#include "sci/video/seq_decoder.h" +#ifdef ENABLE_SCI32 +#include "sci/video/vmd_decoder.h" +#endif + +namespace Sci { + +void playVideo(Graphics::VideoDecoder *videoDecoder) { + if (!videoDecoder) + return; + + byte *scaleBuffer = 0; + uint16 width = videoDecoder->getWidth(); + uint16 height = videoDecoder->getHeight(); + uint16 screenWidth = g_system->getWidth(); + uint16 screenHeight = g_system->getHeight(); + + if (screenWidth == 640 && width <= 320 && height <= 240) { + assert(videoDecoder->getPixelFormat().bytesPerPixel == 1); + width *= 2; + height *= 2; + scaleBuffer = new byte[width * height]; + } + + uint16 x = (screenWidth - width) / 2; + uint16 y = (screenHeight - height) / 2; + bool skipVideo = false; + + while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { + if (videoDecoder->needsUpdate()) { + Graphics::Surface *frame = videoDecoder->decodeNextFrame(); + if (frame) { + if (scaleBuffer) { + // TODO: Probably should do aspect ratio correction in e.g. GK1 Windows + g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight()); + g_system->copyRectToScreen(scaleBuffer, width, x, y, width, height); + } else + g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, width, height); + + if (videoDecoder->hasDirtyPalette()) + videoDecoder->setSystemPalette(); + + g_system->updateScreen(); + } + } + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) { + if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) + skipVideo = true; + } + + g_system->delayMillis(10); + } + + delete[] scaleBuffer; + delete videoDecoder; +} + +reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { + // Hide the cursor if it's showing and then show it again if it was + // previously visible. + bool reshowCursor = g_sci->_gfxCursor->isVisible(); + if (reshowCursor) + g_sci->_gfxCursor->kernelHide(); + + uint16 screenWidth = g_system->getWidth(); + uint16 screenHeight = g_system->getHeight(); + + Graphics::VideoDecoder *videoDecoder = 0; + + if (argv[0].segment != 0) { + Common::String filename = s->_segMan->getString(argv[0]); + + if (g_sci->getPlatform() == Common::kPlatformMacintosh) { + // Mac QuickTime + // The only argument is the string for the video + + // HACK: Switch to 16bpp graphics for Cinepak. + initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL); + + if (g_system->getScreenFormat().bytesPerPixel == 1) { + error("This video requires >8bpp color to be displayed, but could not switch to RGB color mode."); + return NULL_REG; + } + + videoDecoder = new Graphics::QuickTimeDecoder(); + if (!videoDecoder->loadFile(filename)) + error("Could not open '%s'", filename.c_str()); + } else { + // DOS SEQ + // SEQ's are called with no subops, just the string and delay + SeqDecoder *seqDecoder = new SeqDecoder(); + seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks + videoDecoder = seqDecoder; + + if (!videoDecoder->loadFile(filename)) { + warning("Failed to open movie file %s", filename.c_str()); + delete videoDecoder; + videoDecoder = 0; + } + } + } else { + // Windows AVI + // TODO: This appears to be some sort of subop. case 0 contains the string + // for the video, so we'll just play it from there for now. + +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2_1) { + // SCI2.1 always has argv[0] as 1, the rest of the arguments seem to + // follow SCI1.1/2. + if (argv[0].toUint16() != 1) + error("SCI2.1 kShowMovie argv[0] not 1"); + argv++; + argc--; + } +#endif + switch (argv[0].toUint16()) { + case 0: { + Common::String filename = s->_segMan->getString(argv[1]); + videoDecoder = new Graphics::AviDecoder(g_system->getMixer()); + + if (!videoDecoder->loadFile(filename.c_str())) { + warning("Failed to open movie file %s", filename.c_str()); + delete videoDecoder; + videoDecoder = 0; + } + break; + } + default: + warning("Unhandled SCI kShowMovie subop %d", argv[1].toUint16()); + } + } + + if (videoDecoder) { + playVideo(videoDecoder); + + // HACK: Switch back to 8bpp if we played a QuickTime video. + // We also won't be copying the screen to the SCI screen... + if (g_system->getScreenFormat().bytesPerPixel != 1) + initGraphics(screenWidth, screenHeight, screenWidth > 320); + else { + g_sci->_gfxScreen->kernelSyncWithFramebuffer(); + g_sci->_gfxPalette->kernelSyncScreenPalette(); + } + } + + if (reshowCursor) + g_sci->_gfxCursor->kernelShow(); + + return s->r_acc; +} + +#ifdef ENABLE_SCI32 + +reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) { + uint16 operation = argv[0].toUint16(); + Graphics::VideoDecoder *videoDecoder = 0; + bool reshowCursor = g_sci->_gfxCursor->isVisible(); + Common::String fileName, warningMsg; + + switch (operation) { + case 0: // init + // This is actually meant to init the video file, but we play it instead + fileName = s->_segMan->derefString(argv[1]); + // TODO: argv[2] (usually null). When it exists, it points to an "Event" object, + // that holds no data initially (e.g. in the intro of Phantasmagoria 1 demo). + // Perhaps it's meant for syncing + if (argv[2] != NULL_REG) + warning("kPlayVMD: third parameter isn't 0 (it's %04x:%04x - %s)", PRINT_REG(argv[2]), s->_segMan->getObjectName(argv[2])); + + videoDecoder = new VMDDecoder(g_system->getMixer()); + + if (reshowCursor) + g_sci->_gfxCursor->kernelHide(); + + if (videoDecoder && videoDecoder->loadFile(fileName)) + playVideo(videoDecoder); + + if (reshowCursor) + g_sci->_gfxCursor->kernelShow(); + break; + case 1: + { + // Set VMD parameters. Called with a maximum of 6 parameters: + // + // x, y, flags, gammaBoost, gammaFirst, gammaLast + // + // Flags are as follows: + // bit 0 doubled + // bit 1 "drop frames"? + // bit 2 insert black lines + // bit 3 unknown + // bit 4 gamma correction + // bit 5 hold black frame + // bit 6 hold last frame + // bit 7 unknown + // bit 8 stretch + + // gammaBoost boosts palette colors in the range gammaFirst to + // gammaLast, but only if bit 4 in flags is set. Percent value such that + // 0% = no amplification These three parameters are optional if bit 4 is + // clear. Also note that the x, y parameters play subtle games if used + // with subfx 21. The subtleness has to do with creation of temporary + // planes and positioning relative to such planes. + + int flags = argv[3].offset; + Common::String flagspec; + + if (argc > 3) { + if (flags & 1) + flagspec += "doubled "; + if (flags & 2) + flagspec += "dropframes "; + if (flags & 4) + flagspec += "blacklines "; + if (flags & 8) + flagspec += "bit3 "; + if (flags & 16) + flagspec += "gammaboost "; + if (flags & 32) + flagspec += "holdblack "; + if (flags & 64) + flagspec += "holdlast "; + if (flags & 128) + flagspec += "bit7 "; + if (flags & 256) + flagspec += "stretch"; + + warning("VMDFlags: %s", flagspec.c_str()); + } + + warning("x, y: %d, %d", argv[1].offset, argv[2].offset); + + if (argc > 4 && flags & 16) + warning("gammaBoost: %d%% between palette entries %d and %d", argv[4].offset, argv[5].offset, argv[6].offset); + break; + } + case 6: + // Play, perhaps? Or stop? This is the last call made, and takes no extra parameters + case 14: + // Takes an additional integer parameter (e.g. 3) + case 16: + // Takes an additional parameter, usually 0 + case 21: + // Looks to be setting the video size and position. Called with 4 extra integer + // parameters (e.g. 86, 41, 235, 106) + default: + warningMsg = "PlayVMD - unsupported subop. Params: " + + Common::String::printf("%d", argc) + " ("; + + for (int i = 0; i < argc; i++) { + warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i])); + warningMsg += (i == argc - 1 ? ")" : ", "); + } + + warning("%s", warningMsg.c_str()); + break; + } + + return s->r_acc; +} + +#endif + +} // End of namespace Sci diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp index 18e60eb521..cdecc556e8 100644 --- a/engines/sci/engine/message.cpp +++ b/engines/sci/engine/message.cpp @@ -380,7 +380,14 @@ void MessageState::outputString(reg_t buf, const Common::String &str) { if ((unsigned)buffer_r.maxSize >= str.size() + 1) { _segMan->strcpy(buf, str.c_str()); } else { - warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(buf), str.size() + 1, str.c_str()); + // LSL6 sets an exit text here, but the buffer size allocated + // is too small. Don't display a warning in this case, as we + // don't use the exit text anyway - bug report #3035533 + if (g_sci->getGameId() == GID_LSL6 && str.hasPrefix("\r\n(c) 1993 Sierra On-Line, Inc")) { + // LSL6 buggy exit text, don't show warning + } else { + warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(buf), str.size() + 1, str.c_str()); + } // Set buffer to empty string if possible if (buffer_r.maxSize > 0) diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 0fe5f2088a..806c8893b4 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -40,6 +40,7 @@ #include "sci/engine/selector.h" #include "sci/engine/vm_types.h" #include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS +#include "sci/graphics/palette.h" #include "sci/graphics/ports.h" #include "sci/sound/audio.h" #include "sci/sound/music.h" @@ -64,47 +65,20 @@ const uint32 INTMAPPER_MAGIC_KEY = 0xDEADBEEF; #define DEFROBNICATE_HANDLE(handle) (make_reg((handle >> 16) & 0xffff, handle & 0xffff)) void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) { - if (s.getVersion() < 14) { - // Old sound system data. This data is only loaded, never saved (as we're never - // saving in the older version format) - uint32 handle = 0; - s.syncAsSint32LE(handle); - soundObj = DEFROBNICATE_HANDLE(handle); - s.syncAsSint32LE(resourceId); - s.syncAsSint32LE(priority); - s.syncAsSint32LE(status); - s.skip(4); // restoreBehavior - uint32 restoreTime = 0; - s.syncAsSint32LE(restoreTime); - ticker = restoreTime * 60 / 1000; - s.syncAsSint32LE(loop); - s.syncAsSint32LE(hold); - // volume and dataInc will be synced from the sound objects - // when the sound list is reconstructed in gamestate_restore() - volume = MUSIC_VOLUME_MAX; - dataInc = 0; - // No fading info - fadeTo = 0; - fadeStep = 0; - fadeTicker = 0; - fadeTickerStep = 0; - } else { - // A bit more optimized saving - soundObj.saveLoadWithSerializer(s); - s.syncAsSint16LE(resourceId); - s.syncAsSint16LE(dataInc); - s.syncAsSint16LE(ticker); - s.syncAsSint16LE(signal, VER(17)); - s.syncAsByte(priority); - s.syncAsSint16LE(loop, VER(17)); - s.syncAsByte(volume); - s.syncAsByte(hold, VER(17)); - s.syncAsByte(fadeTo); - s.syncAsSint16LE(fadeStep); - s.syncAsSint32LE(fadeTicker); - s.syncAsSint32LE(fadeTickerStep); - s.syncAsByte(status); - } + soundObj.saveLoadWithSerializer(s); + s.syncAsSint16LE(resourceId); + s.syncAsSint16LE(dataInc); + s.syncAsSint16LE(ticker); + s.syncAsSint16LE(signal, VER(17)); + s.syncAsByte(priority); + s.syncAsSint16LE(loop, VER(17)); + s.syncAsByte(volume); + s.syncAsByte(hold, VER(17)); + s.syncAsByte(fadeTo); + s.syncAsSint16LE(fadeStep); + s.syncAsSint32LE(fadeTicker); + s.syncAsSint32LE(fadeTickerStep); + s.syncAsByte(status); // pMidiParser and pStreamAud will be initialized when the // sound list is reconstructed in gamestate_restore() @@ -181,7 +155,7 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) { if (s.isLoading()) resetSegMan(); - s.skip(4, VER(12), VER(18)); // OBSOLETE: Used to be _exportsAreWide + s.skip(4, VER(14), VER(18)); // OBSOLETE: Used to be _exportsAreWide if (s.isLoading()) { // Reset _scriptSegMap, to be restored below @@ -256,40 +230,9 @@ static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj) void EngineState::saveLoadWithSerializer(Common::Serializer &s) { Common::String tmp; - s.syncString(tmp, VER(12), VER(23)); // OBSOLETE: Used to be game_version - - // OBSOLETE: Saved menus. Skip all of the saved data - if (s.getVersion() < 14) { - int totalMenus = 0; - s.syncAsUint32LE(totalMenus); - - // Now iterate through the obsolete saved menu data - for (int i = 0; i < totalMenus; i++) { - s.syncString(tmp); // OBSOLETE: Used to be _title - s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _titleWidth - s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _width - - int menuLength = 0; - s.syncAsUint32LE(menuLength); - - for (int j = 0; j < menuLength; j++) { - s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _type - s.syncString(tmp); // OBSOLETE: Used to be _keytext - - s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _flags - s.skip(64, VER(12), VER(12)); // OBSOLETE: Used to be MENU_SAID_SPEC_SIZE - s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _saidPos - s.syncString(tmp); // OBSOLETE: Used to be _text - s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _textPos - s.skip(4 * 4, VER(12), VER(12)); // OBSOLETE: Used to be _modifiers, _key, _enabled and _tag - } - } - } + s.syncString(tmp, VER(14), VER(23)); // OBSOLETE: Used to be game_version - s.skip(4, VER(12), VER(12)); // obsolete: used to be status_bar_foreground - s.skip(4, VER(12), VER(12)); // obsolete: used to be status_bar_background - - if (s.getVersion() >= 13 && getSciVersion() <= SCI_VERSION_1_1) { + if (getSciVersion() <= SCI_VERSION_1_1) { // Save/Load picPort as well for SCI0-SCI1.1. Necessary for Castle of Dr. Brain, // as the picPort has been changed when loading during the intro int16 picPortTop, picPortLeft; @@ -312,6 +255,7 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) { _segMan->saveLoadWithSerializer(s); g_sci->_soundCmd->syncPlayList(s); + g_sci->_gfxPalette->saveLoadWithSerializer(s); } void LocalVariables::saveLoadWithSerializer(Common::Serializer &s) { @@ -323,7 +267,6 @@ void LocalVariables::saveLoadWithSerializer(Common::Serializer &s) { void Object::saveLoadWithSerializer(Common::Serializer &s) { s.syncAsSint32LE(_flags); _pos.saveLoadWithSerializer(s); - s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be variable_names_nr s.syncAsSint32LE(_methodCount); // that's actually a uint16 syncArray<reg_t>(s, _variables); @@ -449,12 +392,12 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) { if (s.isLoading()) init(_nr, g_sci->getResMan()); - s.skip(4, VER(12), VER(22)); // OBSOLETE: Used to be _bufSize - s.skip(4, VER(12), VER(22)); // OBSOLETE: Used to be _scriptSize - s.skip(4, VER(12), VER(22)); // OBSOLETE: Used to be _heapSize + s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _bufSize + s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _scriptSize + s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _heapSize - s.skip(4, VER(12), VER(19)); // OBSOLETE: Used to be _numExports - s.skip(4, VER(12), VER(19)); // OBSOLETE: Used to be _numSynonyms + s.skip(4, VER(14), VER(19)); // OBSOLETE: Used to be _numExports + s.skip(4, VER(14), VER(19)); // OBSOLETE: Used to be _numSynonyms s.syncAsSint32LE(_lockers); // Sync _objects. This is a hashmap, and we use the following on disk format: @@ -482,7 +425,7 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) { } } - s.skip(4, VER(12), VER(20)); // OBSOLETE: Used to be _localsOffset + s.skip(4, VER(14), VER(20)); // OBSOLETE: Used to be _localsOffset s.syncAsSint32LE(_localsSegment); s.syncAsSint32LE(_markedAsDeleted); @@ -599,14 +542,6 @@ void SoundCommandParser::reconstructPlayList(int savegame_version) { (*i)->soundRes = 0; } if ((*i)->status == kSoundPlaying) { - if (savegame_version < 14) { - (*i)->dataInc = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(dataInc)); - (*i)->signal = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal)); - - if (_soundVersion >= SCI_VERSION_1_LATE) - (*i)->volume = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol)); - } - processPlaySound((*i)->soundObj); } } @@ -628,6 +563,42 @@ void StringTable::saveLoadWithSerializer(Common::Serializer &ser) { } #endif +void GfxPalette::palVarySaveLoadPalette(Common::Serializer &s, Palette *palette) { + s.syncBytes(palette->mapping, 256); + s.syncAsUint32LE(palette->timestamp); + for (int i = 0; i < 256; i++) { + s.syncAsByte(palette->colors[i].used); + s.syncAsByte(palette->colors[i].r); + s.syncAsByte(palette->colors[i].g); + s.syncAsByte(palette->colors[i].b); + } + s.syncBytes(palette->intensity, 256); +} + +void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) { + if (s.getVersion() < 24) + return; + + if (s.isLoading() && _palVaryResourceId != -1) + palVaryRemoveTimer(); + + s.syncAsSint32LE(_palVaryResourceId); + if (_palVaryResourceId != -1) { + palVarySaveLoadPalette(s, &_palVaryOriginPalette); + palVarySaveLoadPalette(s, &_palVaryTargetPalette); + s.syncAsSint16LE(_palVaryStep); + s.syncAsSint16LE(_palVaryStepStop); + s.syncAsSint16LE(_palVaryDirection); + s.syncAsUint16LE(_palVaryTicks); + s.syncAsSint32LE(_palVaryPaused); + } + + if (s.isLoading() && _palVaryResourceId != -1) { + _palVarySignal = 0; + palVaryInstallTimer(); + } +} + void SegManager::reconstructStack(EngineState *s) { DataStack *stack = (DataStack *)(_heap[findSegmentByType(SEG_TYPE_STACK)]); s->stack_base = stack->_entries; @@ -722,6 +693,7 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savenam meta.script0_size = script0->size; meta.game_object_offset = g_sci->getGameObject().offset; + // Checking here again if (s->executionStackBase) { warning("Cannot save from below kernel function"); return false; @@ -804,7 +776,6 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { s->_msgState = new MessageState(s->_segMan); s->abortScriptProcessing = kAbortLoadGame; - s->shrinkStackToBase(); } bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata *meta) { diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h index 9a882f2bf7..fc254ba33d 100644 --- a/engines/sci/engine/savegame.h +++ b/engines/sci/engine/savegame.h @@ -36,8 +36,8 @@ namespace Sci { struct EngineState; enum { - CURRENT_SAVEGAME_VERSION = 23, - MINIMUM_SAVEGAME_VERSION = 12 + CURRENT_SAVEGAME_VERSION = 24, + MINIMUM_SAVEGAME_VERSION = 14 }; // Savegame metadata diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index a293f81d2f..645094d9ec 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -434,13 +434,19 @@ void Script::initialiseClasses(SegManager *segMan) { } if (isClass) { - // WORKAROUND for an invalid species access in the demo of LSL2 + // WORKAROUNDs for off-by-one script errors if (g_sci->getGameId() == GID_LSL2 && g_sci->isDemo() && species == (int)segMan->classTableSize()) segMan->resizeClassTable(segMan->classTableSize() + 1); + if (g_sci->getGameId() == GID_LSL3 && !g_sci->isDemo() && _nr == 500 && species == (int)segMan->classTableSize()) + segMan->resizeClassTable(segMan->classTableSize() + 1); + if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 93 && species == (int)segMan->classTableSize()) + segMan->resizeClassTable(segMan->classTableSize() + 1); + if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 99 && species == (int)segMan->classTableSize()) + segMan->resizeClassTable(segMan->classTableSize() + 1); if (species < 0 || species >= (int)segMan->classTableSize()) - error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d\n", - species, species, segMan->classTableSize(), _nr); + error("Invalid species %d(0x%x) unknown max %d(0x%x) while instantiating script %d\n", + species, species, segMan->classTableSize(), segMan->classTableSize(), _nr); SegmentId segmentId = segMan->getScriptSegment(_nr); segMan->setClassOffset(species, make_reg(segmentId, classpos)); @@ -468,8 +474,10 @@ void Script::initialiseObjectsSci0(SegManager *segMan, SegmentId segmentId) { obj->initSpecies(segMan, addr); if (!obj->initBaseObject(segMan, addr)) { - if (_nr == 202 && g_sci->getGameId() == GID_KQ5 && g_sci->getSciLanguage() == K_LANG_FRENCH) { - // Script 202 of KQ5 French has an invalid object. This is non-fatal. + if (_nr == 202 && g_sci->getGameId() == GID_KQ5) { + // WORKAROUND: Script 202 of KQ5 French and German + // (perhaps Spanish too?) has an invalid object. + // This is non-fatal. Refer to bug #3035396. } else { error("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr)); } diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index 915a6fa994..9c08526fbb 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -488,48 +488,7 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); printf("%04x: ", seeker); - while (seeker < _seeker) { - unsigned char nextitem = script->data [seeker++]; - if (nextitem == 0xFF) - printf("\n%04x: ", seeker); - else if (nextitem >= 0xF0) { - switch (nextitem) { - case 0xf0: - printf(", "); - break; - case 0xf1: - printf("& "); - break; - case 0xf2: - printf("/ "); - break; - case 0xf3: - printf("( "); - break; - case 0xf4: - printf(") "); - break; - case 0xf5: - printf("[ "); - break; - case 0xf6: - printf("] "); - break; - case 0xf7: - printf("# "); - break; - case 0xf8: - printf("< "); - break; - case 0xf9: - printf("> "); - break; - } - } else { - nextitem = nextitem << 8 | script->data [seeker++]; - printf("%s[%03x] ", vocab->getAnyWordFromGroup(nextitem), nextitem); - } - } + vocab->debugDecipherSaidBlock(script->data + seeker); printf("\n"); break; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index ef2279e492..25cf1d069f 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -508,7 +508,7 @@ List *SegManager::lookupList(reg_t addr) { return &(lt->_table[addr.offset]); } -Node *SegManager::lookupNode(reg_t addr) { +Node *SegManager::lookupNode(reg_t addr, bool stopOnDiscarded) { if (addr.isNull()) return NULL; // Non-error null @@ -522,6 +522,9 @@ Node *SegManager::lookupNode(reg_t addr) { NodeTable *nt = (NodeTable *)_heap[addr.segment]; if (!nt->isValidEntry(addr.offset)) { + if (!stopOnDiscarded) + return NULL; + error("Attempt to use invalid or discarded reference %04x:%04x as list node", PRINT_REG(addr)); return NULL; } diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index a7f5f8517f..e0808dbb1b 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -232,7 +232,7 @@ public: * @param addr The address to resolve * @return The list node referenced, or NULL on error */ - Node *lookupNode(reg_t addr); + Node *lookupNode(reg_t addr, bool stopOnDiscarded = true); // 8. Hunk Memory diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index 00480743cc..f5eb9eb73a 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -104,6 +104,8 @@ void Kernel::mapSelectors() { FIND_SELECTOR2(b_incr, "b-incr"); FIND_SELECTOR(xStep); FIND_SELECTOR(yStep); + FIND_SELECTOR(xLast); + FIND_SELECTOR(yLast); FIND_SELECTOR(moveSpeed); FIND_SELECTOR(canBeHere); // cantBeHere FIND_SELECTOR(heading); @@ -176,6 +178,13 @@ void Kernel::mapSelectors() { FIND_SELECTOR(dimmed); FIND_SELECTOR(fore); FIND_SELECTOR(back); + FIND_SELECTOR(fixPriority); + FIND_SELECTOR(mirrored); + FIND_SELECTOR(useInsetRect); + FIND_SELECTOR(inTop); + FIND_SELECTOR(inLeft); + FIND_SELECTOR(inBottom); + FIND_SELECTOR(inRight); #endif } @@ -236,7 +245,7 @@ void invokeSelector(EngineState *s, reg_t object, int selectorId, xstack->sp += argc + 2; xstack->fp += argc + 2; - run_vm(s, false); // Start a new vm + run_vm(s); // Start a new vm } SelectorType lookupSelector(SegManager *segMan, reg_t obj_location, Selector selectorId, ObjVarRef *varp, reg_t *fptr) { diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index acb7912d8d..661290f58c 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -69,6 +69,7 @@ struct SelectorCache { Selector dx, dy; ///< Deltas Selector b_movCnt, b_i1, b_i2, b_di, b_xAxis, b_incr; ///< Various Bresenham vars Selector xStep, yStep; ///< BR adjustments + Selector xLast, yLast; ///< BR last position of client Selector moveSpeed; ///< Used for DoBresen Selector canBeHere; ///< Funcselector: Checks for movement validity in SCI0 Selector heading, mover; ///< Used in DoAvoider @@ -141,6 +142,12 @@ struct SelectorCache { Selector fore; Selector back; Selector dimmed; + + Selector fixPriority; + Selector mirrored; + + Selector useInsetRect; + Selector inTop, inLeft, inBottom, inRight; #endif }; diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index 36b03c0ad9..a069344d61 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -324,4 +324,17 @@ Common::String SciEngine::strSplit(const char *str, const char *sep) { return retval; } +void SciEngine::checkVocabularySwitch() { + uint16 parserLanguage = 1; + if (SELECTOR(parseLang) != -1) + parserLanguage = readSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(parseLang)); + + if (parserLanguage != _vocabularyLanguage) { + delete _vocabulary; + _vocabulary = new Vocabulary(_resMan, parserLanguage > 1 ? true : false); + _vocabulary->reset(); + _vocabularyLanguage = parserLanguage; + } +} + } // End of namespace Sci diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp index 85089e74c8..55e18613e0 100644 --- a/engines/sci/engine/static_selectors.cpp +++ b/engines/sci/engine/static_selectors.cpp @@ -168,6 +168,20 @@ Common::StringArray Kernel::checkStaticSelectorNames() { names[274] = "syncTime"; names[275] = "syncCue"; + } else if (g_sci->getGameId() == GID_ISLANDBRAIN) { + // The demo of Island of Dr. Brain needs the init selector set to match up with the full + // game's workaround - bug #3035033 + if (names.size() < 111) + names.resize(111); + + names[110] = "init"; + } else if (g_sci->getGameId() == GID_LAURABOW2) { + // The floppy of version needs the open selector set to match up with the CD version's + // workaround - bug #3035694 + if (names.size() < 190) + names.resize(190); + + names[189] = "open"; } #ifdef ENABLE_SCI32 diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 8108440102..b7f6896a48 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -38,6 +38,7 @@ #include "sci/engine/seg_manager.h" #include "sci/engine/selector.h" // for SELECTOR #include "sci/engine/gc.h" +#include "sci/engine/workarounds.h" namespace Sci { @@ -48,86 +49,6 @@ const reg_t TRUE_REG = {0, 1}; #define SCI_XS_CALLEE_LOCALS ((SegmentId)-1) -#define END Script_None - -opcode_format g_opcode_formats[128][4] = { - /*00*/ - {Script_None}, {Script_None}, {Script_None}, {Script_None}, - /*04*/ - {Script_None}, {Script_None}, {Script_None}, {Script_None}, - /*08*/ - {Script_None}, {Script_None}, {Script_None}, {Script_None}, - /*0C*/ - {Script_None}, {Script_None}, {Script_None}, {Script_None}, - /*10*/ - {Script_None}, {Script_None}, {Script_None}, {Script_None}, - /*14*/ - {Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END}, - /*18*/ - {Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None}, - /*1C*/ - {Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END}, - /*20*/ - {Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END}, - /*24 (24=ret)*/ - {Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid}, - /*28*/ - {Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END}, - /*2C*/ - {Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid}, - /*30*/ - {Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, - /*34*/ - {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, - /*38*/ - {Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None}, - /*3C*/ - {Script_None}, {Script_None}, {Script_None}, {Script_Word}, - /*40-4F*/ - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - /*50-5F*/ - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - /*60-6F*/ - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - /*70-7F*/ - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}, - {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END} -}; -#undef END - -// TODO: script_adjust_opcode_formats should probably be part of the -// constructor (?) of a VirtualMachine or a ScriptManager class. -void script_adjust_opcode_formats() { - if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) { - g_opcode_formats[op_lofsa][0] = Script_Offset; - g_opcode_formats[op_lofss][0] = Script_Offset; - } - -#ifdef ENABLE_SCI32 - // In SCI32, some arguments are now words instead of bytes - if (getSciVersion() >= SCI_VERSION_2) { - g_opcode_formats[op_calle][2] = Script_Word; - g_opcode_formats[op_callk][1] = Script_Word; - g_opcode_formats[op_super][1] = Script_Word; - g_opcode_formats[op_send][0] = Script_Word; - g_opcode_formats[op_self][0] = Script_Word; - g_opcode_formats[op_call][1] = Script_Word; - g_opcode_formats[op_callb][1] = Script_Word; - } -#endif -} - /** * Adds an entry to the top of the execution stack. * @@ -248,7 +169,7 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in } else { // WORKAROUND: Mixed-Up Mother Goose tries to use an invalid parameter in Event::new(). // Just skip around it here so we don't error out in validate_arithmetic. - if (g_sci->getGameId() == GID_MOTHERGOOSE && getSciVersion() <= SCI_VERSION_1_1 && type == VAR_PARAM && index == 1) + if (g_sci->getGameId() == GID_MOTHERGOOSE && type == VAR_PARAM && index == 1) return false; debugC(2, kDebugLevelVM, "%s", txt.c_str()); @@ -262,109 +183,33 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in return true; } -struct SciTrackOriginReply { - int scriptNr; - Common::String objectName; - Common::String methodName; - int localCallOffset; -}; - -static reg_t trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin) { - EngineState *state = g_sci->getEngineState(); - ExecStack *lastCall = state->xs; - Script *local_script = state->_segMan->getScriptIfLoaded(lastCall->local_segment); - int curScriptNr = local_script->getScriptNumber(); - - if (lastCall->debugLocalCallOffset != -1) { - // if lastcall was actually a local call search back for a real call - Common::List<ExecStack>::iterator callIterator = state->_executionStack.end(); - while (callIterator != state->_executionStack.begin()) { - callIterator--; - ExecStack loopCall = *callIterator; - if ((loopCall.debugSelector != -1) || (loopCall.debugExportId != -1)) { - lastCall->debugSelector = loopCall.debugSelector; - lastCall->debugExportId = loopCall.debugExportId; - break; - } - } - } - - Common::String curObjectName = state->_segMan->getObjectName(lastCall->sendp); - Common::String curMethodName; - const SciGameId gameId = g_sci->getGameId(); - - if (lastCall->type == EXEC_STACK_TYPE_CALL) { - if (lastCall->debugSelector != -1) { - curMethodName = g_sci->getKernel()->getSelectorName(lastCall->debugSelector); - } else if (lastCall->debugExportId != -1) { - curObjectName = ""; - curMethodName = curMethodName.printf("export %d", lastCall->debugExportId); - } - } - - if (workaroundList) { - // Search if there is a workaround for this one - const SciWorkaroundEntry *workaround; - int16 inheritanceLevel = 0; - Common::String searchObjectName = curObjectName; - reg_t searchObject = lastCall->sendp; - do { - workaround = workaroundList; - while (workaround->objectName) { - if (workaround->gameId == gameId && workaround->scriptNr == curScriptNr - && ((workaround->inheritanceLevel == -1) || (workaround->inheritanceLevel == inheritanceLevel)) - && (workaround->objectName == searchObjectName) - && workaround->methodName == curMethodName && workaround->localCallOffset == lastCall->debugLocalCallOffset && workaround->index == index) { - // Workaround found - return workaround->newValue; - } - workaround++; - } - - // Go back to the parent - inheritanceLevel++; - searchObject = state->_segMan->getObject(searchObject)->getSuperClassSelector(); - if (!searchObject.isNull()) - searchObjectName = state->_segMan->getObjectName(searchObject); - } while (!searchObject.isNull()); // no parent left? - } +static bool validate_unsignedInteger(reg_t reg, uint16 &integer) { + if (reg.segment) + return false; + integer = reg.offset; + return true; +} - // give caller origin data - trackOrigin->objectName = curObjectName; - trackOrigin->methodName = curMethodName; - trackOrigin->scriptNr = curScriptNr; - trackOrigin->localCallOffset = lastCall->debugLocalCallOffset; - return make_reg(0xFFFF, 0xFFFF); +static bool validate_signedInteger(reg_t reg, int16 &integer) { + if (reg.segment) + return false; + integer = (int16)reg.offset; + return true; } -// gameID, scriptNr,lvl, object-name, method-name, call, index, replace -static const SciWorkaroundEntry uninitializedReadWorkarounds[] = { - { GID_LAURABOW2, 24, 0, "gcWin", "open", -1, 5, { 0, 0xf } }, // is used as priority for game menu - { GID_FREDDYPHARKAS, 24, 0, "gcWin", "open", -1, 5, { 0, 0xf } }, // is used as priority for game menu - { GID_FREDDYPHARKAS, 31, 0, "quitWin", "open", -1, 5, { 0, 0xf } }, // is used as priority for game menu - { GID_GK2, 11, 0, "", "export 10", -1, 3, { 0, 0 } }, // called when the game starts - { GID_JONES, 232, 0, "weekendText", "draw", 0x3d3, 0, { 0, 0 } }, // jones/cd only - gets called during the game - { GID_JONES, 255, 0, "", "export 0", -1, 13, { 0, 0 } }, // jones/ega&vga only - called when the game starts - { GID_JONES, 255, 0, "", "export 0", -1, 14, { 0, 0 } }, // jones/ega&vga only - called when the game starts - { GID_LSL1, 720, 0, "rm720", "init", -1, 0, { 0, 0 } }, // age check room - { GID_LSL3, 997, 0, "TheMenuBar", "handleEvent", -1, 1, { 0, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's') - { GID_LSL6, 928, -1, "Narrator", "startText", -1, 0, { 0, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class - { GID_ISLANDBRAIN, 140, 0, "piece", "init", -1, 3, { 0, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0 - { GID_ISLANDBRAIN, 268, 0, "anElement", "select", -1, 0, { 0, 0 } }, // elements puzzle, gets used before super TextIcon - { GID_KQ5, 0, 0, "", "export 29", -1, 3, { 0, 0 } }, // called when playing harp for the harpies, is used for kDoAudio - { GID_KQ5, 25, 0, "rm025", "doit", -1, 0, { 0, 0 } }, // inside witch forest, where the walking rock is - { GID_KQ6, 903, 0, "controlWin", "open", -1, 4, { 0, 0 } }, // when opening the controls window (save, load etc) - { GID_KQ6, 500, 0, "rm500", "init", -1, 0, { 0, 0 } }, // going to island of the beast - { GID_KQ6, 520, 0, "rm520", "init", -1, 0, { 0, 0 } }, // going to boiling water trap on beast isle - { GID_KQ6, 30, 0, "rats", "changeState", -1, 0, { 0, 0 } }, // rats in the catacombs - { GID_LSL6, 85, 0, "washcloth", "doVerb", -1, 0, { 0, 0 } }, // washcloth in inventory - { GID_SQ1, 703, 0, "", "export 1", -1, 0, { 0, 0 } }, // sub that's called from several objects while on sarien battle cruiser - { GID_SQ1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { 0, 0 } }, // export 1, but called locally (when shooting at aliens) - { GID_SQ4, 928, 0, "Narrator", "startText", -1, 1000, { 0, 1 } }, // sq4cd: method returns this to the caller - { GID_SQ6, 0, 0, "SQ6", "init", -1, 2, { 0, 0 } }, // called when the game starts - { GID_SQ6, 64950, 0, "View", "handleEvent", -1, 0, { 0, 0 } }, // called when pressing "Start game" in the main menu - SCI_WORKAROUNDENTRY_TERMINATOR -}; +extern const char *opcodeNames[]; // from scriptdebug.cpp + +static reg_t arithmetic_lookForWorkaround(const byte opcode, const SciWorkaroundEntry *workaroundList, reg_t value1, reg_t value2) { + SciTrackOriginReply originReply; + SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, workaroundList, &originReply); + if (solution.type == WORKAROUND_NONE) + error("%s on non-integer (%04x:%04x, %04x:%04x) from method %s::%s (script %d, room %d, localCall %x)", + opcodeNames[opcode], PRINT_REG(value1), PRINT_REG(value2), originReply.objectName.c_str(), + originReply.methodName.c_str(), originReply.scriptNr, g_sci->getEngineState()->currentRoomNumber(), + originReply.localCallOffset); + assert(solution.type == WORKAROUND_FAKE); + return make_reg(0, solution.value); +} static reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, int index, reg_t default_value) { if (validate_variable(r, stack_base, type, max, index)) { @@ -374,9 +219,13 @@ static reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, i // Uninitialized read on a temp // We need to find correct replacements for each situation manually SciTrackOriginReply originReply; - r[index] = trackOriginAndFindWorkaround(index, uninitializedReadWorkarounds, &originReply); - if ((r[index].segment == 0xFFFF) && (r[index].offset == 0xFFFF)) - error("Uninitialized read for temp %d from method %s::%s (script %d, localCall %x)", index, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset); + SciWorkaroundSolution solution = trackOriginAndFindWorkaround(index, uninitializedReadWorkarounds, &originReply); + if (solution.type == WORKAROUND_NONE) + error("Uninitialized read for temp %d from method %s::%s (script %d, room %d, localCall %x)", + index, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, + g_sci->getEngineState()->currentRoomNumber(), originReply.localCallOffset); + assert(solution.type == WORKAROUND_FAKE); + r[index] = make_reg(0, solution.value); break; } case VAR_PARAM: @@ -440,12 +289,9 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i #define WRITE_VAR(type, index, value) validate_write_var(s->variables[type], s->stack_base, type, s->variablesMax[type], index, value, s->_segMan, g_sci->getKernel()) #define WRITE_VAR16(type, index, value) WRITE_VAR(type, index, make_reg(0, value)); -#define ACC_ARITHMETIC_L(op) make_reg(0, (op validate_arithmetic(s->r_acc))) - // Operating on the stack // 16 bit: #define PUSH(v) PUSH32(make_reg(0, v)) -#define POP() (validate_arithmetic(POP32())) // 32 bit: #define PUSH32(a) (*(validate_stack_addr(s, (s->xs->sp)++)) = (a)) #define POP32() (*(validate_stack_addr(s, --(s->xs->sp)))) @@ -618,29 +464,17 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt if (argc > 1) { // argc can indeed be bigger than 1 in some cases, and it's usually the - // result of a script bug + // result of a script bug. Usually these aren't fatal. const char *objectName = s->_segMan->getObjectName(send_obj); - if (!strcmp(objectName, "Sq4GlobalNarrator") && selector == 606) { - // SQ4 has a script bug in the Sq4GlobalNarrator object when invoking the - // returnVal selector, which doesn't affect gameplay, thus don't diplay it - } else if (!strcmp(objectName, "longSong") && selector == 3 && g_sci->getGameId() == GID_QFG1) { - // QFG1VGA has a script bug in the longSong object when invoking the - // loop selector, which doesn't affect gameplay, thus don't diplay it - } else if (!strcmp(objectName, "PuzPiece") && selector == 77 && g_sci->getGameId() == GID_CASTLEBRAIN) { - // Castle of Dr. Brain has a script bug in the PuzPiece object when invoking - // the value selector, which doesn't affect gameplay, thus don't display it - } else { - // Unknown script bug, show it. Usually these aren't fatal. - reg_t oldReg = *varp.getPointer(s->_segMan); - reg_t newReg = argp[1]; - const char *selectorName = g_sci->getKernel()->getSelectorName(selector).c_str(); - warning("send_selector(): argc = %d while modifying variable selector " - "%x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x", - argc, selector, selectorName, PRINT_REG(send_obj), - objectName, PRINT_REG(oldReg), PRINT_REG(newReg)); - } + reg_t oldReg = *varp.getPointer(s->_segMan); + reg_t newReg = argp[1]; + const char *selectorName = g_sci->getKernel()->getSelectorName(selector).c_str(); + debug(2, "send_selector(): argc = %d while modifying variable selector " + "%x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x", + argc, selector, selectorName, PRINT_REG(send_obj), + objectName, PRINT_REG(oldReg), PRINT_REG(newReg)); } { @@ -813,6 +647,61 @@ static void addKernelCallToExecStack(EngineState *s, int kernelCallNr, int argc, xstack->type = EXEC_STACK_TYPE_KERNEL; } +static void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *kernelSubCall, EngineState *s, int argc, reg_t *argv, reg_t result) { + Kernel *kernel = g_sci->getKernel(); + if (!kernelSubCall) { + printf("k%s: ", kernelCall->name); + } else { + int callNameLen = strlen(kernelCall->name); + if (strncmp(kernelCall->name, kernelSubCall->name, callNameLen) == 0) { + const char *subCallName = kernelSubCall->name + callNameLen; + printf("k%s(%s): ", kernelCall->name, subCallName); + } else { + printf("k%s(%s): ", kernelCall->name, kernelSubCall->name); + } + } + for (int parmNr = 0; parmNr < argc; parmNr++) { + if (parmNr) + printf(", "); + uint16 regType = kernel->findRegType(argv[parmNr]); + if (regType & SIG_TYPE_NULL) + printf("0"); + else if (regType & SIG_TYPE_UNINITIALIZED) + printf("UNINIT"); + else if (regType & SIG_IS_INVALID) + printf("INVALID"); + else if (regType & SIG_TYPE_INTEGER) + printf("%d", argv[parmNr].offset); + else { + printf("%04x:%04x", PRINT_REG(argv[parmNr])); + switch (regType) { + case SIG_TYPE_OBJECT: + printf(" (%s)", s->_segMan->getObjectName(argv[parmNr])); + break; + case SIG_TYPE_REFERENCE: + if (kernelCall->function == kSaid) { + SegmentRef saidSpec = s->_segMan->dereference(argv[parmNr]); + if (saidSpec.isRaw) { + printf(" ('"); + g_sci->getVocabulary()->debugDecipherSaidBlock(saidSpec.raw); + printf("')"); + } else { + printf(" (non-raw said-spec)"); + } + } else { + printf(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str()); + } + default: + break; + } + } + } + if (result.segment) + printf(" = %04x:%04x\n", PRINT_REG(result)); + else + printf(" = %d\n", result.offset); +} + static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) { Kernel *kernel = g_sci->getKernel(); @@ -826,17 +715,24 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) { && !kernel->signatureMatch(kernelCall.signature, argc, argv)) { // signature mismatch, check if a workaround is available SciTrackOriginReply originReply; - reg_t workaround; - workaround = trackOriginAndFindWorkaround(0, kernelCall.workarounds, &originReply); - if ((workaround.segment == 0xFFFF) && (workaround.offset == 0xFFFF)) { + SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kernelCall.workarounds, &originReply); + switch (solution.type) { + case WORKAROUND_NONE: kernel->signatureDebug(kernelCall.signature, argc, argv); - error("[VM] k%s[%x]: signature mismatch via method %s::%s (script %d, localCall %x)", kernelCall.name, kernelCallNr, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset); - } - // FIXME: implement some real workaround type logic - ignore call, still do call etc. - if (workaround.segment == 2) - s->r_acc = make_reg(0, workaround.offset); - if (workaround.segment) + error("[VM] k%s[%x]: signature mismatch via method %s::%s (script %d, room %d, localCall 0x%x)", + kernelCall.name, kernelCallNr, originReply.objectName.c_str(), originReply.methodName.c_str(), + originReply.scriptNr, s->currentRoomNumber(), originReply.localCallOffset); + break; + case WORKAROUND_IGNORE: // don't do kernel call, leave acc alone + return; + case WORKAROUND_STILLCALL: // call kernel anyway + break; + case WORKAROUND_FAKE: // don't do kernel call, fake acc + s->r_acc = make_reg(0, solution.value); return; + default: + error("unknown workaround type"); + } } @@ -844,6 +740,9 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) { if (!kernelCall.subFunctionCount) { addKernelCallToExecStack(s, kernelCallNr, argc, argv); s->r_acc = kernelCall.function(s, argc, argv); + + if (kernelCall.debugLogging) + logKernelCall(&kernelCall, NULL, s, argc, argv, s->r_acc); } else { // Sub-functions available, check signature and call that one directly if (argc < 1) @@ -860,43 +759,41 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) { if (kernelSubCall.signature && !kernel->signatureMatch(kernelSubCall.signature, argc, argv)) { // Signature mismatch SciTrackOriginReply originReply; - reg_t workaround; - workaround = trackOriginAndFindWorkaround(0, kernelSubCall.workarounds, &originReply); - if ((workaround.segment == 0xFFFF) && (workaround.offset == 0xFFFF)) { + SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kernelSubCall.workarounds, &originReply); + switch (solution.type) { + case WORKAROUND_NONE: { kernel->signatureDebug(kernelSubCall.signature, argc, argv); int callNameLen = strlen(kernelCall.name); if (strncmp(kernelCall.name, kernelSubCall.name, callNameLen) == 0) { const char *subCallName = kernelSubCall.name + callNameLen; - error("[VM] k%s(%s): signature mismatch via method %s::%s (script %d, localCall %x)", kernelCall.name, subCallName, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset); + error("[VM] k%s(%s): signature mismatch via method %s::%s (script %d, room %d, localCall %x)", + kernelCall.name, subCallName, originReply.objectName.c_str(), originReply.methodName.c_str(), + originReply.scriptNr, s->currentRoomNumber(), originReply.localCallOffset); } - error("[VM] k%s: signature mismatch via method %s::%s (script %d, localCall %x)", kernelSubCall.name, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset); + error("[VM] k%s: signature mismatch via method %s::%s (script %d, room %d, localCall %x)", + kernelSubCall.name, originReply.objectName.c_str(), originReply.methodName.c_str(), + originReply.scriptNr, s->currentRoomNumber(), originReply.localCallOffset); + break; } - // FIXME: implement some real workaround type logic - ignore call, still do call etc. - if (workaround.segment == 2) - s->r_acc = make_reg(0, workaround.offset); - if (workaround.segment) + case WORKAROUND_IGNORE: // don't do kernel call, leave acc alone + return; + case WORKAROUND_STILLCALL: // call kernel anyway + break; + case WORKAROUND_FAKE: // don't do kernel call, fake acc + s->r_acc = make_reg(0, solution.value); return; + default: + error("unknown workaround type"); + } } if (!kernelSubCall.function) error("[VM] k%s: subfunction-id %d requested, but not available", kernelCall.name, subId); addKernelCallToExecStack(s, kernelCallNr, argc, argv); s->r_acc = kernelSubCall.function(s, argc, argv); - } - -#if 0 - // Used for debugging - Common::String debugMsg = Common::String::printf("%s [0x%x]", kernelCall.name, kernelCallNr) + - Common::String::printf(", %d params: ", argc) + - " ("; - for (int i = 0; i < argc; i++) { - debugMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i])); - debugMsg += (i == argc - 1 ? ")" : ", "); - } - - debugMsg += ", result: " + Common::String::printf("%04x:%04x", PRINT_REG(s->r_acc)); - debug("%s", debugMsg.c_str()); -#endif + if (kernelSubCall.debugLogging) + logKernelCall(&kernelCall, &kernelSubCall, s, argc, argv, s->r_acc); + } // Remove callk stack frame again, if there's still an execution stack if (s->_executionStack.begin() != s->_executionStack.end()) @@ -978,7 +875,7 @@ int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4]) return offset; } -void run_vm(EngineState *s, bool restoring) { +void run_vm(EngineState *s) { assert(s); int temp; @@ -999,8 +896,7 @@ void run_vm(EngineState *s, bool restoring) { if (!local_script) error("run_vm(): program counter gone astray (local_script pointer is null)"); - if (!restoring) - s->executionStackBase = s->_executionStack.size() - 1; + s->executionStackBase = s->_executionStack.size() - 1; s->variablesSegment[VAR_TEMP] = s->variablesSegment[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK); s->variablesBase[VAR_TEMP] = s->variablesBase[VAR_PARAM] = s->stack_base; @@ -1059,9 +955,7 @@ void run_vm(EngineState *s, bool restoring) { g_sci->_debugState.breakpointWasHit = false; } Console *con = g_sci->getSciDebugger(); - if (con->isAttached()) { - con->onFrame(); - } + con->onFrame(); if (s->xs->sp < s->xs->fp) error("run_vm(): stack underflow, sp: %04x:%04x, fp: %04x:%04x", @@ -1080,10 +974,15 @@ void run_vm(EngineState *s, bool restoring) { switch (opcode) { - case op_bnot: // 0x00 (00) + case op_bnot: { // 0x00 (00) // Binary not - s->r_acc = ACC_ARITHMETIC_L(0xffff ^ /*acc*/); + int16 value; + if (validate_signedInteger(s->r_acc, value)) + s->r_acc = make_reg(0, 0xffff ^ value); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, NULL_REG); break; + } case op_add: // 0x01 (01) r_temp = POP32(); @@ -1148,45 +1047,96 @@ void run_vm(EngineState *s, bool restoring) { } break; - case op_mul: // 0x03 (03) - s->r_acc = ACC_ARITHMETIC_L(((int16)POP()) * (int16)/*acc*/); + case op_mul: { // 0x03 (03) + r_temp = POP32(); + int16 value1, value2; + if (validate_signedInteger(s->r_acc, value1) && validate_signedInteger(r_temp, value2)) + s->r_acc = make_reg(0, value1 * value2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, r_temp); break; + } case op_div: { // 0x04 (04) - int16 divisor = signed_validate_arithmetic(s->r_acc); - s->r_acc = make_reg(0, (divisor != 0 ? ((int16)POP()) / divisor : 0)); + r_temp = POP32(); + int16 divisor, dividend; + if (validate_signedInteger(s->r_acc, divisor) && validate_signedInteger(r_temp, dividend)) + s->r_acc = make_reg(0, (divisor != 0 ? dividend / divisor : 0)); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeDivWorkarounds, s->r_acc, r_temp); break; } + case op_mod: { // 0x05 (05) - int16 modulo = signed_validate_arithmetic(s->r_acc); - s->r_acc = make_reg(0, (modulo != 0 ? ((int16)POP()) % modulo : 0)); + r_temp = POP32(); + int16 modulo, value; + if (validate_signedInteger(s->r_acc, modulo) && validate_signedInteger(r_temp, value)) + s->r_acc = make_reg(0, (modulo != 0 ? value % modulo : 0)); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, r_temp); break; } - case op_shr: // 0x06 (06) + + case op_shr: { // 0x06 (06) // Shift right logical - s->r_acc = ACC_ARITHMETIC_L(((uint16)POP()) >> /*acc*/); + r_temp = POP32(); + uint16 value, shiftCount; + if (validate_unsignedInteger(r_temp, value) && validate_unsignedInteger(s->r_acc, shiftCount)) + s->r_acc = make_reg(0, value >> shiftCount); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); break; + } - case op_shl: // 0x07 (07) + case op_shl: { // 0x07 (07) // Shift left logical - s->r_acc = ACC_ARITHMETIC_L(((uint16)POP()) << /*acc*/); + r_temp = POP32(); + uint16 value, shiftCount; + if (validate_unsignedInteger(r_temp, value) && validate_unsignedInteger(s->r_acc, shiftCount)) + s->r_acc = make_reg(0, value << shiftCount); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); break; + } - case op_xor: // 0x08 (08) - s->r_acc = ACC_ARITHMETIC_L(POP() ^ /*acc*/); + case op_xor: { // 0x08 (08) + r_temp = POP32(); + uint16 value1, value2; + if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2)) + s->r_acc = make_reg(0, value1 ^ value2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); break; + } - case op_and: // 0x09 (09) - s->r_acc = ACC_ARITHMETIC_L(POP() & /*acc*/); + case op_and: { // 0x09 (09) + r_temp = POP32(); + uint16 value1, value2; + if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2)) + s->r_acc = make_reg(0, value1 & value2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); break; + } - case op_or: // 0x0a (10) - s->r_acc = ACC_ARITHMETIC_L(POP() | /*acc*/); + case op_or: { // 0x0a (10) + r_temp = POP32(); + uint16 value1, value2; + if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2)) + s->r_acc = make_reg(0, value1 | value2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeOrWorkarounds, r_temp, s->r_acc); break; + } - case op_neg: // 0x0b (11) - s->r_acc = ACC_ARITHMETIC_L(-/*acc*/); + case op_neg: { // 0x0b (11) + int16 value; + if (validate_signedInteger(s->r_acc, value)) + s->r_acc = make_reg(0, -value); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, NULL_REG); break; + } case op_not: // 0x0c (12) s->r_acc = make_reg(0, !(s->r_acc.offset || s->r_acc.segment)); @@ -1220,12 +1170,17 @@ void run_vm(EngineState *s, bool restoring) { s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset); } else if (r_temp.segment && !s->r_acc.segment) { if (s->r_acc.offset >= 1000) - error("[VM] op_gt: comparsion between a pointer and number"); - // Pseudo-WORKAROUND: sierra allows any pointer <-> value comparsion + error("[VM] op_gt: comparison between a pointer and number"); + // Pseudo-WORKAROUND: Sierra allows any pointer <-> value comparison // Happens in SQ1, room 28, when throwing the water at Orat s->r_acc = make_reg(0, 1); - } else - s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) > (int16)/*acc*/); + } else { + int16 compare1, compare2; + if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2)) + s->r_acc = make_reg(0, compare1 > compare2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); + } break; case op_ge_: // 0x10 (16) @@ -1236,8 +1191,13 @@ void run_vm(EngineState *s, bool restoring) { if (r_temp.segment != s->r_acc.segment) warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc)); s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset); - } else - s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) >= (int16)/*acc*/); + } else { + int16 compare1, compare2; + if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2)) + s->r_acc = make_reg(0, compare1 >= compare2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); + } break; case op_lt_: // 0x11 (17) @@ -1250,12 +1210,17 @@ void run_vm(EngineState *s, bool restoring) { s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset); } else if (r_temp.segment && !s->r_acc.segment) { if (s->r_acc.offset >= 1000) - error("[VM] op_lt: comparsion between a pointer and number"); - // Pseudo-WORKAROUND: sierra allows any pointer <-> value comparsion + error("[VM] op_lt: comparison between a pointer and number"); + // Pseudo-WORKAROUND: Sierra allows any pointer <-> value comparison // Happens in SQ1, room 58, when giving id-card to robot s->r_acc = make_reg(0, 1); - } else - s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) < (int16)/*acc*/); + } else { + int16 compare1, compare2; + if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2)) + s->r_acc = make_reg(0, compare1 < compare2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); + } break; case op_le_: // 0x12 (18) @@ -1266,8 +1231,13 @@ void run_vm(EngineState *s, bool restoring) { if (r_temp.segment != s->r_acc.segment) warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc)); s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset); - } else - s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) <= (int16)/*acc*/); + } else { + int16 compare1, compare2; + if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2)) + s->r_acc = make_reg(0, compare1 <= compare2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); + } break; case op_ugt_: // 0x13 (19) @@ -1289,8 +1259,13 @@ void run_vm(EngineState *s, bool restoring) { s->r_acc = make_reg(0, 1); else if (r_temp.segment && s->r_acc.segment) s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset); - else - s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) > /*acc*/); + else { + uint16 compare1, compare2; + if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2)) + s->r_acc = make_reg(0, compare1 > compare2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); + } break; case op_uge_: // 0x14 (20) @@ -1303,8 +1278,13 @@ void run_vm(EngineState *s, bool restoring) { s->r_acc = make_reg(0, 1); else if (r_temp.segment && s->r_acc.segment) s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset); - else - s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) >= /*acc*/); + else { + uint16 compare1, compare2; + if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2)) + s->r_acc = make_reg(0, compare1 >= compare2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); + } break; case op_ult_: // 0x15 (21) @@ -1313,12 +1293,18 @@ void run_vm(EngineState *s, bool restoring) { r_temp = POP32(); // See above - if (r_temp.segment && (s->r_acc == make_reg(0, 1000))) + // PQ2 japanese compares pointers to 2000 to find out if its a pointer or a resourceid + if (r_temp.segment && (s->r_acc == make_reg(0, 1000) || (s->r_acc == make_reg(0, 2000)))) s->r_acc = NULL_REG; else if (r_temp.segment && s->r_acc.segment) s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset); - else - s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) < /*acc*/); + else { + uint16 compare1, compare2; + if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2)) + s->r_acc = make_reg(0, compare1 < compare2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); + } break; case op_ule_: // 0x16 (22) @@ -1331,8 +1317,13 @@ void run_vm(EngineState *s, bool restoring) { s->r_acc = NULL_REG; else if (r_temp.segment && s->r_acc.segment) s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset); - else - s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) <= /*acc*/); + else { + uint16 compare1, compare2; + if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2)) + s->r_acc = make_reg(0, compare1 <= compare2); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc); + } break; case op_bt: // 0x17 (23) @@ -1647,32 +1638,55 @@ void run_vm(EngineState *s, bool restoring) { validate_property(obj, (opparams[0] >> 1)) = POP32(); break; - case op_ipToa: // 0x35 (53) - // Incement Property and copy To Accumulator - s->r_acc = validate_property(obj, (opparams[0] >> 1)); - s->r_acc = validate_property(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(1 + /*acc*/); + case op_ipToa: { // 0x35 (53) + // Increment Property and copy To Accumulator + reg_t &opProperty = validate_property(obj, opparams[0] >> 1); + uint16 valueProperty; + if (validate_unsignedInteger(opProperty, valueProperty)) + s->r_acc = make_reg(0, valueProperty + 1); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG); + opProperty = s->r_acc; break; + } case op_dpToa: { // 0x36 (54) // Decrement Property and copy To Accumulator - s->r_acc = validate_property(obj, (opparams[0] >> 1)); - s->r_acc = validate_property(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(-1 + /*acc*/); + reg_t &opProperty = validate_property(obj, opparams[0] >> 1); + uint16 valueProperty; + if (validate_unsignedInteger(opProperty, valueProperty)) + s->r_acc = make_reg(0, valueProperty - 1); + else + s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeDptoaWorkarounds, opProperty, NULL_REG); + opProperty = s->r_acc; break; } - case op_ipTos: // 0x37 (55) + case op_ipTos: { // 0x37 (55) // Increment Property and push to Stack - validate_arithmetic(validate_property(obj, (opparams[0] >> 1))); - temp = ++validate_property(obj, (opparams[0] >> 1)).offset; - PUSH(temp); + reg_t &opProperty = validate_property(obj, opparams[0] >> 1); + uint16 valueProperty; + if (validate_unsignedInteger(opProperty, valueProperty)) + valueProperty++; + else + valueProperty = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG).offset; + opProperty = make_reg(0, valueProperty); + PUSH(valueProperty); break; + } - case op_dpTos: // 0x38 (56) + case op_dpTos: { // 0x38 (56) // Decrement Property and push to Stack - validate_arithmetic(validate_property(obj, (opparams[0] >> 1))); - temp = --validate_property(obj, (opparams[0] >> 1)).offset; - PUSH(temp); + reg_t &opProperty = validate_property(obj, opparams[0] >> 1); + uint16 valueProperty; + if (validate_unsignedInteger(opProperty, valueProperty)) + valueProperty--; + else + valueProperty = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG).offset; + opProperty = make_reg(0, valueProperty); + PUSH(valueProperty); break; + } case op_lofsa: // 0x39 (57) // Load Offset to Accumulator diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h index 81ec4f1c61..ee22e03310 100644 --- a/engines/sci/engine/vm.h +++ b/engines/sci/engine/vm.h @@ -315,9 +315,8 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, * It executes the code on s->heap[pc] until it hits a 'ret' operation * while (stack_base == stack_pos). Requires s to be set up correctly. * @param[in] s The state to use - * @param[in] restoring true if s has just been restored, false otherwise */ -void run_vm(EngineState *s, bool restoring); +void run_vm(EngineState *s); /** * Debugger functionality diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp new file mode 100644 index 0000000000..0db73e34d5 --- /dev/null +++ b/engines/sci/engine/workarounds.cpp @@ -0,0 +1,357 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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 "sci/engine/kernel.h" +#include "sci/engine/state.h" +#include "sci/engine/vm.h" +#include "sci/engine/workarounds.h" + +#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, -1, 0, NULL, NULL, -1, 0, { WORKAROUND_NONE, 0 } } + +namespace Sci { + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry opcodeDivWorkarounds[] = { + { GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // when entering inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry opcodeOrWorkarounds[] = { + { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xcc6, 0, { WORKAROUND_FAKE, 0 } }, // when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #3034464 + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call, index, workaround +const SciWorkaroundEntry opcodeDptoaWorkarounds[] = { + { GID_LSL6, 360, 938, 0, "ROsc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking through tile in the shower room initial cycles get set to an object instead of 2, we fix this by setting 1 after decrease + { GID_LSL6HIRES, 360,64938, 0, "ROsc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking through tile in the shower room initial cycles get set to an object instead of 2, we fix this by setting 1 after decrease + { GID_SQ5, 200, 939, 0, "Osc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when going back to bridge the crew is goofing off, we get an object as cycle count + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry uninitializedReadWorkarounds[] = { + { GID_CNICK_KQ, 200, 0, 1, "Character", "<noname 446>", -1, 504, { WORKAROUND_FAKE, 0 } }, // checkers, like in hoyle 3 + { GID_CNICK_KQ, 200, 0, 1, "Character", "<noname 446>", -1, 505, { WORKAROUND_FAKE, 0 } }, // checkers, like in hoyle 3 + { GID_CNICK_KQ, -1, 700, 0, "gcWindow", "<noname 183>", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu, like in hoyle 3 + { GID_CNICK_LONGBOW, 0, 0, 0, "RH Budget", "<noname 110>", -1, 1, { WORKAROUND_FAKE, 0 } }, // when starting the game + { GID_ECOQUEST, -1, -1, 0, NULL, "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // almost clicking anywhere triggers this in almost all rooms + { GID_FREDDYPHARKAS, -1, 24, 0, "gcWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu + { GID_FREDDYPHARKAS, -1, 31, 0, "quitWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu + { GID_GK1, -1, 64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // sometimes when walk-clicking + { GID_GK2, -1, 11, 0, "", "export 10", -1, 3, { WORKAROUND_FAKE, 0 } }, // called when the game starts + { GID_GK2, -1, 11, 0, "", "export 10", -1, 4, { WORKAROUND_FAKE, 0 } }, // called during the game + { GID_HOYLE1, 4, 104, 0, "GinRummyCardList", "calcRuns", -1, 4, { WORKAROUND_FAKE, 0 } }, // Gin Rummy / right when the game starts + { GID_HOYLE1, 5, 204, 0, "tableau", "checkRuns", -1, 2, { WORKAROUND_FAKE, 0 } }, // Cribbage / during the game + { GID_HOYLE3, -1, 0, 1, "Character", "say", -1, 504, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something + { GID_HOYLE3, -1, 0, 1, "Character", "say", -1, 505, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something + { GID_HOYLE3, -1, 700, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu + { GID_ISLANDBRAIN, 140, 140, 0, "piece", "init", -1, 3, { WORKAROUND_FAKE, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0 + { GID_ISLANDBRAIN, 200, 268, 0, "anElement", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // elements puzzle, gets used before super TextIcon + { GID_JONES, 1, 232, 0, "weekendText", "draw", 0x3d3, 0, { WORKAROUND_FAKE, 0 } }, // jones/cd only - gets called during the game + { GID_JONES, 1, 255, 0, "", "export 0", -1, 13, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends + { GID_JONES, 1, 255, 0, "", "export 0", -1, 14, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends + { GID_JONES, 764, 255, 0, "", "export 0", -1, 13, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts + { GID_JONES, 764, 255, 0, "", "export 0", -1, 14, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts + { GID_KQ5, -1, 0, 0, "", "export 29", -1, 3, { WORKAROUND_FAKE, 0 } }, // called when playing harp for the harpies or when aborting dialog in toy shop, is used for kDoAudio - bug #3034700 + { GID_KQ5, 25, 25, 0, "rm025", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // inside witch forest, when going to the room where the walking rock is + { GID_KQ6, -1, 30, 0, "rats", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // rats in the catacombs (temps 1 - 5) - bugs #3034597, #3035495, #3035824 + { GID_KQ6, 210, 210, 0, "rm210", "scriptCheck", -1, 0, { WORKAROUND_FAKE, 1 } }, // using inventory in that room - bug #3034565 + { GID_KQ6, 500, 500, 0, "rm500", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // going to island of the beast + { GID_KQ6, 520, 520, 0, "rm520", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // going to boiling water trap on beast isle + { GID_KQ6, -1, 903, 0, "controlWin", "open", -1, 4, { WORKAROUND_FAKE, 0 } }, // when opening the controls window (save, load etc) + { GID_KQ7, 30, 64996, 0, "User", "handleEvent", -1, 1, { WORKAROUND_FAKE, 0 } }, // called when pushing a keyboard key + { GID_LAURABOW, 44, 967, 0, "myIcon", "cycle", -1, 1, { WORKAROUND_FAKE, 0 } }, // second dialog box after the intro, when talking with Lillian - bug #3034985 + { GID_LAURABOW2, -1, 24, 0, "gcWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu + { GID_LAURABOW2, -1, 21, 0, "dropCluesCode", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // when asking some questions (e.g. the reporter about the burglary, or the policeman about Ziggy) - bugs #3035068, #3036274 + { GID_LAURABOW2, 240, 240, 0, "sSteveAnimates", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // Steve Dorian's idle animation at the docks - bug #3036291 + { GID_LONGBOW, -1, 213, 0, "clear", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When giving an aswer using the druid hand sign code in any room + { GID_LONGBOW, -1, 213, 0, "letter", "handleEvent", 0xa8, 1, { WORKAROUND_FAKE, 0 } }, // When using the druid hand sign code in any room - bug #3036601 + { GID_LSL1, 250, 250, 0, "increase", "handleEvent", -1, 2, { WORKAROUND_FAKE, 0 } }, // casino, playing game, increasing bet + { GID_LSL1, 720, 720, 0, "rm720", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // age check room + { GID_LSL2, 38, 38, 0, "cloudScript", "changeState", -1, 1, { WORKAROUND_FAKE, 0 } }, // entering the room in the middle deck of the ship - bug #3036483 + { GID_LSL3, 340, 340, 0, "ComicScript", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // right after entering the 3 ethnic groups inside comedy club (temps 200, 201, 202, 203) + { GID_LSL3, -1, 997, 0, "TheMenuBar", "handleEvent", -1, 1, { WORKAROUND_FAKE, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's') + { GID_LSL6, -1, 85, 0, "washcloth", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // washcloth in inventory + { GID_LSL6, -1, 928, -1, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class + { GID_LSL6HIRES, 0, 85, 0, "LL6Inv", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // on startup + { GID_LSL6HIRES, -1, 64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // at least when entering swimming pool area + { GID_LSL6HIRES, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game + { GID_MOTHERGOOSE, 18, 992, 0, "AIPath", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // DEMO: Called when walking north from mother goose's house two screens + { GID_MOTHERGOOSEHIRES,-1,64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // right when clicking on a child at the start and probably also later + { GID_MOTHERGOOSEHIRES,-1,64950, 1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // see above + { GID_QFG2, -1, 71, 0, "theInvSheet", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // accessing the inventory + { GID_QFG2, -1, 701, 0, "Alley", "at", -1, 0, { WORKAROUND_FAKE, 0 } }, // when walking inside the alleys in the town - bug #3035835 + { GID_QFG3, 330, 330, 0, "rajahTeller", "doChild", -1, 0, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Tarna" + { GID_QFG3, 330, 330, 0, "rajahTeller", "doChild", -1, 1, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Rajah" - bug #3036390 + { GID_SQ1, 103, 103, 0, "hand", "internalEvent", -1, 1, { WORKAROUND_FAKE, 0 } }, // spanish (and maybe early versions?) only: when moving cursor over input pad + { GID_SQ1, 103, 103, 0, "hand", "internalEvent", -1, 2, { WORKAROUND_FAKE, 0 } }, // spanish (and maybe early versions?) only: when moving cursor over input pad + { GID_SQ1, -1, 703, 0, "", "export 1", -1, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser + { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens) + { GID_SQ4, -1, 398, 0, "showBox", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // sq4cd: called when rummaging in Software Excess bargain bin + { GID_SQ4, -1, 928, 0, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // sq4cd: method returns this to the caller + { GID_SQ6, 100, 0, 0, "SQ6", "init", -1, 2, { WORKAROUND_FAKE, 0 } }, // called when the game starts + { GID_SQ6, 100, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu + { GID_SQ6, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kAbs_workarounds[] = { + { GID_HOYLE1, 1, 1, 0, "room1", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // crazy eights - called with objects instead of integers + { GID_HOYLE1, 2, 2, 0, "room2", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // old maid - called with objects instead of integers + { GID_HOYLE1, 3, 3, 0, "room3", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // hearts - called with objects instead of integers + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kCelHigh_workarounds[] = { + { GID_SQ1, 1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kCelWide_workarounds[] = { + { GID_SQ1, 1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #3035720 + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kDisplay_workarounds[] = { + { GID_ISLANDBRAIN, 300, 300, 0, "geneDude", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the gene explanation chart - a parameter is an object + { GID_SQ4, 391, 391, 0, "doCatalog", "mode", 0x84, 0, { WORKAROUND_IGNORE, 0 } }, // clicking on catalog in roboter sale - a parameter is an object + { GID_SQ4, 391, 391, 0, "choosePlug", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // ordering connector in roboter sale - a parameter is an object + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kDisposeScript_workarounds[] = { + { GID_LAURABOW, 777, 777, 0, "myStab", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the will is signed, parameter 0 is an object - bug #3034907 + { GID_QFG1, -1, 64, 0, "rm64", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving graveyard, parameter 0 is an object + { GID_SQ4, 150, 151, 0, "fightScript", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during fight with vohaul, parameter 0 is an object + { GID_SQ4, 150, 152, 0, "driveCloseUp", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download", parameter 0 is an object + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kDoSoundFade_workarounds[] = { + { GID_CAMELOT, -1, 989, 0, "rmMusic", "fade", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called frequently with a NULL reference (i.e. 0:0) - bug #3035149 + { GID_KQ1, -1, 989, 0, "gameSound", "fade", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called in several scenes (e.g. graham cracker) with 0:0 + { GID_KQ6, 105, 989, 0, "globalSound", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object + { GID_KQ6, 460, 989, 0, "globalSound2", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #3034567 + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kGetAngle_workarounds[] = { + { GID_KQ6, 740, 752, 0, "throwDazzle", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // after the Genie is exposed in the Palace (short and long ending), it starts shooting lightning bolts around. An extra 5th parameter is passed - bug #3034610 + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kFindKey_workarounds[] = { + { GID_ECOQUEST2, 100, 999, 0, "myList", "contains", -1, 0, { WORKAROUND_FAKE, 0 } }, // When Noah Greene gives Adam the Ecorder, and just before the game gives a demonstration, a null reference to a list is passed - bug #3035186 + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kGraphDrawLine_workarounds[] = { + { GID_ISLANDBRAIN, 300, 300, 0, "dudeViewer", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when looking at the gene explanation chart, gets called with 1 extra parameter + { GID_SQ1, 43, 43, 0, "someoneDied", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when ordering beer, gets called with 1 extra parameter + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kGraphSaveBox_workarounds[] = { + { GID_CASTLEBRAIN, 420, 427, 0, "alienIcon", "select", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when selecting a card during the alien card game, gets called with 1 extra parameter + { GID_ISLANDBRAIN, 290, 291, 0, "upElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // when testing in the elevator puzzle, gets called with 1 argument less - 15 is on stack - bug #3034485 + { GID_ISLANDBRAIN, 290, 291, 0, "downElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // see above + { GID_ISLANDBRAIN, 290, 291, 0, "correctElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // see above (when testing the correct solution) + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = { + { GID_LSL6, -1, 85, 0, "rScroller", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring (sometimes), same as the one below + { GID_LSL6, -1, 85, 0, "lScroller", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring (sometimes), same as the one below + { GID_LSL6, -1, 86, 0, "LL6Inv", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring, is called with hunk segment, but hunk is not allocated at that time + // ^^ TODO: check, if this is really a script error or an issue with our restore code + { GID_LSL6, -1, 86, 0, "LL6Inv", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[] = { + { GID_LSL6, -1, 0, 0, "LSL6", "hideControls", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when giving the bungee key to merrily (room 240) and at least in room 650 too - gets called with additional 5th parameter + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kGraphFillBoxAny_workarounds[] = { + { GID_SQ4, -1, 818, 0, "iconTextSwitch", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = { + { GID_SQ4, 405, 405, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, 406, 406, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified + { GID_SQ4, -1, 704, 0, "shootEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified + { GID_KQ5, -1, 981, 0, "myWindow", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #3036331 + { GID_KQ5, -1, 995, 0, "invW", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kIsObject_workarounds[] = { + { GID_GK1, 50, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #3034519 + { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #3035262 + { GID_QFG3, -1, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kMemory_workarounds[] = { + { GID_LAURABOW2, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train, talking to Mr. Augustini, etc. - bug #3034490 + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kNewWindow_workarounds[] = { + { GID_ECOQUEST, -1, 981, 0, "SysWindow", "<noname 178>", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // EcoQuest 1 demo uses an in-between interpreter from SCI1 to SCI1.1. It's SCI1.1, but uses the SCI1 semantics for this call - bug #3035057 + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = { + { GID_QFG4, 100, 100, 0, "doMovie", "<noname 144>", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #3034506 + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kSetPort_workarounds[] = { + { GID_LSL6, 740, 740, 0, "rm740", "drawPic", -1, 0, { WORKAROUND_IGNORE, 0 } }, // ending scene, is called with additional 3 (!) parameters + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, call,index, workaround +const SciWorkaroundEntry kUnLoad_workarounds[] = { + { GID_CAMELOT, 921, 921, 1, "Script", "changeState", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: While showing Camelot (and other places), the reference is invalid - bug #3035000 + { GID_CAMELOT, 921, 921, 1, "Script", "init", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When being attacked by the boar (and other places), the reference is invalid - bug #3035000 + { GID_CASTLEBRAIN, 320, 377, 0, "SWord", "upDate", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after solving the cross-word-puzzle, trying to unload invalid reference + { GID_CASTLEBRAIN, 320, 377, 0, "theWord", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // 2nd word puzzle, when exiting before solving, trying to unload invalid reference - bug #3034473 + { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after talking to the dolphin the first time + { GID_LAURABOW2, 1, 1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902 + { GID_LAURABOW2, 2, 2, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902 + { GID_LAURABOW2, 4, 4, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: inside the museum, a 3rd parameter is passed by accident - bug #3034902 + { GID_LAURABOW2, 6, 6, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the murder, a 3rd parameter is passed by accident - bug #3034902 + { GID_LAURABOW2, 7, 7, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the logo is shown, a 3rd parameter is passed by accident - bug #3034902 + { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident + { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident + { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident + { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin) { + EngineState *state = g_sci->getEngineState(); + ExecStack *lastCall = state->xs; + Script *local_script = state->_segMan->getScriptIfLoaded(lastCall->local_segment); + int curScriptNr = local_script->getScriptNumber(); + + if (lastCall->debugLocalCallOffset != -1) { + // if lastcall was actually a local call search back for a real call + Common::List<ExecStack>::iterator callIterator = state->_executionStack.end(); + while (callIterator != state->_executionStack.begin()) { + callIterator--; + ExecStack loopCall = *callIterator; + if ((loopCall.debugSelector != -1) || (loopCall.debugExportId != -1)) { + lastCall->debugSelector = loopCall.debugSelector; + lastCall->debugExportId = loopCall.debugExportId; + break; + } + } + } + + Common::String curObjectName = state->_segMan->getObjectName(lastCall->sendp); + Common::String curMethodName; + const SciGameId gameId = g_sci->getGameId(); + const int curRoomNumber = state->currentRoomNumber(); + + if (lastCall->type == EXEC_STACK_TYPE_CALL) { + if (lastCall->debugSelector != -1) { + curMethodName = g_sci->getKernel()->getSelectorName(lastCall->debugSelector); + } else if (lastCall->debugExportId != -1) { + curObjectName = ""; + curMethodName = curMethodName.printf("export %d", lastCall->debugExportId); + } + } + + if (workaroundList) { + // Search if there is a workaround for this one + const SciWorkaroundEntry *workaround; + int16 inheritanceLevel = 0; + Common::String searchObjectName = curObjectName; + reg_t searchObject = lastCall->sendp; + do { + workaround = workaroundList; + while (workaround->methodName) { + if (workaround->gameId == gameId + && ((workaround->scriptNr == -1) || (workaround->scriptNr == curScriptNr)) + && ((workaround->roomNr == -1) || (workaround->roomNr == curRoomNumber)) + && ((workaround->inheritanceLevel == -1) || (workaround->inheritanceLevel == inheritanceLevel)) + && ((workaround->objectName == NULL) || (workaround->objectName == searchObjectName)) + && workaround->methodName == curMethodName && workaround->localCallOffset == lastCall->debugLocalCallOffset + && ((workaround->index == -1) || (workaround->index == index))) { + // Workaround found + return workaround->newValue; + } + workaround++; + } + + // Go back to the parent + inheritanceLevel++; + searchObject = state->_segMan->getObject(searchObject)->getSuperClassSelector(); + if (!searchObject.isNull()) + searchObjectName = state->_segMan->getObjectName(searchObject); + } while (!searchObject.isNull()); // no parent left? + } + + // give caller origin data + trackOrigin->objectName = curObjectName; + trackOrigin->methodName = curMethodName; + trackOrigin->scriptNr = curScriptNr; + trackOrigin->localCallOffset = lastCall->debugLocalCallOffset; + + SciWorkaroundSolution noneFound; + noneFound.type = WORKAROUND_NONE; + noneFound.value = 0; + return noneFound; +} + +} // End of namespace Sci diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h new file mode 100644 index 0000000000..d509d300d7 --- /dev/null +++ b/engines/sci/engine/workarounds.h @@ -0,0 +1,100 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SCI_ENGINE_WORKAROUNDS_H +#define SCI_ENGINE_WORKAROUNDS_H + +#include "sci/engine/vm_types.h" +#include "sci/sci.h" + +namespace Sci { + +enum SciWorkaroundType { + WORKAROUND_NONE, // only used by terminator or when no workaround was found + WORKAROUND_IGNORE, // ignore kernel call + WORKAROUND_STILLCALL, // still do kernel call + WORKAROUND_FAKE // fake kernel call / replace temp value / fake opcode +}; + +struct SciTrackOriginReply { + int scriptNr; + Common::String objectName; + Common::String methodName; + int localCallOffset; +}; + +struct SciWorkaroundSolution { + SciWorkaroundType type; + uint16 value; +}; + +/** + * A structure describing a 'workaround' for a SCI script bug. + * + * Arrays of SciWorkaroundEntry instances are terminated by + * a fake entry in which "objectName" is NULL. + */ +struct SciWorkaroundEntry { + SciGameId gameId; + int roomNr; + int scriptNr; + int16 inheritanceLevel; + const char *objectName; + const char *methodName; + int localCallOffset; + int index; + SciWorkaroundSolution newValue; +}; + +extern const SciWorkaroundEntry opcodeDivWorkarounds[]; +extern const SciWorkaroundEntry opcodeOrWorkarounds[]; +extern const SciWorkaroundEntry opcodeDptoaWorkarounds[]; +extern const SciWorkaroundEntry uninitializedReadWorkarounds[]; +extern const SciWorkaroundEntry kAbs_workarounds[]; +extern const SciWorkaroundEntry kCelHigh_workarounds[]; +extern const SciWorkaroundEntry kCelWide_workarounds[]; +extern const SciWorkaroundEntry kDisplay_workarounds[]; +extern const SciWorkaroundEntry kDisposeScript_workarounds[]; +extern const SciWorkaroundEntry kDoSoundFade_workarounds[]; +extern const SciWorkaroundEntry kFindKey_workarounds[]; +extern const SciWorkaroundEntry kGetAngle_workarounds[]; +extern const SciWorkaroundEntry kGraphDrawLine_workarounds[]; +extern const SciWorkaroundEntry kGraphSaveBox_workarounds[]; +extern const SciWorkaroundEntry kGraphRestoreBox_workarounds[]; +extern const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[]; +extern const SciWorkaroundEntry kGraphFillBoxAny_workarounds[]; +extern const SciWorkaroundEntry kGraphRedrawBox_workarounds[]; +extern const SciWorkaroundEntry kIsObject_workarounds[]; +extern const SciWorkaroundEntry kMemory_workarounds[]; +extern const SciWorkaroundEntry kNewWindow_workarounds[]; +extern const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[]; +extern const SciWorkaroundEntry kSetPort_workarounds[]; +extern const SciWorkaroundEntry kUnLoad_workarounds[]; + +extern SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin); + +} // End of namespace Sci + +#endif // SCI_ENGINE_WORKAROUNDS_H diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index a100dda27c..5923e501cf 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -313,6 +313,10 @@ SciEvent EventManager::getScummVMEvent() { input.type = SCI_EVENT_MOUSE_PRESS; input.data = 2; break; + case Common::EVENT_MBUTTONDOWN: + input.type = SCI_EVENT_MOUSE_PRESS; + input.data = 3; + break; case Common::EVENT_LBUTTONUP: input.type = SCI_EVENT_MOUSE_RELEASE; input.data = 1; @@ -321,6 +325,10 @@ SciEvent EventManager::getScummVMEvent() { input.type = SCI_EVENT_MOUSE_RELEASE; input.data = 2; break; + case Common::EVENT_MBUTTONUP: + input.type = SCI_EVENT_MOUSE_RELEASE; + input.data = 3; + break; // Misc events case Common::EVENT_QUIT: @@ -335,16 +343,20 @@ SciEvent EventManager::getScummVMEvent() { return input; } -SciEvent EventManager::getSciEvent(unsigned int mask) { - //sci_event_t error_event = { SCI_EVT_ERROR, 0, 0, 0 }; - SciEvent event = { 0, 0, 0, 0 }; - +void EventManager::updateScreen() { // Update the screen here, since it's called very often. // Throttle the screen update rate to 60fps. if (g_system->getMillis() - g_sci->getEngineState()->_screenUpdateTime >= 1000 / 60) { g_system->updateScreen(); g_sci->getEngineState()->_screenUpdateTime = g_system->getMillis(); } +} + +SciEvent EventManager::getSciEvent(unsigned int mask) { + //sci_event_t error_event = { SCI_EVT_ERROR, 0, 0, 0 }; + SciEvent event = { 0, 0, 0, 0 }; + + EventManager::updateScreen(); // Get all queued events from graphics driver do { diff --git a/engines/sci/event.h b/engines/sci/event.h index 30098b0f6e..fade7dd337 100644 --- a/engines/sci/event.h +++ b/engines/sci/event.h @@ -114,6 +114,7 @@ public: EventManager(bool fontIsExtended); ~EventManager(); + void updateScreen(); SciEvent getSciEvent(unsigned int mask); bool getUsesNewKeyboardDirectionType(); diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp index 6fb427500d..c637ef8374 100644 --- a/engines/sci/graphics/animate.cpp +++ b/engines/sci/graphics/animate.cpp @@ -28,6 +28,7 @@ #include "graphics/primitives.h" #include "sci/sci.h" +#include "sci/event.h" #include "sci/engine/kernel.h" #include "sci/engine/state.h" #include "sci/engine/selector.h" @@ -98,8 +99,12 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) { if (_s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit()) return true; // Stop processing - // Lookup node again, since the nodetable it was in may have been reallocated - curNode = _s->_segMan->lookupNode(curAddress); + // Lookup node again, since the nodetable it was in may have been reallocated. + // The node might have been deallocated at this point (e.g. LSL2, room 42), + // in which case the node reference will be null and the loop will stop below. + // If the node is deleted from kDeleteKey, it won't have a successor node, thus + // list processing will stop here (which is what SSCI does). + curNode = _s->_segMan->lookupNode(curAddress, false); } if (curNode) { @@ -538,9 +543,6 @@ void GfxAnimate::animateShowPic() { _transitions->doit(picRect); if (previousCursorState) _cursor->kernelShow(); - - // We set SCI1.1 priority band information here - _ports->priorityBandsRecall(); } void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t *argv) { @@ -596,6 +598,10 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t if (_lastCastData.size() > 1) _s->_throttleTrigger = true; + // We update the screen here as well, some scenes like EQ1 credits run w/o calling kGetEvent thus we wouldn't update + // screen at all + g_sci->getEventManager()->updateScreen(); + _ports->setPort(oldPort); } diff --git a/engines/sci/graphics/cache.cpp b/engines/sci/graphics/cache.cpp index 8caa28b3a1..25475c727f 100644 --- a/engines/sci/graphics/cache.cpp +++ b/engines/sci/graphics/cache.cpp @@ -90,11 +90,11 @@ GfxView *GfxCache::getView(GuiResourceId viewId) { } int16 GfxCache::kernelViewGetCelWidth(GuiResourceId viewId, int16 loopNo, int16 celNo) { - return getView(viewId)->getCelInfo(loopNo, celNo)->width; + return getView(viewId)->getCelInfo(loopNo, celNo)->scriptWidth; } int16 GfxCache::kernelViewGetCelHeight(GuiResourceId viewId, int16 loopNo, int16 celNo) { - return getView(viewId)->getCelInfo(loopNo, celNo)->height; + return getView(viewId)->getCelInfo(loopNo, celNo)->scriptHeight; } int16 GfxCache::kernelViewGetLoopCount(GuiResourceId viewId) { diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp index 31c2b210ce..0a186115d0 100644 --- a/engines/sci/graphics/compare.cpp +++ b/engines/sci/graphics/compare.cpp @@ -70,7 +70,7 @@ uint16 GfxCompare::isOnControl(uint16 screenMask, const Common::Rect &rect) { return result; } -bool GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list) { +reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list) { reg_t curAddress = list->first; Node *curNode = _segMan->lookupNode(curAddress); reg_t curObject; @@ -96,14 +96,14 @@ bool GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &c curRect.left < checkRect.right && curRect.bottom > checkRect.top && curRect.top < checkRect.bottom) { - return false; + return curObject; } } } curAddress = curNode->succ; curNode = _segMan->lookupNode(curAddress); } - return true; + return NULL_REG; } uint16 GfxCompare::kernelOnControl(byte screenMask, const Common::Rect &rect) { @@ -132,15 +132,39 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) { view = _cache->getView(viewId); - if (view->isSci2Hires()) - _screen->adjustToUpscaledCoordinates(y, x); +#ifdef ENABLE_SCI32 + switch (getSciVersion()) { + case SCI_VERSION_2: + if (view->isSci2Hires()) + _screen->adjustToUpscaledCoordinates(y, x); + break; + case SCI_VERSION_2_1: + _coordAdjuster->fromScriptToDisplay(y, x); + break; + default: + break; + } +#endif view->getCelRect(loopNo, celNo, x, y, z, celRect); - if (view->isSci2Hires()) { - _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left); - _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); +#ifdef ENABLE_SCI32 + switch (getSciVersion()) { + case SCI_VERSION_2: + if (view->isSci2Hires()) { + _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left); + _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); + } + break; + case SCI_VERSION_2_1: { + _coordAdjuster->fromDisplayToScript(celRect.top, celRect.left); + _coordAdjuster->fromDisplayToScript(celRect.bottom, celRect.right); + break; + } + default: + break; } +#endif if (lookupSelector(_segMan, objectReference, SELECTOR(nsTop), NULL, NULL) == kSelectorVariable) { writeSelectorValue(_segMan, objectReference, SELECTOR(nsLeft), celRect.left); @@ -150,11 +174,11 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) { } } -bool GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) { +reg_t GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) { Common::Rect checkRect; Common::Rect adjustedRect; uint16 signal, controlMask; - bool result; + uint16 result; checkRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft)); checkRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop)); @@ -163,22 +187,22 @@ bool GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) { if (!checkRect.isValidRect()) { // can occur in Iceman - HACK? TODO: is this really occuring in sierra sci? check this warning("kCan(t)BeHere - invalid rect %d, %d -> %d, %d", checkRect.left, checkRect.top, checkRect.right, checkRect.bottom); - return false; + return NULL_REG; } adjustedRect = _coordAdjuster->onControl(checkRect); signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits)); - result = (isOnControl(GFX_SCREEN_MASK_CONTROL, adjustedRect) & controlMask) ? false : true; - if ((result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) { + result = isOnControl(GFX_SCREEN_MASK_CONTROL, adjustedRect) & controlMask; + if ((!result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) { List *list = _segMan->lookupList(listReference); if (!list) error("kCanBeHere called with non-list as parameter"); - result = canBeHereCheckRectList(curObject, checkRect, list); + return canBeHereCheckRectList(curObject, checkRect, list); } - return result; + return make_reg(0, result); } bool GfxCompare::kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Point position) { @@ -205,21 +229,39 @@ void GfxCompare::kernelBaseSetter(reg_t object) { if (viewId == 0xFFFF) // invalid view return; - GfxView *tmpView = _cache->getView(viewId); + // must be something wrong with this TODO check - currently it breaks qfg3 right after the intro + //uint16 scaleSignal = 0; + //if (getSciVersion() >= SCI_VERSION_1_1) { + // scaleSignal = readSelectorValue(_segMan, object, SELECTOR(scaleSignal)) & kScaleSignalDoScaling; + // if (scaleSignal) { + // int16 scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY)); + // if (scaleY < 64) + // scaleSignal = 0; + // } + //} + Common::Rect celRect; - if (tmpView->isSci2Hires()) - _screen->adjustToUpscaledCoordinates(y, x); + //if (!scaleSignal) { + GfxView *tmpView = _cache->getView(viewId); + if (tmpView->isSci2Hires()) + _screen->adjustToUpscaledCoordinates(y, x); - tmpView->getCelRect(loopNo, celNo, x, y, z, celRect); + tmpView->getCelRect(loopNo, celNo, x, y, z, celRect); - if (tmpView->isSci2Hires()) { - _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left); - _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); - } + if (tmpView->isSci2Hires()) { + _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left); + _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); + } - celRect.bottom = y + 1; - celRect.top = celRect.bottom - yStep; + celRect.bottom = y + 1; + celRect.top = celRect.bottom - yStep; + //} else { + // celRect.left = readSelectorValue(_segMan, object, SELECTOR(nsLeft)); + // celRect.right = readSelectorValue(_segMan, object, SELECTOR(nsRight)); + // celRect.top = readSelectorValue(_segMan, object, SELECTOR(nsTop)); + // celRect.bottom = readSelectorValue(_segMan, object, SELECTOR(nsBottom)); + //} writeSelectorValue(_segMan, object, SELECTOR(brLeft), celRect.left); writeSelectorValue(_segMan, object, SELECTOR(brRight), celRect.right); diff --git a/engines/sci/graphics/compare.h b/engines/sci/graphics/compare.h index be461cdc5b..1079f5f98c 100644 --- a/engines/sci/graphics/compare.h +++ b/engines/sci/graphics/compare.h @@ -42,7 +42,7 @@ public: uint16 kernelOnControl(byte screenMask, const Common::Rect &rect); void kernelSetNowSeen(reg_t objectReference); - bool kernelCanBeHere(reg_t curObject, reg_t listReference); + reg_t kernelCanBeHere(reg_t curObject, reg_t listReference); bool kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Point position); void kernelBaseSetter(reg_t object); @@ -60,7 +60,7 @@ private: * *different* from checkObject, has a brRect which is contained inside * checkRect. */ - bool canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list); + reg_t canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list); }; } // End of namespace Sci diff --git a/engines/sci/graphics/controls.cpp b/engines/sci/graphics/controls.cpp index ff5a91eed4..5891413be8 100644 --- a/engines/sci/graphics/controls.cpp +++ b/engines/sci/graphics/controls.cpp @@ -75,7 +75,9 @@ void GfxControls::drawListControl(Common::Rect rect, reg_t obj, int16 maxChars, // draw UP/DOWN arrows // we draw UP arrow one pixel lower than sierra did, because it looks nicer. Also the DOWN arrow has one pixel // line inbetween as well - workerRect.top++; + // They "fixed" this in SQ4 by having the arrow character start one pixel line later, we don't adjust there + if (g_sci->getGameId() != GID_SQ4) + workerRect.top++; _text16->Box(controlListUpArrow, 0, workerRect, SCI_TEXT16_ALIGNMENT_CENTER, 0); workerRect.top = workerRect.bottom - 10; _text16->Box(controlListDownArrow, 0, workerRect, SCI_TEXT16_ALIGNMENT_CENTER, 0); @@ -89,7 +91,7 @@ void GfxControls::drawListControl(Common::Rect rect, reg_t obj, int16 maxChars, _text16->SetFont(fontId); fontSize = _ports->_curPort->fontHeight; _ports->penColor(_ports->_curPort->penClr); _ports->backColor(_ports->_curPort->backClr); - workerRect.bottom = workerRect.top + 9; + workerRect.bottom = workerRect.top + fontSize; lastYpos = rect.bottom - fontSize; // Write actual text diff --git a/engines/sci/graphics/coordadjuster.cpp b/engines/sci/graphics/coordadjuster.cpp index 9481a68f13..bbeade87b5 100644 --- a/engines/sci/graphics/coordadjuster.cpp +++ b/engines/sci/graphics/coordadjuster.cpp @@ -31,6 +31,7 @@ #include "sci/engine/selector.h" #include "sci/graphics/coordadjuster.h" #include "sci/graphics/ports.h" +#include "sci/graphics/screen.h" namespace Sci { @@ -88,32 +89,41 @@ Common::Rect GfxCoordAdjuster16::pictureGetDisplayArea() { #ifdef ENABLE_SCI32 GfxCoordAdjuster32::GfxCoordAdjuster32(SegManager *segMan) : _segMan(segMan) { + scriptsRunningWidth = 0; + scriptsRunningHeight = 0; } GfxCoordAdjuster32::~GfxCoordAdjuster32() { } void GfxCoordAdjuster32::kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject) { - //int16 resY = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resY)); - //int16 resX = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resX)); - //*x = ( *x * _screen->getWidth()) / resX; - //*y = ( *y * _screen->getHeight()) / resY; - x -= readSelectorValue(_segMan, planeObject, SELECTOR(left)); - y -= readSelectorValue(_segMan, planeObject, SELECTOR(top)); + uint16 planeTop = readSelectorValue(_segMan, planeObject, SELECTOR(top)); + uint16 planeLeft = readSelectorValue(_segMan, planeObject, SELECTOR(left)); + + y -= planeTop; + x -= planeLeft; } void GfxCoordAdjuster32::kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject) { - //int16 resY = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resY)); - //int16 resX = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resX)); - x += readSelectorValue(_segMan, planeObject, SELECTOR(left)); - y += readSelectorValue(_segMan, planeObject, SELECTOR(top)); - //*x = ( *x * resX) / _screen->getWidth(); - //*y = ( *y * resY) / _screen->getHeight(); + uint16 planeTop = readSelectorValue(_segMan, planeObject, SELECTOR(top)); + uint16 planeLeft = readSelectorValue(_segMan, planeObject, SELECTOR(left)); + + x += planeLeft; + y += planeTop; } -Common::Rect GfxCoordAdjuster32::onControl(Common::Rect rect) { - Common::Rect adjustedRect = rect; - adjustedRect.translate(0, 10); - return adjustedRect; +void GfxCoordAdjuster32::setScriptsResolution(uint16 width, uint16 height) { + scriptsRunningWidth = width; + scriptsRunningHeight = height; +} + +void GfxCoordAdjuster32::fromDisplayToScript(int16 &y, int16 &x) { + y = ((y * scriptsRunningHeight) / g_sci->_gfxScreen->getHeight()); + x = ((x * scriptsRunningWidth) / g_sci->_gfxScreen->getWidth()); +} + +void GfxCoordAdjuster32::fromScriptToDisplay(int16 &y, int16 &x) { + y = ((y * g_sci->_gfxScreen->getHeight()) / scriptsRunningHeight); + x = ((x * g_sci->_gfxScreen->getWidth()) / scriptsRunningWidth); } void GfxCoordAdjuster32::pictureSetDisplayArea(Common::Rect displayArea) { diff --git a/engines/sci/graphics/coordadjuster.h b/engines/sci/graphics/coordadjuster.h index 9b2bef48f1..59afd1dcb7 100644 --- a/engines/sci/graphics/coordadjuster.h +++ b/engines/sci/graphics/coordadjuster.h @@ -50,6 +50,10 @@ public: virtual void setCursorPos(Common::Point &pos) { } virtual void moveCursor(Common::Point &pos) { } + virtual void setScriptsResolution(uint16 width, uint16 height) { } + virtual void fromScriptToDisplay(int16 &y, int16 &x) { } + virtual void fromDisplayToScript(int16 &y, int16 &x) { } + virtual Common::Rect pictureGetDisplayArea() { return Common::Rect(0, 0); } private: }; @@ -83,7 +87,9 @@ public: void kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject = NULL_REG); void kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject = NULL_REG); - Common::Rect onControl(Common::Rect rect); + void setScriptsResolution(uint16 width, uint16 height); + void fromScriptToDisplay(int16 &y, int16 &x); + void fromDisplayToScript(int16 &y, int16 &x); void pictureSetDisplayArea(Common::Rect displayArea); Common::Rect pictureGetDisplayArea(); @@ -92,6 +98,9 @@ private: SegManager *_segMan; Common::Rect _pictureDisplayArea; + + uint16 scriptsRunningWidth; + uint16 scriptsRunningHeight; }; #endif diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp index e1c05c97da..f6e2077cb3 100644 --- a/engines/sci/graphics/cursor.cpp +++ b/engines/sci/graphics/cursor.cpp @@ -47,7 +47,7 @@ GfxCursor::GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *sc // center mouse cursor setPosition(Common::Point(_screen->getWidth() / 2, _screen->getHeight() / 2)); - kernelSetMoveZone(Common::Rect(0, 0, _screen->getDisplayWidth(), _screen->getDisplayHeight())); + _moveZoneActive = false; } GfxCursor::~GfxCursor() { @@ -283,32 +283,39 @@ Common::Point GfxCursor::getPosition() { } void GfxCursor::refreshPosition() { - bool clipped = false; - Common::Point mousePoint = getPosition(); - - if (mousePoint.x < _moveZone.left) { - mousePoint.x = _moveZone.left; - clipped = true; - } else if (mousePoint.x >= _moveZone.right) { - mousePoint.x = _moveZone.right - 1; - clipped = true; - } + if (_moveZoneActive) { + bool clipped = false; + Common::Point mousePoint = getPosition(); + + if (mousePoint.x < _moveZone.left) { + mousePoint.x = _moveZone.left; + clipped = true; + } else if (mousePoint.x >= _moveZone.right) { + mousePoint.x = _moveZone.right - 1; + clipped = true; + } - if (mousePoint.y < _moveZone.top) { - mousePoint.y = _moveZone.top; - clipped = true; - } else if (mousePoint.y >= _moveZone.bottom) { - mousePoint.y = _moveZone.bottom - 1; - clipped = true; + if (mousePoint.y < _moveZone.top) { + mousePoint.y = _moveZone.top; + clipped = true; + } else if (mousePoint.y >= _moveZone.bottom) { + mousePoint.y = _moveZone.bottom - 1; + clipped = true; + } + + // FIXME: Do this only when mouse is grabbed? + if (clipped) + setPosition(mousePoint); } +} - // FIXME: Do this only when mouse is grabbed? - if (clipped) - setPosition(mousePoint); +void GfxCursor::kernelResetMoveZone() { + _moveZoneActive = false; } void GfxCursor::kernelSetMoveZone(Common::Rect zone) { - _moveZone = zone; + _moveZone = zone; + _moveZoneActive = true; } void GfxCursor::kernelSetPos(Common::Point pos) { diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h index 7acd14acd9..787841f5be 100644 --- a/engines/sci/graphics/cursor.h +++ b/engines/sci/graphics/cursor.h @@ -58,6 +58,11 @@ public: void refreshPosition(); /** + * Removes limit for mouse movement + */ + void kernelResetMoveZone(); + + /** * Limits the mouse movement to a given rectangle. * * @param[in] rect The rectangle @@ -78,6 +83,7 @@ private: int _upscaledHires; + bool _moveZoneActive; Common::Rect _moveZone; // Rectangle in which the pointer can move CursorCache _cachedCursors; diff --git a/engines/sci/graphics/font.h b/engines/sci/graphics/font.h index 90f18e426d..b9bee0fa9e 100644 --- a/engines/sci/graphics/font.h +++ b/engines/sci/graphics/font.h @@ -32,14 +32,14 @@ namespace Sci { class GfxFont { public: - GfxFont() {}; - virtual ~GfxFont() {}; - - virtual GuiResourceId getResourceId() { return 0; }; - virtual byte getHeight() { return 0; }; - virtual bool isDoubleByte(uint16 chr) { return false; }; - virtual byte getCharWidth(uint16 chr) { return 0; }; - virtual void draw(uint16 chr, int16 top, int16 left, byte color, bool greyedOutput) {}; + GfxFont() {} + virtual ~GfxFont() {} + + virtual GuiResourceId getResourceId() { return 0; } + virtual byte getHeight() { return 0; } + virtual bool isDoubleByte(uint16 chr) { return false; } + virtual byte getCharWidth(uint16 chr) { return 0; } + virtual void draw(uint16 chr, int16 top, int16 left, byte color, bool greyedOutput) {} }; diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 9ce4474ee3..06dce6d111 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -48,24 +48,92 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd : _segMan(segMan), _resMan(resMan), _cache(cache), _screen(screen), _palette(palette), _paint32(paint32) { _coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster; + scriptsRunningWidth = 320; + scriptsRunningHeight = 200; } GfxFrameout::~GfxFrameout() { } void GfxFrameout::kernelAddPlane(reg_t object) { - _planes.push_back(object); - sortPlanes(); + PlaneEntry newPlane; + + if (_planes.empty()) { + // There has to be another way for sierra sci to do this or maybe script resolution is compiled into + // interpreter (TODO) + scriptsRunningHeight = readSelectorValue(_segMan, object, SELECTOR(resY)); + scriptsRunningWidth = readSelectorValue(_segMan, object, SELECTOR(resX)); + _coordAdjuster->setScriptsResolution(scriptsRunningWidth, scriptsRunningHeight); + } + + newPlane.object = object; + newPlane.pictureId = 0xFFFF; + newPlane.lastPriority = 0xFFFF; // hidden + _planes.push_back(newPlane); + + kernelUpdatePlane(object); } void GfxFrameout::kernelUpdatePlane(reg_t object) { - sortPlanes(); + for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) { + if (it->object == object) { + // Read some information + it->priority = readSelectorValue(_segMan, object, SELECTOR(priority)); + GuiResourceId lastPictureId = it->pictureId; + it->pictureId = readSelectorValue(_segMan, object, SELECTOR(picture)); + if (lastPictureId != it->pictureId) { + // picture got changed, load new picture + deletePlanePictures(object); + if ((it->pictureId != 0xFFFF) && (it->pictureId != 0xFFFE)) { + // SQ6 gives us a bad picture number for the control menu + if (_resMan->testResource(ResourceId(kResourceTypePic, it->pictureId))) + addPlanePicture(object, it->pictureId, 0); + } + } + sortPlanes(); + return; + } + } + error("kUpdatePlane called on plane that wasn't added before"); } void GfxFrameout::kernelDeletePlane(reg_t object) { - for (Common::List<reg_t>::iterator it = _planes.begin(); it != _planes.end(); it++) { - if (object == *it) { + deletePlanePictures(object); + for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) { + if (it->object == object) { _planes.erase(it); + Common::Rect planeRect; + planeRect.top = readSelectorValue(_segMan, object, SELECTOR(top)); + planeRect.left = readSelectorValue(_segMan, object, SELECTOR(left)); + planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom)) + 1; + planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right)) + 1; + + planeRect.top = (planeRect.top * _screen->getHeight()) / scriptsRunningHeight; + planeRect.left = (planeRect.left * _screen->getWidth()) / scriptsRunningWidth; + planeRect.bottom = (planeRect.bottom * _screen->getHeight()) / scriptsRunningHeight; + planeRect.right = (planeRect.right * _screen->getWidth()) / scriptsRunningWidth; + // Blackout removed plane rect + _paint32->fillRect(planeRect, 0); + return; + } + } +} + +void GfxFrameout::addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX) { + PlanePictureEntry newPicture; + newPicture.object = object; + newPicture.pictureId = pictureId; + newPicture.picture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, pictureId, false); + newPicture.startX = startX; + _planePictures.push_back(newPicture); +} + +void GfxFrameout::deletePlanePictures(reg_t object) { + for (PlanePictureList::iterator it = _planePictures.begin(); it != _planePictures.end(); it++) { + if (it->object == object) { + delete it->picture; + _planePictures.erase(it); + deletePlanePictures(object); return; } } @@ -73,7 +141,6 @@ void GfxFrameout::kernelDeletePlane(reg_t object) { void GfxFrameout::kernelAddScreenItem(reg_t object) { _screenItems.push_back(object); - warning("addScreenItem %X:%X (%s)", object.segment, object.offset, _segMan->getObjectName(object)); } void GfxFrameout::kernelDeleteScreenItem(reg_t object) { @@ -87,32 +154,42 @@ void GfxFrameout::kernelDeleteScreenItem(reg_t object) { int16 GfxFrameout::kernelGetHighPlanePri() { sortPlanes(); - return readSelectorValue(g_sci->getEngineState()->_segMan, _planes.back(), SELECTOR(priority)); + return readSelectorValue(g_sci->getEngineState()->_segMan, _planes.back().object, SELECTOR(priority)); +} + +// No idea yet how to implement this +void GfxFrameout::kernelAddPicAt(reg_t planeObj, int16 forWidth, GuiResourceId pictureId) { + addPlanePicture(planeObj, pictureId, forWidth); } bool sortHelper(const FrameoutEntry* entry1, const FrameoutEntry* entry2) { - return (entry1->priority == entry2->priority) ? (entry1->y < entry2->y) : (entry1->priority < entry2->priority); + if (entry1->priority == entry2->priority) { + if (entry1->y == entry2->y) + return (entry1->givenOrderNr < entry2->givenOrderNr); + return (entry1->y < entry2->y); + } + return (entry1->priority < entry2->priority); } -bool planeSortHelper(const reg_t entry1, const reg_t entry2) { - SegManager *segMan = g_sci->getEngineState()->_segMan; +bool planeSortHelper(const PlaneEntry &entry1, const PlaneEntry &entry2) { +// SegManager *segMan = g_sci->getEngineState()->_segMan; - uint16 plane1Priority = readSelectorValue(segMan, entry1, SELECTOR(priority)); - uint16 plane2Priority = readSelectorValue(segMan, entry2, SELECTOR(priority)); +// uint16 plane1Priority = readSelectorValue(segMan, entry1, SELECTOR(priority)); +// uint16 plane2Priority = readSelectorValue(segMan, entry2, SELECTOR(priority)); - if (plane1Priority == 0xffff) + if (entry1.priority == 0xffff) return true; - if (plane2Priority == 0xffff) + if (entry2.priority == 0xffff) return false; - return plane1Priority < plane2Priority; + return entry1.priority < entry2.priority; } void GfxFrameout::sortPlanes() { // First, remove any invalid planes - for (Common::List<reg_t>::iterator it = _planes.begin(); it != _planes.end();) { - if (!_segMan->isObject(*it)) + for (PlaneList::iterator it = _planes.begin(); it != _planes.end();) { + if (!_segMan->isObject(it->object)) it = _planes.erase(it); else it++; @@ -126,45 +203,72 @@ void GfxFrameout::kernelFrameout() { _palette->palVaryUpdate(); // Allocate enough space for all screen items - FrameoutEntry *itemData = (FrameoutEntry *)malloc(_screenItems.size() * sizeof(FrameoutEntry)); + FrameoutEntry *itemData = new FrameoutEntry[_screenItems.size()]; - const SciGameId gameId = g_sci->getGameId(); - - for (Common::List<reg_t>::iterator it = _planes.begin(); it != _planes.end(); it++) { - reg_t planeObject = *it; - uint16 planePriority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); - - if (planePriority == 0xffff) // Plane currently not meant to be shown - continue; + for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) { + reg_t planeObject = it->object; + uint16 planeLastPriority = it->lastPriority; Common::Rect planeRect; planeRect.top = readSelectorValue(_segMan, planeObject, SELECTOR(top)); planeRect.left = readSelectorValue(_segMan, planeObject, SELECTOR(left)); - planeRect.bottom = readSelectorValue(_segMan, planeObject, SELECTOR(bottom)); - planeRect.right = readSelectorValue(_segMan, planeObject, SELECTOR(right)); - int16 planeResY = readSelectorValue(_segMan, planeObject, SELECTOR(resY)); - int16 planeResX = readSelectorValue(_segMan, planeObject, SELECTOR(resX)); + planeRect.bottom = readSelectorValue(_segMan, planeObject, SELECTOR(bottom)) + 1; + planeRect.right = readSelectorValue(_segMan, planeObject, SELECTOR(right)) + 1; + + // Update priority here, sq6 sets it w/o UpdatePlane + uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); - planeRect.top = (planeRect.top * _screen->getHeight()) / planeResY; - planeRect.left = (planeRect.left * _screen->getWidth()) / planeResX; - planeRect.bottom = (planeRect.bottom * _screen->getHeight()) / planeResY; - planeRect.right = (planeRect.right * _screen->getWidth()) / planeResX; + planeRect.top = (planeRect.top * _screen->getHeight()) / scriptsRunningHeight; + planeRect.left = (planeRect.left * _screen->getWidth()) / scriptsRunningWidth; + planeRect.bottom = (planeRect.bottom * _screen->getHeight()) / scriptsRunningHeight; + planeRect.right = (planeRect.right * _screen->getWidth()) / scriptsRunningWidth; + + int16 planeOffsetX = 0; + + // We get negative left in kq7 in scrolling rooms + if (planeRect.left < 0) { + planeOffsetX = -planeRect.left; + planeRect.left = 0; + } + if (planeRect.top < 0) + planeRect.top = 0; + // We get bad plane-bottom in sq6 + if (planeRect.right > _screen->getWidth()) + planeRect.right = _screen->getWidth(); + if (planeRect.bottom > _screen->getHeight()) + planeRect.bottom = _screen->getHeight(); + + it->lastPriority = planePriority; + if (planePriority == 0xffff) { // Plane currently not meant to be shown + // If plane was shown before, delete plane rect + if (planePriority != planeLastPriority) + _paint32->fillRect(planeRect, 0); + continue; + } + + Common::Rect planeClipRect(planeRect.width(), planeRect.height()); + + Common::Rect upscaledPlaneRect = planeRect; + Common::Rect upscaledPlaneClipRect = planeClipRect; + if (_screen->getUpscaledHires()) { + _screen->adjustToUpscaledCoordinates(upscaledPlaneRect.top, upscaledPlaneRect.left); + _screen->adjustToUpscaledCoordinates(upscaledPlaneRect.bottom, upscaledPlaneRect.right); + _screen->adjustToUpscaledCoordinates(upscaledPlaneClipRect.top, upscaledPlaneClipRect.left); + _screen->adjustToUpscaledCoordinates(upscaledPlaneClipRect.bottom, upscaledPlaneClipRect.right); + } byte planeBack = readSelectorValue(_segMan, planeObject, SELECTOR(back)); if (planeBack) _paint32->fillRect(planeRect, planeBack); - GuiResourceId planePictureNr = readSelectorValue(_segMan, planeObject, SELECTOR(picture)); - GfxPicture *planePicture = 0; - int16 planePictureCels = 0; + GuiResourceId planeMainPictureId = it->pictureId; - if ((planePictureNr != 0xFFFF) && (planePictureNr != 0xFFFE)) { - planePicture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, planePictureNr, false); - planePictureCels = planePicture->getSci32celCount(); + bool planePictureMirrored = false; + if (readSelectorValue(_segMan, planeObject, SELECTOR(mirrored))) + planePictureMirrored = true; - _coordAdjuster->pictureSetDisplayArea(planeRect); - _palette->drewPicture(planePictureNr); - } + _coordAdjuster->pictureSetDisplayArea(planeRect); + _palette->drewPicture(planeMainPictureId); // Fill our itemlist for this plane int16 itemCount = 0; @@ -191,27 +295,14 @@ void GfxFrameout::kernelFrameout() { itemEntry->y = readSelectorValue(_segMan, itemObject, SELECTOR(y)); itemEntry->z = readSelectorValue(_segMan, itemObject, SELECTOR(z)); itemEntry->priority = readSelectorValue(_segMan, itemObject, SELECTOR(priority)); - if (gameId == GID_GK1) { - if ((itemEntry->viewId == 11000) && (itemEntry->loopNo == 0) && (itemEntry->celNo == 0) && (itemEntry->priority == 1)) { - itemEntry->priority = 0; // HACK for gk1 hires main menu - } - if ((itemEntry->viewId == 10100) && (itemEntry->priority == 0)) { - itemEntry->priority = 1; // HACK for gk1 hires main menu - } - } + if (readSelectorValue(_segMan, itemObject, SELECTOR(fixPriority)) == 0) + itemEntry->priority = itemEntry->y; + itemEntry->signal = readSelectorValue(_segMan, itemObject, SELECTOR(signal)); itemEntry->scaleX = readSelectorValue(_segMan, itemObject, SELECTOR(scaleX)); itemEntry->scaleY = readSelectorValue(_segMan, itemObject, SELECTOR(scaleY)); itemEntry->object = itemObject; - - itemEntry->y = ((itemEntry->y * _screen->getHeight()) / planeResY); - itemEntry->x = ((itemEntry->x * _screen->getWidth()) / planeResX); - itemEntry->y += planeRect.top; - itemEntry->x += planeRect.left; - - if (!(itemEntry->signal & 0x0010)) { // kSignalFixedPriority - // TODO: Change priority of this item - } + itemEntry->givenOrderNr = itemNr; itemList.push_back(itemEntry); itemEntry++; @@ -219,32 +310,139 @@ void GfxFrameout::kernelFrameout() { } } + for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) { + if (pictureIt->object == planeObject) { + GfxPicture *planePicture = pictureIt->picture; + // Allocate memory for picture cels + pictureIt->pictureCels = new FrameoutEntry[planePicture->getSci32celCount()]; + + // Add following cels to the itemlist + FrameoutEntry *picEntry = pictureIt->pictureCels; + int planePictureCels = planePicture->getSci32celCount(); + for (int pictureCelNr = 0; pictureCelNr < planePictureCels; pictureCelNr++) { + picEntry->celNo = pictureCelNr; + picEntry->object = NULL_REG; + picEntry->picture = planePicture; + picEntry->y = planePicture->getSci32celY(pictureCelNr); + picEntry->x = planePicture->getSci32celX(pictureCelNr); + picEntry->picStartX = pictureIt->startX; + + picEntry->priority = planePicture->getSci32celPriority(pictureCelNr); + + itemList.push_back(picEntry); + picEntry++; + } + } + } + // Now sort our itemlist Common::sort(itemList.begin(), itemList.end(), sortHelper); // Now display itemlist - int16 planePictureCel = 0; itemEntry = itemData; +// warning("Plane %s", _segMan->getObjectName(planeObject)); + for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) { itemEntry = *listIterator; - if (planePicture) { - while ((planePictureCel <= itemEntry->priority) && (planePictureCel < planePictureCels)) { - planePicture->drawSci32Vga(planePictureCel); - planePictureCel++; + + if (itemEntry->object.isNull()) { + // Picture cel data + itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight); + itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth); + itemEntry->picStartX = ((itemEntry->picStartX * _screen->getWidth()) / scriptsRunningWidth); + + // Out of view + int16 pictureCelStartX = itemEntry->picStartX + itemEntry->x; + int16 pictureCelEndX = pictureCelStartX + itemEntry->picture->getSci32celWidth(itemEntry->celNo); + int16 planeStartX = planeOffsetX; + int16 planeEndX = planeStartX + planeRect.width(); + if (pictureCelEndX < planeStartX) + continue; + if (pictureCelStartX > planeEndX) + continue; + + int16 pictureOffsetX = planeOffsetX; + int16 pictureX = itemEntry->x; + if (planeOffsetX) { + if (planeOffsetX <= itemEntry->picStartX) { + pictureX += itemEntry->picStartX - planeOffsetX; + pictureOffsetX = 0; + } else { + pictureOffsetX = planeOffsetX - itemEntry->picStartX; + } } - } - if (itemEntry->viewId != 0xFFFF) { + itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, planePictureMirrored); +// warning("picture cel %d %d", itemEntry->celNo, itemEntry->priority); + + } else if (itemEntry->viewId != 0xFFFF) { GfxView *view = _cache->getView(itemEntry->viewId); - if (view->isSci2Hires()) - _screen->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x); +// warning("view %s %04x:%04x", _segMan->getObjectName(itemEntry->object), PRINT_REG(itemEntry->object)); - if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) - view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); - else - view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, itemEntry->celRect); + switch (getSciVersion()) { + case SCI_VERSION_2: + if (view->isSci2Hires()) { + int16 dummyX = 0; + _screen->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x); + _screen->adjustToUpscaledCoordinates(itemEntry->z, dummyX); + } + break; + case SCI_VERSION_2_1: + itemEntry->y = (itemEntry->y * _screen->getHeight()) / scriptsRunningHeight; + itemEntry->x = (itemEntry->x * _screen->getWidth()) / scriptsRunningWidth; + itemEntry->z = (itemEntry->z * _screen->getHeight()) / scriptsRunningHeight; + break; + default: + break; + } + // Adjust according to current scroll position + itemEntry->x -= planeOffsetX; + + uint16 useInsetRect = readSelectorValue(_segMan, itemEntry->object, SELECTOR(useInsetRect)); + if (useInsetRect) { + itemEntry->celRect.top = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inTop)); + itemEntry->celRect.left = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inLeft)); + itemEntry->celRect.bottom = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inBottom)) + 1; + itemEntry->celRect.right = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inRight)) + 1; + if (view->isSci2Hires()) { + _screen->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left); + _screen->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right); + } + itemEntry->celRect.translate(itemEntry->x, itemEntry->y); + // TODO: maybe we should clip the cels rect with this, i'm not sure + // the only currently known usage is game menu of gk1 + } else { + if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) + view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); + else + view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, itemEntry->celRect); + + Common::Rect nsRect = itemEntry->celRect; + // Translate back to actual coordinate within scrollable plane + nsRect.translate(planeOffsetX, 0); + switch (getSciVersion()) { + case SCI_VERSION_2: + if (view->isSci2Hires()) { + _screen->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left); + _screen->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right); + } + break; + case SCI_VERSION_2_1: + nsRect.top = (nsRect.top * scriptsRunningHeight) / _screen->getHeight(); + nsRect.left = (nsRect.left * scriptsRunningWidth) / _screen->getWidth(); + nsRect.bottom = (nsRect.bottom * scriptsRunningHeight) / _screen->getHeight(); + nsRect.right = (nsRect.right * scriptsRunningWidth) / _screen->getWidth(); + break; + default: + break; + } + writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsLeft), nsRect.left); + writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsTop), nsRect.top); + writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsRight), nsRect.right); + writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsBottom), nsRect.bottom); + } int16 screenHeight = _screen->getHeight(); int16 screenWidth = _screen->getWidth(); @@ -253,27 +451,30 @@ void GfxFrameout::kernelFrameout() { screenWidth = _screen->getDisplayWidth(); } - if (itemEntry->celRect.top < 0 || itemEntry->celRect.top >= screenHeight) + if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= screenHeight) continue; - if (itemEntry->celRect.left < 0 || itemEntry->celRect.left >= screenWidth) + if (itemEntry->celRect.right < 0 || itemEntry->celRect.left >= screenWidth) continue; - Common::Rect clipRect; + Common::Rect clipRect, translatedClipRect; clipRect = itemEntry->celRect; if (view->isSci2Hires()) { - Common::Rect upscaledPlaneRect = planeRect; - _screen->adjustToUpscaledCoordinates(upscaledPlaneRect.top, upscaledPlaneRect.left); - _screen->adjustToUpscaledCoordinates(upscaledPlaneRect.bottom, upscaledPlaneRect.right); - clipRect.clip(upscaledPlaneRect); + clipRect.clip(upscaledPlaneClipRect); + translatedClipRect = clipRect; + translatedClipRect.translate(upscaledPlaneRect.left, upscaledPlaneRect.top); } else { - clipRect.clip(planeRect); + clipRect.clip(planeClipRect); + translatedClipRect = clipRect; + translatedClipRect.translate(planeRect.left, planeRect.top); } - if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) - view->draw(itemEntry->celRect, clipRect, clipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires()); - else - view->drawScaled(itemEntry->celRect, clipRect, clipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY); + if (!clipRect.isEmpty()) { + if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) + view->draw(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires()); + else + view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY); + } } else { // Most likely a text entry // This draws text the "SCI0-SCI11" way. In SCI2, text is prerendered in kCreateTextBitmap @@ -283,8 +484,12 @@ void GfxFrameout::kernelFrameout() { GfxFont *font = _cache->getFont(readSelectorValue(_segMan, itemEntry->object, SELECTOR(font))); bool dimmed = readSelectorValue(_segMan, itemEntry->object, SELECTOR(dimmed)); uint16 foreColor = readSelectorValue(_segMan, itemEntry->object, SELECTOR(fore)); - uint16 curX = itemEntry->x; - uint16 curY = itemEntry->y; + + itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight); + itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth); + + uint16 curX = itemEntry->x + planeRect.left; + uint16 curY = itemEntry->y + planeRect.top; for (uint32 i = 0; i < text.size(); i++) { unsigned char curChar = text[i]; // TODO: proper text splitting... this is a hack @@ -300,18 +505,17 @@ void GfxFrameout::kernelFrameout() { } } - if (planePicture) { - while (planePictureCel < planePictureCels) { - planePicture->drawSci32Vga(planePictureCel); - planePictureCel++; + for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) { + if (pictureIt->object == planeObject) { + delete[] pictureIt->pictureCels; } - delete planePicture; - planePicture = 0; } } - free(itemData); + delete[] itemData; _screen->copyToScreen(); + + g_sci->getEngineState()->_throttleTrigger = true; } } // End of namespace Sci diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index e4568ad602..f8f7e54a27 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -28,7 +28,17 @@ namespace Sci { +struct PlaneEntry { + reg_t object; + uint16 priority; + uint16 lastPriority; + GuiResourceId pictureId; +}; + +typedef Common::List<PlaneEntry> PlaneList; + struct FrameoutEntry { + uint16 givenOrderNr; reg_t object; GuiResourceId viewId; int16 loopNo; @@ -40,10 +50,22 @@ struct FrameoutEntry { int16 scaleX; int16 scaleY; Common::Rect celRect; + GfxPicture *picture; + int16 picStartX; }; typedef Common::List<FrameoutEntry *> FrameoutList; +struct PlanePictureEntry { + reg_t object; + int16 startX; + GuiResourceId pictureId; + GfxPicture *picture; + FrameoutEntry *pictureCels; // temporary +}; + +typedef Common::List<PlanePictureEntry> PlanePictureList; + class GfxCache; class GfxCoordAdjuster32; class GfxPaint32; @@ -63,8 +85,12 @@ public: void kernelAddScreenItem(reg_t object); void kernelDeleteScreenItem(reg_t object); int16 kernelGetHighPlanePri(); + void kernelAddPicAt(reg_t planeObj, int16 forWidth, GuiResourceId pictureId); void kernelFrameout(); + void addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX); + void deletePlanePictures(reg_t object); + private: SegManager *_segMan; ResourceManager *_resMan; @@ -75,9 +101,13 @@ private: GfxPaint32 *_paint32; Common::Array<reg_t> _screenItems; - Common::List<reg_t> _planes; + PlaneList _planes; + PlanePictureList _planePictures; void sortPlanes(); + + uint16 scriptsRunningWidth; + uint16 scriptsRunningHeight; }; } // End of namespace Sci diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h index f0ffecfb59..8f26ca296b 100644 --- a/engines/sci/graphics/helpers.h +++ b/engines/sci/graphics/helpers.h @@ -28,6 +28,7 @@ #include "common/endian.h" // for READ_LE_UINT16 #include "common/rect.h" +#include "common/serializer.h" #include "sci/engine/vm_types.h" namespace Sci { diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp index bfe58e03d5..630626c128 100644 --- a/engines/sci/graphics/menu.cpp +++ b/engines/sci/graphics/menu.cpp @@ -271,8 +271,15 @@ GuiMenuItemEntry *GfxMenu::findItem(uint16 menuId, uint16 itemId) { void GfxMenu::kernelSetAttribute(uint16 menuId, uint16 itemId, uint16 attributeId, reg_t value) { GuiMenuItemEntry *itemEntry = findItem(menuId, itemId); - if (!itemEntry) - error("Tried to setAttribute() on non-existant menu-item %d:%d", menuId, itemId); + + if (!itemEntry) { + // PQ2 demo calls this, for example, but has no menus (bug report #3034507). Some SCI + // fan games (Al Pond 2, Aquarius) call this too on non-existent menu items. The + // original interpreter ignored these as well. + debugC(2, kDebugLevelGraphics, "Tried to setAttribute() on non-existent menu-item %d:%d", menuId, itemId); + return; + } + switch (attributeId) { case SCI_MENU_ATTRIBUTE_ENABLED: itemEntry->enabled = value.isNull() ? false : true; @@ -391,7 +398,6 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { GuiMenuItemEntry *itemEntry = NULL; bool forceClaimed = false; EngineState *s; - byte saidSpec[64]; switch (eventType) { case SCI_EVENT_KEYBOARD: @@ -431,8 +437,13 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { itemEntry = *itemIterator; if (!itemEntry->saidVmPtr.isNull()) { - // TODO: get a pointer to saidVmPtr or make said() work on VmPtrs - _segMan->memcpy(saidSpec, itemEntry->saidVmPtr, 64); + byte *saidSpec = _segMan->derefBulkPtr(itemEntry->saidVmPtr, 0); + + if (!saidSpec) { + warning("Could not dereference saidSpec"); + continue; + } + if (said(s, saidSpec, 0) != SAID_NO_MATCH) break; } @@ -467,8 +478,10 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { _paint16->bitsShow(_ports->_menuRect); _barSaveHandle = NULL_REG; } - if (_oldPort) + if (_oldPort) { _ports->setPort(_oldPort); + _oldPort = NULL; + } if ((itemEntry) || (forceClaimed)) writeSelector(_segMan, eventObject, SELECTOR(claimed), make_reg(0, 1)); diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index 90b05c68a6..bb3975e9ef 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -32,6 +32,7 @@ #include "sci/engine/features.h" #include "sci/engine/state.h" #include "sci/engine/selector.h" +#include "sci/engine/workarounds.h" #include "sci/graphics/cache.h" #include "sci/graphics/coordadjuster.h" #include "sci/graphics/ports.h" @@ -382,11 +383,11 @@ void GfxPaint16::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, b _ports->setPort(oldPort); } -void GfxPaint16::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) { +void GfxPaint16::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY, bool hiresMode, reg_t upscaledHiresHandle) { // some calls are hiresMode even under kq6 DOS, that's why we check for // upscaled hires here if ((!hiresMode) || (!_screen->getUpscaledHires())) { - drawCelAndShow(viewId, loopNo, celNo, leftPos, topPos, priority, paletteNo); + drawCelAndShow(viewId, loopNo, celNo, leftPos, topPos, priority, paletteNo, scaleX, scaleY); } else { drawHiresCelAndShow(viewId, loopNo, celNo, leftPos, topPos, priority, paletteNo, upscaledHiresHandle); } @@ -463,7 +464,7 @@ void GfxPaint16::kernelGraphRedrawBox(Common::Rect rect) { #define SCI_DISPLAY_DONTSHOWBITS 121 reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { - int displayArg; + reg_t displayArg; TextAlignment alignment = SCI_TEXT16_ALIGNMENT_LEFT; int16 colorPen = -1, colorBack = -1, width = -1, bRedraw = 1; bool doSaveUnder = false; @@ -480,9 +481,11 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { _ports->textGreyedOutput(false); // processing codes in argv while (argc > 0) { - displayArg = argv[0].toUint16(); + displayArg = argv[0]; + if (displayArg.segment) + displayArg.offset = 0xFFFF; argc--; argv++; - switch (displayArg) { + switch (displayArg.offset) { case SCI_DISPLAY_MOVEPEN: _ports->moveTo(argv[0].toUint16(), argv[1].toUint16()); argc -= 2; argv += 2; @@ -531,8 +534,8 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { case SCI_DISPLAY_DUMMY1: case SCI_DISPLAY_DUMMY2: if (!((g_sci->getGameId() == GID_LONGBOW) && (g_sci->isDemo()))) - error("Unknown kDisplay argument %X", displayArg); - if (displayArg == SCI_DISPLAY_DUMMY2) { + error("Unknown kDisplay argument %X", displayArg.offset); + if (displayArg.offset == SCI_DISPLAY_DUMMY2) { if (argc) { argc--; argv++; } else { @@ -541,9 +544,11 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { } break; default: - if ((g_sci->getGameId() == GID_ISLANDBRAIN) && (g_sci->getEngineState()->currentRoomNumber() == 300)) - break; // WORKAROUND: we are called there with an forwarded 0 as additional parameter (script bug) - error("Unknown kDisplay argument %X", displayArg); + SciTrackOriginReply originReply; + SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kDisplay_workarounds, &originReply); + if (solution.type == WORKAROUND_NONE) + error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)", PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset); + assert(solution.type == WORKAROUND_IGNORE); break; } } diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h index 4c3ac255c4..edffa8da6e 100644 --- a/engines/sci/graphics/paint16.h +++ b/engines/sci/graphics/paint16.h @@ -73,7 +73,7 @@ public: void bitsFree(reg_t memoryHandle); void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo); - void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle); + void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY, bool hiresMode, reg_t upscaledHiresHandle); void kernelGraphFillBoxForeground(const Common::Rect &rect); void kernelGraphFillBoxBackground(const Common::Rect &rect); diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index 957199f8b9..5c17f76558 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -60,18 +60,20 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool useMergi _sysPaletteChanged = false; - // Pseudo-WORKAROUND - // Quest for Glory 3 demo, Eco Quest 1 demo, Laura Bow 2 demo, Police Quest 1 vga and all Nick's Picks - // all use an inbetween interpreter, some parts are SCI1.1, some parts are SCI1 - // It's not using the SCI1.1 palette merging (copying over all the colors) but the real merging - // If we use the copying over, we will get issues because some views have marked all colors as being used - // and those will overwrite the current palette in that case + // Quest for Glory 3 demo, Eco Quest 1 demo, Laura Bow 2 demo, Police Quest + // 1 vga and all Nick's Picks all use the older palette format and thus are + // not using the SCI1.1 palette merging (copying over all the colors) but + // the real merging done in earlier games. If we use the copying over, we + // will get issues because some views have marked all colors as being used + // and those will overwrite the current palette in that case _useMerging = useMerging; palVaryInit(); } GfxPalette::~GfxPalette() { + if (_palVaryResourceId != -1) + palVaryRemoveTimer(); } bool GfxPalette::isMerging() { @@ -106,9 +108,11 @@ void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) if (bytesLeft < 37) { // This happens when loading palette of picture 0 in sq5 - the resource is broken and doesn't contain a full // palette - warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette header"); + debugC(2, "GfxPalette::createFromData() - not enough bytes in resource (%d), expected palette header", bytesLeft); return; } + // palette formats in here are not really version exclusive, we can not use sci-version to differentiate between them + // they were just called that way, because they started appearing in sci1.1 for example if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && READ_LE_UINT16(data + 29) == 0)) { // SCI0/SCI1 palette palFormat = SCI_PAL_FORMAT_VARIABLE; // CONSTANT; @@ -484,6 +488,42 @@ void GfxPalette::kernelAnimateSet() { setOnScreen(); } +reg_t GfxPalette::kernelSave() { + SegManager *segMan = g_sci->getEngineState()->_segMan; + reg_t memoryId = segMan->allocateHunkEntry("kPalette(save)", 1024); + byte *memoryPtr = segMan->getHunkPointer(memoryId); + if (memoryPtr) { + for (int colorNr = 0; colorNr < 256; colorNr++) { + *memoryPtr++ = _sysPalette.colors[colorNr].used; + *memoryPtr++ = _sysPalette.colors[colorNr].r; + *memoryPtr++ = _sysPalette.colors[colorNr].g; + *memoryPtr++ = _sysPalette.colors[colorNr].b; + } + } + return memoryId; +} + +void GfxPalette::kernelRestore(reg_t memoryHandle) { + SegManager *segMan = g_sci->getEngineState()->_segMan; + if (!memoryHandle.isNull()) { + byte *memoryPtr = segMan->getHunkPointer(memoryHandle); + if (!memoryPtr) + error("Bad handle used for kPalette(restore)"); + + Palette restoredPalette; + + restoredPalette.timestamp = 0; + for (int colorNr = 0; colorNr < 256; colorNr++) { + restoredPalette.colors[colorNr].used = *memoryPtr++; + restoredPalette.colors[colorNr].r = *memoryPtr++; + restoredPalette.colors[colorNr].g = *memoryPtr++; + restoredPalette.colors[colorNr].b = *memoryPtr++; + } + + set(&restoredPalette, true); + } +} + void GfxPalette::kernelAssertPalette(GuiResourceId resourceId) { // Sometimes invalid viewIds are asked for, ignore those (e.g. qfg1vga) //if (!_resMan->testResource(ResourceId(kResourceTypeView, resourceId))) diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index 6af1d5a490..e10c10718d 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -34,7 +34,7 @@ class Screen; /** * Palette class, handles palette operations like changing intensity, setting up the palette, merging different palettes */ -class GfxPalette { +class GfxPalette : public Common::Serializable { public: GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool useMerging); ~GfxPalette(); @@ -63,6 +63,8 @@ public: int16 kernelFindColor(uint16 r, uint16 g, uint16 b); bool kernelAnimate(byte fromColor, byte toColor, int speed); void kernelAnimateSet(); + reg_t kernelSave(); + void kernelRestore(reg_t memoryHandle); void kernelAssertPalette(GuiResourceId resourceId); void kernelSyncScreenPalette(); @@ -80,6 +82,9 @@ public: Palette _sysPalette; + virtual void saveLoadWithSerializer(Common::Serializer &s); + void palVarySaveLoadPalette(Common::Serializer &s, Palette *palette); + private: void palVaryInit(); void palVaryInstallTimer(); diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index 7cd37b6f46..e568316919 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -43,10 +43,11 @@ GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, } GfxPicture::~GfxPicture() { + _resMan->unlockResource(_resource); } void GfxPicture::initData(GuiResourceId resourceId) { - _resource = _resMan->findResource(ResourceId(kResourceTypePic, resourceId), false); + _resource = _resMan->findResource(ResourceId(kResourceTypePic, resourceId), true); if (!_resource) { error("picture resource %d not found", resourceId); } @@ -56,7 +57,9 @@ GuiResourceId GfxPicture::getResourceId() { return _resourceId; } -// TODO: subclass this +// differentiation between various picture formats can NOT get done using sci-version checks. +// Games like PQ1 use the "old" vector data picture format, but are actually SCI1.1 +// We should leave this that way to decide the format on-the-fly instead of hardcoding it in any way void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) { uint16 headerSize; @@ -70,14 +73,12 @@ void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int1 switch (headerSize) { case 0x26: // SCI 1.1 VGA picture _resourceType = SCI_PICTURE_TYPE_SCI11; - if (_addToFlag) - _priority = 15; drawSci11Vga(); break; #ifdef ENABLE_SCI32 case 0x0e: // SCI32 VGA picture _resourceType = SCI_PICTURE_TYPE_SCI32; - drawSci32Vga(); + //drawSci32Vga(); break; #endif default: @@ -99,28 +100,46 @@ void GfxPicture::reset() { void GfxPicture::drawSci11Vga() { byte *inbuffer = _resource->data; int size = _resource->size; - int has_cel = READ_LE_UINT16(inbuffer + 4); - int vector_dataPos = READ_LE_UINT16(inbuffer + 16); + int priorityBandsCount = inbuffer[3]; + int has_cel = inbuffer[4]; + int vector_dataPos = READ_LE_UINT32(inbuffer + 16); int vector_size = size - vector_dataPos; - int palette_data_ptr = READ_LE_UINT16(inbuffer + 28); - int cel_headerPos = READ_LE_UINT16(inbuffer + 32); - int cel_RlePos = READ_LE_UINT16(inbuffer + cel_headerPos + 24); - int cel_LiteralPos = READ_LE_UINT16(inbuffer + cel_headerPos + 28); + int palette_data_ptr = READ_LE_UINT32(inbuffer + 28); + int cel_headerPos = READ_LE_UINT32(inbuffer + 32); + int cel_RlePos = READ_LE_UINT32(inbuffer + cel_headerPos + 24); + int cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28); Palette palette; + // Header + // [headerSize:WORD] [unknown:BYTE] [priorityBandCount:BYTE] [hasCel:BYTE] [unknown:BYTE] + // [unknown:WORD] [unknown:WORD] [unknown:WORD] [unknown:WORD] [unknown:WORD] + // Offset 16 + // [vectorDataOffset:DWORD] [unknown:DWORD] [unknown:DWORD] [paletteDataOffset:DWORD] + // Offset 32 + // [celHeaderOffset:DWORD] [unknown:DWORD] + // [priorityBandData:WORD] * priorityBandCount + // [priority:BYTE] [unknown:BYTE] + // Create palette and set it _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette); _palette->set(&palette, true); + // priority bands are supposed to be 14 for sci1.1 pictures + assert(priorityBandsCount == 14); + + if (_addToFlag) { + _priority = inbuffer[40 + priorityBandsCount * 2] & 0xF; + } + // display Cel-data if (has_cel) - drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0); + drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, 0); // process vector data drawVectorData(inbuffer + vector_dataPos, vector_size); - // Remember priority band information for later - _ports->priorityBandsRemember(inbuffer + 40); + // Set priority band information + _ports->priorityBandsInitSci11(inbuffer + 40); } #ifdef ENABLE_SCI32 @@ -129,45 +148,80 @@ int16 GfxPicture::getSci32celCount() { return inbuffer[2]; } -void GfxPicture::drawSci32Vga(int16 celNo) { +int16 GfxPicture::getSci32celY(int16 celNo) { + byte *inbuffer = _resource->data; + int header_size = READ_LE_UINT16(inbuffer); + int cel_headerPos = header_size + 42 * celNo; + return READ_LE_UINT16(inbuffer + cel_headerPos + 40); +} + +int16 GfxPicture::getSci32celX(int16 celNo) { + byte *inbuffer = _resource->data; + int header_size = READ_LE_UINT16(inbuffer); + int cel_headerPos = header_size + 42 * celNo; + return READ_LE_UINT16(inbuffer + cel_headerPos + 38); +} + +int16 GfxPicture::getSci32celWidth(int16 celNo) { + byte *inbuffer = _resource->data; + int header_size = READ_LE_UINT16(inbuffer); + int cel_headerPos = header_size + 42 * celNo; + return READ_LE_UINT16(inbuffer + cel_headerPos + 0); +} + +int16 GfxPicture::getSci32celPriority(int16 celNo) { + byte *inbuffer = _resource->data; + int header_size = READ_LE_UINT16(inbuffer); + int cel_headerPos = header_size + 42 * celNo; + return READ_LE_UINT16(inbuffer + cel_headerPos + 36); +} + +void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictureX, bool mirrored) { byte *inbuffer = _resource->data; int size = _resource->size; int header_size = READ_LE_UINT16(inbuffer); int palette_data_ptr = READ_LE_UINT16(inbuffer + 6); - int celCount = inbuffer[2]; +// int celCount = inbuffer[2]; int cel_headerPos = header_size; int cel_RlePos, cel_LiteralPos; - int cel_relXpos, cel_relYpos; Palette palette; // HACK - _mirroredFlag = false; + _mirroredFlag = mirrored; _addToFlag = false; _resourceType = SCI_PICTURE_TYPE_SCI32; - if ((celNo == -1) || (celNo == 0)) { + if (celNo == 0) { // Create palette and set it _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette); _palette->set(&palette, true); } - if (celNo != -1) { - cel_headerPos += 42 * celNo; - celCount = 1; - } - while (celCount > 0) { - cel_RlePos = READ_LE_UINT32(inbuffer + cel_headerPos + 24); - cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28); - cel_relXpos = READ_LE_UINT16(inbuffer + cel_headerPos + 38); - cel_relYpos = READ_LE_UINT16(inbuffer + cel_headerPos + 40); - drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, cel_relXpos, cel_relYpos); - cel_headerPos += 42; - celCount--; + // Header + // [headerSize:WORD] [celCount:BYTE] [Unknown:BYTE] [Unknown:WORD] [paletteOffset:DWORD] [Unknown:DWORD] + // cel-header follow afterwards, each is 42 bytes + // Cel-Header + // [width:WORD] [height:WORD] [displaceX:WORD] [displaceY:WORD] [clearColor:BYTE] [compressed:BYTE] + // offset 10-23 is unknown + // [rleOffset:DWORD] [literalOffset:DWORD] [Unknown:WORD] [Unknown:WORD] [priority:WORD] [relativeXpos:WORD] [relativeYpos:WORD] + + cel_headerPos += 42 * celNo; + + if (mirrored) { + // switch around relativeXpos + Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea(); + drawX = displayArea.width() - drawX - READ_LE_UINT16(inbuffer + cel_headerPos + 0); } + + cel_RlePos = READ_LE_UINT32(inbuffer + cel_headerPos + 24); + cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28); + + drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, drawX, drawY, pictureX); + cel_headerPos += 42; } #endif -void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY) { +void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX) { byte *celBitmap = NULL; byte *ptr = NULL; byte *headerPtr = inbuffer + headerPos; @@ -295,52 +349,71 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea(); - y = callerY + displayArea.top; - lastY = MIN<int16>(height + y, displayArea.bottom); - leftX = callerX + displayArea.left; - rightX = MIN<int16>(width + leftX, displayArea.right); - - // Change clearcolor to white, if we dont add to an existing picture. That way we will paint everything on screen - // but white and that wont matter because the screen is supposed to be already white. It seems that most (if not all) - // SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint - // the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra - if (!_addToFlag) - clearColor = _screen->getColorWhite(); - - ptr = celBitmap; - if (!_mirroredFlag) { - // Draw bitmap to screen - x = leftX; - while (y < lastY) { - curByte = *ptr++; - if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y))) - _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY, curByte, priority, 0); - - x++; - - if (x >= rightX) { - if (width > rightX - leftX) // Skip extra pixels at the end of the row - ptr += width - (rightX - leftX); - x = leftX; - y++; - } + uint16 skipCelBitmapPixels = 0; + int16 displayWidth = width; + if (pictureX) { + // scroll position for picture active, we need to adjust drawX accordingly + drawX -= pictureX; + if (drawX < 0) { + skipCelBitmapPixels = -drawX; + displayWidth -= skipCelBitmapPixels; + drawX = 0; } - } else { - // Draw bitmap to screen (mirrored) - x = rightX - 1; - while (y < lastY) { - curByte = *ptr++; - if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y))) - _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY, curByte, priority, 0); - - if (x == leftX) { - if (width > rightX - leftX) // Skip extra pixels at the end of the row - ptr += width - (rightX - leftX); - x = rightX; - y++; + } + + if (displayWidth > 0) { + y = displayArea.top + drawY; + lastY = MIN<int16>(height + y, displayArea.bottom); + leftX = displayArea.left + drawX; + rightX = MIN<int16>(displayWidth + leftX, displayArea.right); + + uint16 sourcePixelSkipPerRow = 0; + if (width > rightX - leftX) + sourcePixelSkipPerRow = width - (rightX - leftX); + + // Change clearcolor to white, if we dont add to an existing picture. That way we will paint everything on screen + // but white and that wont matter because the screen is supposed to be already white. It seems that most (if not all) + // SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint + // the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra + if (!_addToFlag) + clearColor = _screen->getColorWhite(); + + byte drawMask = priority == 255 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY; + + ptr = celBitmap; + ptr += skipCelBitmapPixels; + if (!_mirroredFlag) { + // Draw bitmap to screen + x = leftX; + while (y < lastY) { + curByte = *ptr++; + if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y))) + _screen->putPixel(x, y, drawMask, curByte, priority, 0); + + x++; + + if (x >= rightX) { + ptr += sourcePixelSkipPerRow; + x = leftX; + y++; + } } + } else { + // Draw bitmap to screen (mirrored) + x = rightX - 1; + while (y < lastY) { + curByte = *ptr++; + if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y))) + _screen->putPixel(x, y, drawMask, curByte, priority, 0); + + if (x == leftX) { + ptr += sourcePixelSkipPerRow; + x = rightX; + y++; + } - x--; + x--; + } } } @@ -537,6 +610,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { // This picture includes garbage data, first a set pattern w/o parameter and then short pattern // I guess that garbage is a left over from the sq4-floppy (sci1) to sq4-cd (sci1.1) conversion switch (_resourceId) { + case 35: case 381: case 376: return; @@ -618,7 +692,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { vectorGetAbsCoordsNoMirror(data, curPos, x, y); size = READ_LE_UINT16(data + curPos); curPos += 2; _priority = pic_priority; // set global priority so the cel gets drawn using current priority as well - drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y); + drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, 0); curPos += size; break; case PIC_OPX_EGA_SET_PRIORITY_TABLE: @@ -658,9 +732,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { vectorGetAbsCoordsNoMirror(data, curPos, x, y); size = READ_LE_UINT16(data + curPos); curPos += 2; _priority = pic_priority; // set global priority so the cel gets drawn using current priority as well - if (pic_priority == 255) - _priority = 0; // if priority not set, use priority 0 - drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y); + drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, 0); curPos += size; break; case PIC_OPX_VGA_PRIORITY_TABLE_EQDIST: diff --git a/engines/sci/graphics/picture.h b/engines/sci/graphics/picture.h index 5a86539b37..7cd0d71b67 100644 --- a/engines/sci/graphics/picture.h +++ b/engines/sci/graphics/picture.h @@ -56,14 +56,18 @@ public: #ifdef ENABLE_SCI32 int16 getSci32celCount(); - void drawSci32Vga(int16 celNo = -1); + int16 getSci32celY(int16 celNo); + int16 getSci32celX(int16 celNo); + int16 getSci32celWidth(int16 celNo); + int16 getSci32celPriority(int16 celNo); + void drawSci32Vga(int16 celNo, int16 callerX, int16 callerY, int16 pictureX, bool mirrored); #endif private: void initData(GuiResourceId resourceId); void reset(); void drawSci11Vga(); - void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY); + void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX); void drawVectorData(byte *data, int size); bool vectorIsNonOpcode(byte pixel); void vectorGetAbsCoords(byte *data, int &curPos, int16 &x, int16 &y); diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index 611db1061a..9268888b6c 100644 --- a/engines/sci/graphics/ports.cpp +++ b/engines/sci/graphics/ports.cpp @@ -134,8 +134,6 @@ void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *te if (g_sci->_features->usesOldGfxFunctions()) _picWind->top = offTop; - priorityBandsMemoryActive = false; - kernelInitPriorityBands(); } @@ -173,7 +171,7 @@ reg_t GfxPorts::kernelGetActive() { reg_t GfxPorts::kernelNewWindow(Common::Rect dims, Common::Rect restoreRect, uint16 style, int16 priority, int16 colorPen, int16 colorBack, const char *title) { Window *wnd = NULL; - if (restoreRect.top != 0 && restoreRect.left != 0 && restoreRect.height() != 0 && restoreRect.width() != 0) + if (restoreRect.bottom != 0 && restoreRect.right != 0) wnd = newWindow(dims, &restoreRect, title, style, priority, false); else wnd = newWindow(dims, NULL, title, style, priority, false); @@ -250,9 +248,10 @@ Window *GfxPorts::newWindow(const Common::Rect &dims, const Common::Rect *restor r = dims; if (r.width() > _screen->getWidth()) { - // We get invalid dimensions at least at the end of sq3 (script bug!) - // same happens very often in lsl5, sierra sci didnt fix it but it looked awful - warning("fixing too large window, given left&right was %d, %d", dims.left, dims.right); + // We get invalid dimensions at least at the end of sq3 (script bug!). + // Same happens very often in lsl5, sierra sci didnt fix it but it looked awful. + // Also happens frequently in the demo of GK1. + warning("Fixing too large window, left: %d, right: %d", dims.left, dims.right); r.left = 0; r.right = _screen->getWidth() - 1; if ((style != _styleUser) && !(style & SCI_WINDOWMGR_STYLE_NOFRAME)) @@ -367,7 +366,7 @@ void GfxPorts::drawWindow(Window *pWnd) { if (!(wndStyle & SCI_WINDOWMGR_STYLE_TRANSPARENT)) _paint16->fillRect(r, GFX_SCREEN_MASK_VISUAL, pWnd->backClr); - _paint16->bitsShow(pWnd->restoreRect); + _paint16->bitsShow(pWnd->dims); } setPort(oldport); } @@ -540,22 +539,14 @@ void GfxPorts::priorityBandsInit(byte *data) { _priorityBands[i++] = inx; } -// Gets used by picture class to remember priority bands data from sci1.1 pictures that need to get applied when -// transitioning to that picture -void GfxPorts::priorityBandsRemember(byte *data) { - int bandNo; - for (bandNo = 0; bandNo < 14; bandNo++) { - priorityBandsMemory[bandNo] = READ_LE_UINT16(data); +// Gets used to read priority bands data from sci1.1 pictures +void GfxPorts::priorityBandsInitSci11(byte *data) { + byte priorityBands[14]; + for (int bandNo = 0; bandNo < 14; bandNo++) { + priorityBands[bandNo] = READ_LE_UINT16(data); data += 2; } - priorityBandsMemoryActive = true; -} - -void GfxPorts::priorityBandsRecall() { - if (priorityBandsMemoryActive) { - priorityBandsInit((byte *)&priorityBandsMemory); - priorityBandsMemoryActive = false; - } + priorityBandsInit(priorityBands); } void GfxPorts::kernelInitPriorityBands() { diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h index 818f92f44f..d10bc6772f 100644 --- a/engines/sci/graphics/ports.h +++ b/engines/sci/graphics/ports.h @@ -81,8 +81,7 @@ public: void priorityBandsInit(int16 bandCount, int16 top, int16 bottom); void priorityBandsInit(byte *data); - void priorityBandsRemember(byte *data); - void priorityBandsRecall(); + void priorityBandsInitSci11(byte *data); void kernelInitPriorityBands(); void kernelGraphAdjustPriority(int top, int bottom); @@ -121,9 +120,6 @@ private: // Priority Bands related variables int16 _priorityTop, _priorityBottom, _priorityBandCount; byte _priorityBands[200]; - - byte priorityBandsMemory[14]; - bool priorityBandsMemoryActive; }; } // End of namespace Sci diff --git a/engines/sci/graphics/robot.cpp b/engines/sci/graphics/robot.cpp index d926e037f4..1572a0a9ec 100644 --- a/engines/sci/graphics/robot.cpp +++ b/engines/sci/graphics/robot.cpp @@ -28,6 +28,8 @@ #include "sci/graphics/screen.h" #include "sci/graphics/robot.h" +#include "common/file.h" + namespace Sci { #ifdef ENABLE_SCI32 @@ -35,18 +37,33 @@ GfxRobot::GfxRobot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId res : _resMan(resMan), _screen(screen), _resourceId(resourceId) { assert(resourceId != -1); initData(resourceId); + _resourceData = 0; } GfxRobot::~GfxRobot() { - _resMan->unlockResource(_resource); + delete[] _resourceData; } void GfxRobot::initData(GuiResourceId resourceId) { - _resource = _resMan->findResource(ResourceId(kResourceTypeRobot, resourceId), true); - if (!_resource) { - error("robot resource %d not found", resourceId); + char fileName[10]; + sprintf(fileName, "%d.rbt", resourceId); + + Common::File robotFile; + if (robotFile.open(fileName)) { + _resourceData = new byte[robotFile.size()]; + robotFile.read(_resourceData, robotFile.size()); + robotFile.close(); + } else { + warning("Unable to open robot file %s", fileName); + return; + } + + byte version = _resourceData[6]; + + if (version != 4 && version != 5) { + warning("Robot version %d isn't supported yet", version); + return; } - _resourceData = _resource->data; // sample data: // Header - 14 bytes @@ -152,8 +169,11 @@ void GfxRobot::initData(GuiResourceId resourceId) { // ^ ?? // 00000120: 70 70 70 70 70 70 70 70-70 70 70 70 70 70 70 70 pppppppppppppppp - _frameCount = READ_LE_UINT16(_resourceData + 12); - _frameSize = READ_LE_UINT32(_resourceData + 34); + _frameCount = READ_LE_UINT16(_resourceData + 14); + //_frameSize = READ_LE_UINT32(_resourceData + 34); + byte hasSound = _resourceData[25]; + + debug("Robot %d, %d frames, sound: %d\n", resourceId, _frameCount, hasSound); } // TODO: just trying around in here... diff --git a/engines/sci/graphics/robot.h b/engines/sci/graphics/robot.h index 80dae95f95..3ea9a7f735 100644 --- a/engines/sci/graphics/robot.h +++ b/engines/sci/graphics/robot.h @@ -45,7 +45,6 @@ private: GfxScreen *_screen; GuiResourceId _resourceId; - Resource *_resource; byte *_resourceData; uint16 _width; diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 488bb83ab3..131a3d2eb8 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -127,7 +127,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { // the icon bar. Of course, both KQ6 and QFG1 VGA differ in size. if (g_sci->getGameId() == GID_KQ6) initGraphics(_displayWidth, _displayHeight + 26, _displayWidth > 320); - else if (g_sci->getGameId() == GID_QFG1) + else if (g_sci->getGameId() == GID_QFG1VGA) initGraphics(_displayWidth, _displayHeight + 20, _displayWidth > 320); else error("Unknown SCI1.1 Mac game"); diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp index 4aa2346aed..08be203230 100644 --- a/engines/sci/graphics/text16.cpp +++ b/engines/sci/graphics/text16.cpp @@ -289,6 +289,8 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId if (fontId != -1) SetFont(fontId); + else + fontId = oldFontId; if (g_sci->getLanguage() == Common::JA_JPN) SwitchToFont900OnSjis(text); @@ -296,7 +298,7 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId rect.top = rect.left = 0; if (maxWidth < 0) { // force output as single line - StringWidth(text, oldFontId, textWidth, textHeight); + StringWidth(text, fontId, textWidth, textHeight); rect.bottom = textHeight; rect.right = textWidth; } else { @@ -308,7 +310,7 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId charCount = GetLongest(curPos, rect.right, oldFontId); if (charCount == 0) break; - Width(curPos, 0, charCount, oldFontId, textWidth, textHeight); + Width(curPos, 0, charCount, fontId, textWidth, textHeight); maxTextWidth = MAX(textWidth, maxTextWidth); totalHeight += textHeight; curPos += charCount; @@ -390,6 +392,8 @@ void GfxText16::Box(const char *text, int16 bshow, const Common::Rect &rect, Tex if (fontId != -1) SetFont(fontId); + else + fontId = orgFontId; if (g_sci->getLanguage() == Common::JA_JPN) { if (SwitchToFont900OnSjis(text)) @@ -420,9 +424,9 @@ void GfxText16::Box(const char *text, int16 bshow, const Common::Rect &rect, Tex _ports->moveTo(rect.left + offset, rect.top + hline); if (bshow) { - Show(text, 0, charCount, orgFontId, orgPenColor); + Show(text, 0, charCount, fontId, orgPenColor); } else { - Draw(text, 0, charCount, orgFontId, orgPenColor); + Draw(text, 0, charCount, fontId, orgPenColor); } hline += textHeight; diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp index c1f626449f..abb5e74cbd 100644 --- a/engines/sci/graphics/transitions.cpp +++ b/engines/sci/graphics/transitions.cpp @@ -407,39 +407,39 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) { } } +void GfxTransitions::scrollCopyOldToScreen(Common::Rect screenRect, int16 x, int16 y) { + byte *oldScreenPtr = _oldScreen; + int16 screenWidth = _screen->getDisplayWidth(); + if (_screen->getUpscaledHires()) { + _screen->adjustToUpscaledCoordinates(screenRect.top, screenRect.left); + _screen->adjustToUpscaledCoordinates(screenRect.bottom, screenRect.right); + _screen->adjustToUpscaledCoordinates(y, x); + } + oldScreenPtr += screenRect.left + screenRect.top * screenWidth; + g_system->copyRectToScreen(oldScreenPtr, screenWidth, x, y, screenRect.width(), screenRect.height()); +} + // Scroll old screen (up/down/left/right) and insert new screen that way - works // on _picRect area only. void GfxTransitions::scroll(int16 number) { int16 screenWidth, screenHeight; - byte *oldScreenPtr; int16 stepNr = 0; Common::Rect oldMoveRect = _picRect; + Common::Rect oldScreenRect = _picRect; Common::Rect newMoveRect = _picRect; Common::Rect newScreenRect = _picRect; _screen->copyFromScreen(_oldScreen); screenWidth = _screen->getDisplayWidth(); screenHeight = _screen->getDisplayHeight(); - oldScreenPtr = _oldScreen + _picRect.left + _picRect.top * screenWidth; - if (_screen->getUpscaledHires()) { - oldScreenPtr += _picRect.left + _picRect.top * screenWidth; - } - switch (number) { case SCI_TRANSITIONS_SCROLL_LEFT: newScreenRect.right = newScreenRect.left; newMoveRect.left = newMoveRect.right; while (oldMoveRect.left < oldMoveRect.right) { - oldScreenPtr++; - if (_screen->getUpscaledHires()) - oldScreenPtr++; - oldMoveRect.right--; - if (oldMoveRect.right > oldMoveRect.left) { - if (!_screen->getUpscaledHires()) - g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left, oldMoveRect.top, oldMoveRect.width(), oldMoveRect.height()); - else - g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left * 2, oldMoveRect.top * 2, oldMoveRect.width() * 2, oldMoveRect.height() * 2); - } + oldMoveRect.right--; oldScreenRect.left++; + if (oldMoveRect.right > oldMoveRect.left) + scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top); newScreenRect.right++; newMoveRect.left--; _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top); if ((stepNr & 1) == 0) { @@ -458,13 +458,9 @@ void GfxTransitions::scroll(int16 number) { case SCI_TRANSITIONS_SCROLL_RIGHT: newScreenRect.left = newScreenRect.right; while (oldMoveRect.left < oldMoveRect.right) { - oldMoveRect.left++; - if (oldMoveRect.right > oldMoveRect.left) { - if (!_screen->getUpscaledHires()) - g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left, oldMoveRect.top, oldMoveRect.width(), oldMoveRect.height()); - else - g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left * 2, oldMoveRect.top * 2, oldMoveRect.width() * 2, oldMoveRect.height() * 2); - } + oldMoveRect.left++; oldScreenRect.right--; + if (oldMoveRect.right > oldMoveRect.left) + scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top); newScreenRect.left--; _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top); if ((stepNr & 1) == 0) { @@ -484,16 +480,9 @@ void GfxTransitions::scroll(int16 number) { newScreenRect.bottom = newScreenRect.top; newMoveRect.top = newMoveRect.bottom; while (oldMoveRect.top < oldMoveRect.bottom) { - oldScreenPtr += screenWidth; - if (_screen->getUpscaledHires()) - oldScreenPtr += screenWidth; - oldMoveRect.top++; - if (oldMoveRect.top < oldMoveRect.bottom) { - if (!_screen->getUpscaledHires()) - g_system->copyRectToScreen(oldScreenPtr, screenWidth, _picRect.left, _picRect.top, oldMoveRect.width(), oldMoveRect.height()); - else - g_system->copyRectToScreen(oldScreenPtr, screenWidth, _picRect.left * 2, _picRect.top * 2, oldMoveRect.width() * 2, oldMoveRect.height() * 2); - } + oldMoveRect.top++; oldScreenRect.top++; + if (oldMoveRect.top < oldMoveRect.bottom) + scrollCopyOldToScreen(oldScreenRect, _picRect.left, _picRect.top); newScreenRect.bottom++; newMoveRect.top--; _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top); updateScreenAndWait(3); @@ -503,13 +492,9 @@ void GfxTransitions::scroll(int16 number) { case SCI_TRANSITIONS_SCROLL_DOWN: newScreenRect.top = newScreenRect.bottom; while (oldMoveRect.top < oldMoveRect.bottom) { - oldMoveRect.top++; - if (oldMoveRect.top < oldMoveRect.bottom) { - if (!_screen->getUpscaledHires()) - g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left, oldMoveRect.top, oldMoveRect.width(), oldMoveRect.height()); - else - g_system->copyRectToScreen(oldScreenPtr, screenWidth, oldMoveRect.left * 2, oldMoveRect.top * 2, oldMoveRect.width() * 2, oldMoveRect.height() * 2); - } + oldMoveRect.top++; oldScreenRect.bottom--; + if (oldMoveRect.top < oldMoveRect.bottom) + scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top); newScreenRect.top--; _screen->copyRectToScreen(newScreenRect, _picRect.left, _picRect.top); updateScreenAndWait(3); @@ -539,7 +524,7 @@ void GfxTransitions::verticalRollFromCenter(bool blackoutFlag) { // only void GfxTransitions::verticalRollToCenter(bool blackoutFlag) { Common::Rect leftRect = Common::Rect(_picRect.left, _picRect.top, _picRect.left + 1, _picRect.bottom); - Common::Rect rightRect = Common::Rect(leftRect.right - 1, _picRect.top, leftRect.right, _picRect.bottom); + Common::Rect rightRect = Common::Rect(_picRect.right - 1, _picRect.top, _picRect.right, _picRect.bottom); while (leftRect.left < rightRect.right) { copyRectToScreen(leftRect, blackoutFlag); leftRect.translate(1, 0); diff --git a/engines/sci/graphics/transitions.h b/engines/sci/graphics/transitions.h index 788cefabca..233638ffda 100644 --- a/engines/sci/graphics/transitions.h +++ b/engines/sci/graphics/transitions.h @@ -83,6 +83,7 @@ private: void pixelation(bool blackoutFlag); void blocks(bool blackoutFlag); void straight(int16 number, bool blackoutFlag); + void scrollCopyOldToScreen(Common::Rect screenRect, int16 x, int16 y); void scroll(int16 number); void verticalRollFromCenter(bool blackoutFlag); void verticalRollToCenter(bool blackoutFlag); diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index 93df45820c..d32e60335f 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -28,6 +28,7 @@ #include "sci/engine/state.h" #include "sci/graphics/screen.h" #include "sci/graphics/palette.h" +#include "sci/graphics/coordadjuster.h" #include "sci/graphics/view.h" namespace Sci { @@ -35,6 +36,7 @@ namespace Sci { GfxView::GfxView(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId) : _resMan(resMan), _screen(screen), _palette(palette), _resourceId(resourceId) { assert(resourceId != -1); + _coordAdjuster = g_sci->_gfxCoordAdjuster; initData(resourceId); } @@ -160,8 +162,8 @@ void GfxView::initData(GuiResourceId resourceId) { // For EGA // Width:WORD Height:WORD DisplaceX:BYTE DisplaceY:BYTE ClearKey:BYTE EGAData starts now directly cel = &_loop[loopNo].cel[celNo]; - cel->width = READ_LE_UINT16(celData); - cel->height = READ_LE_UINT16(celData + 2); + cel->scriptWidth = cel->width = READ_LE_UINT16(celData); + cel->scriptHeight = cel->height = READ_LE_UINT16(celData + 2); cel->displaceX = (signed char)celData[4]; cel->displaceY = celData[5]; cel->clearKey = celData[6]; @@ -231,8 +233,8 @@ void GfxView::initData(GuiResourceId resourceId) { _loop[loopNo].cel = new CelInfo[celCount]; for (celNo = 0; celNo < celCount; celNo++) { cel = &_loop[loopNo].cel[celNo]; - cel->width = READ_SCI11ENDIAN_UINT16(celData); - cel->height = READ_SCI11ENDIAN_UINT16(celData + 2); + cel->scriptWidth = cel->width = READ_SCI11ENDIAN_UINT16(celData); + cel->scriptHeight = cel->height = READ_SCI11ENDIAN_UINT16(celData + 2); cel->displaceX = READ_SCI11ENDIAN_UINT16(celData + 4); cel->displaceY = READ_SCI11ENDIAN_UINT16(celData + 6); @@ -253,6 +255,29 @@ void GfxView::initData(GuiResourceId resourceId) { celData += celSize; } } +#ifdef ENABLE_SCI32 + // adjust width/height returned to scripts + switch (getSciVersion()) { + case SCI_VERSION_2: + if (_isSci2Hires) { + for (loopNo = 0; loopNo < _loopCount; loopNo++) { + for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++) { + _screen->adjustBackUpscaledCoordinates(_loop[loopNo].cel[celNo].scriptWidth, _loop[loopNo].cel[celNo].scriptHeight); + } + } + } + break; + + case SCI_VERSION_2_1: + for (loopNo = 0; loopNo < _loopCount; loopNo++) { + for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++) { + _coordAdjuster->fromDisplayToScript(_loop[loopNo].cel[celNo].scriptHeight, _loop[loopNo].cel[celNo].scriptWidth); + } + } + default: + break; + } +#endif break; default: diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h index 6eb1830b99..25e110ad13 100644 --- a/engines/sci/graphics/view.h +++ b/engines/sci/graphics/view.h @@ -30,6 +30,7 @@ namespace Sci { struct CelInfo { int16 width, height; + int16 scriptWidth, scriptHeight; int16 displaceX; int16 displaceY; byte clearKey; @@ -81,6 +82,7 @@ private: void unditherBitmap(byte *bitmap, int16 width, int16 height, byte clearKey); ResourceManager *_resMan; + GfxCoordAdjuster *_coordAdjuster; GfxScreen *_screen; GfxPalette *_palette; diff --git a/engines/sci/module.mk b/engines/sci/module.mk index 85988b8f1b..dae2807cc2 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -25,6 +25,7 @@ MODULE_OBJS := \ engine/kscripts.o \ engine/ksound.o \ engine/kstring.o \ + engine/kvideo.o \ engine/message.o \ engine/savegame.o \ engine/script.o \ @@ -35,6 +36,7 @@ MODULE_OBJS := \ engine/state.o \ engine/static_selectors.o \ engine/vm.o \ + engine/workarounds.o \ graphics/animate.o \ graphics/cache.o \ graphics/compare.o \ @@ -63,7 +65,7 @@ MODULE_OBJS := \ sound/music.o \ sound/soundcmd.o \ sound/drivers/adlib.o \ - sound/drivers/amiga.o \ + sound/drivers/amigamac.o \ sound/drivers/fb01.o \ sound/drivers/midi.o \ sound/drivers/pcjr.o \ @@ -72,7 +74,6 @@ MODULE_OBJS := \ ifdef ENABLE_SCI32 MODULE_OBJS += \ - engine/kernel32.o \ graphics/frameout.o \ graphics/paint32.o \ graphics/robot.o \ diff --git a/engines/sci/parser/grammar.cpp b/engines/sci/parser/grammar.cpp index 070e6767cf..6f37b49919 100644 --- a/engines/sci/parser/grammar.cpp +++ b/engines/sci/parser/grammar.cpp @@ -422,44 +422,44 @@ ParseRuleList *Vocabulary::buildGNF(bool verbose) { return tlist; } -static int _vbpt_pareno(parse_tree_node_t *nodes, int *pos, int base) { +static int _vbpt_pareno(ParseTreeNode *nodes, int *pos, int base) { // Opens parentheses - nodes[base].content.branches[0] = (*pos) + 1; + nodes[base].left = &nodes[(*pos) + 1]; nodes[++(*pos)].type = kParseTreeBranchNode; - nodes[*pos].content.branches[0] = 0; - nodes[*pos].content.branches[1] = 0; + nodes[*pos].left = 0; + nodes[*pos].right = 0; return *pos; } -static int _vbpt_parenc(parse_tree_node_t *nodes, int *pos, int paren) { +static int _vbpt_parenc(ParseTreeNode *nodes, int *pos, int paren) { // Closes parentheses for appending - nodes[paren].content.branches[1] = ++(*pos); + nodes[paren].right = &nodes[++(*pos)]; nodes[*pos].type = kParseTreeBranchNode; - nodes[*pos].content.branches[0] = 0; - nodes[*pos].content.branches[1] = 0; + nodes[*pos].left = 0; + nodes[*pos].right = 0; return *pos; } -static int _vbpt_append(parse_tree_node_t *nodes, int *pos, int base, int value) { +static int _vbpt_append(ParseTreeNode *nodes, int *pos, int base, int value) { // writes one value to an existing base node and creates a successor node for writing - nodes[base].content.branches[0] = ++(*pos); + nodes[base].left = &nodes[++(*pos)]; nodes[*pos].type = kParseTreeLeafNode; - nodes[*pos].content.value = value; - nodes[base].content.branches[1] = ++(*pos); + nodes[*pos].value = value; + nodes[base].right = &nodes[++(*pos)]; nodes[*pos].type = kParseTreeBranchNode; - nodes[*pos].content.branches[0] = 0; - nodes[*pos].content.branches[1] = 0; + nodes[*pos].left = 0; + nodes[*pos].right = 0; return *pos; } -static int _vbpt_terminate(parse_tree_node_t *nodes, int *pos, int base, int value) { +static int _vbpt_terminate(ParseTreeNode *nodes, int *pos, int base, int value) { // Terminates, overwriting a nextwrite forknode nodes[base].type = kParseTreeLeafNode; - nodes[base].content.value = value; + nodes[base].value = value; return *pos; } -static int _vbpt_write_subexpression(parse_tree_node_t *nodes, int *pos, ParseRule *rule, uint rulepos, int writepos) { +static int _vbpt_write_subexpression(ParseTreeNode *nodes, int *pos, ParseRule *rule, uint rulepos, int writepos) { uint token; while ((token = ((rulepos < rule->_data.size()) ? rule->_data[rulepos++] : TOKEN_CPAREN)) != TOKEN_CPAREN) { @@ -565,15 +565,15 @@ int Vocabulary::parseGNF(const ResultWordList &words, bool verbose) { int temp, pos; _parserNodes[0].type = kParseTreeBranchNode; - _parserNodes[0].content.branches[0] = 1; - _parserNodes[0].content.branches[1] = 2; + _parserNodes[0].left = &_parserNodes[1]; + _parserNodes[0].right = &_parserNodes[2]; _parserNodes[1].type = kParseTreeLeafNode; - _parserNodes[1].content.value = 0x141; + _parserNodes[1].value = 0x141; _parserNodes[2].type = kParseTreeBranchNode; - _parserNodes[2].content.branches[0] = 0; - _parserNodes[2].content.branches[1] = 0; + _parserNodes[2].left = 0; + _parserNodes[2].right = 0; pos = 2; diff --git a/engines/sci/parser/said.cpp b/engines/sci/parser/said.cpp index f49704372a..9c07be2dff 100644 --- a/engines/sci/parser/said.cpp +++ b/engines/sci/parser/said.cpp @@ -1,111 +1,3 @@ -/* A Bison parser, made by GNU Bison 2.3. */ - -/* Skeleton implementation for Bison's Yacc-like parsers in C - - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. - - 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, 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. */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* C LALR(1) parser skeleton written by Richard Stallman, by - simplifying the original so-called "semantic" parser. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output. */ -#define YYBISON 1 - -/* Bison version. */ -#define YYBISON_VERSION "2.3" - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 0 - -/* Using locations. */ -#define YYLSP_NEEDED 0 - - - -/* Tokens. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum yytokentype { - WGROUP = 258, - YY_COMMA = 259, - YY_AMP = 260, - YY_SLASH = 261, - YY_PARENO = 262, - YY_PARENC = 263, - YY_BRACKETSO = 264, - YY_BRACKETSC = 265, - YY_HASH = 266, - YY_LT = 267, - YY_GT = 268, - YY_BRACKETSO_LT = 269, - YY_BRACKETSO_SLASH = 270, - YY_LT_BRACKETSO = 271, - YY_LT_PARENO = 272 - }; -#endif -/* Tokens. */ -#define WGROUP 258 -#define YY_COMMA 259 -#define YY_AMP 260 -#define YY_SLASH 261 -#define YY_PARENO 262 -#define YY_PARENC 263 -#define YY_BRACKETSO 264 -#define YY_BRACKETSC 265 -#define YY_HASH 266 -#define YY_LT 267 -#define YY_GT 268 -#define YY_BRACKETSO_LT 269 -#define YY_BRACKETSO_SLASH 270 -#define YY_LT_BRACKETSO 271 -#define YY_LT_PARENO 272 - - - - -/* Copy the first part of user declarations. */ - - /* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names @@ -133,14 +25,6 @@ #include "sci/engine/state.h" - -// Bison generates an empty switch statement that gives a warning in MSVC. -// This disables that warning. -#ifdef _MSC_VER -#pragma warning(disable:4065) -#endif - - namespace Sci { #define SAID_BRANCH_NULL 0 @@ -150,25 +34,8 @@ namespace Sci { // Maximum number of words to be expected in a parsed sentence #define AUGMENT_MAX_WORDS 64 - -#define ANYWORD 0xfff - -#define WORD_TYPE_BASE 0x141 -#define WORD_TYPE_REF 0x144 -#define WORD_TYPE_SYNTACTIC_SUGAR 0x145 - -#define AUGMENT_SENTENCE_PART_BRACKETS 0x152 - -// Minor numbers -#define AUGMENT_SENTENCE_MINOR_MATCH_PHRASE 0x14c -#define AUGMENT_SENTENCE_MINOR_MATCH_WORD 0x153 -#define AUGMENT_SENTENCE_MINOR_RECURSE 0x144 -#define AUGMENT_SENTENCE_MINOR_PARENTHESES 0x14f - - -#undef YYDEBUG /*1*/ -//#define SAID_DEBUG*/ -//#define SCI_DEBUG_PARSE_TREE_AUGMENTATION // uncomment to debug parse tree augmentation +// uncomment to debug parse tree augmentation +//#define SCI_DEBUG_PARSE_TREE_AUGMENTATION #ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION @@ -179,1840 +46,622 @@ void print_nothing(...) { } #endif -static char *said_parse_error; - static int said_token; static int said_tokens_nr; static int said_tokens[MAX_SAID_TOKENS]; -static int said_blessed; // increminated by said_top_branch - -static int said_tree_pos; // Set to 0 if we're out of space -#define SAID_TREE_START 4; // Reserve space for the 4 top nodes - -#define VALUE_IGNORE -424242 - -static parse_tree_node_t said_tree[VOCAB_TREE_NODES]; - -typedef int wgroup_t; -typedef int tree_t; -typedef int said_spec_t; - -static tree_t said_aug_branch(int, int, tree_t, tree_t); -static tree_t said_attach_branch(tree_t, tree_t); -/* -static tree_t said_wgroup_branch(wgroup_t); -*/ -static said_spec_t said_top_branch(tree_t); -static tree_t said_paren(tree_t, tree_t); -static tree_t said_value(int, tree_t); -static tree_t said_terminal(int); - -static int yylex(); - -static int yyerror(const char *s) { - said_parse_error = strdup(s); - return 1; /* Abort */ -} - - - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 0 -#endif - -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 -#endif - -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -typedef int YYSTYPE; -# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -# define YYSTYPE_IS_DECLARED 1 -# define YYSTYPE_IS_TRIVIAL 1 -#endif - - - -/* Copy the second part of user declarations. */ - - -/* Line 216 of yacc.c. */ - - -#ifdef short -# undef short -#endif -#ifdef YYTYPE_UINT8 -typedef YYTYPE_UINT8 yytype_uint8; -#else -typedef unsigned char yytype_uint8; -#endif - -#ifdef YYTYPE_INT8 -typedef YYTYPE_INT8 yytype_int8; -#elif (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -typedef signed char yytype_int8; -#else -typedef short int yytype_int8; -#endif - -#ifdef YYTYPE_UINT16 -typedef YYTYPE_UINT16 yytype_uint16; -#else -typedef unsigned short int yytype_uint16; -#endif - -#ifdef YYTYPE_INT16 -typedef YYTYPE_INT16 yytype_int16; -#else -typedef short int yytype_int16; -#endif - -#ifndef YYSIZE_T -# ifdef __SIZE_TYPE__ -# define YYSIZE_T __SIZE_TYPE__ -# elif defined size_t -# define YYSIZE_T size_t -# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# else -# define YYSIZE_T unsigned int -# endif -#endif - -#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) - -#ifndef YY_ -# if YYENABLE_NLS -# if ENABLE_NLS -# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ -# define YY_(msgid) dgettext ("bison-runtime", msgid) -# endif -# endif -# ifndef YY_ -# define YY_(msgid) msgid -# endif -#endif - -/* Suppress unused-variable warnings by "using" E. */ -#if ! defined lint || defined __GNUC__ -# define YYUSE(e) ((void) (e)) -#else -# define YYUSE(e) /* empty */ -#endif - -/* Identity function, used to suppress warnings about constant conditions. */ -#ifndef lint -# define YYID(n) (n) -#else -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static int -YYID (int i) -#else -static int -YYID (i) - int i; -#endif -{ - return i; -} -#endif - -#if ! defined yyoverflow || YYERROR_VERBOSE - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -# ifdef YYSTACK_USE_ALLOCA -# if YYSTACK_USE_ALLOCA -# ifdef __GNUC__ -# define YYSTACK_ALLOC __builtin_alloca -# elif defined __BUILTIN_VA_ARG_INCR -# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ -# elif defined _AIX -# define YYSTACK_ALLOC __alloca -# elif defined _MSC_VER -# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ -# define alloca _alloca -# else -# define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 -# endif -# endif -# endif -# endif -# endif - -# ifdef YYSTACK_ALLOC - /* Pacify GCC's `empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) -# ifndef YYSTACK_ALLOC_MAXIMUM - /* The OS might guarantee only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely - invoke alloca (N) if N exceeds 4096. Use a slightly smaller number - to allow for a few compiler-allocated temporary stack slots. */ -# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ -# endif -# else -# define YYSTACK_ALLOC YYMALLOC -# define YYSTACK_FREE YYFREE -# ifndef YYSTACK_ALLOC_MAXIMUM -# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM -# endif -# if (defined __cplusplus && ! defined _STDLIB_H \ - && ! ((defined YYMALLOC || defined malloc) \ - && (defined YYFREE || defined free))) -# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 -# endif -# endif -# ifndef YYMALLOC -# define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# ifndef YYFREE -# define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -void free (void *); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# endif -#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ - - -#if (! defined yyoverflow \ - && (! defined __cplusplus \ - || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc -{ - yytype_int16 yyss; - YYSTYPE yyvs; - }; - -/* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -# define YYSTACK_BYTES(N) \ - ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ - + YYSTACK_GAP_MAXIMUM) - -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (YYID (0)) -# endif -# endif - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack, Stack, yysize); \ - Stack = &yyptr->Stack; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (YYID (0)) - -#endif - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 23 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 80 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 18 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 13 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 35 -/* YYNRULES -- Number of states. */ -#define YYNSTATES 69 - -/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ -#define YYUNDEFTOK 2 -#define YYMAXUTOK 272 - -#define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) - -/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ -static const yytype_uint8 yytranslate[] = -{ - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17 -}; - -#if YYDEBUG -/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in - YYRHS. */ -static const yytype_uint8 yyprhs[] = -{ - 0, 0, 3, 6, 10, 15, 16, 18, 19, 21, - 24, 29, 31, 34, 39, 41, 43, 45, 49, 51, - 55, 59, 64, 70, 73, 75, 77, 79, 83, 88, - 92, 97, 100, 105, 109, 112 -}; - -/* YYRHS -- A `-1'-separated list of the rules' RHS. */ -static const yytype_int8 yyrhs[] = -{ - 19, 0, -1, 21, 20, -1, 21, 22, 20, -1, - 21, 22, 23, 20, -1, -1, 13, -1, -1, 27, - -1, 6, 27, -1, 15, 6, 27, 10, -1, 6, - -1, 6, 27, -1, 15, 6, 27, 10, -1, 6, - -1, 3, -1, 26, -1, 9, 26, 10, -1, 24, - -1, 7, 27, 8, -1, 26, 4, 26, -1, 26, - 14, 29, 10, -1, 26, 4, 9, 26, 10, -1, - 25, 28, -1, 25, -1, 28, -1, 29, -1, 14, - 29, 10, -1, 29, 14, 29, 10, -1, 12, 24, - 30, -1, 17, 7, 27, 8, -1, 12, 26, -1, - 16, 9, 26, 10, -1, 12, 26, 30, -1, 12, - 26, -1, 17, 7, 27, 8, -1 +static int said_tree_pos; +#define SAID_TREE_START 4 // Reserve space for the 4 top nodes + +enum SaidToken { + TOKEN_COMMA = 0xF000, + TOKEN_AMP = 0xF100, + TOKEN_SLASH = 0xF200, + TOKEN_PARENO = 0xF300, + TOKEN_PARENC = 0xF400, + TOKEN_BRACKETO = 0xF500, + TOKEN_BRACKETC = 0xF600, + TOKEN_HASH = 0xF700, + TOKEN_LT = 0xF800, + TOKEN_GT = 0xF900, + TOKEN_TERM = 0xFF00 }; -/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ -static const yytype_uint8 yyrline[] = -{ - 0, 130, 130, 132, 134, 140, 141, 148, 149, 155, - 157, 159, 165, 167, 169, 174, 179, 181, 186, 188, - 190, 192, 194, 199, 201, 203, 208, 210, 212, 217, - 219, 221, 223, 228, 230, 232 +enum SaidWord { + WORD_NONE = 0x0ffe, + WORD_ANY = 0x0fff }; -#endif -#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE -/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -static const char *const yytname[] = -{ - "$end", "error", "$undefined", "WGROUP", "YY_COMMA", "YY_AMP", - "YY_SLASH", "YY_PARENO", "YY_PARENC", "YY_BRACKETSO", "YY_BRACKETSC", - "YY_HASH", "YY_LT", "YY_GT", "YY_BRACKETSO_LT", "YY_BRACKETSO_SLASH", - "YY_LT_BRACKETSO", "YY_LT_PARENO", "$accept", "saidspec", "optcont", - "leftspec", "midspec", "rightspec", "word", "cwordset", "wordset", - "expr", "cwordrefset", "wordrefset", "recref", 0 -}; -#endif -# ifdef YYPRINT -/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to - token YYLEX-NUM. */ -static const yytype_uint16 yytoknum[] = -{ - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272 -}; -# endif -/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const yytype_uint8 yyr1[] = -{ - 0, 18, 19, 19, 19, 20, 20, 21, 21, 22, - 22, 22, 23, 23, 23, 24, 25, 25, 26, 26, - 26, 26, 26, 27, 27, 27, 28, 28, 28, 29, - 29, 29, 29, 30, 30, 30 -}; +// TODO: maybe turn this into a proper n-ary tree instead of an +// n-ary tree implemented in terms of a binary tree. +// (Together with _parserNodes in Vocabulary) -/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ -static const yytype_uint8 yyr2[] = -{ - 0, 2, 2, 3, 4, 0, 1, 0, 1, 2, - 4, 1, 2, 4, 1, 1, 1, 3, 1, 3, - 3, 4, 5, 2, 1, 1, 1, 3, 4, 3, - 4, 2, 4, 3, 2, 4 -}; +static ParseTreeNode said_tree[VOCAB_TREE_NODES]; -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero - means the default is an error. */ -static const yytype_uint8 yydefact[] = -{ - 7, 15, 0, 0, 0, 0, 0, 0, 0, 5, - 18, 24, 16, 8, 25, 26, 0, 0, 18, 31, - 0, 0, 0, 1, 11, 6, 0, 2, 5, 23, - 0, 0, 0, 19, 17, 0, 0, 29, 27, 0, - 0, 9, 0, 14, 0, 3, 5, 0, 20, 0, - 0, 34, 0, 32, 30, 0, 12, 0, 4, 0, - 21, 28, 33, 0, 10, 0, 22, 35, 13 -}; - -/* YYDEFGOTO[NTERM-NUM]. */ -static const yytype_int8 yydefgoto[] = -{ - -1, 8, 27, 9, 28, 46, 10, 11, 12, 13, - 14, 15, 37 -}; - -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -#define YYPACT_NINF -24 -static const yytype_int8 yypact[] = -{ - -1, -24, -1, 62, 62, 54, 1, 5, 18, 38, - -24, 47, 3, -24, -24, 12, 23, 15, -3, 3, - 28, 62, -1, -24, -1, -24, 42, -24, 39, -24, - 53, 54, 54, -24, -24, 62, 50, -24, -24, 29, - 41, -24, -1, -1, 52, -24, 55, 62, 3, 57, - 63, 20, -1, -24, -24, 64, -24, -1, -24, 32, - -24, -24, -24, 67, -24, 66, -24, -24, -24 -}; - -/* YYPGOTO[NTERM-NUM]. */ -static const yytype_int8 yypgoto[] = -{ - -24, -24, -23, -24, -24, -24, 68, -24, 0, -2, - 69, -4, 26 -}; - -/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -1 -static const yytype_uint8 yytable[] = -{ - 16, 20, 1, 17, 19, 45, 2, 30, 3, 35, - 21, 4, 22, 5, 36, 6, 7, 31, 23, 30, - 40, 39, 41, 58, 30, 34, 32, 49, 50, 31, - 48, 33, 35, 30, 31, 51, 30, 36, 38, 53, - 55, 56, 66, 31, 24, 43, 31, 59, 42, 54, - 63, 25, 25, 26, 44, 65, 1, 52, 57, 4, - 2, 5, 47, 6, 7, 1, 4, 60, 25, 2, - 6, 7, 18, 61, 64, 67, 68, 62, 0, 0, - 29 -}; - -static const yytype_int8 yycheck[] = -{ - 2, 5, 3, 3, 4, 28, 7, 4, 9, 12, - 9, 12, 7, 14, 17, 16, 17, 14, 0, 4, - 22, 21, 24, 46, 4, 10, 14, 31, 32, 14, - 30, 8, 12, 4, 14, 35, 4, 17, 10, 10, - 42, 43, 10, 14, 6, 6, 14, 47, 6, 8, - 52, 13, 13, 15, 15, 57, 3, 7, 6, 12, - 7, 14, 9, 16, 17, 3, 12, 10, 13, 7, - 16, 17, 4, 10, 10, 8, 10, 51, -1, -1, - 11 -}; - -/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ -static const yytype_uint8 yystos[] = -{ - 0, 3, 7, 9, 12, 14, 16, 17, 19, 21, - 24, 25, 26, 27, 28, 29, 27, 26, 24, 26, - 29, 9, 7, 0, 6, 13, 15, 20, 22, 28, - 4, 14, 14, 8, 10, 12, 17, 30, 10, 26, - 27, 27, 6, 6, 15, 20, 23, 9, 26, 29, - 29, 26, 7, 10, 8, 27, 27, 6, 20, 26, - 10, 10, 30, 27, 10, 27, 10, 8, 10 -}; - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) -#define YYEMPTY (-2) -#define YYEOF 0 - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab - - -/* Like YYERROR except do call yyerror. This remains here temporarily - to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ - -#define YYFAIL goto yyerrlab - -#define YYRECOVERING() (!!yyerrstatus) - -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ - YYPOPSTACK (1); \ - goto yybackup; \ - } \ - else \ - { \ - yyerror (YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ -while (YYID (0)) - - -#define YYTERROR 1 -#define YYERRCODE 256 - - -/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. - If N is 0, then set CURRENT to the empty location which ends - the previous symbol: RHS[0] (always defined). */ - -#define YYRHSLOC(Rhs, K) ((Rhs)[K]) -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - do \ - if (YYID (N)) \ - { \ - (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ - } \ - else \ - { \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC (Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC (Rhs, 0).last_column; \ - } \ - while (YYID (0)) -#endif - - -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ - -#ifndef YY_LOCATION_PRINT -# if YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif -#endif +typedef int wgroup_t; +typedef int said_spec_t; -/* YYLEX -- calling `yylex' with the right arguments. */ -#ifdef YYLEX_PARAM -# define YYLEX yylex (YYLEX_PARAM) -#else -# define YYLEX yylex () -#endif +static ParseTreeNode* said_next_node() { + assert(said_tree_pos > 0 && said_tree_pos < VOCAB_TREE_NODES); -/* Enable debugging if requested. */ -#if YYDEBUG - -# ifndef YYFPRINTF -# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ -# define YYFPRINTF fprintf -# endif - -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (YYID (0)) - -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yy_symbol_print (stderr, \ - Type, Value); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (YYID (0)) - - -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -/*ARGSUSED*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) -#else -static void -yy_symbol_value_print (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE const * const yyvaluep; -#endif -{ - if (!yyvaluep) - return; -# ifdef YYPRINT - if (yytype < YYNTOKENS) - YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); -# else - YYUSE (yyoutput); -# endif - switch (yytype) - { - default: - break; - } + return &said_tree[said_tree_pos++]; } +static ParseTreeNode* said_leaf_node(ParseTreeNode* pos, int value) { + pos->type = kParseTreeLeafNode; + pos->value = value; -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) -#else -static void -yy_symbol_print (yyoutput, yytype, yyvaluep) - FILE *yyoutput; - int yytype; - YYSTYPE const * const yyvaluep; -#endif -{ - if (yytype < YYNTOKENS) - YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); - else - YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); - - yy_symbol_value_print (yyoutput, yytype, yyvaluep); - YYFPRINTF (yyoutput, ")"); + return pos; } -/*------------------------------------------------------------------. -| yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (included). | -`------------------------------------------------------------------*/ +static ParseTreeNode* said_word_node(ParseTreeNode* pos, int value) { + pos->type = kParseTreeWordNode; + pos->value = value; -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) -#else -static void -yy_stack_print (bottom, top) - yytype_int16 *bottom; - yytype_int16 *top; -#endif -{ - YYFPRINTF (stderr, "Stack now"); - for (; bottom <= top; ++bottom) - YYFPRINTF (stderr, " %d", *bottom); - YYFPRINTF (stderr, "\n"); + return pos; } -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (yydebug) \ - yy_stack_print ((Bottom), (Top)); \ -} while (YYID (0)) - +static ParseTreeNode* said_branch_node(ParseTreeNode* pos, + ParseTreeNode* left, + ParseTreeNode* right) { + pos->type = kParseTreeBranchNode; + pos->left = left; + pos->right = right; -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yy_reduce_print (YYSTYPE *yyvsp, int yyrule) -#else -static void -yy_reduce_print (yyvsp, yyrule) - YYSTYPE *yyvsp; - int yyrule; -#endif -{ - int yynrhs = yyr2[yyrule]; - int yyi; - unsigned long int yylno = yyrline[yyrule]; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", - yyrule - 1, yylno); - /* The symbols being reduced. */ - for (yyi = 0; yyi < yynrhs; yyi++) - { - fprintf (stderr, " $%d = ", yyi + 1); - yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], - &(yyvsp[(yyi + 1) - (yynrhs)]) - ); - fprintf (stderr, "\n"); - } + return pos; } -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (yydebug) \ - yy_reduce_print (yyvsp, Rule); \ -} while (YYID (0)) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -# define YYDPRINTF(Args) -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) -# define YY_STACK_PRINT(Bottom, Top) -# define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -# define YYINITDEPTH 200 -#endif +static ParseTreeNode* said_branch_attach_left(ParseTreeNode* pos, + ParseTreeNode* left) { + pos->type = kParseTreeBranchNode; + pos->left = left; -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#ifndef YYMAXDEPTH -# define YYMAXDEPTH 10000 -#endif + return pos; - +} -#if YYERROR_VERBOSE +static ParseTreeNode* said_branch_attach_right(ParseTreeNode* pos, + ParseTreeNode* right) { + pos->type = kParseTreeBranchNode; + pos->right = right; -# ifndef yystrlen -# if defined __GLIBC__ && defined _STRING_H -# define yystrlen strlen -# else -/* Return the length of YYSTR. */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static YYSIZE_T -yystrlen (const char *yystr) -#else -static YYSIZE_T -yystrlen (yystr) - const char *yystr; -#endif -{ - YYSIZE_T yylen; - for (yylen = 0; yystr[yylen]; yylen++) - continue; - return yylen; + return pos; } -# endif -# endif - -# ifndef yystpcpy -# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE -# define yystpcpy stpcpy -# else -/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static char * -yystpcpy (char *yydest, const char *yysrc) -#else -static char * -yystpcpy (yydest, yysrc) - char *yydest; - const char *yysrc; -#endif -{ - char *yyd = yydest; - const char *yys = yysrc; - while ((*yyd++ = *yys++) != '\0') - continue; - return yyd - 1; -} -# endif -# endif - -# ifndef yytnamerr -/* Copy to YYRES the contents of YYSTR after stripping away unnecessary - quotes and backslashes, so that it's suitable for yyerror. The - heuristic is that double-quoting is unnecessary unless the string - contains an apostrophe, a comma, or backslash (other than - backslash-backslash). YYSTR is taken from yytname. If YYRES is - null, do not copy; instead, return the length of what the result - would have been. */ -static YYSIZE_T -yytnamerr (char *yyres, const char *yystr) -{ - if (*yystr == '"') - { - YYSIZE_T yyn = 0; - char const *yyp = yystr; - - for (;;) - switch (*++yyp) - { - case '\'': - case ',': - goto do_not_strip_quotes; - - case '\\': - if (*++yyp != '\\') - goto do_not_strip_quotes; - /* Fall through. */ - default: - if (yyres) - yyres[yyn] = *yyp; - yyn++; - break; - - case '"': - if (yyres) - yyres[yyn] = '\0'; - return yyn; - } - do_not_strip_quotes: ; - } - - if (! yyres) - return yystrlen (yystr); - - return yystpcpy (yyres, yystr) - yyres; -} -# endif - -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) -{ - int yyn = yypact[yystate]; - - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else - { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; - -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } - - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - - if (yysize_overflow) - return YYSIZE_MAXIMUM; - - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; - } -} -#endif /* YYERROR_VERBOSE */ - - -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -/*ARGSUSED*/ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -static void -yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) -#else -static void -yydestruct (yymsg, yytype, yyvaluep) - const char *yymsg; - int yytype; - YYSTYPE *yyvaluep; -#endif -{ - YYUSE (yyvaluep); +/* + pos + / \ + . \ + * + / \ + / 0 + * + / \ + / \ + / subtree + major / \ + / . + minor + + . = unchanged child node + * = new branch node + 0 = NULL child node. (Location for future siblings of the subtree) - if (!yymsg) - yymsg = "Deleting"; - YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); +*/ - switch (yytype) - { +static bool said_attach_subtree(ParseTreeNode* pos, int major, int minor, + ParseTreeNode* subtree) { + bool retval = true; - default: - break; - } -} - + said_branch_attach_right(pos, + said_branch_node(said_next_node(), + said_branch_node(said_next_node(), + said_leaf_node(said_next_node(), major), + said_branch_attach_left(subtree, + said_leaf_node(said_next_node(), minor))), + 0)); -/* Prevent warnings from -Wmissing-prototypes. */ + return retval; +} -#ifdef YYPARSE_PARAM -#if defined __STDC__ || defined __cplusplus -int yyparse (void *YYPARSE_PARAM); -#else -int yyparse (); -#endif -#else /* ! YYPARSE_PARAM */ -#if defined __STDC__ || defined __cplusplus -int yyparse (void); -#else -int yyparse (); -#endif -#endif /* ! YYPARSE_PARAM */ -/* The look-ahead symbol. */ -int yychar; +/*****************/ +/**** Parsing ****/ +/*****************/ -/* The semantic value of the look-ahead symbol. */ -YYSTYPE yylval; +static bool parseSpec(ParseTreeNode* parentNode); +static bool parsePart2(ParseTreeNode* parentNode, bool& nonempty); +static bool parsePart3(ParseTreeNode* parentNode, bool& nonempty); +static bool parseSlash(ParseTreeNode* parentNode); +static bool parseExpr(ParseTreeNode* parentNode); +static bool parseRef(ParseTreeNode* parentNode); +static bool parseComma(ParseTreeNode* parentNode); +static bool parseList(ParseTreeNode* parentNode); +static bool parseListEntry(ParseTreeNode* parentNode); +static bool parseWord(ParseTreeNode* parentNode); -/* Number of syntax errors so far. */ -int yynerrs; +static bool parseWord(ParseTreeNode* parentNode) +{ + int token = said_tokens[said_token]; + if (token & 0x8000) + return false; + said_token++; + ParseTreeNode* newNode = said_word_node(said_next_node(), token); -/*----------. -| yyparse. | -`----------*/ + parentNode->right = newNode; -#ifdef YYPARSE_PARAM -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -int -yyparse (void *YYPARSE_PARAM) -#else -int -yyparse (YYPARSE_PARAM) - void *YYPARSE_PARAM; -#endif -#else /* ! YYPARSE_PARAM */ -#if (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -int -yyparse (void) -#else -int -yyparse () + return true; +} -#endif -#endif +static bool parsePart2(ParseTreeNode* parentNode, bool& nonempty) { - - int yystate; - int yyn; - int yyresult; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - /* Look-ahead token as an internal (translated) token number. */ - int yytoken = 0; -#if YYERROR_VERBOSE - /* Buffer for error messages, and its allocated size. */ - char yymsgbuf[128]; - char *yymsg = yymsgbuf; - YYSIZE_T yymsg_alloc = sizeof yymsgbuf; -#endif + // Store current state for rolling back if we fail + int curToken = said_token; + int curTreePos = said_tree_pos; + ParseTreeNode* curRightChild = parentNode->right; - /* Three stacks and their tools: - `yyss': related to states, - `yyvs': related to semantic values, - `yyls': related to locations. - - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss = yyssa; - yytype_int16 *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - YYSTYPE *yyvsp; - - - -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0); - YYSIZE_T yystacksize = YYINITDEPTH; - - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; + nonempty = true; + bool found; - /* The number of symbols on the RHS of the reduced rule. - Keep to zero when no symbol should be popped. */ - int yylen = 0; + found = parseSlash(newNode); - YYDPRINTF ((stderr, "Starting parse\n")); + if (found) { - yystate = 0; - yyerrstatus = 0; - yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ + said_attach_subtree(parentNode, 0x142, 0x14a, newNode); - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ + return true; - yyssp = yyss; - yyvsp = yyvs; + } else if (said_tokens[said_token] == TOKEN_BRACKETO) { + said_token++; + + found = parsePart2(newNode, nonempty); - goto yysetstate; + if (found) { -/*------------------------------------------------------------. -| yynewstate -- Push a new state, which is found in yystate. | -`------------------------------------------------------------*/ - yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. So pushing a state here evens the stacks. */ - yyssp++; - - yysetstate: - *yyssp = yystate; - - if (yyss + yystacksize - 1 <= yyssp) - { - /* Get the current used size of the three stacks, in elements. */ - YYSIZE_T yysize = yyssp - yyss + 1; - -#ifdef yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - YYSTYPE *yyvs1 = yyvs; - yytype_int16 *yyss1 = yyss; - - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow (YY_("memory exhausted"), - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), - - &yystacksize); - - yyss = yyss1; - yyvs = yyvs1; - } -#else /* no yyoverflow */ -# ifndef YYSTACK_RELOCATE - goto yyexhaustedlab; -# else - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) - goto yyexhaustedlab; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - { - yytype_int16 *yyss1 = yyss; - union yyalloc *yyptr = - (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); - if (! yyptr) - goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss); - YYSTACK_RELOCATE (yyvs); - -# undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); - } -# endif -#endif /* no yyoverflow */ - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - - YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) yystacksize)); - - if (yyss + yystacksize - 1 <= yyssp) - YYABORT; - } - - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - - goto yybackup; - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - - /* Do appropriate processing given the current state. Read a - look-ahead token if we need one and don't already have one. */ - - /* First try to decide what to do without reference to look-ahead token. */ - yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) - goto yydefault; - - /* Not known => get a look-ahead token if don't already have one. */ - - /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ - if (yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token: ")); - yychar = YYLEX; - } - - if (yychar <= YYEOF) - { - yychar = yytoken = YYEOF; - YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else - { - yytoken = YYTRANSLATE (yychar); - YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - yyn += yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) - goto yydefault; - yyn = yytable[yyn]; - if (yyn <= 0) - { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } + if (said_tokens[said_token] == TOKEN_BRACKETC) { + said_token++; - if (yyn == YYFINAL) - YYACCEPT; + said_attach_subtree(parentNode, 0x152, 0x142, newNode); - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; + return true; + } + } - /* Shift the look-ahead token. */ - YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + } - /* Discard the shifted token unless it is eof. */ - if (yychar != YYEOF) - yychar = YYEMPTY; + // CHECKME: this doesn't look right if the [] section matched partially + // Should the below 'if' be an 'else if' ? - yystate = yyn; - *++yyvsp = yylval; + if (said_tokens[said_token] == TOKEN_SLASH) { + said_token++; - goto yynewstate; + nonempty = false; + return true; -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; + } + // Rollback + said_token = curToken; + said_tree_pos = curTreePos; + parentNode->right = curRightChild; + return false; +} -/*-----------------------------. -| yyreduce -- Do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; +static bool parsePart3(ParseTreeNode* parentNode, bool& nonempty) +{ + // Store current state for rolling back if we fail + int curToken = said_token; + int curTreePos = said_tree_pos; + ParseTreeNode* curRightChild = parentNode->right; - /* If YYLEN is nonzero, implement the default value of the action: - `$$ = $1'. + ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0); - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1-yylen]; + bool found; + nonempty = true; - YY_REDUCE_PRINT (yyn); - switch (yyn) - { - case 2: + found = parseSlash(newNode); - { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]))); ;} - break; + if (found) { - case 3: + said_attach_subtree(parentNode, 0x143, 0x14a, newNode); - { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (3)]), said_attach_branch((yyvsp[(2) - (3)]), (yyvsp[(3) - (3)])))); ;} - break; + return true; - case 4: + } else if (said_tokens[said_token] == TOKEN_BRACKETO) { + said_token++; + + found = parsePart3(newNode, nonempty); - { (yyval) = said_top_branch(said_attach_branch((yyvsp[(1) - (4)]), said_attach_branch((yyvsp[(2) - (4)]), said_attach_branch((yyvsp[(3) - (4)]), (yyvsp[(4) - (4)]))))); ;} - break; + if (found) { - case 5: + if (said_tokens[said_token] == TOKEN_BRACKETC) { + said_token++; - { (yyval) = SAID_BRANCH_NULL; ;} - break; + said_attach_subtree(parentNode, 0x152, 0x143, newNode); - case 6: + return true; + } + } - { (yyval) = said_paren(said_value(0x14b, said_value(0xf900, said_terminal(0xf900))), SAID_BRANCH_NULL); ;} - break; + } - case 7: + // CHECKME: this doesn't look right if the [] section matched partially + // Should the below 'if' be an 'else if' ? - { (yyval) = SAID_BRANCH_NULL; ;} - break; + if (said_tokens[said_token] == TOKEN_SLASH) { + said_token++; - case 8: + nonempty = false; - { (yyval) = said_paren(said_value(0x141, said_value(0x149, (yyvsp[(1) - (1)]))), SAID_BRANCH_NULL); ;} - break; + return true; - case 9: + } - { (yyval) = said_aug_branch(0x142, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); ;} - break; + // Rollback + said_token = curToken; + said_tree_pos = curTreePos; + parentNode->right = curRightChild; + return false; +} - case 10: - { (yyval) = said_aug_branch(0x152, 0x142, said_aug_branch(0x142, 0x14a, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;} - break; +static bool parseSlash(ParseTreeNode* parentNode) +{ + // Store current state for rolling back if we fail + int curToken = said_token; + int curTreePos = said_tree_pos; + ParseTreeNode* curRightChild = parentNode->right; - case 11: + if (said_tokens[said_token] == TOKEN_SLASH) { + said_token++; - { (yyval) = SAID_BRANCH_NULL; ;} - break; + bool found = parseExpr(parentNode); - case 12: + if (found) + return true; - { (yyval) = said_aug_branch(0x143, 0x14a, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); ;} - break; + } - case 13: + // Rollback + said_token = curToken; + said_tree_pos = curTreePos; + parentNode->right = curRightChild; + return false; +} - { (yyval) = said_aug_branch(0x152, 0x143, said_aug_branch(0x143, 0x14a, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;} - break; - case 14: +static bool parseRef(ParseTreeNode* parentNode) +{ + // Store current state for rolling back if we fail + int curToken = said_token; + int curTreePos = said_tree_pos; + ParseTreeNode* curRightChild = parentNode->right; - { (yyval) = SAID_BRANCH_NULL; ;} - break; + ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0); - case 15: + ParseTreeNode* newParent = parentNode; - { (yyval) = said_paren(said_value(0x141, said_value(0x153, said_terminal((yyvsp[(1) - (1)])))), SAID_BRANCH_NULL); ;} - break; + bool found; - case 16: + if (said_tokens[said_token] == TOKEN_LT) { + said_token++; - { (yyval) = said_aug_branch(0x141, 0x14f, (yyvsp[(1) - (1)]), SAID_BRANCH_NULL); ;} - break; + found = parseList(newNode); - case 17: + if (found) { - { (yyval) = said_aug_branch(0x141, 0x14f, said_aug_branch(0x152, 0x14c, said_aug_branch(0x141, 0x14f, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;} - break; + said_attach_subtree(newParent, 0x144, 0x14f, newNode); - case 18: + newParent = newParent->right; + + newNode = said_branch_node(said_next_node(), 0, 0); - { (yyval) = (yyvsp[(1) - (1)]); ;} - break; + found = parseRef(newNode); - case 19: + if (found) { - { (yyval) = said_aug_branch(0x141, 0x14c, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL); ;} - break; + said_attach_subtree(newParent, 0x141, 0x144, newNode); - case 20: + } - { (yyval) = said_attach_branch((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); ;} - break; + return true; - case 21: + } - { (yyval) = said_attach_branch((yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); ;} - break; + } - case 22: + // NB: This is not an "else if'. + // If there is a "< [ ... ]", that is parsed as "< ..." - { (yyval) = said_attach_branch((yyvsp[(1) - (5)]), (yyvsp[(3) - (5)])); ;} - break; + if (said_tokens[said_token] == TOKEN_BRACKETO) { + said_token++; + + found = parseRef(newNode); - case 23: + if (found) { - { (yyval) = said_attach_branch((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); ;} - break; + if (said_tokens[said_token] == TOKEN_BRACKETC) { + said_token++; - case 24: + said_attach_subtree(parentNode, 0x152, 0x144, newNode); - { (yyval) = (yyvsp[(1) - (1)]); ;} - break; + return true; + } + } - case 25: + } - { (yyval) = (yyvsp[(1) - (1)]); ;} - break; + // Rollback + said_token = curToken; + said_tree_pos = curTreePos; + parentNode->right = curRightChild; + return false; +} - case 26: +static bool parseComma(ParseTreeNode* parentNode) +{ + // Store current state for rolling back if we fail + int curToken = said_token; + int curTreePos = said_tree_pos; + ParseTreeNode* curRightChild = parentNode->right; - { (yyval) = (yyvsp[(1) - (1)]); ;} - break; + if (said_tokens[said_token] == TOKEN_COMMA) { + said_token++; - case 27: + bool found = parseList(parentNode); - { (yyval) = said_aug_branch(0x152, 0x144, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL); ;} - break; + if (found) + return true; - case 28: + } - { (yyval) = said_attach_branch((yyvsp[(1) - (4)]), said_aug_branch(0x152, 0x144, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL)); ;} - break; + // Rollback + said_token = curToken; + said_tree_pos = curTreePos; + parentNode->right = curRightChild; + return false; +} - case 29: +static bool parseListEntry(ParseTreeNode* parentNode) +{ + // Store current state for rolling back if we fail + int curToken = said_token; + int curTreePos = said_tree_pos; + ParseTreeNode* curRightChild = parentNode->right; - { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (3)]), (yyvsp[(3) - (3)])); ;} - break; + ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0); - case 30: + bool found; - { (yyval) = said_aug_branch(0x144, 0x14f, said_aug_branch(0x141, 0x144, (yyvsp[(2) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;} - break; + if (said_tokens[said_token] == TOKEN_BRACKETO) { + said_token++; - case 31: + found = parseExpr(newNode); - { (yyval) = said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL); ;} - break; + if (found) { - case 32: + if (said_tokens[said_token] == TOKEN_BRACKETC) { + said_token++; - { (yyval) = said_aug_branch(0x152, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(3) - (4)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;} - break; + said_attach_subtree(parentNode, 0x152, 0x14c, newNode); - case 33: + return true; + } + } - { (yyval) = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (3)]), SAID_BRANCH_NULL), (yyvsp[(3) - (3)])); ;} - break; + } else if (said_tokens[said_token] == TOKEN_PARENO) { + said_token++; - case 34: + found = parseExpr(newNode); - { (yyval) = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, (yyvsp[(2) - (2)]), SAID_BRANCH_NULL), SAID_BRANCH_NULL); ;} - break; + if (found) { - case 35: + if (said_tokens[said_token] == TOKEN_PARENC) { + said_token++; - { (yyval) = said_aug_branch(0x141, 0x14c, (yyvsp[(2) - (4)]), SAID_BRANCH_NULL); ;} - break; + said_attach_subtree(parentNode, 0x141, 0x14c, newNode); + return true; + } + } -/* Line 1267 of yacc.c. */ + } else if (parseWord(newNode)) { - default: break; - } - YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + said_attach_subtree(parentNode, 0x141, 0x153, newNode); - YYPOPSTACK (yylen); - yylen = 0; - YY_STACK_PRINT (yyss, yyssp); + return true; - *++yyvsp = yyval; + } - /* Now `shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ + // Rollback + said_token = curToken; + said_tree_pos = curTreePos; + parentNode->right = curRightChild; + return false; +} - yyn = yyr1[yyn]; +static bool parseList(ParseTreeNode* parentNode) +{ + // Store current state for rolling back if we fail + int curToken = said_token; + int curTreePos = said_tree_pos; + ParseTreeNode* curRightChild = parentNode->right; - yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; - if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) - yystate = yytable[yystate]; - else - yystate = yydefgoto[yyn - YYNTOKENS]; + bool found; - goto yynewstate; + ParseTreeNode* newParent = parentNode; + found = parseListEntry(newParent); -/*------------------------------------. -| yyerrlab -- here on detecting error | -`------------------------------------*/ -yyerrlab: - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) - { - ++yynerrs; -#if ! YYERROR_VERBOSE - yyerror (YY_("syntax error")); -#else - { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } - } -#endif - } + if (found) { + newParent = newParent->right; + found = parseComma(newParent); - if (yyerrstatus == 3) - { - /* If just tried and failed to reuse look-ahead token after an - error, discard it. */ + return true; - if (yychar <= YYEOF) - { - /* Return failure if at end of input. */ - if (yychar == YYEOF) - YYABORT; - } - else - { - yydestruct ("Error: discarding", - yytoken, &yylval); - yychar = YYEMPTY; - } - } - - /* Else will try to reuse look-ahead token after shifting the error - token. */ - goto yyerrlab1; - - -/*---------------------------------------------------. -| yyerrorlab -- error raised explicitly by YYERROR. | -`---------------------------------------------------*/ -yyerrorlab: - - /* Pacify compilers like GCC when the user code never invokes - YYERROR and the label yyerrorlab therefore never appears in user - code. */ - if (/*CONSTCOND*/ 0) - goto yyerrorlab; - - /* Do not reclaim the symbols of the rule which action triggered - this YYERROR. */ - YYPOPSTACK (yylen); - yylen = 0; - YY_STACK_PRINT (yyss, yyssp); - yystate = *yyssp; - goto yyerrlab1; - - -/*-------------------------------------------------------------. -| yyerrlab1 -- common code for both syntax error and YYERROR. | -`-------------------------------------------------------------*/ -yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ - - for (;;) - { - yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) - { - yyn += YYTERROR; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } } - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) - YYABORT; + // Rollback + said_token = curToken; + said_tree_pos = curTreePos; + parentNode->right = curRightChild; + return false; +} +static bool parseExpr(ParseTreeNode* parentNode) +{ + // Store current state for rolling back if we fail + int curToken = said_token; + int curTreePos = said_tree_pos; + ParseTreeNode* curRightChild = parentNode->right; - yydestruct ("Error: popping", - yystos[yystate], yyvsp); - YYPOPSTACK (1); - yystate = *yyssp; - YY_STACK_PRINT (yyss, yyssp); - } + ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0); - if (yyn == YYFINAL) - YYACCEPT; + bool ret = false; + bool found; - *++yyvsp = yylval; + ParseTreeNode* newParent = parentNode; + found = parseList(newNode); - /* Shift the error token. */ - YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + if (found) { + ret = true; - yystate = yyn; - goto yynewstate; + said_attach_subtree(newParent, 0x141, 0x14F, newNode); + newParent = newParent->right; -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturn; + } -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturn; + found = parseRef(newParent); -#ifndef yyoverflow -/*-------------------------------------------------. -| yyexhaustedlab -- memory exhaustion comes here. | -`-------------------------------------------------*/ -yyexhaustedlab: - yyerror (YY_("memory exhausted")); - yyresult = 2; - /* Fall through. */ -#endif + if (found || ret) + return true; -yyreturn: - if (yychar != YYEOF && yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); - /* Do not reclaim the symbols of the rule which action triggered - this YYABORT or YYACCEPT. */ - YYPOPSTACK (yylen); - YY_STACK_PRINT (yyss, yyssp); - while (yyssp != yyss) - { - yydestruct ("Cleanup: popping", - yystos[*yyssp], yyvsp); - YYPOPSTACK (1); - } -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE (yyss); -#endif -#if YYERROR_VERBOSE - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); -#endif - /* Make sure YYID is used. */ - return YYID (yyresult); + // Rollback + said_token = curToken; + said_tree_pos = curTreePos; + parentNode->right = curRightChild; + return false; } +static bool parseSpec(ParseTreeNode* parentNode) +{ + // Store current state for rolling back if we fail + int curToken = said_token; + int curTreePos = said_tree_pos; + ParseTreeNode* curRightChild = parentNode->right; + ParseTreeNode* newNode = said_branch_node(said_next_node(), 0, 0); + bool ret = false; + bool found; -int parse_yy_token_lookup[] = {YY_COMMA, YY_AMP, YY_SLASH, YY_PARENO, YY_PARENC, YY_BRACKETSO, YY_BRACKETSC, YY_HASH, YY_LT, YY_GT}; + ParseTreeNode* newParent = parentNode; -static int yylex() { - int retval = said_tokens[said_token++]; + found = parseExpr(newNode); - if (retval < SAID_LONG(SAID_FIRST)) { - yylval = retval; - retval = WGROUP; - } else { - retval >>= 8; - - if (retval == SAID_TERM) - retval = 0; - else { - assert(retval >= SAID_FIRST); - retval = parse_yy_token_lookup[retval - SAID_FIRST]; - if (retval == YY_BRACKETSO) { - if ((said_tokens[said_token] >> 8) == SAID_LT) - retval = YY_BRACKETSO_LT; - else - if ((said_tokens[said_token] >> 8) == SAID_SLASH) - retval = YY_BRACKETSO_SLASH; - } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_BRACKO) { - retval = YY_LT_BRACKETSO; - } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_PARENO) { - retval = YY_LT_PARENO; - } - } - } + if (found) { + // Sentence part 1 found + said_attach_subtree(newParent, 0x141, 0x149, newNode); - return retval; -} + newParent = newParent->right; -static int said_next_node() { - return ((said_tree_pos == 0) || (said_tree_pos >= VOCAB_TREE_NODES)) ? said_tree_pos = 0 : said_tree_pos++; -} + ret = true; + } -#define SAID_NEXT_NODE said_next_node() + bool nonempty; -static int said_leaf_node(tree_t pos, int value) { - said_tree[pos].type = kParseTreeLeafNode; + found = parsePart2(newParent, nonempty); - if (value != VALUE_IGNORE) - said_tree[pos].content.value = value; + if (found) { - return pos; -} + ret = true; -static int said_branch_node(tree_t pos, int left, int right) { - said_tree[pos].type = kParseTreeBranchNode; + if (nonempty) // non-empty part found + newParent = newParent->right; - if (left != VALUE_IGNORE) - said_tree[pos].content.branches[0] = left; - if (right != VALUE_IGNORE) - said_tree[pos].content.branches[1] = right; + found = parsePart3(newParent, nonempty); - return pos; -} + if (found) { -static tree_t said_paren(tree_t t1, tree_t t2) { - if (t1) - return said_branch_node(SAID_NEXT_NODE, t1, t2); - else - return t2; -} + if (nonempty) + newParent = newParent->right; + } + } -static tree_t said_value(int val, tree_t t) { - return said_branch_node(SAID_NEXT_NODE, said_leaf_node(SAID_NEXT_NODE, val), t); + if (said_tokens[said_token] == TOKEN_GT) { + said_token++; -} + newNode = said_branch_node(said_next_node(), 0, + said_leaf_node(said_next_node(), TOKEN_GT)); -static tree_t said_terminal(int val) { - return said_leaf_node(SAID_NEXT_NODE, val); -} + said_attach_subtree(newParent, 0x14B, TOKEN_GT, newNode); -static tree_t said_aug_branch(int n1, int n2, tree_t t1, tree_t t2) { - int retval; + } - retval = said_branch_node(SAID_NEXT_NODE, - said_branch_node(SAID_NEXT_NODE, - said_leaf_node(SAID_NEXT_NODE, n1), - said_branch_node(SAID_NEXT_NODE, - said_leaf_node(SAID_NEXT_NODE, n2), - t1) - ), - t2); - -#ifdef SAID_DEBUG - fprintf(stderr, "AUG(0x%x, 0x%x, [%04x], [%04x]) = [%04x]\n", n1, n2, t1, t2, retval); -#endif - return retval; + if (ret) + return true; + + // Rollback + said_token = curToken; + said_tree_pos = curTreePos; + parentNode->right = curRightChild; + return false; } -static tree_t said_attach_branch(tree_t base, tree_t attacheant) { -#ifdef SAID_DEBUG - fprintf(stderr, "ATT2([%04x], [%04x]) = [%04x]\n", base, attacheant, base); -#endif - if (!attacheant) - return base; - if (!base) - return attacheant; +static bool buildSaidTree() { + said_branch_node(said_tree, &said_tree[1], &said_tree[2]); + said_leaf_node(&said_tree[1], 0x141); // Magic number #1 + said_branch_node(&said_tree[2], &said_tree[3], 0); + said_leaf_node(&said_tree[3], 0x13f); // Magic number #2 - if (!base) - return 0; // Happens if we're out of space + said_tree_pos = SAID_TREE_START; - said_branch_node(base, VALUE_IGNORE, attacheant); + bool ret = parseSpec(&said_tree[2]); - return base; -} + if (!ret) + return false; -static said_spec_t said_top_branch(tree_t first) { -#ifdef SAID_DEBUG - fprintf(stderr, "TOP([%04x])\n", first); -#endif - said_branch_node(0, 1, 2); - said_leaf_node(1, 0x141); // Magic number #1 - said_branch_node(2, 3, first); - said_leaf_node(3, 0x13f); // Magic number #2 + if (said_tokens[said_token] != TOKEN_TERM) { + // No terminator, so parse error. - ++said_blessed; + // Rollback + said_tree[2].right = 0; + said_token = 0; + said_tree_pos = SAID_TREE_START; + return false; + } - return 0; + return true; } -static int said_parse_spec(byte *spec) { +static int said_parse_spec(const byte *spec) { int nextitem; - said_parse_error = NULL; said_token = 0; said_tokens_nr = 0; - said_blessed = 0; said_tree_pos = SAID_TREE_START; @@ -2025,26 +674,13 @@ static int said_parse_spec(byte *spec) { } while ((nextitem != SAID_TERM) && (said_tokens_nr < MAX_SAID_TOKENS)); - if (nextitem == SAID_TERM) - yyparse(); - else { + if (nextitem != SAID_TERM) { warning("SAID spec is too long"); return 1; } - if (said_parse_error) { - warning("Error while parsing SAID spec: %s", said_parse_error); - free(said_parse_error); - return 1; - } - - if (said_tree_pos == 0) { - warning("Out of tree space while parsing SAID spec"); - return 1; - } - - if (said_blessed != 1) { - warning("Found multiple top branches"); + if (!buildSaidTree()) { + warning("Error while parsing SAID spec"); return 1; } @@ -2055,385 +691,304 @@ static int said_parse_spec(byte *spec) { /**** Augmentation ****/ /**********************/ -// primitive functions +static bool dontclaim; +static int outputDepth; -#define AUG_READ_BRANCH(a, br, p) \ - if (tree[p].type != kParseTreeBranchNode) \ - return 0; \ - a = tree[p].content.branches[br]; +enum ScanSaidType { + SCAN_SAID_AND = 0, + SCAN_SAID_OR = 1 +}; -#define AUG_READ_VALUE(a, p) \ - if (tree[p].type != kParseTreeLeafNode) \ - return 0; \ - a = tree[p].content.value; +static int matchTrees(ParseTreeNode* parseT, ParseTreeNode* saidT); +static int scanSaidChildren(ParseTreeNode* parseT, ParseTreeNode* saidT, + ScanSaidType type); +static int scanParseChildren(ParseTreeNode* parseT, ParseTreeNode* saidT); -#define AUG_ASSERT(i) \ - if (!i) return 0; -static int aug_get_next_sibling(parse_tree_node_t *tree, int pos, int *first, int *second) { - // Returns the next sibling relative to the specified position in 'tree', - // sets *first and *second to its augment node values, returns the new position - // or 0 if there was no next sibling - int seek, valpos; +static int node_major(ParseTreeNode* node) { + assert(node->type == kParseTreeBranchNode); + assert(node->left->type == kParseTreeLeafNode); + return node->left->value; +} +static int node_minor(ParseTreeNode* node) { + assert(node->type == kParseTreeBranchNode); + assert(node->right->type == kParseTreeBranchNode); + assert(node->right->left->type == kParseTreeLeafNode); + return node->right->left->value; +} +static bool node_is_terminal(ParseTreeNode* node) { + return (node->right->right && + node->right->right->type != kParseTreeBranchNode); +} +static int node_terminal_value(ParseTreeNode* node) { + assert(node_is_terminal(node)); + return node->right->right->value; +} +#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION +static void node_print_desc(ParseTreeNode* node) { + assert(node); + assert(node->left); + if (node->left->type == kParseTreeBranchNode) { + scidprintf("< "); + node_print_desc(node->left); + scidprintf(", ...>"); + } else { + if (node_is_terminal(node)) { + scidprintf("(%03x %03x %03x)", node_major(node), + node_minor(node), + node_terminal_value(node)); + } else { + scidprintf("(%03x %03x <...>)", node_major(node), + node_minor(node)); + } + } +} +#else +static void node_print_desc(ParseTreeNode*) { } +#endif - AUG_READ_BRANCH(pos, 1, pos); - AUG_ASSERT(pos); - AUG_READ_BRANCH(seek, 0, pos); - AUG_ASSERT(seek); - // Now retrieve first value - AUG_READ_BRANCH(valpos, 0, seek); - AUG_ASSERT(valpos); - AUG_READ_VALUE(*first, valpos); - // Get second value - AUG_READ_BRANCH(seek, 1, seek); - AUG_ASSERT(seek); - AUG_READ_BRANCH(valpos, 0, seek); - AUG_ASSERT(valpos); - AUG_READ_VALUE(*second, valpos); - return pos; -} +static int matchTrees(ParseTreeNode* parseT, ParseTreeNode* saidT) +{ + outputDepth++; + scidprintf("%*smatchTrees on ", outputDepth, ""); + node_print_desc(parseT); + scidprintf(" and "); + node_print_desc(saidT); + scidprintf("\n"); -static int aug_get_wgroup(parse_tree_node_t *tree, int pos) { - // Returns 0 if pos in tree is not the root of a 3-element list, otherwise - // it returns the last element (which, in practice, is the word group - int val; + bool inParen = node_minor(saidT) == 0x14F || node_minor(saidT) == 0x150; + bool inBracket = node_major(saidT) == 0x152; - AUG_READ_BRANCH(pos, 0, pos); - AUG_ASSERT(pos); - AUG_READ_BRANCH(pos, 1, pos); - AUG_ASSERT(pos); - AUG_READ_BRANCH(pos, 1, pos); - AUG_ASSERT(pos); - AUG_READ_VALUE(val, pos); + int ret; - return val; -} + if (node_major(parseT) != 0x141 && + node_major(saidT) != 0x141 && node_major(saidT) != 0x152 && + node_major(saidT) != node_major(parseT)) + { + ret = -1; + } -static int aug_get_base_node(parse_tree_node_t *tree) { - int startpos = 0; - AUG_READ_BRANCH(startpos, 1, startpos); + // parse major is 0x141 and/or + // said major is 0x141/0x152 and/or + // said major is parse major - return startpos; -} + else if (node_is_terminal(saidT) && node_is_terminal(parseT) ) { -// semi-primitive functions + // both saidT and parseT are terminals -static int aug_get_first_child(parse_tree_node_t *tree, int pos, int *first, int *second) { - // like aug_get_next_sibling, except that it recurses into the tree and - // finds the first child (usually *not* Ayanami Rei) of the current branch - // rather than its next sibling. - AUG_READ_BRANCH(pos, 0, pos); - AUG_ASSERT(pos); - AUG_READ_BRANCH(pos, 1, pos); - AUG_ASSERT(pos); + int said_val = node_terminal_value(saidT); + int parse_val = node_terminal_value(parseT); - return aug_get_next_sibling(tree, pos, first, second); -} + if (said_val != WORD_NONE && + (said_val == parse_val || said_val == WORD_ANY || + parse_val == WORD_ANY)) + ret = 1; + else + ret = -1; -static void aug_find_words_recursively(parse_tree_node_t *tree, int startpos, int *base_words, int *base_words_nr, - int *ref_words, int *ref_words_nr, int maxwords, int refbranch) { - // Finds and lists all base (141) and reference (144) words */ - int major, minor; - int word; - int pos = aug_get_first_child(tree, startpos, &major, &minor); + scidprintf("%*smatchTrees matching terminals: %03x vs %03x (%d)\n", + outputDepth, "", parse_val, said_val, ret); - //if (major == WORD_TYPE_REF) - // refbranch = 1; + } else if (node_is_terminal(saidT) && !node_is_terminal(parseT)) { - while (pos) { - if ((word = aug_get_wgroup(tree, pos))) { // found a word - if (!refbranch && major == WORD_TYPE_BASE) { - if ((*base_words_nr) == maxwords) { - warning("Out of regular words"); - return; // return gracefully - } + // saidT is a terminal, but parseT isn't - base_words[*base_words_nr] = word; // register word - ++(*base_words_nr); + if (node_major(parseT) == 0x141 || + node_major(parseT) == node_major(saidT)) + ret = scanParseChildren(parseT->right->right, saidT); + else + ret = 0; - } - if (major == WORD_TYPE_REF || refbranch) { - if ((*ref_words_nr) == maxwords) { - warning("Out of reference words"); - return; // return gracefully - } + } else if (node_is_terminal(parseT)) { - ref_words[*ref_words_nr] = word; // register word - ++(*ref_words_nr); + // parseT is a terminal, but saidT isn't - } - if (major != WORD_TYPE_SYNTACTIC_SUGAR && major != WORD_TYPE_BASE && major != WORD_TYPE_REF) - warning("aug_find_words_recursively(): Unknown word type %03x", major); + if (node_major(saidT) == 0x141 || node_major(saidT) == 0x152 || + node_major(saidT) == node_major(parseT)) + ret = scanSaidChildren(parseT, saidT->right->right, + inParen ? SCAN_SAID_OR : SCAN_SAID_AND ); + else + ret = 0; - } else // Did NOT find a word group: Attempt to recurse - aug_find_words_recursively(tree, pos, base_words, base_words_nr, - ref_words, ref_words_nr, maxwords, refbranch || major == WORD_TYPE_REF); + } else if (node_major(saidT) != 0x141 && node_major(saidT) != 0x152 && + node_major(saidT) != node_major(parseT)) { - pos = aug_get_next_sibling(tree, pos, &major, &minor); - } -} + // parseT and saidT both aren't terminals + // said major is not 0x141 or 0x152 or parse major + ret = scanParseChildren(parseT->right->right, saidT); -static void aug_find_words(parse_tree_node_t *tree, int startpos, int *base_words, int *base_words_nr, - int *ref_words, int *ref_words_nr, int maxwords) { - // initializing wrapper for aug_find_words_recursively() - *base_words_nr = 0; - *ref_words_nr = 0; + } else { - aug_find_words_recursively(tree, startpos, base_words, base_words_nr, ref_words, ref_words_nr, maxwords, 0); -} + // parseT and saidT are both not terminals, + // said major 0x141 or 0x152 or equal to parse major + ret = scanSaidChildren(parseT->right->right, saidT->right->right, + inParen ? SCAN_SAID_OR : SCAN_SAID_AND); -static int aug_contains_word(int *list, int length, int word) { - int i; + } - if (word == ANYWORD) - return (length); + if (inBracket && ret == 0) { + scidprintf("%*smatchTrees changing ret to 1 due to brackets\n", + outputDepth, ""); + ret = 1; + } - for (i = 0; i < length; i++) - if (list[i] == word) - return 1; + scidprintf("%*smatchTrees returning %d\n", outputDepth, "", ret); + outputDepth--; - return 0; + return ret; } -static int augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset, - int parse_branch, int major, int minor, int *base_words, int base_words_nr, - int *ref_words, int ref_words_nr); +static int scanSaidChildren(ParseTreeNode* parseT, ParseTreeNode* saidT, + ScanSaidType type) { + outputDepth++; + scidprintf("%*sscanSaid(%s) on ", outputDepth, "", + type == SCAN_SAID_OR ? "OR" : "AND"); + node_print_desc(parseT); + scidprintf(" and "); + node_print_desc(saidT); + scidprintf("\n"); -static int augment_match_expression_p(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset, - int parse_basepos, int major, int minor, - int *base_words, int base_words_nr, int *ref_words, int ref_words_nr) { - int cmajor, cminor, cpos; - cpos = aug_get_first_child(saidt, augment_pos, &cmajor, &cminor); - if (!cpos) { - warning("augment_match_expression_p(): Empty condition"); - return 1; - } + int ret = 1; - scidprintf("Attempting to match (%03x %03x (%03x %03x\n", major, minor, cmajor, cminor); - - if ((major == WORD_TYPE_BASE) && (minor == AUGMENT_SENTENCE_MINOR_RECURSE)) - return augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor, - base_words, base_words_nr, ref_words, ref_words_nr); - - switch (major) { - - case WORD_TYPE_BASE: - while (cpos) { - if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) { - int word = aug_get_wgroup(saidt, cpos); - scidprintf("Looking for word %03x\n", word); - - if (aug_contains_word(base_words, base_words_nr, word)) - return 1; - } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) { - if (augment_sentence_expression(saidt, cpos, parset, parse_basepos, cmajor, cminor, - base_words, base_words_nr, ref_words, ref_words_nr)) - return 1; - } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) { - int gc_major, gc_minor; - int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor); - - while (gchild) { - if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, major, - minor, base_words, base_words_nr, - ref_words, ref_words_nr)) - return 1; - gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor); - } - } else - warning("augment_match_expression_p(): Unknown type 141 minor number %3x", cminor); - - cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor); + assert(!(type == SCAN_SAID_OR && !saidT)); - } - break; - - case WORD_TYPE_REF: - while (cpos) { - if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) { - int word = aug_get_wgroup(saidt, cpos); - scidprintf("Looking for refword %03x\n", word); - - if (aug_contains_word(ref_words, ref_words_nr, word)) - return 1; - } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) { - if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor, - base_words, base_words_nr, ref_words, ref_words_nr)) - return 1; - } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) { - int gc_major, gc_minor; - int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor); - - while (gchild) { - if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, major, - minor, base_words, base_words_nr, - ref_words, ref_words_nr)) - return 1; - gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor); - } - } else - warning("augment_match_expression_p(): Unknown type 144 minor number %3x", cminor); - - cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor); + while (saidT) { + assert(saidT->type == kParseTreeBranchNode); - } - break; + ParseTreeNode* saidChild = saidT->left; + assert(saidChild); - case AUGMENT_SENTENCE_PART_BRACKETS: - if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor, - base_words, base_words_nr, ref_words, ref_words_nr)) - return 1; + if (node_major(saidChild) != 0x145) { - scidprintf("Didn't match subexpression; checking sub-bracked predicate %03x\n", cmajor); + ret = scanParseChildren(parseT, saidChild); - switch (cmajor) { - case WORD_TYPE_BASE: - if (!base_words_nr) - return 1; - break; + if (type == SCAN_SAID_AND && ret != 1) + break; - case WORD_TYPE_REF: - if (!ref_words_nr) - return 1; - break; + if (type == SCAN_SAID_OR && ret == 1) + break; - default: - warning("augment_match_expression_p(): (subp1) Unkonwn sub-bracket predicate %03x", cmajor); } - break; + saidT = saidT->right; - default: - warning("augment_match_expression_p(): Unknown predicate %03x", major); + } + scidprintf("%*sscanSaid returning %d\n", outputDepth, "", ret); + outputDepth--; + return ret; +} + + +static int scanParseChildren(ParseTreeNode* parseT, ParseTreeNode* saidT) { + + outputDepth++; + scidprintf("%*sscanParse on ", outputDepth, ""); + node_print_desc(parseT); + scidprintf(" and "); + node_print_desc(saidT); + scidprintf("\n"); + + if (node_major(saidT) == 0x14B) { + dontclaim = true; + scidprintf("%*sscanParse returning 1 (0x14B)\n", outputDepth, ""); + outputDepth--; + return 1; } - scidprintf("augment_match_expression_p(): Generic failure\n"); + bool inParen = node_minor(saidT) == 0x14F || node_minor(saidT) == 0x150; + bool inBracket = node_major(saidT) == 0x152; - return 0; -} + int ret; -static int augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset, - int parse_branch, int major, int minor, int *base_words, int base_words_nr, - int *ref_words, int ref_words_nr) { - int check_major, check_minor; - int check_pos = aug_get_first_child(saidt, augment_pos, &check_major, &check_minor); - do { - if (!(augment_match_expression_p(saidt, check_pos, parset, parse_branch, check_major, check_minor, - base_words, base_words_nr, ref_words, ref_words_nr))) - return 0; - } while ((check_pos = aug_get_next_sibling(saidt, check_pos, &check_major, &check_minor))); + // descend further down saidT before actually scanning parseT + if ((node_major(saidT) == 0x141 || node_major(saidT) == 0x152) && + !node_is_terminal(saidT)) { - return 1; -} + ret = scanSaidChildren(parseT, saidT->right->right, + inParen ? SCAN_SAID_OR : SCAN_SAID_AND ); -static int augment_sentence_part(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset, int parse_basepos, int major, int minor) { - int pmajor, pminor; - int parse_branch = parse_basepos; - int optional = 0; - int foundwords = 0; + } else if (parseT && parseT->left->type == kParseTreeBranchNode) { - scidprintf("Augmenting (%03x %03x\n", major, minor); + ret = 0; + int subresult = 0; - if (major == AUGMENT_SENTENCE_PART_BRACKETS) { // '[/ foo]' is true if '/foo' or if there - // exists no x for which '/x' is true - if ((augment_pos = aug_get_first_child(saidt, augment_pos, &major, &minor))) { - scidprintf("Optional part: Now augmenting (%03x %03x\n", major, minor); - optional = 1; - } else { - scidprintf("Matched empty optional expression\n"); - return 1; - } - } + while (parseT) { + assert(parseT->type == kParseTreeBranchNode); - if ((major < 0x141) || (major > 0x143)) { - scidprintf("augment_sentence_part(): Unexpected sentence part major number %03x\n", major); - return 0; - } + ParseTreeNode* parseChild = parseT->left; + assert(parseChild); - while ((parse_branch = aug_get_next_sibling(parset, parse_branch, &pmajor, &pminor))) { - if (pmajor == major) { // found matching sentence part - int success; - int base_words_nr; - int ref_words_nr; - int base_words[AUGMENT_MAX_WORDS]; - int ref_words[AUGMENT_MAX_WORDS]; -#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION - int i; -#endif + scidprintf("%*sscanning next: ", outputDepth, ""); + node_print_desc(parseChild); + scidprintf("\n"); - scidprintf("Found match with pminor = %03x\n", pminor); - aug_find_words(parset, parse_branch, base_words, &base_words_nr, ref_words, &ref_words_nr, AUGMENT_MAX_WORDS); - foundwords |= (ref_words_nr | base_words_nr); -#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION - printf("%d base words:", base_words_nr); - for (i = 0; i < base_words_nr; i++) - printf(" %03x", base_words[i]); - printf("\n%d reference words:", ref_words_nr); - for (i = 0; i < ref_words_nr; i++) - printf(" %03x", ref_words[i]); - printf("\n"); -#endif + if (node_major(parseChild) == node_major(saidT) || + node_major(parseChild) == 0x141) + subresult = matchTrees(parseChild, saidT); - success = augment_sentence_expression(saidt, augment_pos, parset, parse_basepos, major, minor, - base_words, base_words_nr, ref_words, ref_words_nr); + if (subresult != 0) + ret = subresult; + + if (ret == 1) + break; + + parseT = parseT->right; - if (success) { - scidprintf("SUCCESS on augmenting (%03x %03x\n", major, minor); - return 1; - } } + + // ret is now: + // 1 if ANY matchTrees(parseSibling, saidTree) returned 1 + // ELSE: -1 if ANY returned -1 + // ELSE: 0 + + } else { + + ret = matchTrees(parseT, saidT); + } - if (optional && (foundwords == 0)) { - scidprintf("Found no words and optional branch => SUCCESS on augmenting (%03x %03x\n", major, minor); - return 1; + if (inBracket && ret == 0) { + scidprintf("%*sscanParse changing ret to 1 due to brackets\n", + outputDepth, ""); + ret = 1; } - scidprintf("FAILURE on augmenting (%03x %03x\n", major, minor); - return 0; + scidprintf("%*sscanParse returning %d\n", outputDepth, "", ret); + outputDepth--; + + return ret; } -static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *saidt) { - int augment_basepos = 0; - int parse_basepos; - int major, minor; - int dontclaim = 0; - parse_basepos = aug_get_base_node(parset); - if (!parse_basepos) { - warning("augment_parse_nodes(): Parse tree is corrupt"); - return 0; - } - augment_basepos = aug_get_base_node(saidt); - if (!augment_basepos) { - warning("augment_parse_nodes(): Said tree is corrupt"); - return 0; - } +static int augment_parse_nodes(ParseTreeNode *parseT, ParseTreeNode *saidT) { + outputDepth = 0; + scidprintf("augment_parse_nodes on "); + node_print_desc(parseT); + scidprintf(" and "); + node_print_desc(saidT); + scidprintf("\n"); - while ((augment_basepos = aug_get_next_sibling(saidt, augment_basepos, &major, &minor))) { - if ((major == 0x14b) && (minor == SAID_LONG(SAID_GT))) - dontclaim = 1; // special case - else // normal sentence part - if (!(augment_sentence_part(saidt, augment_basepos, parset, parse_basepos, major, minor))) { - scidprintf("Returning failure\n"); - return 0; // fail - } - } + dontclaim = false; + + int ret = matchTrees(parseT, saidT); + + scidprintf("matchTrees returned %d\n", ret); - scidprintf("Returning success with dontclaim=%d\n", dontclaim); + if (ret != 1) + return 0; if (dontclaim) return SAID_PARTIAL_MATCH; - else - return 1; // full match + + return 1; } @@ -2441,22 +996,19 @@ static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *sai /**** Main code ****/ /*******************/ -int said(EngineState *s, byte *spec, bool verbose) { +int said(EngineState *s, const byte *spec, bool verbose) { int retval; Vocabulary *voc = g_sci->getVocabulary(); - parse_tree_node_t *parse_tree_ptr = voc->_parserNodes; + ParseTreeNode *parse_tree_ptr = voc->_parserNodes; if (voc->parserIsValid) { - if (said_parse_spec(spec)) { - printf("Offending spec was: "); - voc->decipherSaidBlock(spec); + if (said_parse_spec(spec)) return SAID_NO_MATCH; - } if (verbose) - vocab_dump_parse_tree("Said-tree", said_tree); // Nothing better to do yet - retval = augment_parse_nodes(parse_tree_ptr, &(said_tree[0])); + vocab_dump_parse_tree("Said-tree", said_tree); + retval = augment_parse_nodes(parse_tree_ptr, said_tree); if (!retval) return SAID_NO_MATCH; @@ -2470,15 +1022,108 @@ int said(EngineState *s, byte *spec, bool verbose) { } -#ifdef SAID_DEBUG_PROGRAM -int main (int argc, char *argv) { - byte block[] = {0x01, 0x00, 0xf8, 0xf5, 0x02, 0x01, 0xf6, 0xf2, 0x02, 0x01, 0xf2, 0x01, 0x03, 0xff}; - EngineState s; +/* + +Some test expressions for in the ScummVM debugging console, using +Codename: ICEMAN's vocabulary: + + + +said green board & [!*] / 8af < 1f6 +True + +said get green board & [!*] / 8af < 1f6 +False + +said green board & [!*] / 8af [< 1f6 ] +True + +said climb up & 19b , 426 [< 142 ] [/ 81e ] +True + +said climb up ladder & 19b , 426 [< 142 ] [/ 81e ] +True + +said climb down & 19b , 426 [< 142 ] [/ 81e ] +False + +said climb up tree & 19b , 426 [< 142 ] [/ 81e ] +False + +said climb up & 19b , 446 , 426 [< 143 ] [/ 81e ] +False + +said climb down & 19b , 446 , 426 [< 143 ] [/ 81e ] +True + +said use green device & 1a5 / 8c1 [< 21d ] +False + +said use electronic device & 1a5 / 8c1 [< 21d ] +True + +said use device & 1a5 / 8c1 [< 21d ] +True + +said eat & 429 [/ !* ] +True + +said eat ladder & 429 [/ !* ] +False + +said look at the ladder & 3f8 / 81e [< !* ] +True + +said look at the green ladder & 3f8 / 81e [< !* ] +False + +said look green book & / 7f6 [< 8d2 ] +False + +said look green book & 3f8 [< ca ] +True + +said get a blue board for the green ladder & 3f9 / 8af [ < 1f6 ] / 81e < 1f6 +False + +said get a board for the green ladder & 3f9 / 8af [ < 1f6 ] / 81e < 1f6 +True + +said get a blue board & 3f9 / 8af [ < 1f6 ] +False + +said get up & ( 3f8 , 3f9 ) [ < ( 142 , 143 ) ] +True + +said get left & ( 3f8 , 3f9 ) [ < ( 142 , 143 ) ] +False + +said look down & ( 3f8 , 3f9 ) [ < ( 142 , 143 ) ] +True + +said get & ( 3f8 , 3f9 ) [ < ( 142 , 143 ) ] +True + +said put washer on shaft & 455 , ( 3fa < cb ) / 8c6 +True + +said depth correct & [!*] < 8b1 / 22 +True + +said depth acknowledged & / 46d , 460 , 44d < 8b1 +True + +said depth confirmed & / 46d , 460 , 44d < 8b1 +True + +said depth attained & / 46d , 460 , 44d < 8b1 +True + + +*/ + + - s.parser_valid = 1; - said(&s, block); -} -#endif } // End of namespace Sci diff --git a/engines/sci/parser/said.y b/engines/sci/parser/said.y deleted file mode 100644 index cbb2ff3e62..0000000000 --- a/engines/sci/parser/said.y +++ /dev/null @@ -1,839 +0,0 @@ -%{ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * 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 "sci/engine/state.h" - - -// Bison generates an empty switch statement that gives a warning in MSVC. -// This disables that warning. -#ifdef _MSC_VER -#pragma warning(disable:4065) -#endif - - -namespace Sci { - -#define SAID_BRANCH_NULL 0 - -#define MAX_SAID_TOKENS 128 - -// Maximum number of words to be expected in a parsed sentence -#define AUGMENT_MAX_WORDS 64 - - -#define ANYWORD 0xfff - -#define WORD_TYPE_BASE 0x141 -#define WORD_TYPE_REF 0x144 -#define WORD_TYPE_SYNTACTIC_SUGAR 0x145 - -#define AUGMENT_SENTENCE_PART_BRACKETS 0x152 - -// Minor numbers -#define AUGMENT_SENTENCE_MINOR_MATCH_PHRASE 0x14c -#define AUGMENT_SENTENCE_MINOR_MATCH_WORD 0x153 -#define AUGMENT_SENTENCE_MINOR_RECURSE 0x144 -#define AUGMENT_SENTENCE_MINOR_PARENTHESES 0x14f - - -#undef YYDEBUG /*1*/ -//#define SAID_DEBUG*/ -//#define SCI_DEBUG_PARSE_TREE_AUGMENTATION // uncomment to debug parse tree augmentation - - -#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION -#define scidprintf printf -#else -void print_nothing(...) { } -#define scidprintf print_nothing -#endif - - -static char *said_parse_error; - -static int said_token; -static int said_tokens_nr; -static int said_tokens[MAX_SAID_TOKENS]; -static int said_blessed; // increminated by said_top_branch - -static int said_tree_pos; // Set to 0 if we're out of space -#define SAID_TREE_START 4; // Reserve space for the 4 top nodes - -#define VALUE_IGNORE -424242 - -static parse_tree_node_t said_tree[VOCAB_TREE_NODES]; - -typedef int wgroup_t; -typedef int tree_t; -typedef int said_spec_t; - -static tree_t said_aug_branch(int, int, tree_t, tree_t); -static tree_t said_attach_branch(tree_t, tree_t); -/* -static tree_t said_wgroup_branch(wgroup_t); -*/ -static said_spec_t said_top_branch(tree_t); -static tree_t said_paren(tree_t, tree_t); -static tree_t said_value(int, tree_t); -static tree_t said_terminal(int); - -static int yylex(); - -static int yyerror(const char *s) { - said_parse_error = strdup(s); - return 1; /* Abort */ -} - -%} - -%token WGROUP /* Word group */ -%token YY_COMMA /* 0xf0 */ -%token YY_AMP /* 0xf1 */ -%token YY_SLASH /* 0xf2 */ -%token YY_PARENO /* 0xf3 */ -%token YY_PARENC /* 0xf4 */ -%token YY_BRACKETSO /* 0xf5 */ -%token YY_BRACKETSC /* 0xf6 */ -%token YY_HASH /* 0xf7 */ -%token YY_LT /* 0xf8 */ -%token YY_GT /* 0xf9 */ -%token YY_BRACKETSO_LT /* special token used to imitate LR(2) behaviour */ -%token YY_BRACKETSO_SLASH /* special token used to imitate LR(2) behaviour */ -%token YY_LT_BRACKETSO /* special token used to imitate LR(2) behaviour */ -%token YY_LT_PARENO /* special token used to imitate LR(2) behaviour */ - -%% - -saidspec : leftspec optcont - { $$ = said_top_branch(said_attach_branch($1, $2)); } - | leftspec midspec optcont - { $$ = said_top_branch(said_attach_branch($1, said_attach_branch($2, $3))); } - | leftspec midspec rightspec optcont - { $$ = said_top_branch(said_attach_branch($1, said_attach_branch($2, said_attach_branch($3, $4)))); } - ; - - -optcont : /* empty */ - { $$ = SAID_BRANCH_NULL; } - | YY_GT - { $$ = said_paren(said_value(0x14b, said_value(0xf900, said_terminal(0xf900))), SAID_BRANCH_NULL); } - ; - - - -leftspec : /* empty */ - { $$ = SAID_BRANCH_NULL; } - | expr - { $$ = said_paren(said_value(0x141, said_value(0x149, $1)), SAID_BRANCH_NULL); } - ; - - - -midspec : YY_SLASH expr - { $$ = said_aug_branch(0x142, 0x14a, $2, SAID_BRANCH_NULL); } - | YY_BRACKETSO_SLASH YY_SLASH expr YY_BRACKETSC - { $$ = said_aug_branch(0x152, 0x142, said_aug_branch(0x142, 0x14a, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); } - | YY_SLASH - { $$ = SAID_BRANCH_NULL; } - ; - - - -rightspec : YY_SLASH expr - { $$ = said_aug_branch(0x143, 0x14a, $2, SAID_BRANCH_NULL); } - | YY_BRACKETSO_SLASH YY_SLASH expr YY_BRACKETSC - { $$ = said_aug_branch(0x152, 0x143, said_aug_branch(0x143, 0x14a, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); } - | YY_SLASH - { $$ = SAID_BRANCH_NULL; } - ; - - -word : WGROUP - { $$ = said_paren(said_value(0x141, said_value(0x153, said_terminal($1))), SAID_BRANCH_NULL); } - ; - - -cwordset : wordset - { $$ = said_aug_branch(0x141, 0x14f, $1, SAID_BRANCH_NULL); } - | YY_BRACKETSO wordset YY_BRACKETSC - { $$ = said_aug_branch(0x141, 0x14f, said_aug_branch(0x152, 0x14c, said_aug_branch(0x141, 0x14f, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL), SAID_BRANCH_NULL); } - ; - - -wordset : word - { $$ = $1; } - | YY_PARENO expr YY_PARENC - { $$ = said_aug_branch(0x141, 0x14c, $2, SAID_BRANCH_NULL); } - | wordset YY_COMMA wordset - { $$ = said_attach_branch($1, $3); } - | wordset YY_BRACKETSO_LT wordrefset YY_BRACKETSC - { $$ = said_attach_branch($1, $3); } - | wordset YY_COMMA YY_BRACKETSO wordset YY_BRACKETSC - { $$ = said_attach_branch($1, $3); } - ; - - -expr : cwordset cwordrefset - { $$ = said_attach_branch($1, $2); } - | cwordset - { $$ = $1; } - | cwordrefset - { $$ = $1; } - ; - - -cwordrefset : wordrefset - { $$ = $1; } - | YY_BRACKETSO_LT wordrefset YY_BRACKETSC - { $$ = said_aug_branch(0x152, 0x144, $2, SAID_BRANCH_NULL); } - | wordrefset YY_BRACKETSO_LT wordrefset YY_BRACKETSC - { $$ = said_attach_branch($1, said_aug_branch(0x152, 0x144, $3, SAID_BRANCH_NULL)); } - ; - - -wordrefset : YY_LT word recref - { $$ = said_aug_branch(0x144, 0x14f, $2, $3); } - | YY_LT_PARENO YY_PARENO expr YY_PARENC - { $$ = said_aug_branch(0x144, 0x14f, said_aug_branch(0x141, 0x144, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL); } - | YY_LT wordset - { $$ = said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL); } - | YY_LT_BRACKETSO YY_BRACKETSO wordset YY_BRACKETSC - { $$ = said_aug_branch(0x152, 0x144, said_aug_branch(0x144, 0x14f, $3, SAID_BRANCH_NULL), SAID_BRANCH_NULL); } - ; - - -recref : YY_LT wordset recref - { $$ = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL), $3); } - | YY_LT wordset - { $$ = said_aug_branch(0x141, 0x144, said_aug_branch(0x144, 0x14f, $2, SAID_BRANCH_NULL), SAID_BRANCH_NULL); } - | YY_LT_PARENO YY_PARENO expr YY_PARENC - { $$ = said_aug_branch(0x141, 0x14c, $2, SAID_BRANCH_NULL); } - ; - -%% - -int parse_yy_token_lookup[] = {YY_COMMA, YY_AMP, YY_SLASH, YY_PARENO, YY_PARENC, YY_BRACKETSO, YY_BRACKETSC, YY_HASH, YY_LT, YY_GT}; - -static int yylex() { - int retval = said_tokens[said_token++]; - - if (retval < SAID_LONG(SAID_FIRST)) { - yylval = retval; - retval = WGROUP; - } else { - retval >>= 8; - - if (retval == SAID_TERM) - retval = 0; - else { - assert(retval >= SAID_FIRST); - retval = parse_yy_token_lookup[retval - SAID_FIRST]; - if (retval == YY_BRACKETSO) { - if ((said_tokens[said_token] >> 8) == SAID_LT) - retval = YY_BRACKETSO_LT; - else - if ((said_tokens[said_token] >> 8) == SAID_SLASH) - retval = YY_BRACKETSO_SLASH; - } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_BRACKO) { - retval = YY_LT_BRACKETSO; - } else if (retval == YY_LT && (said_tokens[said_token] >> 8) == SAID_PARENO) { - retval = YY_LT_PARENO; - } - } - } - - return retval; -} - -static int said_next_node() { - return ((said_tree_pos == 0) || (said_tree_pos >= VOCAB_TREE_NODES)) ? said_tree_pos = 0 : said_tree_pos++; -} - -#define SAID_NEXT_NODE said_next_node() - -static int said_leaf_node(tree_t pos, int value) { - said_tree[pos].type = kParseTreeLeafNode; - - if (value != VALUE_IGNORE) - said_tree[pos].content.value = value; - - return pos; -} - -static int said_branch_node(tree_t pos, int left, int right) { - said_tree[pos].type = kParseTreeBranchNode; - - if (left != VALUE_IGNORE) - said_tree[pos].content.branches[0] = left; - - if (right != VALUE_IGNORE) - said_tree[pos].content.branches[1] = right; - - return pos; -} - -static tree_t said_paren(tree_t t1, tree_t t2) { - if (t1) - return said_branch_node(SAID_NEXT_NODE, t1, t2); - else - return t2; -} - -static tree_t said_value(int val, tree_t t) { - return said_branch_node(SAID_NEXT_NODE, said_leaf_node(SAID_NEXT_NODE, val), t); - -} - -static tree_t said_terminal(int val) { - return said_leaf_node(SAID_NEXT_NODE, val); -} - -static tree_t said_aug_branch(int n1, int n2, tree_t t1, tree_t t2) { - int retval; - - retval = said_branch_node(SAID_NEXT_NODE, - said_branch_node(SAID_NEXT_NODE, - said_leaf_node(SAID_NEXT_NODE, n1), - said_branch_node(SAID_NEXT_NODE, - said_leaf_node(SAID_NEXT_NODE, n2), - t1) - ), - t2); - -#ifdef SAID_DEBUG - fprintf(stderr, "AUG(0x%x, 0x%x, [%04x], [%04x]) = [%04x]\n", n1, n2, t1, t2, retval); -#endif - - return retval; -} - -static tree_t said_attach_branch(tree_t base, tree_t attacheant) { -#ifdef SAID_DEBUG - fprintf(stderr, "ATT2([%04x], [%04x]) = [%04x]\n", base, attacheant, base); -#endif - - if (!attacheant) - return base; - if (!base) - return attacheant; - - if (!base) - return 0; // Happens if we're out of space - - said_branch_node(base, VALUE_IGNORE, attacheant); - - return base; -} - -static said_spec_t said_top_branch(tree_t first) { -#ifdef SAID_DEBUG - fprintf(stderr, "TOP([%04x])\n", first); -#endif - said_branch_node(0, 1, 2); - said_leaf_node(1, 0x141); // Magic number #1 - said_branch_node(2, 3, first); - said_leaf_node(3, 0x13f); // Magic number #2 - - ++said_blessed; - - return 0; -} - -static int said_parse_spec(byte *spec) { - int nextitem; - - said_parse_error = NULL; - said_token = 0; - said_tokens_nr = 0; - said_blessed = 0; - - said_tree_pos = SAID_TREE_START; - - do { - nextitem = *spec++; - if (nextitem < SAID_FIRST) - said_tokens[said_tokens_nr++] = nextitem << 8 | *spec++; - else - said_tokens[said_tokens_nr++] = SAID_LONG(nextitem); - - } while ((nextitem != SAID_TERM) && (said_tokens_nr < MAX_SAID_TOKENS)); - - if (nextitem == SAID_TERM) - yyparse(); - else { - warning("SAID spec is too long"); - return 1; - } - - if (said_parse_error) { - warning("Error while parsing SAID spec: %s", said_parse_error); - free(said_parse_error); - return 1; - } - - if (said_tree_pos == 0) { - warning("Out of tree space while parsing SAID spec"); - return 1; - } - - if (said_blessed != 1) { - warning("Found multiple top branches"); - return 1; - } - - return 0; -} - -/**********************/ -/**** Augmentation ****/ -/**********************/ - -// primitive functions - -#define AUG_READ_BRANCH(a, br, p) \ - if (tree[p].type != kParseTreeBranchNode) \ - return 0; \ - a = tree[p].content.branches[br]; - -#define AUG_READ_VALUE(a, p) \ - if (tree[p].type != kParseTreeLeafNode) \ - return 0; \ - a = tree[p].content.value; - -#define AUG_ASSERT(i) \ - if (!i) return 0; - -static int aug_get_next_sibling(parse_tree_node_t *tree, int pos, int *first, int *second) { - // Returns the next sibling relative to the specified position in 'tree', - // sets *first and *second to its augment node values, returns the new position - // or 0 if there was no next sibling - int seek, valpos; - - AUG_READ_BRANCH(pos, 1, pos); - AUG_ASSERT(pos); - AUG_READ_BRANCH(seek, 0, pos); - AUG_ASSERT(seek); - - // Now retrieve first value - AUG_READ_BRANCH(valpos, 0, seek); - AUG_ASSERT(valpos); - AUG_READ_VALUE(*first, valpos); - - // Get second value - AUG_READ_BRANCH(seek, 1, seek); - AUG_ASSERT(seek); - AUG_READ_BRANCH(valpos, 0, seek); - AUG_ASSERT(valpos); - AUG_READ_VALUE(*second, valpos); - - return pos; -} - -static int aug_get_wgroup(parse_tree_node_t *tree, int pos) { - // Returns 0 if pos in tree is not the root of a 3-element list, otherwise - // it returns the last element (which, in practice, is the word group - int val; - - AUG_READ_BRANCH(pos, 0, pos); - AUG_ASSERT(pos); - AUG_READ_BRANCH(pos, 1, pos); - AUG_ASSERT(pos); - AUG_READ_BRANCH(pos, 1, pos); - AUG_ASSERT(pos); - AUG_READ_VALUE(val, pos); - - return val; -} - -static int aug_get_base_node(parse_tree_node_t *tree) { - int startpos = 0; - AUG_READ_BRANCH(startpos, 1, startpos); - - return startpos; -} - -// semi-primitive functions - -static int aug_get_first_child(parse_tree_node_t *tree, int pos, int *first, int *second) { - // like aug_get_next_sibling, except that it recurses into the tree and - // finds the first child (usually *not* Ayanami Rei) of the current branch - // rather than its next sibling. - AUG_READ_BRANCH(pos, 0, pos); - AUG_ASSERT(pos); - AUG_READ_BRANCH(pos, 1, pos); - AUG_ASSERT(pos); - - return aug_get_next_sibling(tree, pos, first, second); -} - -static void aug_find_words_recursively(parse_tree_node_t *tree, int startpos, int *base_words, int *base_words_nr, - int *ref_words, int *ref_words_nr, int maxwords, int refbranch) { - // Finds and lists all base (141) and reference (144) words */ - int major, minor; - int word; - int pos = aug_get_first_child(tree, startpos, &major, &minor); - - //if (major == WORD_TYPE_REF) - // refbranch = 1; - - while (pos) { - if ((word = aug_get_wgroup(tree, pos))) { // found a word - if (!refbranch && major == WORD_TYPE_BASE) { - if ((*base_words_nr) == maxwords) { - warning("Out of regular words"); - return; // return gracefully - } - - base_words[*base_words_nr] = word; // register word - ++(*base_words_nr); - - } - if (major == WORD_TYPE_REF || refbranch) { - if ((*ref_words_nr) == maxwords) { - warning("Out of reference words"); - return; // return gracefully - } - - ref_words[*ref_words_nr] = word; // register word - ++(*ref_words_nr); - - } - if (major != WORD_TYPE_SYNTACTIC_SUGAR && major != WORD_TYPE_BASE && major != WORD_TYPE_REF) - warning("aug_find_words_recursively(): Unknown word type %03x", major); - - } else // Did NOT find a word group: Attempt to recurse - aug_find_words_recursively(tree, pos, base_words, base_words_nr, - ref_words, ref_words_nr, maxwords, refbranch || major == WORD_TYPE_REF); - - pos = aug_get_next_sibling(tree, pos, &major, &minor); - } -} - - -static void aug_find_words(parse_tree_node_t *tree, int startpos, int *base_words, int *base_words_nr, - int *ref_words, int *ref_words_nr, int maxwords) { - // initializing wrapper for aug_find_words_recursively() - *base_words_nr = 0; - *ref_words_nr = 0; - - aug_find_words_recursively(tree, startpos, base_words, base_words_nr, ref_words, ref_words_nr, maxwords, 0); -} - - -static int aug_contains_word(int *list, int length, int word) { - int i; - - if (word == ANYWORD) - return (length); - - for (i = 0; i < length; i++) - if (list[i] == word) - return 1; - - return 0; -} - - -static int augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset, - int parse_branch, int major, int minor, int *base_words, int base_words_nr, - int *ref_words, int ref_words_nr); - -static int augment_match_expression_p(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset, - int parse_basepos, int major, int minor, - int *base_words, int base_words_nr, int *ref_words, int ref_words_nr) { - int cmajor, cminor, cpos; - cpos = aug_get_first_child(saidt, augment_pos, &cmajor, &cminor); - if (!cpos) { - warning("augment_match_expression_p(): Empty condition"); - return 1; - } - - scidprintf("Attempting to match (%03x %03x (%03x %03x\n", major, minor, cmajor, cminor); - - if ((major == WORD_TYPE_BASE) && (minor == AUGMENT_SENTENCE_MINOR_RECURSE)) - return augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor, - base_words, base_words_nr, ref_words, ref_words_nr); - - switch (major) { - - case WORD_TYPE_BASE: - while (cpos) { - if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) { - int word = aug_get_wgroup(saidt, cpos); - scidprintf("Looking for word %03x\n", word); - - if (aug_contains_word(base_words, base_words_nr, word)) - return 1; - } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) { - if (augment_sentence_expression(saidt, cpos, parset, parse_basepos, cmajor, cminor, - base_words, base_words_nr, ref_words, ref_words_nr)) - return 1; - } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) { - int gc_major, gc_minor; - int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor); - - while (gchild) { - if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, major, - minor, base_words, base_words_nr, - ref_words, ref_words_nr)) - return 1; - gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor); - } - } else - warning("augment_match_expression_p(): Unknown type 141 minor number %3x", cminor); - - cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor); - - } - break; - - case WORD_TYPE_REF: - while (cpos) { - if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_WORD) { - int word = aug_get_wgroup(saidt, cpos); - scidprintf("Looking for refword %03x\n", word); - - if (aug_contains_word(ref_words, ref_words_nr, word)) - return 1; - } else if (cminor == AUGMENT_SENTENCE_MINOR_MATCH_PHRASE) { - if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor, - base_words, base_words_nr, ref_words, ref_words_nr)) - return 1; - } else if (cminor == AUGMENT_SENTENCE_MINOR_PARENTHESES) { - int gc_major, gc_minor; - int gchild = aug_get_first_child(saidt, cpos, &gc_major, &gc_minor); - - while (gchild) { - if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, major, - minor, base_words, base_words_nr, - ref_words, ref_words_nr)) - return 1; - gchild = aug_get_next_sibling(saidt, gchild, &gc_major, &gc_minor); - } - } else - warning("augment_match_expression_p(): Unknown type 144 minor number %3x", cminor); - - cpos = aug_get_next_sibling(saidt, cpos, &cmajor, &cminor); - - } - break; - - case AUGMENT_SENTENCE_PART_BRACKETS: - if (augment_match_expression_p(saidt, cpos, parset, parse_basepos, cmajor, cminor, - base_words, base_words_nr, ref_words, ref_words_nr)) - return 1; - - scidprintf("Didn't match subexpression; checking sub-bracked predicate %03x\n", cmajor); - - switch (cmajor) { - case WORD_TYPE_BASE: - if (!base_words_nr) - return 1; - break; - - case WORD_TYPE_REF: - if (!ref_words_nr) - return 1; - break; - - default: - warning("augment_match_expression_p(): (subp1) Unkonwn sub-bracket predicate %03x", cmajor); - } - - break; - - default: - warning("augment_match_expression_p(): Unknown predicate %03x", major); - - } - - scidprintf("augment_match_expression_p(): Generic failure\n"); - - return 0; -} - -static int augment_sentence_expression(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset, - int parse_branch, int major, int minor, int *base_words, int base_words_nr, - int *ref_words, int ref_words_nr) { - int check_major, check_minor; - int check_pos = aug_get_first_child(saidt, augment_pos, &check_major, &check_minor); - do { - if (!(augment_match_expression_p(saidt, check_pos, parset, parse_branch, check_major, check_minor, - base_words, base_words_nr, ref_words, ref_words_nr))) - return 0; - } while ((check_pos = aug_get_next_sibling(saidt, check_pos, &check_major, &check_minor))); - - return 1; -} - -static int augment_sentence_part(parse_tree_node_t *saidt, int augment_pos, parse_tree_node_t *parset, int parse_basepos, int major, int minor) { - int pmajor, pminor; - int parse_branch = parse_basepos; - int optional = 0; - int foundwords = 0; - - scidprintf("Augmenting (%03x %03x\n", major, minor); - - if (major == AUGMENT_SENTENCE_PART_BRACKETS) { // '[/ foo]' is true if '/foo' or if there - // exists no x for which '/x' is true - if ((augment_pos = aug_get_first_child(saidt, augment_pos, &major, &minor))) { - scidprintf("Optional part: Now augmenting (%03x %03x\n", major, minor); - optional = 1; - } else { - scidprintf("Matched empty optional expression\n"); - return 1; - } - } - - if ((major < 0x141) || (major > 0x143)) { - scidprintf("augment_sentence_part(): Unexpected sentence part major number %03x\n", major); - return 0; - } - - while ((parse_branch = aug_get_next_sibling(parset, parse_branch, &pmajor, &pminor))) { - if (pmajor == major) { // found matching sentence part - int success; - int base_words_nr; - int ref_words_nr; - int base_words[AUGMENT_MAX_WORDS]; - int ref_words[AUGMENT_MAX_WORDS]; -#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION - int i; -#endif - - scidprintf("Found match with pminor = %03x\n", pminor); - aug_find_words(parset, parse_branch, base_words, &base_words_nr, ref_words, &ref_words_nr, AUGMENT_MAX_WORDS); - foundwords |= (ref_words_nr | base_words_nr); -#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION - printf("%d base words:", base_words_nr); - for (i = 0; i < base_words_nr; i++) - printf(" %03x", base_words[i]); - printf("\n%d reference words:", ref_words_nr); - for (i = 0; i < ref_words_nr; i++) - printf(" %03x", ref_words[i]); - printf("\n"); -#endif - - success = augment_sentence_expression(saidt, augment_pos, parset, parse_basepos, major, minor, - base_words, base_words_nr, ref_words, ref_words_nr); - - if (success) { - scidprintf("SUCCESS on augmenting (%03x %03x\n", major, minor); - return 1; - } - } - } - - if (optional && (foundwords == 0)) { - scidprintf("Found no words and optional branch => SUCCESS on augmenting (%03x %03x\n", major, minor); - return 1; - } - scidprintf("FAILURE on augmenting (%03x %03x\n", major, minor); - - return 0; -} - -static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *saidt) { - int augment_basepos = 0; - int parse_basepos; - int major, minor; - int dontclaim = 0; - - parse_basepos = aug_get_base_node(parset); - if (!parse_basepos) { - warning("augment_parse_nodes(): Parse tree is corrupt"); - return 0; - } - - augment_basepos = aug_get_base_node(saidt); - if (!augment_basepos) { - warning("augment_parse_nodes(): Said tree is corrupt"); - return 0; - } - - while ((augment_basepos = aug_get_next_sibling(saidt, augment_basepos, &major, &minor))) { - if ((major == 0x14b) && (minor == SAID_LONG(SAID_GT))) - dontclaim = 1; // special case - else // normal sentence part - if (!(augment_sentence_part(saidt, augment_basepos, parset, parse_basepos, major, minor))) { - scidprintf("Returning failure\n"); - return 0; // fail - } - } - - scidprintf("Returning success with dontclaim=%d\n", dontclaim); - - if (dontclaim) - return SAID_PARTIAL_MATCH; - else - return 1; // full match -} - - -/*******************/ -/**** Main code ****/ -/*******************/ - -int said(EngineState *s, byte *spec, bool verbose) { - int retval; - Vocabulary *voc = g_sci->getVocabulary(); - - parse_tree_node_t *parse_tree_ptr = voc->_parserNodes; - - if (voc->parserIsValid) { - if (said_parse_spec(spec)) { - printf("Offending spec was: "); - voc->decipherSaidBlock(spec); - return SAID_NO_MATCH; - } - - if (verbose) - vocab_dump_parse_tree("Said-tree", said_tree); // Nothing better to do yet - retval = augment_parse_nodes(parse_tree_ptr, &(said_tree[0])); - - if (!retval) - return SAID_NO_MATCH; - else if (retval != SAID_PARTIAL_MATCH) - return SAID_FULL_MATCH; - else - return SAID_PARTIAL_MATCH; - } - - return SAID_NO_MATCH; -} - - -#ifdef SAID_DEBUG_PROGRAM -int main (int argc, char *argv) { - byte block[] = {0x01, 0x00, 0xf8, 0xf5, 0x02, 0x01, 0xf6, 0xf2, 0x02, 0x01, 0xf2, 0x01, 0x03, 0xff}; - EngineState s; - - s.parser_valid = 1; - said(&s, block); -} -#endif - -} // End of namespace Sci diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp index e48a9cdfda..20436d5b30 100644 --- a/engines/sci/parser/vocabulary.cpp +++ b/engines/sci/parser/vocabulary.cpp @@ -33,18 +33,34 @@ namespace Sci { -Vocabulary::Vocabulary(ResourceManager *resMan) : _resMan(resMan) { +Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan), _foreign(foreign) { _parserRules = NULL; - _vocabVersion = kVocabularySCI0; memset(_parserNodes, 0, sizeof(_parserNodes)); // Mark parse tree as unused _parserNodes[0].type = kParseTreeLeafNode; - _parserNodes[0].content.value = 0; + _parserNodes[0].value = 0; _synonyms.clear(); // No synonyms debug(2, "Initializing vocabulary"); + if (_resMan->testResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_MAIN_VOCAB))) { + _vocabVersion = kVocabularySCI0; + _resourceIdWords = VOCAB_RESOURCE_SCI0_MAIN_VOCAB; + _resourceIdSuffixes = VOCAB_RESOURCE_SCI0_SUFFIX_VOCAB; + _resourceIdBranches = VOCAB_RESOURCE_SCI0_PARSE_TREE_BRANCHES; + } else { + _vocabVersion = kVocabularySCI1; + _resourceIdWords = VOCAB_RESOURCE_SCI1_MAIN_VOCAB; + _resourceIdSuffixes = VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB; + _resourceIdBranches = VOCAB_RESOURCE_SCI1_PARSE_TREE_BRANCHES; + } + + if (_foreign) { + _resourceIdWords += 10; + _resourceIdSuffixes += 10; + _resourceIdBranches += 10; + } if (getSciVersion() <= SCI_VERSION_1_EGA && loadParserWords()) { loadSuffixes(); @@ -66,27 +82,46 @@ Vocabulary::~Vocabulary() { freeSuffixes(); } -bool Vocabulary::loadParserWords() { +void Vocabulary::reset() { + parserIsValid = false; // Invalidate parser + parser_event = NULL_REG; // Invalidate parser event + parser_base = make_reg(g_sci->getEngineState()->_segMan->getSysStringsSegment(), SYS_STRING_PARSER_BASE); +} - char currentword[256] = ""; // They're not going to use words longer than 255 ;-) - int currentwordpos = 0; +bool Vocabulary::loadParserWords() { + char currentWord[VOCAB_MAX_WORDLENGTH] = ""; + int currentWordPos = 0; // First try to load the SCI0 vocab resource. - Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_MAIN_VOCAB), 0); + Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdWords), 0); if (!resource) { - warning("SCI0: Could not find a main vocabulary, trying SCI01"); - resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_MAIN_VOCAB), 0); - _vocabVersion = kVocabularySCI1; + warning("Could not find a main vocabulary"); + return false; // NOT critical: SCI1 games and some demos don't have one! } - if (!resource) { - warning("SCI1: Could not find a main vocabulary"); - return false; // NOT critical: SCI1 games and some demos don't have one! + VocabularyVersions resourceType = _vocabVersion; + + if (resourceType == kVocabularySCI0) { + if (resource->size < 26 * 2) { + warning("Invalid main vocabulary encountered: Much too small"); + return false; + } + // Check the alphabet-offset table for any content + int alphabetNr; + for (alphabetNr = 0; alphabetNr < 26; alphabetNr++) { + if (READ_LE_UINT16(resource->data + alphabetNr * 2)) + break; + } + // If all of them were empty, we are definitely seeing SCI01 vocab in disguise (e.g. pq2 japanese) + if (alphabetNr == 26) { + warning("SCI0: Found SCI01 vocabulary in disguise"); + resourceType = kVocabularySCI1; + } } unsigned int seeker; - if (_vocabVersion == kVocabularySCI1) + if (resourceType == kVocabularySCI1) seeker = 255 * 2; // vocab.900 starts with 255 16-bit pointers which we don't use else seeker = 26 * 2; // vocab.000 starts with 26 16-bit pointers which we don't use @@ -102,13 +137,13 @@ bool Vocabulary::loadParserWords() { while (seeker < resource->size) { byte c; - currentwordpos = resource->data[seeker++]; // Parts of previous words may be re-used + currentWordPos = resource->data[seeker++]; // Parts of previous words may be re-used - if (_vocabVersion == kVocabularySCI1) { + if (resourceType == kVocabularySCI1) { c = 1; - while (seeker < resource->size && currentwordpos < 255 && c) { + while (seeker < resource->size && currentWordPos < 255 && c) { c = resource->data[seeker++]; - currentword[currentwordpos++] = c; + currentWord[currentWordPos++] = c; } if (seeker == resource->size) { warning("SCI1: Vocabulary not usable, disabling"); @@ -118,11 +153,11 @@ bool Vocabulary::loadParserWords() { } else { do { c = resource->data[seeker++]; - currentword[currentwordpos++] = c & 0x7f; // 0x80 is used to terminate the string + currentWord[currentWordPos++] = c & 0x7f; // 0x80 is used to terminate the string } while (c < 0x80); } - currentword[currentwordpos] = 0; + currentWord[currentWordPos] = 0; // Now decode class and group: c = resource->data[seeker + 1]; @@ -131,7 +166,7 @@ bool Vocabulary::loadParserWords() { newWord._group = (resource->data[seeker + 2]) | ((c & 0x0f) << 8); // Add the word to the list - _parserWords[currentword] = newWord; + _parserWords[currentWord] = newWord; seeker += 3; } @@ -142,23 +177,20 @@ bool Vocabulary::loadParserWords() { const char *Vocabulary::getAnyWordFromGroup(int group) { if (group == VOCAB_MAGIC_NUMBER_GROUP) return "{number}"; + if (group == VOCAB_MAGIC_NOTHING_GROUP) + return "{nothing}"; - for (WordMap::const_iterator i = _parserWords.begin(); i != _parserWords.end(); ++i) + for (WordMap::const_iterator i = _parserWords.begin(); i != _parserWords.end(); ++i) { if (i->_value._group == group) return i->_key.c_str(); + } return "{invalid}"; } bool Vocabulary::loadSuffixes() { // Determine if we can find a SCI1 suffix vocabulary first - Resource* resource = NULL; - - if (_vocabVersion == kVocabularySCI0) - resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_SUFFIX_VOCAB), 1); - else - resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB), 1); - + Resource* resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdSuffixes), 1); if (!resource) return false; // No vocabulary found @@ -171,7 +203,7 @@ bool Vocabulary::loadSuffixes() { suffix.alt_suffix_length = strlen(suffix.alt_suffix); seeker += suffix.alt_suffix_length + 1; // Hit end of string - suffix.class_mask = (int16)READ_BE_UINT16(resource->data + seeker); + suffix.result_class = (int16)READ_BE_UINT16(resource->data + seeker); seeker += 2; // Beginning of next string - skip leading '*' @@ -181,7 +213,7 @@ bool Vocabulary::loadSuffixes() { suffix.word_suffix_length = strlen(suffix.word_suffix); seeker += suffix.word_suffix_length + 1; - suffix.result_class = (int16)READ_BE_UINT16(resource->data + seeker); + suffix.class_mask = (int16)READ_BE_UINT16(resource->data + seeker); seeker += 3; // Next entry _parserSuffixes.push_back(suffix); @@ -191,13 +223,7 @@ bool Vocabulary::loadSuffixes() { } void Vocabulary::freeSuffixes() { - Resource* resource = NULL; - - if (_vocabVersion == kVocabularySCI0) - resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_SUFFIX_VOCAB), 0); - else - resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB), 0); - + Resource* resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdSuffixes), 0); if (resource) _resMan->unlockResource(resource); @@ -205,12 +231,7 @@ void Vocabulary::freeSuffixes() { } bool Vocabulary::loadBranches() { - Resource *resource = NULL; - - if (_vocabVersion == kVocabularySCI0) - resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_PARSE_TREE_BRANCHES), 0); - else - resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_PARSE_TREE_BRANCHES), 0); + Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, _resourceIdBranches), 0); _parserBranches.clear(); @@ -243,7 +264,7 @@ bool Vocabulary::loadBranches() { return true; } - +// we assume that *word points to an already lowercased word ResultWord Vocabulary::lookupWord(const char *word, int word_len) { Common::String tempword(word, word_len); @@ -270,7 +291,7 @@ ResultWord Vocabulary::lookupWord(const char *word, int word_len) { int suff_index = word_len - suffix->alt_suffix_length; // Offset of the start of the suffix - if (scumm_strnicmp(suffix->alt_suffix, word + suff_index, suffix->alt_suffix_length) == 0) { // Suffix matched! + if (strncmp(suffix->alt_suffix, word + suff_index, suffix->alt_suffix_length) == 0) { // Suffix matched! // Terminate word at suffix start position...: Common::String tempword2(word, MIN(word_len, suff_index)); @@ -300,82 +321,109 @@ ResultWord Vocabulary::lookupWord(const char *word, int word_len) { return retval; } -void Vocabulary::decipherSaidBlock(byte *addr) { - byte nextitem; +void Vocabulary::debugDecipherSaidBlock(const byte *addr) { + bool first = true; + uint16 nextItem; do { - nextitem = *addr++; - - if (nextitem < 0xf0) { - nextitem = nextitem << 8 | *addr++; - printf(" %s[%03x]", getAnyWordFromGroup(nextitem), nextitem); - - nextitem = 42; // Make sure that group 0xff doesn't abort - } else switch (nextitem) { - case 0xf0: - printf(" ,"); - break; - case 0xf1: - printf(" &"); - break; - case 0xf2: - printf(" /"); - break; - case 0xf3: - printf(" ("); - break; - case 0xf4: - printf(" )"); - break; - case 0xf5: - printf(" ["); - break; - case 0xf6: - printf(" ]"); - break; - case 0xf7: - printf(" #"); - break; - case 0xf8: - printf(" <"); - break; - case 0xf9: - printf(" >"); - break; - case 0xff: - break; + nextItem = *addr++; + if (nextItem != 0xff) { + if ((!first) && (nextItem != 0xf0)) + printf(" "); + first = false; + + if (nextItem < 0xf0) { + nextItem = nextItem << 8 | *addr++; + printf("%s{%03x}", getAnyWordFromGroup(nextItem), nextItem); + + nextItem = 0; // Make sure that group 0xff doesn't abort + } else switch (nextItem) { + case 0xf0: + printf(","); + break; + case 0xf1: + printf("&"); + break; + case 0xf2: + printf("/"); + break; + case 0xf3: + printf("("); + break; + case 0xf4: + printf(")"); + break; + case 0xf5: + printf("["); + break; + case 0xf6: + printf("]"); + break; + case 0xf7: + printf("#"); + break; + case 0xf8: + printf("<"); + break; + case 0xf9: + printf(">"); + break; + case 0xff: + break; } - } while (nextitem != 0xff); - - printf("\n"); + } + } while (nextItem != 0xff); } +static const byte lowerCaseMap[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // 0x00 + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // 0x10 + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, // 0x20 + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, // 0x30 + 0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', // 0x40 + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, // 0x50 + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, // 0x60 + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, // 0x70 + 0x87, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x84, 0x86, // 0x80 + //^^ ^^^^ ^^^^ + 0x82, 0x91, 0x91, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x94, 0x81, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, // 0x90 + //^^ ^^^^ ^^^^ ^^^^ + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, // 0xa0 + // ^^^^ + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, // 0xb0 + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, // 0xc0 + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, // 0xd0 + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, // 0xe0 + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff // 0xf0 +}; + bool Vocabulary::tokenizeString(ResultWordList &retval, const char *sentence, char **error) { - const char *lastword = sentence; + char currentWord[VOCAB_MAX_WORDLENGTH] = ""; int pos_in_sentence = 0; - char c; - int wordlen = 0; + unsigned char c; + int wordLen = 0; *error = NULL; do { c = sentence[pos_in_sentence++]; - - if (isalnum(c) || (c == '-' && wordlen)) - ++wordlen; + if (isalnum(c) || (c == '-' && wordLen) || (c >= 0x80)) { + currentWord[wordLen] = lowerCaseMap[c]; + ++wordLen; + } // Continue on this word */ // Words may contain a '-', but may not // start with one. else { - if (wordlen) { // Finished a word? + if (wordLen) { // Finished a word? - ResultWord lookup_result = lookupWord(lastword, wordlen); + ResultWord lookup_result = lookupWord(currentWord, wordLen); // Look it up if (lookup_result._class == -1) { // Not found? - *error = (char *)calloc(wordlen + 1, 1); - strncpy(*error, lastword, wordlen); // Set the offending word + *error = (char *)calloc(wordLen + 1, 1); + strncpy(*error, currentWord, wordLen); // Set the offending word retval.clear(); return false; // And return with error } @@ -384,8 +432,7 @@ bool Vocabulary::tokenizeString(ResultWordList &retval, const char *sentence, ch retval.push_back(lookup_result); } - lastword = sentence + pos_in_sentence; - wordlen = 0; + wordLen = 0; } } while (c); // Until terminator is hit @@ -394,7 +441,7 @@ bool Vocabulary::tokenizeString(ResultWordList &retval, const char *sentence, ch } void Vocabulary::printSuffixes() const { - char word_buf[256], alt_buf[256]; + char word_buf[VOCAB_MAX_WORDLENGTH], alt_buf[VOCAB_MAX_WORDLENGTH]; Console *con = g_sci->getSciDebugger(); int i = 0; @@ -423,30 +470,25 @@ void Vocabulary::printParserWords() const { con->DebugPrintf("\n"); } -void _vocab_recursive_ptree_dump_treelike(parse_tree_node_t *nodes, int nr, int prevnr) { - if ((nr > VOCAB_TREE_NODES)/* || (nr < prevnr)*/) { - printf("Error(%04x)", nr); - return; - } +void _vocab_recursive_ptree_dump_treelike(ParseTreeNode *tree) { + assert(tree); - if (nodes[nr].type == kParseTreeLeafNode) - //printf("[%03x]%04x", nr, nodes[nr].content.value); - printf("%x", nodes[nr].content.value); + if (tree->type == kParseTreeLeafNode) + printf("%x", tree->value); else { - int lbranch = nodes[nr].content.branches[0]; - int rbranch = nodes[nr].content.branches[1]; - //printf("<[%03x]", nr); + ParseTreeNode* lbranch = tree->left; + ParseTreeNode* rbranch = tree->right; printf("<"); if (lbranch) - _vocab_recursive_ptree_dump_treelike(nodes, lbranch, nr); + _vocab_recursive_ptree_dump_treelike(lbranch); else printf("NULL"); printf(","); if (rbranch) - _vocab_recursive_ptree_dump_treelike(nodes, rbranch, nr); + _vocab_recursive_ptree_dump_treelike(rbranch); else printf("NULL"); @@ -454,55 +496,52 @@ void _vocab_recursive_ptree_dump_treelike(parse_tree_node_t *nodes, int nr, int } } -void _vocab_recursive_ptree_dump(parse_tree_node_t *nodes, int nr, int prevnr, int blanks) { - int lbranch = nodes[nr].content.branches[0]; - int rbranch = nodes[nr].content.branches[1]; - int i; +void _vocab_recursive_ptree_dump(ParseTreeNode *tree, int blanks) { + assert(tree); - if (nodes[nr].type == kParseTreeLeafNode) { - printf("vocab_dump_parse_tree: Error: consp is nil for element %03x\n", nr); - return; - } + ParseTreeNode* lbranch = tree->left; + ParseTreeNode* rbranch = tree->right; + int i; - if ((nr > VOCAB_TREE_NODES)/* || (nr < prevnr)*/) { - printf("Error(%04x))", nr); + if (tree->type == kParseTreeLeafNode) { + printf("vocab_dump_parse_tree: Error: consp is nil\n"); return; } if (lbranch) { - if (nodes[lbranch].type == kParseTreeBranchNode) { + if (lbranch->type == kParseTreeBranchNode) { printf("\n"); for (i = 0; i < blanks; i++) printf(" "); printf("("); - _vocab_recursive_ptree_dump(nodes, lbranch, nr, blanks + 1); + _vocab_recursive_ptree_dump(lbranch, blanks + 1); printf(")\n"); for (i = 0; i < blanks; i++) printf(" "); } else - printf("%x", nodes[lbranch].content.value); + printf("%x", lbranch->value); printf(" "); }/* else printf ("nil");*/ if (rbranch) { - if (nodes[rbranch].type == kParseTreeBranchNode) - _vocab_recursive_ptree_dump(nodes, rbranch, nr, blanks); + if (rbranch->type == kParseTreeBranchNode) + _vocab_recursive_ptree_dump(rbranch, blanks); else - printf("%x", nodes[rbranch].content.value); + printf("%x", rbranch->value); }/* else printf("nil");*/ } -void vocab_dump_parse_tree(const char *tree_name, parse_tree_node_t *nodes) { +void vocab_dump_parse_tree(const char *tree_name, ParseTreeNode *nodes) { //_vocab_recursive_ptree_dump_treelike(nodes, 0, 0); printf("(setq %s \n'(", tree_name); - _vocab_recursive_ptree_dump(nodes, 0, 0, 1); + _vocab_recursive_ptree_dump(nodes, 1); printf("))\n"); } void Vocabulary::dumpParseTree() { //_vocab_recursive_ptree_dump_treelike(nodes, 0, 0); printf("(setq parse-tree \n'("); - _vocab_recursive_ptree_dump(_parserNodes, 0, 0, 1); + _vocab_recursive_ptree_dump(_parserNodes, 1); printf("))\n"); } @@ -522,10 +561,10 @@ void Vocabulary::printParserNodes(int num) { for (int i = 0; i < num; i++) { con->DebugPrintf(" Node %03x: ", i); if (_parserNodes[i].type == kParseTreeLeafNode) - con->DebugPrintf("Leaf: %04x\n", _parserNodes[i].content.value); + con->DebugPrintf("Leaf: %04x\n", _parserNodes[i].value); else - con->DebugPrintf("Branch: ->%04x, ->%04x\n", _parserNodes[i].content.branches[0], - _parserNodes[i].content.branches[1]); + con->DebugPrintf("Branch: ->%04x, ->%04x\n", _parserNodes[i].left, + _parserNodes[i].right); } } @@ -538,7 +577,7 @@ int Vocabulary::parseNodes(int *i, int *pos, int type, int nr, int argc, const c if (type == kParseNumber) { _parserNodes[*pos += 1].type = kParseTreeLeafNode; - _parserNodes[*pos].content.value = nr; + _parserNodes[*pos].value = nr; return *pos; } if (type == kParseEndOfInput) { @@ -570,7 +609,15 @@ int Vocabulary::parseNodes(int *i, int *pos, int type, int nr, int argc, const c } } - if ((newPos = _parserNodes[oldPos].content.branches[j] = parseNodes(i, pos, nextToken, nextValue, argc, argv)) == -1) + newPos = parseNodes(i, pos, nextToken, nextValue, argc, argv); + + if (j == 0) + _parserNodes[oldPos].left = &_parserNodes[newPos]; + else + _parserNodes[oldPos].right = &_parserNodes[newPos]; + + + if (newPos == -1) return -1; } diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h index dccef0f5f3..d4df8af715 100644 --- a/engines/sci/parser/vocabulary.h +++ b/engines/sci/parser/vocabulary.h @@ -73,13 +73,16 @@ enum { kParseNumber = 4 }; +#define VOCAB_MAX_WORDLENGTH 256 + /* Anywords are ignored by the parser */ #define VOCAB_CLASS_ANYWORD 0xff /* This word class is used for numbers */ #define VOCAB_MAGIC_NUMBER_GROUP 0xffd /* 0xffe ? */ +#define VOCAB_MAGIC_NOTHING_GROUP 0xffe -/* Number of nodes for each parse_tree_node structure */ +/* Number of nodes for each ParseTreeNode structure */ #define VOCAB_TREE_NODES 500 #define VOCAB_TREE_NODE_LAST_WORD_STORAGE 0x140 @@ -115,7 +118,7 @@ struct ResultWord { typedef Common::List<ResultWord> ResultWordList; -typedef Common::HashMap<Common::String, ResultWord, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> WordMap; +typedef Common::HashMap<Common::String, ResultWord, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> WordMap; struct ParseRuleList; @@ -149,16 +152,16 @@ struct parse_tree_branch_t { }; enum ParseTypes { - kParseTreeLeafNode = 0, - kParseTreeBranchNode = 1 + kParseTreeWordNode = 4, + kParseTreeLeafNode = 5, + kParseTreeBranchNode = 6 }; -struct parse_tree_node_t { +struct ParseTreeNode { ParseTypes type; /**< leaf or branch */ - union { - int value; /**< For leaves */ - short branches[2]; /**< For branches */ - } content; + int value; /**< For leaves */ + ParseTreeNode* left; /**< Left child, for branches */ + ParseTreeNode* right; /**< Right child, for branches */ }; enum VocabularyVersions { @@ -168,9 +171,12 @@ enum VocabularyVersions { class Vocabulary { public: - Vocabulary(ResourceManager *resMan); + Vocabulary(ResourceManager *resMan, bool foreign); ~Vocabulary(); + // reset parser status + void reset(); + /** * Gets any word from the specified group. For debugging only. * @param group Group number @@ -229,7 +235,7 @@ public: * For debugging only. * @param pos pointer to the data to dump */ - void decipherSaidBlock(byte *pos); + void debugDecipherSaidBlock(const byte *pos); /** * Prints the parser suffixes to the debug console. @@ -301,6 +307,11 @@ private: ResourceManager *_resMan; VocabularyVersions _vocabVersion; + bool _foreign; + uint16 _resourceIdWords; + uint16 _resourceIdSuffixes; + uint16 _resourceIdBranches; + // Parser-related lists SuffixList _parserSuffixes; ParseRuleList *_parserRules; /**< GNF rules used in the parser algorithm */ @@ -310,7 +321,7 @@ private: public: // Accessed by said() - parse_tree_node_t _parserNodes[VOCAB_TREE_NODES]; /**< The parse tree */ + ParseTreeNode _parserNodes[VOCAB_TREE_NODES]; /**< The parse tree */ // Parser data: reg_t parser_base; /**< Base address for the parser error reporting mechanism */ @@ -323,7 +334,7 @@ public: * @param tree_name Name of the tree to dump (free-form) * @param nodes The nodes containing the parse tree */ -void vocab_dump_parse_tree(const char *tree_name, parse_tree_node_t *nodes); +void vocab_dump_parse_tree(const char *tree_name, ParseTreeNode *nodes); @@ -334,7 +345,7 @@ void vocab_dump_parse_tree(const char *tree_name, parse_tree_node_t *nodes); * @param verbose Whether to display the parse tree after building it * @return 1 on a match, 0 otherwise */ -int said(EngineState *s, byte *spec, bool verbose); +int said(EngineState *s, const byte *spec, bool verbose); } // End of namespace Sci diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 2958ca1e3b..111bf6ad9b 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -108,30 +108,79 @@ static const char *sci_error_types[] = { "SCI version is unsupported" }; -// These are the 20 resource types supported by SCI1.1 -static const char *resourceTypeNames[] = { +static const char *s_resourceTypeNames[] = { "view", "pic", "script", "text", "sound", "memory", "vocab", "font", "cursor", "patch", "bitmap", "palette", "cdaudio", "audio", "sync", "message", "map", "heap", - "audio36", "sync36", "", "", "robot", "vmd" + "audio36", "sync36", "xlate", "robot", "vmd", + "chunk", "macibin", "macibis", "macpict" }; -static const char *resourceTypeSuffixes[] = { +static const char *s_resourceTypeSuffixes[] = { "v56", "p56", "scr", "tex", "snd", - " ", "voc", "fon", "cur", "pat", + "", "voc", "fon", "cur", "pat", "bit", "pal", "cda", "aud", "syn", - "msg", "map", "hep", "aud", "syn", - "trn", " ", "rbt", "vmd" -}; + "msg", "map", "hep", "", "", + "trn", "rbt", "vmd", "chk", "", + "", "" +}; const char *getResourceTypeName(ResourceType restype) { if (restype != kResourceTypeInvalid) - return resourceTypeNames[restype]; + return s_resourceTypeNames[restype]; else return "invalid"; } +static const ResourceType s_resTypeMapSci0[] = { + kResourceTypeView, kResourceTypePic, kResourceTypeScript, kResourceTypeText, // 0x00-0x03 + kResourceTypeSound, kResourceTypeMemory, kResourceTypeVocab, kResourceTypeFont, // 0x04-0x07 + kResourceTypeCursor, kResourceTypePatch, kResourceTypeBitmap, kResourceTypePalette, // 0x08-0x0B + kResourceTypeCdAudio, kResourceTypeAudio, kResourceTypeSync, kResourceTypeMessage, // 0x0C-0x0F + kResourceTypeMap, kResourceTypeHeap, kResourceTypeAudio36, kResourceTypeSync36, // 0x10-0x13 + kResourceTypeTranslation // 0x14 +}; + +#ifdef ENABLE_SCI32 +// TODO: 12 should be "Wave", but SCI seems to just store it in Audio resources +static const ResourceType s_resTypeMapSci21[] = { + kResourceTypeView, kResourceTypePic, kResourceTypeScript, kResourceTypeText, // 0x00-0x03 + kResourceTypeSound, kResourceTypeMemory, kResourceTypeVocab, kResourceTypeFont, // 0x04-0x07 + kResourceTypeCursor, kResourceTypePatch, kResourceTypeBitmap, kResourceTypePalette, // 0x08-0x0B + kResourceTypeInvalid, kResourceTypeAudio, kResourceTypeSync, kResourceTypeMessage, // 0x0C-0x0F + kResourceTypeMap, kResourceTypeHeap, kResourceTypeChunk, kResourceTypeAudio36, // 0x10-0x13 + kResourceTypeSync36, kResourceTypeTranslation, kResourceTypeRobot, kResourceTypeVMD // 0x14-0x17 +}; +#endif + +ResourceType ResourceManager::convertResType(byte type) { + type &= 0x7f; + + if (_mapVersion != kResVersionSci32) { + // SCI0 - SCI2 + if (type < ARRAYSIZE(s_resTypeMapSci0)) + return s_resTypeMapSci0[type]; + } else { + // SCI2.1+ +#ifdef ENABLE_SCI32 + if (type < ARRAYSIZE(s_resTypeMapSci21)) { + // LSL6 hires doesn't have the chunk resource type, to match + // the resource types of the lowres version, thus we use the + // older resource types here + if (g_sci && g_sci->getGameId() == GID_LSL6HIRES) + return s_resTypeMapSci0[type]; + else + return s_resTypeMapSci21[type]; + } +#else + error("SCI32 support not compiled in"); +#endif + } + + return kResourceTypeInvalid; +} + //-- Resource main functions -- Resource::Resource(ResourceId id) : _id(id) { data = NULL; @@ -337,7 +386,7 @@ void MacResourceForkResourceSource::loadResource(ResourceManager *resMan, Resour Common::SeekableReadStream *stream = _macResMan->getResource(resTypeToMacTag(res->getType()), res->getNumber()); if (!stream) - error("Could not get Mac resource fork resource: %d %d", res->getType(), res->getNumber()); + error("Could not get Mac resource fork resource: %s %d", getResourceTypeName(res->getType()), res->getNumber()); int error = res->decompress(resMan->getVolVersion(), stream); if (error) { @@ -527,9 +576,13 @@ int ResourceManager::addAppropriateSources() { #endif addPatchDir("."); + if (Common::File::exists("message.map")) addSource(new VolumeResourceSource("resource.msg", addExternalMap("message.map"), 0)); + if (Common::File::exists("altres.map")) + addSource(new VolumeResourceSource("altres.000", addExternalMap("altres.map"), 0)); + return 1; } @@ -1093,7 +1146,7 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType resource return; } - byte patchType = fileStream->readByte() & 0x7F; + byte patchType = convertResType(fileStream->readByte()); byte patchDataOffset = fileStream->readByte(); delete fileStream; @@ -1231,7 +1284,11 @@ void ResourceManager::readResourcePatches() { const char *szResType; ResourceSource *psrcPatch; - for (int i = kResourceTypeView; i <= kResourceTypeHeap; ++i) { + for (int i = kResourceTypeView; i < kResourceTypeInvalid; ++i) { + // Ignore the types that can't be patched (and Robot/VMD is handled externally for now) + if (!s_resourceTypeSuffixes[i] || i == kResourceTypeRobot || i == kResourceTypeVMD) + continue; + files.clear(); szResType = getResourceTypeName((ResourceType)i); // SCI0 naming - type.nnn @@ -1240,7 +1297,7 @@ void ResourceManager::readResourcePatches() { SearchMan.listMatchingMembers(files, mask); // SCI1 and later naming - nnn.typ mask = "*."; - mask += resourceTypeSuffixes[i]; + mask += s_resourceTypeSuffixes[i]; SearchMan.listMatchingMembers(files, mask); for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { @@ -1304,7 +1361,7 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map) { if (offset == 0xFFFFFFFF) break; - type = (ResourceType)(id >> 11); + type = convertResType(id >> 11); number = id & 0x7FF; ResourceId resId = ResourceId(type, number); // adding a new resource @@ -1389,14 +1446,20 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { warning("Error while reading %s", map->getLocationName().c_str()); return SCI_ERROR_RESMAP_NOT_FOUND; } - resId = ResourceId((ResourceType)type, number); + resId = ResourceId(convertResType(type), number); // adding new resource only if it does not exist if (_resMap.contains(resId) == false) { // NOTE: We add the map's volume number here to the specified volume number // for SCI2.1 and SCI3 maps that are not resmap.000. The resmap.* files' numbers // need to be used in concurrence with the volume specified in the map to get // the actual resource file. - addResource(resId, findVolume(map, volume_nr + map->_volumeNumber), off); + int mapVolumeNr = volume_nr + map->_volumeNumber; + ResourceSource *source = findVolume(map, mapVolumeNr); + // FIXME: this code has serious issues with multiple RESMAP.* files (like in unmodified gk2) + // adding a resource with source == NULL would crash later on + if (!source) + error("Unable to find volume for map %s volumeNr %d", map->getLocationName().c_str(), mapVolumeNr); + addResource(resId, source, off); } } } @@ -1875,14 +1938,18 @@ void ResourceManager::detectSciVersion() { return; } - // New decompressors. It's either SCI_VERSION_1_EGA or SCI_VERSION_1_EARLY. - if (hasSci1Voc900()) { - s_sciVersion = SCI_VERSION_1_EGA; - return; + // New decompressors. It's either SCI_VERSION_0_LATE, SCI_VERSION_1_EGA or SCI_VERSION_1_EARLY. + if (testResource(ResourceId(kResourceTypeVocab, 900))) { + if (hasSci1Voc900()) { + s_sciVersion = SCI_VERSION_1_EGA; + return; + } else { + s_sciVersion = SCI_VERSION_0_LATE; + return; + } } - // SCI_VERSION_1_EARLY EGA versions seem to be lacking a valid vocab.900. - // If this turns out to be unreliable, we could do some pic resource checks instead. + // SCI_VERSION_1_EARLY EGA versions lack the parser vocab s_sciVersion = SCI_VERSION_1_EARLY; return; case kResVersionSci1Middle: diff --git a/engines/sci/resource.h b/engines/sci/resource.h index f66b5b3956..f1ea2f15f9 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -95,22 +95,21 @@ enum ResourceType { kResourceTypeHeap, kResourceTypeAudio36, kResourceTypeSync36, - kResourceTypeUnknown1, // Translation, currently unsupported - kResourceTypeUnknown2, + kResourceTypeTranslation, // Currently unsupported kResourceTypeRobot, kResourceTypeVMD, - kResourceTypeInvalid, + kResourceTypeChunk, - // Mac-only resources, these resource types are self-defined - // Numbers subject to change - kResourceTypeMacIconBarPictN = -1, // IBIN resources (icon bar, not selected) - kResourceTypeMacIconBarPictS = -2, // IBIS resources (icon bar, selected) - kResourceTypeMacPict = -3 // PICT resources (inventory) + // Mac-only resources + kResourceTypeMacIconBarPictN, // IBIN resources (icon bar, not selected) + kResourceTypeMacIconBarPictS, // IBIS resources (icon bar, selected) + kResourceTypeMacPict, // PICT resources (inventory) + + kResourceTypeInvalid }; const char *getResourceTypeName(ResourceType restype); - enum ResVersion { kResVersionUnknown, kResVersionSci0Sci1Early, @@ -126,7 +125,7 @@ class ResourceSource; class ResourceId { static inline ResourceType fixupType(ResourceType type) { - if (type < kResourceTypeMacPict || type > kResourceTypeInvalid) + if (type >= kResourceTypeInvalid) return kResourceTypeInvalid; return type; } @@ -150,11 +149,11 @@ public: Common::String toString() const { char buf[32]; - snprintf(buf, 32, "%s.%i", getResourceTypeName(_type), _number); + snprintf(buf, 32, "%s.%d", getResourceTypeName(_type), _number); Common::String retStr = buf; if (_tuple != 0) { - snprintf(buf, 32, "(%i, %i, %i, %i)", _tuple >> 24, (_tuple >> 16) & 0xff, (_tuple >> 8) & 0xff, _tuple & 0xff); + snprintf(buf, 32, "(%d, %d, %d, %d)", _tuple >> 24, (_tuple >> 16) & 0xff, (_tuple >> 8) & 0xff, _tuple & 0xff); retStr += buf; } @@ -344,6 +343,13 @@ public: */ reg_t findGameObject(bool addSci11ScriptOffset = true); + /** + * Converts a map resource type to our type + * @param sciType The type from the map/patch + * @return The ResourceType + */ + ResourceType convertResType(byte type); + protected: // Maximum number of bytes to allow being allocated for resources // Note: maxMemory will not be interpreted as a hard limit, only as a restriction diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp index 13aa81cb24..08e05f5ccd 100644 --- a/engines/sci/resource_audio.cpp +++ b/engines/sci/resource_audio.cpp @@ -543,6 +543,7 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers case SCI_VERSION_1_EARLY: case SCI_VERSION_1_LATE: + case SCI_VERSION_2_1: data = resource->data; // Count # of tracks _trackCount = 0; diff --git a/engines/sci/resource_intern.h b/engines/sci/resource_intern.h index 45421dd722..73986444a4 100644 --- a/engines/sci/resource_intern.h +++ b/engines/sci/resource_intern.h @@ -73,7 +73,7 @@ public: */ virtual ResourceSource *findVolume(ResourceSource *map, int volNum) { return NULL; - }; + } /** * Scan this source for TODO. @@ -122,7 +122,7 @@ public: if (_associatedMap == map && _volumeNumber == volNum) return this; return NULL; - }; + } }; class ExtMapResourceSource : public ResourceSource { diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 1ebc6a2ba3..f07cc257bd 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -54,6 +54,7 @@ #include "sci/graphics/menu.h" #include "sci/graphics/paint16.h" #include "sci/graphics/paint32.h" +#include "sci/graphics/picture.h" #include "sci/graphics/ports.h" #include "sci/graphics/palette.h" #include "sci/graphics/screen.h" @@ -85,6 +86,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam _gamestate = 0; _kernel = 0; _vocabulary = 0; + _vocabularyLanguage = 1; // we load english vocabulary on startup _eventMan = 0; _console = 0; @@ -120,6 +122,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam SearchMan.addSubDirectoryMatching(gameDataDir, "avi"); // AVI movie files for Windows versions SearchMan.addSubDirectoryMatching(gameDataDir, "seq"); // SEQ movie files for DOS versions SearchMan.addSubDirectoryMatching(gameDataDir, "robot"); // robot movie files + SearchMan.addSubDirectoryMatching(gameDataDir, "robots"); // robot movie files SearchMan.addSubDirectoryMatching(gameDataDir, "movies"); // vmd movie files SearchMan.addSubDirectoryMatching(gameDataDir, "vmd"); // vmd movie files @@ -202,7 +205,7 @@ Common::Error SciEngine::run() { _kernel = new Kernel(_resMan, segMan); _features = new GameFeatures(segMan, _kernel); // Only SCI0 and SCI01 games used a parser - _vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA) ? new Vocabulary(_resMan) : NULL; + _vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA) ? new Vocabulary(_resMan, false) : NULL; _audio = new AudioPlayer(_resMan); _gamestate = new EngineState(segMan); _eventMan = new EventManager(_resMan->detectFontExtended()); @@ -275,10 +278,6 @@ bool SciEngine::initGame() { } _gamestate->initGlobals(); - - if (_gamestate->abortScriptProcessing == kAbortRestartGame && _gfxMenu) - _gfxMenu->reset(); - _gamestate->_segMan->initSysStrings(); _gamestate->r_acc = _gamestate->r_prev = NULL_REG; @@ -300,9 +299,7 @@ bool SciEngine::initGame() { // Reset parser if (_vocabulary) { - _vocabulary->parserIsValid = false; // Invalidate parser - _vocabulary->parser_event = NULL_REG; // Invalidate parser event - _vocabulary->parser_base = make_reg(_gamestate->_segMan->getSysStringsSegment(), SYS_STRING_PARSER_BASE); + _vocabulary->reset(); } _gamestate->gameStartTime = _gamestate->lastWaitTime = _gamestate->_screenUpdateTime = g_system->getMillis(); @@ -411,7 +408,7 @@ void SciEngine::runGame() { do { _gamestate->_executionStackPosChanged = false; - run_vm(_gamestate, (_gamestate->abortScriptProcessing == kAbortLoadGame)); + run_vm(_gamestate); exitGame(); if (_gamestate->abortScriptProcessing == kAbortRestartGame) { @@ -419,9 +416,15 @@ void SciEngine::runGame() { initGame(); initStackBaseWithSelector(SELECTOR(play)); _gamestate->gameWasRestarted = true; + if (_gfxMenu) + _gfxMenu->reset(); + _gamestate->abortScriptProcessing = kAbortNone; } else if (_gamestate->abortScriptProcessing == kAbortLoadGame) { _gamestate->abortScriptProcessing = kAbortNone; + _gamestate->_executionStack.clear(); initStackBaseWithSelector(SELECTOR(replay)); + _gamestate->shrinkStackToBase(); + _gamestate->abortScriptProcessing = kAbortNone; } else { break; // exit loop } @@ -497,8 +500,11 @@ Common::String SciEngine::getFilePrefix() const { // Quest for Glory 3 wants to read files from Quest for Glory 2 to import character data if (_gamestate->currentRoomNumber() == 54) return "qfg2"; + } else if (_gameId == GID_QFG4) { + // Quest for Glory 4 wants to read files from Quest for Glory 3 to import character data + if (_gamestate->currentRoomNumber() == 54) + return "qfg3"; } - // TODO: Implement the same for qfg4, when sci32 is good enough return _targetName; } diff --git a/engines/sci/sci.h b/engines/sci/sci.h index c15f87e4e2..13c9d03614 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -143,6 +143,7 @@ enum SciGameId { GID_LSL6HIRES, // We have a separate ID for LSL6 SCI32, because it's actually a completely different game GID_LSL7, GID_MOTHERGOOSE, + GID_MOTHERGOOSEHIRES, // We have a separate ID for Mother Goose SCI32, because it's actually a completely different game GID_MSASTROCHICKEN, GID_PEPPER, GID_PHANTASMAGORIA, @@ -153,6 +154,7 @@ enum SciGameId { GID_PQ4, GID_PQSWAT, GID_QFG1, + GID_QFG1VGA, GID_QFG2, GID_QFG3, GID_QFG4, @@ -266,6 +268,9 @@ public: Common::String getSciLanguageString(const char *str, kLanguage lang, kLanguage *lang2 = NULL) const; + // Check if vocabulary needs to get switched (in multilingual parser games) + void checkVocabularySwitch(); + // Initializes ports and paint16 for non-sci32 games, also sets default palette void initGraphics(); @@ -331,6 +336,7 @@ private: EngineState *_gamestate; Kernel *_kernel; Vocabulary *_vocabulary; + int16 _vocabularyLanguage; EventManager *_eventMan; reg_t _gameObj; /**< Pointer to the game object */ Console *_console; diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp index d9e9d2e8db..640273c20e 100644 --- a/engines/sci/sound/audio.cpp +++ b/engines/sci/sound/audio.cpp @@ -35,6 +35,7 @@ #include "sound/audiostream.h" #include "sound/decoders/aiff.h" #include "sound/decoders/flac.h" +#include "sound/decoders/mac_snd.h" #include "sound/decoders/mp3.h" #include "sound/decoders/raw.h" #include "sound/decoders/vorbis.h" @@ -337,19 +338,9 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 } else if (audioRes->size > 14 && READ_BE_UINT16(audioRes->data) == 1 && READ_BE_UINT16(audioRes->data + 2) == 1 && READ_BE_UINT16(audioRes->data + 4) == 5 && READ_BE_UINT32(audioRes->data + 10) == 0x00018051) { // Mac snd detected - // See http://developer.apple.com/legacy/mac/library/documentation/mac/Sound/Sound-60.html#HEADING60-15 for more details + Common::MemoryReadStream *sndStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO); - uint32 soundHeaderOffset = READ_BE_UINT32(audioRes->data + 16); - assert(READ_BE_UINT32(audioRes->data + soundHeaderOffset) == 0); - size = READ_BE_UINT32(audioRes->data + soundHeaderOffset + 4); - _audioRate = READ_BE_UINT16(audioRes->data + soundHeaderOffset + 8); // Really floating point, but we're just truncating - - if (*(audioRes->data + soundHeaderOffset + 20) != 0) - error("Unhandled Mac snd extended/compressed header"); - - data = (byte *)malloc(size); - memcpy(data, audioRes->data + soundHeaderOffset + 22, size); - flags = Audio::FLAG_UNSIGNED; + audioSeekStream = Audio::makeMacSndStream(sndStream, DisposeAfterUse::YES); } else { // SCI1 raw audio size = audioRes->size; diff --git a/engines/sci/sound/drivers/amiga.cpp b/engines/sci/sound/drivers/amigamac.cpp index 0413dbb79e..4fb9146b53 100644 --- a/engines/sci/sound/drivers/amiga.cpp +++ b/engines/sci/sound/drivers/amigamac.cpp @@ -25,6 +25,7 @@ #include "sound/softsynth/emumidi.h" #include "sci/sound/drivers/mididriver.h" +#include "sci/resource.h" #include "common/file.h" #include "common/frac.h" @@ -34,35 +35,14 @@ namespace Sci { /* #define DEBUG */ -// Frequencies for every note -// FIXME Store only one octave -static const int freq_table[] = { - 58, 62, 65, 69, 73, 78, 82, 87, - 92, 98, 104, 110, 117, 124, 131, 139, - 147, 156, 165, 175, 185, 196, 208, 220, - 234, 248, 262, 278, 294, 312, 331, 350, - 371, 393, 417, 441, 468, 496, 525, 556, - 589, 625, 662, 701, 743, 787, 834, 883, - 936, 992, 1051, 1113, 1179, 1250, 1324, 1403, - 1486, 1574, 1668, 1767, 1872, 1984, 2102, 2227, - 2359, 2500, 2648, 2806, 2973, 3149, 3337, 3535, - 3745, 3968, 4204, 4454, 4719, 5000, 5297, 5612, - 5946, 6299, 6674, 7071, 7491, 7937, 8408, 8908, - 9438, 10000, 10594, 11224, 11892, 12599, 13348, 14142, - 14983, 15874, 16817, 17817, 18877, 20000, 21189, 22449, - 23784, 25198, 26696, 28284, 29966, 31748, 33635, 35635, - 37754, 40000, 42378, 44898, 47568, 50396, 53393, 56568, - 59932, 63496, 67271, 71271, 75509, 80000, 84757, 89796 -}; - -class MidiDriver_Amiga : public MidiDriver_Emulated { +class MidiDriver_AmigaMac : public MidiDriver_Emulated { public: enum { kVoices = 4 }; - MidiDriver_Amiga(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15) { } - virtual ~MidiDriver_Amiga() { } + MidiDriver_AmigaMac(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15) { } + virtual ~MidiDriver_AmigaMac() { } // MidiDriver int open(); @@ -99,6 +79,7 @@ private: int instrument; int volume; int pan; + uint16 pitch; }; struct Envelope { @@ -121,7 +102,7 @@ private: frac_t rate; }; - struct Instrument { + struct InstrumentSample { char name[30]; int mode; int size; // Size of non-looping part in bytes @@ -130,35 +111,55 @@ private: Envelope envelope[4]; // Envelope int8 *samples; int8 *loop; + int16 startNote; + int16 endNote; + bool isUnsigned; + uint16 baseFreq; + uint16 baseNote; + int16 fixedNote; + }; + + class Instrument : public Common::Array<InstrumentSample *> { + public: + char name[30]; }; struct Bank { char name[30]; uint size; - Instrument *instruments[256]; + Common::Array<Instrument> instruments; }; + bool _isSci1; bool _playSwitch; int _masterVolume; int _frequency; Envelope _envDecay; Bank _bank; // Instrument bank + double _freqTable[48]; Channel _channels[MIDI_CHANNELS]; /* Internal channels */ Voice _voices[kChannels]; void setEnvelope(Voice *channel, Envelope *envelope, int phase); - int interpolate(int8 *samples, frac_t offset); + void setOutputFrac(int voice); + int interpolate(int8 *samples, frac_t offset, bool isUnsigned); void playInstrument(int16 *dest, Voice *channel, int count); void changeInstrument(int channel, int instrument); void stopChannel(int ch); void stopNote(int ch, int note); void startNote(int ch, int note, int velocity); - Instrument *readInstrument(Common::File &file, int *id); + InstrumentSample *findInstrument(int instrument, int note); + void pitchWheel(int ch, uint16 pitch); + + bool loadInstrumentsSCI0(Common::File &file); + bool loadInstrumentsSCI0Mac(Common::SeekableReadStream &file); + InstrumentSample *readInstrumentSCI0(Common::SeekableReadStream &file, int *id); + bool loadInstrumentsSCI1(Common::SeekableReadStream &file); }; -void MidiDriver_Amiga::setEnvelope(Voice *channel, Envelope *envelope, int phase) { +void MidiDriver_AmigaMac::setEnvelope(Voice *channel, Envelope *envelope, int phase) { channel->envelope = phase; channel->envelope_samples = envelope[phase].length; @@ -168,25 +169,32 @@ void MidiDriver_Amiga::setEnvelope(Voice *channel, Envelope *envelope, int phase channel->velocity = envelope[phase - 1].target; } -int MidiDriver_Amiga::interpolate(int8 *samples, frac_t offset) { +int MidiDriver_AmigaMac::interpolate(int8 *samples, frac_t offset, bool isUnsigned) { int x = fracToInt(offset); - int diff = (samples[x + 1] - samples[x]) << 8; + if (isUnsigned) { + int s1 = (byte)samples[x] - 0x80; + int s2 = (byte)samples[x + 1] - 0x80; + int diff = (s2 - s1) << 8; + return (s1 << 8) + fracToInt(diff * (offset & FRAC_LO_MASK)); + } + + int diff = (samples[x + 1] - samples[x]) << 8; return (samples[x] << 8) + fracToInt(diff * (offset & FRAC_LO_MASK)); } -void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) { +void MidiDriver_AmigaMac::playInstrument(int16 *dest, Voice *channel, int count) { int index = 0; int vol = _channels[channel->hw_channel].volume; - Instrument *instrument = _bank.instruments[channel->instrument]; + InstrumentSample *instrument = findInstrument(channel->instrument, channel->note); while (1) { /* Available source samples until end of segment */ frac_t lin_avail; - int seg_end, rem, i, amount; + uint32 seg_end, rem, i, amount; int8 *samples; - if (channel->looping) { + if (channel->looping && instrument->loop) { samples = instrument->loop; seg_end = instrument->loop_size; } else { @@ -208,11 +216,11 @@ void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) { amount = rem; /* Stop at next envelope event */ - if ((channel->envelope_samples != -1) && (amount > channel->envelope_samples)) + if ((channel->envelope_samples != -1) && (amount > (uint32)channel->envelope_samples)) amount = channel->envelope_samples; for (i = 0; i < amount; i++) { - dest[index++] = interpolate(samples, channel->offset) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127); + dest[index++] = interpolate(samples, channel->offset, instrument->isUnsigned) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127); channel->offset += channel->rate; } @@ -263,7 +271,7 @@ void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) { if (index == count) break; - if (fracToInt(channel->offset) >= seg_end) { + if ((uint32)fracToInt(channel->offset) >= seg_end) { if (instrument->mode & kModeLoop) { /* Loop the samples */ channel->offset -= intToFrac(seg_end); @@ -277,9 +285,9 @@ void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) { } } -void MidiDriver_Amiga::changeInstrument(int channel, int instrument) { +void MidiDriver_AmigaMac::changeInstrument(int channel, int instrument) { #ifdef DEBUG - if (_bank.instruments[instrument]) + if (_bank.instruments[instrument][0]) printf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument]->name, instrument); else warning("[sfx:seq:amiga] instrument %i does not exist (channel %i)", instrument, channel); @@ -287,7 +295,7 @@ void MidiDriver_Amiga::changeInstrument(int channel, int instrument) { _channels[channel].instrument = instrument; } -void MidiDriver_Amiga::stopChannel(int ch) { +void MidiDriver_AmigaMac::stopChannel(int ch) { int i; /* Start decay phase for note on this hw channel, if any */ @@ -300,9 +308,16 @@ void MidiDriver_Amiga::stopChannel(int ch) { } } -void MidiDriver_Amiga::stopNote(int ch, int note) { +void MidiDriver_AmigaMac::pitchWheel(int ch, uint16 pitch) { + _channels[ch].pitch = pitch; + + for (int i = 0; i < kChannels; i++) + if (_voices[i].note != -1 && _voices[i].hw_channel == ch) + setOutputFrac(i); +} + +void MidiDriver_AmigaMac::stopNote(int ch, int note) { int channel; - Instrument *instrument; for (channel = 0; channel < kChannels; channel++) if (_voices[channel].note == note && _voices[channel].hw_channel == ch && !_voices[channel].decay) @@ -315,15 +330,75 @@ void MidiDriver_Amiga::stopNote(int ch, int note) { return; } - instrument = _bank.instruments[_voices[channel].instrument]; + InstrumentSample *instrument = findInstrument(_voices[channel].instrument, note); + + // FIXME: SCI1 envelope support is not perfect yet /* Start the envelope phases for note-off if looping is on and envelope is enabled */ if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0)) setEnvelope(&_voices[channel], instrument->envelope, 2); } -void MidiDriver_Amiga::startNote(int ch, int note, int velocity) { - Instrument *instrument; +MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::findInstrument(int instrument, int note) { + if ((uint)instrument >= _bank.instruments.size()) + return 0; + + for (uint32 i = 0; i < _bank.instruments[instrument].size(); i++) { + InstrumentSample *sample = _bank.instruments[instrument][i]; + if (note >= sample->startNote && note <= sample->endNote) + return sample; + } + + return 0; +} + +void MidiDriver_AmigaMac::setOutputFrac(int voice) { + InstrumentSample *instrument = findInstrument(_voices[voice].instrument, _voices[voice].note); + + int fnote = 0; + + if (instrument->fixedNote == -1) { + fnote = _voices[voice].note; + + // Handle SCI0-style transposing here + if (!_isSci1) + fnote += instrument->transpose; + + if (fnote < 0 || fnote > 127) { + warning("[sfx:seq:amiga] illegal note %i", fnote); + return; + } + } else + fnote = instrument->fixedNote; + + // Compute rate for note + int mulFact = 1, divFact = 1; + + fnote -= instrument->baseNote; + fnote *= 4; + // FIXME: check how SSCI maps this + fnote += (_channels[_voices[voice].hw_channel].pitch - 0x2000) / 169; + + while (fnote < 0) { + divFact *= 2; + fnote += 12 * 4; + } + + while (fnote >= 12 * 4) { + mulFact *= 2; + fnote -= 12 * 4; + } + + double freq = _freqTable[fnote] * instrument->baseFreq * mulFact / divFact; + + // Handle SCI1-style transposing here + if (instrument->transpose && _isSci1) + freq = freq + ((_freqTable[4] - 1.0) * freq * (double)instrument->transpose / (double)16); + + _voices[voice].rate = doubleToFrac(freq / _frequency); +} + +void MidiDriver_AmigaMac::startNote(int ch, int note, int velocity) { int channel; if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) { @@ -331,7 +406,7 @@ void MidiDriver_Amiga::startNote(int ch, int note, int velocity) { return; } - instrument = _bank.instruments[_channels[ch].instrument]; + InstrumentSample *instrument = findInstrument(_channels[ch].instrument, note); if (!instrument) { warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument); @@ -349,19 +424,6 @@ void MidiDriver_Amiga::startNote(int ch, int note, int velocity) { stopChannel(ch); - if (instrument->mode & kModePitch) { - int fnote = note + instrument->transpose; - - if (fnote < 0 || fnote > 127) { - warning("[sfx:seq:amiga] illegal note %i\n", fnote); - return; - } - - /* Compute rate for note */ - _voices[channel].rate = doubleToFrac(freq_table[fnote] / (double) _frequency); - } else - _voices[channel].rate = doubleToFrac(kBaseFreq / (double) _frequency); - _voices[channel].instrument = _channels[ch].instrument; _voices[channel].note = note; _voices[channel].note_velocity = velocity; @@ -377,30 +439,34 @@ void MidiDriver_Amiga::startNote(int ch, int note, int velocity) { _voices[channel].hw_channel = ch; _voices[channel].decay = 0; _voices[channel].looping = 0; + setOutputFrac(channel); } -MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &file, int *id) { - Instrument *instrument; +MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(Common::SeekableReadStream &file, int *id) { byte header[61]; - int size; - int seg_size[3]; - int loop_offset; - int i; if (file.read(header, 61) < 61) { warning("[sfx:seq:amiga] failed to read instrument header"); return NULL; } - instrument = new Instrument; - + int seg_size[3]; seg_size[0] = READ_BE_UINT16(header + 35) * 2; seg_size[1] = READ_BE_UINT16(header + 41) * 2; seg_size[2] = READ_BE_UINT16(header + 47) * 2; + InstrumentSample *instrument = new InstrumentSample; + + instrument->startNote = 0; + instrument->endNote = 127; + instrument->isUnsigned = false; + instrument->baseFreq = kBaseFreq; + instrument->baseNote = 101; + instrument->fixedNote = 101; + instrument->mode = header[33]; instrument->transpose = (int8) header[34]; - for (i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { int length = (int8) header[49 + i]; if (length == 0 && i > 0) @@ -413,13 +479,14 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil /* Final target must be 0 */ instrument->envelope[3].target = 0; - loop_offset = READ_BE_UINT32(header + 37) & ~1; - size = seg_size[0] + seg_size[1] + seg_size[2]; + int loop_offset = READ_BE_UINT32(header + 37) & ~1; + int size = seg_size[0] + seg_size[1] + seg_size[2]; *id = READ_BE_UINT16(header); strncpy(instrument->name, (char *) header + 2, 29); instrument->name[29] = 0; + #ifdef DEBUG printf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n", *id, instrument->name, size); @@ -429,6 +496,7 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil printf(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]); printf(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43)); #endif + instrument->samples = (int8 *) malloc(size + 1); if (file.read(instrument->samples, size) < (unsigned int)size) { warning("[sfx:seq:amiga] failed to read instrument samples"); @@ -437,6 +505,9 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil return NULL; } + if (instrument->mode & kModePitch) + instrument->fixedNote = -1; + if (instrument->mode & kModeLoop) { if (loop_offset + seg_size[1] > size) { #ifdef DEBUG @@ -463,6 +534,7 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil instrument->loop[instrument->loop_size] = instrument->loop[0]; } else { instrument->loop = NULL; + instrument->loop_size = 0; instrument->size = size; instrument->samples[instrument->size] = 0; } @@ -470,7 +542,7 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil return instrument; } -uint32 MidiDriver_Amiga::property(int prop, uint32 param) { +uint32 MidiDriver_AmigaMac::property(int prop, uint32 param) { switch(prop) { case MIDI_PROP_MASTER_VOLUME: if (param != 0xffff) @@ -482,28 +554,17 @@ uint32 MidiDriver_Amiga::property(int prop, uint32 param) { return 0; } -int MidiDriver_Amiga::open() { +int MidiDriver_AmigaMac::open() { + _isSci1 = false; + + for (int i = 0; i < 48; i++) + _freqTable[i] = pow(2, i / (double)48); + _frequency = _mixer->getOutputRate(); _envDecay.length = _frequency / (32 * 64); _envDecay.delta = 1; _envDecay.target = 0; - Common::File file; - byte header[40]; - - if (!file.open("bank.001")) { - warning("[sfx:seq:amiga] file bank.001 not found"); - return Common::kUnknownError; - } - - if (file.read(header, 40) < 40) { - warning("[sfx:seq:amiga] failed to read header of file bank.001"); - return Common::kUnknownError; - } - - for (uint i = 0; i < 256; i++) - _bank.instruments[i] = NULL; - for (uint i = 0; i < kChannels; i++) { _voices[i].note = -1; _voices[i].hw_channel = 0; @@ -513,32 +574,46 @@ int MidiDriver_Amiga::open() { _channels[i].instrument = -1; _channels[i].volume = 127; _channels[i].pan = (i % 4 == 0 || i % 4 == 3 ? kPanLeft : kPanRight); + _channels[i].pitch = 0x2000; } - _bank.size = READ_BE_UINT16(header + 38); - strncpy(_bank.name, (char *) header + 8, 29); - _bank.name[29] = 0; -#ifdef DEBUG - printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name); -#endif - - for (uint i = 0; i < _bank.size; i++) { - int id; - Instrument *instrument = readInstrument(file, &id); + Common::File file; - if (!instrument) { - warning("[sfx:seq:amiga] failed to read bank.001"); + if (file.open("bank.001")) { + if (!loadInstrumentsSCI0(file)) { + file.close(); return Common::kUnknownError; } + file.close(); + } else { + ResourceManager *resMan = g_sci->getResMan(); - if (id < 0 || id > 255) { - warning("[sfx:seq:amiga] Error: instrument ID out of bounds"); + Resource *resource = resMan->findResource(ResourceId(kResourceTypePatch, 7), false); + if (!resource) + resource = resMan->findResource(ResourceId(kResourceTypePatch, 9), false); + + // If we have a patch by this point, it's SCI1 + if (resource) + _isSci1 = true; + + // Check for the SCI0 Mac patch + if (!resource) + resource = resMan->findResource(ResourceId(kResourceTypePatch, 200), false); + + if (!resource) { + warning("Could not open patch for Amiga sound driver"); return Common::kUnknownError; } - _bank.instruments[id] = instrument; - } + Common::MemoryReadStream stream(resource->data, resource->size); + if (_isSci1) { + if (!loadInstrumentsSCI1(stream)) + return Common::kUnknownError; + } else if (!loadInstrumentsSCI0Mac(stream)) + return Common::kUnknownError; + } + MidiDriver_Emulated::open(); _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); @@ -546,28 +621,30 @@ int MidiDriver_Amiga::open() { return Common::kNoError; } -void MidiDriver_Amiga::close() { +void MidiDriver_AmigaMac::close() { _mixer->stopHandle(_mixerSoundHandle); for (uint i = 0; i < _bank.size; i++) { - if (_bank.instruments[i]) { - if (_bank.instruments[i]->loop) - free(_bank.instruments[i]->loop); - free(_bank.instruments[i]->samples); - delete _bank.instruments[i]; + for (uint32 j = 0; j < _bank.instruments[i].size(); j++) { + if (_bank.instruments[i][j]) { + if (_bank.instruments[i][j]->loop) + free(_bank.instruments[i][j]->loop); + free(_bank.instruments[i][j]->samples); + delete _bank.instruments[i][j]; + } } } } -void MidiDriver_Amiga::playSwitch(bool play) { +void MidiDriver_AmigaMac::playSwitch(bool play) { _playSwitch = play; } -void MidiDriver_Amiga::setVolume(byte volume_) { +void MidiDriver_AmigaMac::setVolume(byte volume_) { _masterVolume = volume_; } -void MidiDriver_Amiga::send(uint32 b) { +void MidiDriver_AmigaMac::send(uint32 b) { byte command = b & 0xf0; byte channel = b & 0xf; byte op1 = (b >> 8) & 0xff; @@ -603,12 +680,15 @@ void MidiDriver_Amiga::send(uint32 b) { case 0xc0: changeInstrument(channel, op1); break; + case 0xe0: + pitchWheel(channel, (op2 << 7) | op1); + break; default: warning("[sfx:seq:amiga] unknown event %02x", command); } } -void MidiDriver_Amiga::generateSamples(int16 *data, int len) { +void MidiDriver_AmigaMac::generateSamples(int16 *data, int len) { if (len == 0) return; @@ -651,24 +731,256 @@ void MidiDriver_Amiga::generateSamples(int16 *data, int len) { free(buffers); } -class MidiPlayer_Amiga : public MidiPlayer { +bool MidiDriver_AmigaMac::loadInstrumentsSCI0(Common::File &file) { + _isSci1 = false; + + byte header[40]; + + if (file.read(header, 40) < 40) { + warning("[sfx:seq:amiga] failed to read header of file bank.001"); + return false; + } + + _bank.size = READ_BE_UINT16(header + 38); + strncpy(_bank.name, (char *) header + 8, 29); + _bank.name[29] = 0; +#ifdef DEBUG + printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name); +#endif + + for (uint i = 0; i < _bank.size; i++) { + int id; + InstrumentSample *instrument = readInstrumentSCI0(file, &id); + + if (!instrument) { + warning("[sfx:seq:amiga] failed to read bank.001"); + return false; + } + + if (id < 0 || id > 255) { + warning("[sfx:seq:amiga] Error: instrument ID out of bounds"); + return false; + } + + if ((uint)id >= _bank.instruments.size()) + _bank.instruments.resize(id + 1); + + _bank.instruments[id].push_back(instrument); + memcpy(_bank.instruments[id].name, instrument->name, sizeof(instrument->name)); + } + + return true; +} + +bool MidiDriver_AmigaMac::loadInstrumentsSCI0Mac(Common::SeekableReadStream &file) { + byte header[40]; + + if (file.read(header, 40) < 40) { + warning("[sfx:seq:amiga] failed to read header of file patch.200"); + return false; + } + + _bank.size = 128; + strncpy(_bank.name, (char *) header + 8, 29); + _bank.name[29] = 0; +#ifdef DEBUG + printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name); +#endif + + Common::Array<uint32> instrumentOffsets; + instrumentOffsets.resize(_bank.size); + _bank.instruments.resize(_bank.size); + + for (uint32 i = 0; i < _bank.size; i++) + instrumentOffsets[i] = file.readUint32BE(); + + for (uint i = 0; i < _bank.size; i++) { + // 0 signifies it doesn't exist + if (instrumentOffsets[i] == 0) + continue; + + file.seek(instrumentOffsets[i]); + + uint16 id = file.readUint16BE(); + if (id != i) + error("Instrument number mismatch"); + + InstrumentSample *instrument = new InstrumentSample; + + instrument->startNote = 0; + instrument->endNote = 127; + instrument->isUnsigned = true; + instrument->baseFreq = kBaseFreq; + instrument->baseNote = 101; + instrument->fixedNote = 101; + instrument->mode = file.readUint16BE(); + + // Read in the offsets + int32 seg_size[3]; + seg_size[0] = file.readUint32BE(); + seg_size[1] = file.readUint32BE(); + seg_size[2] = file.readUint32BE(); + + instrument->transpose = file.readUint16BE(); + + for (byte j = 0; j < 4; j++) { + int length = (int8)file.readByte(); + + if (length == 0 && j > 0) + length = 256; + + instrument->envelope[j].length = length * _frequency / 60; + instrument->envelope[j].delta = (int8)file.readByte(); + instrument->envelope[j].target = file.readByte(); + } + + // Final target must be 0 + instrument->envelope[3].target = 0; + + file.read(instrument->name, 30); + + if (instrument->mode & kModePitch) + instrument->fixedNote = -1; + + uint32 size = seg_size[2]; + uint32 loop_offset = seg_size[0]; + + instrument->samples = (int8 *)malloc(size + 1); + if (file.read(instrument->samples, size) < size) { + warning("[sfx:seq:amiga] failed to read instrument sample"); + free(instrument->samples); + delete instrument; + continue; + } + + if (instrument->mode & kModeLoop) { + instrument->size = seg_size[0]; + instrument->loop_size = seg_size[1] - seg_size[0]; + + instrument->loop = (int8*)malloc(instrument->loop_size + 1); + memcpy(instrument->loop, instrument->samples + loop_offset, instrument->loop_size); + + instrument->samples[instrument->size] = instrument->loop[0]; + instrument->loop[instrument->loop_size] = instrument->loop[0]; + } else { + instrument->loop = NULL; + instrument->loop_size = 0; + instrument->size = size; + instrument->samples[instrument->size] = (int8)0x80; + } + + _bank.instruments[id].push_back(instrument); + memcpy(_bank.instruments[id].name, instrument->name, sizeof(instrument->name)); + } + + return true; +} + +bool MidiDriver_AmigaMac::loadInstrumentsSCI1(Common::SeekableReadStream &file) { + _bank.size = 128; + + Common::Array<uint32> instrumentOffsets; + instrumentOffsets.resize(_bank.size); + _bank.instruments.resize(_bank.size); + + for (uint32 i = 0; i < _bank.size; i++) + instrumentOffsets[i] = file.readUint32BE(); + + for (uint32 i = 0; i < _bank.size; i++) { + // 0 signifies it doesn't exist + if (instrumentOffsets[i] == 0) + continue; + + file.seek(instrumentOffsets[i]); + + // Read in the instrument name + file.read(_bank.instruments[i].name, 10); // last two bytes are always 0 + + for (uint32 j = 0; ; j++) { + InstrumentSample *sample = new InstrumentSample; + memset(sample, 0, sizeof(InstrumentSample)); + + sample->startNote = file.readSint16BE(); + + // startNote being -1 signifies we're done with this instrument + if (sample->startNote == -1) { + delete sample; + break; + } + + sample->endNote = file.readSint16BE(); + uint32 samplePtr = file.readUint32BE(); + sample->transpose = file.readSint16BE(); + for (int env = 0; env < 3; env++) { + sample->envelope[env].length = file.readByte() * _frequency / 60; + sample->envelope[env].delta = (env == 0 ? 10 : -10); + sample->envelope[env].target = file.readByte(); + } + + sample->envelope[3].length = 0; + sample->fixedNote = file.readSint16BE(); + int16 loop = file.readSint16BE(); + uint32 nextSamplePos = file.pos(); + + file.seek(samplePtr); + file.read(sample->name, 8); + + sample->isUnsigned = file.readUint16BE() == 0; + uint16 phase1Offset = file.readUint16BE(); + uint16 phase1End = file.readUint16BE(); + uint16 phase2Offset = file.readUint16BE(); + uint16 phase2End = file.readUint16BE(); + sample->baseNote = file.readUint16BE(); + uint32 periodTableOffset = file.readUint32BE(); + uint32 sampleDataPos = file.pos(); + + sample->size = phase1End - phase1Offset + 1; + sample->loop_size = phase2End - phase2Offset + 1; + + sample->samples = (int8 *)malloc(sample->size + 1); + file.seek(phase1Offset + sampleDataPos); + file.read(sample->samples, sample->size); + sample->samples[sample->size] = (sample->isUnsigned ? (int8)0x80 : 0); + + if (loop == 0 && sample->loop_size > 1) { + sample->loop = (int8 *)malloc(sample->loop_size + 1); + file.seek(phase2Offset + sampleDataPos); + file.read(sample->loop, sample->loop_size); + sample->mode |= kModeLoop; + sample->samples[sample->size] = sample->loop[0]; + sample->loop[sample->loop_size] = sample->loop[0]; + } + + _bank.instruments[i].push_back(sample); + + file.seek(periodTableOffset + 0xe0); + sample->baseFreq = file.readUint16BE(); + + file.seek(nextSamplePos); + } + } + + return true; +} + +class MidiPlayer_AmigaMac : public MidiPlayer { public: - MidiPlayer_Amiga(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_Amiga(g_system->getMixer()); } + MidiPlayer_AmigaMac(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_AmigaMac(g_system->getMixer()); } byte getPlayId(); - int getPolyphony() const { return MidiDriver_Amiga::kVoices; } + int getPolyphony() const { return MidiDriver_AmigaMac::kVoices; } bool hasRhythmChannel() const { return false; } - void setVolume(byte volume) { static_cast<MidiDriver_Amiga *>(_driver)->setVolume(volume); } - void playSwitch(bool play) { static_cast<MidiDriver_Amiga *>(_driver)->playSwitch(play); } + void setVolume(byte volume) { static_cast<MidiDriver_AmigaMac *>(_driver)->setVolume(volume); } + void playSwitch(bool play) { static_cast<MidiDriver_AmigaMac *>(_driver)->playSwitch(play); } void loadInstrument(int idx, byte *data); }; -MidiPlayer *MidiPlayer_Amiga_create(SciVersion version) { - return new MidiPlayer_Amiga(version); +MidiPlayer *MidiPlayer_AmigaMac_create(SciVersion version) { + return new MidiPlayer_AmigaMac(version); } -byte MidiPlayer_Amiga::getPlayId() { - if (_version != SCI_VERSION_0_LATE) - error("Amiga sound support not available for this SCI version"); +byte MidiPlayer_AmigaMac::getPlayId() { + if (_version > SCI_VERSION_0_LATE) + return 0x06; return 0x40; } diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h index 58803db260..2db6f25c70 100644 --- a/engines/sci/sound/drivers/mididriver.h +++ b/engines/sci/sound/drivers/mididriver.h @@ -86,7 +86,7 @@ public: virtual byte getPlayId() = 0; virtual int getPolyphony() const = 0; - virtual int getFirstChannel() { return 0; }; + virtual int getFirstChannel() { return 0; } virtual void setVolume(byte volume) { if(_driver) @@ -113,7 +113,7 @@ protected: }; extern MidiPlayer *MidiPlayer_AdLib_create(SciVersion version); -extern MidiPlayer *MidiPlayer_Amiga_create(SciVersion version); +extern MidiPlayer *MidiPlayer_AmigaMac_create(SciVersion version); extern MidiPlayer *MidiPlayer_PCJr_create(SciVersion version); extern MidiPlayer *MidiPlayer_PCSpeaker_create(SciVersion version); extern MidiPlayer *MidiPlayer_Midi_create(SciVersion version); diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index cc09ba79f0..e58fa5120b 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -475,7 +475,7 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { // on tick 0. Signal isn't set at that point by sierra sci and it would cause the castle daventry text to // get immediately removed, so we currently filter it. // Sierra SCI ignores them as well at that time - if (_position._play_tick) { + if ((_position._play_tick) || (info.delta)) { _signalSet = true; _signalToSet = info.basic.param1; } @@ -516,10 +516,11 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { break; case SCI_VERSION_1_EARLY: case SCI_VERSION_1_LATE: + case SCI_VERSION_2_1: _dataincToAdd = 1; break; default: - break; + error("unsupported _soundVersion"); } break; case kResetOnPause: @@ -672,6 +673,7 @@ void MidiParser_SCI::setVolume(byte volume) { case SCI_VERSION_1_EARLY: case SCI_VERSION_1_LATE: + case SCI_VERSION_2_1: // Send previous channel volumes again to actually update the volume for (int i = 0; i < 15; i++) if (_channelRemap[i] != -1) diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 55a7e1fdc4..c3315bd2b5 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -63,23 +63,15 @@ void SciMusic::init() { // SCI sound init _dwTempo = 0; - // Default to MIDI in SCI32 games, as many don't have AdLib support. - // WORKAROUND: Default to MIDI in Amiga SCI1_EGA+ games as we don't support - // those patches yet. We also don't yet support the 7.pat file of SCI1+ Mac - // games or SCI0 Mac patches, so we default to MIDI in those games to let - // them run. + // Default to MIDI in SCI2.1+ games, as many don't have AdLib support. Common::Platform platform = g_sci->getPlatform(); - uint32 dev = MidiDriver::detectDevice( - (getSciVersion() >= SCI_VERSION_2 || platform == Common::kPlatformMacintosh || - (platform == Common::kPlatformAmiga && getSciVersion() >= SCI_VERSION_1_EGA)) - ? (MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM) - : (MDT_PCSPK | MDT_ADLIB | MDT_MIDI)); + uint32 dev = MidiDriver::detectDevice((getSciVersion() >= SCI_VERSION_2_1) ? (MDT_PCSPK | MDT_PCJR | MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM) : (MDT_PCSPK | MDT_PCJR | MDT_ADLIB | MDT_MIDI)); switch (MidiDriver::getMusicType(dev)) { case MT_ADLIB: // FIXME: There's no Amiga sound option, so we hook it up to AdLib - if (g_sci->getPlatform() == Common::kPlatformAmiga) - _pMidiDrv = MidiPlayer_Amiga_create(_soundVersion); + if (g_sci->getPlatform() == Common::kPlatformAmiga || platform == Common::kPlatformMacintosh) + _pMidiDrv = MidiPlayer_AmigaMac_create(_soundVersion); else _pMidiDrv = MidiPlayer_AdLib_create(_soundVersion); break; @@ -410,6 +402,8 @@ void SciMusic::soundStop(MusicEntry *pSnd) { pSnd->pMidiParser->mainThreadEnd(); _mutex.unlock(); } + + pSnd->fadeStep = 0; // end fading, if fading was in progress } void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) { @@ -604,7 +598,7 @@ MusicEntry::MusicEntry() { priority = 0; loop = 0; volume = MUSIC_VOLUME_DEFAULT; - hold = 0; + hold = -1; pauseCounter = 0; sampleLoopCounter = 0; diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h index 943a5bd2a8..37e3c30030 100644 --- a/engines/sci/sound/music.h +++ b/engines/sci/sound/music.h @@ -71,7 +71,7 @@ public: byte priority; uint16 loop; int16 volume; - byte hold; + int16 hold; int16 pauseCounter; uint sampleLoopCounter; diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index 51832af09f..bd88a5fca8 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -33,9 +33,6 @@ namespace Sci { -#define SCI1_SOUND_FLAG_MAY_PAUSE 1 /* Only here for completeness; The interpreter doesn't touch this bit */ -#define SCI1_SOUND_FLAG_SCRIPTED_PRI 2 /* but does touch this */ - SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, AudioPlayer *audio, SciVersion soundVersion) : _resMan(resMan), _segMan(segMan), _kernel(kernel), _audio(audio), _soundVersion(soundVersion) { @@ -48,6 +45,7 @@ SoundCommandParser::~SoundCommandParser() { } reg_t SoundCommandParser::kDoSoundInit(int argc, reg_t *argv, reg_t acc) { + debugC(2, kDebugLevelSound, "kDoSound(init): %04x:%04x", PRINT_REG(argv[0])); processInitSound(argv[0]); return acc; } @@ -73,8 +71,8 @@ void SoundCommandParser::processInitSound(reg_t obj) { if (_soundVersion >= SCI_VERSION_1_EARLY) newSound->volume = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX); - debugC(2, kDebugLevelSound, "kDoSound(init): number %d, loop %d, prio %d, vol %d", resourceId, - newSound->loop, newSound->priority, newSound->volume); + debugC(2, kDebugLevelSound, "kDoSound(init): %04x:%04x number %d, loop %d, prio %d, vol %d", PRINT_REG(obj), + resourceId, newSound->loop, newSound->priority, newSound->volume); // In SCI1.1 games, sound effects are started from here. If we can find // a relevant audio resource, play it, otherwise switch to synthesized @@ -105,6 +103,7 @@ void SoundCommandParser::processInitSound(reg_t obj) { } reg_t SoundCommandParser::kDoSoundPlay(int argc, reg_t *argv, reg_t acc) { + debugC(2, kDebugLevelSound, "kDoSound(play): %04x:%04x", PRINT_REG(argv[0])); processPlaySound(argv[0]); return acc; } @@ -142,8 +141,8 @@ void SoundCommandParser::processPlaySound(reg_t obj) { if (_soundVersion >= SCI_VERSION_1_EARLY) musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol)); - debugC(2, kDebugLevelSound, "kDoSound(play): number %d, loop %d, prio %d, vol %d", resourceId, - musicSlot->loop, musicSlot->priority, musicSlot->volume); + debugC(2, kDebugLevelSound, "kDoSound(play): %04x:%04x number %d, loop %d, prio %d, vol %d", PRINT_REG(obj), + resourceId, musicSlot->loop, musicSlot->priority, musicSlot->volume); _music->soundPlay(musicSlot); } @@ -154,6 +153,7 @@ reg_t SoundCommandParser::kDoSoundDummy(int argc, reg_t *argv, reg_t acc) { } reg_t SoundCommandParser::kDoSoundDispose(int argc, reg_t *argv, reg_t acc) { + debugC(2, kDebugLevelSound, "kDoSound(dispose): %04x:%04x", PRINT_REG(argv[0])); processDisposeSound(argv[0]); return acc; } @@ -176,6 +176,7 @@ void SoundCommandParser::processDisposeSound(reg_t obj) { } reg_t SoundCommandParser::kDoSoundStop(int argc, reg_t *argv, reg_t acc) { + debugC(2, kDebugLevelSound, "kDoSound(stop): %04x:%04x", PRINT_REG(argv[0])); processStopSound(argv[0], false); return acc; } @@ -209,6 +210,11 @@ void SoundCommandParser::processStopSound(reg_t obj, bool sampleFinishedPlaying) } reg_t SoundCommandParser::kDoSoundPause(int argc, reg_t *argv, reg_t acc) { + if (argc == 1) + debugC(2, kDebugLevelSound, "kDoSound(pause): %04x:%04x", PRINT_REG(argv[0])); + else + debugC(2, kDebugLevelSound, "kDoSound(pause): %04x:%04x, %04x:%04x", PRINT_REG(argv[0]), PRINT_REG(argv[1])); + if (_soundVersion <= SCI_VERSION_0_LATE) { // SCI0 games give us 0/1 for either resuming or pausing the current music // this one doesn't count, so pausing 2 times and resuming once means here that we are supposed to resume @@ -250,26 +256,18 @@ reg_t SoundCommandParser::kDoSoundPause(int argc, reg_t *argv, reg_t acc) { } // SCI0 only command -reg_t SoundCommandParser::kDoSoundResume(int argc, reg_t *argv, reg_t acc) { - // this doesn't seem to do what we think it's doing - // it's called with no arguments at all (just restore a game in qfg1) - return acc; - reg_t obj = argv[0]; - - MusicEntry *musicSlot = _music->getSlot(obj); - if (!musicSlot) { - warning("kDoSound(resume):: Slot not found (%04x:%04x)", PRINT_REG(obj)); - return acc; - } - - writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying); - _music->soundResume(musicSlot); +// It's called right after restoring a game - it's responsible to kick off playing music again +// we don't need this at all, so we don't do anything here +reg_t SoundCommandParser::kDoSoundResumeAfterRestore(int argc, reg_t *argv, reg_t acc) { return acc; } reg_t SoundCommandParser::kDoSoundMute(int argc, reg_t *argv, reg_t acc) { - if (argc > 0) + if (argc > 0) { + debugC(2, kDebugLevelSound, "kDoSound(mute): %d", argv[0].toUint16()); _music->soundSetSoundOn(argv[0].toUint16()); + } + return make_reg(0, _music->soundGetSoundOn()); } @@ -300,7 +298,7 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) { // If sound is not playing currently, set signal directly if (musicSlot->status != kSoundPlaying) { - debugC(2, kDebugLevelSound, "kDoSound(fade): fading requested, but sound is currently not playing"); + debugC(2, kDebugLevelSound, "kDoSound(fade): %04x:%04x fading requested, but sound is currently not playing", PRINT_REG(obj)); writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); return acc; } @@ -328,7 +326,7 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) { error("kDoSound(fade): unsupported argc %d", argc); } - debugC(2, kDebugLevelSound, "kDoSound(fade): to %d, step %d, ticker %d", musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep); + debugC(2, kDebugLevelSound, "kDoSound(fade): %04x:%04x to %d, step %d, ticker %d", PRINT_REG(obj), musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep); return acc; } @@ -339,6 +337,8 @@ reg_t SoundCommandParser::kDoSoundGetPolyphony(int argc, reg_t *argv, reg_t acc) reg_t SoundCommandParser::kDoSoundUpdate(int argc, reg_t *argv, reg_t acc) { reg_t obj = argv[0]; + debugC(2, kDebugLevelSound, "kDoSound(update): %04x:%04x", PRINT_REG(argv[0])); + MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { warning("kDoSound(update): Slot not found (%04x:%04x)", PRINT_REG(obj)); @@ -379,9 +379,13 @@ void SoundCommandParser::processUpdateCues(reg_t obj) { musicSlot->loop -= currentLoopCounter - musicSlot->sampleLoopCounter; musicSlot->sampleLoopCounter = currentLoopCounter; } - if ((!_music->soundIsActive(musicSlot)) && (musicSlot->status != kSoundPaused)) { - processStopSound(obj, true); - } else { + if (musicSlot->status == kSoundPlaying) { + if (!_music->soundIsActive(musicSlot)) { + processStopSound(obj, true); + } else { + _music->updateAudioStreamTicker(musicSlot); + } + } else if (musicSlot->status == kSoundPaused) { _music->updateAudioStreamTicker(musicSlot); } // We get a flag from MusicEntry::doFade() here to set volume for the stream @@ -443,9 +447,15 @@ reg_t SoundCommandParser::kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc) { byte channel = argv[1].toUint16() & 0xf; byte midiCmd = argv[2].toUint16() & 0xff; + // TODO: first there is a 4-parameter variant of this call which needs to get reversed + // second the current code isn't 100% accurate, sierra sci does checks on the 4th parameter + if (argc == 4) + return acc; + uint16 controller = argv[3].toUint16(); uint16 param = argv[4].toUint16(); + debugC(2, kDebugLevelSound, "kDoSound(sendMidi): %04x:%04x, %d, %d, %d, %d", PRINT_REG(obj), channel, midiCmd, controller, param); if (channel) channel--; // channel is given 1-based, we are using 0-based @@ -464,6 +474,7 @@ reg_t SoundCommandParser::kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc) { } reg_t SoundCommandParser::kDoSoundReverb(int argc, reg_t *argv, reg_t acc) { + debugC(2, kDebugLevelSound, "doSoundReverb: %d", argv[0].toUint16() & 0xF); _music->setReverb(argv[0].toUint16() & 0xF); return acc; } @@ -471,6 +482,8 @@ reg_t SoundCommandParser::kDoSoundReverb(int argc, reg_t *argv, reg_t acc) { reg_t SoundCommandParser::kDoSoundSetHold(int argc, reg_t *argv, reg_t acc) { reg_t obj = argv[0]; + debugC(2, kDebugLevelSound, "doSoundSetHold: %04x:%04x, %d", PRINT_REG(argv[0]), argv[1].toUint16()); + MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { warning("kDoSound(setHold): Slot not found (%04x:%04x)", PRINT_REG(obj)); @@ -488,6 +501,11 @@ reg_t SoundCommandParser::kDoSoundGetAudioCapability(int argc, reg_t *argv, reg_ } reg_t SoundCommandParser::kDoSoundStopAll(int argc, reg_t *argv, reg_t acc) { + // TODO: this can't be right, this gets called in kq1 - e.g. being in witch house, getting the note + // now the point jingle plays and after a messagebox they call this - and would stop the background effects with it + // this doesn't make sense, so i disable it for now + return acc; + Common::StackLock(_music->_mutex); const MusicList::iterator end = _music->getPlayListEnd(); @@ -535,6 +553,8 @@ reg_t SoundCommandParser::kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc) reg_t obj = argv[0]; int16 value = argv[1].toSint16(); + debugC(2, kDebugLevelSound, "kDoSound(setPriority): %04x:%04x, %d", PRINT_REG(obj), value); + MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { warning("kDoSound(setPriority): Slot not found (%04x:%04x)", PRINT_REG(obj)); @@ -565,6 +585,8 @@ reg_t SoundCommandParser::kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc) { reg_t obj = argv[0]; int16 value = argv[1].toSint16(); + debugC(2, kDebugLevelSound, "kDoSound(setLoop): %04x:%04x, %d", PRINT_REG(obj), value); + MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { // Apparently, it's perfectly normal for a game to call cmdSetSoundLoop diff --git a/engines/sci/sound/soundcmd.h b/engines/sci/sound/soundcmd.h index a8bc1eb280..10915e8ea9 100644 --- a/engines/sci/sound/soundcmd.h +++ b/engines/sci/sound/soundcmd.h @@ -85,7 +85,7 @@ public: reg_t kDoSoundDummy(int argc, reg_t *argv, reg_t acc); reg_t kDoSoundMute(int argc, reg_t *argv, reg_t acc); reg_t kDoSoundPause(int argc, reg_t *argv, reg_t acc); - reg_t kDoSoundResume(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundResumeAfterRestore(int argc, reg_t *argv, reg_t acc); reg_t kDoSoundStop(int argc, reg_t *argv, reg_t acc); reg_t kDoSoundStopAll(int argc, reg_t *argv, reg_t acc); reg_t kDoSoundDispose(int argc, reg_t *argv, reg_t acc); diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp index ea29e25a1f..b5a4070f0b 100644 --- a/engines/scumm/debugger.cpp +++ b/engines/scumm/debugger.cpp @@ -62,8 +62,6 @@ ScummDebugger::ScummDebugger(ScummEngine *s) _vm = s; // Register variables - DVar_Register("debug_countdown", &_frame_countdown, DVAR_INT, 0); - DVar_Register("scumm_speed", &_vm->_fastMode, DVAR_BYTE, 0); DVar_Register("scumm_room", &_vm->_currentRoom, DVAR_BYTE, 0); DVar_Register("scumm_roomresource", &_vm->_roomResource, DVAR_INT, 0); @@ -128,7 +126,7 @@ void ScummDebugger::postEnter() { bool ScummDebugger::Cmd_Restart(int argc, const char **argv) { _vm->restart(); - _detach_now = true; + detach(); return false; } @@ -202,7 +200,7 @@ bool ScummDebugger::Cmd_LoadGame(int argc, const char **argv) { _vm->requestLoad(slot); - _detach_now = true; + detach(); return false; } @@ -867,7 +865,7 @@ bool ScummDebugger::Cmd_Passcode(int argc, const char **argv) { } _vm->_bootParam = 0; - _detach_now = true; + detach(); } else { DebugPrintf("Current Passcode is %d \nUse 'passcode <SEGA CD Passcode>'\n",_vm->_scummVars[411]); @@ -878,9 +876,7 @@ bool ScummDebugger::Cmd_Passcode(int argc, const char **argv) { bool ScummDebugger::Cmd_ResetCursors(int argc, const char **argv) { _vm->resetCursors(); - - _detach_now = true; - + detach(); return false; } diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp index 298917477f..7b0d4909d6 100644 --- a/engines/scumm/gfx.cpp +++ b/engines/scumm/gfx.cpp @@ -1482,7 +1482,7 @@ void GdiV2::prepareDrawBitmap(const byte *ptr, VirtScreen *vs, // Since V3, all graphics data was encoded in strips, which is very efficient // for redrawing only parts of the screen. However, V2 is different: here // the whole graphics are encoded as one big chunk. That makes it rather - // dificult to draw only parts of a room/object. We handle the V2 graphics + // difficult to draw only parts of a room/object. We handle the V2 graphics // differently from all other (newer) graphic formats for this reason. // StripTable *table = (_objectMode ? 0 : _roomStrips); @@ -2823,7 +2823,7 @@ void GdiPCEngine::decodePCEngineObject(const byte *ptr, int xpos, int ypos, int free(stripOffsets); } -void setTileData(byte *tile, int index, byte byte0, byte byte1) { +void GdiPCEngine::setTileData(byte *tile, int index, byte byte0, byte byte1) { int row = index % 8; int plane = (index / 8) * 2; int plane02Bit, plane13Bit; diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h index 108fd4555d..cdb473a67c 100644 --- a/engines/scumm/gfx.h +++ b/engines/scumm/gfx.h @@ -322,6 +322,7 @@ protected: protected: void decodePCEngineGfx(const byte *room); void decodeStrip(const byte *ptr, uint16 *tiles, byte *colors, uint16 *masks, int numRows, bool isObject); + void setTileData(byte *tile, int index, byte byte0, byte byte1); void decodePCEngineTileData(const byte *ptr); void decodePCEngineMaskData(const byte *ptr); void decodePCEngineObject(const byte *ptr, int xpos, int ypos, int width, int height); diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 86ec8c4748..7e2db22c5b 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -1840,8 +1840,7 @@ Common::Error ScummEngine::go() { while (!shouldQuit()) { - if (_debugger->isAttached()) - _debugger->onFrame(); + _debugger->onFrame(); // Randomize the PRNG by calling it at regular intervals. This ensures // that it will be in a different state each time you run the program. diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index 62fdf48440..30281cb565 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -1110,6 +1110,8 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize) } num += (_game.version == 8) ? 4 : 2; } + } else if (_game.id == GID_DIG && (chr == 1 || chr == 2 || chr == 3 || chr == 8)) { + // Skip these characters } else { if (!(chr == '@') || (_game.id == GID_CMI && _language == Common::ZH_TWN) || (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN)) diff --git a/engines/sky/sky.cpp b/engines/sky/sky.cpp index 45b3cab947..edf96f8e8c 100644 --- a/engines/sky/sky.cpp +++ b/engines/sky/sky.cpp @@ -185,8 +185,7 @@ Common::Error SkyEngine::go() { uint32 delayCount = _system->getMillis(); while (!shouldQuit()) { - if (_debugger->isAttached()) - _debugger->onFrame(); + _debugger->onFrame(); if (shouldPerformAutoSave(_lastSaveTime)) { if (_skyControl->loadSaveAllowed()) { diff --git a/engines/sword1/memman.h b/engines/sword1/memman.h index 845a8638a2..b489eae2f9 100644 --- a/engines/sword1/memman.h +++ b/engines/sword1/memman.h @@ -42,11 +42,7 @@ struct MemHandle { #define MEM_CAN_FREE 1 #define MEM_DONT_FREE 2 -#ifdef PALMOS_MODE -#define MAX_ALLOC (3*1024*1024) // max amount of mem we want to alloc(). -#else #define MAX_ALLOC (6*1024*1024) // max amount of mem we want to alloc(). -#endif class MemMan { public: diff --git a/engines/sword2/anims.cpp b/engines/sword2/anims.cpp index 1bf3967047..7fd36fcc86 100644 --- a/engines/sword2/anims.cpp +++ b/engines/sword2/anims.cpp @@ -56,8 +56,6 @@ int Router::doAnimate(byte *ob_logic, byte *ob_graph, int32 animRes, bool revers ObjectGraphic obGraph(ob_graph); if (obLogic.getLooping() == 0) { - byte *ptr; - // This is the start of the anim - set up the first frame // For testing all anims! @@ -75,12 +73,8 @@ int Router::doAnimate(byte *ob_logic, byte *ob_graph, int32 animRes, bool revers return IR_STOP; } - ptr = _vm->_resman->openResource(animRes); - // if it's not an animation file if (_vm->_resman->fetchType(animRes) != ANIMATION_FILE) { - _vm->_resman->closeResource(animRes); - // switch off the sprite // don't animate - just continue // script next cycle @@ -88,8 +82,6 @@ int Router::doAnimate(byte *ob_logic, byte *ob_graph, int32 animRes, bool revers return IR_STOP; } - _vm->_resman->closeResource(animRes); - // switch on the sprite setSpriteStatus(ob_graph, SORT_SPRITE); } diff --git a/engines/sword2/music.cpp b/engines/sword2/music.cpp index a68baed097..c052aa6b46 100644 --- a/engines/sword2/music.cpp +++ b/engines/sword2/music.cpp @@ -136,10 +136,8 @@ static Audio::AudioStream *getAudioStream(SoundFileHandle *fh, const char *base, return NULL; } if (fh->fileSize != fh->file.size()) { - if (fh->idxTab) { - free(fh->idxTab); - fh->idxTab = NULL; - } + free(fh->idxTab); + fh->idxTab = NULL; } } else alreadyOpen = true; diff --git a/engines/sword2/resman.h b/engines/sword2/resman.h index 72bdf73b98..dcc79927ea 100644 --- a/engines/sword2/resman.h +++ b/engines/sword2/resman.h @@ -32,11 +32,7 @@ namespace Common { class File; } -#ifdef PALMOS_MODE -#define MAX_MEM_CACHE (4 * 1024 * 1024) // 4 seems to be enough, 8 = out of memory -#else #define MAX_MEM_CACHE (8 * 1024 * 1024) // we keep up to 8 megs of resource data files in memory -#endif #define MAX_res_files 20 namespace Sword2 { diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp index 29f567d7ef..3cdab2bd2b 100644 --- a/engines/sword2/sword2.cpp +++ b/engines/sword2/sword2.cpp @@ -326,12 +326,20 @@ void Sword2Engine::registerDefaultSettings() { } void Sword2Engine::syncSoundSettings() { - // Sound settings. At the time of writing, not all of these can be set - // by the global options dialog, but it seems silly to split them up. _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); setSubtitles(ConfMan.getBool("subtitles")); + + // Our own settings dialog can mute the music, speech and sound effects + // individually. ScummVM's settings dialog has one master mute setting. + + if (ConfMan.getBool("mute")) { + ConfMan.setBool("music_mute", true); + ConfMan.setBool("speech_mute", true); + ConfMan.setBool("sfx_mute", true); + } + _sound->muteMusic(ConfMan.getBool("music_mute")); _sound->muteSpeech(ConfMan.getBool("speech_mute")); _sound->muteFx(ConfMan.getBool("sfx_mute")); @@ -356,6 +364,13 @@ void Sword2Engine::writeSettings() { ConfMan.setBool("object_labels", _mouse->getObjectLabels()); ConfMan.setInt("reverse_stereo", _sound->isReverseStereo()); + // If even one sound type is unmuted, we can't say that all sound is + // muted. + + if (!_sound->isMusicMute() || !_sound->isSpeechMute() || !_sound->isFxMute()) { + ConfMan.setBool("mute", false); + } + ConfMan.flushToDisk(); } @@ -458,8 +473,7 @@ Common::Error Sword2Engine::run() { _screen->initialiseRenderCycle(); while (1) { - if (_debugger->isAttached()) - _debugger->onFrame(); + _debugger->onFrame(); #ifdef SWORD2_DEBUG if (_stepOneCycle) { diff --git a/engines/teenagent/module.mk b/engines/teenagent/module.mk index 2c04c99376..01ba3c79cb 100644 --- a/engines/teenagent/module.mk +++ b/engines/teenagent/module.mk @@ -1,23 +1,23 @@ MODULE := engines/teenagent MODULE_OBJS := \ + actor.o \ + animation.o \ + callbacks.o \ + console.o \ detection.o \ - teenagent.o \ - resources.o \ + dialog.o \ + font.o \ + inventory.o \ + music.o \ + objects.o \ pack.o \ - segment.o \ + resources.o \ scene.o \ - animation.o \ - font.o \ + segment.o \ surface.o \ surface_list.o \ - actor.o \ - callbacks.o \ - inventory.o \ - objects.o \ - music.o \ - console.o \ - dialog.o + teenagent.o # This module can be built as a plugin ifeq ($(ENABLE_TEENAGENT), DYNAMIC_PLUGIN) diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp index 838275670b..f1527fc78d 100644 --- a/engines/teenagent/teenagent.cpp +++ b/engines/teenagent/teenagent.cpp @@ -617,9 +617,7 @@ Common::Error TeenAgentEngine::run() { _system->updateScreen(); - if (console->isAttached()) { - console->onFrame(); - } + console->onFrame(); uint32 next_tick = MIN(game_timer, mark_timer); if (next_tick > 0) { diff --git a/engines/tinsel/actors.cpp b/engines/tinsel/actors.cpp index efcefb442c..3577f4e0cc 100644 --- a/engines/tinsel/actors.cpp +++ b/engines/tinsel/actors.cpp @@ -197,10 +197,8 @@ void RegisterActors(int num) { } void FreeActors() { - if (actorInfo) { - free(actorInfo); - actorInfo = NULL; - } + free(actorInfo); + actorInfo = NULL; } /** diff --git a/engines/tinsel/bmv.cpp b/engines/tinsel/bmv.cpp index 938507c3f9..b13de103c0 100644 --- a/engines/tinsel/bmv.cpp +++ b/engines/tinsel/bmv.cpp @@ -730,16 +730,12 @@ void BMVPlayer::FinishBMV() { stream.close(); // Release the data buffer - if (bigBuffer != NULL) { - free(bigBuffer); - bigBuffer = NULL; - } + free(bigBuffer); + bigBuffer = NULL; // Release the screen buffer - if (screenBuffer != NULL) { - free(screenBuffer); - screenBuffer = NULL; - } + free(screenBuffer); + screenBuffer = NULL; // Ditch any text objects for (i = 0; i < 2; i++) { diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp index 60eb08a2dd..fdc4484a7c 100644 --- a/engines/tinsel/handle.cpp +++ b/engines/tinsel/handle.cpp @@ -175,22 +175,18 @@ void SetupHandleTable() { } void FreeHandleTable() { - if (handleTable) { - free(handleTable); - handleTable = NULL; - } - if (cdGraphStream) { - delete cdGraphStream; - cdGraphStream = 0; - } + free(handleTable); + handleTable = NULL; + + delete cdGraphStream; + cdGraphStream = NULL; } /** * Loads a memory block as a file. */ void OpenCDGraphFile() { - if (cdGraphStream) - delete cdGraphStream; + delete cdGraphStream; // As the theory goes, the right CD will be in there! diff --git a/engines/tinsel/object.cpp b/engines/tinsel/object.cpp index f91e37a063..7a93a0b30a 100644 --- a/engines/tinsel/object.cpp +++ b/engines/tinsel/object.cpp @@ -49,10 +49,8 @@ static int maxObj = 0; #endif void FreeObjectList() { - if (objectList) { - free(objectList); - objectList = NULL; - } + free(objectList); + objectList = NULL; } /** diff --git a/engines/tinsel/savescn.cpp b/engines/tinsel/savescn.cpp index 50231d34bb..2b5c815d3c 100644 --- a/engines/tinsel/savescn.cpp +++ b/engines/tinsel/savescn.cpp @@ -162,10 +162,8 @@ void InitialiseSaveScenes() { } void FreeSaveScenes() { - if (ssData) { - free(ssData); - ssData = NULL; - } + free(ssData); + ssData = NULL; } /** diff --git a/engines/tinsel/strres.cpp b/engines/tinsel/strres.cpp index 8f9f72f446..2416d6a8fa 100644 --- a/engines/tinsel/strres.cpp +++ b/engines/tinsel/strres.cpp @@ -88,11 +88,9 @@ void ChangeLanguage(LANGUAGE newLang) { textLanguage = newLang; sampleLanguage = newLang; - if (textBuffer) { - // free the previous buffer - free(textBuffer); - textBuffer = NULL; - } + // free the previous buffer + free(textBuffer); + textBuffer = NULL; // Try and open the specified language file. If it fails, and the language // isn't English, try falling back on opening 'english.txt' - some foreign @@ -355,10 +353,8 @@ int SubStringCount(int id) { void FreeTextBuffer() { - if (textBuffer) { - free(textBuffer); - textBuffer = NULL; - } + free(textBuffer); + textBuffer = NULL; } /** diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp index 3a4191227a..dc706c82d9 100644 --- a/engines/tinsel/tinsel.cpp +++ b/engines/tinsel/tinsel.cpp @@ -1000,8 +1000,7 @@ Common::Error TinselEngine::run() { uint32 timerVal = 0; while (!shouldQuit()) { assert(_console); - if (_console->isAttached()) - _console->onFrame(); + _console->onFrame(); // Check for time to do next game cycle if ((g_system->getMillis() > timerVal + GAME_FRAME_DELAY)) { |