- 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
This commit is contained in:
Filippos Karapetis 2009-11-19 23:05:12 +00:00
parent eefa01af68
commit f1036e47ad
2 changed files with 187 additions and 227 deletions

View file

@ -233,110 +233,14 @@ 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
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;
break;
case 0x13D:
_doSoundType = SCI_VERSION_1_EARLY;
break;
case 0x13E:
#ifdef ENABLE_SCI32
case 0x14B:
#endif
_doSoundType = SCI_VERSION_1_LATE;
}
}
if (_doSoundType == SCI_VERSION_AUTODETECT) {
} else {
if (!dissectSelector(kDetectSoundType)) {
warning("DoSound detection failed, taking an educated guess");
if (getSciVersion() >= SCI_VERSION_1_MIDDLE)
@ -346,6 +250,7 @@ SciVersion EngineState::detectDoSoundType() {
else
_doSoundType = SCI_VERSION_0_EARLY;
}
}
debugC(1, kDebugLevelSound, "Detected DoSound type: %s", getSciVersionDesc(_doSoundType).c_str());
}
@ -355,15 +260,11 @@ 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 {
if (!dissectSelector(kDetectSetCursorType)) {
warning("SetCursor detection failed, taking an educated guess");
if (getSciVersion() >= SCI_VERSION_1_1)
@ -371,6 +272,7 @@ SciVersion EngineState::detectSetCursorType() {
else
_setCursorType = SCI_VERSION_0_EARLY;
}
}
debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType).c_str());
}
@ -499,40 +401,44 @@ SciVersion EngineState::detectLofsType() {
return _lofsType;
}
SciVersion EngineState::detectGfxFunctionsType() {
if (_gfxFunctionsType == SCI_VERSION_AUTODETECT) {
// This detection only works (and is only needed) for SCI0 games
if (getSciVersion() >= SCI_VERSION_01) {
_gfxFunctionsType = SCI_VERSION_0_LATE;
return _gfxFunctionsType;
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;
}
if (getSciVersion() > SCI_VERSION_0_EARLY) {
if (_kernel->findSelector("shiftParser") != -1) {
// The shiftParser selector was introduced just a bit after the
// changes to the graphics functions, so if it exists, the game is
// definitely using newer graphics functions
_gfxFunctionsType = SCI_VERSION_0_LATE;
} else {
// No shiftparser selector, check if the game is using an overlay
if (_kernel->_selectorCache.overlay == -1) {
// No overlay selector found, therefore the game is definitely
// using old graphics functions
_gfxFunctionsType = SCI_VERSION_0_EARLY;
} else {
// 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
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;
}
reg_t roomObjAddr = _segMan->findObjectByName("Rm");
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;
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 {
@ -571,6 +477,8 @@ SciVersion EngineState::detectGfxFunctionsType() {
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.
@ -586,6 +494,42 @@ SciVersion EngineState::detectGfxFunctionsType() {
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;
@ -604,10 +548,37 @@ SciVersion EngineState::detectGfxFunctionsType() {
}
}
} 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
if (getSciVersion() >= SCI_VERSION_01) {
_gfxFunctionsType = SCI_VERSION_0_LATE;
return _gfxFunctionsType;
}
if (!found) {
if (getSciVersion() > SCI_VERSION_0_EARLY) {
if (_kernel->findSelector("shiftParser") != -1) {
// The shiftParser selector was introduced just a bit after the
// changes to the graphics functions, so if it exists, the game is
// definitely using newer graphics functions
_gfxFunctionsType = SCI_VERSION_0_LATE;
} else {
// No shiftparser selector, check if the game is using an overlay
if (_kernel->_selectorCache.overlay == -1) {
// No overlay selector found, therefore the game is definitely
// using old graphics functions
_gfxFunctionsType = SCI_VERSION_0_EARLY;
} else {
// 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 (!dissectSelector(kDetectGfxFunctions)) {
warning("Graphics functions detection failed, taking an educated guess");
// Try detecting the graphics function types from the existence of the motionCue
@ -635,30 +606,12 @@ 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;
}
}
if (!found) {
} else {
if (!dissectSelector(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");
}

View file

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