From cf5483c3d862a1076943a9b7d986b38e3d1908de Mon Sep 17 00:00:00 2001 From: Walter van Niftrik Date: Sun, 30 Aug 2009 01:37:52 +0000 Subject: SCI: Add SetCursor detection. Cleanup. svn-id: r43812 --- engines/sci/engine/kgraphics.cpp | 110 ++++++++++++++------------------- engines/sci/engine/ksound.cpp | 12 ++-- engines/sci/engine/script.cpp | 1 + engines/sci/engine/state.cpp | 130 ++++++++++++++++++++++----------------- engines/sci/engine/state.h | 18 +++--- engines/sci/engine/vm.h | 2 + 6 files changed, 138 insertions(+), 135 deletions(-) (limited to 'engines/sci') diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 18017e0d08..89fed9d2d8 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -301,82 +301,62 @@ static gfx_color_t graph_map_color(EngineState *s, int color, int priority, int return retval; } -reg_t kSetCursor(EngineState *s, int funct_nr, int argc, reg_t *argv) { - SciVersion version = s->resourceManager->sciVersion(); +static reg_t kSetCursorSci0(EngineState *s, int funct_nr, int argc, reg_t *argv) { + uint16 cursor = argv[0].toSint16(); + + if ((argc >= 2) && (argv[1].toSint16() == 0)) + cursor = GFXOP_NO_POINTER; + + GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, cursor)); + + // Set pointer position, if requested + if (argc >= 4) { + Common::Point newPos = Common::Point(argv[2].toSint16() + s->port->_bounds.x, argv[3].toSint16() + s->port->_bounds.y); + GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, newPos)); + } + + return s->r_acc; +} + +static reg_t kSetCursorSci11(EngineState *s, int funct_nr, int argc, reg_t *argv) { + Common::Point *hotspot = NULL; switch (argc) { - case 1 : - if (version < SCI_VERSION_1_LATE) { - GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, argv[0].toSint16())); - } else if (version < SCI_VERSION_1_1) { - if (argv[0].toSint16() <= 1) { - // Newer (SCI1.1) semantics: show/hide cursor - CursorMan.showMouse(argv[0].toSint16() != 0); - } else { - // Pre-SCI1.1: set cursor according to the first parameter - GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, argv[0].toSint16())); - } - } else { - // SCI1.1: Show/hide cursor - CursorMan.showMouse(argv[0].toSint16() != 0); - } - break; - case 2 : - if (version < SCI_VERSION_1_LATE) { - GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, - argv[1].toSint16() == 0 ? GFXOP_NO_POINTER : argv[0].toSint16())); - } else if (version < SCI_VERSION_1_1) { - // Pre-SCI1.1: set cursor according to the first parameter, and toggle its - // visibility based on the second parameter - // Some late SCI1 games actually use the SCI1.1 version of this call (EcoQuest 1 - // and KQ5 CD, but I haven't seen this case happen), but we can determine the - // semantics from the second parameter passed. - // Rationale: with the older behavior, the second parameter can either be 0 - // (hide cursor) or 1/-1 (show cursor). This could be problematic if the engine - // tries to place the cursor at (x, 0) or (x, 1), but no SCI1 game does that, as - // this would open the menu on top. LSL5 is an exception, as the game can open - // the menu when the player presses a button during the intro, but the cursor is - // not placed on (x, 0) or (x, 1) - if (argv[1].toSint16() <= 1) { - GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, - argv[1].toSint16() == 0 ? GFXOP_NO_POINTER : argv[0].toSint16())); - } else { // newer (SCI1.1) semantics: set pointer position - GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, - Common::Point(argv[0].toUint16(), argv[1].toUint16()))); - } - } else { - // SCI1.1 and newer: set pointer position - GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, - Common::Point(argv[0].toUint16(), argv[1].toUint16()))); - } + case 1: + CursorMan.showMouse(argv[0].toSint16() != 0); break; - case 4 : - GFX_ASSERT(gfxop_set_pointer_cursor(s->gfx_state, - argv[0].toUint16() == 0 ? GFXOP_NO_POINTER : argv[0].toSint16())); - - // Set pointer position, if requested - if (argc > 2) { - Common::Point newPos = Common::Point(argv[2].toSint16() + s->port->_bounds.x, argv[3].toSint16() + s->port->_bounds.y); - GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, newPos)); - } + case 2: + GFX_ASSERT(gfxop_set_pointer_position(s->gfx_state, + Common::Point(argv[0].toUint16(), argv[1].toUint16()))); break; - case 3 : - case 5 : - case 9 : - if (argc > 3) { - Common::Point hotspot = Common::Point(argv[3].toSint16(), argv[4].toSint16()); - GFX_ASSERT(gfxop_set_pointer_view(s->gfx_state, argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16(), &hotspot)); - } else { - GFX_ASSERT(gfxop_set_pointer_view(s->gfx_state, argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16(), NULL)); - } + case 5: + case 9: + hotspot = new Common::Point(argv[3].toSint16(), argv[4].toSint16()); + // Fallthrough + case 3: + GFX_ASSERT(gfxop_set_pointer_view(s->gfx_state, argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16(), hotspot)); + if (hotspot) + delete hotspot; break; default : - error("kSetCursor: Unhandled case: %d arguments given", argc); + warning("kSetCursor: Unhandled case: %d arguments given", argc); break; } return s->r_acc; } +reg_t kSetCursor(EngineState *s, int funct_nr, int argc, reg_t *argv) { + switch (s->detectSetCursorType()) { + case SCI_VERSION_0_EARLY: + return kSetCursorSci0(s, funct_nr, argc, argv); + case SCI_VERSION_1_1: + return kSetCursorSci11(s, funct_nr, argc, argv); + default: + warning("Unknown SetCursor type"); + return NULL_REG; + } +} + reg_t kMoveCursor(EngineState *s, int funct_nr, int argc, reg_t *argv) { Common::Point newPos; diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp index 514f063f7e..4c71120f50 100644 --- a/engines/sci/engine/ksound.cpp +++ b/engines/sci/engine/ksound.cpp @@ -206,7 +206,7 @@ void process_sound_events(EngineState *s) { /* Get all sound events, apply their } -reg_t kDoSoundSci0(EngineState *s, int funct_nr, int argc, reg_t *argv) { +static reg_t kDoSoundSci0(EngineState *s, int funct_nr, int argc, reg_t *argv) { SegManager *segManager = s->segmentManager; reg_t obj = (argc > 1) ? argv[1] : NULL_REG; uint16 command = argv[0].toUint16(); @@ -386,7 +386,7 @@ reg_t kDoSoundSci0(EngineState *s, int funct_nr, int argc, reg_t *argv) { } -reg_t kDoSoundSci1Early(EngineState *s, int funct_nr, int argc, reg_t *argv) { +static reg_t kDoSoundSci1Early(EngineState *s, int funct_nr, int argc, reg_t *argv) { SegManager *segManager = s->segmentManager; uint16 command = argv[0].toUint16(); reg_t obj = (argc > 1) ? argv[1] : NULL_REG; @@ -677,7 +677,7 @@ reg_t kDoSoundSci1Early(EngineState *s, int funct_nr, int argc, reg_t *argv) { return s->r_acc; } -reg_t kDoSoundSci1Late(EngineState *s, int funct_nr, int argc, reg_t *argv) { +static reg_t kDoSoundSci1Late(EngineState *s, int funct_nr, int argc, reg_t *argv) { SegManager *segManager = s->segmentManager; uint16 command = argv[0].toUint16(); reg_t obj = (argc > 1) ? argv[1] : NULL_REG; @@ -994,11 +994,11 @@ reg_t kDoSoundSci1Late(EngineState *s, int funct_nr, int argc, reg_t *argv) { */ reg_t kDoSound(EngineState *s, int funct_nr, int argc, reg_t *argv) { switch(s->detectDoSoundType()) { - case EngineState::kDoSoundTypeSci0: + case SCI_VERSION_0_EARLY: return kDoSoundSci0(s, funct_nr, argc, argv); - case EngineState::kDoSoundTypeSci1Early: + case SCI_VERSION_1_EARLY: return kDoSoundSci1Early(s, funct_nr, argc, argv); - case EngineState::kDoSoundTypeSci1Late: + case SCI_VERSION_1_LATE: return kDoSoundSci1Late(s, funct_nr, argc, argv); default: warning("Unknown DoSound type"); diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 1534208d62..4dacd6b505 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -241,6 +241,7 @@ void Kernel::mapSelectors() { FIND_SELECTOR(parseLang); FIND_SELECTOR(motionCue); FIND_SELECTOR(egoMoveSpeed); + FIND_SELECTOR(setCursor); } void Kernel::dumpScriptObject(char *data, int seeker, int objsize) { diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index da14d460f1..ca4190df8e 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -119,7 +119,8 @@ EngineState::EngineState(ResourceManager *res, uint32 flags) speedThrottler = new SpeedThrottler(res->sciVersion()); - _doSoundType = kDoSoundTypeUnknown; + _setCursorType = SCI_VERSION_AUTODETECT; + _doSoundType = SCI_VERSION_AUTODETECT; } EngineState::~EngineState() { @@ -246,78 +247,97 @@ Common::String EngineState::strSplit(const char *str, const char *sep) { return retval; } -EngineState::DoSoundType EngineState::detectDoSoundType() { - if (_doSoundType == kDoSoundTypeUnknown) { +int EngineState::methodChecksum(reg_t objAddress, Selector sel, int offset, uint size) const { + reg_t fptr; + + Object *obj = obj_get(segmentManager, objAddress); + SelectorType selType = lookup_selector(this->segmentManager, objAddress, sel, NULL, &fptr); + + if (!obj || (selType != kSelectorMethod)) + return -1; + + Script *script = segmentManager->getScript(fptr.segment); + + if (!script->buf || (fptr.offset + offset < 0)) + return -1; + + fptr.offset += offset; + + if (fptr.offset + size > script->buf_size) + return -1; + + byte *buf = script->buf + fptr.offset; + + uint sum = 0; + for (uint i = 0; i < size; i++) + sum += buf[i]; + + return sum; +} + +SciVersion EngineState::detectDoSoundType() { + if (_doSoundType == SCI_VERSION_AUTODETECT) { reg_t soundClass; - const uint checkBytes = 6; // Number of bytes to check if (!parse_reg_t(this, "?Sound", &soundClass)) { - reg_t fptr; - - Object *obj = obj_get(segmentManager, soundClass); - SelectorType sel = lookup_selector(this->segmentManager, soundClass, ((SciEngine*)g_engine)->getKernel()->_selectorMap.play, NULL, &fptr); - - if (obj && (sel == kSelectorMethod)) { - Script *script = segmentManager->getScript(fptr.segment); - - if (fptr.offset > checkBytes) { - // Go to the last portion of Sound::init, should be right before the play function - fptr.offset -= checkBytes; - byte *buf = script->buf + fptr.offset; - - // Check the call to DoSound's INIT_HANDLE function. - // It's either subfunction 0, 5 or 6, depending on the version of DoSound. - uint sum = 0; - for (uint i = 0; i < checkBytes; i++) - sum += buf[i]; - - switch(sum) { - case 0x1B2: // SCI0 - case 0x1AE: // SCI01 - _doSoundType = kDoSoundTypeSci0; - break; - case 0x13D: - _doSoundType = kDoSoundTypeSci1Early; - break; - case 0x13E: + int sum = methodChecksum(soundClass, ((SciEngine *)g_engine)->getKernel()->_selectorMap.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: + case 0x14B: #endif - _doSoundType = kDoSoundTypeSci1Late; - } - } + _doSoundType = SCI_VERSION_1_LATE; } } - if (_doSoundType == kDoSoundTypeUnknown) { + if (_doSoundType == SCI_VERSION_AUTODETECT) { warning("DoSound detection failed, taking an educated guess"); if (resourceManager->sciVersion() >= SCI_VERSION_1_MIDDLE) - _doSoundType = kDoSoundTypeSci1Late; + _doSoundType = SCI_VERSION_1_LATE; else if (resourceManager->sciVersion() > SCI_VERSION_01) - _doSoundType = kDoSoundTypeSci1Early; + _doSoundType = SCI_VERSION_1_EARLY; else - _doSoundType = kDoSoundTypeSci0; + _doSoundType = SCI_VERSION_0_EARLY; } - debugCN(1, kDebugLevelSound, "Detected DoSound type: "); - - switch(_doSoundType) { - case kDoSoundTypeSci0: - debugC(1, kDebugLevelSound, "SCI0"); - break; - case kDoSoundTypeSci1Early: - debugC(1, kDebugLevelSound, "SCI1 Early"); - break; - case kDoSoundTypeSci1Late: - debugC(1, kDebugLevelSound, "SCI1 Late"); - break; - default: - break; - } + debugC(1, kDebugLevelSound, "Detected DoSound type: %s", ((SciEngine *)g_engine)->getSciVersionDesc(_doSoundType).c_str()); } return _doSoundType; } +SciVersion EngineState::detectSetCursorType() { + if (_setCursorType == SCI_VERSION_AUTODETECT) { + int sum = methodChecksum(game_obj, ((SciEngine *)g_engine)->getKernel()->_selectorMap.setCursor, 0, 21); + + if ((sum == 0x4D5) || (sum == 0x552)) { + // Standard setCursor + _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 (resourceManager->sciVersion() >= SCI_VERSION_1_1) + _setCursorType = SCI_VERSION_1_1; + else + _setCursorType = SCI_VERSION_0_EARLY; + } + + debugC(0, kDebugLevelGraphics, "Detected SetCursor type: %s", ((SciEngine *)g_engine)->getSciVersionDesc(_setCursorType).c_str()); + } + + return _setCursorType; +} + } // End of namespace Sci diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index 9fee7111a0..c822f88c41 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -164,13 +164,6 @@ public: EngineState(ResourceManager *res, uint32 flags); virtual ~EngineState(); - enum DoSoundType { - kDoSoundTypeUnknown, - kDoSoundTypeSci0, - kDoSoundTypeSci1Early, - kDoSoundTypeSci1Late - }; - virtual void saveLoadWithSerializer(Common::Serializer &ser); kLanguage getLanguage(); @@ -283,7 +276,13 @@ public: * Autodetects the DoSound type * @return DoSound type */ - DoSoundType detectDoSoundType(); + SciVersion detectDoSoundType(); + + /** + * Autodetects the SetCursor type + * @return SetCursor type + */ + SciVersion detectSetCursorType(); /* Debugger data: */ Breakpoint *bp_list; /**< List of breakpoints */ @@ -315,8 +314,9 @@ public: Common::String getLanguageString(const char *str, kLanguage lang) const; private: - DoSoundType _doSoundType; + SciVersion _doSoundType, _setCursorType; kLanguage charToLanguage(const char c) const; + int methodChecksum(reg_t objAddress, Selector sel, int offset, uint size) const; }; /** diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h index a514b12370..f828de42e7 100644 --- a/engines/sci/engine/vm.h +++ b/engines/sci/engine/vm.h @@ -200,6 +200,8 @@ struct selector_map_t { Selector printLang; /**< Used for i18n */ Selector subtitleLang; Selector parseLang; + + Selector setCursor; /** For autodetection */ }; // A reference to an object's variable. -- cgit v1.2.3