From f1036e47ad41d01f15abf5832cee6904b1aff46c Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Thu, 19 Nov 2009 23:05:12 +0000 Subject: - Simplified the different feature detection types, removed some duplicate code and merged the feature detection code which relies on selectors - Replaced the function checksum calculations to make it more apparent what the feature detections do - Removed the now obsolete (and unused) firstRetOffset function svn-id: r45995 --- engines/sci/engine/state.cpp | 405 +++++++++++++++++++------------------------ engines/sci/engine/state.h | 11 +- 2 files changed, 188 insertions(+), 228 deletions(-) (limited to 'engines') diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index 9245d7c1df..c38eeb4601 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -233,120 +233,25 @@ Common::String EngineState::strSplit(const char *str, const char *sep) { return retval; } -int EngineState::methodChecksum(reg_t objAddress, Selector sel, int offset, uint size) const { - reg_t fptr; - - Object *obj = _segMan->getObject(objAddress); - SelectorType selType = lookup_selector(_segMan, objAddress, sel, NULL, &fptr); - - if (!obj || (selType != kSelectorMethod)) - return -1; - - Script *script = _segMan->getScript(fptr.segment); - - if (!script->_buf || (fptr.offset + offset < 0)) - return -1; - - fptr.offset += offset; - - if (fptr.offset + size > script->_bufSize) - return -1; - - byte *buf = script->_buf + fptr.offset; - - uint sum = 0; - for (uint i = 0; i < size; i++) - sum += buf[i]; - - return sum; -} - -uint16 EngineState::firstRetOffset(reg_t objectAddress) const { - Script *script = _segMan->getScript(objectAddress.segment); - - if ((script == NULL) || (script->_buf == NULL)) - return 0; - - uint16 offset = objectAddress.offset; - - while (offset < script->_bufSize) { - byte opcode = script->_buf[offset++]; - byte opnumber = opcode >> 1; - - if (opnumber == 0x24) // ret - return offset - 1; - - // Skip operands for non-ret opcodes - for (int i = 0; g_opcode_formats[opnumber][i]; i++) { - switch (g_opcode_formats[opnumber][i]) { - case Script_Byte: - case Script_SByte: - offset++; - break; - case Script_Word: - case Script_SWord: - offset += 2; - break; - case Script_Variable: - case Script_Property: - case Script_Local: - case Script_Temp: - case Script_Global: - case Script_Param: - case Script_SVariable: - case Script_SRelative: - case Script_Offset: - offset++; - if (!(opcode & 1)) - offset++; - break; - case Script_End: - return offset; - break; - case Script_Invalid: - default: - warning("opcode %02x: Invalid", opcode); - } - } // end for - } // end while - - return 0; -} - SciVersion EngineState::detectDoSoundType() { if (_doSoundType == SCI_VERSION_AUTODETECT) { - reg_t soundClass = _segMan->findObjectByName("Sound"); - - if (!soundClass.isNull()) { - int sum = methodChecksum(soundClass, _kernel->_selectorCache.play, -6, 6); - - switch (sum) { - case 0x1B2: // SCI0 - case 0x1AE: // SCI01 - _doSoundType = SCI_VERSION_0_EARLY; - break; - case 0x13D: - _doSoundType = SCI_VERSION_1_EARLY; - break; - case 0x13E: -#ifdef ENABLE_SCI32 - case 0x14B: -#endif - _doSoundType = SCI_VERSION_1_LATE; + if (_kernel->_selectorCache.nodePtr == -1) { + // No nodePtr selector, so this game is definitely using + // SCI0 sound code (i.e. SCI_VERSION_0_EARLY) + _doSoundType = SCI_VERSION_0_EARLY; + } else { + if (!dissectSelector(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; + else + _doSoundType = SCI_VERSION_0_EARLY; } } - if (_doSoundType == SCI_VERSION_AUTODETECT) { - 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; - else - _doSoundType = SCI_VERSION_0_EARLY; - } - debugC(1, kDebugLevelSound, "Detected DoSound type: %s", getSciVersionDesc(_doSoundType).c_str()); } @@ -355,21 +260,18 @@ SciVersion EngineState::detectDoSoundType() { SciVersion EngineState::detectSetCursorType() { if (_setCursorType == SCI_VERSION_AUTODETECT) { - int sum = methodChecksum(_gameObj, _kernel->_selectorCache.setCursor, 0, 21); - - if ((sum == 0x4D5) || (sum == 0x552)) { - // Standard setCursor + if (getSciVersion() <= SCI_VERSION_01) { + // SCI0/SCI01 games always have non-colored cursors _setCursorType = SCI_VERSION_0_EARLY; - } else if (sum != -1) { - // Assume that others use fancy cursors - _setCursorType = SCI_VERSION_1_1; } else { - warning("SetCursor detection failed, taking an educated guess"); + if (!dissectSelector(kDetectSetCursorType)) { + 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; + 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()); @@ -499,6 +401,157 @@ SciVersion EngineState::detectLofsType() { return _lofsType; } +bool EngineState::dissectSelector(FeatureDetection featureDetection) { + Common::String objName; + Selector slc; + + // Get address of target script + switch (featureDetection) { + case kDetectGfxFunctions: + objName = "Rm"; + slc = _kernel->_selectorCache.overlay; + break; + case kDetectMoveCountType: + objName = "Motion"; + slc = _kernel->_selectorCache.doit; + break; + case kDetectSoundType: + objName = "Sound"; + slc = _kernel->_selectorCache.play; + break; + case kDetectSetCursorType: + objName = "Game"; + slc = _kernel->_selectorCache.setCursor; + default: + break; + } + + reg_t addr; + reg_t objAddr = _segMan->findObjectByName(objName); + if (objAddr.isNull()) { + warning("dissectSelector: %s object couldn't be found", objName.c_str()); + return false; + } + + if (lookup_selector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) { + warning("dissectSelector: target selector is not a method of object %s", objName.c_str()); + return false; + } + + bool found = false; + uint16 offset = addr.offset; + byte *scr = _segMan->getScript(addr.segment)->_buf; + do { + uint16 kFuncNum; + int opsize = scr[offset++]; + uint opcode = opsize >> 1; + int i = 0; + byte argc; + + 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 = scr[offset++]; + else { + kFuncNum = 0xffff & (scr[offset] | (scr[offset + 1] << 8)); + offset += 2; + } + + if (opcode == op_callk) { + argc = scr[offset++]; + + switch (featureDetection) { + case kDetectGfxFunctions: + if (kFuncNum == 8) { // kDrawPic + // 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 + if (argc == 6) { + _gfxFunctionsType = SCI_VERSION_0_EARLY; + found = true; + } else if (argc == 8) { + _gfxFunctionsType = SCI_VERSION_0_LATE; + found = true; + } else { + warning("overlay selector calling kDrawPic with %d parameters", argc); + } + } + break; + case kDetectMoveCountType: + // Games which ignore move count call kAbs before calling kDoBresen + if (kFuncNum == 61) { // kAbs + _moveCountType = kIgnoreMoveCount; + found = true; + } else if (kFuncNum == 80) { // kDoBresen + // If we reached here, a call to kAbs hasn't been found + _moveCountType = kIncrementMoveCount; + found = true; + } + break; + case kDetectSoundType: + // Late SCI1 games call kIsObject before kDoSound + if (kFuncNum == 6) { // kIsObject + _doSoundType = SCI_VERSION_1_LATE; + found = true; + } else if (kFuncNum == 45) { // kDoSound + // If we reached here, a call to kIsObject hasn't been found + _doSoundType = SCI_VERSION_1_EARLY; + found = true; + } + break; + case kDetectSetCursorType: + // Games with colored mouse cursors call kIsObject before kSetCursor + if (kFuncNum == 6) { // kIsObject + _setCursorType = SCI_VERSION_1_1; + found = true; + } else if (kFuncNum == 40) { // kSetCursor + // If we reached here, a call to kIsObject hasn't been found + _setCursorType = SCI_VERSION_0_EARLY; + found = true; + } + 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 && !found); + + return found; +} + SciVersion EngineState::detectGfxFunctionsType() { if (_gfxFunctionsType == SCI_VERSION_AUTODETECT) { // This detection only works (and is only needed) for SCI0 games @@ -525,89 +578,7 @@ SciVersion EngineState::detectGfxFunctionsType() { // overlay. Therefore, check it to see how it calls kDrawPic to // determine the graphics functions type used - reg_t roomObjAddr = _segMan->findObjectByName("Rm"); - - bool found = false; - - if (!roomObjAddr.isNull()) { - reg_t addr; - - if (lookup_selector(_segMan, roomObjAddr, _kernel->_selectorCache.overlay, NULL, &addr) == kSelectorMethod) { - uint16 offset = addr.offset; - byte *scr = _segMan->getScript(addr.segment)->_buf; - do { - uint16 kFuncNum; - int opsize = scr[offset++]; - uint opcode = opsize >> 1; - int i = 0; - byte argc; - - 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 = scr[offset++]; - else { - kFuncNum = 0xffff & (scr[offset] | (scr[offset + 1] << 8)); - offset += 2; - } - - if (opcode == op_callk) { - argc = scr[offset++]; - - if (kFuncNum == 8) { // kDrawPic - // 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 - if (argc == 6) { - _gfxFunctionsType = SCI_VERSION_0_EARLY; - found = true; - } else if (argc == 8) { - _gfxFunctionsType = SCI_VERSION_0_LATE; - found = true; - } else { - warning("overlay selector calling kDrawPic with %d parameters", argc); - } - } - } - 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 && !found); - } - } - - if (!found) { + if (!dissectSelector(kDetectGfxFunctions)) { warning("Graphics functions detection failed, taking an educated guess"); // Try detecting the graphics function types from the existence of the motionCue @@ -635,31 +606,13 @@ MoveCountType EngineState::detectMoveCountType() { // SCI0/SCI01 games always increment move count if (getSciVersion() <= SCI_VERSION_01) { _moveCountType = kIncrementMoveCount; - return _moveCountType; - } - - reg_t motionClass = _segMan->findObjectByName("Motion"); - bool found = false; - - if (!motionClass.isNull()) { - Object *obj = _segMan->getObject(motionClass); - reg_t fptr; - - if (obj && lookup_selector(_segMan, motionClass, _kernel->_selectorCache.doit, NULL, &fptr) == kSelectorMethod) { - byte *buf = _segMan->getScript(fptr.segment)->_buf + fptr.offset; - int checksum = 0; - for (int i = 0; i < 8; i++) - checksum += *(buf++); - _moveCountType = (checksum == 0x216) ? kIncrementMoveCount : kIgnoreMoveCount; - found = true; + } else { + if (!dissectSelector(kDetectMoveCountType)) { + warning("Move count autodetection failed"); + _moveCountType = kIncrementMoveCount; // Most games do this, so best guess } } - if (!found) { - 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"); } diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index 7332eae61c..a97ee3a12c 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -102,6 +102,13 @@ enum kLanguage { K_LANG_PORTUGUESE = 351 }; +enum FeatureDetection { + kDetectGfxFunctions = 0, + kDetectMoveCountType = 1, + kDetectSoundType = 2, + kDetectSetCursorType = 3 +}; + class FileHandle { public: Common::String _name; @@ -285,11 +292,11 @@ public: Common::String getLanguageString(const char *str, kLanguage lang) const; private: + bool dissectSelector(FeatureDetection featureDetection); + SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType; MoveCountType _moveCountType; kLanguage charToLanguage(const char c) const; - int methodChecksum(reg_t objAddress, Selector sel, int offset, uint size) const; - uint16 firstRetOffset(reg_t objectAddress) const; bool _usesCdTrack; }; -- cgit v1.2.3