diff options
Diffstat (limited to 'engines/sci/engine/features.cpp')
-rw-r--r-- | engines/sci/engine/features.cpp | 194 |
1 files changed, 138 insertions, 56 deletions
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index 48f7c2d64f..f99d412c64 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -24,6 +24,7 @@ */ #include "sci/engine/features.h" +#include "sci/engine/kernel.h" #include "sci/engine/script.h" #include "sci/engine/selector.h" #include "sci/engine/vm.h" @@ -37,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 @@ -51,13 +53,13 @@ reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc reg_t addr; if (objAddr.isNull()) { - warning("getDetectionAddr: %s object couldn't be found", objName.c_str()); + error("getDetectionAddr: %s object couldn't be found", objName.c_str()); return NULL_REG; } if (methodNum == -1) { if (lookupSelector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) { - warning("getDetectionAddr: target selector is not a method of object %s", objName.c_str()); + error("getDetectionAddr: target selector is not a method of object %s", objName.c_str()); return NULL_REG; } } else { @@ -69,7 +71,7 @@ reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc bool GameFeatures::autoDetectSoundType() { // Look up the script address - reg_t addr = getDetectionAddr("Sound", _kernel->_selectorCache.play); + reg_t addr = getDetectionAddr("Sound", SELECTOR(play)); if (!addr.segment) return false; @@ -83,17 +85,17 @@ bool GameFeatures::autoDetectSoundType() { int16 opparams[4]; byte extOpcode; byte opcode; - offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams); + offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams); opcode = extOpcode >> 1; // Check for end of script if (opcode == op_ret || offset >= script->getBufSize()) break; - // 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 + // The play method of the Sound object pushes the DoSound command that + // it will 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 intParam = opparams[0]; @@ -104,8 +106,8 @@ bool GameFeatures::autoDetectSoundType() { 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 + // First, check which DoSound function is called by the play + // method of the Sound object switch (intParam) { case 1: _doSoundType = SCI_VERSION_0_EARLY; @@ -118,8 +120,8 @@ bool GameFeatures::autoDetectSoundType() { 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 + // 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; } @@ -136,9 +138,14 @@ bool GameFeatures::autoDetectSoundType() { 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) + // This game is using early SCI0 sound code (different headers than + // SCI0 late) _doSoundType = SCI_VERSION_0_EARLY; - } else if (_kernel->_selectorCache.nodePtr == -1) { +#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) _doSoundType = SCI_VERSION_0_LATE; @@ -171,14 +178,16 @@ SciVersion GameFeatures::detectSetCursorType() { // SCI1.1 games always use cursor views _setCursorType = SCI_VERSION_1_1; } else { // SCI1 late game, detect cursor semantics - // If the Cursor object doesn't exist, we're using the SCI0 early kSetCursor semantics. + // If the Cursor object doesn't exist, we're using the SCI0 early + // kSetCursor semantics. if (_segMan->findObjectByName("Cursor") == NULL_REG) { _setCursorType = SCI_VERSION_0_EARLY; debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType)); return _setCursorType; } - // Check for the existence of the handCursor object (first found). This is based on KQ5. + // Check for the existence of the handCursor object (first found). + // This is based on KQ5. reg_t objAddr = _segMan->findObjectByName("handCursor", 0); // If that doesn't exist, we assume it uses SCI1.1 kSetCursor semantics @@ -188,11 +197,13 @@ SciVersion GameFeatures::detectSetCursorType() { return _setCursorType; } - // Now we check what the number variable holds in the handCursor object. + // Now we check what the number variable holds in the handCursor + // object. uint16 number = readSelectorValue(_segMan, objAddr, SELECTOR(number)); - // If the number is 0, it uses views and therefore the SCI1.1 kSetCursor semantics, - // otherwise it uses the SCI0 early kSetCursor semantics. + // If the number is 0, it uses views and therefore the SCI1.1 + // kSetCursor semantics, otherwise it uses the SCI0 early kSetCursor + // semantics. if (number == 0) _setCursorType = SCI_VERSION_1_1; else @@ -205,9 +216,9 @@ SciVersion GameFeatures::detectSetCursorType() { return _setCursorType; } -bool GameFeatures::autoDetectLofsType(int methodNum) { +bool GameFeatures::autoDetectLofsType(Common::String gameSuperClassName, int methodNum) { // Look up the script address - reg_t addr = getDetectionAddr("Game", -1, methodNum); + reg_t addr = getDetectionAddr(gameSuperClassName.c_str(), -1, methodNum); if (!addr.segment) return false; @@ -219,7 +230,7 @@ bool GameFeatures::autoDetectLofsType(int methodNum) { int16 opparams[4]; byte extOpcode; byte opcode; - offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams); + offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams); opcode = extOpcode >> 1; // Check for end of script @@ -264,20 +275,35 @@ SciVersion GameFeatures::detectLofsType() { return _lofsType; } + // Find the "Game" object, super class of the actual game-object + const reg_t game = g_sci->getGameObject(); + const Object *gameObject = _segMan->getObject(game); + reg_t gameSuperClass = NULL_REG; + if (gameObject) { + gameSuperClass = gameObject->getSuperClassSelector(); + } + // Find a function of the game object which invokes lofsa/lofss - reg_t gameClass = _segMan->findObjectByName("Game"); - const Object *obj = _segMan->getObject(gameClass); bool found = false; + if (!gameSuperClass.isNull()) { + Common::String gameSuperClassName = _segMan->getObjectName(gameSuperClass); + const Object *gameSuperObject = _segMan->getObject(gameSuperClass); - for (uint m = 0; m < obj->getMethodCount(); m++) { - found = autoDetectLofsType(m); - - if (found) - break; + if (gameSuperObject) { + for (uint m = 0; m < gameSuperObject->getMethodCount(); m++) { + found = autoDetectLofsType(gameSuperClassName, m); + if (found) + break; + } + } else { + warning("detectLofsType(): Could not get superclass object"); + } + } else { + warning("detectLofsType(): Could not find superclass of game object"); } if (!found) { - warning("Lofs detection failed, taking an educated guess"); + warning("detectLofsType(): failed, taking an educated guess"); if (getSciVersion() >= SCI_VERSION_1_MIDDLE) _lofsType = SCI_VERSION_1_MIDDLE; @@ -293,7 +319,7 @@ SciVersion GameFeatures::detectLofsType() { bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) { // Look up the script address - reg_t addr = getDetectionAddr("Rm", _kernel->_selectorCache.overlay, methodNum); + reg_t addr = getDetectionAddr("Rm", SELECTOR(overlay), methodNum); if (!addr.segment) return false; @@ -305,7 +331,7 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) { int16 opparams[4]; byte extOpcode; byte opcode; - offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams); + offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams); opcode = extOpcode >> 1; // Check for end of script @@ -317,10 +343,10 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) { uint16 argc = opparams[1]; if (kFuncNum == 8) { // kDrawPic (SCI0 - SCI11) - // If kDrawPic is called with 6 parameters from the - // overlay selector, the game is using old graphics functions. + // 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 + // graphics functions. _gfxFunctionsType = (argc == 8) ? SCI_VERSION_0_LATE : SCI_VERSION_0_EARLY; return true; } @@ -343,33 +369,36 @@ SciVersion GameFeatures::detectGfxFunctionsType() { bool searchRoomObj = false; reg_t rmObjAddr = _segMan->findObjectByName("Rm"); - if (_kernel->_selectorCache.overlay != -1) { - // The game has an overlay selector, check how it calls kDrawPicto determine - // the graphics functions type used - if (lookupSelector(_segMan, rmObjAddr, _kernel->_selectorCache.overlay, NULL, NULL) == kSelectorMethod) { + if (SELECTOR(overlay) != -1) { + // The game has an overlay selector, check how it calls kDrawPic + // to determine the graphics functions type used + if (lookupSelector(_segMan, rmObjAddr, SELECTOR(overlay), NULL, NULL) == kSelectorMethod) { if (!autoDetectGfxFunctionsType()) { 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) + // 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 { - // The game has an overlay selector, but it's not a method of the Rm object - // (like in Hoyle 1 and 2), so search for other methods + // The game has an overlay selector, but it's not a method + // of the Rm object (like in Hoyle 1 and 2), so search for + // other methods searchRoomObj = true; } } else { - // The game doesn't have an overlay selector, so search for it manually + // The game doesn't have an overlay selector, so search for it + // manually searchRoomObj = true; } if (searchRoomObj) { - // If requested, check if any method of the Rm object is calling kDrawPic, - // as the overlay selector might be missing in demos + // If requested, check if any method of the Rm object is calling + // kDrawPic, as the overlay selector might be missing in demos bool found = false; const Object *obj = _segMan->getObject(rmObjAddr); @@ -380,8 +409,9 @@ SciVersion GameFeatures::detectGfxFunctionsType() { } if (!found) { - // No method of the Rm object is calling kDrawPic, thus the game - // doesn't have overlays and is using older graphics functions + // No method of the Rm object is calling kDrawPic, thus the + // game doesn't have overlays and is using older graphics + // functions _gfxFunctionsType = SCI_VERSION_0_EARLY; } } @@ -393,10 +423,60 @@ 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()) { + delete resources; + + // 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); + delete resources; + + // 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 + reg_t soundObjAddr = _segMan->findObjectByName("Sound"); + if (soundObjAddr.isNull()) { + // Usually, this means that the Sound object isn't loaded yet. + // This case doesn't occur in early SCI2.1 games, and we've only + // seen it happen in the RAMA demo, thus we can assume that the + // game is using a SCI2.1 table + warning("autoDetectSci21KernelType(): Sound object not loaded, assuming a SCI2.1 table"); + _sci21KernelType = SCI_VERSION_2_1; + return true; + } + // Look up the script address - reg_t addr = getDetectionAddr("Sound", _kernel->_selectorCache.play); + reg_t addr = getDetectionAddr("Sound", SELECTOR(play)); if (!addr.segment) return false; @@ -408,7 +488,7 @@ bool GameFeatures::autoDetectSci21KernelType() { int16 opparams[4]; byte extOpcode; byte opcode; - offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams); + offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams); opcode = extOpcode >> 1; // Check for end of script @@ -419,9 +499,11 @@ bool GameFeatures::autoDetectSci21KernelType() { uint16 kFuncNum = opparams[0]; // Here we check for the kDoSound opcode that's used in SCI2.1. - // Finding 0x40 as kDoSound in the Sound::play() function means the game is using - // the modified SCI2 kernel table found in some older SCI2.1 games (GK2 demo, KQ7 v1.4). - // Finding 0x75 as kDoSound means the game is using the regular SCI2.1 kernel table. + // Finding 0x40 as kDoSound in the Sound::play() function means the + // game is using the modified SCI2 kernel table found in some older + // SCI2.1 games (GK2 demo, KQ7 v1.4). + // Finding 0x75 as kDoSound means the game is using the regular + // SCI2.1 kernel table. if (kFuncNum == 0x40) { _sci21KernelType = SCI_VERSION_2; return true; @@ -448,7 +530,7 @@ SciVersion GameFeatures::detectSci21KernelType() { bool GameFeatures::autoDetectMoveCountType() { // Look up the script address - reg_t addr = getDetectionAddr("Motion", _kernel->_selectorCache.doit); + reg_t addr = getDetectionAddr("Motion", SELECTOR(doit)); if (!addr.segment) return false; @@ -461,7 +543,7 @@ bool GameFeatures::autoDetectMoveCountType() { int16 opparams[4]; byte extOpcode; byte opcode; - offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams); + offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams); opcode = extOpcode >> 1; // Check for end of script @@ -491,7 +573,7 @@ MoveCountType GameFeatures::detectMoveCountType() { _moveCountType = kIncrementMoveCount; } else { if (!autoDetectMoveCountType()) { - warning("Move count autodetection failed"); + error("Move count autodetection failed"); _moveCountType = kIncrementMoveCount; // Most games do this, so best guess } } |