/* 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 3 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, see . * */ #include "common/config-manager.h" #include "common/debug-channels.h" #include "common/error.h" #include "common/punycode.h" #include "common/tokenizer.h" #include "graphics/macgui/macwindowmanager.h" #include "graphics/wincursor.h" #include "director/director.h" #include "director/debugger.h" #include "director/archive.h" #include "director/cast.h" #include "director/movie.h" #include "director/score.h" #include "director/sound.h" #include "director/window.h" #include "director/lingo/lingo.h" #include "director/detection.h" /** * When detection is compiled dynamically, directory globs end up in detection plugin and * engine cannot link to them so duplicate them in the engine in this case */ #ifndef DETECTION_STATIC #include "director/detection_paths.h" #endif namespace Director { DirectorEngine *g_director; DirectorEngine::DirectorEngine(OSystem *syst, const DirectorGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) { g_director = this; g_debugger = new Debugger(); setDebugger(g_debugger); _dirSeparator = ':'; parseOptions(); // Setup mixer syncSoundSettings(); // Load Palettes loadDefaultPalettes(); // Load Patterns loadPatterns(); // Load key codes loadKeyCodes(); memset(_currentPalette, 0, 768); _currentPaletteLength = 0; _stage = nullptr; _windowList = new Datum; _windowList->type = ARRAY; _windowList->u.farr = new FArray; _currentWindow = nullptr; _cursorWindow = nullptr; _lingo = nullptr; _clipBoard = nullptr; _version = getDescriptionVersion(); _fixStageSize = false; _fixStageRect = Common::Rect(); _wmMode = 0; _wmWidth = 1024; _wmHeight = 768; _wm = nullptr; _gameDataDir = Common::FSNode(ConfMan.get("path")); SearchMan.addDirectory(_gameDataDir.getPath(), _gameDataDir, 0, 5); for (uint i = 0; Director::directoryGlobs[i]; i++) { Common::String directoryGlob = directoryGlobs[i]; SearchMan.addSubDirectoryMatching(_gameDataDir, directoryGlob); } if (debugChannelSet(-1, kDebug32bpp)) _colorDepth = 32; else _colorDepth = 8; // 256-color switch (getPlatform()) { case Common::kPlatformMacintoshII: _machineType = 4; break; case Common::kPlatformPippin: _machineType = 71; break; case Common::kPlatformWindows: _machineType = 256; break; case Common::kPlatformMacintosh: default: _machineType = 9; // Macintosh IIci } _playbackPaused = false; _skipFrameAdvance = false; _centerStage = true; _surface = nullptr; _tickBaseline = 0; } DirectorEngine::~DirectorEngine() { delete _windowList; delete _lingo; delete _wm; delete _surface; for (Common::HashMap::iterator it = _allOpenResFiles.begin(); it != _allOpenResFiles.end(); ++it) { delete it->_value; } for (uint i = 0; i < _winCursor.size(); i++) delete _winCursor[i]; clearPalettes(); } Archive *DirectorEngine::getMainArchive() const { return _currentWindow->getMainArchive(); } Movie *DirectorEngine::getCurrentMovie() const { return _currentWindow->getCurrentMovie(); } Common::String DirectorEngine::getCurrentPath() const { return _currentWindow->getCurrentPath(); } Common::String DirectorEngine::getCurrentAbsolutePath() { Common::String currentPath = getCurrentPath(); Common::String result; result += (getPlatform() == Common::kPlatformWindows) ? "C:\\" : ""; result += convertPath(currentPath); return result; } static bool buildbotErrorHandler(const char *msg) { return true; } void DirectorEngine::setCurrentMovie(Movie *movie) { _currentWindow = movie->getWindow(); } void DirectorEngine::setVersion(uint16 version) { if (version == _version) return; debug("Switching to Director v%d", version); _version = version; _lingo->reloadBuiltIns(); } Common::Error DirectorEngine::run() { debug("Starting v%d Director game", getVersion()); // We want to avoid GUI errors for buildbot, because they hang it if (debugChannelSet(-1, kDebugFewFramesOnly)) Common::setErrorHandler(buildbotErrorHandler); if (!_mixer->isReady()) { return Common::kAudioDeviceInitFailed; } memset(_currentPalette, 0, 768); // we run mac-style menus | and we will redraw all widgets _wmMode = Graphics::kWMModalMenuMode | Graphics::kWMModeManualDrawWidgets; if (!debugChannelSet(-1, kDebugDesktop)) _wmMode |= Graphics::kWMModeFullscreen | Graphics::kWMModeNoDesktop; if (debugChannelSet(-1, kDebug32bpp)) _wmMode |= Graphics::kWMMode32bpp; _wm = new Graphics::MacWindowManager(_wmMode, &_director3QuickDrawPatterns, getLanguage()); _wm->setEngine(this); gameQuirks(_gameDescription->desc.gameId, _gameDescription->desc.platform); _wm->setDesktopMode(_wmMode); _wm->printWMMode(); _pixelformat = _wm->_pixelformat; debug("Director pixelformat is: %s", _pixelformat.toString().c_str()); _stage = new Window(_wm->getNextId(), false, false, false, _wm, this, true); *_stage->_refCount += 1; if (!desktopEnabled()) _stage->disableBorder(); _surface = new Graphics::ManagedSurface(1, 1); _wm->setScreen(_surface); _wm->addWindowInitialized(_stage); _wm->setActiveWindow(_stage->getId()); setPalette(-1); _currentWindow = _stage; _lingo = new Lingo(this); _lingo->switchStateFromWindow(); if (getGameGID() == GID_TEST) { _currentWindow->runTests(); return Common::kNoError; } else if (getGameGID() == GID_TESTALL) { _currentWindow->enqueueAllMovies(); } if (getPlatform() == Common::kPlatformWindows) _machineType = 256; // IBM PC-type machine Common::Error err = _currentWindow->loadInitialMovie(); if (err.getCode() != Common::kNoError) return err; if (debugChannelSet(-1, kDebugConsole)) { g_debugger->attach(); g_system->updateScreen(); } bool loop = true; while (loop) { if (_stage->getCurrentMovie()) processEvents(); _currentWindow = _stage; g_lingo->switchStateFromWindow(); loop = _currentWindow->step(); if (loop) { FArray *windowList = g_lingo->_windowList.u.farr; for (uint i = 0; i < windowList->arr.size(); i++) { if (windowList->arr[i].type != OBJECT || windowList->arr[i].u.obj->getObjType() != kWindowObj) continue; _currentWindow = static_cast(windowList->arr[i].u.obj); g_lingo->switchStateFromWindow(); _currentWindow->step(); } } draw(); _system->delayMillis(10); } return Common::kNoError; } Common::CodePage DirectorEngine::getPlatformEncoding() { // Returns the default encoding for the platform we're pretending to be. // (English Mac OS, Japanese Mac OS, English Windows, etc.) return getEncoding(getPlatform(), getLanguage()); } Common::String DirectorEngine::getEXEName() const { StartMovie startMovie = getStartMovie(); if (startMovie.startMovie.size() > 0) return startMovie.startMovie; return Common::punycode_decodefilename(Common::lastPathComponent(_gameDescription->desc.filesDescriptions[0].fileName, '/')); } void DirectorEngine::parseOptions() { _options.startMovie.startFrame = -1; if (!ConfMan.hasKey("start_movie")) return; Common::StringTokenizer tok(ConfMan.get("start_movie"), ","); while (!tok.empty()) { Common::String part = tok.nextToken(); int eqPos = part.findLastOf("="); Common::String key; Common::String value; if ((uint)eqPos != Common::String::npos) { key = part.substr(0, eqPos); value = part.substr(eqPos + 1, part.size()); } else { value = part; } if (key == "movie" || key.empty()) { // Format is movie[@startFrame] if (!_options.startMovie.startMovie.empty()) { warning("parseOptions(): Duplicate startup movie: %s", value.c_str()); } int atPos = value.findLastOf("@"); if ((uint)atPos == Common::String::npos) { _options.startMovie.startMovie = value; } else { _options.startMovie.startMovie = value.substr(0, atPos); Common::String tail = value.substr(atPos + 1, value.size()); if (tail.size() > 0) _options.startMovie.startFrame = atoi(tail.c_str()); } _options.startMovie.startMovie = Common::punycode_decodepath(_options.startMovie.startMovie).toString(_dirSeparator); debug(2, "parseOptions(): Movie is: %s, frame is: %d", _options.startMovie.startMovie.c_str(), _options.startMovie.startFrame); } else if (key == "startup") { _options.startupPath = value; debug(2, "parseOptions(): Startup is: %s", value.c_str()); } else { warning("parseOptions(): unknown option %s", part.c_str()); } } } StartMovie DirectorEngine::getStartMovie() const { return _options.startMovie; } Common::String DirectorEngine::getStartupPath() const { return _options.startupPath; } bool DirectorEngine::desktopEnabled() { return !(_wmMode & Graphics::kWMModeNoDesktop); } } // End of namespace Director