aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine/state.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine/state.cpp')
-rw-r--r--engines/sci/engine/state.cpp483
1 files changed, 3 insertions, 480 deletions
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