Initial commit
This commit is contained in:
496
engines/director/director.cpp
Normal file
496
engines/director/director.cpp
Normal file
@@ -0,0 +1,496 @@
|
||||
/* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/modular-backend.h"
|
||||
#include "backends/graphics/graphics.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug-channels.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/picture.h"
|
||||
#include "director/score.h"
|
||||
#include "director/sound.h"
|
||||
#include "director/window.h"
|
||||
#include "director/debugger/debugtools.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);
|
||||
|
||||
// parseOptions depends on the _dirSeparator
|
||||
_version = getDescriptionVersion();
|
||||
if (getPlatform() == Common::kPlatformWindows && _version >= 400) {
|
||||
_dirSeparator = '\\';
|
||||
} else {
|
||||
_dirSeparator = ':';
|
||||
}
|
||||
|
||||
parseOptions();
|
||||
|
||||
// Setup mixer
|
||||
syncSoundSettings();
|
||||
_defaultVolume = _mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType);
|
||||
|
||||
// Load Palettes
|
||||
loadDefaultPalettes();
|
||||
|
||||
// Load Patterns
|
||||
loadPatterns();
|
||||
|
||||
// Load key codes
|
||||
loadKeyCodes();
|
||||
|
||||
memset(_currentPalette, 0, 768);
|
||||
_currentPaletteLength = 0;
|
||||
_stage = nullptr;
|
||||
_mainArchive = nullptr;
|
||||
_currentWindow = nullptr;
|
||||
_cursorWindow = nullptr;
|
||||
_lingo = nullptr;
|
||||
_clipBoard = nullptr;
|
||||
_fixStageSize = false;
|
||||
_fixStageRect = Common::Rect();
|
||||
_wmMode = 0;
|
||||
_primitives = nullptr;
|
||||
|
||||
_wmWidth = 1024;
|
||||
_wmHeight = 768;
|
||||
|
||||
_fpsLimit = 0;
|
||||
_forceDate.tm_sec = -1;
|
||||
_forceDate.tm_min = -1;
|
||||
_forceDate.tm_hour = -1;
|
||||
_forceDate.tm_mday = -1;
|
||||
_forceDate.tm_mon = -1;
|
||||
_forceDate.tm_year = -1;
|
||||
_forceDate.tm_wday = -1;
|
||||
_loadSlowdownFactor = 0;
|
||||
_loadSlowdownCooldownTime = 0;
|
||||
_fileIOType = 0;
|
||||
_vfwPaletteHack = false;
|
||||
|
||||
_wm = nullptr;
|
||||
|
||||
_gameDataDir = Common::FSNode(ConfMan.getPath("path"));
|
||||
|
||||
SearchMan.addDirectory(_gameDataDir, 0, 5);
|
||||
|
||||
for (uint i = 0; Director::directoryGlobs[i]; i++) {
|
||||
Common::String directoryGlob = directoryGlobs[i];
|
||||
SearchMan.addSubDirectoryMatching(_gameDataDir, directoryGlob, 0, 5);
|
||||
}
|
||||
|
||||
if (ConfMan.getBool("true_color") || (getGameFlags() & GF_32BPP) || debugChannelSet(-1, kDebug32bpp)) {
|
||||
#ifdef USE_RGB_COLOR
|
||||
_colorDepth = 32;
|
||||
#else
|
||||
warning("32-bpp color dept is not supported, forcing 8-bit");
|
||||
_colorDepth = 8;
|
||||
#endif
|
||||
} else {
|
||||
_colorDepth = 8; // 256-color
|
||||
}
|
||||
// Enable Macintosh gamma correction. This resolves the issue of Mac games appearing too dark.
|
||||
// Enabled by default for Macintosh and Pippin games in the detection code.
|
||||
// Right now only used in 8-bit mode to adjust the palette.
|
||||
// FIXME: How do we add this to true color rendering without a heap of workarounds?
|
||||
if ((getPlatform() == Common::kPlatformMacintosh) || (getPlatform() == Common::kPlatformPippin)) {
|
||||
_gammaCorrection = ConfMan.getBool("gamma_correction");
|
||||
} else {
|
||||
// FIXME: It would be good if we could have this option for non-Mac, except not
|
||||
// enabled by default.
|
||||
_gammaCorrection = false;
|
||||
}
|
||||
|
||||
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;
|
||||
_centerStage = true;
|
||||
|
||||
_surface = nullptr;
|
||||
_tickBaseline = 0;
|
||||
|
||||
_emulateMultiButtonMouse = false;
|
||||
}
|
||||
|
||||
DirectorEngine::~DirectorEngine() {
|
||||
delete _lingo;
|
||||
|
||||
clearPalettes();
|
||||
|
||||
for (auto &it : _windowList) {
|
||||
it->decRefCount();
|
||||
}
|
||||
_stage->decRefCount();
|
||||
_stage = nullptr;
|
||||
if (_currentWindow) {
|
||||
_currentWindow->decRefCount();
|
||||
_currentWindow = nullptr;
|
||||
}
|
||||
delete _wm;
|
||||
for (auto &it : _allSeenResFiles) {
|
||||
delete it._value;
|
||||
}
|
||||
for (uint i = 0; i < _winCursor.size(); i++)
|
||||
delete _winCursor[i];
|
||||
|
||||
delete _surface;
|
||||
delete _primitives;
|
||||
}
|
||||
|
||||
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 && _version >= 400) ? "C:\\" : "@:";
|
||||
result += convertPath(currentPath);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool buildbotErrorHandler(const char *msg) { return true; }
|
||||
|
||||
Window *DirectorEngine::getOrCreateWindow(Common::String &name) {
|
||||
for (auto &it : _windowList) {
|
||||
if (it->getName().equalsIgnoreCase(name)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
Window *window = new Window(_wm->getNextId(), false, false, false, _wm, g_director, false);
|
||||
window->setName(name);
|
||||
window->setTitle(name);
|
||||
window->resizeInner(1, 1);
|
||||
window->setVisible(false, true);
|
||||
window->move(0, 0);
|
||||
window->incRefCount();
|
||||
_wm->addWindowInitialized(window->getMacWindow());
|
||||
_windowList.push_back(window);
|
||||
return window;
|
||||
}
|
||||
|
||||
void DirectorEngine::forgetWindow(Window *window) {
|
||||
for (auto &it : _windowsToForget) {
|
||||
if (it == window)
|
||||
return;
|
||||
}
|
||||
window->setVisible(false, true);
|
||||
_windowsToForget.push_back(window);
|
||||
}
|
||||
|
||||
void DirectorEngine::setCurrentWindow(Window *window) {
|
||||
if (_currentWindow == window)
|
||||
return;
|
||||
if (_currentWindow)
|
||||
_currentWindow->decRefCount();
|
||||
_currentWindow = window;
|
||||
_currentWindow->incRefCount();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
#ifdef USE_RGB_COLOR
|
||||
if (ConfMan.getBool("true_color") || (getGameFlags() & GF_32BPP) || debugChannelSet(-1, kDebug32bpp))
|
||||
_wmMode |= Graphics::kWMMode32bpp;
|
||||
#endif
|
||||
|
||||
if (getGameFlags() & GF_DESKTOP)
|
||||
_wmMode &= ~Graphics::kWMModeNoDesktop;
|
||||
|
||||
if (getGameFlags() & GF_640x480) {
|
||||
_wmWidth = 640;
|
||||
_wmHeight = 480;
|
||||
}
|
||||
|
||||
_wm = new Graphics::MacWindowManager(_wmMode, &_director3QuickDrawPatterns, getLanguage());
|
||||
_wm->setEngine(this);
|
||||
|
||||
gameQuirks(_gameDescription->desc.gameId, _gameDescription->desc.platform);
|
||||
// Mix in all the saved files for the current target
|
||||
// Assign higher priority to save games to load them before original game files
|
||||
SearchMan.add(kSavedFilesArchive, new SavedArchive(_targetName), 1);
|
||||
|
||||
_wm->setDesktopMode(_wmMode);
|
||||
|
||||
_wm->printWMMode();
|
||||
|
||||
_pixelformat = _wm->_pixelformat;
|
||||
|
||||
debugC(1, kDebugImages, "Director pixelformat is: %s", _pixelformat.toString().c_str());
|
||||
|
||||
_stage = new Window(_wm->getNextId(), false, false, false, _wm, this, true);
|
||||
_stage->incRefCount();
|
||||
|
||||
// Set this as background so it doesn't come to foreground when multiple windows present
|
||||
_wm->setBackgroundWindow(_stage->getMacWindow());
|
||||
|
||||
if (!desktopEnabled())
|
||||
_stage->disableBorder();
|
||||
|
||||
_surface = new Graphics::ManagedSurface(1, 1);
|
||||
_wm->setScreen(_surface);
|
||||
_wm->addWindowInitialized(_stage->getMacWindow());
|
||||
_wm->setActiveWindow(_stage->getId());
|
||||
setPalette(CastMemberID(kClutSystemMac, -1));
|
||||
|
||||
setCurrentWindow(_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();
|
||||
|
||||
// Exit gracefully when run with buildbot
|
||||
if (debugChannelSet(-1, kDebugFewFramesOnly) && err.getCode() == Common::kNoGameDataFoundError)
|
||||
return Common::kNoError;
|
||||
|
||||
if (err.getCode() != Common::kNoError)
|
||||
return err;
|
||||
|
||||
if (debugChannelSet(-1, kDebugConsole)) {
|
||||
g_debugger->attach();
|
||||
g_system->updateScreen();
|
||||
}
|
||||
|
||||
#ifdef USE_IMGUI
|
||||
ImGuiCallbacks callbacks;
|
||||
bool drawImGui = debugChannelSet(-1, kDebugImGui);
|
||||
callbacks.init = DT::onImGuiInit;
|
||||
callbacks.render = drawImGui ? DT::onImGuiRender : nullptr;
|
||||
callbacks.cleanup = DT::onImGuiCleanup;
|
||||
_system->setImGuiCallbacks(callbacks);
|
||||
#endif
|
||||
|
||||
bool loop = true;
|
||||
|
||||
while (loop) {
|
||||
if (_stage->getCurrentMovie())
|
||||
processEvents();
|
||||
|
||||
setCurrentWindow(_stage);
|
||||
g_lingo->switchStateFromWindow();
|
||||
loop = _currentWindow->step();
|
||||
|
||||
if (loop) {
|
||||
for (auto &it : _windowList) {
|
||||
setCurrentWindow(it);
|
||||
g_lingo->switchStateFromWindow();
|
||||
_currentWindow->step();
|
||||
}
|
||||
}
|
||||
|
||||
draw();
|
||||
while (!_windowsToForget.empty()) {
|
||||
Window *window = _windowsToForget.back();
|
||||
_windowsToForget.pop_back();
|
||||
for (size_t i = 0; i < _windowList.size(); i++) {
|
||||
if (_windowList[i] == window) {
|
||||
_windowList.remove_at(i);
|
||||
// FIXME: force window to be removed from WM
|
||||
window->decRefCount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_director->delayMillis(10);
|
||||
#ifdef USE_IMGUI
|
||||
// For performance reasons, disable the renderer callback if the ImGui debug flag isn't set
|
||||
if (debugChannelSet(-1, kDebugImGui) != drawImGui) {
|
||||
drawImGui = !drawImGui;
|
||||
callbacks.render = drawImGui ? DT::onImGuiRender : nullptr;
|
||||
_system->setImGuiCallbacks(callbacks);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_IMGUI
|
||||
_system->setImGuiCallbacks(ImGuiCallbacks());
|
||||
#endif
|
||||
|
||||
if (debugChannelSet(10, kDebugSaving)) {
|
||||
//_mainArchive->writeToFile(Common::String(""), getCurrentMovie());
|
||||
}
|
||||
|
||||
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::getRawEXEName() const {
|
||||
if (!_gameDescription->desc.filesDescriptions[0].fileName)
|
||||
return Common::String();
|
||||
|
||||
// Returns raw executable name (without getting overloaded from --start-movie option)
|
||||
return Common::Path(_gameDescription->desc.filesDescriptions[0].fileName).toString(g_director->_dirSeparator);
|
||||
}
|
||||
|
||||
Common::String DirectorEngine::getEXEName() const {
|
||||
StartMovie startMovie = getStartMovie();
|
||||
if (startMovie.startMovie.size() > 0)
|
||||
return startMovie.startMovie;
|
||||
|
||||
return getRawEXEName();
|
||||
}
|
||||
|
||||
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::Path(_options.startMovie.startMovie).punycodeDecode().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::Path DirectorEngine::getStartupPath() const {
|
||||
return Common::Path(_options.startupPath, g_director->_dirSeparator);
|
||||
}
|
||||
|
||||
bool DirectorEngine::desktopEnabled() {
|
||||
return !(_wmMode & Graphics::kWMModeNoDesktop);
|
||||
}
|
||||
|
||||
PatternTile::~PatternTile() {
|
||||
delete img;
|
||||
}
|
||||
|
||||
} // End of namespace Director
|
||||
Reference in New Issue
Block a user