diff options
-rw-r--r-- | engines/sci/console.cpp | 12 | ||||
-rw-r--r-- | engines/sci/engine/features.cpp | 520 | ||||
-rw-r--r-- | engines/sci/engine/features.h | 121 | ||||
-rw-r--r-- | engines/sci/engine/game.cpp | 6 | ||||
-rw-r--r-- | engines/sci/engine/kernel32.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/kevent.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/kgraphics.cpp | 4 | ||||
-rw-r--r-- | engines/sci/engine/kmovement.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/ksound.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/savegame.cpp | 6 | ||||
-rw-r--r-- | engines/sci/engine/script.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/state.cpp | 483 | ||||
-rw-r--r-- | engines/sci/engine/state.h | 81 | ||||
-rw-r--r-- | engines/sci/engine/vm.cpp | 4 | ||||
-rw-r--r-- | engines/sci/module.mk | 1 | ||||
-rw-r--r-- | engines/sci/sci.cpp | 6 |
16 files changed, 674 insertions, 580 deletions
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 72eceb05a0..3348ed4973 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -417,11 +417,11 @@ bool Console::cmdGetVersion(int argc, const char **argv) { DebugPrintf("\n"); DebugPrintf("Detected features:\n"); DebugPrintf("------------------\n"); - DebugPrintf("Sound type: %s\n", getSciVersionDesc(s->detectDoSoundType()).c_str()); - DebugPrintf("Graphics functions type: %s\n", getSciVersionDesc(s->detectGfxFunctionsType()).c_str()); - DebugPrintf("Lofs type: %s\n", getSciVersionDesc(s->detectLofsType()).c_str()); - DebugPrintf("Move count type: %s\n", (s->detectMoveCountType() == kIncrementMoveCount) ? "increment" : "ignore"); - DebugPrintf("SetCursor type: %s\n", getSciVersionDesc(s->detectSetCursorType()).c_str()); + DebugPrintf("Sound type: %s\n", getSciVersionDesc(s->_features->detectDoSoundType()).c_str()); + DebugPrintf("Graphics functions type: %s\n", getSciVersionDesc(s->_features->detectGfxFunctionsType()).c_str()); + DebugPrintf("Lofs type: %s\n", getSciVersionDesc(s->_features->detectLofsType()).c_str()); + DebugPrintf("Move count type: %s\n", (s->_features->detectMoveCountType() == kIncrementMoveCount) ? "increment" : "ignore"); + DebugPrintf("SetCursor type: %s\n", getSciVersionDesc(s->_features->detectSetCursorType()).c_str()); DebugPrintf("View type: %s\n", viewTypeDesc[s->resMan->getViewType()]); DebugPrintf("Resource volume version: %s\n", s->resMan->getVolVersionDesc()); DebugPrintf("Resource map version: %s\n", s->resMan->getMapVersionDesc()); @@ -1592,7 +1592,7 @@ bool Console::cmdIsSample(int argc, const char **argv) { return true; } - SoundResource *soundRes = new SoundResource(number, _engine->getResourceManager(), _engine->_gamestate->detectDoSoundType()); + SoundResource *soundRes = new SoundResource(number, _engine->getResourceManager(), _engine->_gamestate->_features->detectDoSoundType()); if (!soundRes) { DebugPrintf("Not a sound resource!\n"); diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp new file mode 100644 index 0000000000..e5fcc673cf --- /dev/null +++ b/engines/sci/engine/features.cpp @@ -0,0 +1,520 @@ +/* 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/features.h" +#include "sci/engine/script.h" +#include "sci/engine/selector.h" +#include "sci/engine/vm.h" + +namespace Sci { + +GameFeatures::GameFeatures(SegManager *segMan, Kernel *kernel) : _segMan(segMan), _kernel(kernel) { + _setCursorType = SCI_VERSION_NONE; + _doSoundType = SCI_VERSION_NONE; + _lofsType = SCI_VERSION_NONE; + _gfxFunctionsType = SCI_VERSION_NONE; + _moveCountType = kMoveCountUninitialized; + +#ifdef ENABLE_SCI32 + _sci21KernelType = SCI_VERSION_NONE; +#endif + _usesCdTrack = Common::File::exists("cdaudio.map"); +} + +void GameFeatures::setGameInfo(reg_t gameObj, Common::String gameId) { + _gameObj = gameObj; + _gameId = gameId; +} + +bool GameFeatures::autoDetectFeature(FeatureDetection featureDetection, int methodNum) { + Common::String objName; + Selector slc = 0; + reg_t objAddr; + bool foundTarget = false; + + // Get address of target script + switch (featureDetection) { + case kDetectGfxFunctions: + objName = "Rm"; + objAddr = _segMan->findObjectByName(objName); + slc = _kernel->_selectorCache.overlay; + break; + case kDetectMoveCountType: + objName = "Motion"; + objAddr = _segMan->findObjectByName(objName); + slc = _kernel->_selectorCache.doit; + break; + case kDetectSoundType: + objName = "Sound"; + objAddr = _segMan->findObjectByName(objName); + slc = _kernel->_selectorCache.play; + break; + case kDetectSetCursorType: + objName = "Game"; + objAddr = _segMan->findObjectByName(objName); + // KQ5CD overrides the default setCursor selector of the Game object, + // so we need to handle this separately + // KQ5 PC floppy is early SCI1, Amiga middle SCI1, and CD late SCI1 + assert(!_gameId.empty()); + if (_gameId == "kq5" && getSciVersion() == SCI_VERSION_1_LATE) + objAddr = _gameObj; + slc = _kernel->_selectorCache.setCursor; + break; + case kDetectLofsType: + objName = "Game"; + objAddr = _segMan->findObjectByName(objName); + break; +#ifdef ENABLE_SCI32 + case kDetectSci21KernelTable: + objName = "Sound"; + objAddr = _segMan->findObjectByName(objName); + slc = _kernel->_selectorCache.play; + break; +#endif + default: + warning("autoDetectFeature: invalid featureDetection value %x", featureDetection); + return false; + } + + reg_t addr; + if (objAddr.isNull()) { + warning("autoDetectFeature: %s object couldn't be found", objName.c_str()); + return false; + } + + if (methodNum == -1) { + if (lookup_selector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) { + warning("autoDetectFeature: target selector is not a method of object %s", objName.c_str()); + return false; + } + } else { + addr = _segMan->getObject(objAddr)->getFunction(methodNum); + } + + uint16 offset = addr.offset; + Script *script = _segMan->getScript(addr.segment); + uint16 intParam = 0xFFFF; + + do { + uint16 kFuncNum; + int opsize = script->_buf[offset++]; + uint opcode = opsize >> 1; + int i = 0; + byte argc; + + if (featureDetection == kDetectLofsType) { + if (opcode == op_lofsa || opcode == op_lofss) { + uint16 lofs; + + // Load lofs operand + if (opsize & 1) { + if (offset >= script->_bufSize) + break; + lofs = script->_buf[offset++]; + } else { + if ((uint32)offset + 1 >= (uint32)script->_bufSize) + break; + lofs = READ_LE_UINT16(script->_buf + offset); + offset += 2; + } + + // Check for going out of bounds when interpreting as abs/rel + if (lofs >= script->_bufSize) + _lofsType = SCI_VERSION_0_EARLY; + + if ((signed)offset + (int16)lofs < 0) + _lofsType = SCI_VERSION_1_MIDDLE; + + if ((signed)offset + (int16)lofs >= (signed)script->_bufSize) + _lofsType = SCI_VERSION_1_MIDDLE; + + if (_lofsType != SCI_VERSION_NONE) + return true; + + // If we reach here, we haven't been able to deduce the lofs parameter + // type, but we have advanced the offset pointer already. So move on + // to the next opcode + continue; + } + } + + if (featureDetection == kDetectSoundType) { + // The play method of the Sound object pushes the DoSound command + // that it'll use just before it calls DoSound. We intercept that here + // in order to check what sound semantics are used, cause the position + // of the sound commands has changed at some point during SCI1 middle + if (opcode == op_pushi) { + // Load the pushi parameter + if (opsize & 1) { + if (offset >= script->_bufSize) + break; + intParam = script->_buf[offset++]; + } else { + if ((uint32)offset + 1 >= (uint32)script->_bufSize) + break; + intParam = READ_LE_UINT16(script->_buf + offset); + offset += 2; + } + + continue; + } + } + + while (g_opcode_formats[opcode][i]) { + switch (g_opcode_formats[opcode][i++]) { + case Script_Invalid: + break; + case Script_SByte: + case Script_Byte: + offset++; + break; + case Script_Word: + case Script_SWord: + offset += 2; + break; + case Script_SVariable: + case Script_Variable: + case Script_Property: + case Script_Global: + case Script_Local: + case Script_Temp: + case Script_Param: + if (opsize & 1) + kFuncNum = script->_buf[offset++]; + else { + kFuncNum = 0xffff & (script->_buf[offset] | (script->_buf[offset + 1] << 8)); + offset += 2; + } + + if (opcode == op_callk) { + argc = script->_buf[offset++]; + + switch (featureDetection) { + case kDetectGfxFunctions: + if (kFuncNum == 8) { // kDrawPic (SCI0 - SCI11) + // If kDrawPic is called with 6 parameters from the + // overlay selector, the game is using old graphics functions. + // Otherwise, if it's called with 8 parameters, it's using new + // graphics functions + _gfxFunctionsType = (argc == 8) ? SCI_VERSION_0_LATE : SCI_VERSION_0_EARLY; + return true; + } + break; + case kDetectMoveCountType: + // Games which ignore move count call kAbs before calling kDoBresen + if (_kernel->getKernelName(kFuncNum) == "Abs") { + foundTarget = true; + } else if (_kernel->getKernelName(kFuncNum) == "DoBresen") { + _moveCountType = foundTarget ? kIgnoreMoveCount : kIncrementMoveCount; + return true; + } + break; + case kDetectSoundType: + // Late SCI1 games call kIsObject before kDoSound + if (kFuncNum == 6) { // kIsObject (SCI0-SCI11) + foundTarget = true; + } else if (kFuncNum == 45) { // kDoSound (SCI1) + // First, check which DoSound function is called by the play method of + // the Sound object + switch (intParam) { + case 1: + _doSoundType = SCI_VERSION_0_EARLY; + break; + case 7: + _doSoundType = SCI_VERSION_1_EARLY; + break; + case 8: + _doSoundType = SCI_VERSION_1_LATE; + break; + default: + // Unknown case... should never happen. We fall back to + // alternative detection here, which works in general, apart from + // some transitive games like Jones CD + _doSoundType = foundTarget ? SCI_VERSION_1_LATE : SCI_VERSION_1_EARLY; + break; + } + + if (_doSoundType != SCI_VERSION_NONE) + return true; + } + break; + case kDetectSetCursorType: + // Games with colored mouse cursors call kIsObject before kSetCursor + if (kFuncNum == 6) { // kIsObject (SCI0-SCI11) + foundTarget = true; + } else if (kFuncNum == 40) { // kSetCursor (SCI0-SCI11) + _setCursorType = foundTarget ? SCI_VERSION_1_1 : SCI_VERSION_0_EARLY; + return true; + } + break; +#ifdef ENABLE_SCI32 + case kDetectSci21KernelTable: + if (kFuncNum == 0x40) { + _sci21KernelType = SCI_VERSION_2; + return true; + } else if (kFuncNum == 0x75) { + _sci21KernelType = SCI_VERSION_2_1; + return true; + } + break; +#endif + default: + break; + } + } + break; + + case Script_Offset: + case Script_SRelative: + offset++; + if (!opsize & 1) + offset++; + break; + case Script_End: + offset = 0; // exit loop + break; + default: + warning("opcode %02x: Invalid", opcode); + + } + } + } while (offset > 0); + + // Some games, like KQ5CD, never actually call SetCursor inside Game::setCursor + // but call isObject. Cover this case here, if we're actually reading the selector + // itself, and not iterating through the Game object (i.e. when the selector + // dictionary is missing) + if (featureDetection == kDetectSetCursorType && methodNum == -1 && foundTarget) { + _setCursorType = SCI_VERSION_1_1; + return true; + } + + return false; // not found +} + +SciVersion GameFeatures::detectDoSoundType() { + if (_doSoundType == SCI_VERSION_NONE) { + if (getSciVersion() == SCI_VERSION_0_EARLY) { + // This game is using early SCI0 sound code (different headers than SCI0 late) + _doSoundType = SCI_VERSION_0_EARLY; + } else if (_kernel->_selectorCache.nodePtr == -1) { + // No nodePtr selector, so this game is definitely using newer + // SCI0 sound code (i.e. SCI_VERSION_0_LATE) + _doSoundType = SCI_VERSION_0_LATE; + } else { + if (getSciVersion() >= SCI_VERSION_1_LATE) { + // All SCI1 late games use the newer doSound semantics + _doSoundType = SCI_VERSION_1_LATE; + } else { + if (!autoDetectFeature(kDetectSoundType)) { + warning("DoSound detection failed, taking an educated guess"); + + if (getSciVersion() >= SCI_VERSION_1_MIDDLE) + _doSoundType = SCI_VERSION_1_LATE; + else if (getSciVersion() > SCI_VERSION_01) + _doSoundType = SCI_VERSION_1_EARLY; + } + } + } + + debugC(1, kDebugLevelSound, "Detected DoSound type: %s", getSciVersionDesc(_doSoundType).c_str()); + } + + return _doSoundType; +} + +SciVersion GameFeatures::detectSetCursorType() { + if (_setCursorType == SCI_VERSION_NONE) { + if (getSciVersion() <= SCI_VERSION_01) { + // SCI0/SCI01 games never use cursor views + _setCursorType = SCI_VERSION_0_EARLY; + } else if (getSciVersion() >= SCI_VERSION_1_EARLY && getSciVersion() <= SCI_VERSION_1_MIDDLE) { + // SCI1 early/SCI1 middle games never use cursor views + _setCursorType = SCI_VERSION_0_EARLY; + } else if (getSciVersion() >= SCI_VERSION_1_1) { + // SCI1.1 games always use cursor views + _setCursorType = SCI_VERSION_1_1; + } else { // SCI1 late game, detect cursor semantics + bool found = false; + + if (_kernel->_selectorCache.setCursor == -1) { + // Find which function of the Game object calls setCursor + + Object *obj = _segMan->getObject(_gameObj); + for (uint m = 0; m < obj->getMethodCount(); m++) { + found = autoDetectFeature(kDetectSetCursorType, m); + if (found) + break; + } + } else { + found = autoDetectFeature(kDetectSetCursorType); + } + + if (!found) { + // Quite normal in several demos which don't have a cursor + warning("SetCursor detection failed, taking an educated guess"); + + if (getSciVersion() >= SCI_VERSION_1_1) + _setCursorType = SCI_VERSION_1_1; + else + _setCursorType = SCI_VERSION_0_EARLY; + } + } + + debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType).c_str()); + } + + return _setCursorType; +} + +SciVersion GameFeatures::detectLofsType() { + if (_lofsType == SCI_VERSION_NONE) { + // This detection only works (and is only needed) for SCI 1 + if (getSciVersion() <= SCI_VERSION_01) { + _lofsType = SCI_VERSION_0_EARLY; + return _lofsType; + } + + if (getSciVersion() >= SCI_VERSION_1_1) { + _lofsType = SCI_VERSION_1_1; + return _lofsType; + } + + // Find a function of the game object which invokes lofsa/lofss + reg_t gameClass = _segMan->findObjectByName("Game"); + Object *obj = _segMan->getObject(gameClass); + bool found = false; + + for (uint m = 0; m < obj->getMethodCount(); m++) { + found = autoDetectFeature(kDetectLofsType, m); + + if (found) + break; + } + + if (!found) { + warning("Lofs detection failed, taking an educated guess"); + + if (getSciVersion() >= SCI_VERSION_1_MIDDLE) + _lofsType = SCI_VERSION_1_MIDDLE; + else + _lofsType = SCI_VERSION_0_EARLY; + } + + debugC(1, kDebugLevelVM, "Detected Lofs type: %s", getSciVersionDesc(_lofsType).c_str()); + } + + return _lofsType; +} + +SciVersion GameFeatures::detectGfxFunctionsType() { + if (_gfxFunctionsType == SCI_VERSION_NONE) { + // This detection only works (and is only needed) for SCI0 games + if (getSciVersion() >= SCI_VERSION_01) { + _gfxFunctionsType = SCI_VERSION_0_LATE; + return _gfxFunctionsType; + } + + if (getSciVersion() > SCI_VERSION_0_EARLY) { + // Check if the game is using an overlay + bool found = false; + + if (_kernel->_selectorCache.overlay == -1) { + // No overlay selector found, check if any method of the Rm object + // is calling kDrawPic, as the overlay selector might be missing in demos + + Object *obj = _segMan->getObject(_segMan->findObjectByName("Rm")); + for (uint m = 0; m < obj->getMethodCount(); m++) { + found = autoDetectFeature(kDetectGfxFunctions, m); + if (found) + break; + } + } + + if (_kernel->_selectorCache.overlay == -1 && !found) { + // No overlay selector found, therefore the game is definitely + // using old graphics functions + _gfxFunctionsType = SCI_VERSION_0_EARLY; + } else if (_kernel->_selectorCache.overlay == -1 && found) { + // Detection already done above + } else { // _kernel->_selectorCache.overlay != -1 + // An in-between case: The game does not have a shiftParser + // selector, but it does have an overlay selector, so it uses an + // overlay. Therefore, check it to see how it calls kDrawPic to + // determine the graphics functions type used + + if (!autoDetectFeature(kDetectGfxFunctions)) { + warning("Graphics functions detection failed, taking an educated guess"); + + // Try detecting the graphics function types from the existence of the motionCue + // selector (which is a bit of a hack) + if (_kernel->findSelector("motionCue") != -1) + _gfxFunctionsType = SCI_VERSION_0_LATE; + else + _gfxFunctionsType = SCI_VERSION_0_EARLY; + } + } + } else { // (getSciVersion() == SCI_VERSION_0_EARLY) + // Old SCI0 games always used old graphics functions + _gfxFunctionsType = SCI_VERSION_0_EARLY; + } + + debugC(1, kDebugLevelVM, "Detected graphics functions type: %s", getSciVersionDesc(_gfxFunctionsType).c_str()); + } + + return _gfxFunctionsType; +} + +#ifdef ENABLE_SCI32 +SciVersion GameFeatures::detectSci21KernelType() { + if (_sci21KernelType == SCI_VERSION_NONE) + if (!autoDetectFeature(kDetectSci21KernelTable)) + error("Could not detect the SCI2.1 kernel table type"); + + debugC(1, kDebugLevelVM, "Detected SCI2.1 kernel type: %s", getSciVersionDesc(_sci21KernelType).c_str()); + + return _sci21KernelType; +} +#endif + +MoveCountType GameFeatures::detectMoveCountType() { + if (_moveCountType == kMoveCountUninitialized) { + // SCI0/SCI01 games always increment move count + if (getSciVersion() <= SCI_VERSION_01) { + _moveCountType = kIncrementMoveCount; + } else { + if (!autoDetectFeature(kDetectMoveCountType)) { + warning("Move count autodetection failed"); + _moveCountType = kIncrementMoveCount; // Most games do this, so best guess + } + } + + debugC(1, kDebugLevelVM, "Detected move count handling: %s", (_moveCountType == kIncrementMoveCount) ? "increment" : "ignore"); + } + + return _moveCountType; +} + +} // End of namespace Sci diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h new file mode 100644 index 0000000000..11948a5987 --- /dev/null +++ b/engines/sci/engine/features.h @@ -0,0 +1,121 @@ +/* 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_INCLUDE_FEATURES_H +#define SCI_INCLUDE_FEATURES_H + +#include "sci/resource.h" +#include "sci/engine/seg_manager.h" + +namespace Sci { + +enum FeatureDetection { + kDetectGfxFunctions = 0, + kDetectMoveCountType = 1, + kDetectSoundType = 2, + kDetectSetCursorType = 3, + kDetectLofsType = 4, + kDetectSci21KernelTable = 5 +}; + +class GameFeatures { +public: + GameFeatures(SegManager *segMan, Kernel *kernel); + ~GameFeatures() {} + + void setGameInfo(reg_t gameObj, Common::String gameId); + + /** + * Autodetects the DoSound type + * @return DoSound type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE / + * SCI_VERSION_1_EARLY / SCI_VERSION_1_LATE + */ + SciVersion detectDoSoundType(); + + /** + * Autodetects the SetCursor type + * @return SetCursor type, SCI_VERSION_0_EARLY / SCI_VERSION_1_1 + */ + SciVersion detectSetCursorType(); + + /** + * Autodetects the Lofs type + * @return Lofs type, SCI_VERSION_0_EARLY / SCI_VERSION_1_MIDDLE / SCI_VERSION_1_1 + */ + SciVersion detectLofsType(); + + /** + * Autodetects the graphics functions used + * @return Graphics functions type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE + */ + SciVersion detectGfxFunctionsType(); + +#ifdef ENABLE_SCI32 + /** + * Autodetects the kernel functions used in SCI2.1 + * @return Graphics functions type, SCI_VERSION_2 / SCI_VERSION_2_1 + */ + SciVersion detectSci21KernelType(); +#endif + + /** + * Applies to all versions before 0.000.502 + * Old SCI versions used to interpret the third DrawPic() parameter inversely, + * with the opposite default value (obviously). + * Also, they used 15 priority zones from 42 to 200 instead of 14 priority + * zones from 42 to 190. + */ + bool usesOldGfxFunctions() { return detectGfxFunctionsType() == SCI_VERSION_0_EARLY; } + + /** + * Autodetects the Bresenham routine used in the actor movement functions + * @return Move count type, kIncrementMoveCnt / kIgnoreMoveCnt + */ + MoveCountType detectMoveCountType(); + + bool handleMoveCount() { return detectMoveCountType() == kIncrementMoveCount; } + + bool usesCdTrack() { return _usesCdTrack; } + +private: + bool autoDetectFeature(FeatureDetection featureDetection, int methodNum = -1); + + SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType; +#ifdef ENABLE_SCI32 + SciVersion _sci21KernelType; +#endif + + MoveCountType _moveCountType; + bool _usesCdTrack; + + SegManager *_segMan; + Kernel *_kernel; + reg_t _gameObj; + Common::String _gameId; +}; + +} // End of namespace Sci + +#endif // SCI_INCLUDE_ENGINE_H diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp index 9baa9f7c6e..b04dfc32c2 100644 --- a/engines/sci/engine/game.cpp +++ b/engines/sci/engine/game.cpp @@ -221,7 +221,7 @@ int script_init_engine(EngineState *s) { s->_breakpoints.clear(); // No breakpoints defined s->_activeBreakpointTypes = 0; - if (s->detectLofsType() == SCI_VERSION_1_MIDDLE) + if (s->_features->detectLofsType() == SCI_VERSION_1_MIDDLE) s->_segMan->setExportAreWide(true); else s->_segMan->setExportAreWide(false); @@ -280,7 +280,7 @@ int game_init(EngineState *s) { #ifdef USE_OLD_MUSIC_FUNCTIONS if (s->sfx_init_flags & SFX_STATE_FLAG_NOSOUND) - game_init_sound(s, 0, s->detectDoSoundType()); + game_init_sound(s, 0, s->_features->detectDoSoundType()); #endif // Load game language into printLang property of game object @@ -296,7 +296,7 @@ int game_exit(EngineState *s) { #ifdef USE_OLD_MUSIC_FUNCTIONS s->_sound.sfx_exit(); // Reinit because some other code depends on having a valid state - game_init_sound(s, SFX_STATE_FLAG_NOSOUND, s->detectDoSoundType()); + game_init_sound(s, SFX_STATE_FLAG_NOSOUND, s->_features->detectDoSoundType()); #else s->_audio->stopAllAudio(); s->_soundCmd->clearPlayList(); diff --git a/engines/sci/engine/kernel32.cpp b/engines/sci/engine/kernel32.cpp index 917637a7c1..06d1b8b044 100644 --- a/engines/sci/engine/kernel32.cpp +++ b/engines/sci/engine/kernel32.cpp @@ -382,7 +382,7 @@ void Kernel::setKernelNamesSci21(EngineState *s) { // 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 (s->detectSci21KernelType() == SCI_VERSION_2) { + if (s->_features->detectSci21KernelType() == SCI_VERSION_2) { _kernelNames = Common::StringList(sci2_default_knames, kKernelEntriesGk2Demo); // OnMe is IsOnMe here, but they should be compatible _kernelNames[0x23] = "Robot"; // Graph in SCI2 diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index d1a4e8eae6..40ea736a8c 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -151,7 +151,7 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { } #ifndef USE_OLD_MUSIC_FUNCTIONS - if (s->detectDoSoundType() <= SCI_VERSION_0_LATE) { + if (s->_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 diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index f14f20c97a..762b786a79 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -190,7 +190,7 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) { } reg_t kSetCursor(EngineState *s, int argc, reg_t *argv) { - switch (s->detectSetCursorType()) { + switch (s->_features->detectSetCursorType()) { case SCI_VERSION_0_EARLY: return kSetCursorSci0(s, argc, argv); case SCI_VERSION_1_1: @@ -553,7 +553,7 @@ reg_t kDrawPic(EngineState *s, int argc, reg_t *argv) { if (argc >= 3) { if (!argv[2].isNull()) addToFlag = true; - if (!s->usesOldGfxFunctions()) + if (!s->_features->usesOldGfxFunctions()) addToFlag = !addToFlag; } if (argc >= 4) diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp index dc4c2ffc93..6a2d468531 100644 --- a/engines/sci/engine/kmovement.cpp +++ b/engines/sci/engine/kmovement.cpp @@ -267,7 +267,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { //printf("movecnt %d, move speed %d\n", movcnt, max_movcnt); - if (s->handleMoveCount()) { + if (s->_features->handleMoveCount()) { if (max_movcnt > movcnt) { ++movcnt; PUT_SEL32V(segMan, mover, b_movCnt, movcnt); // Needed for HQ1/Ogre? diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp index 5352774812..8106a19aec 100644 --- a/engines/sci/engine/ksound.cpp +++ b/engines/sci/engine/ksound.cpp @@ -90,7 +90,7 @@ reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) { reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { // JonesCD uses different functions based on the cdaudio.map file // to use red book tracks. - if (s->usesCdTrack()) + if (s->_features->usesCdTrack()) return kDoCdAudio(s, argc, argv); Audio::Mixer *mixer = g_system->getMixer(); diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index d322a80c15..377c676392 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -976,7 +976,7 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { #ifdef USE_OLD_MUSIC_FUNCTIONS temp = retval->_sound._songlib; - retval->_sound.sfx_init(retval->resMan, s->sfx_init_flags, s->detectDoSoundType()); + retval->_sound.sfx_init(retval->resMan, s->sfx_init_flags, s->_features->detectDoSoundType()); retval->sfx_init_flags = s->sfx_init_flags; retval->_sound._songlib.freeSounds(); retval->_sound._songlib = temp; @@ -1008,6 +1008,8 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { retval->successor = NULL; retval->_gameId = s->_gameId; + retval->_features->setGameInfo(retval->_gameObj, retval->_gameId); + #ifdef USE_OLD_MUSIC_FUNCTIONS retval->_sound._it = NULL; retval->_sound._flags = s->_sound._flags; @@ -1028,7 +1030,7 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { } else { #endif retval->_gui->resetEngineState(retval); - retval->_gui->init(retval->usesOldGfxFunctions()); + retval->_gui->init(retval->_features->usesOldGfxFunctions()); #ifdef ENABLE_SCI32 } #endif diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 32bc9182f3..58302b1aeb 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -95,7 +95,7 @@ opcode_format g_opcode_formats[128][4] = { // constructor (?) of a VirtualMachine or a ScriptManager class. void script_adjust_opcode_formats(EngineState *s) { // TODO: Check that this is correct - if (s->detectLofsType() != SCI_VERSION_0_EARLY) { + if (s->_features->detectLofsType() != SCI_VERSION_0_EARLY) { g_opcode_formats[op_lofsa][0] = Script_Offset; g_opcode_formats[op_lofss][0] = Script_Offset; } diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index dbbbb9d499..89078e6381 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -40,6 +40,8 @@ EngineState::EngineState(ResourceManager *res, Kernel *kernel, Vocabulary *voc, sfx_init_flags = 0; #endif + _features = new GameFeatures(_segMan, _kernel); + restarting_flags = 0; last_wait_time = 0; @@ -72,24 +74,13 @@ EngineState::EngineState(ResourceManager *res, Kernel *kernel, Vocabulary *voc, _throttleLastTime = 0; _throttleTrigger = false; - _setCursorType = SCI_VERSION_NONE; - _doSoundType = SCI_VERSION_NONE; - _lofsType = SCI_VERSION_NONE; - _gfxFunctionsType = SCI_VERSION_NONE; - _moveCountType = kMoveCountUninitialized; - -#ifdef ENABLE_SCI32 - _sci21KernelType = SCI_VERSION_NONE; -#endif - _memorySegmentSize = 0; - _usesCdTrack = Common::File::exists("cdaudio.map"); - _soundCmd = 0; } EngineState::~EngineState() { + delete _features; delete _msgState; } @@ -218,472 +209,4 @@ Common::String EngineState::strSplit(const char *str, const char *sep) { return retval; } -bool EngineState::autoDetectFeature(FeatureDetection featureDetection, int methodNum) { - Common::String objName; - Selector slc = 0; - reg_t objAddr; - bool foundTarget = false; - - // Get address of target script - switch (featureDetection) { - case kDetectGfxFunctions: - objName = "Rm"; - objAddr = _segMan->findObjectByName(objName); - slc = _kernel->_selectorCache.overlay; - break; - case kDetectMoveCountType: - objName = "Motion"; - objAddr = _segMan->findObjectByName(objName); - slc = _kernel->_selectorCache.doit; - break; - case kDetectSoundType: - objName = "Sound"; - objAddr = _segMan->findObjectByName(objName); - slc = _kernel->_selectorCache.play; - break; - case kDetectSetCursorType: - objName = "Game"; - objAddr = _segMan->findObjectByName(objName); - // KQ5CD overrides the default setCursor selector of the Game object, - // so we need to handle this separately - // KQ5 PC floppy is early SCI1, Amiga middle SCI1, and CD late SCI1 - if (_gameId == "kq5" && getSciVersion() == SCI_VERSION_1_LATE) - objAddr = _gameObj; - slc = _kernel->_selectorCache.setCursor; - break; - case kDetectLofsType: - objName = "Game"; - objAddr = _segMan->findObjectByName(objName); - break; -#ifdef ENABLE_SCI32 - case kDetectSci21KernelTable: - objName = "Sound"; - objAddr = _segMan->findObjectByName(objName); - slc = _kernel->_selectorCache.play; - break; -#endif - default: - warning("autoDetectFeature: invalid featureDetection value %x", featureDetection); - return false; - } - - reg_t addr; - if (objAddr.isNull()) { - warning("autoDetectFeature: %s object couldn't be found", objName.c_str()); - return false; - } - - if (methodNum == -1) { - if (lookup_selector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) { - warning("autoDetectFeature: target selector is not a method of object %s", objName.c_str()); - return false; - } - } else { - addr = _segMan->getObject(objAddr)->getFunction(methodNum); - } - - uint16 offset = addr.offset; - Script *script = _segMan->getScript(addr.segment); - uint16 intParam = 0xFFFF; - - do { - uint16 kFuncNum; - int opsize = script->_buf[offset++]; - uint opcode = opsize >> 1; - int i = 0; - byte argc; - - if (featureDetection == kDetectLofsType) { - if (opcode == op_lofsa || opcode == op_lofss) { - uint16 lofs; - - // Load lofs operand - if (opsize & 1) { - if (offset >= script->_bufSize) - break; - lofs = script->_buf[offset++]; - } else { - if ((uint32)offset + 1 >= (uint32)script->_bufSize) - break; - lofs = READ_LE_UINT16(script->_buf + offset); - offset += 2; - } - - // Check for going out of bounds when interpreting as abs/rel - if (lofs >= script->_bufSize) - _lofsType = SCI_VERSION_0_EARLY; - - if ((signed)offset + (int16)lofs < 0) - _lofsType = SCI_VERSION_1_MIDDLE; - - if ((signed)offset + (int16)lofs >= (signed)script->_bufSize) - _lofsType = SCI_VERSION_1_MIDDLE; - - if (_lofsType != SCI_VERSION_NONE) - return true; - - // If we reach here, we haven't been able to deduce the lofs parameter - // type, but we have advanced the offset pointer already. So move on - // to the next opcode - continue; - } - } - - if (featureDetection == kDetectSoundType) { - // The play method of the Sound object pushes the DoSound command - // that it'll use just before it calls DoSound. We intercept that here - // in order to check what sound semantics are used, cause the position - // of the sound commands has changed at some point during SCI1 middle - if (opcode == op_pushi) { - // Load the pushi parameter - if (opsize & 1) { - if (offset >= script->_bufSize) - break; - intParam = script->_buf[offset++]; - } else { - if ((uint32)offset + 1 >= (uint32)script->_bufSize) - break; - intParam = READ_LE_UINT16(script->_buf + offset); - offset += 2; - } - - continue; - } - } - - while (g_opcode_formats[opcode][i]) { - switch (g_opcode_formats[opcode][i++]) { - case Script_Invalid: - break; - case Script_SByte: - case Script_Byte: - offset++; - break; - case Script_Word: - case Script_SWord: - offset += 2; - break; - case Script_SVariable: - case Script_Variable: - case Script_Property: - case Script_Global: - case Script_Local: - case Script_Temp: - case Script_Param: - if (opsize & 1) - kFuncNum = script->_buf[offset++]; - else { - kFuncNum = 0xffff & (script->_buf[offset] | (script->_buf[offset + 1] << 8)); - offset += 2; - } - - if (opcode == op_callk) { - argc = script->_buf[offset++]; - - switch (featureDetection) { - case kDetectGfxFunctions: - if (kFuncNum == 8) { // kDrawPic (SCI0 - SCI11) - // If kDrawPic is called with 6 parameters from the - // overlay selector, the game is using old graphics functions. - // Otherwise, if it's called with 8 parameters, it's using new - // graphics functions - _gfxFunctionsType = (argc == 8) ? SCI_VERSION_0_LATE : SCI_VERSION_0_EARLY; - return true; - } - break; - case kDetectMoveCountType: - // Games which ignore move count call kAbs before calling kDoBresen - if (_kernel->getKernelName(kFuncNum) == "Abs") { - foundTarget = true; - } else if (_kernel->getKernelName(kFuncNum) == "DoBresen") { - _moveCountType = foundTarget ? kIgnoreMoveCount : kIncrementMoveCount; - return true; - } - break; - case kDetectSoundType: - // Late SCI1 games call kIsObject before kDoSound - if (kFuncNum == 6) { // kIsObject (SCI0-SCI11) - foundTarget = true; - } else if (kFuncNum == 45) { // kDoSound (SCI1) - // First, check which DoSound function is called by the play method of - // the Sound object - switch (intParam) { - case 1: - _doSoundType = SCI_VERSION_0_EARLY; - break; - case 7: - _doSoundType = SCI_VERSION_1_EARLY; - break; - case 8: - _doSoundType = SCI_VERSION_1_LATE; - break; - default: - // Unknown case... should never happen. We fall back to - // alternative detection here, which works in general, apart from - // some transitive games like Jones CD - _doSoundType = foundTarget ? SCI_VERSION_1_LATE : SCI_VERSION_1_EARLY; - break; - } - - if (_doSoundType != SCI_VERSION_NONE) - return true; - } - break; - case kDetectSetCursorType: - // Games with colored mouse cursors call kIsObject before kSetCursor - if (kFuncNum == 6) { // kIsObject (SCI0-SCI11) - foundTarget = true; - } else if (kFuncNum == 40) { // kSetCursor (SCI0-SCI11) - _setCursorType = foundTarget ? SCI_VERSION_1_1 : SCI_VERSION_0_EARLY; - return true; - } - break; -#ifdef ENABLE_SCI32 - case kDetectSci21KernelTable: - if (kFuncNum == 0x40) { - _sci21KernelType = SCI_VERSION_2; - return true; - } else if (kFuncNum == 0x75) { - _sci21KernelType = SCI_VERSION_2_1; - return true; - } - break; -#endif - default: - break; - } - } - break; - - case Script_Offset: - case Script_SRelative: - offset++; - if (!opsize & 1) - offset++; - break; - case Script_End: - offset = 0; // exit loop - break; - default: - warning("opcode %02x: Invalid", opcode); - - } - } - } while (offset > 0); - - // Some games, like KQ5CD, never actually call SetCursor inside Game::setCursor - // but call isObject. Cover this case here, if we're actually reading the selector - // itself, and not iterating through the Game object (i.e. when the selector - // dictionary is missing) - if (featureDetection == kDetectSetCursorType && methodNum == -1 && foundTarget) { - _setCursorType = SCI_VERSION_1_1; - return true; - } - - return false; // not found -} - -SciVersion EngineState::detectDoSoundType() { - if (_doSoundType == SCI_VERSION_NONE) { - if (getSciVersion() == SCI_VERSION_0_EARLY) { - // This game is using early SCI0 sound code (different headers than SCI0 late) - _doSoundType = SCI_VERSION_0_EARLY; - } else if (_kernel->_selectorCache.nodePtr == -1) { - // No nodePtr selector, so this game is definitely using newer - // SCI0 sound code (i.e. SCI_VERSION_0_LATE) - _doSoundType = SCI_VERSION_0_LATE; - } else { - if (getSciVersion() >= SCI_VERSION_1_LATE) { - // All SCI1 late games use the newer doSound semantics - _doSoundType = SCI_VERSION_1_LATE; - } else { - if (!autoDetectFeature(kDetectSoundType)) { - warning("DoSound detection failed, taking an educated guess"); - - if (getSciVersion() >= SCI_VERSION_1_MIDDLE) - _doSoundType = SCI_VERSION_1_LATE; - else if (getSciVersion() > SCI_VERSION_01) - _doSoundType = SCI_VERSION_1_EARLY; - } - } - } - - debugC(1, kDebugLevelSound, "Detected DoSound type: %s", getSciVersionDesc(_doSoundType).c_str()); - } - - return _doSoundType; -} - -SciVersion EngineState::detectSetCursorType() { - if (_setCursorType == SCI_VERSION_NONE) { - if (getSciVersion() <= SCI_VERSION_01) { - // SCI0/SCI01 games never use cursor views - _setCursorType = SCI_VERSION_0_EARLY; - } else if (getSciVersion() >= SCI_VERSION_1_EARLY && getSciVersion() <= SCI_VERSION_1_MIDDLE) { - // SCI1 early/SCI1 middle games never use cursor views - _setCursorType = SCI_VERSION_0_EARLY; - } else if (getSciVersion() >= SCI_VERSION_1_1) { - // SCI1.1 games always use cursor views - _setCursorType = SCI_VERSION_1_1; - } else { // SCI1 late game, detect cursor semantics - bool found = false; - - if (_kernel->_selectorCache.setCursor == -1) { - // Find which function of the Game object calls setCursor - - Object *obj = _segMan->getObject(_gameObj); - for (uint m = 0; m < obj->getMethodCount(); m++) { - found = autoDetectFeature(kDetectSetCursorType, m); - if (found) - break; - } - } else { - found = autoDetectFeature(kDetectSetCursorType); - } - - if (!found) { - // Quite normal in several demos which don't have a cursor - warning("SetCursor detection failed, taking an educated guess"); - - if (getSciVersion() >= SCI_VERSION_1_1) - _setCursorType = SCI_VERSION_1_1; - else - _setCursorType = SCI_VERSION_0_EARLY; - } - } - - debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType).c_str()); - } - - return _setCursorType; -} - -SciVersion EngineState::detectLofsType() { - if (_lofsType == SCI_VERSION_NONE) { - // This detection only works (and is only needed) for SCI 1 - if (getSciVersion() <= SCI_VERSION_01) { - _lofsType = SCI_VERSION_0_EARLY; - return _lofsType; - } - - if (getSciVersion() >= SCI_VERSION_1_1) { - _lofsType = SCI_VERSION_1_1; - return _lofsType; - } - - // Find a function of the game object which invokes lofsa/lofss - reg_t gameClass = _segMan->findObjectByName("Game"); - Object *obj = _segMan->getObject(gameClass); - bool found = false; - - for (uint m = 0; m < obj->getMethodCount(); m++) { - found = autoDetectFeature(kDetectLofsType, m); - - if (found) - break; - } - - if (!found) { - warning("Lofs detection failed, taking an educated guess"); - - if (getSciVersion() >= SCI_VERSION_1_MIDDLE) - _lofsType = SCI_VERSION_1_MIDDLE; - else - _lofsType = SCI_VERSION_0_EARLY; - } - - debugC(1, kDebugLevelVM, "Detected Lofs type: %s", getSciVersionDesc(_lofsType).c_str()); - } - - return _lofsType; -} - -SciVersion EngineState::detectGfxFunctionsType() { - if (_gfxFunctionsType == SCI_VERSION_NONE) { - // This detection only works (and is only needed) for SCI0 games - if (getSciVersion() >= SCI_VERSION_01) { - _gfxFunctionsType = SCI_VERSION_0_LATE; - return _gfxFunctionsType; - } - - if (getSciVersion() > SCI_VERSION_0_EARLY) { - // Check if the game is using an overlay - bool found = false; - - if (_kernel->_selectorCache.overlay == -1) { - // No overlay selector found, check if any method of the Rm object - // is calling kDrawPic, as the overlay selector might be missing in demos - - Object *obj = _segMan->getObject(_segMan->findObjectByName("Rm")); - for (uint m = 0; m < obj->getMethodCount(); m++) { - found = autoDetectFeature(kDetectGfxFunctions, m); - if (found) - break; - } - } - - if (_kernel->_selectorCache.overlay == -1 && !found) { - // No overlay selector found, therefore the game is definitely - // using old graphics functions - _gfxFunctionsType = SCI_VERSION_0_EARLY; - } else if (_kernel->_selectorCache.overlay == -1 && found) { - // Detection already done above - } else { // _kernel->_selectorCache.overlay != -1 - // An in-between case: The game does not have a shiftParser - // selector, but it does have an overlay selector, so it uses an - // overlay. Therefore, check it to see how it calls kDrawPic to - // determine the graphics functions type used - - if (!autoDetectFeature(kDetectGfxFunctions)) { - warning("Graphics functions detection failed, taking an educated guess"); - - // Try detecting the graphics function types from the existence of the motionCue - // selector (which is a bit of a hack) - if (_kernel->findSelector("motionCue") != -1) - _gfxFunctionsType = SCI_VERSION_0_LATE; - else - _gfxFunctionsType = SCI_VERSION_0_EARLY; - } - } - } else { // (getSciVersion() == SCI_VERSION_0_EARLY) - // Old SCI0 games always used old graphics functions - _gfxFunctionsType = SCI_VERSION_0_EARLY; - } - - debugC(1, kDebugLevelVM, "Detected graphics functions type: %s", getSciVersionDesc(_gfxFunctionsType).c_str()); - } - - return _gfxFunctionsType; -} - -#ifdef ENABLE_SCI32 -SciVersion EngineState::detectSci21KernelType() { - if (_sci21KernelType == SCI_VERSION_NONE) - if (!autoDetectFeature(kDetectSci21KernelTable)) - error("Could not detect the SCI2.1 kernel table type"); - - debugC(1, kDebugLevelVM, "Detected SCI2.1 kernel type: %s", getSciVersionDesc(_sci21KernelType).c_str()); - - return _sci21KernelType; -} -#endif - -MoveCountType EngineState::detectMoveCountType() { - if (_moveCountType == kMoveCountUninitialized) { - // SCI0/SCI01 games always increment move count - if (getSciVersion() <= SCI_VERSION_01) { - _moveCountType = kIncrementMoveCount; - } else { - if (!autoDetectFeature(kDetectMoveCountType)) { - warning("Move count autodetection failed"); - _moveCountType = kIncrementMoveCount; // Most games do this, so best guess - } - } - - debugC(1, kDebugLevelVM, "Detected move count handling: %s", (_moveCountType == kIncrementMoveCount) ? "increment" : "ignore"); - } - - return _moveCountType; -} - } // End of namespace Sci diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index a956fd9563..fb39e7d4ca 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -37,6 +37,7 @@ namespace Common { #include "sci/sci.h" #include "sci/resource.h" +#include "sci/engine/features.h" #include "sci/engine/seg_manager.h" #include "sci/parser/vocabulary.h" @@ -113,15 +114,6 @@ enum kLanguage { K_LANG_PORTUGUESE = 351 }; -enum FeatureDetection { - kDetectGfxFunctions = 0, - kDetectMoveCountType = 1, - kDetectSoundType = 2, - kDetectSetCursorType = 3, - kDetectLofsType = 4, - kDetectSci21KernelTable = 5 -}; - class FileHandle { public: Common::String _name; @@ -151,6 +143,8 @@ public: Common::String _gameId; /**< Designation of the primary object (which inherits from Game) */ + GameFeatures *_features; + /* Non-VM information */ GfxAnimate *_gfxAnimate; // Animate for 16-bit gfx @@ -257,75 +251,6 @@ public: private: kLanguage charToLanguage(const char c) const; - - -public: - // TODO: The following methods and member variables deal with (detecting) - // features and capabilities the active game expects to find in the engine. - // It should likely be moved to a separate class. - - /** - * Autodetects the DoSound type - * @return DoSound type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE / - * SCI_VERSION_1_EARLY / SCI_VERSION_1_LATE - */ - SciVersion detectDoSoundType(); - - /** - * Autodetects the SetCursor type - * @return SetCursor type, SCI_VERSION_0_EARLY / SCI_VERSION_1_1 - */ - SciVersion detectSetCursorType(); - - /** - * Autodetects the Lofs type - * @return Lofs type, SCI_VERSION_0_EARLY / SCI_VERSION_1_MIDDLE / SCI_VERSION_1_1 - */ - SciVersion detectLofsType(); - - /** - * Autodetects the graphics functions used - * @return Graphics functions type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE - */ - SciVersion detectGfxFunctionsType(); - -#ifdef ENABLE_SCI32 - /** - * Autodetects the kernel functions used in SCI2.1 - * @return Graphics functions type, SCI_VERSION_2 / SCI_VERSION_2_1 - */ - SciVersion detectSci21KernelType(); -#endif - - /** - * Applies to all versions before 0.000.502 - * Old SCI versions used to interpret the third DrawPic() parameter inversely, - * with the opposite default value (obviously). - * Also, they used 15 priority zones from 42 to 200 instead of 14 priority - * zones from 42 to 190. - */ - bool usesOldGfxFunctions() { return detectGfxFunctionsType() == SCI_VERSION_0_EARLY; } - - /** - * Autodetects the Bresenham routine used in the actor movement functions - * @return Move count type, kIncrementMoveCnt / kIgnoreMoveCnt - */ - MoveCountType detectMoveCountType(); - - bool handleMoveCount() { return detectMoveCountType() == kIncrementMoveCount; } - - bool usesCdTrack() { return _usesCdTrack; } - -private: - bool autoDetectFeature(FeatureDetection featureDetection, int methodNum = -1); - - SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType; -#ifdef ENABLE_SCI32 - SciVersion _sci21KernelType; -#endif - - MoveCountType _moveCountType; - bool _usesCdTrack; }; } // End of namespace Sci diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 7c3e5cb03d..67f2fa13ea 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -1361,7 +1361,7 @@ void run_vm(EngineState *s, bool restoring) { case op_lofsa: // 0x39 (57) s->r_acc.segment = scriptState.xs->addr.pc.segment; - switch (s->detectLofsType()) { + switch (s->_features->detectLofsType()) { case SCI_VERSION_1_1: s->r_acc.offset = opparams[0] + local_script->_scriptSize; break; @@ -1383,7 +1383,7 @@ void run_vm(EngineState *s, bool restoring) { case op_lofss: // 0x3a (58) r_temp.segment = scriptState.xs->addr.pc.segment; - switch (s->detectLofsType()) { + switch (s->_features->detectLofsType()) { case SCI_VERSION_1_1: r_temp.offset = opparams[0] + local_script->_scriptSize; break; diff --git a/engines/sci/module.mk b/engines/sci/module.mk index 3d68ce1092..9ebb66f02a 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -7,6 +7,7 @@ MODULE_OBJS := \ event.o \ resource.o \ sci.o \ + engine/features.o \ engine/game.o \ engine/gc.o \ engine/kernel.o \ diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index c03b9dbc68..e01e0fdac0 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -209,7 +209,9 @@ Common::Error SciEngine::run() { assert(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value != 0); strcpy(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value, ""); - SciVersion soundVersion = _gamestate->detectDoSoundType(); + _gamestate->_features->setGameInfo(_gamestate->_gameObj, _gamestate->_gameId); + + SciVersion soundVersion = _gamestate->_features->detectDoSoundType(); _gamestate->_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, soundVersion); @@ -229,7 +231,7 @@ Common::Error SciEngine::run() { _gamestate->_gui32->init(); else #endif - _gamestate->_gui->init(_gamestate->usesOldGfxFunctions()); + _gamestate->_gui->init(_gamestate->_features->usesOldGfxFunctions()); debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion()).c_str()); |