scummvm/engines/ags/engine/main/quit.cpp
2021-02-06 16:37:31 -08:00

317 lines
8.3 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.
*
*/
//
// Quit game procedure
//
#include "ags/shared/core/platform.h"
#include "ags/engine/ac/cdaudio.h"
#include "ags/engine/ac/gamesetup.h"
#include "ags/shared/ac/gamesetupstruct.h"
#include "ags/engine/ac/roomstatus.h"
#include "ags/engine/ac/translation.h"
#include "ags/engine/debugging/agseditordebugger.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/mainheader.h"
#include "ags/engine/main/quit.h"
#include "ags/shared/ac/spritecache.h"
#include "ags/engine/gfx/graphicsdriver.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/shared/core/assetmanager.h"
#include "ags/engine/plugin/plugin_engine.h"
#include "ags/engine/media/audio/audio_system.h"
#include "ags/engine/globals.h"
#include "ags/ags.h"
namespace AGS3 {
using namespace AGS::Shared;
using namespace AGS::Engine;
extern GameSetupStruct game;
extern SpriteCache spriteset;
extern RoomStruct thisroom;
extern RoomStatus troom; // used for non-saveable rooms, eg. intro
extern int our_eip;
extern GameSetup usetup;
extern char pexbuf[STD_BUFFER_SIZE];
extern int proper_exit;
extern char check_dynamic_sprites_at_exit;
extern int editor_debugging_initialized;
extern IAGSEditorDebugger *editor_debugger;
extern int need_to_stop_cd;
extern int use_cdplayer;
extern IGraphicsDriver *gfxDriver;
bool handledErrorInEditor;
void quit_tell_editor_debugger(const String &qmsg, QuitReason qreason) {
if (editor_debugging_initialized) {
if (qreason & kQuitKind_GameException)
handledErrorInEditor = send_exception_to_editor(qmsg);
send_message_to_editor("EXIT");
editor_debugger->Shutdown();
}
}
void quit_stop_cd() {
if (need_to_stop_cd)
cd_manager(3, 0);
}
void quit_shutdown_scripts() {
ccUnregisterAllObjects();
}
void quit_check_dynamic_sprites(QuitReason qreason) {
if ((qreason & kQuitKind_NormalExit) && (check_dynamic_sprites_at_exit) &&
(game.options[OPT_DEBUGMODE] != 0)) {
// game exiting normally -- make sure the dynamic sprites
// have been deleted
for (int i = 1; i < spriteset.GetSpriteSlotCount(); i++) {
if (game.SpriteInfos[i].Flags & SPF_DYNAMICALLOC)
debug_script_warn("Dynamic sprite %d was never deleted", i);
}
}
}
void quit_shutdown_platform(QuitReason qreason) {
// Be sure to unlock mouse on exit, or users will hate us
platform->UnlockMouse();
platform->AboutToQuitGame();
our_eip = 9016;
pl_stop_plugins();
quit_check_dynamic_sprites(qreason);
platform->FinishedUsingGraphicsMode();
if (use_cdplayer)
platform->ShutdownCDPlayer();
}
void quit_shutdown_audio() {
our_eip = 9917;
game.options[OPT_CROSSFADEMUSIC] = 0;
stopmusic();
#ifndef PSP_NO_MOD_PLAYBACK
if (usetup.mod_player)
remove_mod_player();
#endif
// Quit the sound thread.
audioThread.Stop();
remove_sound();
}
QuitReason quit_check_for_error_state(const char *&qmsg, 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"
"(ACI version %s)\n\n", EngineVersion.LongString.GetCStr());
}
alertis.Append(get_cur_script(5));
if (qreason != kQuit_UserAbort)
alertis.Append("\nError: ");
else
qmsg = "";
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"
"(ACI version %s)\n\n%s\n", EngineVersion.LongString.GetCStr(), get_cur_script(5).GetCStr());
return kQuit_GameWarning;
} else {
alertis.Format("An internal error has occurred. Please note down the following information.\n"
"If the problem persists, post the details on the AGS Technical Forum.\n"
"(ACI version %s)\n"
"\nError: ", EngineVersion.LongString.GetCStr());
return kQuit_FatalError;
}
}
void quit_message_on_exit(const char *qmsg, String &alertis, QuitReason qreason) {
// successful exit displays no messages (because Windoze closes the dos-box
// if it is empty).
if ((qreason & kQuitKind_NormalExit) == 0 && !handledErrorInEditor) {
// Display the message (at this point the window still exists)
sprintf(pexbuf, "%s\n", qmsg);
alertis.Append(pexbuf);
platform->DisplayAlert("%s", alertis.GetCStr());
}
}
void quit_release_data() {
resetRoomStatuses();
thisroom.Free();
play.Free();
/* _CrtMemState memstart;
_CrtMemCheckpoint(&memstart);
_CrtMemDumpStatistics( &memstart );*/
Shared::AssetManager::DestroyInstance();
}
void quit_delete_temp_files() {
#ifdef TODO
al_ffblk dfb;
int dun = al_findfirst("~ac*.tmp", &dfb, FA_SEARCH);
while (!dun) {
::remove(dfb.name);
dun = al_findnext(&dfb);
}
al_findclose(&dfb);
#endif
}
// TODO: move to test unit
extern Bitmap *test_allegro_bitmap;
extern IDriverDependantBitmap *test_allegro_ddb;
void allegro_bitmap_test_release() {
delete test_allegro_bitmap;
if (test_allegro_ddb)
gfxDriver->DestroyDDB(test_allegro_ddb);
}
char return_to_roomedit[30] = "\0";
char return_to_room[150] = "\0";
char quit_message[256] = "\0";
// 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) {
strncpy(quit_message, quitmsg, 256);
_G(abort_engine) = true;
}
void quit_free() {
String alertis;
if (strlen(quit_message) == 0)
strcpy(quit_message, "|bye!");
const char *quitmsg = quit_message;
QuitReason qreason = quit_check_for_error_state(quitmsg, alertis);
if (qreason & kQuitKind_NormalExit)
save_config_file();
allegro_bitmap_test_release();
handledErrorInEditor = false;
quit_tell_editor_debugger(quit_message, qreason);
our_eip = 9900;
quit_stop_cd();
our_eip = 9020;
quit_shutdown_scripts();
quit_shutdown_platform(qreason);
our_eip = 9019;
quit_shutdown_audio();
our_eip = 9901;
shutdown_font_renderer();
our_eip = 9902;
spriteset.Reset();
our_eip = 9907;
close_translation();
our_eip = 9908;
shutdown_pathfinder();
engine_shutdown_gfxmode();
quit_message_on_exit(quitmsg, alertis, qreason);
quit_release_data();
// release backed library
// WARNING: no Allegro objects should remain in memory after this,
// if their destruction is called later, program will crash!
allegro_exit();
platform->PostAllegroExit();
our_eip = 9903;
quit_delete_temp_files();
proper_exit = 1;
Debug::Printf(kDbgMsg_Alert, "***** ENGINE HAS SHUTDOWN");
shutdown_debug();
our_eip = 9904;
}
extern "C" {
void quit_c(char *msg) {
quit(msg);
}
}
} // namespace AGS3