diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 19b0366812..55c993a08f 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -26,20 +26,18 @@ #include #include "game_interpreter.h" #include "async_handler.h" -#include "audio.h" #include "game_dynrpg.h" #include "filefinder.h" #include "game_destiny.h" #include "game_map.h" #include "game_event.h" -#include "game_enemyparty.h" -#include "game_ineluki.h" #include "game_player.h" #include "game_targets.h" #include "game_switches.h" #include "game_variables.h" #include "game_party.h" #include "game_actors.h" +#include "game_strings.h" #include "game_system.h" #include "game_message.h" #include "game_pictures.h" @@ -70,7 +68,6 @@ #include "transition.h" #include "baseui.h" #include "algo.h" -#include "rand.h" using namespace Game_Interpreter_Shared; @@ -4903,91 +4900,21 @@ bool Game_Interpreter::CommandManiacControlGlobalSave(lcf::rpg::EventCommand con int operation = com.parameters[0]; - auto load_global_save = [&]() { - Main_Data::global_save_opened = true; - - // Load - auto lgs = FileFinder::Save().OpenFile("Save.lgs"); - if (!lgs) { - return; - } - - lcf::LcfReader reader(lgs); - std::string header; - reader.ReadString(header, reader.ReadInt()); - if (header.length() != 13 || header != "LcfGlobalSave") { - Output::Debug("This is not a valid global save."); - return; - } - - lcf::LcfReader::Chunk chunk; - - while (!reader.Eof()) { - chunk.ID = reader.ReadInt(); - chunk.length = reader.ReadInt(); - switch (chunk.ID) { - case 1: { - Game_Switches::Switches_t switches; - reader.Read(switches, chunk.length); - Main_Data::game_switches_global->SetData(std::move(switches)); - break; - } - case 2: { - Game_Variables::Variables_t variables; - reader.Read(variables, chunk.length); - Main_Data::game_variables_global->SetData(std::move(variables)); - break; - } - default: - reader.Skip(chunk, "CommandManiacControlGlobalSave"); - } - } - }; - if (operation == 0) { - // Open - load_global_save(); + // Open: Fill Global Save with data from Save.lgs + // Does nothing when already opened + ManiacPatch::GlobalSave::Load(); } else if (operation == 1) { // Close - Main_Data::global_save_opened = false; + // Marks the file as closed and does nothing + ManiacPatch::GlobalSave::Close(); } else if (operation == 2 || operation == 3) { // 2: Save (write to file) // 3: Save and Close - if (!Main_Data::global_save_opened) { - return true; - } - - auto savelgs_name = FileFinder::Save().FindFile("Save.lgs"); - if (savelgs_name.empty()) { - savelgs_name = "Save.lgs"; - } - - auto lgs_out = FileFinder::Save().OpenOutputStream(savelgs_name); - if (!lgs_out) { - Output::Warning("Maniac ControlGlobalSave: Saving failed"); - return true; - } - - lcf::LcfWriter writer(lgs_out, lcf::EngineVersion::e2k3); - writer.WriteInt(13); - const std::string header = "LcfGlobalSave"; - writer.Write(header); - writer.WriteInt(1); - writer.WriteInt(Main_Data::game_switches_global->GetSize()); - writer.Write(Main_Data::game_switches_global->GetData()); - writer.WriteInt(2); - writer.WriteInt(Main_Data::game_variables_global->GetSize() * sizeof(int32_t)); - writer.Write(Main_Data::game_variables_global->GetData()); - - AsyncHandler::SaveFilesystem(); - - if (operation == 3) { - Main_Data::global_save_opened = false; - } + ManiacPatch::GlobalSave::Save(operation == 3); } else if (operation == 4 || operation == 5) { - if (!Main_Data::global_save_opened) { - load_global_save(); - } + // Reload the file when it was already closed + ManiacPatch::GlobalSave::Load(); int type = com.parameters[2]; int game_state_idx = ValueOrVariableBitfield(com.parameters[1], 0, com.parameters[3]); diff --git a/src/game_interpreter_shared.cpp b/src/game_interpreter_shared.cpp index 3b2ea92732..6d8f417419 100644 --- a/src/game_interpreter_shared.cpp +++ b/src/game_interpreter_shared.cpp @@ -16,24 +16,12 @@ */ #include "game_interpreter_shared.h" -#include "game_actors.h" -#include "game_enemyparty.h" -#include "game_ineluki.h" -#include "game_map.h" -#include "game_party.h" -#include "game_player.h" +#include "game_strings.h" #include "game_switches.h" -#include "game_system.h" +#include "game_variables.h" #include "maniac_patch.h" #include "main_data.h" -#include "output.h" #include "player.h" -#include "rand.h" -#include "util_macro.h" -#include "utils.h" -#include "audio.h" -#include "baseui.h" -#include #include #include #include diff --git a/src/main_data.cpp b/src/main_data.cpp index 02e6523f72..34e9553f67 100644 --- a/src/main_data.cpp +++ b/src/main_data.cpp @@ -18,9 +18,7 @@ // Headers #include #include "main_data.h" -#include "filefinder.h" #include "filefinder_rtp.h" -#include "filesystem.h" #include "game_destiny.h" #include "game_system.h" #include "game_actors.h" @@ -38,10 +36,7 @@ #include "game_targets.h" #include "game_quit.h" #include "game_windows.h" -#include "font.h" -#include "player.h" #include "system.h" -#include "output.h" #ifndef _WIN32 # include @@ -75,7 +70,6 @@ namespace Main_Data { std::unique_ptr game_dynrpg; std::unique_ptr game_ineluki; std::unique_ptr game_destiny; - bool global_save_opened = false; std::unique_ptr game_switches_global; std::unique_ptr game_variables_global; @@ -132,7 +126,6 @@ void Main_Data::Cleanup() { game_dynrpg.reset(); game_ineluki.reset(); game_destiny.reset(); - global_save_opened = false; game_switches_global.reset(); game_variables_global.reset(); } diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index 92dc26361e..928d1aaf77 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -17,6 +17,7 @@ #include "maniac_patch.h" +#include "filesystem_stream.h" #include "input.h" #include "game_actors.h" #include "game_interpreter_control_variables.h" @@ -29,7 +30,9 @@ #include "output.h" #include "player.h" +#include #include +#include #include /* @@ -117,6 +120,8 @@ namespace { Divmul, Between }; + + bool global_save_opened = false; } struct ProcessAssignmentRet { @@ -941,3 +946,111 @@ std::string_view ManiacPatch::GetLcfDescription(int data_type, int id, bool is_d Output::Warning("GetLcfDescription: Unsupported data_type {} {}", data_type, id); return {}; } + +bool ManiacPatch::GlobalSave::Load() { + if (!Player::IsPatchManiac()) { + return true; + } + + if (global_save_opened) { + return true; + } + + // Even consider it opened when the file is missing + // It will be created on Save + global_save_opened = true; + + auto lgs_in = FileFinder::Save().OpenFile("Save.lgs"); + if (!lgs_in) { + return false; + } + + return Load(lgs_in); +} + +bool ManiacPatch::GlobalSave::Load(Filesystem_Stream::InputStream& lgs_in) { + if (!lgs_in) { + return false; + } + + lcf::LcfReader reader(lgs_in); + std::string header; + reader.ReadString(header, reader.ReadInt()); + if (header.length() != 13 || header != "LcfGlobalSave") { + Output::Debug("This is not a valid global save."); + return false; + } + + lcf::LcfReader::Chunk chunk; + + while (!reader.Eof()) { + chunk.ID = reader.ReadInt(); + chunk.length = reader.ReadInt(); + switch (chunk.ID) { + case 1: { + Game_Switches::Switches_t switches; + reader.Read(switches, chunk.length); + Main_Data::game_switches_global->SetData(std::move(switches)); + break; + } + case 2: { + Game_Variables::Variables_t variables; + reader.Read(variables, chunk.length); + Main_Data::game_variables_global->SetData(std::move(variables)); + break; + } + default: + reader.Skip(chunk, "CommandManiacControlGlobalSave"); + } + } + + return true; +} + +bool ManiacPatch::GlobalSave::Save(bool close_global_save) { + if (!Player::IsPatchManiac()) { + return true; + } + + if (!global_save_opened) { + return true; + } + + auto savelgs_name = FileFinder::Save().FindFile("Save.lgs"); + if (savelgs_name.empty()) { + savelgs_name = "Save.lgs"; + } + + auto lgs_out = FileFinder::Save().OpenOutputStream(savelgs_name); + if (!Save(lgs_out)) { + Output::Warning("Maniac ControlGlobalSave: Saving failed"); + return false; + } + + global_save_opened = !close_global_save; + + AsyncHandler::SaveFilesystem(); + return true; +} + +bool ManiacPatch::GlobalSave::Save(Filesystem_Stream::OutputStream& lgs_out) { + if (!lgs_out) { + return false; + } + + lcf::LcfWriter writer(lgs_out, lcf::EngineVersion::e2k3); + writer.WriteInt(13); + const std::string header = "LcfGlobalSave"; + writer.Write(header); + writer.WriteInt(1); + writer.WriteInt(Main_Data::game_switches_global->GetSize()); + writer.Write(Main_Data::game_switches_global->GetData()); + writer.WriteInt(2); + writer.WriteInt(Main_Data::game_variables_global->GetSize() * sizeof(int32_t)); + writer.Write(Main_Data::game_variables_global->GetData()); + return true; +} + +void ManiacPatch::GlobalSave::Close() { + global_save_opened = false; +} diff --git a/src/maniac_patch.h b/src/maniac_patch.h index 34d26bc16c..51ec17a544 100644 --- a/src/maniac_patch.h +++ b/src/maniac_patch.h @@ -22,17 +22,15 @@ #include #include #include +#include "filesystem_stream.h" #include "span.h" -#include "game_strings.h" - class Game_BaseInterpreterContext; namespace ManiacPatch { int32_t ParseExpression(Span op_codes, const Game_BaseInterpreterContext& interpreter); std::vector ParseExpressions(Span op_codes, const Game_BaseInterpreterContext& interpreter); - std::array GetKeyRange(); bool GetKeyState(uint32_t key_id); @@ -41,6 +39,57 @@ namespace ManiacPatch { std::string_view GetLcfName(int data_type, int id, bool is_dynamic); std::string_view GetLcfDescription(int data_type, int id, bool is_dynamic); + + namespace GlobalSave { + /** + * Attempts to load Save.lgs from the save directory. + * On success the data is stored in Main_Data::*_global variables. + * + * Is a no-op and returns true, when: + * - Maniac Patch is not enabled + * - The global save is already opened + * + * @return Whether loading was successful (on success the global save is considered opened) + */ + bool Load(); + + /** + * Attempts to load a Maniac Global Save file. + * On success the data is stored in Main_Data::*_global variables. + * + * @param lgs_in Stream to read the global save from + * @return Whether loading was successful + */ + bool Load(Filesystem_Stream::InputStream& lgs_in); + + /** + * Saves to a Maniac Global Save file (Save.lgs in the save directory). + * The save data is read from Main_Data::*_global variables. + * + * Is a no-op and returns true, when: + * - Maniac Patch is not enabled + * - The global save is not opened + * + * @param close_global_save When true marks the global save as closed on success + * @return Whether saving was successful + */ + bool Save(bool close_global_save); + + /** + * Saves to a Maniac Global Save file. + * The save data is read from Main_Data::*_global variables. + * + * @param lgs_out Stream to write the global save to + * @return Whether saving was successful + */ + bool Save(Filesystem_Stream::OutputStream& lgs_out); + + /** + * Resets the "open" flag for the global save. + * Open operations reload the file now. + */ + void Close(); + } } #endif diff --git a/src/player.cpp b/src/player.cpp index 8a209c678e..25e2fb116b 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -20,8 +20,6 @@ #include #include #include -#include -#include #include #ifdef _WIN32 @@ -45,7 +43,6 @@ #include "game_battle.h" #include "game_destiny.h" #include "game_map.h" -#include "game_message.h" #include "game_enemyparty.h" #include "game_ineluki.h" #include "game_party.h" @@ -85,6 +82,7 @@ #include "game_clock.h" #include "message_overlay.h" #include "audio_midi.h" +#include "maniac_patch.h" #if defined(__ANDROID__) && !defined(USE_LIBRETRO) #include "platform/android/android.h" @@ -911,6 +909,8 @@ void Player::RestoreBaseResolution() { void Player::ResetGameObjects() { // The init order is important + ManiacPatch::GlobalSave::Save(true); + Main_Data::Cleanup(); Main_Data::game_switches = std::make_unique(); diff --git a/src/scene_gamebrowser.cpp b/src/scene_gamebrowser.cpp index 0acf02fbe9..f892f458be 100644 --- a/src/scene_gamebrowser.cpp +++ b/src/scene_gamebrowser.cpp @@ -52,7 +52,7 @@ void Scene_GameBrowser::Continue(SceneType /* prev_scene */) { AudioSeCache::Clear(); MidiDecoder::Reset(); lcf::Data::Clear(); - Main_Data::Cleanup(); + Player::ResetGameObjects(); // Restore the base resolution Player::RestoreBaseResolution(); diff --git a/src/scene_map.cpp b/src/scene_map.cpp index 9c9037af8c..b347c4482d 100644 --- a/src/scene_map.cpp +++ b/src/scene_map.cpp @@ -16,15 +16,12 @@ */ // Headers -#include "scene_gameover.h" #include "scene_map.h" -#include "scene_menu.h" #include "scene_save.h" #include "scene_debug.h" #include "scene_settings.h" #include "main_data.h" #include "game_map.h" -#include "game_actors.h" #include "game_message.h" #include "game_party.h" #include "game_player.h" @@ -38,9 +35,6 @@ #include "transition.h" #include "audio.h" #include "input.h" -#include "screen.h" -#include "scene_load.h" -#include "output.h" #include "game_dynrpg.h" using namespace std::chrono_literals;