scummvm/engines/groovie/groovie.cpp
Colin Snover 432fd522d2 ENGINES: Remove default1x scaler flag
This flag is removed for a few reasons:

* Engines universally set this flag to true for widths > 320,
  which made it redundant everywhere;
* This flag functioned primarily as a "force 1x scaler" flag,
  since its behaviour was almost completely undocumented and users
  would need to figure out that they'd need an explicit non-default
  scaler set to get a scaler to operate at widths > 320;
* (Most importantly) engines should not be in the business of
  deciding how the backend may choose to render its virtual screen.
  The choice of rendering behaviour belongs to the user, and the
  backend, in that order.

A nearby future commit restores the default1x scaler behaviour in
the SDL backend code for the moment, but in the future it is my
hope that there will be a better configuration UI to allow users
to specify how they want scaling to work for high resolutions.
2017-10-07 12:30:29 -05:00

390 lines
12 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 "groovie/groovie.h"
#include "groovie/cursor.h"
#include "groovie/detection.h"
#include "groovie/graphics.h"
#include "groovie/music.h"
#include "groovie/resource.h"
#include "groovie/stuffit.h"
#include "groovie/vdx.h"
#ifdef ENABLE_GROOVIE2
#include "groovie/roq.h"
#endif
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/file.h"
#include "common/macresman.h"
#include "common/textconsole.h"
#include "backends/audiocd/audiocd.h"
#include "engines/util.h"
#include "graphics/fontman.h"
#include "audio/mixer.h"
namespace Groovie {
GroovieEngine::GroovieEngine(OSystem *syst, const GroovieGameDescription *gd) :
Engine(syst), _gameDescription(gd), _debugger(NULL), _script(NULL),
_resMan(NULL), _grvCursorMan(NULL), _videoPlayer(NULL), _musicPlayer(NULL),
_graphicsMan(NULL), _macResFork(NULL), _waitingForInput(false), _font(NULL),
_spookyMode(false) {
// Initialize the custom debug levels
DebugMan.addDebugChannel(kDebugVideo, "Video", "Debug video and audio playback");
DebugMan.addDebugChannel(kDebugResource, "Resource", "Debug resource management");
DebugMan.addDebugChannel(kDebugScript, "Script", "Debug the scripts");
DebugMan.addDebugChannel(kDebugUnknown, "Unknown", "Report values of unknown data in files");
DebugMan.addDebugChannel(kDebugHotspots, "Hotspots", "Show the hotspots");
DebugMan.addDebugChannel(kDebugCursor, "Cursor", "Debug cursor decompression / switching");
DebugMan.addDebugChannel(kDebugMIDI, "MIDI", "Debug MIDI / XMIDI files");
DebugMan.addDebugChannel(kDebugScriptvars, "Scriptvars", "Print out any change to script variables");
DebugMan.addDebugChannel(kDebugCell, "Cell", "Debug the cell game (in the microscope)");
DebugMan.addDebugChannel(kDebugFast, "Fast", "Play videos quickly, with no sound (unstable)");
// Adding the default directories
const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "groovie");
SearchMan.addSubDirectoryMatching(gameDataDir, "media");
SearchMan.addSubDirectoryMatching(gameDataDir, "system");
SearchMan.addSubDirectoryMatching(gameDataDir, "MIDI");
_modeSpeed = kGroovieSpeedNormal;
if (ConfMan.hasKey("fast_movie_speed") && ConfMan.getBool("fast_movie_speed"))
_modeSpeed = kGroovieSpeedFast;
}
GroovieEngine::~GroovieEngine() {
// Delete the remaining objects
delete _debugger;
delete _resMan;
delete _grvCursorMan;
delete _videoPlayer;
delete _musicPlayer;
delete _graphicsMan;
delete _script;
delete _macResFork;
}
Common::Error GroovieEngine::run() {
if (_gameDescription->version == kGroovieV2 && getPlatform() == Common::kPlatformMacintosh) {
// Load the Mac installer with the lowest priority (in case the user has installed
// the game and has the MIDI folder present; faster to just load them)
Common::Archive *archive = createStuffItArchive("The 11th Hour Installer");
if (archive)
SearchMan.add("The 11th Hour Installer", archive);
}
_script = new Script(this, _gameDescription->version);
// Initialize the graphics
switch (_gameDescription->version) {
case kGroovieV2: {
// Request the mode with the highest precision available
Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0);
initGraphics(640, 480, &format);
if (_system->getScreenFormat() != format)
return Common::kUnsupportedColorMode;
// Save the enabled mode
_pixelFormat = format;
break;
}
case kGroovieT7G:
initGraphics(640, 480);
_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
break;
}
// Create debugger. It requires GFX to be initialized
_debugger = new Debugger(this);
_script->setDebugger(_debugger);
// Create the graphics manager
_graphicsMan = new GraphicsMan(this);
// Create the resource and cursor managers and the video player
// Prepare the font too
switch (_gameDescription->version) {
case kGroovieT7G:
if (getPlatform() == Common::kPlatformMacintosh) {
_macResFork = new Common::MacResManager();
if (!_macResFork->open(_gameDescription->desc.filesDescriptions[0].fileName))
error("Could not open %s as a resource fork", _gameDescription->desc.filesDescriptions[0].fileName);
// The Macintosh release used system fonts. We use GUI fonts.
_font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
} else {
Common::File fontfile;
if (!fontfile.open("sphinx.fnt")) {
error("Couldn't open sphinx.fnt");
return Common::kNoGameDataFoundError;
} else if (!_sphinxFont.load(fontfile)) {
error("Error loading sphinx.fnt");
return Common::kUnknownError;
}
fontfile.close();
_font = &_sphinxFont;
}
_resMan = new ResMan_t7g(_macResFork);
_grvCursorMan = new GrvCursorMan_t7g(_system, _macResFork);
_videoPlayer = new VDXPlayer(this);
break;
case kGroovieV2:
_resMan = new ResMan_v2();
_grvCursorMan = new GrvCursorMan_v2(_system);
#ifdef ENABLE_GROOVIE2
_videoPlayer = new ROQPlayer(this);
#endif
break;
}
// Detect ScummVM Music Enhancement Project presence (T7G only)
if (Common::File::exists("gu16.ogg") && _gameDescription->version == kGroovieT7G) {
// Load player for external files
_musicPlayer = new MusicPlayerIOS(this);
} else {
// Create the music player
switch (getPlatform()) {
case Common::kPlatformMacintosh:
if (_gameDescription->version == kGroovieT7G)
_musicPlayer = new MusicPlayerMac_t7g(this);
else
_musicPlayer = new MusicPlayerMac_v2(this);
break;
case Common::kPlatformIOS:
_musicPlayer = new MusicPlayerIOS(this);
break;
default:
_musicPlayer = new MusicPlayerXMI(this, _gameDescription->version == kGroovieT7G ? "fat" : "sample");
break;
}
}
// Load volume levels
syncSoundSettings();
// Get the name of the main script
Common::String filename = _gameDescription->desc.filesDescriptions[0].fileName;
if (_gameDescription->version == kGroovieT7G) {
// Run The 7th Guest's demo if requested
if (ConfMan.hasKey("demo_mode") && ConfMan.getBool("demo_mode"))
filename = "demo.grv";
else if (getPlatform() == Common::kPlatformMacintosh)
filename = "script.grv"; // Stored inside the executable's resource fork
} else if (_gameDescription->version == kGroovieV2) {
// Open the disk index
Common::File disk;
if (!disk.open(filename)) {
error("Couldn't open %s", filename.c_str());
return Common::kNoGameDataFoundError;
}
// Search the entry
bool found = false;
int index = 0;
while (!found && !disk.eos()) {
Common::String line = disk.readLine();
if (line.hasPrefix("title: ")) {
// A new entry
index++;
} else if (line.hasPrefix("boot: ") && index == _gameDescription->indexEntry) {
// It's the boot of the entry we're looking for,
// get the script filename
filename = line.c_str() + 6;
found = true;
}
}
// Couldn't find the entry
if (!found) {
error("Couldn't find entry %d in %s", _gameDescription->indexEntry, filename.c_str());
return Common::kUnknownError;
}
}
// Check the script file extension
if (!filename.hasSuffix(".grv")) {
error("%s isn't a valid script filename", filename.c_str());
return Common::kUnknownError;
}
// Load the script
if (!_script->loadScript(filename)) {
error("Couldn't load the script file %s", filename.c_str());
return Common::kUnknownError;
}
// Should I load a saved game?
if (ConfMan.hasKey("save_slot")) {
// Get the requested slot
int slot = ConfMan.getInt("save_slot");
_script->directGameLoad(slot);
}
// Game timer counter
uint16 tmr = 0;
// Check that the game files and the audio tracks aren't together run from
// the same cd
if (getPlatform() != Common::kPlatformIOS) {
checkCD();
_system->getAudioCDManager()->open();
}
while (!shouldQuit()) {
// Give the debugger a chance to act
_debugger->onFrame();
// Handle input
Common::Event ev;
while (_eventMan->pollEvent(ev)) {
switch (ev.type) {
case Common::EVENT_KEYDOWN:
// CTRL-D: Attach the debugger
if ((ev.kbd.flags & Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d)
_debugger->attach();
// Send the event to the scripts
_script->setKbdChar(ev.kbd.ascii);
// Continue the script execution to handle the key
_waitingForInput = false;
break;
case Common::EVENT_MAINMENU:
// Closing the GMM
case Common::EVENT_MOUSEMOVE:
// Continue the script execution, the mouse pointer
// may fall inside a different hotspot now
_waitingForInput = false;
break;
case Common::EVENT_LBUTTONDOWN:
// Send the event to the scripts
_script->setMouseClick(1);
// Continue the script execution to handle
// the click
_waitingForInput = false;
break;
case Common::EVENT_RBUTTONDOWN:
// Send the event to the scripts (to skip the video)
_script->setMouseClick(2);
break;
case Common::EVENT_QUIT:
quitGame();
break;
default:
break;
}
}
// The event loop may have triggered the quit status. In this case,
// stop the execution.
if (shouldQuit()) {
continue;
}
if (_waitingForInput) {
// Still waiting for input, just update the mouse, game timer and then wait a bit more
_grvCursorMan->animate();
_system->updateScreen();
tmr++;
// Wait a little bit between increments. While mouse is moving, this triggers
// only negligably slower.
if (tmr > 4) {
_script->timerTick();
tmr = 0;
}
_system->delayMillis(50);
} else if (_graphicsMan->isFading()) {
// We're waiting for a fading to end, let the CPU rest
// for a while and continue
_system->delayMillis(30);
} else {
// Everything's fine, execute another script step
_script->step();
}
// Update the screen if required
_graphicsMan->update();
}
return Common::kNoError;
}
Common::Platform GroovieEngine::getPlatform() const {
return _gameDescription->desc.platform;
}
bool GroovieEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsRTL) ||
(f == kSupportsLoadingDuringRuntime);
}
void GroovieEngine::syncSoundSettings() {
Engine::syncSoundSettings();
bool mute = ConfMan.getBool("mute");
// Set the music volume
_musicPlayer->setUserVolume(mute ? 0 : ConfMan.getInt("music_volume"));
// Videos just contain one digital audio track, which can be used for
// both SFX or Speech, but the engine doesn't know what they contain, so
// we have to use just one volume setting for videos.
// We use "speech" because most users will want to change the videos
// volume when they can't hear the speech because of the music.
_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType,
mute ? 0 : ConfMan.getInt("speech_volume"));
}
bool GroovieEngine::canLoadGameStateCurrently() {
// TODO: verify the engine has been initialized
return true;
}
Common::Error GroovieEngine::loadGameState(int slot) {
_script->directGameLoad(slot);
// TODO: Use specific error codes
return Common::kNoError;
}
void GroovieEngine::waitForInput() {
_waitingForInput = true;
}
} // End of namespace Groovie