SCI: Add lofs detection.

svn-id: r43824
This commit is contained in:
Walter van Niftrik 2009-08-30 14:53:58 +00:00
parent dceadc9ba0
commit 3e543d2518
11 changed files with 163 additions and 53 deletions

View File

@ -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);

View File

@ -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() {

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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() */

View File

@ -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, "/");