252 lines
7.6 KiB
C++
252 lines
7.6 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 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/>.
|
|
*
|
|
*/
|
|
|
|
//
|
|
// Quit game procedure
|
|
//
|
|
|
|
#include "ags/shared/core/platform.h"
|
|
#include "ags/engine/ac/cd_audio.h"
|
|
#include "ags/engine/ac/game.h"
|
|
#include "ags/engine/ac/game_setup.h"
|
|
#include "ags/shared/ac/game_setup_struct.h"
|
|
#include "ags/engine/ac/game_state.h"
|
|
#include "ags/engine/ac/room_status.h"
|
|
#include "ags/engine/ac/route_finder.h"
|
|
#include "ags/engine/ac/translation.h"
|
|
#include "ags/engine/ac/dynobj/dynobj_manager.h"
|
|
#include "ags/engine/debugging/ags_editor_debugger.h"
|
|
#include "ags/engine/debugging/debug_log.h"
|
|
#include "ags/engine/debugging/debugger.h"
|
|
#include "ags/shared/debugging/out.h"
|
|
#include "ags/shared/font/fonts.h"
|
|
#include "ags/engine/main/config.h"
|
|
#include "ags/engine/main/engine.h"
|
|
#include "ags/engine/main/main.h"
|
|
#include "ags/engine/main/quit.h"
|
|
#include "ags/shared/ac/sprite_cache.h"
|
|
#include "ags/engine/gfx/graphics_driver.h"
|
|
#include "ags/shared/gfx/bitmap.h"
|
|
#include "ags/shared/core/asset_manager.h"
|
|
#include "ags/engine/platform/base/ags_platform_driver.h"
|
|
#include "ags/engine/platform/base/sys_main.h"
|
|
#include "ags/plugins/plugin_engine.h"
|
|
#include "ags/shared/script/cc_common.h"
|
|
#include "ags/engine/media/audio/audio_system.h"
|
|
#include "ags/globals.h"
|
|
#include "ags/ags.h"
|
|
|
|
namespace AGS3 {
|
|
|
|
using namespace AGS::Shared;
|
|
using namespace AGS::Engine;
|
|
|
|
void quit_tell_editor_debugger(const String &qmsg, QuitReason qreason) {
|
|
if (_G(editor_debugging_initialized)) {
|
|
if (qreason & kQuitKind_GameException)
|
|
_G(handledErrorInEditor) = send_exception_to_debugger(qmsg.GetCStr());
|
|
send_state_to_debugger("EXIT");
|
|
_G(editor_debugger)->Shutdown();
|
|
}
|
|
}
|
|
|
|
void quit_stop_cd() {
|
|
if (_G(need_to_stop_cd))
|
|
cd_manager(3, 0);
|
|
}
|
|
|
|
void quit_check_dynamic_sprites(QuitReason qreason) {
|
|
if ((qreason & kQuitKind_NormalExit) && _G(check_dynamic_sprites_at_exit) && (_GP(game).options[OPT_DEBUGMODE] != 0)) {
|
|
// Check that the dynamic sprites have been deleted;
|
|
// ignore those that are owned by the game objects.
|
|
for (size_t i = 1; i < _GP(spriteset).GetSpriteSlotCount(); i++) {
|
|
if ((_GP(game).SpriteInfos[i].Flags & SPF_DYNAMICALLOC) &&
|
|
((_GP(game).SpriteInfos[i].Flags & SPF_OBJECTOWNED) == 0)) {
|
|
debug_script_warn("Dynamic sprite %d was never deleted", i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void quit_shutdown_audio() {
|
|
set_our_eip(9917);
|
|
_GP(game).options[OPT_CROSSFADEMUSIC] = 0;
|
|
shutdown_sound();
|
|
}
|
|
|
|
// Parses the quit message; returns:
|
|
// * QuitReason - which is a code of the reason we're quitting (game error, etc);
|
|
// * errmsg - a pure error message (extracted from the parsed string).
|
|
// * alertis - a complex message to post into the engine output (stdout, log);
|
|
QuitReason quit_check_for_error_state(const char *qmsg, String &errmsg, String &alertis) {
|
|
if (qmsg[0] == '|') {
|
|
return kQuit_GameRequest;
|
|
} else if (qmsg[0] == '!') {
|
|
QuitReason qreason;
|
|
qmsg++;
|
|
|
|
if (qmsg[0] == '|') {
|
|
qreason = kQuit_UserAbort;
|
|
alertis = "Abort key pressed.\n\n";
|
|
} else if (qmsg[0] == '?') {
|
|
qmsg++;
|
|
qreason = kQuit_ScriptAbort;
|
|
alertis = "A fatal error has been generated by the script using the AbortGame function. Please contact the game author for support.\n\n";
|
|
} else {
|
|
qreason = kQuit_GameError;
|
|
alertis.Format("An error has occurred. Please contact the game author for support, as this "
|
|
"is likely to be a scripting error and not a bug in AGS.\n"
|
|
"(Engine version %s)\n\n", _G(EngineVersion).LongString.GetCStr());
|
|
}
|
|
|
|
alertis.Append(cc_get_err_callstack());
|
|
|
|
if (qreason != kQuit_UserAbort) {
|
|
alertis.AppendFmt("\nError: %s", qmsg);
|
|
errmsg = qmsg;
|
|
Debug::Printf(kDbgMsg_Fatal, "ERROR: %s\n%s", qmsg, cc_get_error().CallStack.GetCStr());
|
|
}
|
|
return qreason;
|
|
} else if (qmsg[0] == '%') {
|
|
qmsg++;
|
|
alertis.Format("A warning has been generated. This is not normally fatal, but you have selected "
|
|
"to treat warnings as errors.\n"
|
|
"(Engine version %s)\n\n%s\n%s", _G(EngineVersion).LongString.GetCStr(), cc_get_err_callstack().GetCStr(), qmsg);
|
|
errmsg = qmsg;
|
|
return kQuit_GameWarning;
|
|
} else {
|
|
alertis.Format("An internal error has occurred. Please note down the following information.\n"
|
|
"(Engine version %s)\n"
|
|
"\nError: %s", _G(EngineVersion).LongString.GetCStr(), qmsg);
|
|
return kQuit_FatalError;
|
|
}
|
|
}
|
|
|
|
void quit_delete_temp_files() {
|
|
#ifdef TODO
|
|
al_ffblk dfb;
|
|
int dun = al_findfirst("~ac*.tmp", &dfb, FA_SEARCH);
|
|
while (!dun) {
|
|
File::DeleteFile(dfb.name);
|
|
dun = al_findnext(&dfb);
|
|
}
|
|
al_findclose(&dfb);
|
|
#endif
|
|
}
|
|
|
|
// quit - exits the engine, shutting down everything gracefully
|
|
// The parameter is the message to print. If this message begins with
|
|
// an '!' character, then it is printed as a "contact game author" error.
|
|
// If it begins with a '|' then it is treated as a "thanks for playing" type
|
|
// message. If it begins with anything else, it is treated as an internal
|
|
// error.
|
|
// "!|" is a special code used to mean that the player has aborted (Alt+X)
|
|
void quit(const char *quitmsg) {
|
|
if (!_G(abort_engine)) {
|
|
strncpy(_G(quit_message), quitmsg, sizeof(_G(quit_message)) - 1);
|
|
_G(quit_message)[sizeof(_G(quit_message)) - 1] = '\0';
|
|
_G(abort_engine) = true;
|
|
}
|
|
}
|
|
|
|
void quit_free() {
|
|
if (strlen(_G(quit_message)) == 0)
|
|
Common::strcpy_s(_G(quit_message), "|bye!");
|
|
|
|
const char *quitmsg = _G(quit_message);
|
|
|
|
Debug::Printf(kDbgMsg_Info, "Quitting the game...");
|
|
|
|
// NOTE: we must not use the quitmsg pointer past this step,
|
|
// as it may be from a plugin and we're about to free plugins
|
|
String errmsg, fullmsg;
|
|
QuitReason qreason = quit_check_for_error_state(quitmsg, errmsg, fullmsg);
|
|
|
|
if (qreason & kQuitKind_NormalExit)
|
|
save_config_file();
|
|
|
|
_G(handledErrorInEditor) = false;
|
|
|
|
quit_tell_editor_debugger(errmsg, qreason);
|
|
|
|
set_our_eip(9900);
|
|
|
|
quit_stop_cd();
|
|
|
|
set_our_eip(9020);
|
|
|
|
// Be sure to unlock mouse on exit, or users will hate us
|
|
sys_window_lock_mouse(false);
|
|
|
|
set_our_eip(9016);
|
|
|
|
quit_check_dynamic_sprites(qreason);
|
|
|
|
if (_G(use_cdplayer))
|
|
_G(platform)->ShutdownCDPlayer();
|
|
|
|
set_our_eip(9019);
|
|
|
|
quit_shutdown_audio();
|
|
|
|
set_our_eip(9901);
|
|
|
|
_GP(spriteset).Reset();
|
|
|
|
set_our_eip(9908);
|
|
|
|
shutdown_pathfinder();
|
|
|
|
unload_game();
|
|
|
|
engine_shutdown_gfxmode();
|
|
|
|
_G(platform)->PreBackendExit();
|
|
|
|
// On abnormal exit: display the message (at this point the window still exists)
|
|
if ((qreason & kQuitKind_NormalExit) == 0 && !_G(handledErrorInEditor)) {
|
|
_G(platform)->DisplayAlert("%s", fullmsg.GetCStr());
|
|
}
|
|
|
|
// release backed library
|
|
// WARNING: no Allegro objects should remain in memory after this,
|
|
// if their destruction is called later, program will crash!
|
|
shutdown_font_renderer();
|
|
allegro_exit();
|
|
sys_main_shutdown();
|
|
|
|
_G(platform)->PostAllegroExit();
|
|
|
|
set_our_eip(9903);
|
|
|
|
quit_delete_temp_files();
|
|
|
|
_G(proper_exit) = 1;
|
|
|
|
Debug::Printf(kDbgMsg_Alert, "***** ENGINE HAS SHUTDOWN");
|
|
|
|
shutdown_debug();
|
|
|
|
set_our_eip(9904);
|
|
}
|
|
|
|
} // namespace AGS3
|