From 3e543d2518ee7c6197db2a58721bcb70f0cbfb90 Mon Sep 17 00:00:00 2001 From: Walter van Niftrik Date: Sun, 30 Aug 2009 14:53:58 +0000 Subject: [PATCH] SCI: Add lofs detection. svn-id: r43824 --- engines/sci/engine/game.cpp | 2 +- engines/sci/engine/kernel.cpp | 18 ---- engines/sci/engine/kernel.h | 10 +- engines/sci/engine/script.cpp | 9 +- engines/sci/engine/script.h | 2 +- engines/sci/engine/state.cpp | 127 +++++++++++++++++++++++- engines/sci/engine/state.h | 12 ++- engines/sci/engine/static_selectors.cpp | 4 +- engines/sci/engine/vm.cpp | 28 +++--- engines/sci/engine/vm.h | 1 - engines/sci/sci.cpp | 3 +- 11 files changed, 163 insertions(+), 53 deletions(-) diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp index 6f36e404404..d7fd5c99d99 100644 --- a/engines/sci/engine/game.cpp +++ b/engines/sci/engine/game.cpp @@ -226,7 +226,7 @@ int script_init_engine(EngineState *s) { s->bp_list = NULL; // No breakpoints defined s->have_bp = 0; - if (((SciEngine*)g_engine)->getKernel()->hasLofsAbsolute()) + if (s->detectLofsType() == SCI_VERSION_1_MIDDLE) s->segmentManager->setExportWidth(1); else s->segmentManager->setExportWidth(0); diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index ebb1dfdb658..15d37a55fda 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -401,16 +401,6 @@ void Kernel::detectSciFeatures() { features |= kFeatureOldGfxFunctions; } - // Lofs absolute/relative - if (version >= SCI_VERSION_1_MIDDLE && version < SCI_VERSION_1_1) { - // Assume all games use absolute lofs - features |= kFeatureLofsAbsolute; - } else if (version == SCI_VERSION_1_EARLY) { - // Use heuristic - if (_selectorMap.egoMoveSpeed != -1) - features |= kFeatureLofsAbsolute; - } - printf("Kernel auto-detected features:\n"); printf("Graphics functions: "); @@ -418,14 +408,6 @@ void Kernel::detectSciFeatures() { printf("old\n"); else printf("new\n"); - - if (version < SCI_VERSION_1_1) { - printf("lofs parameters: "); - if (features & kFeatureLofsAbsolute) - printf("absolute\n"); - else - printf("relative\n"); - } } void Kernel::loadSelectorNames() { diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index a780d04cf79..29228baee79 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -54,8 +54,7 @@ struct KernelFuncWithSignature { enum AutoDetectedFeatures { kFeatureOldScriptHeader = 1 << 0, - kFeatureOldGfxFunctions = 1 << 1, - kFeatureLofsAbsolute = 1 << 2 + kFeatureOldGfxFunctions = 1 << 1 }; class Kernel { @@ -95,13 +94,6 @@ public: */ bool usesOldGfxFunctions() const { return (features & kFeatureOldGfxFunctions); } - /** - * Applies to all SCI1 versions after 1.000.200 - * In late SCI1 versions, the argument of lofs[as] instructions - * is absolute rather than relative. - */ - bool hasLofsAbsolute() const { return (features & kFeatureLofsAbsolute); } - // Script dissection/dumping functions void dissectScript(int scriptNumber, Vocabulary *vocab); void dumpScriptObject(char *data, int seeker, int objsize); diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 4dacd6b505b..3a2d01f61f7 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -88,14 +88,16 @@ opcode_format g_opcode_formats[128][4] = { }; #undef END -void script_adjust_opcode_formats(SciVersion version) { +void script_adjust_opcode_formats(EngineState *s) { // TODO: Check that this is correct - if ((version >= SCI_VERSION_1_1) || ((SciEngine*)g_engine)->getKernel()->hasLofsAbsolute()) { + if (s->detectLofsType() != SCI_VERSION_0_EARLY) { g_opcode_formats[op_lofsa][0] = Script_Offset; g_opcode_formats[op_lofss][0] = Script_Offset; } - + #ifdef ENABLE_SCI32 + SciVersion version = s->resourceManager->sciVersion(); + // In SCI32, some arguments are now words instead of bytes if (version >= SCI_VERSION_2) { g_opcode_formats[op_calle][2] = Script_Word; @@ -240,7 +242,6 @@ void Kernel::mapSelectors() { FIND_SELECTOR(subtitleLang); FIND_SELECTOR(parseLang); FIND_SELECTOR(motionCue); - FIND_SELECTOR(egoMoveSpeed); FIND_SELECTOR(setCursor); } diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h index dd5980eafe4..62c5596d551 100644 --- a/engines/sci/engine/script.h +++ b/engines/sci/engine/script.h @@ -202,7 +202,7 @@ enum sci_opcodes { /* FIXME */ extern opcode_format g_opcode_formats[128][4]; -void script_adjust_opcode_formats(SciVersion version); +void script_adjust_opcode_formats(EngineState *s); void script_free_breakpoints(EngineState *s); diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index ca4190df8ee..e562f8cd4ac 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -25,6 +25,7 @@ #include "sci/engine/state.h" #include "sci/engine/vm.h" +#include "sci/engine/script.h" #include "sci/console.h" // For parse_reg_t namespace Sci { @@ -121,6 +122,7 @@ EngineState::EngineState(ResourceManager *res, uint32 flags) _setCursorType = SCI_VERSION_AUTODETECT; _doSoundType = SCI_VERSION_AUTODETECT; + _lofsType = SCI_VERSION_AUTODETECT; } EngineState::~EngineState() { @@ -334,10 +336,133 @@ SciVersion EngineState::detectSetCursorType() { _setCursorType = SCI_VERSION_0_EARLY; } - debugC(0, kDebugLevelGraphics, "Detected SetCursor type: %s", ((SciEngine *)g_engine)->getSciVersionDesc(_setCursorType).c_str()); + debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", ((SciEngine *)g_engine)->getSciVersionDesc(_setCursorType).c_str()); } return _setCursorType; } +SciVersion EngineState::detectLofsType() { + if (_lofsType == SCI_VERSION_AUTODETECT) { + SciVersion version = segmentManager->sciVersion(); // FIXME: for VM_OBJECT_READ_FUNCTION + + // This detection only works (and is only needed) pre-SCI1.1 + if (version >= SCI_VERSION_1_1) { + _lofsType = SCI_VERSION_1_1; + return _lofsType; + } + + reg_t gameClass; + Object *obj = NULL; + + if (!parse_reg_t(this, "?Game", &gameClass)) + obj = obj_get(segmentManager, gameClass); + + bool couldBeAbs = true; + bool couldBeRel = true; + + // Check methods of the Game class for lofs operations + if (obj) { + for (int m = 0; m < obj->methods_nr; m++) { + reg_t fptr = VM_OBJECT_READ_FUNCTION(obj, m); + + Script *script = segmentManager->getScript(fptr.segment); + + if ((script == NULL) || (script->buf == NULL)) + continue; + + uint offset = fptr.offset; + bool done = false; + + while (!done) { + // Read opcode + if (offset >= script->buf_size) + break; + + byte opcode = script->buf[offset++]; + byte opnumber = opcode >> 1; + + if ((opnumber == 0x39) || (opnumber == 0x3a)) { + uint16 lofs; + + // Load lofs operand + if (opcode & 1) { + if (offset >= script->buf_size) + break; + lofs = script->buf[offset++]; + } else { + if (offset + 1 >= script->buf_size) + break; + lofs = READ_LE_UINT16(script->buf + offset); + offset += 2; + } + + // Check for going out of bounds when interpreting as abs/rel + if (lofs >= script->buf_size) + couldBeAbs = false; + + if ((signed)offset + (int16)lofs < 0) + couldBeRel = false; + + if ((signed)offset + (int16)lofs >= (signed)script->buf_size) + couldBeRel = false; + + continue; + } + + // Skip operands for non-lofs 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: + done = true; + break; + case Script_Invalid: + default: + warning("opcode %02x: Invalid", opcode); + } + } + } + } + } + + if (couldBeRel == couldBeAbs) { + warning("Lofs detection failed, taking an educated guess"); + + if (version >= SCI_VERSION_1_MIDDLE) + _lofsType = SCI_VERSION_1_MIDDLE; + + _lofsType = SCI_VERSION_0_EARLY; + } else if (couldBeAbs) { + _lofsType = SCI_VERSION_1_MIDDLE; + } else { + _lofsType = SCI_VERSION_0_EARLY; + } + + debugC(1, kDebugLevelVM, "Detected Lofs type: %s", ((SciEngine *)g_engine)->getSciVersionDesc(_lofsType).c_str()); + } + + return _lofsType; +} + } // End of namespace Sci diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index c822f88c415..c394e5d197c 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -274,16 +274,22 @@ public: /** * Autodetects the DoSound type - * @return DoSound type + * @return DoSound type, SCI_VERSION_0_EARLY / SCI_VERSION_1_EARLY / SCI_VERSION_1_LATE */ SciVersion detectDoSoundType(); /** * Autodetects the SetCursor type - * @return 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(); + /* Debugger data: */ Breakpoint *bp_list; /**< List of breakpoints */ int have_bp; /**< Bit mask specifying which types of breakpoints are used in bp_list */ @@ -314,7 +320,7 @@ public: Common::String getLanguageString(const char *str, kLanguage lang) const; private: - SciVersion _doSoundType, _setCursorType; + SciVersion _doSoundType, _setCursorType, _lofsType; kLanguage charToLanguage(const char c) const; int methodChecksum(reg_t objAddress, Selector sel, int offset, uint size) const; }; diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp index 047eb4112cc..a26dd16b8fc 100644 --- a/engines/sci/engine/static_selectors.cpp +++ b/engines/sci/engine/static_selectors.cpp @@ -120,7 +120,7 @@ static const SelectorRemap lsl1_demo_selectors[] = { { "cue", 136 }, { "owner", 150 }, { "setVol", 156 }, { "completed", 210 }, { "motionCue", 213 }, { "cycler", 215 }, { "setTarget", 221 }, { "distance", 224 }, { "canBeHere", 232 }, - { "syncTime", 247 }, { "syncCue", 248 }, { "egoMoveSpeed", 370 } + { "syncTime", 247 }, { "syncCue", 248 } }; // Taken from Space Quest 1 VGA (Demo) @@ -133,7 +133,7 @@ static const SelectorRemap lsl5_demo_selectors[] = { { "moveDone", 100 }, { "init", 103 }, { "dispose", 104 }, { "caller", 133 }, { "cue", 135 }, { "owner", 149 }, { "flags", 150 }, { "completed", 207 }, { "motionCue", 210 }, - { "cycler", 212 }, { "distance", 221 }, { "egoMoveSpeed", 357 } + { "cycler", 212 }, { "distance", 221 } }; // A macro for loading one of the above tables in the function below diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index acd251f1918..8e09e56a68a 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -1160,13 +1160,15 @@ void run_vm(EngineState *s, int restoring) { case 0x39: // lofsa s->r_acc.segment = scriptState.xs->addr.pc.segment; - if (s->resourceManager->sciVersion() >= SCI_VERSION_1_1) { + switch (s->detectLofsType()) { + case SCI_VERSION_1_1: s->r_acc.offset = opparams[0] + local_script->script_size; - } else { - if (((SciEngine*)g_engine)->getKernel()->hasLofsAbsolute()) - s->r_acc.offset = opparams[0]; - else - s->r_acc.offset = scriptState.xs->addr.pc.offset + opparams[0]; + break; + case SCI_VERSION_1_MIDDLE: + s->r_acc.offset = opparams[0]; + break; + default: + s->r_acc.offset = scriptState.xs->addr.pc.offset + opparams[0]; } #ifndef DISABLE_VALIDATIONS @@ -1180,13 +1182,15 @@ void run_vm(EngineState *s, int restoring) { case 0x3a: // lofss r_temp.segment = scriptState.xs->addr.pc.segment; - if (s->resourceManager->sciVersion() >= SCI_VERSION_1_1) { + switch (s->detectLofsType()) { + case SCI_VERSION_1_1: r_temp.offset = opparams[0] + local_script->script_size; - } else { - if (((SciEngine*)g_engine)->getKernel()->hasLofsAbsolute()) - r_temp.offset = opparams[0]; - else - r_temp.offset = scriptState.xs->addr.pc.offset + opparams[0]; + break; + case SCI_VERSION_1_MIDDLE: + r_temp.offset = opparams[0]; + break; + default: + r_temp.offset = scriptState.xs->addr.pc.offset + opparams[0]; } #ifndef DISABLE_VALIDATIONS diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h index f828de42e71..f4bc0d142fd 100644 --- a/engines/sci/engine/vm.h +++ b/engines/sci/engine/vm.h @@ -190,7 +190,6 @@ struct selector_map_t { Selector flags; Selector motionCue; /**< Used to determine if a game is using old gfx functions or not */ - Selector egoMoveSpeed; /**< Used to determine if a game is using absolute lofs parameters */ Selector points; /**< Used by AvoidPath() */ diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index a0fad0adb2b..9164e601087 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -136,7 +136,6 @@ Common::Error SciEngine::run() { _kernel = new Kernel(_resourceManager); _vocabulary = new Vocabulary(_resourceManager); - script_adjust_opcode_formats(_resourceManager->sciVersion()); _gamestate = new EngineState(_resourceManager, flags); @@ -149,6 +148,8 @@ Common::Error SciEngine::run() { return Common::kUnknownError; } + script_adjust_opcode_formats(_gamestate); + // Set the savegame dir (actually, we set it to a fake value, // since we cannot let the game control where saves are stored) script_set_gamestate_save_dir(_gamestate, "/");