scummvm/engines/ags/engine/main/engine.cpp
2021-03-06 15:11:38 -08:00

1379 lines
45 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.
*
*/
//
// Engine initialization
//
#include "ags/shared/core/platform.h"
#include "ags/globals.h"
#include "ags/engine/main/mainheader.h"
#include "ags/engine/ac/asset_helper.h"
#include "ags/shared/ac/common.h"
#include "ags/engine/ac/character.h"
#include "ags/engine/ac/characterextras.h"
#include "ags/shared/ac/characterinfo.h"
#include "ags/engine/ac/draw.h"
#include "ags/engine/ac/game.h"
#include "ags/engine/ac/gamesetup.h"
#include "ags/shared/ac/gamesetupstruct.h"
#include "ags/engine/ac/global_character.h"
#include "ags/engine/ac/global_game.h"
#include "ags/engine/ac/gui.h"
#include "ags/engine/ac/lipsync.h"
#include "ags/engine/ac/objectcache.h"
#include "ags/engine/ac/path_helper.h"
#include "ags/engine/ac/sys_events.h"
#include "ags/engine/ac/roomstatus.h"
#include "ags/engine/ac/speech.h"
#include "ags/shared/ac/spritecache.h"
#include "ags/engine/ac/translation.h"
#include "ags/engine/ac/viewframe.h"
#include "ags/engine/ac/dynobj/scriptobject.h"
#include "ags/engine/ac/dynobj/scriptsystem.h"
#include "ags/shared/core/assetmanager.h"
#include "ags/engine/debugging/debug_log.h"
#include "ags/engine/debugging/debugger.h"
#include "ags/shared/debugging/out.h"
#include "ags/shared/font/agsfontrenderer.h"
#include "ags/shared/font/fonts.h"
#include "ags/engine/gfx/graphicsdriver.h"
#include "ags/engine/gfx/gfxdriverfactory.h"
#include "ags/engine/gfx/ddb.h"
#include "ags/engine/main/config.h"
#include "ags/engine/main/game_file.h"
#include "ags/engine/main/game_start.h"
#include "ags/engine/main/engine.h"
#include "ags/engine/main/engine_setup.h"
#include "ags/engine/main/graphics_mode.h"
#include "ags/engine/main/main.h"
#include "ags/engine/main/main_allegro.h"
#include "ags/engine/media/audio/audio_system.h"
#include "ags/engine/platform/util/pe.h"
#include "ags/shared/gfx/image.h"
#include "ags/shared/util/directory.h"
#include "ags/shared/util/error.h"
#include "ags/shared/util/misc.h"
#include "ags/shared/util/path.h"
#include "ags/globals.h"
#include "ags/ags.h"
#include "common/fs.h"
namespace AGS3 {
using namespace AGS::Shared;
using namespace AGS::Engine;
extern char check_dynamic_sprites_at_exit;
extern int our_eip;
extern int proper_exit;
extern char pexbuf[STD_BUFFER_SIZE];
extern int displayed_room;
extern int eip_guinum;
extern int eip_guiobj;
extern SpeechLipSyncLine *splipsync;
extern int numLipLines, curLipLine, curLipLinePhoneme;
extern IGraphicsDriver *gfxDriver;
extern Bitmap **actsps;
extern color palette[256];
extern CharacterExtras *charextra;
extern CharacterInfo *playerchar;
extern Bitmap **guibg;
extern IDriverDependantBitmap **guibgbmp;
ResourcePaths ResPaths;
t_engine_pre_init_callback engine_pre_init_callback = nullptr;
#define ALLEGRO_KEYBOARD_HANDLER
bool engine_init_allegro() {
Debug::Printf(kDbgMsg_Info, "Initializing allegro");
our_eip = -199;
// Initialize allegro
set_uformat(U_ASCII);
if (install_allegro()) {
const char *al_err = get_allegro_error();
const char *user_hint = platform->GetAllegroFailUserHint();
platform->DisplayAlert("Unable to initialize Allegro system driver.\n%s\n\n%s",
al_err[0] ? al_err : "Allegro library provided no further information on the problem.",
user_hint);
return false;
}
return true;
}
void engine_setup_allegro() {
// Setup allegro using constructed config string
const char *al_config_data = "[mouse]\n"
"mouse_accel_factor = 0\n";
override_config_data(al_config_data, ustrsize(al_config_data));
}
void engine_setup_window() {
Debug::Printf(kDbgMsg_Info, "Setting up window");
our_eip = -198;
//set_window_title("Adventure Game Studio");
our_eip = -197;
platform->SetGameWindowIcon();
}
// Starts up setup application, if capable.
// Returns TRUE if should continue running the game, otherwise FALSE.
bool engine_run_setup(const String &exe_path, ConfigTree &cfg, int &app_res) {
app_res = EXIT_NORMAL;
#if AGS_PLATFORM_OS_WINDOWS
{
String cfg_file = find_user_cfg_file();
if (cfg_file.IsEmpty()) {
app_res = EXIT_ERROR;
return false;
}
Debug::Printf(kDbgMsg_Info, "Running Setup");
ConfigTree cfg_out;
SetupReturnValue res = platform->RunSetup(cfg, cfg_out);
if (res != kSetup_Cancel) {
if (!IniUtil::Merge(cfg_file, cfg_out)) {
platform->DisplayAlert("Unable to write to the configuration file (error code 0x%08X).\n%s",
platform->GetLastSystemError(), platform->GetDiskWriteAccessTroubleshootingText());
}
}
if (res != kSetup_RunGame)
return false;
// TODO: investigate if the full program restart may (should) be avoided
// Just re-reading the config file seems to cause a caching
// problem on Win9x, so let's restart the process.
allegro_exit();
char quotedpath[MAX_PATH];
snprintf(quotedpath, MAX_PATH, "\"%s\"", exe_path.GetCStr());
_spawnl(_P_OVERLAY, exe_path, quotedpath, NULL);
}
#endif
return true;
}
void engine_force_window() {
// Force to run in a window, override the config file
// TODO: actually overwrite config tree instead
if (_G(force_window) == 1) {
_GP(usetup).Screen.DisplayMode.Windowed = true;
_GP(usetup).Screen.DisplayMode.ScreenSize.SizeDef = kScreenDef_ByGameScaling;
} else if (_G(force_window) == 2) {
_GP(usetup).Screen.DisplayMode.Windowed = false;
_GP(usetup).Screen.DisplayMode.ScreenSize.SizeDef = kScreenDef_MaxDisplay;
}
}
String find_game_data_in_directory(const String &path) {
Common::String test_file;
String first_nonstd_fn;
Common::FSNode folder(path);
Common::FSList files;
if (folder.getChildren(files, Common::FSNode::kListFilesOnly)) {
// Select first found data file; files with standart names (*.ags) have
// higher priority over files with custom names.
for (Common::FSList::iterator it = files.begin(); it != files.end(); ++it) {
test_file = it->getName();
// Add a bit of sanity and do not parse contents of the 10k-files-large
// digital sound libraries.
// NOTE: we could certainly benefit from any kind of flag in file lib
// that would tell us this is the main lib without extra parsing.
if (test_file.hasSuffixIgnoreCase(".vox"))
continue;
// *.ags is a standart cross-platform file pattern for AGS games,
// ac2game.dat is a legacy file name for very old games,
// *.exe is a MS Win executable; it is included to this case because
// users often run AGS ports with Windows versions of games.
bool is_std_name = test_file.hasSuffixIgnoreCase(".ags") ||
test_file.equalsIgnoreCase("ac2game.dat") ||
test_file.hasSuffixIgnoreCase(".exe");
if (is_std_name || first_nonstd_fn.IsEmpty()) {
test_file = it->getName();
if (IsMainGameLibrary(test_file)) {
if (is_std_name) {
return test_file;
} else
first_nonstd_fn = test_file;
}
}
}
}
return first_nonstd_fn;
}
bool search_for_game_data_file(String &filename, String &search_path) {
Debug::Printf("Looking for the game data file");
// 1. From command line argument, treated as a directory
if (!_G(cmdGameDataPath).IsEmpty()) {
// set from cmd arg (do any conversions if needed)
filename = _G(cmdGameDataPath);
if (!filename.IsEmpty() && Path::IsDirectory(filename)) {
search_path = filename;
filename = find_game_data_in_directory(search_path);
}
}
// 2.2. Search in the provided data dir
else if (!_GP(usetup).data_files_dir.IsEmpty()) {
search_path = _GP(usetup).data_files_dir;
filename = find_game_data_in_directory(search_path);
}
// 3. Look in known locations
else {
#ifdef DEPRECATED
// 3.1. Look for attachment in the running executable
//
// this will use argument zero, the executable's name
filename = GetPathFromCmdArg(0);
if (filename.IsEmpty() || !Shared::AssetManager::IsDataFile(filename)) {
// 3.2 Look in current directory
search_path = Directory::GetCurrentDirectory();
filename = find_game_data_in_directory(search_path);
if (filename.IsEmpty()) {
// 3.3 Look in executable's directory (if it's different from current dir)
if (Path::ComparePaths(_G(appDirectory), search_path)) {
search_path = _G(appDirectory);
filename = find_game_data_in_directory(search_path);
}
}
}
#endif
}
// Finally, store game file's absolute path, or report error
if (filename.IsEmpty()) {
Debug::Printf(kDbgMsg_Error, "Game data file could not be found. Search path used: '%s'", search_path.GetCStr());
return false;
}
filename = Path::MakeAbsolutePath(filename);
Debug::Printf(kDbgMsg_Info, "Located game data file: %s", filename.GetCStr());
return true;
}
// Try to initialize main game package found at the given path
bool engine_try_init_gamedata(String gamepak_path) {
// Search for an available game package in the known locations
AssetError err = AssetManager::SetDataFile(gamepak_path);
if (err != kAssetNoError) {
platform->DisplayAlert("ERROR: The game data is missing, is of unsupported format or corrupt.\nFile: '%s'", gamepak_path.GetCStr());
return false;
}
return true;
}
void engine_init_fonts() {
Debug::Printf(kDbgMsg_Info, "Initializing TTF renderer");
init_font_renderer();
}
void engine_init_mouse() {
int res = minstalled();
if (res < 0)
Debug::Printf(kDbgMsg_Info, "Initializing mouse: failed");
else
Debug::Printf(kDbgMsg_Info, "Initializing mouse: number of buttons reported is %d", res);
Mouse::SetSpeed(_GP(usetup).mouse_speed);
}
void engine_locate_speech_pak() {
_GP(play).want_speech = -2;
if (!_GP(usetup).no_speech_pack) {
String speech_file = "speech.vox";
String speech_filepath = find_assetlib(speech_file);
if (!speech_filepath.IsEmpty()) {
Debug::Printf("Initializing speech vox");
if (AssetManager::SetDataFile(speech_filepath) != Shared::kAssetNoError) {
platform->DisplayAlert("Unable to read voice pack, file could be corrupted or of unknown format.\nSpeech voice-over will be disabled.");
AssetManager::SetDataFile(ResPaths.GamePak.Path); // switch back to the main data pack
return;
}
// TODO: why is this read right here??? move this to InitGameState!
Stream *speechsync = AssetManager::OpenAsset("syncdata.dat");
if (speechsync != nullptr) {
// this game has voice lip sync
int lipsync_fmt = speechsync->ReadInt32();
if (lipsync_fmt != 4) {
Debug::Printf(kDbgMsg_Info, "Unknown speech lip sync format (%d).\nLip sync disabled.", lipsync_fmt);
} else {
numLipLines = speechsync->ReadInt32();
splipsync = (SpeechLipSyncLine *)malloc(sizeof(SpeechLipSyncLine) * numLipLines);
for (int ee = 0; ee < numLipLines; ee++) {
splipsync[ee].numPhonemes = speechsync->ReadInt16();
speechsync->Read(splipsync[ee].filename, 14);
splipsync[ee].endtimeoffs = (int32_t *)malloc(splipsync[ee].numPhonemes * sizeof(int32_t));
speechsync->ReadArrayOfInt32(splipsync[ee].endtimeoffs, splipsync[ee].numPhonemes);
splipsync[ee].frame = (short *)malloc(splipsync[ee].numPhonemes * sizeof(short));
speechsync->ReadArrayOfInt16(splipsync[ee].frame, splipsync[ee].numPhonemes);
}
}
delete speechsync;
}
AssetManager::SetDataFile(ResPaths.GamePak.Path); // switch back to the main data pack
Debug::Printf(kDbgMsg_Info, "Voice pack found and initialized.");
_GP(play).want_speech = 1;
} else if (Path::ComparePaths(ResPaths.DataDir, get_voice_install_dir()) != 0) {
// If we have custom voice directory set, we will enable voice-over even if speech.vox does not exist
Debug::Printf(kDbgMsg_Info, "Voice pack was not found, but voice installation directory is defined: enabling voice-over.");
_GP(play).want_speech = 1;
}
ResPaths.SpeechPak.Name = speech_file;
ResPaths.SpeechPak.Path = speech_filepath;
}
}
void engine_locate_audio_pak() {
_GP(play).separate_music_lib = 0;
String music_file = _GP(game).GetAudioVOXName();
String music_filepath = find_assetlib(music_file);
if (!music_filepath.IsEmpty()) {
if (AssetManager::SetDataFile(music_filepath) == kAssetNoError) {
AssetManager::SetDataFile(ResPaths.GamePak.Path);
Debug::Printf(kDbgMsg_Info, "%s found and initialized.", music_file.GetCStr());
_GP(play).separate_music_lib = 1;
ResPaths.AudioPak.Name = music_file;
ResPaths.AudioPak.Path = music_filepath;
} else {
platform->DisplayAlert("Unable to initialize digital audio pack '%s', file could be corrupt or of unsupported format.",
music_file.GetCStr());
}
}
}
void engine_init_keyboard() {
#ifdef ALLEGRO_KEYBOARD_HANDLER
Debug::Printf(kDbgMsg_Info, "Initializing keyboard");
install_keyboard();
#endif
#if AGS_PLATFORM_OS_LINUX
// setlocale(LC_NUMERIC, "C"); // needed in X platform because install keyboard affects locale of printfs
#endif
}
void engine_init_timer() {
Debug::Printf(kDbgMsg_Info, "Install timer");
skipMissedTicks();
}
void engine_init_audio() {
#if !AGS_PLATFORM_SCUMMVM
if (_GP(usetup).audio_backend != 0) {
Debug::Printf("Initializing audio");
audio_core_init(); // audio core system
}
#endif
our_eip = -181;
if (_GP(usetup).audio_backend == 0) {
// all audio is disabled
// and the voice mode should not go to Voice Only
_GP(play).want_speech = -2;
_GP(play).separate_music_lib = 0;
}
}
void engine_init_debug() {
if ((_G(debug_flags) & (~DBG_DEBUGMODE)) > 0) {
platform->DisplayAlert("Engine debugging enabled.\n"
"\nNOTE: You have selected to enable one or more engine debugging options.\n"
"These options cause many parts of the game to behave abnormally, and you\n"
"may not see the game as you are used to it. The point is to test whether\n"
"the engine passes a point where it is crashing on you normally.\n"
"[Debug flags enabled: 0x%02X]", _G(debug_flags));
}
}
void atexit_handler() {
if (proper_exit == 0) {
platform->DisplayAlert("Error: the program has exited without requesting it.\n"
"Program pointer: %+03d (write this number down), ACI version %s\n"
"If you see a list of numbers above, please write them down and contact\n"
"developers. Otherwise, note down any other information displayed.",
our_eip, _G(EngineVersion).LongString.GetCStr());
}
}
void engine_init_exit_handler() {
Debug::Printf(kDbgMsg_Info, "Install exit handler");
atexit(atexit_handler);
}
void engine_init_rand() {
_GP(play).randseed = g_system->getMillis();
::AGS::g_vm->setRandomNumberSeed(_GP(play).randseed);
}
void engine_init_pathfinder() {
init_pathfinder(loaded_game_file_version);
}
void engine_pre_init_gfx() {
//Debug::Printf("Initialize gfx");
//platform->InitialiseAbufAtStartup();
}
int engine_load_game_data() {
Debug::Printf("Load game data");
our_eip = -17;
HError err = load_game_file();
if (!err) {
proper_exit = 1;
platform->FinishedUsingGraphicsMode();
display_game_file_error(err);
return EXIT_ERROR;
}
return 0;
}
int engine_check_register_game() {
if (_G(justRegisterGame)) {
platform->RegisterGameWithGameExplorer();
proper_exit = 1;
return EXIT_NORMAL;
}
if (_G(justUnRegisterGame)) {
platform->UnRegisterGameWithGameExplorer();
proper_exit = 1;
return EXIT_NORMAL;
}
return 0;
}
void engine_init_title() {
our_eip = -91;
::AGS::g_vm->set_window_title(_GP(game).gamename);
Debug::Printf(kDbgMsg_Info, "Game title: '%s'", _GP(game).gamename);
}
void engine_init_directories() {
Debug::Printf(kDbgMsg_Info, "Data directory: %s", _GP(usetup).data_files_dir.GetCStr());
if (!_GP(usetup).install_dir.IsEmpty())
Debug::Printf(kDbgMsg_Info, "Optional install directory: %s", _GP(usetup).install_dir.GetCStr());
if (!_GP(usetup).install_audio_dir.IsEmpty())
Debug::Printf(kDbgMsg_Info, "Optional audio directory: %s", _GP(usetup).install_audio_dir.GetCStr());
if (!_GP(usetup).install_voice_dir.IsEmpty())
Debug::Printf(kDbgMsg_Info, "Optional voice-over directory: %s", _GP(usetup).install_voice_dir.GetCStr());
if (!_GP(usetup).user_data_dir.IsEmpty())
Debug::Printf(kDbgMsg_Info, "User data directory: %s", _GP(usetup).user_data_dir.GetCStr());
if (!_GP(usetup).shared_data_dir.IsEmpty())
Debug::Printf(kDbgMsg_Info, "Shared data directory: %s", _GP(usetup).shared_data_dir.GetCStr());
ResPaths.DataDir = _GP(usetup).data_files_dir;
ResPaths.GamePak.Path = _GP(usetup).main_data_filepath;
ResPaths.GamePak.Name = Shared::Path::get_filename(_GP(usetup).main_data_filepath);
set_install_dir(_GP(usetup).install_dir, _GP(usetup).install_audio_dir, _GP(usetup).install_voice_dir);
if (!_GP(usetup).install_dir.IsEmpty()) {
// running in debugger: don't redirect to the game exe folder (_Debug)
// TODO: find out why we need to do this (and do we?)
ResPaths.DataDir = ".";
}
// if end-user specified custom save path, use it
bool res = false;
if (!_GP(usetup).user_data_dir.IsEmpty()) {
res = SetCustomSaveParent(_GP(usetup).user_data_dir);
if (!res) {
Debug::Printf(kDbgMsg_Warn, "WARNING: custom user save path failed, using default system paths");
res = false;
}
}
// if there is no custom path, or if custom path failed, use default system path
if (!res) {
char newDirBuffer[MAX_PATH];
sprintf(newDirBuffer, "%s/%s", UserSavedgamesRootToken, _GP(game).saveGameFolderName);
SetSaveGameDirectoryPath(newDirBuffer);
}
}
#if AGS_PLATFORM_OS_ANDROID
extern char android_base_directory[256];
#endif // AGS_PLATFORM_OS_ANDROID
int check_write_access() {
#if AGS_PLATFORM_SCUMMVM
return true;
#else
if (platform->GetDiskFreeSpaceMB() < 2)
return 0;
our_eip = -1895;
// The Save Game Dir is the only place that we should write to
String svg_dir = get_save_game_directory();
String tempPath = String::FromFormat("%s""tmptest.tmp", svg_dir.GetCStr());
Stream *temp_s = Shared::File::CreateFile(tempPath);
if (!temp_s)
// TODO: move this somewhere else (Android platform driver init?)
#if AGS_PLATFORM_OS_ANDROID
{
// put_backslash(android_base_directory);
tempPath.Format("%s""tmptest.tmp", android_base_directory);
temp_s = Shared::File::CreateFile(tempPath);
if (temp_s == NULL) return 0;
else SetCustomSaveParent(android_base_directory);
}
#else
return 0;
#endif // AGS_PLATFORM_OS_ANDROID
our_eip = -1896;
temp_s->Write("just to test the drive free space", 30);
delete temp_s;
our_eip = -1897;
if (::remove(tempPath))
return 0;
return 1;
#endif
}
int engine_check_disk_space() {
Debug::Printf(kDbgMsg_Info, "Checking for disk space");
if (check_write_access() == 0) {
platform->DisplayAlert("Unable to write in the savegame directory.\n%s", platform->GetDiskWriteAccessTroubleshootingText());
proper_exit = 1;
return EXIT_ERROR;
}
return 0;
}
int engine_check_font_was_loaded() {
if (!font_first_renderer_loaded()) {
platform->DisplayAlert("No game fonts found. At least one font is required to run the game.");
proper_exit = 1;
return EXIT_ERROR;
}
return 0;
}
// Do the preload graphic if available
void show_preload() {
color temppal[256];
Bitmap *splashsc = BitmapHelper::CreateRawBitmapOwner(load_pcx("preload.pcx", temppal));
if (splashsc != nullptr) {
Debug::Printf("Displaying preload image");
if (splashsc->GetColorDepth() == 8)
set_palette_range(temppal, 0, 255, 0);
if (gfxDriver->UsesMemoryBackBuffer())
gfxDriver->GetMemoryBackBuffer()->Clear();
const Rect &view = _GP(play).GetMainViewport();
Bitmap *tsc = BitmapHelper::CreateBitmapCopy(splashsc, _GP(game).GetColorDepth());
if (!gfxDriver->HasAcceleratedTransform() && view.GetSize() != tsc->GetSize()) {
Bitmap *stretched = new Bitmap(view.GetWidth(), view.GetHeight(), tsc->GetColorDepth());
stretched->StretchBlt(tsc, RectWH(0, 0, view.GetWidth(), view.GetHeight()));
delete tsc;
tsc = stretched;
}
IDriverDependantBitmap *ddb = gfxDriver->CreateDDBFromBitmap(tsc, false, true);
ddb->SetStretch(view.GetWidth(), view.GetHeight());
gfxDriver->ClearDrawLists();
gfxDriver->DrawSprite(0, 0, ddb);
render_to_screen();
gfxDriver->DestroyDDB(ddb);
delete splashsc;
delete tsc;
platform->Delay(500);
}
}
int engine_init_sprites() {
Debug::Printf(kDbgMsg_Info, "Initialize sprites");
HError err = _GP(spriteset).InitFile(SpriteCache::DefaultSpriteFileName, SpriteCache::DefaultSpriteIndexName);
if (!err) {
platform->FinishedUsingGraphicsMode();
allegro_exit();
proper_exit = 1;
platform->DisplayAlert("Could not load sprite set file %s\n%s",
SpriteCache::DefaultSpriteFileName,
err->FullMessage().GetCStr());
return EXIT_ERROR;
}
return 0;
}
void engine_init_game_settings() {
our_eip = -7;
Debug::Printf("Initialize game settings");
int ee;
for (ee = 0; ee < MAX_ROOM_OBJECTS + _GP(game).numcharacters; ee++)
actsps[ee] = nullptr;
for (ee = 0; ee < 256; ee++) {
if (_GP(game).paluses[ee] != PAL_BACKGROUND)
palette[ee] = _GP(game).defpal[ee];
}
for (ee = 0; ee < _GP(game).numcursors; ee++) {
// The cursor graphics are assigned to _G(mousecurs)[] and so cannot
// be removed from memory
if (_GP(game).mcurs[ee].pic >= 0)
_GP(spriteset).Precache(_GP(game).mcurs[ee].pic);
// just in case they typed an invalid view number in the editor
if (_GP(game).mcurs[ee].view >= _GP(game).numviews)
_GP(game).mcurs[ee].view = -1;
if (_GP(game).mcurs[ee].view >= 0)
precache_view(_GP(game).mcurs[ee].view);
}
// may as well preload the character gfx
if (playerchar->view >= 0)
precache_view(playerchar->view);
for (ee = 0; ee < MAX_ROOM_OBJECTS; ee++)
_G(objcache)[ee].image = nullptr;
/* dummygui.guiId = -1;
dummyguicontrol.guin = -1;
dummyguicontrol.objn = -1;*/
our_eip = -6;
// _GP(game).chars[0].talkview=4;
//init_language_text(_GP(game).langcodes[0]);
for (ee = 0; ee < MAX_ROOM_OBJECTS; ee++) {
_G(scrObj)[ee].id = ee;
// 64 bit: Using the id instead
// _G(scrObj)[ee].obj = NULL;
}
for (ee = 0; ee < _GP(game).numcharacters; ee++) {
memset(&_GP(game).chars[ee].inv[0], 0, MAX_INV * sizeof(short));
_GP(game).chars[ee].activeinv = -1;
_GP(game).chars[ee].following = -1;
_GP(game).chars[ee].followinfo = 97 | (10 << 8);
_GP(game).chars[ee].idletime = 20; // can be overridden later with SetIdle or summink
_GP(game).chars[ee].idleleft = _GP(game).chars[ee].idletime;
_GP(game).chars[ee].transparency = 0;
_GP(game).chars[ee].baseline = -1;
_GP(game).chars[ee].walkwaitcounter = 0;
_GP(game).chars[ee].z = 0;
charextra[ee].xwas = INVALID_X;
charextra[ee].zoom = 100;
if (_GP(game).chars[ee].view >= 0) {
// set initial loop to 0
_GP(game).chars[ee].loop = 0;
// or to 1 if they don't have up/down frames
if (_G(views)[_GP(game).chars[ee].view].loops[0].numFrames < 1)
_GP(game).chars[ee].loop = 1;
}
charextra[ee].process_idle_this_time = 0;
charextra[ee].invorder_count = 0;
charextra[ee].slow_move_counter = 0;
charextra[ee].animwait = 0;
}
// multiply up gui positions
guibg = (Bitmap **)malloc(sizeof(Bitmap *) * _GP(game).numgui);
guibgbmp = (IDriverDependantBitmap **)malloc(sizeof(IDriverDependantBitmap *) * _GP(game).numgui);
for (ee = 0; ee < _GP(game).numgui; ee++) {
guibg[ee] = nullptr;
guibgbmp[ee] = nullptr;
}
our_eip = -5;
for (ee = 0; ee < _GP(game).numinvitems; ee++) {
if (_GP(game).invinfo[ee].flags & IFLG_STARTWITH) playerchar->inv[ee] = 1;
else playerchar->inv[ee] = 0;
}
_GP(play).score = 0;
_GP(play).sierra_inv_color = 7;
// copy the value set by the editor
if (_GP(game).options[OPT_GLOBALTALKANIMSPD] >= 0) {
_GP(play).talkanim_speed = _GP(game).options[OPT_GLOBALTALKANIMSPD];
_GP(game).options[OPT_GLOBALTALKANIMSPD] = 1;
} else {
_GP(play).talkanim_speed = -_GP(game).options[OPT_GLOBALTALKANIMSPD] - 1;
_GP(game).options[OPT_GLOBALTALKANIMSPD] = 0;
}
_GP(play).inv_item_wid = 40;
_GP(play).inv_item_hit = 22;
_GP(play).messagetime = -1;
_GP(play).disabled_user_interface = 0;
_GP(play).gscript_timer = -1;
_GP(play).debug_mode = _GP(game).options[OPT_DEBUGMODE];
_GP(play).inv_top = 0;
_GP(play).inv_numdisp = 0;
_GP(play).obsolete_inv_numorder = 0;
_GP(play).text_speed = 15;
_GP(play).text_min_display_time_ms = 1000;
_GP(play).ignore_user_input_after_text_timeout_ms = 500;
_GP(play).ClearIgnoreInput();
_GP(play).lipsync_speed = 15;
_GP(play).close_mouth_speech_time = 10;
_GP(play).disable_antialiasing = 0;
_GP(play).rtint_enabled = false;
_GP(play).rtint_level = 0;
_GP(play).rtint_light = 0;
_GP(play).text_speed_modifier = 0;
_GP(play).text_align = kHAlignLeft;
// Make the default alignment to the right with right-to-left text
if (_GP(game).options[OPT_RIGHTLEFTWRITE])
_GP(play).text_align = kHAlignRight;
_GP(play).speech_bubble_width = get_fixed_pixel_size(100);
_GP(play).bg_frame = 0;
_GP(play).bg_frame_locked = 0;
_GP(play).bg_anim_delay = 0;
_GP(play).anim_background_speed = 0;
_GP(play).silent_midi = 0;
_GP(play).current_music_repeating = 0;
_GP(play).skip_until_char_stops = -1;
_GP(play).get_loc_name_last_time = -1;
_GP(play).get_loc_name_save_cursor = -1;
_GP(play).restore_cursor_mode_to = -1;
_GP(play).restore_cursor_image_to = -1;
_GP(play).ground_level_areas_disabled = 0;
_GP(play).next_screen_transition = -1;
_GP(play).temporarily_turned_off_character = -1;
_GP(play).inv_backwards_compatibility = 0;
_GP(play).gamma_adjustment = 100;
_GP(play).do_once_tokens.resize(0);
_GP(play).music_queue_size = 0;
_GP(play).shakesc_length = 0;
_GP(play).wait_counter = 0;
_GP(play).key_skip_wait = SKIP_NONE;
_GP(play).cur_music_number = -1;
_GP(play).music_repeat = 1;
_GP(play).music_master_volume = 100 + LegacyMusicMasterVolumeAdjustment;
_GP(play).digital_master_volume = 100;
_GP(play).screen_flipped = 0;
_GP(play).cant_skip_speech = user_to_internal_skip_speech((SkipSpeechStyle)_GP(game).options[OPT_NOSKIPTEXT]);
_GP(play).sound_volume = 255;
_GP(play).speech_volume = 255;
_GP(play).normal_font = 0;
_GP(play).speech_font = 1;
_GP(play).speech_text_shadow = 16;
_GP(play).screen_tint = -1;
_GP(play).bad_parsed_word[0] = 0;
_GP(play).swap_portrait_side = 0;
_GP(play).swap_portrait_lastchar = -1;
_GP(play).swap_portrait_lastlastchar = -1;
_GP(play).in_conversation = 0;
_GP(play).skip_display = 3;
_GP(play).no_multiloop_repeat = 0;
_GP(play).in_cutscene = 0;
_GP(play).fast_forward = 0;
_GP(play).totalscore = _GP(game).totalscore;
_GP(play).roomscript_finished = 0;
_GP(play).no_textbg_when_voice = 0;
_GP(play).max_dialogoption_width = get_fixed_pixel_size(180);
_GP(play).no_hicolor_fadein = 0;
_GP(play).bgspeech_game_speed = 0;
_GP(play).bgspeech_stay_on_display = 0;
_GP(play).unfactor_speech_from_textlength = 0;
_GP(play).mp3_loop_before_end = 70;
_GP(play).speech_music_drop = 60;
_GP(play).room_changes = 0;
_GP(play).check_interaction_only = 0;
_GP(play).replay_hotkey_unused = -1; // StartRecording: not supported.
_GP(play).dialog_options_x = 0;
_GP(play).dialog_options_y = 0;
_GP(play).min_dialogoption_width = 0;
_GP(play).disable_dialog_parser = 0;
_GP(play).ambient_sounds_persist = 0;
_GP(play).screen_is_faded_out = 0;
_GP(play).player_on_region = 0;
_GP(play).top_bar_backcolor = 8;
_GP(play).top_bar_textcolor = 16;
_GP(play).top_bar_bordercolor = 8;
_GP(play).top_bar_borderwidth = 1;
_GP(play).top_bar_ypos = 25;
_GP(play).top_bar_font = -1;
_GP(play).screenshot_width = 160;
_GP(play).screenshot_height = 100;
_GP(play).speech_text_align = kHAlignCenter;
_GP(play).auto_use_walkto_points = 1;
_GP(play).inventory_greys_out = 0;
_GP(play).skip_speech_specific_key = 0;
_GP(play).abort_key = 324; // Alt+X
_GP(play).fade_to_red = 0;
_GP(play).fade_to_green = 0;
_GP(play).fade_to_blue = 0;
_GP(play).show_single_dialog_option = 0;
_GP(play).keep_screen_during_instant_transition = 0;
_GP(play).read_dialog_option_colour = -1;
_GP(play).speech_portrait_placement = 0;
_GP(play).speech_portrait_x = 0;
_GP(play).speech_portrait_y = 0;
_GP(play).speech_display_post_time_ms = 0;
_GP(play).dialog_options_highlight_color = DIALOG_OPTIONS_HIGHLIGHT_COLOR_DEFAULT;
_GP(play).speech_has_voice = false;
_GP(play).speech_voice_blocking = false;
_GP(play).speech_in_post_state = false;
_GP(play).narrator_speech = _GP(game).playercharacter;
_GP(play).crossfading_out_channel = 0;
_GP(play).speech_textwindow_gui = _GP(game).options[OPT_TWCUSTOM];
if (_GP(play).speech_textwindow_gui == 0)
_GP(play).speech_textwindow_gui = -1;
strcpy(_GP(play).game_name, _GP(game).gamename);
_GP(play).lastParserEntry[0] = 0;
_GP(play).follow_change_room_timer = 150;
for (ee = 0; ee < MAX_ROOM_BGFRAMES; ee++)
_GP(play).raw_modified[ee] = 0;
_GP(play).game_speed_modifier = 0;
if (_G(debug_flags) & DBG_DEBUGMODE)
_GP(play).debug_mode = 1;
gui_disabled_style = convert_gui_disabled_style(_GP(game).options[OPT_DISABLEOFF]);
_GP(play).shake_screen_yoff = 0;
memset(&_GP(play).walkable_areas_on[0], 1, MAX_WALK_AREAS + 1);
memset(&_GP(play).script_timers[0], 0, MAX_TIMERS * sizeof(int32_t));
memset(&_GP(play).default_audio_type_volumes[0], -1, MAX_AUDIO_TYPES * sizeof(int32_t));
// reset graphical script vars (they're still used by some games)
for (ee = 0; ee < MAXGLOBALVARS; ee++)
_GP(play).globalvars[ee] = 0;
for (ee = 0; ee < MAXGLOBALSTRINGS; ee++)
_GP(play).globalstrings[ee][0] = 0;
if (!_GP(usetup).translation.IsEmpty())
init_translation(_GP(usetup).translation, "", true);
update_invorder();
displayed_room = -10;
_G(currentcursor) = 0;
our_eip = -4;
_G(mousey) = 100; // stop icon bar popping up
// We use same variable to read config and be used at runtime for now,
// so update it here with regards to game design option
_GP(usetup).RenderAtScreenRes =
(_GP(game).options[OPT_RENDERATSCREENRES] == kRenderAtScreenRes_UserDefined && _GP(usetup).RenderAtScreenRes) ||
_GP(game).options[OPT_RENDERATSCREENRES] == kRenderAtScreenRes_Enabled;
}
void engine_setup_scsystem_auxiliary() {
// ScriptSystem::aci_version is only 10 chars long
strncpy(_GP(scsystem).aci_version, _G(EngineVersion).LongString, 10);
if (_GP(usetup).override_script_os >= 0) {
_GP(scsystem).os = _GP(usetup).override_script_os;
} else {
_GP(scsystem).os = platform->GetSystemOSID();
}
}
void engine_prepare_to_start_game() {
Debug::Printf("Prepare to start game");
engine_setup_scsystem_auxiliary();
#if AGS_PLATFORM_SCUMMVM
clear_sound_cache();
#endif
#if AGS_PLATFORM_OS_ANDROID
if (psp_load_latest_savegame)
selectLatestSavegame();
#endif
}
// TODO: move to test unit
Bitmap *test_allegro_bitmap;
IDriverDependantBitmap *test_allegro_ddb;
void allegro_bitmap_test_init() {
test_allegro_bitmap = nullptr;
// Switched the test off for now
//test_allegro_bitmap = AllegroBitmap::CreateBitmap(320,200,32);
}
// Only allow searching around for game data on desktop systems;
// otherwise use explicit argument either from program wrapper, command-line
// or read from default config.
#if AGS_PLATFORM_OS_WINDOWS || AGS_PLATFORM_OS_LINUX || AGS_PLATFORM_OS_MACOS
#define AGS_SEARCH_FOR_GAME_ON_LAUNCH
#endif
// Define location of the game data either using direct settings or searching
// for the available resource packs in common locations
HError define_gamedata_location_checkall(const String &exe_path) {
// First try if they provided a startup option
if (!_G(cmdGameDataPath).IsEmpty()) {
// If not a valid path - bail out
if (!Path::IsFileOrDir(_G(cmdGameDataPath)))
return new Error(String::FromFormat("Defined game location is not a valid path.\nPath: '%s'", _G(cmdGameDataPath).GetCStr()));
// Switch working dir to this path to be able to look for config and other assets there
Directory::SetCurrentDirectory(Path::GetDirectoryPath(_G(cmdGameDataPath)));
// If it's a file, then keep it and proceed
if (Path::IsFile(_G(cmdGameDataPath))) {
_GP(usetup).main_data_filepath = _G(cmdGameDataPath);
return HError::None();
}
}
// Read game data location from the default config file.
// This is an optional setting that may instruct which game file to use as a primary asset library.
ConfigTree cfg;
String def_cfg_file = find_default_cfg_file(exe_path);
IniUtil::Read(def_cfg_file, cfg);
read_game_data_location(cfg);
if (!_GP(usetup).main_data_filename.IsEmpty())
return HError::None();
#if defined (AGS_SEARCH_FOR_GAME_ON_LAUNCH)
// No direct filepath provided, search in common locations.
String path, search_path;
if (search_for_game_data_file(path, search_path)) {
_GP(usetup).main_data_filepath = path;
return HError::None();
}
return new Error("Engine was not able to find any compatible game data.",
search_path.IsEmpty() ? String() : String::FromFormat("Searched in: %s", search_path.GetCStr()));
#else
return new Error("The game location was not defined by startup settings.");
#endif
}
// Define location of the game data
bool define_gamedata_location(const String &exe_path) {
HError err = define_gamedata_location_checkall(exe_path);
if (!err) {
platform->DisplayAlert("ERROR: Unable to determine game data.\n%s", err->FullMessage().GetCStr());
main_print_help();
return false;
}
// On success: set all the necessary path and filename settings,
// derive missing ones from available.
if (_GP(usetup).main_data_filename.IsEmpty()) {
_GP(usetup).main_data_filename = Shared::Path::get_filename(_GP(usetup).main_data_filepath);
} else if (_GP(usetup).main_data_filepath.IsEmpty()) {
if (_GP(usetup).data_files_dir.IsEmpty() || !is_relative_filename(_GP(usetup).main_data_filename))
_GP(usetup).main_data_filepath = _GP(usetup).main_data_filename;
else
_GP(usetup).main_data_filepath = Path::ConcatPaths(_GP(usetup).data_files_dir, _GP(usetup).main_data_filename);
}
if (_GP(usetup).data_files_dir.IsEmpty())
_GP(usetup).data_files_dir = Path::GetDirectoryPath(_GP(usetup).main_data_filepath);
return true;
}
// Find and preload main game data
bool engine_init_gamedata(const String &exe_path) {
Debug::Printf(kDbgMsg_Info, "Initializing game data");
if (!define_gamedata_location(exe_path))
return false;
if (!engine_try_init_gamedata(_GP(usetup).main_data_filepath))
return false;
// Pre-load game name and savegame folder names from data file
// TODO: research if that is possible to avoid this step and just
// read the full head game data at this point. This might require
// further changes of the order of initialization.
HError err = preload_game_data();
if (!err) {
display_game_file_error(err);
return false;
}
return true;
}
void engine_read_config(const String &exe_path, ConfigTree &cfg) {
// Read default configuration file
String def_cfg_file = find_default_cfg_file(exe_path);
IniUtil::Read(def_cfg_file, cfg);
// Disabled on Windows because people were afraid that this config could be mistakenly
// created by some installer and screw up their games. Until any kind of solution is found.
String user_global_cfg_file;
#if !AGS_PLATFORM_SCUMMVM
#if !AGS_PLATFORM_OS_WINDOWS
// Read user global configuration file
user_global_cfg_file = find_user_global_cfg_file();
if (Path::ComparePaths(user_global_cfg_file, def_cfg_file) != 0)
IniUtil::Read(user_global_cfg_file, cfg);
#endif
// Read user configuration file
String user_cfg_file = find_user_cfg_file();
if (Path::ComparePaths(user_cfg_file, def_cfg_file) != 0 &&
Path::ComparePaths(user_cfg_file, user_global_cfg_file) != 0)
IniUtil::Read(user_cfg_file, cfg);
#endif
// Apply overriding options from mobile port settings
// TODO: normally, those should be instead stored in the same config file in a uniform way
// NOTE: the variable is historically called "ignore" but we use it in "override" meaning here
if (_G(psp_ignore_acsetup_cfg_file))
override_config_ext(cfg);
}
// Gathers settings from all available sources into single ConfigTree
void engine_prepare_config(ConfigTree &cfg, const String &exe_path, const ConfigTree &startup_opts) {
Debug::Printf(kDbgMsg_Info, "Setting up game configuration");
// Read configuration files
engine_read_config(exe_path, cfg);
// Merge startup options in
for (const auto &sectn : startup_opts)
for (const auto &opt : sectn._value)
cfg[sectn._key][opt._key] = opt._value;
// Add "meta" config settings to let setup application(s)
// display correct properties to the user
INIwriteint(cfg, "misc", "defaultres", _GP(game).GetResolutionType());
INIwriteint(cfg, "misc", "letterbox", _GP(game).options[OPT_LETTERBOX]);
INIwriteint(cfg, "misc", "game_width", _GP(game).GetDefaultRes().Width);
INIwriteint(cfg, "misc", "game_height", _GP(game).GetDefaultRes().Height);
INIwriteint(cfg, "misc", "gamecolordepth", _GP(game).color_depth * 8);
if (_GP(game).options[OPT_RENDERATSCREENRES] != kRenderAtScreenRes_UserDefined) {
// force enabled/disabled
INIwriteint(cfg, "graphics", "render_at_screenres", _GP(game).options[OPT_RENDERATSCREENRES] == kRenderAtScreenRes_Enabled);
INIwriteint(cfg, "disabled", "render_at_screenres", 1);
}
}
// Applies configuration to the running game
void engine_set_config(const ConfigTree cfg) {
config_defaults();
apply_config(cfg);
post_config();
}
//
// --tell command support: printing engine/game info by request
//
static bool print_info_needs_game(const std::set<String> &keys) {
return keys.count("all") > 0 || keys.count("config") > 0 || keys.count("configpath") > 0 ||
keys.count("data") > 0;
}
static void engine_print_info(const std::set<String> &keys, const String &exe_path, ConfigTree *user_cfg) {
const bool all = keys.count("all") > 0;
ConfigTree data;
if (all || keys.count("engine") > 0) {
data["engine"]["name"] = get_engine_name();
data["engine"]["version"] = get_engine_version();
}
if (all || keys.count("graphicdriver") > 0) {
StringV drv;
AGS::Engine::GetGfxDriverFactoryNames(drv);
for (size_t i = 0; i < drv.size(); ++i) {
data["graphicdriver"][String::FromFormat("%u", i)] = drv[i];
}
}
if (all || keys.count("configpath") > 0) {
String def_cfg_file = find_default_cfg_file(exe_path);
String gl_cfg_file = find_user_global_cfg_file();
String user_cfg_file = find_user_cfg_file();
data["config-path"]["default"] = def_cfg_file;
data["config-path"]["global"] = gl_cfg_file;
data["config-path"]["user"] = user_cfg_file;
}
if ((all || keys.count("config") > 0) && user_cfg) {
for (const auto &sectn : *user_cfg) {
String cfg_sectn = String::FromFormat("config@%s", sectn._key.GetCStr());
for (const auto &opt : sectn._value)
data[cfg_sectn][opt._key] = opt._value;
}
}
if (all || keys.count("data") > 0) {
data["data"]["gamename"] = _GP(game).gamename;
data["data"]["version"] = String::FromFormat("%d", loaded_game_file_version);
data["data"]["compiledwith"] = _GP(game).compiled_with;
data["data"]["basepack"] = _GP(usetup).main_data_filepath;
}
String full;
IniUtil::WriteToString(full, data);
platform->WriteStdOut("%s", full.GetCStr());
}
// Custom resource search callback for Allegro's system driver.
// It helps us direct Allegro to our game data location, because it won't know.
static int al_find_resource(char *dest, const char *resource, int dest_size) {
String path = Path::ConcatPaths(get_install_dir(), resource);
if (File::TestReadFile(path)) {
snprintf(dest, dest_size, "%s", path.GetCStr());
return 0;
}
return -1;
}
// TODO: this function is still a big mess, engine/system-related initialization
// is mixed with game-related data adjustments. Divide it in parts, move game
// data init into either InitGameState() or other game method as appropriate.
int initialize_engine(const ConfigTree &startup_opts) {
if (engine_pre_init_callback) {
engine_pre_init_callback();
}
//-----------------------------------------------------
// Install backend
if (!engine_init_allegro())
return EXIT_ERROR;
//-----------------------------------------------------
// Locate game data and assemble game config
const String exe_path = _G(global_argv)[0];
if (_G(justTellInfo) && !print_info_needs_game(_G(tellInfoKeys))) {
engine_print_info(_G(tellInfoKeys), exe_path, nullptr);
return EXIT_NORMAL;
}
if (!engine_init_gamedata(exe_path))
return EXIT_ERROR;
ConfigTree cfg;
engine_prepare_config(cfg, exe_path, startup_opts);
if (_G(justTellInfo)) {
engine_print_info(_G(tellInfoKeys), exe_path, &cfg);
return EXIT_NORMAL;
}
// Test if need to run built-in setup program (where available)
if (_G(justRunSetup)) {
int res;
if (!engine_run_setup(exe_path, cfg, res))
return res;
}
// Set up game options from user config
engine_set_config(cfg);
engine_setup_allegro();
engine_force_window();
our_eip = -190;
//-----------------------------------------------------
// Init data paths and other directories, locate general data files
engine_init_directories();
our_eip = -191;
engine_locate_speech_pak();
our_eip = -192;
engine_locate_audio_pak();
our_eip = -193;
// Assign custom find resource callback for limited Allegro operations
system_driver->find_resource = al_find_resource;
//-----------------------------------------------------
// Begin setting up systems
engine_setup_window();
our_eip = -194;
engine_init_fonts();
our_eip = -195;
engine_init_keyboard();
our_eip = -196;
engine_init_mouse();
our_eip = -197;
engine_init_timer();
our_eip = -198;
engine_init_audio();
our_eip = -199;
engine_init_debug();
our_eip = -10;
engine_init_exit_handler();
engine_init_rand();
engine_init_pathfinder();
set_game_speed(40);
our_eip = -20;
our_eip = -19;
int res = engine_load_game_data();
if (res != 0)
return res;
res = engine_check_register_game();
if (res != 0)
return res;
engine_init_title();
our_eip = -189;
res = engine_check_disk_space();
if (res != 0)
return res;
// Make sure that at least one font was loaded in the process of loading
// the game data.
// TODO: Fold this check into engine_load_game_data()
res = engine_check_font_was_loaded();
if (res != 0)
return res;
our_eip = -179;
engine_init_resolution_settings(_GP(game).GetGameRes());
// Attempt to initialize graphics mode
if (!engine_try_set_gfxmode_any(_GP(usetup).Screen))
return EXIT_ERROR;
SetMultitasking(0);
// [ER] 2014-03-13
// Hide the system cursor via allegro
show_os_cursor(MOUSE_CURSOR_NONE);
show_preload();
res = engine_init_sprites();
if (res != 0)
return res;
engine_init_game_settings();
engine_prepare_to_start_game();
allegro_bitmap_test_init();
initialize_start_and_play_game(_G(override_start_room), _G(loadSaveGameOnStartup));
return EXIT_NORMAL;
}
bool engine_try_set_gfxmode_any(const ScreenSetup &setup) {
engine_shutdown_gfxmode();
const Size init_desktop = get_desktop_size();
if (!graphics_mode_init_any(_GP(game).GetGameRes(), setup, ColorDepthOption(_GP(game).GetColorDepth())))
return false;
engine_post_gfxmode_setup(init_desktop);
return true;
}
bool engine_try_switch_windowed_gfxmode() {
if (!gfxDriver || !gfxDriver->IsModeSet())
return false;
// Keep previous mode in case we need to revert back
DisplayMode old_dm = gfxDriver->GetDisplayMode();
GameFrameSetup old_frame = graphics_mode_get_render_frame();
// Release engine resources that depend on display mode
engine_pre_gfxmode_release();
Size init_desktop = get_desktop_size();
bool switch_to_windowed = !old_dm.Windowed;
ActiveDisplaySetting setting = graphics_mode_get_last_setting(switch_to_windowed);
DisplayMode last_opposite_mode = setting.Dm;
GameFrameSetup use_frame_setup = setting.FrameSetup;
// If there are saved parameters for given mode (fullscreen/windowed)
// then use them, if there are not, get default setup for the new mode.
bool res;
if (last_opposite_mode.IsValid()) {
res = graphics_mode_set_dm(last_opposite_mode);
} else {
// we need to clone from initial config, because not every parameter is set by graphics_mode_get_defaults()
DisplayModeSetup dm_setup = _GP(usetup).Screen.DisplayMode;
dm_setup.Windowed = !old_dm.Windowed;
graphics_mode_get_defaults(dm_setup.Windowed, dm_setup.ScreenSize, use_frame_setup);
res = graphics_mode_set_dm_any(_GP(game).GetGameRes(), dm_setup, old_dm.ColorDepth, use_frame_setup);
}
// Apply corresponding frame render method
if (res)
res = graphics_mode_set_render_frame(use_frame_setup);
if (!res) {
// If failed, try switching back to previous gfx mode
res = graphics_mode_set_dm(old_dm) &&
graphics_mode_set_render_frame(old_frame);
}
if (res) {
// If succeeded (with any case), update engine objects that rely on
// active display mode.
if (gfxDriver->GetDisplayMode().Windowed)
init_desktop = get_desktop_size();
engine_post_gfxmode_setup(init_desktop);
}
ags_clear_input_buffer();
return res;
}
void engine_shutdown_gfxmode() {
if (!gfxDriver)
return;
engine_pre_gfxsystem_shutdown();
graphics_mode_shutdown();
}
const char *get_engine_name() {
return "Adventure Game Studio run-time engine";
}
const char *get_engine_version() {
return _G(EngineVersion).LongString.GetCStr();
}
void engine_set_pre_init_callback(t_engine_pre_init_callback callback) {
engine_pre_init_callback = callback;
}
} // namespace AGS3