aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorFilippos Karapetis2010-02-03 11:02:43 +0000
committerFilippos Karapetis2010-02-03 11:02:43 +0000
commit2fb37063a4ef85aa862b8fdf035d03f6b801679e (patch)
treeb79cdb9e904ffa954d3ff86fef5eee5b616e10d5 /engines
parentbaf6b53431bbb1b5a630fd153fe3df350e3d16fd (diff)
downloadscummvm-rg350-2fb37063a4ef85aa862b8fdf035d03f6b801679e.tar.gz
scummvm-rg350-2fb37063a4ef85aa862b8fdf035d03f6b801679e.tar.bz2
scummvm-rg350-2fb37063a4ef85aa862b8fdf035d03f6b801679e.zip
Placed all the game feature detection code in a separate class
svn-id: r47850
Diffstat (limited to 'engines')
-rw-r--r--engines/sci/console.cpp12
-rw-r--r--engines/sci/engine/features.cpp520
-rw-r--r--engines/sci/engine/features.h121
-rw-r--r--engines/sci/engine/game.cpp6
-rw-r--r--engines/sci/engine/kernel32.cpp2
-rw-r--r--engines/sci/engine/kevent.cpp2
-rw-r--r--engines/sci/engine/kgraphics.cpp4
-rw-r--r--engines/sci/engine/kmovement.cpp2
-rw-r--r--engines/sci/engine/ksound.cpp2
-rw-r--r--engines/sci/engine/savegame.cpp6
-rw-r--r--engines/sci/engine/script.cpp2
-rw-r--r--engines/sci/engine/state.cpp483
-rw-r--r--engines/sci/engine/state.h81
-rw-r--r--engines/sci/engine/vm.cpp4
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/sci.cpp6
16 files changed, 674 insertions, 580 deletions
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 72eceb05a0..3348ed4973 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -417,11 +417,11 @@ bool Console::cmdGetVersion(int argc, const char **argv) {
DebugPrintf("\n");
DebugPrintf("Detected features:\n");
DebugPrintf("------------------\n");
- DebugPrintf("Sound type: %s\n", getSciVersionDesc(s->detectDoSoundType()).c_str());
- DebugPrintf("Graphics functions type: %s\n", getSciVersionDesc(s->detectGfxFunctionsType()).c_str());
- DebugPrintf("Lofs type: %s\n", getSciVersionDesc(s->detectLofsType()).c_str());
- DebugPrintf("Move count type: %s\n", (s->detectMoveCountType() == kIncrementMoveCount) ? "increment" : "ignore");
- DebugPrintf("SetCursor type: %s\n", getSciVersionDesc(s->detectSetCursorType()).c_str());
+ DebugPrintf("Sound type: %s\n", getSciVersionDesc(s->_features->detectDoSoundType()).c_str());
+ DebugPrintf("Graphics functions type: %s\n", getSciVersionDesc(s->_features->detectGfxFunctionsType()).c_str());
+ DebugPrintf("Lofs type: %s\n", getSciVersionDesc(s->_features->detectLofsType()).c_str());
+ DebugPrintf("Move count type: %s\n", (s->_features->detectMoveCountType() == kIncrementMoveCount) ? "increment" : "ignore");
+ DebugPrintf("SetCursor type: %s\n", getSciVersionDesc(s->_features->detectSetCursorType()).c_str());
DebugPrintf("View type: %s\n", viewTypeDesc[s->resMan->getViewType()]);
DebugPrintf("Resource volume version: %s\n", s->resMan->getVolVersionDesc());
DebugPrintf("Resource map version: %s\n", s->resMan->getMapVersionDesc());
@@ -1592,7 +1592,7 @@ bool Console::cmdIsSample(int argc, const char **argv) {
return true;
}
- SoundResource *soundRes = new SoundResource(number, _engine->getResourceManager(), _engine->_gamestate->detectDoSoundType());
+ SoundResource *soundRes = new SoundResource(number, _engine->getResourceManager(), _engine->_gamestate->_features->detectDoSoundType());
if (!soundRes) {
DebugPrintf("Not a sound resource!\n");
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
new file mode 100644
index 0000000000..e5fcc673cf
--- /dev/null
+++ b/engines/sci/engine/features.cpp
@@ -0,0 +1,520 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sci/engine/features.h"
+#include "sci/engine/script.h"
+#include "sci/engine/selector.h"
+#include "sci/engine/vm.h"
+
+namespace Sci {
+
+GameFeatures::GameFeatures(SegManager *segMan, Kernel *kernel) : _segMan(segMan), _kernel(kernel) {
+ _setCursorType = SCI_VERSION_NONE;
+ _doSoundType = SCI_VERSION_NONE;
+ _lofsType = SCI_VERSION_NONE;
+ _gfxFunctionsType = SCI_VERSION_NONE;
+ _moveCountType = kMoveCountUninitialized;
+
+#ifdef ENABLE_SCI32
+ _sci21KernelType = SCI_VERSION_NONE;
+#endif
+ _usesCdTrack = Common::File::exists("cdaudio.map");
+}
+
+void GameFeatures::setGameInfo(reg_t gameObj, Common::String gameId) {
+ _gameObj = gameObj;
+ _gameId = gameId;
+}
+
+bool GameFeatures::autoDetectFeature(FeatureDetection featureDetection, int methodNum) {
+ Common::String objName;
+ Selector slc = 0;
+ reg_t objAddr;
+ bool foundTarget = false;
+
+ // Get address of target script
+ switch (featureDetection) {
+ case kDetectGfxFunctions:
+ objName = "Rm";
+ objAddr = _segMan->findObjectByName(objName);
+ slc = _kernel->_selectorCache.overlay;
+ break;
+ case kDetectMoveCountType:
+ objName = "Motion";
+ objAddr = _segMan->findObjectByName(objName);
+ slc = _kernel->_selectorCache.doit;
+ break;
+ case kDetectSoundType:
+ objName = "Sound";
+ objAddr = _segMan->findObjectByName(objName);
+ slc = _kernel->_selectorCache.play;
+ break;
+ case kDetectSetCursorType:
+ objName = "Game";
+ objAddr = _segMan->findObjectByName(objName);
+ // KQ5CD overrides the default setCursor selector of the Game object,
+ // so we need to handle this separately
+ // KQ5 PC floppy is early SCI1, Amiga middle SCI1, and CD late SCI1
+ assert(!_gameId.empty());
+ if (_gameId == "kq5" && getSciVersion() == SCI_VERSION_1_LATE)
+ objAddr = _gameObj;
+ slc = _kernel->_selectorCache.setCursor;
+ break;
+ case kDetectLofsType:
+ objName = "Game";
+ objAddr = _segMan->findObjectByName(objName);
+ break;
+#ifdef ENABLE_SCI32
+ case kDetectSci21KernelTable:
+ objName = "Sound";
+ objAddr = _segMan->findObjectByName(objName);
+ slc = _kernel->_selectorCache.play;
+ break;
+#endif
+ default:
+ warning("autoDetectFeature: invalid featureDetection value %x", featureDetection);
+ return false;
+ }
+
+ reg_t addr;
+ if (objAddr.isNull()) {
+ warning("autoDetectFeature: %s object couldn't be found", objName.c_str());
+ return false;
+ }
+
+ if (methodNum == -1) {
+ if (lookup_selector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) {
+ warning("autoDetectFeature: target selector is not a method of object %s", objName.c_str());
+ return false;
+ }
+ } else {
+ addr = _segMan->getObject(objAddr)->getFunction(methodNum);
+ }
+
+ uint16 offset = addr.offset;
+ Script *script = _segMan->getScript(addr.segment);
+ uint16 intParam = 0xFFFF;
+
+ do {
+ uint16 kFuncNum;
+ int opsize = script->_buf[offset++];
+ uint opcode = opsize >> 1;
+ int i = 0;
+ byte argc;
+
+ if (featureDetection == kDetectLofsType) {
+ if (opcode == op_lofsa || opcode == op_lofss) {
+ uint16 lofs;
+
+ // Load lofs operand
+ if (opsize & 1) {
+ if (offset >= script->_bufSize)
+ break;
+ lofs = script->_buf[offset++];
+ } else {
+ if ((uint32)offset + 1 >= (uint32)script->_bufSize)
+ break;
+ lofs = READ_LE_UINT16(script->_buf + offset);
+ offset += 2;
+ }
+
+ // Check for going out of bounds when interpreting as abs/rel
+ if (lofs >= script->_bufSize)
+ _lofsType = SCI_VERSION_0_EARLY;
+
+ if ((signed)offset + (int16)lofs < 0)
+ _lofsType = SCI_VERSION_1_MIDDLE;
+
+ if ((signed)offset + (int16)lofs >= (signed)script->_bufSize)
+ _lofsType = SCI_VERSION_1_MIDDLE;
+
+ if (_lofsType != SCI_VERSION_NONE)
+ return true;
+
+ // If we reach here, we haven't been able to deduce the lofs parameter
+ // type, but we have advanced the offset pointer already. So move on
+ // to the next opcode
+ continue;
+ }
+ }
+
+ if (featureDetection == kDetectSoundType) {
+ // The play method of the Sound object pushes the DoSound command
+ // that it'll use just before it calls DoSound. We intercept that here
+ // in order to check what sound semantics are used, cause the position
+ // of the sound commands has changed at some point during SCI1 middle
+ if (opcode == op_pushi) {
+ // Load the pushi parameter
+ if (opsize & 1) {
+ if (offset >= script->_bufSize)
+ break;
+ intParam = script->_buf[offset++];
+ } else {
+ if ((uint32)offset + 1 >= (uint32)script->_bufSize)
+ break;
+ intParam = READ_LE_UINT16(script->_buf + offset);
+ offset += 2;
+ }
+
+ continue;
+ }
+ }
+
+ 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 = script->_buf[offset++];
+ else {
+ kFuncNum = 0xffff & (script->_buf[offset] | (script->_buf[offset + 1] << 8));
+ offset += 2;
+ }
+
+ if (opcode == op_callk) {
+ argc = script->_buf[offset++];
+
+ switch (featureDetection) {
+ case kDetectGfxFunctions:
+ if (kFuncNum == 8) { // kDrawPic (SCI0 - SCI11)
+ // 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
+ _gfxFunctionsType = (argc == 8) ? SCI_VERSION_0_LATE : SCI_VERSION_0_EARLY;
+ return true;
+ }
+ break;
+ case kDetectMoveCountType:
+ // Games which ignore move count call kAbs before calling kDoBresen
+ if (_kernel->getKernelName(kFuncNum) == "Abs") {
+ foundTarget = true;
+ } else if (_kernel->getKernelName(kFuncNum) == "DoBresen") {
+ _moveCountType = foundTarget ? kIgnoreMoveCount : kIncrementMoveCount;
+ return true;
+ }
+ break;
+ case kDetectSoundType:
+ // Late SCI1 games call kIsObject before kDoSound
+ if (kFuncNum == 6) { // kIsObject (SCI0-SCI11)
+ foundTarget = true;
+ } else if (kFuncNum == 45) { // kDoSound (SCI1)
+ // First, check which DoSound function is called by the play method of
+ // the Sound object
+ switch (intParam) {
+ case 1:
+ _doSoundType = SCI_VERSION_0_EARLY;
+ break;
+ case 7:
+ _doSoundType = SCI_VERSION_1_EARLY;
+ break;
+ case 8:
+ _doSoundType = SCI_VERSION_1_LATE;
+ break;
+ default:
+ // Unknown case... should never happen. We fall back to
+ // alternative detection here, which works in general, apart from
+ // some transitive games like Jones CD
+ _doSoundType = foundTarget ? SCI_VERSION_1_LATE : SCI_VERSION_1_EARLY;
+ break;
+ }
+
+ if (_doSoundType != SCI_VERSION_NONE)
+ return true;
+ }
+ break;
+ case kDetectSetCursorType:
+ // Games with colored mouse cursors call kIsObject before kSetCursor
+ if (kFuncNum == 6) { // kIsObject (SCI0-SCI11)
+ foundTarget = true;
+ } else if (kFuncNum == 40) { // kSetCursor (SCI0-SCI11)
+ _setCursorType = foundTarget ? SCI_VERSION_1_1 : SCI_VERSION_0_EARLY;
+ return true;
+ }
+ break;
+#ifdef ENABLE_SCI32
+ case kDetectSci21KernelTable:
+ if (kFuncNum == 0x40) {
+ _sci21KernelType = SCI_VERSION_2;
+ return true;
+ } else if (kFuncNum == 0x75) {
+ _sci21KernelType = SCI_VERSION_2_1;
+ return true;
+ }
+ break;
+#endif
+ 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);
+
+ // Some games, like KQ5CD, never actually call SetCursor inside Game::setCursor
+ // but call isObject. Cover this case here, if we're actually reading the selector
+ // itself, and not iterating through the Game object (i.e. when the selector
+ // dictionary is missing)
+ if (featureDetection == kDetectSetCursorType && methodNum == -1 && foundTarget) {
+ _setCursorType = SCI_VERSION_1_1;
+ return true;
+ }
+
+ return false; // not found
+}
+
+SciVersion GameFeatures::detectDoSoundType() {
+ if (_doSoundType == SCI_VERSION_NONE) {
+ if (getSciVersion() == SCI_VERSION_0_EARLY) {
+ // This game is using early SCI0 sound code (different headers than SCI0 late)
+ _doSoundType = SCI_VERSION_0_EARLY;
+ } else if (_kernel->_selectorCache.nodePtr == -1) {
+ // No nodePtr selector, so this game is definitely using newer
+ // SCI0 sound code (i.e. SCI_VERSION_0_LATE)
+ _doSoundType = SCI_VERSION_0_LATE;
+ } else {
+ if (getSciVersion() >= SCI_VERSION_1_LATE) {
+ // All SCI1 late games use the newer doSound semantics
+ _doSoundType = SCI_VERSION_1_LATE;
+ } else {
+ if (!autoDetectFeature(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;
+ }
+ }
+ }
+
+ debugC(1, kDebugLevelSound, "Detected DoSound type: %s", getSciVersionDesc(_doSoundType).c_str());
+ }
+
+ return _doSoundType;
+}
+
+SciVersion GameFeatures::detectSetCursorType() {
+ if (_setCursorType == SCI_VERSION_NONE) {
+ if (getSciVersion() <= SCI_VERSION_01) {
+ // SCI0/SCI01 games never use cursor views
+ _setCursorType = SCI_VERSION_0_EARLY;
+ } else if (getSciVersion() >= SCI_VERSION_1_EARLY && getSciVersion() <= SCI_VERSION_1_MIDDLE) {
+ // SCI1 early/SCI1 middle games never use cursor views
+ _setCursorType = SCI_VERSION_0_EARLY;
+ } else if (getSciVersion() >= SCI_VERSION_1_1) {
+ // SCI1.1 games always use cursor views
+ _setCursorType = SCI_VERSION_1_1;
+ } else { // SCI1 late game, detect cursor semantics
+ bool found = false;
+
+ if (_kernel->_selectorCache.setCursor == -1) {
+ // Find which function of the Game object calls setCursor
+
+ Object *obj = _segMan->getObject(_gameObj);
+ for (uint m = 0; m < obj->getMethodCount(); m++) {
+ found = autoDetectFeature(kDetectSetCursorType, m);
+ if (found)
+ break;
+ }
+ } else {
+ found = autoDetectFeature(kDetectSetCursorType);
+ }
+
+ if (!found) {
+ // Quite normal in several demos which don't have a cursor
+ 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;
+ }
+ }
+
+ debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType).c_str());
+ }
+
+ return _setCursorType;
+}
+
+SciVersion GameFeatures::detectLofsType() {
+ if (_lofsType == SCI_VERSION_NONE) {
+ // This detection only works (and is only needed) for SCI 1
+ if (getSciVersion() <= SCI_VERSION_01) {
+ _lofsType = SCI_VERSION_0_EARLY;
+ return _lofsType;
+ }
+
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ _lofsType = SCI_VERSION_1_1;
+ return _lofsType;
+ }
+
+ // Find a function of the game object which invokes lofsa/lofss
+ reg_t gameClass = _segMan->findObjectByName("Game");
+ Object *obj = _segMan->getObject(gameClass);
+ bool found = false;
+
+ for (uint m = 0; m < obj->getMethodCount(); m++) {
+ found = autoDetectFeature(kDetectLofsType, m);
+
+ if (found)
+ break;
+ }
+
+ if (!found) {
+ warning("Lofs detection failed, taking an educated guess");
+
+ if (getSciVersion() >= SCI_VERSION_1_MIDDLE)
+ _lofsType = SCI_VERSION_1_MIDDLE;
+ else
+ _lofsType = SCI_VERSION_0_EARLY;
+ }
+
+ debugC(1, kDebugLevelVM, "Detected Lofs type: %s", getSciVersionDesc(_lofsType).c_str());
+ }
+
+ return _lofsType;
+}
+
+SciVersion GameFeatures::detectGfxFunctionsType() {
+ if (_gfxFunctionsType == SCI_VERSION_NONE) {
+ // This detection only works (and is only needed) for SCI0 games
+ if (getSciVersion() >= SCI_VERSION_01) {
+ _gfxFunctionsType = SCI_VERSION_0_LATE;
+ return _gfxFunctionsType;
+ }
+
+ if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ // Check if the game is using an overlay
+ bool found = false;
+
+ if (_kernel->_selectorCache.overlay == -1) {
+ // No overlay selector found, check if any method of the Rm object
+ // is calling kDrawPic, as the overlay selector might be missing in demos
+
+ Object *obj = _segMan->getObject(_segMan->findObjectByName("Rm"));
+ for (uint m = 0; m < obj->getMethodCount(); m++) {
+ found = autoDetectFeature(kDetectGfxFunctions, m);
+ if (found)
+ break;
+ }
+ }
+
+ if (_kernel->_selectorCache.overlay == -1 && !found) {
+ // No overlay selector found, therefore the game is definitely
+ // using old graphics functions
+ _gfxFunctionsType = SCI_VERSION_0_EARLY;
+ } else if (_kernel->_selectorCache.overlay == -1 && found) {
+ // Detection already done above
+ } else { // _kernel->_selectorCache.overlay != -1
+ // 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 (!autoDetectFeature(kDetectGfxFunctions)) {
+ warning("Graphics functions detection failed, taking an educated guess");
+
+ // Try detecting the graphics function types from the existence of the motionCue
+ // selector (which is a bit of a hack)
+ if (_kernel->findSelector("motionCue") != -1)
+ _gfxFunctionsType = SCI_VERSION_0_LATE;
+ else
+ _gfxFunctionsType = SCI_VERSION_0_EARLY;
+ }
+ }
+ } else { // (getSciVersion() == SCI_VERSION_0_EARLY)
+ // Old SCI0 games always used old graphics functions
+ _gfxFunctionsType = SCI_VERSION_0_EARLY;
+ }
+
+ debugC(1, kDebugLevelVM, "Detected graphics functions type: %s", getSciVersionDesc(_gfxFunctionsType).c_str());
+ }
+
+ return _gfxFunctionsType;
+}
+
+#ifdef ENABLE_SCI32
+SciVersion GameFeatures::detectSci21KernelType() {
+ if (_sci21KernelType == SCI_VERSION_NONE)
+ if (!autoDetectFeature(kDetectSci21KernelTable))
+ error("Could not detect the SCI2.1 kernel table type");
+
+ debugC(1, kDebugLevelVM, "Detected SCI2.1 kernel type: %s", getSciVersionDesc(_sci21KernelType).c_str());
+
+ return _sci21KernelType;
+}
+#endif
+
+MoveCountType GameFeatures::detectMoveCountType() {
+ if (_moveCountType == kMoveCountUninitialized) {
+ // SCI0/SCI01 games always increment move count
+ if (getSciVersion() <= SCI_VERSION_01) {
+ _moveCountType = kIncrementMoveCount;
+ } else {
+ if (!autoDetectFeature(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");
+ }
+
+ return _moveCountType;
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h
new file mode 100644
index 0000000000..11948a5987
--- /dev/null
+++ b/engines/sci/engine/features.h
@@ -0,0 +1,121 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCI_INCLUDE_FEATURES_H
+#define SCI_INCLUDE_FEATURES_H
+
+#include "sci/resource.h"
+#include "sci/engine/seg_manager.h"
+
+namespace Sci {
+
+enum FeatureDetection {
+ kDetectGfxFunctions = 0,
+ kDetectMoveCountType = 1,
+ kDetectSoundType = 2,
+ kDetectSetCursorType = 3,
+ kDetectLofsType = 4,
+ kDetectSci21KernelTable = 5
+};
+
+class GameFeatures {
+public:
+ GameFeatures(SegManager *segMan, Kernel *kernel);
+ ~GameFeatures() {}
+
+ void setGameInfo(reg_t gameObj, Common::String gameId);
+
+ /**
+ * Autodetects the DoSound type
+ * @return DoSound type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE /
+ * SCI_VERSION_1_EARLY / SCI_VERSION_1_LATE
+ */
+ SciVersion detectDoSoundType();
+
+ /**
+ * Autodetects the 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();
+
+ /**
+ * Autodetects the graphics functions used
+ * @return Graphics functions type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE
+ */
+ SciVersion detectGfxFunctionsType();
+
+#ifdef ENABLE_SCI32
+ /**
+ * Autodetects the kernel functions used in SCI2.1
+ * @return Graphics functions type, SCI_VERSION_2 / SCI_VERSION_2_1
+ */
+ SciVersion detectSci21KernelType();
+#endif
+
+ /**
+ * Applies to all versions before 0.000.502
+ * Old SCI versions used to interpret the third DrawPic() parameter inversely,
+ * with the opposite default value (obviously).
+ * Also, they used 15 priority zones from 42 to 200 instead of 14 priority
+ * zones from 42 to 190.
+ */
+ bool usesOldGfxFunctions() { return detectGfxFunctionsType() == SCI_VERSION_0_EARLY; }
+
+ /**
+ * Autodetects the Bresenham routine used in the actor movement functions
+ * @return Move count type, kIncrementMoveCnt / kIgnoreMoveCnt
+ */
+ MoveCountType detectMoveCountType();
+
+ bool handleMoveCount() { return detectMoveCountType() == kIncrementMoveCount; }
+
+ bool usesCdTrack() { return _usesCdTrack; }
+
+private:
+ bool autoDetectFeature(FeatureDetection featureDetection, int methodNum = -1);
+
+ SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType;
+#ifdef ENABLE_SCI32
+ SciVersion _sci21KernelType;
+#endif
+
+ MoveCountType _moveCountType;
+ bool _usesCdTrack;
+
+ SegManager *_segMan;
+ Kernel *_kernel;
+ reg_t _gameObj;
+ Common::String _gameId;
+};
+
+} // End of namespace Sci
+
+#endif // SCI_INCLUDE_ENGINE_H
diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp
index 9baa9f7c6e..b04dfc32c2 100644
--- a/engines/sci/engine/game.cpp
+++ b/engines/sci/engine/game.cpp
@@ -221,7 +221,7 @@ int script_init_engine(EngineState *s) {
s->_breakpoints.clear(); // No breakpoints defined
s->_activeBreakpointTypes = 0;
- if (s->detectLofsType() == SCI_VERSION_1_MIDDLE)
+ if (s->_features->detectLofsType() == SCI_VERSION_1_MIDDLE)
s->_segMan->setExportAreWide(true);
else
s->_segMan->setExportAreWide(false);
@@ -280,7 +280,7 @@ int game_init(EngineState *s) {
#ifdef USE_OLD_MUSIC_FUNCTIONS
if (s->sfx_init_flags & SFX_STATE_FLAG_NOSOUND)
- game_init_sound(s, 0, s->detectDoSoundType());
+ game_init_sound(s, 0, s->_features->detectDoSoundType());
#endif
// Load game language into printLang property of game object
@@ -296,7 +296,7 @@ int game_exit(EngineState *s) {
#ifdef USE_OLD_MUSIC_FUNCTIONS
s->_sound.sfx_exit();
// Reinit because some other code depends on having a valid state
- game_init_sound(s, SFX_STATE_FLAG_NOSOUND, s->detectDoSoundType());
+ game_init_sound(s, SFX_STATE_FLAG_NOSOUND, s->_features->detectDoSoundType());
#else
s->_audio->stopAllAudio();
s->_soundCmd->clearPlayList();
diff --git a/engines/sci/engine/kernel32.cpp b/engines/sci/engine/kernel32.cpp
index 917637a7c1..06d1b8b044 100644
--- a/engines/sci/engine/kernel32.cpp
+++ b/engines/sci/engine/kernel32.cpp
@@ -382,7 +382,7 @@ void Kernel::setKernelNamesSci21(EngineState *s) {
// This is interesting because they all have the same interpreter version (2.100.002), yet
// they would not be compatible with other games of the same interpreter.
- if (s->detectSci21KernelType() == SCI_VERSION_2) {
+ if (s->_features->detectSci21KernelType() == SCI_VERSION_2) {
_kernelNames = Common::StringList(sci2_default_knames, kKernelEntriesGk2Demo);
// OnMe is IsOnMe here, but they should be compatible
_kernelNames[0x23] = "Robot"; // Graph in SCI2
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index d1a4e8eae6..40ea736a8c 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -151,7 +151,7 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
}
#ifndef USE_OLD_MUSIC_FUNCTIONS
- if (s->detectDoSoundType() <= SCI_VERSION_0_LATE) {
+ if (s->_features->detectDoSoundType() <= SCI_VERSION_0_LATE) {
// If we're running a SCI0 game, update the sound cues, to compensate
// for the fact that SCI0 does not poll to update the sound cues itself,
// like SCI01 and later do with cmdUpdateSoundCues. kGetEvent is called
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index f14f20c97a..762b786a79 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -190,7 +190,7 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
}
reg_t kSetCursor(EngineState *s, int argc, reg_t *argv) {
- switch (s->detectSetCursorType()) {
+ switch (s->_features->detectSetCursorType()) {
case SCI_VERSION_0_EARLY:
return kSetCursorSci0(s, argc, argv);
case SCI_VERSION_1_1:
@@ -553,7 +553,7 @@ reg_t kDrawPic(EngineState *s, int argc, reg_t *argv) {
if (argc >= 3) {
if (!argv[2].isNull())
addToFlag = true;
- if (!s->usesOldGfxFunctions())
+ if (!s->_features->usesOldGfxFunctions())
addToFlag = !addToFlag;
}
if (argc >= 4)
diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp
index dc4c2ffc93..6a2d468531 100644
--- a/engines/sci/engine/kmovement.cpp
+++ b/engines/sci/engine/kmovement.cpp
@@ -267,7 +267,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
//printf("movecnt %d, move speed %d\n", movcnt, max_movcnt);
- if (s->handleMoveCount()) {
+ if (s->_features->handleMoveCount()) {
if (max_movcnt > movcnt) {
++movcnt;
PUT_SEL32V(segMan, mover, b_movCnt, movcnt); // Needed for HQ1/Ogre?
diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp
index 5352774812..8106a19aec 100644
--- a/engines/sci/engine/ksound.cpp
+++ b/engines/sci/engine/ksound.cpp
@@ -90,7 +90,7 @@ reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) {
reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
// JonesCD uses different functions based on the cdaudio.map file
// to use red book tracks.
- if (s->usesCdTrack())
+ if (s->_features->usesCdTrack())
return kDoCdAudio(s, argc, argv);
Audio::Mixer *mixer = g_system->getMixer();
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index d322a80c15..377c676392 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -976,7 +976,7 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
#ifdef USE_OLD_MUSIC_FUNCTIONS
temp = retval->_sound._songlib;
- retval->_sound.sfx_init(retval->resMan, s->sfx_init_flags, s->detectDoSoundType());
+ retval->_sound.sfx_init(retval->resMan, s->sfx_init_flags, s->_features->detectDoSoundType());
retval->sfx_init_flags = s->sfx_init_flags;
retval->_sound._songlib.freeSounds();
retval->_sound._songlib = temp;
@@ -1008,6 +1008,8 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
retval->successor = NULL;
retval->_gameId = s->_gameId;
+ retval->_features->setGameInfo(retval->_gameObj, retval->_gameId);
+
#ifdef USE_OLD_MUSIC_FUNCTIONS
retval->_sound._it = NULL;
retval->_sound._flags = s->_sound._flags;
@@ -1028,7 +1030,7 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
} else {
#endif
retval->_gui->resetEngineState(retval);
- retval->_gui->init(retval->usesOldGfxFunctions());
+ retval->_gui->init(retval->_features->usesOldGfxFunctions());
#ifdef ENABLE_SCI32
}
#endif
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 32bc9182f3..58302b1aeb 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -95,7 +95,7 @@ opcode_format g_opcode_formats[128][4] = {
// constructor (?) of a VirtualMachine or a ScriptManager class.
void script_adjust_opcode_formats(EngineState *s) {
// TODO: Check that this is correct
- if (s->detectLofsType() != SCI_VERSION_0_EARLY) {
+ if (s->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
g_opcode_formats[op_lofsa][0] = Script_Offset;
g_opcode_formats[op_lofss][0] = Script_Offset;
}
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index dbbbb9d499..89078e6381 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -40,6 +40,8 @@ EngineState::EngineState(ResourceManager *res, Kernel *kernel, Vocabulary *voc,
sfx_init_flags = 0;
#endif
+ _features = new GameFeatures(_segMan, _kernel);
+
restarting_flags = 0;
last_wait_time = 0;
@@ -72,24 +74,13 @@ EngineState::EngineState(ResourceManager *res, Kernel *kernel, Vocabulary *voc,
_throttleLastTime = 0;
_throttleTrigger = false;
- _setCursorType = SCI_VERSION_NONE;
- _doSoundType = SCI_VERSION_NONE;
- _lofsType = SCI_VERSION_NONE;
- _gfxFunctionsType = SCI_VERSION_NONE;
- _moveCountType = kMoveCountUninitialized;
-
-#ifdef ENABLE_SCI32
- _sci21KernelType = SCI_VERSION_NONE;
-#endif
-
_memorySegmentSize = 0;
- _usesCdTrack = Common::File::exists("cdaudio.map");
-
_soundCmd = 0;
}
EngineState::~EngineState() {
+ delete _features;
delete _msgState;
}
@@ -218,472 +209,4 @@ Common::String EngineState::strSplit(const char *str, const char *sep) {
return retval;
}
-bool EngineState::autoDetectFeature(FeatureDetection featureDetection, int methodNum) {
- Common::String objName;
- Selector slc = 0;
- reg_t objAddr;
- bool foundTarget = false;
-
- // Get address of target script
- switch (featureDetection) {
- case kDetectGfxFunctions:
- objName = "Rm";
- objAddr = _segMan->findObjectByName(objName);
- slc = _kernel->_selectorCache.overlay;
- break;
- case kDetectMoveCountType:
- objName = "Motion";
- objAddr = _segMan->findObjectByName(objName);
- slc = _kernel->_selectorCache.doit;
- break;
- case kDetectSoundType:
- objName = "Sound";
- objAddr = _segMan->findObjectByName(objName);
- slc = _kernel->_selectorCache.play;
- break;
- case kDetectSetCursorType:
- objName = "Game";
- objAddr = _segMan->findObjectByName(objName);
- // KQ5CD overrides the default setCursor selector of the Game object,
- // so we need to handle this separately
- // KQ5 PC floppy is early SCI1, Amiga middle SCI1, and CD late SCI1
- if (_gameId == "kq5" && getSciVersion() == SCI_VERSION_1_LATE)
- objAddr = _gameObj;
- slc = _kernel->_selectorCache.setCursor;
- break;
- case kDetectLofsType:
- objName = "Game";
- objAddr = _segMan->findObjectByName(objName);
- break;
-#ifdef ENABLE_SCI32
- case kDetectSci21KernelTable:
- objName = "Sound";
- objAddr = _segMan->findObjectByName(objName);
- slc = _kernel->_selectorCache.play;
- break;
-#endif
- default:
- warning("autoDetectFeature: invalid featureDetection value %x", featureDetection);
- return false;
- }
-
- reg_t addr;
- if (objAddr.isNull()) {
- warning("autoDetectFeature: %s object couldn't be found", objName.c_str());
- return false;
- }
-
- if (methodNum == -1) {
- if (lookup_selector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) {
- warning("autoDetectFeature: target selector is not a method of object %s", objName.c_str());
- return false;
- }
- } else {
- addr = _segMan->getObject(objAddr)->getFunction(methodNum);
- }
-
- uint16 offset = addr.offset;
- Script *script = _segMan->getScript(addr.segment);
- uint16 intParam = 0xFFFF;
-
- do {
- uint16 kFuncNum;
- int opsize = script->_buf[offset++];
- uint opcode = opsize >> 1;
- int i = 0;
- byte argc;
-
- if (featureDetection == kDetectLofsType) {
- if (opcode == op_lofsa || opcode == op_lofss) {
- uint16 lofs;
-
- // Load lofs operand
- if (opsize & 1) {
- if (offset >= script->_bufSize)
- break;
- lofs = script->_buf[offset++];
- } else {
- if ((uint32)offset + 1 >= (uint32)script->_bufSize)
- break;
- lofs = READ_LE_UINT16(script->_buf + offset);
- offset += 2;
- }
-
- // Check for going out of bounds when interpreting as abs/rel
- if (lofs >= script->_bufSize)
- _lofsType = SCI_VERSION_0_EARLY;
-
- if ((signed)offset + (int16)lofs < 0)
- _lofsType = SCI_VERSION_1_MIDDLE;
-
- if ((signed)offset + (int16)lofs >= (signed)script->_bufSize)
- _lofsType = SCI_VERSION_1_MIDDLE;
-
- if (_lofsType != SCI_VERSION_NONE)
- return true;
-
- // If we reach here, we haven't been able to deduce the lofs parameter
- // type, but we have advanced the offset pointer already. So move on
- // to the next opcode
- continue;
- }
- }
-
- if (featureDetection == kDetectSoundType) {
- // The play method of the Sound object pushes the DoSound command
- // that it'll use just before it calls DoSound. We intercept that here
- // in order to check what sound semantics are used, cause the position
- // of the sound commands has changed at some point during SCI1 middle
- if (opcode == op_pushi) {
- // Load the pushi parameter
- if (opsize & 1) {
- if (offset >= script->_bufSize)
- break;
- intParam = script->_buf[offset++];
- } else {
- if ((uint32)offset + 1 >= (uint32)script->_bufSize)
- break;
- intParam = READ_LE_UINT16(script->_buf + offset);
- offset += 2;
- }
-
- continue;
- }
- }
-
- 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 = script->_buf[offset++];
- else {
- kFuncNum = 0xffff & (script->_buf[offset] | (script->_buf[offset + 1] << 8));
- offset += 2;
- }
-
- if (opcode == op_callk) {
- argc = script->_buf[offset++];
-
- switch (featureDetection) {
- case kDetectGfxFunctions:
- if (kFuncNum == 8) { // kDrawPic (SCI0 - SCI11)
- // 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
- _gfxFunctionsType = (argc == 8) ? SCI_VERSION_0_LATE : SCI_VERSION_0_EARLY;
- return true;
- }
- break;
- case kDetectMoveCountType:
- // Games which ignore move count call kAbs before calling kDoBresen
- if (_kernel->getKernelName(kFuncNum) == "Abs") {
- foundTarget = true;
- } else if (_kernel->getKernelName(kFuncNum) == "DoBresen") {
- _moveCountType = foundTarget ? kIgnoreMoveCount : kIncrementMoveCount;
- return true;
- }
- break;
- case kDetectSoundType:
- // Late SCI1 games call kIsObject before kDoSound
- if (kFuncNum == 6) { // kIsObject (SCI0-SCI11)
- foundTarget = true;
- } else if (kFuncNum == 45) { // kDoSound (SCI1)
- // First, check which DoSound function is called by the play method of
- // the Sound object
- switch (intParam) {
- case 1:
- _doSoundType = SCI_VERSION_0_EARLY;
- break;
- case 7:
- _doSoundType = SCI_VERSION_1_EARLY;
- break;
- case 8:
- _doSoundType = SCI_VERSION_1_LATE;
- break;
- default:
- // Unknown case... should never happen. We fall back to
- // alternative detection here, which works in general, apart from
- // some transitive games like Jones CD
- _doSoundType = foundTarget ? SCI_VERSION_1_LATE : SCI_VERSION_1_EARLY;
- break;
- }
-
- if (_doSoundType != SCI_VERSION_NONE)
- return true;
- }
- break;
- case kDetectSetCursorType:
- // Games with colored mouse cursors call kIsObject before kSetCursor
- if (kFuncNum == 6) { // kIsObject (SCI0-SCI11)
- foundTarget = true;
- } else if (kFuncNum == 40) { // kSetCursor (SCI0-SCI11)
- _setCursorType = foundTarget ? SCI_VERSION_1_1 : SCI_VERSION_0_EARLY;
- return true;
- }
- break;
-#ifdef ENABLE_SCI32
- case kDetectSci21KernelTable:
- if (kFuncNum == 0x40) {
- _sci21KernelType = SCI_VERSION_2;
- return true;
- } else if (kFuncNum == 0x75) {
- _sci21KernelType = SCI_VERSION_2_1;
- return true;
- }
- break;
-#endif
- 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);
-
- // Some games, like KQ5CD, never actually call SetCursor inside Game::setCursor
- // but call isObject. Cover this case here, if we're actually reading the selector
- // itself, and not iterating through the Game object (i.e. when the selector
- // dictionary is missing)
- if (featureDetection == kDetectSetCursorType && methodNum == -1 && foundTarget) {
- _setCursorType = SCI_VERSION_1_1;
- return true;
- }
-
- return false; // not found
-}
-
-SciVersion EngineState::detectDoSoundType() {
- if (_doSoundType == SCI_VERSION_NONE) {
- if (getSciVersion() == SCI_VERSION_0_EARLY) {
- // This game is using early SCI0 sound code (different headers than SCI0 late)
- _doSoundType = SCI_VERSION_0_EARLY;
- } else if (_kernel->_selectorCache.nodePtr == -1) {
- // No nodePtr selector, so this game is definitely using newer
- // SCI0 sound code (i.e. SCI_VERSION_0_LATE)
- _doSoundType = SCI_VERSION_0_LATE;
- } else {
- if (getSciVersion() >= SCI_VERSION_1_LATE) {
- // All SCI1 late games use the newer doSound semantics
- _doSoundType = SCI_VERSION_1_LATE;
- } else {
- if (!autoDetectFeature(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;
- }
- }
- }
-
- debugC(1, kDebugLevelSound, "Detected DoSound type: %s", getSciVersionDesc(_doSoundType).c_str());
- }
-
- return _doSoundType;
-}
-
-SciVersion EngineState::detectSetCursorType() {
- if (_setCursorType == SCI_VERSION_NONE) {
- if (getSciVersion() <= SCI_VERSION_01) {
- // SCI0/SCI01 games never use cursor views
- _setCursorType = SCI_VERSION_0_EARLY;
- } else if (getSciVersion() >= SCI_VERSION_1_EARLY && getSciVersion() <= SCI_VERSION_1_MIDDLE) {
- // SCI1 early/SCI1 middle games never use cursor views
- _setCursorType = SCI_VERSION_0_EARLY;
- } else if (getSciVersion() >= SCI_VERSION_1_1) {
- // SCI1.1 games always use cursor views
- _setCursorType = SCI_VERSION_1_1;
- } else { // SCI1 late game, detect cursor semantics
- bool found = false;
-
- if (_kernel->_selectorCache.setCursor == -1) {
- // Find which function of the Game object calls setCursor
-
- Object *obj = _segMan->getObject(_gameObj);
- for (uint m = 0; m < obj->getMethodCount(); m++) {
- found = autoDetectFeature(kDetectSetCursorType, m);
- if (found)
- break;
- }
- } else {
- found = autoDetectFeature(kDetectSetCursorType);
- }
-
- if (!found) {
- // Quite normal in several demos which don't have a cursor
- 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;
- }
- }
-
- debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType).c_str());
- }
-
- return _setCursorType;
-}
-
-SciVersion EngineState::detectLofsType() {
- if (_lofsType == SCI_VERSION_NONE) {
- // This detection only works (and is only needed) for SCI 1
- if (getSciVersion() <= SCI_VERSION_01) {
- _lofsType = SCI_VERSION_0_EARLY;
- return _lofsType;
- }
-
- if (getSciVersion() >= SCI_VERSION_1_1) {
- _lofsType = SCI_VERSION_1_1;
- return _lofsType;
- }
-
- // Find a function of the game object which invokes lofsa/lofss
- reg_t gameClass = _segMan->findObjectByName("Game");
- Object *obj = _segMan->getObject(gameClass);
- bool found = false;
-
- for (uint m = 0; m < obj->getMethodCount(); m++) {
- found = autoDetectFeature(kDetectLofsType, m);
-
- if (found)
- break;
- }
-
- if (!found) {
- warning("Lofs detection failed, taking an educated guess");
-
- if (getSciVersion() >= SCI_VERSION_1_MIDDLE)
- _lofsType = SCI_VERSION_1_MIDDLE;
- else
- _lofsType = SCI_VERSION_0_EARLY;
- }
-
- debugC(1, kDebugLevelVM, "Detected Lofs type: %s", getSciVersionDesc(_lofsType).c_str());
- }
-
- return _lofsType;
-}
-
-SciVersion EngineState::detectGfxFunctionsType() {
- if (_gfxFunctionsType == SCI_VERSION_NONE) {
- // This detection only works (and is only needed) for SCI0 games
- if (getSciVersion() >= SCI_VERSION_01) {
- _gfxFunctionsType = SCI_VERSION_0_LATE;
- return _gfxFunctionsType;
- }
-
- if (getSciVersion() > SCI_VERSION_0_EARLY) {
- // Check if the game is using an overlay
- bool found = false;
-
- if (_kernel->_selectorCache.overlay == -1) {
- // No overlay selector found, check if any method of the Rm object
- // is calling kDrawPic, as the overlay selector might be missing in demos
-
- Object *obj = _segMan->getObject(_segMan->findObjectByName("Rm"));
- for (uint m = 0; m < obj->getMethodCount(); m++) {
- found = autoDetectFeature(kDetectGfxFunctions, m);
- if (found)
- break;
- }
- }
-
- if (_kernel->_selectorCache.overlay == -1 && !found) {
- // No overlay selector found, therefore the game is definitely
- // using old graphics functions
- _gfxFunctionsType = SCI_VERSION_0_EARLY;
- } else if (_kernel->_selectorCache.overlay == -1 && found) {
- // Detection already done above
- } else { // _kernel->_selectorCache.overlay != -1
- // 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 (!autoDetectFeature(kDetectGfxFunctions)) {
- warning("Graphics functions detection failed, taking an educated guess");
-
- // Try detecting the graphics function types from the existence of the motionCue
- // selector (which is a bit of a hack)
- if (_kernel->findSelector("motionCue") != -1)
- _gfxFunctionsType = SCI_VERSION_0_LATE;
- else
- _gfxFunctionsType = SCI_VERSION_0_EARLY;
- }
- }
- } else { // (getSciVersion() == SCI_VERSION_0_EARLY)
- // Old SCI0 games always used old graphics functions
- _gfxFunctionsType = SCI_VERSION_0_EARLY;
- }
-
- debugC(1, kDebugLevelVM, "Detected graphics functions type: %s", getSciVersionDesc(_gfxFunctionsType).c_str());
- }
-
- return _gfxFunctionsType;
-}
-
-#ifdef ENABLE_SCI32
-SciVersion EngineState::detectSci21KernelType() {
- if (_sci21KernelType == SCI_VERSION_NONE)
- if (!autoDetectFeature(kDetectSci21KernelTable))
- error("Could not detect the SCI2.1 kernel table type");
-
- debugC(1, kDebugLevelVM, "Detected SCI2.1 kernel type: %s", getSciVersionDesc(_sci21KernelType).c_str());
-
- return _sci21KernelType;
-}
-#endif
-
-MoveCountType EngineState::detectMoveCountType() {
- if (_moveCountType == kMoveCountUninitialized) {
- // SCI0/SCI01 games always increment move count
- if (getSciVersion() <= SCI_VERSION_01) {
- _moveCountType = kIncrementMoveCount;
- } else {
- if (!autoDetectFeature(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");
- }
-
- return _moveCountType;
-}
-
} // End of namespace Sci
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index a956fd9563..fb39e7d4ca 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -37,6 +37,7 @@ namespace Common {
#include "sci/sci.h"
#include "sci/resource.h"
+#include "sci/engine/features.h"
#include "sci/engine/seg_manager.h"
#include "sci/parser/vocabulary.h"
@@ -113,15 +114,6 @@ enum kLanguage {
K_LANG_PORTUGUESE = 351
};
-enum FeatureDetection {
- kDetectGfxFunctions = 0,
- kDetectMoveCountType = 1,
- kDetectSoundType = 2,
- kDetectSetCursorType = 3,
- kDetectLofsType = 4,
- kDetectSci21KernelTable = 5
-};
-
class FileHandle {
public:
Common::String _name;
@@ -151,6 +143,8 @@ public:
Common::String _gameId; /**< Designation of the primary object (which inherits from Game) */
+ GameFeatures *_features;
+
/* Non-VM information */
GfxAnimate *_gfxAnimate; // Animate for 16-bit gfx
@@ -257,75 +251,6 @@ public:
private:
kLanguage charToLanguage(const char c) const;
-
-
-public:
- // TODO: The following methods and member variables deal with (detecting)
- // features and capabilities the active game expects to find in the engine.
- // It should likely be moved to a separate class.
-
- /**
- * Autodetects the DoSound type
- * @return DoSound type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE /
- * SCI_VERSION_1_EARLY / SCI_VERSION_1_LATE
- */
- SciVersion detectDoSoundType();
-
- /**
- * Autodetects the 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();
-
- /**
- * Autodetects the graphics functions used
- * @return Graphics functions type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE
- */
- SciVersion detectGfxFunctionsType();
-
-#ifdef ENABLE_SCI32
- /**
- * Autodetects the kernel functions used in SCI2.1
- * @return Graphics functions type, SCI_VERSION_2 / SCI_VERSION_2_1
- */
- SciVersion detectSci21KernelType();
-#endif
-
- /**
- * Applies to all versions before 0.000.502
- * Old SCI versions used to interpret the third DrawPic() parameter inversely,
- * with the opposite default value (obviously).
- * Also, they used 15 priority zones from 42 to 200 instead of 14 priority
- * zones from 42 to 190.
- */
- bool usesOldGfxFunctions() { return detectGfxFunctionsType() == SCI_VERSION_0_EARLY; }
-
- /**
- * Autodetects the Bresenham routine used in the actor movement functions
- * @return Move count type, kIncrementMoveCnt / kIgnoreMoveCnt
- */
- MoveCountType detectMoveCountType();
-
- bool handleMoveCount() { return detectMoveCountType() == kIncrementMoveCount; }
-
- bool usesCdTrack() { return _usesCdTrack; }
-
-private:
- bool autoDetectFeature(FeatureDetection featureDetection, int methodNum = -1);
-
- SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType;
-#ifdef ENABLE_SCI32
- SciVersion _sci21KernelType;
-#endif
-
- MoveCountType _moveCountType;
- bool _usesCdTrack;
};
} // End of namespace Sci
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 7c3e5cb03d..67f2fa13ea 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -1361,7 +1361,7 @@ void run_vm(EngineState *s, bool restoring) {
case op_lofsa: // 0x39 (57)
s->r_acc.segment = scriptState.xs->addr.pc.segment;
- switch (s->detectLofsType()) {
+ switch (s->_features->detectLofsType()) {
case SCI_VERSION_1_1:
s->r_acc.offset = opparams[0] + local_script->_scriptSize;
break;
@@ -1383,7 +1383,7 @@ void run_vm(EngineState *s, bool restoring) {
case op_lofss: // 0x3a (58)
r_temp.segment = scriptState.xs->addr.pc.segment;
- switch (s->detectLofsType()) {
+ switch (s->_features->detectLofsType()) {
case SCI_VERSION_1_1:
r_temp.offset = opparams[0] + local_script->_scriptSize;
break;
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 3d68ce1092..9ebb66f02a 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS := \
event.o \
resource.o \
sci.o \
+ engine/features.o \
engine/game.o \
engine/gc.o \
engine/kernel.o \
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index c03b9dbc68..e01e0fdac0 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -209,7 +209,9 @@ Common::Error SciEngine::run() {
assert(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value != 0);
strcpy(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value, "");
- SciVersion soundVersion = _gamestate->detectDoSoundType();
+ _gamestate->_features->setGameInfo(_gamestate->_gameObj, _gamestate->_gameId);
+
+ SciVersion soundVersion = _gamestate->_features->detectDoSoundType();
_gamestate->_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, soundVersion);
@@ -229,7 +231,7 @@ Common::Error SciEngine::run() {
_gamestate->_gui32->init();
else
#endif
- _gamestate->_gui->init(_gamestate->usesOldGfxFunctions());
+ _gamestate->_gui->init(_gamestate->_features->usesOldGfxFunctions());
debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion()).c_str());