mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-13 15:40:57 +00:00
![Colin Snover](/assets/img/avatar_default.png)
This commit implements all of the known plane transitions from SCI2 through SCI2.1mid games. Because kSetShowStyle is always called indirectly via the Styler game script, it is difficult to find all the places where transitions are used. As such, transitions that appeared to never be used have been added as stubs which will trigger a game crash with a message to report what was being done, so any missed transition types can be identified quickly and then implemented.
1166 lines
39 KiB
C++
1166 lines
39 KiB
C++
/* 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.
|
|
*
|
|
*/
|
|
|
|
#include "common/system.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/debug-channels.h"
|
|
|
|
#include "engines/advancedDetector.h"
|
|
#include "engines/util.h"
|
|
|
|
#include "sci/sci.h"
|
|
#include "sci/debug.h"
|
|
#include "sci/console.h"
|
|
#include "sci/event.h"
|
|
|
|
#include "sci/engine/features.h"
|
|
#include "sci/engine/message.h"
|
|
#include "sci/engine/object.h"
|
|
#include "sci/engine/state.h"
|
|
#include "sci/engine/kernel.h"
|
|
#include "sci/engine/script.h" // for script_adjust_opcode_formats
|
|
#include "sci/engine/script_patches.h"
|
|
#include "sci/engine/selector.h" // for SELECTOR
|
|
|
|
#include "sci/sound/audio.h"
|
|
#include "sci/sound/music.h"
|
|
#include "sci/sound/sync.h"
|
|
#include "sci/sound/soundcmd.h"
|
|
#include "sci/graphics/animate.h"
|
|
#include "sci/graphics/cache.h"
|
|
#include "sci/graphics/compare.h"
|
|
#include "sci/graphics/controls16.h"
|
|
#include "sci/graphics/coordadjuster.h"
|
|
#include "sci/graphics/cursor.h"
|
|
#include "sci/graphics/maciconbar.h"
|
|
#include "sci/graphics/menu.h"
|
|
#include "sci/graphics/paint16.h"
|
|
#include "sci/graphics/paint32.h"
|
|
#include "sci/graphics/picture.h"
|
|
#include "sci/graphics/ports.h"
|
|
#include "sci/graphics/palette.h"
|
|
#include "sci/graphics/remap.h"
|
|
#include "sci/graphics/screen.h"
|
|
#include "sci/graphics/text16.h"
|
|
#include "sci/graphics/transitions.h"
|
|
|
|
#ifdef ENABLE_SCI32
|
|
#include "sci/graphics/controls32.h"
|
|
#include "sci/graphics/frameout.h"
|
|
#include "sci/graphics/palette32.h"
|
|
#include "sci/graphics/remap32.h"
|
|
#include "sci/graphics/text32.h"
|
|
#include "sci/graphics/transitions32.h"
|
|
#include "sci/graphics/video32.h"
|
|
#include "sci/sound/audio32.h"
|
|
// TODO: Move this to video32
|
|
#include "sci/video/robot_decoder.h"
|
|
#endif
|
|
|
|
namespace Sci {
|
|
|
|
SciEngine *g_sci = 0;
|
|
|
|
|
|
class GfxDriver;
|
|
|
|
SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gameId)
|
|
: Engine(syst), _gameDescription(desc), _gameId(gameId), _rng("sci") {
|
|
|
|
assert(g_sci == 0);
|
|
g_sci = this;
|
|
|
|
_gfxMacIconBar = 0;
|
|
|
|
_audio = 0;
|
|
_sync = nullptr;
|
|
#ifdef ENABLE_SCI32
|
|
_audio32 = nullptr;
|
|
_video32 = nullptr;
|
|
#endif
|
|
_features = 0;
|
|
_resMan = 0;
|
|
_gamestate = 0;
|
|
_kernel = 0;
|
|
_vocabulary = 0;
|
|
_vocabularyLanguage = 1; // we load english vocabulary on startup
|
|
_eventMan = 0;
|
|
_console = 0;
|
|
_opcode_formats = 0;
|
|
|
|
_forceHiresGraphics = false;
|
|
|
|
// Set up the engine specific debug levels
|
|
DebugMan.addDebugChannel(kDebugLevelError, "Error", "Script error debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelNodes, "Lists", "Lists and nodes debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelGraphics, "Graphics", "Graphics debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelStrings, "Strings", "Strings debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelMemory, "Memory", "Memory debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelFuncCheck, "Func", "Function parameter debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelBresen, "Bresenham", "Bresenham algorithms debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelSound, "Sound", "Sound debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelBaseSetter, "Base", "Base Setter debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelParser, "Parser", "Parser debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelSaid, "Said", "Said specs debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelFile, "File", "File I/O debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelTime, "Time", "Time debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelRoom, "Room", "Room number debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelAvoidPath, "Pathfinding", "Pathfinding debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelDclInflate, "DCL", "DCL inflate debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelVM, "VM", "VM debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelScripts, "Scripts", "Notifies when scripts are unloaded");
|
|
DebugMan.addDebugChannel(kDebugLevelScriptPatcher, "ScriptPatcher", "Notifies when scripts are patched");
|
|
DebugMan.addDebugChannel(kDebugLevelWorkarounds, "Workarounds", "Notifies when workarounds are triggered");
|
|
DebugMan.addDebugChannel(kDebugLevelGC, "GC", "Garbage Collector debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelResMan, "ResMan", "Resource manager debugging");
|
|
DebugMan.addDebugChannel(kDebugLevelOnStartup, "OnStartup", "Enter debugger at start of game");
|
|
DebugMan.addDebugChannel(kDebugLevelDebugMode, "DebugMode", "Enable game debug mode at start of game");
|
|
|
|
const Common::FSNode gameDataDir(ConfMan.get("path"));
|
|
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "actors"); // KQ6 hi-res portraits
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "aud"); // resource.aud and audio files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "audio");// resource.aud and audio files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "audiosfx");// resource.aud and audio files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "wav"); // speech files in WAV format
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "sfx"); // music/sound files in WAV format
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "avi"); // AVI movie files for Windows versions
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "seq"); // SEQ movie files for DOS versions
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "robot"); // robot movie files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "robots"); // robot movie files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "movie"); // VMD movie files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "movies"); // VMD movie files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "vmd"); // VMD movie files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "duk"); // Duck movie files in Phantasmagoria 2
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "Robot Folder"); // Mac robot files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "Sound Folder"); // Mac audio files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "Voices Folder", 0, 2, true); // Mac audio36 files (recursive for Torin)
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "Voices"); // Mac audio36 files
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "VMD Folder"); // Mac VMD files
|
|
|
|
// Add the patches directory, except for KQ6CD; The patches folder in some versions of KQ6CD
|
|
// is for the demo of Phantasmagoria, included in the disk
|
|
if (_gameId != GID_KQ6)
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "patches"); // resource patches
|
|
}
|
|
|
|
SciEngine::~SciEngine() {
|
|
// Remove all of our debug levels here
|
|
DebugMan.clearAllDebugChannels();
|
|
|
|
#ifdef ENABLE_SCI32
|
|
delete _gfxControls32;
|
|
delete _gfxPaint32;
|
|
delete _gfxText32;
|
|
delete _robotDecoder;
|
|
// GfxFrameout and GfxPalette32 must be deleted after Video32 since
|
|
// destruction of screen items in the Video32 destructor relies on these
|
|
// components
|
|
delete _video32;
|
|
delete _gfxPalette32;
|
|
delete _gfxTransitions32;
|
|
delete _gfxFrameout;
|
|
delete _gfxRemap32;
|
|
delete _audio32;
|
|
#endif
|
|
delete _gfxMenu;
|
|
delete _gfxControls16;
|
|
delete _gfxText16;
|
|
delete _gfxAnimate;
|
|
delete _gfxPaint16;
|
|
delete _gfxTransitions;
|
|
delete _gfxCompare;
|
|
delete _gfxCoordAdjuster;
|
|
delete _gfxPorts;
|
|
delete _gfxCache;
|
|
delete _gfxPalette16;
|
|
delete _gfxRemap16;
|
|
delete _gfxCursor;
|
|
delete _gfxScreen;
|
|
|
|
delete _audio;
|
|
delete _sync;
|
|
delete _soundCmd;
|
|
delete _kernel;
|
|
delete _vocabulary;
|
|
delete _console;
|
|
delete _features;
|
|
delete _gfxMacIconBar;
|
|
|
|
delete _eventMan;
|
|
delete _gamestate->_segMan;
|
|
delete _gamestate;
|
|
|
|
delete[] _opcode_formats;
|
|
|
|
delete _scriptPatcher;
|
|
delete _resMan; // should be deleted last
|
|
g_sci = 0;
|
|
}
|
|
|
|
extern void showScummVMDialog(const Common::String &message);
|
|
|
|
Common::Error SciEngine::run() {
|
|
_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();
|
|
|
|
_scriptPatcher = new ScriptPatcher();
|
|
SegManager *segMan = new SegManager(_resMan, _scriptPatcher);
|
|
|
|
// Read user option for hires graphics
|
|
// Only show/selectable for:
|
|
// - King's Quest 6 CD
|
|
// - King's Quest 6 CD demo
|
|
// - Gabriel Knight 1 CD
|
|
// - Police Quest 4 CD
|
|
// TODO: Check, if Gabriel Knight 1 floppy supports high resolution
|
|
// TODO: Check, if Gabriel Knight 1 on Mac supports high resolution
|
|
switch (getPlatform()) {
|
|
case Common::kPlatformDOS:
|
|
case Common::kPlatformWindows:
|
|
// Only DOS+Windows
|
|
switch (_gameId) {
|
|
case GID_KQ6:
|
|
case GID_GK1:
|
|
case GID_PQ4:
|
|
if (isCD())
|
|
_forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
};
|
|
|
|
// Initialize the game screen
|
|
_gfxScreen = new GfxScreen(_resMan);
|
|
_gfxScreen->enableUndithering(ConfMan.getBool("disable_dithering"));
|
|
|
|
_kernel = new Kernel(_resMan, segMan);
|
|
_kernel->init();
|
|
|
|
_features = new GameFeatures(segMan, _kernel);
|
|
// Only SCI0, SCI01 and SCI1 EGA games used a parser
|
|
_vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA_ONLY) ? 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);
|
|
|
|
_gamestate = new EngineState(segMan);
|
|
_eventMan = new EventManager(_resMan->detectFontExtended());
|
|
#ifdef ENABLE_SCI32
|
|
if (getSciVersion() >= SCI_VERSION_2_1_EARLY) {
|
|
_audio32 = new Audio32(_resMan);
|
|
} else
|
|
#endif
|
|
_audio = new AudioPlayer(_resMan);
|
|
#ifdef ENABLE_SCI32
|
|
if (getSciVersion() >= SCI_VERSION_2) {
|
|
_video32 = new Video32(segMan, _eventMan);
|
|
}
|
|
#endif
|
|
_sync = new Sync(_resMan, segMan);
|
|
|
|
// Create debugger console. It requires GFX and _gamestate to be initialized
|
|
_console = new Console(this);
|
|
|
|
// 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;
|
|
}
|
|
|
|
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();
|
|
|
|
// Load our Mac executable here for icon bar palettes and high-res fonts
|
|
loadMacExecutable();
|
|
|
|
// Initialize all graphics related subsystems
|
|
initGraphics();
|
|
|
|
// 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) {
|
|
_gamestate->_delayedRestoreGame = true;
|
|
_gamestate->_delayedRestoreGameId = directSaveSlotLoading;
|
|
_gamestate->_delayedRestoreFromLauncher = true;
|
|
|
|
// Jones only initializes its menus when restarting/restoring, thus set
|
|
// the gameIsRestarting flag here before initializing. Fixes bug #6536.
|
|
if (g_sci->getGameId() == GID_JONES)
|
|
_gamestate->gameIsRestarting = GAMEISRESTARTING_RESTORE;
|
|
}
|
|
|
|
// 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");
|
|
}
|
|
}
|
|
|
|
if (getGameId() == GID_KQ7 && ConfMan.getBool("subtitles")) {
|
|
showScummVMDialog("Subtitles are enabled, but subtitling in King's"
|
|
" Quest 7 was unfinished and disabled in the release"
|
|
" version of the game. ScummVM allows the subtitles"
|
|
" to be re-enabled, but because they were removed from"
|
|
" the original game, they do not always render"
|
|
" properly or reflect the actual game speech."
|
|
" This is not a ScummVM bug -- it is a problem with"
|
|
" the game's assets.");
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
bool SciEngine::gameHasFanMadePatch() {
|
|
struct FanMadePatchInfo {
|
|
SciGameId gameID;
|
|
uint16 targetScript;
|
|
uint16 targetSize;
|
|
uint16 patchedByteOffset;
|
|
byte patchedByte;
|
|
};
|
|
|
|
const FanMadePatchInfo patchInfo[] = {
|
|
// game script size offset byte
|
|
// ** NRS Patches **************************
|
|
{ GID_HOYLE3, 994, 2580, 656, 0x78 },
|
|
{ GID_KQ1, 85, 5156, 631, 0x02 },
|
|
{ GID_LAURABOW2, 994, 4382, 0, 0x00 },
|
|
{ GID_LONGBOW, 994, 4950, 1455, 0x78 }, // English
|
|
{ GID_LONGBOW, 994, 5020, 1469, 0x78 }, // German
|
|
{ GID_LSL1, 803, 592, 342, 0x01 },
|
|
{ GID_LSL3, 380, 6148, 195, 0x35 },
|
|
{ GID_LSL5, 994, 4810, 1342, 0x78 }, // English
|
|
{ GID_LSL5, 994, 4942, 1392, 0x76 }, // German
|
|
{ GID_PQ1, 994, 4332, 1473, 0x78 },
|
|
{ GID_PQ2, 200, 10614, 0, 0x00 },
|
|
{ GID_PQ3, 994, 4686, 1291, 0x78 }, // English
|
|
{ GID_PQ3, 994, 4734, 1283, 0x78 }, // German
|
|
{ GID_QFG1VGA, 994, 4388, 0, 0x00 },
|
|
{ GID_QFG3, 994, 4714, 2, 0x48 },
|
|
// TODO: Disabled, as it fixes a whole lot of bugs which can't be tested till SCI2.1 support is finished
|
|
//{ GID_QFG4, 710, 11477, 0, 0x00 },
|
|
{ GID_SQ1, 994, 4740, 0, 0x00 },
|
|
{ GID_SQ5, 994, 4142, 1496, 0x78 }, // English/German/French
|
|
// TODO: Disabled, till we can test the Italian version
|
|
//{ GID_SQ5, 994, 4148, 0, 0x00 }, // Italian - patched file is the same size as the original
|
|
// TODO: The bugs in SQ6 can't be tested till SCI2.1 support is finished
|
|
//{ GID_SQ6, 380, 16308, 15042, 0x0C }, // English
|
|
//{ GID_SQ6, 380, 11652, 0, 0x00 }, // German - patched file is the same size as the original
|
|
// ** End marker ***************************
|
|
{ GID_FANMADE, 0, 0, 0, 0x00 }
|
|
};
|
|
|
|
int curEntry = 0;
|
|
|
|
while (true) {
|
|
if (patchInfo[curEntry].targetSize == 0)
|
|
break;
|
|
|
|
if (patchInfo[curEntry].gameID == getGameId()) {
|
|
Resource *targetScript = _resMan->findResource(ResourceId(kResourceTypeScript, patchInfo[curEntry].targetScript), 0);
|
|
|
|
if (targetScript && targetScript->size + 2 == patchInfo[curEntry].targetSize) {
|
|
if (patchInfo[curEntry].patchedByteOffset == 0)
|
|
return true;
|
|
else if (targetScript->data[patchInfo[curEntry].patchedByteOffset - 2] == patchInfo[curEntry].patchedByte)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
curEntry++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static byte patchGameRestoreSave[] = {
|
|
0x39, 0x03, // pushi 03
|
|
0x76, // push0
|
|
0x38, 0xff, 0xff, // pushi -1
|
|
0x76, // push0
|
|
0x43, 0xff, 0x06, // callk kRestoreGame/kSaveGame (will get changed afterwards)
|
|
0x48, // ret
|
|
};
|
|
|
|
// SCI2 version: Same as above, but the second parameter to callk is a word
|
|
static byte patchGameRestoreSaveSci2[] = {
|
|
0x39, 0x03, // pushi 03
|
|
0x76, // push0
|
|
0x38, 0xff, 0xff, // pushi -1
|
|
0x76, // push0
|
|
0x43, 0xff, 0x06, 0x00, // callk kRestoreGame/kSaveGame (will get changed afterwards)
|
|
0x48, // ret
|
|
};
|
|
|
|
// SCI21 version: Same as above, but the second parameter to callk is a word
|
|
static byte patchGameRestoreSaveSci21[] = {
|
|
0x39, 0x04, // pushi 04
|
|
0x76, // push0 // 0: save, 1: restore (will get changed afterwards)
|
|
0x76, // push0
|
|
0x38, 0xff, 0xff, // pushi -1
|
|
0x76, // push0
|
|
0x43, 0xff, 0x08, 0x00, // callk kSave (will get changed afterwards)
|
|
0x48, // ret
|
|
};
|
|
|
|
static void patchGameSaveRestoreCode(SegManager *segMan, reg_t methodAddress, byte id) {
|
|
Script *script = segMan->getScript(methodAddress.getSegment());
|
|
byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset()));
|
|
|
|
if (getSciVersion() <= SCI_VERSION_1_1) {
|
|
memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
|
|
} else { // SCI2+
|
|
memcpy(patchPtr, patchGameRestoreSaveSci2, sizeof(patchGameRestoreSaveSci2));
|
|
|
|
if (g_sci->isBE()) {
|
|
// LE -> BE
|
|
patchPtr[9] = 0x00;
|
|
patchPtr[10] = 0x06;
|
|
}
|
|
}
|
|
|
|
patchPtr[8] = id;
|
|
}
|
|
|
|
static void patchGameSaveRestoreCodeSci21(SegManager *segMan, reg_t methodAddress, byte id, bool doRestore) {
|
|
Script *script = segMan->getScript(methodAddress.getSegment());
|
|
byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset()));
|
|
memcpy(patchPtr, patchGameRestoreSaveSci21, sizeof(patchGameRestoreSaveSci21));
|
|
|
|
if (doRestore)
|
|
patchPtr[2] = 0x78; // push1
|
|
|
|
if (g_sci->isBE()) {
|
|
// LE -> BE
|
|
patchPtr[10] = 0x00;
|
|
patchPtr[11] = 0x08;
|
|
}
|
|
|
|
patchPtr[9] = id;
|
|
}
|
|
|
|
void SciEngine::patchGameSaveRestore() {
|
|
SegManager *segMan = _gamestate->_segMan;
|
|
const Object *gameObject = segMan->getObject(_gameObjectAddress);
|
|
const Object *gameSuperObject = segMan->getObject(gameObject->getSuperClassSelector());
|
|
if (!gameSuperObject)
|
|
gameSuperObject = gameObject; // happens in KQ5CD, when loading saved games before r54510
|
|
byte kernelIdRestore = 0;
|
|
byte kernelIdSave = 0;
|
|
|
|
switch (_gameId) {
|
|
case GID_HOYLE1: // gets confused, although the game doesn't support saving/restoring at all
|
|
case GID_HOYLE2: // gets confused, see hoyle1
|
|
case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required
|
|
case GID_MOTHERGOOSE: // mother goose EGA saves/restores directly and has no save/restore dialogs
|
|
case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs
|
|
case GID_PHANTASMAGORIA: // has custom save/load code
|
|
case GID_SHIVERS: // has custom save/load code
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (ConfMan.getBool("originalsaveload"))
|
|
return;
|
|
|
|
uint16 kernelNamesSize = _kernel->getKernelNamesSize();
|
|
for (uint16 kernelNr = 0; kernelNr < kernelNamesSize; kernelNr++) {
|
|
Common::String kernelName = _kernel->getKernelName(kernelNr);
|
|
if (kernelName == "RestoreGame")
|
|
kernelIdRestore = kernelNr;
|
|
if (kernelName == "SaveGame")
|
|
kernelIdSave = kernelNr;
|
|
if (kernelName == "Save")
|
|
kernelIdSave = kernelIdRestore = kernelNr;
|
|
}
|
|
|
|
// Search for gameobject superclass ::restore
|
|
uint16 gameSuperObjectMethodCount = gameSuperObject->getMethodCount();
|
|
for (uint16 methodNr = 0; methodNr < gameSuperObjectMethodCount; methodNr++) {
|
|
uint16 selectorId = gameSuperObject->getFuncSelector(methodNr);
|
|
Common::String methodName = _kernel->getSelectorName(selectorId);
|
|
if (methodName == "restore") {
|
|
if (kernelIdSave != kernelIdRestore)
|
|
patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore);
|
|
else
|
|
patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdRestore, true);
|
|
}
|
|
else if (methodName == "save") {
|
|
if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog
|
|
if (kernelIdSave != kernelIdRestore)
|
|
patchGameSaveRestoreCode(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave);
|
|
else
|
|
patchGameSaveRestoreCodeSci21(segMan, gameSuperObject->getFunction(methodNr), kernelIdSave, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
const Object *patchObjectSave = nullptr;
|
|
|
|
if (getSciVersion() < SCI_VERSION_2) {
|
|
// Patch gameobject ::save for now for SCI0 - SCI1.1
|
|
// TODO: It seems this was never adjusted to superclass, but adjusting it now may cause
|
|
// issues with some game. Needs to get checked and then possibly changed.
|
|
patchObjectSave = gameObject;
|
|
} else {
|
|
// Patch superclass ::save for SCI32
|
|
patchObjectSave = gameSuperObject;
|
|
}
|
|
|
|
// Search for gameobject ::save, if there is one patch that one too
|
|
uint16 patchObjectMethodCount = patchObjectSave->getMethodCount();
|
|
for (uint16 methodNr = 0; methodNr < patchObjectMethodCount; methodNr++) {
|
|
uint16 selectorId = patchObjectSave->getFuncSelector(methodNr);
|
|
Common::String methodName = _kernel->getSelectorName(selectorId);
|
|
if (methodName == "save") {
|
|
if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog
|
|
if (kernelIdSave != kernelIdRestore)
|
|
patchGameSaveRestoreCode(segMan, patchObjectSave->getFunction(methodNr), kernelIdSave);
|
|
else
|
|
patchGameSaveRestoreCodeSci21(segMan, patchObjectSave->getFunction(methodNr), kernelIdSave, false);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SciEngine::initGame() {
|
|
// Script 0 needs to be allocated here before anything else!
|
|
int script0Segment = _gamestate->_segMan->getScriptSegment(0, SCRIPT_GET_LOCK);
|
|
DataStack *stack = _gamestate->_segMan->allocateStack(VM_STACK_SIZE, NULL);
|
|
|
|
_gamestate->_msgState = new MessageState(_gamestate->_segMan);
|
|
_gamestate->gcCountDown = GC_INTERVAL - 1;
|
|
|
|
// Script 0 should always be at segment 1
|
|
if (script0Segment != 1) {
|
|
debug(2, "Failed to instantiate script 0");
|
|
return false;
|
|
}
|
|
|
|
_gamestate->initGlobals();
|
|
_gamestate->_segMan->initSysStrings();
|
|
|
|
_gamestate->r_acc = _gamestate->r_prev = NULL_REG;
|
|
|
|
_gamestate->_executionStack.clear(); // Start without any execution stack
|
|
_gamestate->executionStackBase = -1; // No vm is running yet
|
|
_gamestate->_executionStackPosChanged = false;
|
|
|
|
_gamestate->abortScriptProcessing = kAbortNone;
|
|
_gamestate->gameIsRestarting = GAMEISRESTARTING_NONE;
|
|
|
|
_gamestate->stack_base = stack->_entries;
|
|
_gamestate->stack_top = stack->_entries + stack->_capacity;
|
|
|
|
if (!_gamestate->_segMan->instantiateScript(0)) {
|
|
error("initGame(): Could not instantiate script 0");
|
|
return false;
|
|
}
|
|
|
|
// Reset parser
|
|
if (_vocabulary)
|
|
_vocabulary->reset();
|
|
|
|
_gamestate->lastWaitTime = _gamestate->_screenUpdateTime = g_system->getMillis();
|
|
|
|
// Load game language into printLang property of game object
|
|
setSciLanguage();
|
|
|
|
return true;
|
|
}
|
|
|
|
void SciEngine::initGraphics() {
|
|
|
|
// Reset all graphics objects
|
|
_gfxAnimate = 0;
|
|
_gfxCache = 0;
|
|
_gfxCompare = 0;
|
|
_gfxControls16 = 0;
|
|
_gfxCoordAdjuster = 0;
|
|
_gfxCursor = 0;
|
|
_gfxMacIconBar = 0;
|
|
_gfxMenu = 0;
|
|
_gfxPaint16 = 0;
|
|
_gfxPalette16 = 0;
|
|
_gfxRemap16 = 0;
|
|
_gfxPorts = 0;
|
|
_gfxText16 = 0;
|
|
_gfxTransitions = 0;
|
|
#ifdef ENABLE_SCI32
|
|
_gfxControls32 = 0;
|
|
_gfxText32 = 0;
|
|
_robotDecoder = 0;
|
|
_gfxFrameout = 0;
|
|
_gfxPaint32 = 0;
|
|
_gfxPalette32 = 0;
|
|
_gfxRemap32 = 0;
|
|
_gfxTransitions32 = 0;
|
|
#endif
|
|
|
|
if (hasMacIconBar())
|
|
_gfxMacIconBar = new GfxMacIconBar();
|
|
|
|
#ifdef ENABLE_SCI32
|
|
if (getSciVersion() >= SCI_VERSION_2) {
|
|
_gfxPalette32 = new GfxPalette32(_resMan);
|
|
_gfxRemap32 = new GfxRemap32();
|
|
} else {
|
|
#endif
|
|
_gfxPalette16 = new GfxPalette(_resMan, _gfxScreen);
|
|
if (getGameId() == GID_QFG4DEMO)
|
|
_gfxRemap16 = new GfxRemap(_gfxPalette16);
|
|
#ifdef ENABLE_SCI32
|
|
}
|
|
#endif
|
|
|
|
_gfxCache = new GfxCache(_resMan, _gfxScreen, _gfxPalette16);
|
|
_gfxCursor = new GfxCursor(_resMan, _gfxPalette16, _gfxScreen);
|
|
|
|
#ifdef ENABLE_SCI32
|
|
if (getSciVersion() >= SCI_VERSION_2) {
|
|
// SCI32 graphic objects creation
|
|
_gfxCoordAdjuster = new GfxCoordAdjuster32(_gamestate->_segMan);
|
|
_gfxCursor->init(_gfxCoordAdjuster, _eventMan);
|
|
_gfxCompare = new GfxCompare(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxCoordAdjuster);
|
|
_gfxPaint32 = new GfxPaint32(_gamestate->_segMan);
|
|
_robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh);
|
|
_gfxTransitions32 = new GfxTransitions32(_gamestate->_segMan);
|
|
_gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxScreen, _gfxPalette32, _gfxTransitions32);
|
|
_gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache);
|
|
_gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32);
|
|
_gfxFrameout->run();
|
|
} else {
|
|
#endif
|
|
// SCI0-SCI1.1 graphic objects creation
|
|
_gfxPorts = new GfxPorts(_gamestate->_segMan, _gfxScreen);
|
|
_gfxCoordAdjuster = new GfxCoordAdjuster16(_gfxPorts);
|
|
_gfxCursor->init(_gfxCoordAdjuster, _eventMan);
|
|
_gfxCompare = new GfxCompare(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxCoordAdjuster);
|
|
_gfxTransitions = new GfxTransitions(_gfxScreen, _gfxPalette16);
|
|
_gfxPaint16 = new GfxPaint16(_resMan, _gamestate->_segMan, _gfxCache, _gfxPorts, _gfxCoordAdjuster, _gfxScreen, _gfxPalette16, _gfxTransitions, _audio);
|
|
_gfxAnimate = new GfxAnimate(_gamestate, _scriptPatcher, _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen, _gfxPalette16, _gfxCursor, _gfxTransitions);
|
|
_gfxText16 = new GfxText16(_gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen);
|
|
_gfxControls16 = new GfxControls16(_gamestate->_segMan, _gfxPorts, _gfxPaint16, _gfxText16, _gfxScreen);
|
|
_gfxMenu = new GfxMenu(_eventMan, _gamestate->_segMan, _gfxPorts, _gfxPaint16, _gfxText16, _gfxScreen, _gfxCursor);
|
|
|
|
_gfxMenu->reset();
|
|
|
|
_gfxPorts->init(_features->usesOldGfxFunctions(), _gfxPaint16, _gfxText16);
|
|
_gfxPaint16->init(_gfxAnimate, _gfxText16);
|
|
|
|
#ifdef ENABLE_SCI32
|
|
}
|
|
#endif
|
|
|
|
if (getSciVersion() < SCI_VERSION_2) {
|
|
// Set default (EGA, amiga or resource 999) palette
|
|
_gfxPalette16->setDefault();
|
|
}
|
|
}
|
|
|
|
void SciEngine::initStackBaseWithSelector(Selector selector) {
|
|
_gamestate->stack_base[0] = make_reg(0, (uint16)selector);
|
|
_gamestate->stack_base[1] = NULL_REG;
|
|
|
|
// Register the first element on the execution stack
|
|
if (!send_selector(_gamestate, _gameObjectAddress, _gameObjectAddress, _gamestate->stack_base, 2, _gamestate->stack_base)) {
|
|
_console->printObject(_gameObjectAddress);
|
|
error("initStackBaseWithSelector: error while registering the first selector in the call stack");
|
|
}
|
|
|
|
}
|
|
|
|
void SciEngine::runGame() {
|
|
setTotalPlayTime(0);
|
|
|
|
initStackBaseWithSelector(SELECTOR(play)); // Call the play selector
|
|
|
|
// Attach the debug console on game startup, if requested
|
|
if (DebugMan.isDebugChannelEnabled(kDebugLevelOnStartup))
|
|
_console->attach();
|
|
|
|
_gamestate->_syncedAudioOptions = false;
|
|
|
|
do {
|
|
_gamestate->_executionStackPosChanged = false;
|
|
run_vm(_gamestate);
|
|
exitGame();
|
|
|
|
_gamestate->_syncedAudioOptions = true;
|
|
|
|
if (_gamestate->abortScriptProcessing == kAbortRestartGame) {
|
|
_gamestate->_segMan->resetSegMan();
|
|
initGame();
|
|
initStackBaseWithSelector(SELECTOR(play));
|
|
patchGameSaveRestore();
|
|
setLauncherLanguage();
|
|
_gamestate->gameIsRestarting = GAMEISRESTARTING_RESTART;
|
|
_gamestate->_throttleLastTime = 0;
|
|
if (_gfxMenu)
|
|
_gfxMenu->reset();
|
|
_gamestate->abortScriptProcessing = kAbortNone;
|
|
_gamestate->_syncedAudioOptions = false;
|
|
} else if (_gamestate->abortScriptProcessing == kAbortLoadGame) {
|
|
_gamestate->abortScriptProcessing = kAbortNone;
|
|
_gamestate->_executionStack.clear();
|
|
initStackBaseWithSelector(SELECTOR(replay));
|
|
patchGameSaveRestore();
|
|
setLauncherLanguage();
|
|
_gamestate->shrinkStackToBase();
|
|
_gamestate->abortScriptProcessing = kAbortNone;
|
|
|
|
syncSoundSettings();
|
|
syncIngameAudioOptions();
|
|
// Games do not set their audio settings when loading
|
|
} else {
|
|
break; // exit loop
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
void SciEngine::exitGame() {
|
|
if (_gamestate->abortScriptProcessing != kAbortLoadGame) {
|
|
_gamestate->_executionStack.clear();
|
|
if (_audio) {
|
|
_audio->stopAllAudio();
|
|
}
|
|
_sync->stop();
|
|
_soundCmd->clearPlayList();
|
|
}
|
|
|
|
// TODO Free parser segment here
|
|
|
|
// TODO Free scripts here
|
|
|
|
// Close all opened file handles
|
|
_gamestate->_fileHandles.clear();
|
|
_gamestate->_fileHandles.resize(5);
|
|
}
|
|
|
|
// Invoked by error() when a severe error occurs
|
|
GUI::Debugger *SciEngine::getDebugger() {
|
|
if (_gamestate) {
|
|
ExecStack *xs = &(_gamestate->_executionStack.back());
|
|
if (xs) {
|
|
xs->addr.pc.setOffset(_debugState.old_pc_offset);
|
|
xs->sp = _debugState.old_sp;
|
|
}
|
|
}
|
|
|
|
_debugState.runningStep = 0; // Stop multiple execution
|
|
_debugState.seeking = kDebugSeekNothing; // Stop special seeks
|
|
|
|
return _console;
|
|
}
|
|
|
|
// Used to obtain the engine's console in order to print messages to it
|
|
Console *SciEngine::getSciDebugger() {
|
|
return _console;
|
|
}
|
|
|
|
const char *SciEngine::getGameIdStr() const {
|
|
return _gameDescription->gameId;
|
|
}
|
|
|
|
Common::Language SciEngine::getLanguage() const {
|
|
return _gameDescription->language;
|
|
}
|
|
|
|
Common::Platform SciEngine::getPlatform() const {
|
|
return _gameDescription->platform;
|
|
}
|
|
|
|
bool SciEngine::isDemo() const {
|
|
return _gameDescription->flags & ADGF_DEMO;
|
|
}
|
|
|
|
bool SciEngine::isCD() const {
|
|
return _gameDescription->flags & ADGF_CD;
|
|
}
|
|
|
|
bool SciEngine::forceHiresGraphics() const {
|
|
return _forceHiresGraphics;
|
|
}
|
|
|
|
bool SciEngine::isBE() const{
|
|
switch(_gameDescription->platform) {
|
|
case Common::kPlatformAmiga:
|
|
case Common::kPlatformMacintosh:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SciEngine::hasMacIconBar() const {
|
|
return _resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1 &&
|
|
(getGameId() == GID_KQ6 || getGameId() == GID_FREDDYPHARKAS);
|
|
}
|
|
|
|
Common::String SciEngine::getSavegameName(int nr) const {
|
|
return _targetName + Common::String::format(".%03d", nr);
|
|
}
|
|
|
|
Common::String SciEngine::getSavegamePattern() const {
|
|
return _targetName + ".###";
|
|
}
|
|
|
|
Common::String SciEngine::getFilePrefix() const {
|
|
return _targetName;
|
|
}
|
|
|
|
Common::String SciEngine::wrapFilename(const Common::String &name) const {
|
|
return getFilePrefix() + "-" + name;
|
|
}
|
|
|
|
Common::String SciEngine::unwrapFilename(const Common::String &name) const {
|
|
Common::String prefix = getFilePrefix() + "-";
|
|
if (name.hasPrefix(prefix.c_str()))
|
|
return Common::String(name.c_str() + prefix.size());
|
|
return name;
|
|
}
|
|
|
|
const char *SciEngine::getGameObjectName() {
|
|
return _gamestate->_segMan->getObjectName(_gameObjectAddress);
|
|
}
|
|
|
|
int SciEngine::inQfGImportRoom() const {
|
|
if (_gameId == GID_QFG2 && _gamestate->currentRoomNumber() == 805) {
|
|
// QFG2 character import screen
|
|
return 2;
|
|
} else if (_gameId == GID_QFG3 && _gamestate->currentRoomNumber() == 54) {
|
|
// QFG3 character import screen
|
|
return 3;
|
|
} else if (_gameId == GID_QFG4 && _gamestate->currentRoomNumber() == 54) {
|
|
return 4;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void SciEngine::setLauncherLanguage() {
|
|
if (_gameDescription->flags & ADGF_ADDENGLISH) {
|
|
// If game is multilingual
|
|
Common::Language chosenLanguage = Common::parseLanguage(ConfMan.get("language"));
|
|
uint16 languageToSet = 0;
|
|
|
|
switch (chosenLanguage) {
|
|
case Common::EN_ANY:
|
|
// and English was selected as language
|
|
languageToSet = K_LANG_ENGLISH;
|
|
break;
|
|
case Common::JA_JPN: {
|
|
// Set Japanese for FM-Towns games
|
|
// KQ5 on FM-Towns has no initial language set
|
|
if (g_sci->getPlatform() == Common::kPlatformFMTowns) {
|
|
languageToSet = K_LANG_JAPANESE;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (languageToSet) {
|
|
if (SELECTOR(printLang) != -1) // set text language
|
|
writeSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(printLang), languageToSet);
|
|
if (SELECTOR(parseLang) != -1) // and set parser language as well
|
|
writeSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(parseLang), languageToSet);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SciEngine::pauseEngineIntern(bool pause) {
|
|
_mixer->pauseAll(pause);
|
|
if (_soundCmd)
|
|
_soundCmd->pauseAll(pause);
|
|
}
|
|
|
|
void SciEngine::syncSoundSettings() {
|
|
Engine::syncSoundSettings();
|
|
|
|
bool mute = false;
|
|
if (ConfMan.hasKey("mute"))
|
|
mute = ConfMan.getBool("mute");
|
|
|
|
int soundVolumeMusic = (mute ? 0 : ConfMan.getInt("music_volume"));
|
|
|
|
if (_gamestate && _soundCmd) {
|
|
int vol = (soundVolumeMusic + 1) * MUSIC_MASTERVOLUME_MAX / Audio::Mixer::kMaxMixerVolume;
|
|
_soundCmd->setMasterVolume(vol);
|
|
}
|
|
}
|
|
|
|
// used by Script Patcher. Used to find out, if Laura Bow 2/King's Quest 6 need patching for Speech+Subtitles - or not
|
|
bool SciEngine::speechAndSubtitlesEnabled() {
|
|
bool subtitlesOn = ConfMan.getBool("subtitles");
|
|
bool speechOn = !ConfMan.getBool("speech_mute");
|
|
|
|
if (isCD() && subtitlesOn && speechOn)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void SciEngine::syncIngameAudioOptions() {
|
|
bool useGlobal90 = false;
|
|
|
|
// Sync the in-game speech/subtitles settings for SCI1.1 CD games
|
|
if (isCD()) {
|
|
switch (getSciVersion()) {
|
|
case SCI_VERSION_1_1:
|
|
// All SCI1.1 CD games use global 90
|
|
useGlobal90 = true;
|
|
break;
|
|
#ifdef ENABLE_SCI32
|
|
case SCI_VERSION_2:
|
|
case SCI_VERSION_2_1_EARLY:
|
|
case SCI_VERSION_2_1_MIDDLE:
|
|
case SCI_VERSION_2_1_LATE:
|
|
// Only use global 90 for some specific games, not all SCI32 games used this method
|
|
switch (_gameId) {
|
|
case GID_KQ7: // SCI2.1
|
|
case GID_GK1: // SCI2
|
|
case GID_GK2: // SCI2.1
|
|
case GID_SQ6: // SCI2.1
|
|
case GID_TORIN: // SCI2.1
|
|
case GID_QFG4: // SCI2.1
|
|
useGlobal90 = true;
|
|
break;
|
|
case GID_LSL6: // SCI2.1
|
|
// TODO: Uses gameFlags array
|
|
break;
|
|
// TODO: Unknown at the moment:
|
|
// Shivers - seems not to use global 90
|
|
// Police Quest: SWAT - unable to check
|
|
// Police Quest 4 - unable to check
|
|
// Mixed Up Mother Goose - unable to check
|
|
// Phantasmagoria - seems to use global 90, unable to check for subtitles atm
|
|
default:
|
|
return;
|
|
}
|
|
break;
|
|
#endif // ENABLE_SCI32
|
|
default:
|
|
return;
|
|
}
|
|
|
|
bool subtitlesOn = ConfMan.getBool("subtitles");
|
|
bool speechOn = !ConfMan.getBool("speech_mute");
|
|
|
|
if (useGlobal90) {
|
|
if (subtitlesOn && !speechOn) {
|
|
_gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 1); // subtitles
|
|
} else if (!subtitlesOn && speechOn) {
|
|
_gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 2); // speech
|
|
} else if (subtitlesOn && speechOn) {
|
|
// Is it a game that supports simultaneous speech and subtitles?
|
|
switch (_gameId) {
|
|
case GID_SQ4:
|
|
case GID_FREDDYPHARKAS:
|
|
case GID_ECOQUEST:
|
|
case GID_LSL6:
|
|
case GID_LAURABOW2:
|
|
case GID_KQ6:
|
|
#ifdef ENABLE_SCI32
|
|
// Unsure about Gabriel Knight 2
|
|
case GID_KQ7: // SCI2.1
|
|
case GID_GK1: // SCI2
|
|
case GID_SQ6: // SCI2.1, SQ6 seems to always use subtitles anyway
|
|
case GID_TORIN: // SCI2.1
|
|
case GID_QFG4: // SCI2.1
|
|
#endif // ENABLE_SCI32
|
|
_gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 3); // speech + subtitles
|
|
break;
|
|
default:
|
|
// Game does not support speech and subtitles, set it to speech
|
|
_gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 2); // speech
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SciEngine::updateScummVMAudioOptions() {
|
|
// Update ScummVM's speech/subtitles settings for SCI1.1 CD games,
|
|
// depending on the in-game settings
|
|
if (isCD() && getSciVersion() == SCI_VERSION_1_1) {
|
|
uint16 ingameSetting = _gamestate->variables[VAR_GLOBAL][90].getOffset();
|
|
|
|
switch (ingameSetting) {
|
|
case 1:
|
|
// subtitles
|
|
ConfMan.setBool("subtitles", true);
|
|
ConfMan.setBool("speech_mute", true);
|
|
break;
|
|
case 2:
|
|
// speech
|
|
ConfMan.setBool("subtitles", false);
|
|
ConfMan.setBool("speech_mute", false);
|
|
break;
|
|
case 3:
|
|
// speech + subtitles
|
|
ConfMan.setBool("subtitles", true);
|
|
ConfMan.setBool("speech_mute", false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SciEngine::loadMacExecutable() {
|
|
if (getPlatform() != Common::kPlatformMacintosh || getSciVersion() < SCI_VERSION_1_EARLY || getSciVersion() > SCI_VERSION_1_1)
|
|
return;
|
|
|
|
Common::String filename;
|
|
|
|
switch (getGameId()) {
|
|
case GID_KQ6:
|
|
filename = "King's Quest VI";
|
|
break;
|
|
case GID_FREDDYPHARKAS:
|
|
filename = "Freddy Pharkas";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (filename.empty())
|
|
return;
|
|
|
|
if (!_macExecutable.open(filename) || !_macExecutable.hasResFork()) {
|
|
// KQ6/Freddy require the executable to load their icon bar palettes
|
|
if (hasMacIconBar())
|
|
error("Could not load Mac resource fork '%s'", filename.c_str());
|
|
|
|
// TODO: Show some sort of warning dialog saying they can't get any
|
|
// high-res Mac fonts, when we get to that point ;)
|
|
}
|
|
}
|
|
|
|
uint32 SciEngine::getTickCount() {
|
|
return g_engine->getTotalPlayTime() * 60 / 1000;
|
|
}
|
|
void SciEngine::setTickCount(const uint32 ticks) {
|
|
return g_engine->setTotalPlayTime(ticks * 1000 / 60);
|
|
}
|
|
} // End of namespace Sci
|