ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

engine.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #define FORBIDDEN_SYMBOL_EXCEPTION_getcwd
00024 
00025 #if defined(WIN32) && !defined(__SYMBIAN32__)
00026 #define WIN32_LEAN_AND_MEAN
00027 #include <windows.h>
00028 #include <direct.h>
00029 #endif
00030 
00031 #include "engines/engine.h"
00032 #include "engines/dialogs.h"
00033 #include "engines/util.h"
00034 #include "engines/metaengine.h"
00035 
00036 #include "common/config-manager.h"
00037 #include "common/events.h"
00038 #include "common/file.h"
00039 #include "common/system.h"
00040 #include "common/str.h"
00041 #include "common/error.h"
00042 #include "common/list.h"
00043 #include "common/memstream.h"
00044 #include "common/savefile.h"
00045 #include "common/scummsys.h"
00046 #include "common/taskbar.h"
00047 #include "common/textconsole.h"
00048 #include "common/translation.h"
00049 #include "common/singleton.h"
00050 
00051 #include "backends/keymapper/action.h"
00052 #include "backends/keymapper/keymapper.h"
00053 #include "base/version.h"
00054 
00055 #include "gui/gui-manager.h"
00056 #include "gui/debugger.h"
00057 #include "gui/dialog.h"
00058 #include "gui/message.h"
00059 #include "gui/saveload.h"
00060 
00061 #include "audio/mixer.h"
00062 
00063 #include "graphics/cursorman.h"
00064 #include "graphics/fontman.h"
00065 #include "graphics/pixelformat.h"
00066 #include "image/bmp.h"
00067 
00068 #ifdef USE_TTS
00069 #include "common/text-to-speech.h"
00070 #endif
00071 
00072 // FIXME: HACK for error()
00073 Engine *g_engine = 0;
00074 
00075 // Output formatter for debug() and error() which invokes
00076 // the errorString method of the active engine, if any.
00077 static void defaultOutputFormatter(char *dst, const char *src, size_t dstSize) {
00078     if (g_engine) {
00079         g_engine->errorString(src, dst, dstSize);
00080     } else {
00081         Common::strlcpy(dst, src, dstSize);
00082     }
00083 }
00084 
00085 static void defaultErrorHandler(const char *msg) {
00086     // Unless this error -originated- within the debugger itself, we
00087     // now invoke the debugger, if available / supported.
00088     if (g_engine) {
00089         GUI::Debugger *debugger = g_engine->getOrCreateDebugger();
00090 
00091 #if defined(USE_TASKBAR)
00092         g_system->getTaskbarManager()->notifyError();
00093 #endif
00094 
00095         if (debugger && !debugger->isActive()) {
00096             debugger->attach(msg);
00097             debugger->onFrame();
00098         }
00099 
00100 
00101 #if defined(USE_TASKBAR)
00102         g_system->getTaskbarManager()->clearError();
00103 #endif
00104 
00105     }
00106 }
00107 
00108 // Chained games manager
00109 
00110 ChainedGamesManager::ChainedGamesManager() {
00111     clear();
00112 }
00113 
00114 void ChainedGamesManager::clear() {
00115     _chainedGames.clear();
00116 }
00117 
00118 void ChainedGamesManager::push(const Common::String target, const int slot) {
00119     Game game;
00120     game.target = target;
00121     game.slot = slot;
00122     _chainedGames.push(game);
00123 }
00124 
00125 bool ChainedGamesManager::pop(Common::String &target, int &slot) {
00126     if (_chainedGames.empty()) {
00127         return false;
00128     }
00129     Game game = _chainedGames.pop();
00130     target = game.target;
00131     slot = game.slot;
00132     return true;
00133 }
00134 
00135 namespace Common {
00136 DECLARE_SINGLETON(ChainedGamesManager);
00137 }
00138 
00139 Engine::Engine(OSystem *syst)
00140     : _system(syst),
00141         _mixer(_system->getMixer()),
00142         _timer(_system->getTimerManager()),
00143         _eventMan(_system->getEventManager()),
00144         _saveFileMan(_system->getSavefileManager()),
00145         _targetName(ConfMan.getActiveDomainName()),
00146         _pauseLevel(0),
00147         _pauseStartTime(0),
00148         _saveSlotToLoad(-1),
00149         _engineStartTime(_system->getMillis()),
00150         _mainMenuDialog(NULL),
00151         _debugger(NULL),
00152         _autosaveInterval(ConfMan.getInt("autosave_period")),
00153         _lastAutosaveTime(_system->getMillis()) {
00154 
00155     g_engine = this;
00156     Common::setErrorOutputFormatter(defaultOutputFormatter);
00157     Common::setErrorHandler(defaultErrorHandler);
00158 
00159     // FIXME: Get rid of the following again. It is only here
00160     // temporarily. We really should never run with a non-working Mixer,
00161     // so ought to handle this at a much earlier stage. If we *really*
00162     // want to support systems without a working mixer, then we need
00163     // more work. E.g. we could modify the Mixer to immediately drop any
00164     // streams passed to it. This way, at least we don't crash because
00165     // heaps of (sound) memory get allocated but never freed. Of course,
00166     // there still would be problems with many games...
00167     if (!_mixer->isReady())
00168         warning("Sound initialization failed. This may cause severe problems in some games");
00169 
00170     // Setup a dummy cursor and palette, so that all engines can use
00171     // CursorMan.replace without having any headaches about memory leaks.
00172     //
00173     // If an engine only used CursorMan.replaceCursor and no cursor has
00174     // been setup before, then replaceCursor just uses pushCursor. This
00175     // means that that the engine's cursor is never again removed from
00176     // CursorMan. Hence we setup a fake cursor here and remove it again
00177     // in the destructor.
00178     CursorMan.pushCursor(NULL, 0, 0, 0, 0, 0);
00179     // Note: Using this dummy palette will actually disable cursor
00180     // palettes till the user enables it again.
00181     CursorMan.pushCursorPalette(NULL, 0, 0);
00182 }
00183 
00184 Engine::~Engine() {
00185     _mixer->stopAll();
00186 
00187     delete _debugger;
00188     delete _mainMenuDialog;
00189     g_engine = NULL;
00190 
00191     // Remove our cursors again to prevent memory leaks
00192     CursorMan.popCursor();
00193     CursorMan.popCursorPalette();
00194 }
00195 
00196 void Engine::initializePath(const Common::FSNode &gamePath) {
00197     SearchMan.addDirectory(gamePath.getPath(), gamePath, 0, 4);
00198 }
00199 
00200 void initCommonGFX() {
00201     const Common::ConfigManager::Domain *gameDomain = ConfMan.getActiveDomain();
00202 
00203     // Any global or command line settings already have been applied at the time
00204     // we get here, so we only do something if the game domain overrides those
00205     // values
00206     if (gameDomain) {
00207         if (gameDomain->contains("aspect_ratio"))
00208             g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, ConfMan.getBool("aspect_ratio"));
00209 
00210         if (gameDomain->contains("fullscreen"))
00211             g_system->setFeatureState(OSystem::kFeatureFullscreenMode, ConfMan.getBool("fullscreen"));
00212 
00213         if (gameDomain->contains("filtering"))
00214             g_system->setFeatureState(OSystem::kFeatureFilteringMode, ConfMan.getBool("filtering"));
00215 
00216         if (gameDomain->contains("stretch_mode"))
00217             g_system->setStretchMode(ConfMan.get("stretch_mode").c_str());
00218 
00219         if (gameDomain->contains("shader"))
00220             g_system->setShader(ConfMan.get("shader").c_str());
00221     }
00222 }
00223 
00224 // Please leave the splash screen in working order for your releases, even if they're commercial.
00225 // This is a proper and good way to show your appreciation for our hard work over these years.
00226 bool splash = false;
00227 
00228 #include "logo_data.h"
00229 
00230 void splashScreen() {
00231     Common::MemoryReadStream stream(logo_data, ARRAYSIZE(logo_data));
00232 
00233     Image::BitmapDecoder bitmap;
00234 
00235     if (!bitmap.loadStream(stream)) {
00236         warning("Error loading logo file");
00237         return;
00238     }
00239 
00240     g_system->showOverlay();
00241 
00242     // Fill with blue - ResidualVM theme
00243     Graphics::Surface screen;
00244     screen.create(g_system->getOverlayWidth(), g_system->getOverlayHeight(), g_system->getOverlayFormat());
00245     screen.fillRect(Common::Rect(screen.w, screen.h), screen.format.ARGBToColor(0xff, 0x1e, 0x6f, 0x95)); // ResidualVM theme
00246 
00247     // Load logo
00248     Graphics::Surface *logo = bitmap.getSurface()->convertTo(g_system->getOverlayFormat(), bitmap.getPalette());
00249     int lx = MAX((g_system->getOverlayWidth() - logo->w) / 2, 0);
00250     int ly = MAX((g_system->getOverlayHeight() - logo->h) / 2, 0);
00251 
00252     // Print version information
00253     const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont);
00254     int w = font->getStringWidth(gScummVMVersionDate);
00255     int x = g_system->getOverlayWidth() - w - 5; // lx + logo->w - w + 5;
00256     int y = g_system->getOverlayHeight() - font->getFontHeight() - 5; //ly + logo->h + 5;
00257     font->drawString(&screen, gScummVMVersionDate, x, y, w, screen.format.ARGBToColor(0xff, 0, 0, 0));
00258 
00259     g_system->copyRectToOverlay(screen.getPixels(), screen.pitch, 0, 0, screen.w, screen.h);
00260     screen.free();
00261 
00262     // Draw logo
00263     int lw = MIN<uint16>(logo->w, g_system->getOverlayWidth() - lx);
00264     int lh = MIN<uint16>(logo->h, g_system->getOverlayHeight() - ly);
00265 
00266     g_system->copyRectToOverlay(logo->getPixels(), logo->pitch, lx, ly, lw, lh);
00267     logo->free();
00268     delete logo;
00269 
00270     g_system->updateScreen();
00271 
00272     // Delay 0.6 secs
00273     uint time0 = g_system->getMillis();
00274     Common::Event event;
00275 
00276     // We must poll an event in order to have the window shown at least on Mac
00277     g_system->getEventManager()->pollEvent(event);
00278 
00279     while (time0 + 600 > g_system->getMillis()) {
00280         g_system->delayMillis(10);
00281     }
00282     g_system->hideOverlay();
00283 
00284     splash = true;
00285 }
00286 
00287 void initGraphicsModes(const Graphics::ModeList &modes) {
00288     g_system->initSizeHint(modes);
00289 }
00290 
00291 void initGraphics(int width, int height, const Graphics::PixelFormat *format) {
00292 
00293     g_system->beginGFXTransaction();
00294 
00295         initCommonGFX();
00296 #ifdef USE_RGB_COLOR
00297         if (format)
00298             g_system->initSize(width, height, format);
00299         else {
00300             Graphics::PixelFormat bestFormat = g_system->getSupportedFormats().front();
00301             g_system->initSize(width, height, &bestFormat);
00302         }
00303 #else
00304         g_system->initSize(width, height);
00305 #endif
00306 
00307     OSystem::TransactionError gfxError = g_system->endGFXTransaction();
00308 
00309     if (!splash && !GUI::GuiManager::instance()._launched)
00310         splashScreen();
00311 
00312     if (gfxError == OSystem::kTransactionSuccess)
00313         return;
00314 
00315     // Error out on size switch failure
00316     if (gfxError & OSystem::kTransactionSizeChangeFailed) {
00317         Common::String message;
00318         message = Common::String::format(_("Could not switch to resolution '%dx%d'."), width, height);
00319 
00320         GUIErrorMessage(message);
00321         error("%s", message.c_str());
00322     }
00323 
00324     // Just show warnings then these occur:
00325 #ifdef USE_RGB_COLOR
00326     if (gfxError & OSystem::kTransactionFormatNotSupported) {
00327         Common::String message = _("Could not initialize color format.");
00328 
00329         GUI::MessageDialog dialog(message);
00330         dialog.runModal();
00331     }
00332 #endif
00333 
00334     if (gfxError & OSystem::kTransactionModeSwitchFailed) {
00335         Common::String message;
00336         message = Common::String::format(_("Could not switch to video mode '%s'."), ConfMan.get("gfx_mode").c_str());
00337 
00338         GUI::MessageDialog dialog(message);
00339         dialog.runModal();
00340     }
00341 
00342     if (gfxError & OSystem::kTransactionStretchModeSwitchFailed) {
00343         Common::String message;
00344         message = Common::String::format(_("Could not switch to stretch mode '%s'."), ConfMan.get("stretch_mode").c_str());
00345 
00346         GUI::MessageDialog dialog(message);
00347         dialog.runModal();
00348     }
00349 
00350     if (gfxError & OSystem::kTransactionAspectRatioFailed) {
00351         GUI::MessageDialog dialog(_("Could not apply aspect ratio setting."));
00352         dialog.runModal();
00353     }
00354 
00355     if (gfxError & OSystem::kTransactionFullscreenFailed) {
00356         GUI::MessageDialog dialog(_("Could not apply fullscreen setting."));
00357         dialog.runModal();
00358     }
00359 
00360     if (gfxError & OSystem::kTransactionFilteringFailed) {
00361         GUI::MessageDialog dialog(_("Could not apply filtering setting."));
00362         dialog.runModal();
00363     }
00364 }
00365 
00374 inline Graphics::PixelFormat findCompatibleFormat(const Common::List<Graphics::PixelFormat> &backend, const Common::List<Graphics::PixelFormat> &frontend) {
00375 #ifdef USE_RGB_COLOR
00376     for (Common::List<Graphics::PixelFormat>::const_iterator i = backend.begin(); i != backend.end(); ++i) {
00377         for (Common::List<Graphics::PixelFormat>::const_iterator j = frontend.begin(); j != frontend.end(); ++j) {
00378             if (*i == *j)
00379                 return *i;
00380         }
00381     }
00382 #endif
00383     return Graphics::PixelFormat::createFormatCLUT8();
00384 }
00385 
00386 
00387 void initGraphics(int width, int height, const Common::List<Graphics::PixelFormat> &formatList) {
00388     Graphics::PixelFormat format = findCompatibleFormat(g_system->getSupportedFormats(), formatList);
00389     initGraphics(width, height, &format);
00390 }
00391 
00392 void initGraphics(int width, int height) {
00393     Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
00394     initGraphics(width, height, &format);
00395 }
00396 
00397 void GUIErrorMessage(const Common::String &msg) {
00398     g_system->setWindowCaption("Error");
00399     g_system->beginGFXTransaction();
00400         initCommonGFX();
00401         g_system->initSize(320, 200);
00402         g_system->launcherInitSize(640, 480);//ResidualVM specific
00403     if (g_system->endGFXTransaction() == OSystem::kTransactionSuccess) {
00404         GUI::MessageDialog dialog(msg);
00405         dialog.runModal();
00406     } else {
00407         error("%s", msg.c_str());
00408     }
00409 }
00410 
00411 void GUIErrorMessageFormat(const char *fmt, ...) {
00412     Common::String msg;
00413 
00414     va_list va;
00415     va_start(va, fmt);
00416     msg = Common::String::vformat(fmt, va);
00417     va_end(va);
00418 
00419     GUIErrorMessage(msg);
00420 }
00421 
00422 void Engine::checkCD() {
00423 #if defined(WIN32) && !defined(__SYMBIAN32__)
00424     // It is a known bug under Windows that games that play CD audio cause
00425     // ScummVM to crash if the data files are read from the same CD. Check
00426     // if this appears to be the case and issue a warning.
00427 
00428     // If we can find a compressed audio track, then it should be ok even
00429     // if it's running from CD.
00430 
00431 #ifdef USE_VORBIS
00432     if (Common::File::exists("track1.ogg") ||
00433         Common::File::exists("track01.ogg"))
00434         return;
00435 #endif
00436 #ifdef USE_FLAC
00437     if (Common::File::exists("track1.fla") ||
00438             Common::File::exists("track1.flac") ||
00439         Common::File::exists("track01.fla") ||
00440         Common::File::exists("track01.flac"))
00441         return;
00442 #endif
00443 #ifdef USE_MAD
00444     if (Common::File::exists("track1.mp3") ||
00445         Common::File::exists("track01.mp3"))
00446         return;
00447 #endif
00448 
00449     char buffer[MAXPATHLEN];
00450     int i;
00451 
00452     const Common::FSNode gameDataDir(ConfMan.get("path"));
00453 
00454     if (gameDataDir.getPath().empty()) {
00455         // That's it! I give up!
00456         if (getcwd(buffer, MAXPATHLEN) == NULL)
00457             return;
00458     } else
00459         Common::strlcpy(buffer, gameDataDir.getPath().c_str(), sizeof(buffer));
00460 
00461     for (i = 0; i < MAXPATHLEN - 1; i++) {
00462         if (buffer[i] == '\\')
00463             break;
00464     }
00465 
00466     buffer[i + 1] = 0;
00467 
00468     if (GetDriveType(buffer) == DRIVE_CDROM) {
00469         GUI::MessageDialog dialog(
00470             _("You appear to be playing this game directly\n"
00471             "from the CD. This is known to cause problems,\n"
00472             "and it is therefore recommended that you copy\n"
00473             "the data files to your hard disk instead.\n"
00474             "See the README file for details."), _("OK"));
00475         dialog.runModal();
00476     } else {
00477         // If we reached here, the game has audio tracks,
00478         // it's not ran from the CD and the tracks have not
00479         // been ripped.
00480         GUI::MessageDialog dialog(
00481             _("This game has audio tracks in its disk. These\n"
00482             "tracks need to be ripped from the disk using\n"
00483             "an appropriate CD audio extracting tool in\n"
00484             "order to listen to the game's music.\n"
00485             "See the README file for details."), _("OK"));
00486         dialog.runModal();
00487     }
00488 #endif
00489 }
00490 
00491 void Engine::handleAutoSave() {
00492     const int diff = _system->getMillis() - _lastAutosaveTime;
00493 
00494     if (_autosaveInterval != 0 && diff > (_autosaveInterval * 1000)) {
00495         // Save the autosave
00496         saveAutosaveIfEnabled();
00497     }
00498 }
00499 
00500 void Engine::saveAutosaveIfEnabled() {
00501     if (_autosaveInterval != 0) {
00502         bool saveFlag = canSaveAutosaveCurrently();
00503 
00504         if (saveFlag) {
00505             // First check for an existing savegame in the slot, and if present, if it's an autosave
00506             SaveStateDescriptor desc = getMetaEngine().querySaveMetaInfos(
00507                 _targetName.c_str(), getAutosaveSlot());
00508             saveFlag = desc.getSaveSlot() == -1 || desc.isAutosave();
00509         }
00510 
00511         if (saveFlag && saveGameState(getAutosaveSlot(), _("Autosave"), true).getCode() != Common::kNoError) {
00512             // Couldn't autosave at the designated time
00513             g_system->displayMessageOnOSD(_("Error occurred making autosave"));
00514             saveFlag = false;
00515         }
00516 
00517         if (!saveFlag) {
00518             // Set the next autosave interval to be in 5 minutes, rather than whatever
00519             // full autosave interval the user has selected
00520             _lastAutosaveTime = _system->getMillis() + (5 * 60 * 1000) - _autosaveInterval;
00521             return;
00522         }
00523     }
00524 
00525     // Reset the last autosave time
00526     _lastAutosaveTime = _system->getMillis();
00527 }
00528 
00529 void Engine::errorString(const char *buf1, char *buf2, int size) {
00530     Common::strlcpy(buf2, buf1, size);
00531 }
00532 
00533 void Engine::pauseEngine(bool pause) {
00534     assert((pause && _pauseLevel >= 0) || (!pause && _pauseLevel));
00535 
00536     if (pause)
00537         _pauseLevel++;
00538     else
00539         _pauseLevel--;
00540 
00541     if (_pauseLevel == 1 && pause) {
00542         _pauseStartTime = _system->getMillis();
00543         pauseEngineIntern(true);
00544     } else if (_pauseLevel == 0) {
00545         pauseEngineIntern(false);
00546         _engineStartTime += _system->getMillis() - _pauseStartTime;
00547         _pauseStartTime = 0;
00548     }
00549 }
00550 
00551 void Engine::pauseEngineIntern(bool pause) {
00552     // By default, just (un)pause all digital sounds
00553     _mixer->pauseAll(pause);
00554 }
00555 
00556 void Engine::openMainMenuDialog() {
00557     if (!_mainMenuDialog)
00558         _mainMenuDialog = new MainMenuDialog(this);
00559 #ifdef USE_TTS
00560     Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
00561     ttsMan->pushState();
00562     g_gui.initTextToSpeech();
00563 #endif
00564 
00565     setGameToLoadSlot(-1);
00566 
00567     runDialog(*_mainMenuDialog);
00568 
00569     // Load savegame after main menu execution
00570     // (not from inside the menu loop to avoid
00571     // mouse cursor glitches and similar bugs,
00572     // e.g. #2822778).
00573     if (_saveSlotToLoad >= 0) {
00574         Common::Error status = loadGameState(_saveSlotToLoad);
00575         if (status.getCode() != Common::kNoError) {
00576             Common::String failMessage = Common::String::format(_("Failed to load saved game (%s)! "
00577                   "Please consult the README for basic information, and for "
00578                   "instructions on how to obtain further assistance."), status.getDesc().c_str());
00579             GUI::MessageDialog dialog(failMessage);
00580             dialog.runModal();
00581         }
00582     }
00583 
00584     applyGameSettings();
00585     syncSoundSettings();
00586 #ifdef USE_TTS
00587     ttsMan->popState();
00588 #endif
00589 }
00590 
00591 bool Engine::warnUserAboutUnsupportedGame() {
00592     if (ConfMan.getBool("enable_unsupported_game_warning")) {
00593         GUI::MessageDialog alert(_("WARNING: The game you are about to start is"
00594             " not yet fully supported by ResidualVM. As such, it is likely to be"
00595             " unstable, and any saved game you make might not work in future"
00596             " versions of ResidualVM."), _("Start anyway"), _("Cancel"));
00597         return alert.runModal() == GUI::kMessageOK;
00598     }
00599     return true;
00600 }
00601 
00602 uint32 Engine::getTotalPlayTime() const {
00603     if (!_pauseLevel)
00604         return _system->getMillis() - _engineStartTime;
00605     else
00606         return _pauseStartTime - _engineStartTime;
00607 }
00608 
00609 void Engine::setTotalPlayTime(uint32 time) {
00610     const uint32 currentTime = _system->getMillis();
00611 
00612     // We need to reset the pause start time here in case the engine is already
00613     // paused to avoid any incorrect play time counting.
00614     if (_pauseLevel > 0)
00615         _pauseStartTime = currentTime;
00616 
00617     _engineStartTime = currentTime - time;
00618 }
00619 
00620 int Engine::runDialog(GUI::Dialog &dialog) {
00621     pauseEngine(true);
00622     int result = dialog.runModal();
00623     pauseEngine(false);
00624 
00625     return result;
00626 }
00627 
00628 void Engine::setGameToLoadSlot(int slot) {
00629     _saveSlotToLoad = slot;
00630 }
00631 
00632 void Engine::syncSoundSettings() {
00633     // Sync the engine with the config manager
00634     int soundVolumeMusic = ConfMan.getInt("music_volume");
00635     int soundVolumeSFX = ConfMan.getInt("sfx_volume");
00636     int soundVolumeSpeech = ConfMan.getInt("speech_volume");
00637 
00638     bool mute = false;
00639     if (ConfMan.hasKey("mute"))
00640         mute = ConfMan.getBool("mute");
00641 
00642     // We need to handle the speech mute separately here. This is because the
00643     // engine code should be able to rely on all speech sounds muted when the
00644     // user specified subtitles only mode, which results in "speech_mute" to
00645     // be set to "true". The global mute setting has precedence over the
00646     // speech mute setting though.
00647     bool speechMute = mute;
00648     if (!speechMute)
00649         speechMute = ConfMan.getBool("speech_mute");
00650 
00651     _mixer->muteSoundType(Audio::Mixer::kPlainSoundType, mute);
00652     _mixer->muteSoundType(Audio::Mixer::kMusicSoundType, mute);
00653     _mixer->muteSoundType(Audio::Mixer::kSFXSoundType, mute);
00654     _mixer->muteSoundType(Audio::Mixer::kSpeechSoundType, speechMute);
00655 
00656     _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, Audio::Mixer::kMaxMixerVolume);
00657     _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
00658     _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSFX);
00659     _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech);
00660 }
00661 
00662 void Engine::flipMute() {
00663     // Mute will be set to true by default here. This has two reasons:
00664     // - if the game already has an "mute" config entry, it will be overwritten anyway.
00665     // - if it does not have a "mute" config entry, the sound is unmuted currently and should be muted now.
00666     bool mute = true;
00667 
00668     if (ConfMan.hasKey("mute")) {
00669         mute = !ConfMan.getBool("mute");
00670     }
00671 
00672     ConfMan.setBool("mute", mute);
00673 
00674     syncSoundSettings();
00675 }
00676 
00677 Common::Error Engine::loadGameState(int slot) {
00678     // In case autosaves are on, do a save first before loading the new save
00679     saveAutosaveIfEnabled();
00680 
00681     Common::InSaveFile *saveFile = _saveFileMan->openForLoading(getSaveStateName(slot));
00682 
00683     if (!saveFile)
00684         return Common::kReadingFailed;
00685 
00686     Common::Error result = loadGameStream(saveFile);
00687     if (result.getCode() == Common::kNoError) {
00688         ExtendedSavegameHeader header;
00689         if (MetaEngine::readSavegameHeader(saveFile, &header))
00690             setTotalPlayTime(header.playtime);
00691     }
00692 
00693     delete saveFile;
00694     return result;
00695 }
00696 
00697 Common::Error Engine::loadGameStream(Common::SeekableReadStream *stream) {
00698     // Default to returning an error when not implemented
00699     return Common::kReadingFailed;
00700 }
00701 
00702 bool Engine::canLoadGameStateCurrently() {
00703     // Do not allow loading by default
00704     return false;
00705 }
00706 
00707 Common::Error Engine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
00708     Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(getSaveStateName(slot));
00709 
00710     if (!saveFile)
00711         return Common::kWritingFailed;
00712 
00713     Common::Error result = saveGameStream(saveFile, isAutosave);
00714     if (result.getCode() == Common::kNoError) {
00715         MetaEngine::appendExtendedSave(saveFile, getTotalPlayTime() / 1000, desc, isAutosave);
00716 
00717         saveFile->finalize();
00718     }
00719 
00720     delete saveFile;
00721     return result;
00722 }
00723 
00724 Common::Error Engine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
00725     // Default to returning an error when not implemented
00726     return Common::kWritingFailed;
00727 }
00728 
00729 bool Engine::canSaveGameStateCurrently() {
00730     // Do not allow saving by default
00731     return false;
00732 }
00733 
00734 bool Engine::loadGameDialog() {
00735     if (!canLoadGameStateCurrently()) {
00736         g_system->displayMessageOnOSD(_("Loading game is currently unavailable"));
00737         return false;
00738     }
00739 
00740     GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"), false);
00741     pauseEngine(true);
00742     int slotNum = dialog->runModalWithCurrentTarget();
00743     pauseEngine(false);
00744     delete dialog;
00745 
00746     if (slotNum < 0)
00747         return false;
00748 
00749     Common::Error loadError = loadGameState(slotNum);
00750     if (loadError.getCode() != Common::kNoError) {
00751         GUI::MessageDialog errorDialog(loadError.getDesc());
00752         errorDialog.runModal();
00753         return false;
00754     }
00755 
00756     return true;
00757 }
00758 
00759 bool Engine::saveGameDialog() {
00760     if (!canSaveGameStateCurrently()) {
00761         g_system->displayMessageOnOSD(_("Saving game is currently unavailable"));
00762         return false;
00763     }
00764 
00765     GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
00766     pauseEngine(true);
00767     int slotNum = dialog->runModalWithCurrentTarget();
00768     pauseEngine(false);
00769 
00770     Common::String desc = dialog->getResultString();
00771     if (desc.empty())
00772         desc = dialog->createDefaultSaveDescription(slotNum);
00773 
00774     delete dialog;
00775 
00776     if (slotNum < 0)
00777         return false;
00778 
00779     Common::Error saveError = saveGameState(slotNum, desc);
00780     if (saveError.getCode() != Common::kNoError) {
00781         GUI::MessageDialog errorDialog(saveError.getDesc());
00782         errorDialog.runModal();
00783         return false;
00784     }
00785 
00786     return true;
00787 }
00788 
00789 void Engine::quitGame() {
00790     Common::Event event;
00791 
00792     event.type = Common::EVENT_QUIT;
00793     g_system->getEventManager()->pushEvent(event);
00794 }
00795 
00796 bool Engine::shouldQuit() {
00797     Common::EventManager *eventMan = g_system->getEventManager();
00798     return (eventMan->shouldQuit() || eventMan->shouldRTL());
00799 }
00800 
00801 GUI::Debugger *Engine::getOrCreateDebugger() {
00802     if (!_debugger)
00803         // Create a bare-bones debugger. This is useful for engines without their own
00804         // debugger when an error occurs
00805         _debugger = new GUI::Debugger();
00806 
00807     return _debugger;
00808 }
00809 
00810 /*
00811 EnginePlugin *Engine::getMetaEnginePlugin() const {
00812     return EngineMan.findPlugin(ConfMan.get("engineid"));
00813 }
00814 
00815 */
00816 
00817 MetaEngine &Engine::getMetaEngine() {
00818     const Plugin *plugin = EngineMan.findPlugin(ConfMan.get("engineid"));
00819     assert(plugin);
00820     return plugin->get<MetaEngine>();
00821 }


Generated on Sat May 30 2020 05:00:43 for ResidualVM by doxygen 1.7.1
curved edge   curved edge