SciVersion GameFeatures::detectMessageFunctionType() { if (_messageFunctionType != SCI_VERSION_NONE) return _messageFunctionType; if (getSciVersion() > SCI_VERSION_1_1) { _messageFunctionType = SCI_VERSION_1_1; return _messageFunctionType; } else if (getSciVersion() < SCI_VERSION_1_1) { _messageFunctionType = SCI_VERSION_1_LATE; return _messageFunctionType; } Common::List<ResourceId> resources = g_sci->getResMan()->listResources(kResourceTypeMessage, -1); if (resources.empty()) { // No messages found, so this doesn't really matter anyway... _messageFunctionType = SCI_VERSION_1_1; return _messageFunctionType; } Resource *res = g_sci->getResMan()->findResource(*resources.begin(), false); assert(res); // Only v2 Message resources use the kGetMessage kernel function. // v3-v5 use the kMessage kernel function. if (READ_SCI11ENDIAN_UINT32(res->data) / 1000 == 2) _messageFunctionType = SCI_VERSION_1_LATE; else _messageFunctionType = SCI_VERSION_1_1; debugC(1, kDebugLevelVM, "Detected message function type: %s", getSciVersionDesc(_messageFunctionType)); return _messageFunctionType; }
SciVersion GameFeatures::detectDoSoundType() { if (_doSoundType == SCI_VERSION_NONE) { if (getSciVersion() == SCI_VERSION_0_EARLY) { // Almost all of the SCI0EARLY games use different sound resources than // SCI0LATE. Although the last SCI0EARLY game (lsl2) uses SCI0LATE resources _doSoundType = g_sci->getResMan()->detectEarlySound() ? SCI_VERSION_0_EARLY : SCI_VERSION_0_LATE; #ifdef ENABLE_SCI32 } else if (getSciVersion() >= SCI_VERSION_2_1_EARLY) { _doSoundType = SCI_VERSION_2_1_EARLY; #endif } else if (SELECTOR(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 (!autoDetectSoundType()) { 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)); } return _doSoundType; }
SciVersion GameFeatures::detectSetCursorType() { if (_setCursorType == SCI_VERSION_NONE) { if (getSciVersion() <= SCI_VERSION_1_MIDDLE) { // SCI1 middle and older 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 // If the Cursor object doesn't exist, we're using the SCI0 early // kSetCursor semantics. if (_segMan->findObjectByName("Cursor") == NULL_REG) { _setCursorType = SCI_VERSION_0_EARLY; debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType)); return _setCursorType; } // Check for the existence of the handCursor object (first found). // This is based on KQ5. reg_t objAddr = _segMan->findObjectByName("handCursor", 0); // If that doesn't exist, we assume it uses SCI1.1 kSetCursor semantics if (objAddr == NULL_REG) { _setCursorType = SCI_VERSION_1_1; debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType)); return _setCursorType; } // Now we check what the number variable holds in the handCursor // object. uint16 number = readSelectorValue(_segMan, objAddr, SELECTOR(number)); // If the number is 0, it uses views and therefore the SCI1.1 // kSetCursor semantics, otherwise it uses the SCI0 early kSetCursor // semantics. if (number == 0) _setCursorType = SCI_VERSION_1_1; else _setCursorType = SCI_VERSION_0_EARLY; } debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType)); } return _setCursorType; }
SciVersion GameFeatures::detectSci21KernelType() { if (_sci21KernelType == SCI_VERSION_NONE) { if (!autoDetectSci21KernelType()) error("Could not detect the SCI2.1 kernel table type"); debugC(1, kDebugLevelVM, "Detected SCI2.1 kernel type: %s", getSciVersionDesc(_sci21KernelType)); } return _sci21KernelType; }
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 && getSciVersion() <= SCI_VERSION_2_1) { // SCI1.1 type, i.e. we compensate for the fact that the heap is attached // to the end of the script _lofsType = SCI_VERSION_1_1; return _lofsType; } if (getSciVersion() == SCI_VERSION_3) { // SCI3 type, same as pre-SCI1.1, really, as there is no separate heap // resource _lofsType = SCI_VERSION_3; return _lofsType; } // Find a function of the "Game" object (which is the game super class) which invokes lofsa/lofss reg_t gameSuperClass = g_sci->getGameSuperClassAddress(); bool found = false; if (!gameSuperClass.isNull()) { Common::String gameSuperClassName = _segMan->getObjectName(gameSuperClass); const Object *gameSuperObject = _segMan->getObject(gameSuperClass); if (gameSuperObject) { for (uint m = 0; m < gameSuperObject->getMethodCount(); m++) { found = autoDetectLofsType(gameSuperClassName, m); if (found) break; } } else { warning("detectLofsType(): Could not get superclass object"); } } else { warning("detectLofsType(): Could not find superclass of game object"); } if (!found) { warning("detectLofsType(): 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)); } return _lofsType; }
Common::Error SciEngine::run() { g_eventRec.registerRandomSource(_rng, "sci"); // Assign default values to the config manager, in case settings are missing ConfMan.registerDefault("sci_originalsaveload", "false"); ConfMan.registerDefault("native_fb01", "false"); ConfMan.registerDefault("windows_cursors", "false"); // Windows cursors for KQ6 Windows _resMan = new ResourceManager(); assert(_resMan); _resMan->addAppropriateSources(); _resMan->init(); // TODO: Add error handling. Check return values of addAppropriateSources // and init. We first have to *add* sensible return values, though ;). /* if (!_resMan) { warning("No resources found, aborting"); return Common::kNoGameDataFoundError; } */ // Reset, so that error()s before SoundCommandParser is initialized wont cause a crash _soundCmd = NULL; // Add the after market GM patches for the specified game, if they exist _resMan->addNewGMPatch(_gameId); _gameObjectAddress = _resMan->findGameObject(); _gameSuperClassAddress = NULL_REG; SegManager *segMan = new SegManager(_resMan); // Initialize the game screen _gfxScreen = new GfxScreen(_resMan); _gfxScreen->debugUnditherSetState(ConfMan.getBool("disable_dithering")); // Create debugger console. It requires GFX to be initialized _console = new Console(this); _kernel = new Kernel(_resMan, segMan); _features = new GameFeatures(segMan, _kernel); // Only SCI0, SCI01 and SCI1 EGA games used a parser _vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA) ? new Vocabulary(_resMan, false) : NULL; // Also, XMAS1990 apparently had a parser too. Refer to http://forums.scummvm.org/viewtopic.php?t=9135 if (getGameId() == GID_CHRISTMAS1990) _vocabulary = new Vocabulary(_resMan, false); _audio = new AudioPlayer(_resMan); _gamestate = new EngineState(segMan); _eventMan = new EventManager(_resMan->detectFontExtended()); // The game needs to be initialized before the graphics system is initialized, as // the graphics code checks parts of the seg manager upon initialization (e.g. for // the presence of the fastCast object) if (!initGame()) { /* Initialize */ warning("Game initialization failed: Aborting..."); // TODO: Add an "init failed" error? return Common::kUnknownError; } // we try to find the super class address of the game object, we can't do that earlier const Object *gameObject = segMan->getObject(_gameObjectAddress); if (!gameObject) { warning("Could not get game object, aborting..."); return Common::kUnknownError; } _gameSuperClassAddress = gameObject->getSuperClassSelector(); script_adjust_opcode_formats(); // Must be called after game_init(), as they use _features _kernel->loadKernelNames(_features); _soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, _features->detectDoSoundType()); syncSoundSettings(); syncIngameAudioOptions(); // Initialize all graphics related subsystems initGraphics(); debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion())); // Patch in our save/restore code, so that dialogs are replaced patchGameSaveRestore(); setLauncherLanguage(); // Check whether loading a savestate was requested int directSaveSlotLoading = ConfMan.getInt("save_slot"); if (directSaveSlotLoading >= 0) { // call GameObject::play (like normally) initStackBaseWithSelector(SELECTOR(play)); // We set this, so that the game automatically quit right after init _gamestate->variables[VAR_GLOBAL][4] = TRUE_REG; _gamestate->_executionStackPosChanged = false; run_vm(_gamestate); // As soon as we get control again, actually restore the game reg_t restoreArgv[2] = { NULL_REG, make_reg(0, directSaveSlotLoading) }; // special call (argv[0] is NULL) kRestoreGame(_gamestate, 2, restoreArgv); // this indirectly calls GameObject::init, which will setup menu, text font/color codes etc. // without this games would be pretty badly broken } // Show any special warnings for buggy scripts with severe game bugs, // which have been patched by Sierra if (getGameId() == GID_LONGBOW) { // Longbow 1.0 has a buggy script which prevents the game // from progressing during the Green Man riddle sequence. // A patch for this buggy script has been released by Sierra, // and is necessary to complete the game without issues. // The patched script is included in Longbow 1.1. // Refer to bug #3036609. Resource *buggyScript = _resMan->findResource(ResourceId(kResourceTypeScript, 180), 0); if (buggyScript && (buggyScript->size == 12354 || buggyScript->size == 12362)) { showScummVMDialog("A known buggy game script has been detected, which could " "prevent you from progressing later on in the game, during " "the sequence with the Green Man's riddles. Please, apply " "the latest patch for this game by Sierra to avoid possible " "problems"); } } // Show a warning if the user has selected a General MIDI device, no GM patch exists // (i.e. patch 4) and the game is one of the known 8 SCI1 games that Sierra has provided // after market patches for in their "General MIDI Utility". if (_soundCmd->getMusicType() == MT_GM && !ConfMan.getBool("native_mt32")) { if (!_resMan->findResource(ResourceId(kResourceTypePatch, 4), 0)) { switch (getGameId()) { case GID_ECOQUEST: case GID_HOYLE3: case GID_LSL1: case GID_LSL5: case GID_LONGBOW: case GID_SQ1: case GID_SQ4: case GID_FAIRYTALES: showScummVMDialog("You have selected General MIDI as a sound device. Sierra " "has provided after-market support for General MIDI for this " "game in their \"General MIDI Utility\". Please, apply this " "patch in order to enjoy MIDI music with this game. Once you " "have obtained it, you can unpack all of the included *.PAT " "files in your ScummVM extras folder and ScummVM will add the " "appropriate patch automatically. Alternatively, you can follow " "the instructions in the READ.ME file included in the patch and " "rename the associated *.PAT file to 4.PAT and place it in the " "game folder. Without this patch, General MIDI music for this " "game will sound badly distorted."); break; default: break; } } } if (gameHasFanMadePatch()) { showScummVMDialog("Your game is patched with a fan made script patch. Such patches have " "been reported to cause issues, as they modify game scripts extensively. " "The issues that these patches fix do not occur in ScummVM, so you are " "advised to remove this patch from your game folder in order to avoid " "having unexpected errors and/or issues later on."); } runGame(); ConfMan.flushToDisk(); return Common::kNoError; }
SciVersion GameFeatures::detectGfxFunctionsType() { if (_gfxFunctionsType == SCI_VERSION_NONE) { if (getSciVersion() == SCI_VERSION_0_EARLY) { // Old SCI0 games always used old graphics functions _gfxFunctionsType = SCI_VERSION_0_EARLY; } else if (getSciVersion() >= SCI_VERSION_01) { // SCI01 and newer games always used new graphics functions _gfxFunctionsType = SCI_VERSION_0_LATE; } else { // SCI0 late // Check if the game is using an overlay bool searchRoomObj = false; reg_t rmObjAddr = _segMan->findObjectByName("Rm"); if (SELECTOR(overlay) != -1) { // The game has an overlay selector, check how it calls kDrawPic // to determine the graphics functions type used if (lookupSelector(_segMan, rmObjAddr, SELECTOR(overlay), NULL, NULL) == kSelectorMethod) { if (!autoDetectGfxFunctionsType()) { 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 { // The game has an overlay selector, but it's not a method // of the Rm object (like in Hoyle 1 and 2), so search for // other methods searchRoomObj = true; } } else { // The game doesn't have an overlay selector, so search for it // manually searchRoomObj = true; } if (searchRoomObj) { // If requested, check if any method of the Rm object is calling // kDrawPic, as the overlay selector might be missing in demos bool found = false; const Object *obj = _segMan->getObject(rmObjAddr); for (uint m = 0; m < obj->getMethodCount(); m++) { found = autoDetectGfxFunctionsType(m); if (found) break; } if (!found) { // No method of the Rm object is calling kDrawPic, thus the // game doesn't have overlays and is using older graphics // functions _gfxFunctionsType = SCI_VERSION_0_EARLY; } } } debugC(1, kDebugLevelVM, "Detected graphics functions type: %s", getSciVersionDesc(_gfxFunctionsType)); } return _gfxFunctionsType; }