mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-22 09:22:18 -05:00
Merge pull request #2889 from Malkierian/sectional-saves
Sectional Saves
This commit is contained in:
commit
f4fbc79b5d
@ -61,6 +61,15 @@ typedef struct {
|
||||
u8 isRoom;
|
||||
} SceneTimestamp;
|
||||
|
||||
typedef enum { // Pre-existing IDs for save sections in base code
|
||||
SECTION_ID_BASE,
|
||||
SECTION_ID_RANDOMIZER,
|
||||
SECTION_ID_STATS,
|
||||
SECTION_ID_ENTRANCES,
|
||||
SECTION_ID_SCENES,
|
||||
SECTION_ID_MAX
|
||||
} SaveFuncIDs;
|
||||
|
||||
typedef struct {
|
||||
/* */ char buildVersion[50];
|
||||
/* */ s16 buildVersionMajor;
|
||||
|
@ -2,6 +2,9 @@ extern "C" {
|
||||
#include "gameplaystats.h"
|
||||
}
|
||||
|
||||
#include "soh/SaveManager.h"
|
||||
#include "functions.h"
|
||||
#include "macros.h"
|
||||
#include "ImGuiImpl.h"
|
||||
#include "../UIWidgets.hpp"
|
||||
|
||||
@ -273,6 +276,104 @@ std::string formatHexOnlyGameplayStat(uint32_t value) {
|
||||
return fmt::format("{:#x}", value, value);
|
||||
}
|
||||
|
||||
void LoadStatsVersion1() {
|
||||
std::string buildVersion;
|
||||
SaveManager::Instance->LoadData("buildVersion", buildVersion);
|
||||
strncpy(gSaveContext.sohStats.buildVersion, buildVersion.c_str(), ARRAY_COUNT(gSaveContext.sohStats.buildVersion) - 1);
|
||||
gSaveContext.sohStats.buildVersion[ARRAY_COUNT(gSaveContext.sohStats.buildVersion) - 1] = 0;
|
||||
SaveManager::Instance->LoadData("buildVersionMajor", gSaveContext.sohStats.buildVersionMajor);
|
||||
SaveManager::Instance->LoadData("buildVersionMinor", gSaveContext.sohStats.buildVersionMinor);
|
||||
SaveManager::Instance->LoadData("buildVersionPatch", gSaveContext.sohStats.buildVersionPatch);
|
||||
|
||||
SaveManager::Instance->LoadData("heartPieces", gSaveContext.sohStats.heartPieces);
|
||||
SaveManager::Instance->LoadData("heartContainers", gSaveContext.sohStats.heartContainers);
|
||||
SaveManager::Instance->LoadArray("dungeonKeys", ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.dungeonKeys[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadData("rtaTiming", gSaveContext.sohStats.rtaTiming);
|
||||
SaveManager::Instance->LoadData("fileCreatedAt", gSaveContext.sohStats.fileCreatedAt);
|
||||
SaveManager::Instance->LoadData("playTimer", gSaveContext.sohStats.playTimer);
|
||||
SaveManager::Instance->LoadData("pauseTimer", gSaveContext.sohStats.pauseTimer);
|
||||
SaveManager::Instance->LoadArray("itemTimestamps", ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.itemTimestamp[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("sceneTimestamps", ARRAY_COUNT(gSaveContext.sohStats.sceneTimestamps), [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
int scene, room, sceneTime, roomTime, isRoom;
|
||||
SaveManager::Instance->LoadData("scene", scene);
|
||||
SaveManager::Instance->LoadData("room", room);
|
||||
SaveManager::Instance->LoadData("sceneTime", sceneTime);
|
||||
SaveManager::Instance->LoadData("roomTime", roomTime);
|
||||
SaveManager::Instance->LoadData("isRoom", isRoom);
|
||||
if (scene == 0 && room == 0 && sceneTime == 0 && roomTime == 0 && isRoom == 0) {
|
||||
return;
|
||||
}
|
||||
gSaveContext.sohStats.sceneTimestamps[i].scene = scene;
|
||||
gSaveContext.sohStats.sceneTimestamps[i].room = room;
|
||||
gSaveContext.sohStats.sceneTimestamps[i].sceneTime = sceneTime;
|
||||
gSaveContext.sohStats.sceneTimestamps[i].roomTime = roomTime;
|
||||
gSaveContext.sohStats.sceneTimestamps[i].isRoom = isRoom;
|
||||
});
|
||||
});
|
||||
SaveManager::Instance->LoadData("tsIdx", gSaveContext.sohStats.tsIdx);
|
||||
SaveManager::Instance->LoadArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.count[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("scenesDiscovered", ARRAY_COUNT(gSaveContext.sohStats.scenesDiscovered), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.scenesDiscovered[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("entrancesDiscovered", ARRAY_COUNT(gSaveContext.sohStats.entrancesDiscovered), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.entrancesDiscovered[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("locationsSkipped", ARRAY_COUNT(gSaveContext.sohStats.locationsSkipped), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.locationsSkipped[i]);
|
||||
});
|
||||
}
|
||||
|
||||
void SaveStats(SaveContext* saveContext, int sectionID) {
|
||||
SaveManager::Instance->SaveData("buildVersion", saveContext->sohStats.buildVersion);
|
||||
SaveManager::Instance->SaveData("buildVersionMajor", saveContext->sohStats.buildVersionMajor);
|
||||
SaveManager::Instance->SaveData("buildVersionMinor", saveContext->sohStats.buildVersionMinor);
|
||||
SaveManager::Instance->SaveData("buildVersionPatch", saveContext->sohStats.buildVersionPatch);
|
||||
|
||||
SaveManager::Instance->SaveData("heartPieces", saveContext->sohStats.heartPieces);
|
||||
SaveManager::Instance->SaveData("heartContainers", saveContext->sohStats.heartContainers);
|
||||
SaveManager::Instance->SaveArray("dungeonKeys", ARRAY_COUNT(saveContext->sohStats.dungeonKeys), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.dungeonKeys[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveData("rtaTiming", saveContext->sohStats.rtaTiming);
|
||||
SaveManager::Instance->SaveData("fileCreatedAt", saveContext->sohStats.fileCreatedAt);
|
||||
SaveManager::Instance->SaveData("playTimer", saveContext->sohStats.playTimer);
|
||||
SaveManager::Instance->SaveData("pauseTimer", saveContext->sohStats.pauseTimer);
|
||||
SaveManager::Instance->SaveArray("itemTimestamps", ARRAY_COUNT(saveContext->sohStats.itemTimestamp), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.itemTimestamp[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveArray("sceneTimestamps", ARRAY_COUNT(saveContext->sohStats.sceneTimestamps), [&](size_t i) {
|
||||
if (saveContext->sohStats.sceneTimestamps[i].scene != 254 && saveContext->sohStats.sceneTimestamps[i].room != 254) {
|
||||
SaveManager::Instance->SaveStruct("", [&]() {
|
||||
SaveManager::Instance->SaveData("scene", saveContext->sohStats.sceneTimestamps[i].scene);
|
||||
SaveManager::Instance->SaveData("room", saveContext->sohStats.sceneTimestamps[i].room);
|
||||
SaveManager::Instance->SaveData("sceneTime", saveContext->sohStats.sceneTimestamps[i].sceneTime);
|
||||
SaveManager::Instance->SaveData("roomTime", saveContext->sohStats.sceneTimestamps[i].roomTime);
|
||||
SaveManager::Instance->SaveData("isRoom", saveContext->sohStats.sceneTimestamps[i].isRoom);
|
||||
});
|
||||
}
|
||||
});
|
||||
SaveManager::Instance->SaveData("tsIdx", saveContext->sohStats.tsIdx);
|
||||
SaveManager::Instance->SaveArray("counts", ARRAY_COUNT(saveContext->sohStats.count), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.count[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveArray("scenesDiscovered", ARRAY_COUNT(saveContext->sohStats.scenesDiscovered), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.scenesDiscovered[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveArray("entrancesDiscovered", ARRAY_COUNT(saveContext->sohStats.entrancesDiscovered), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.entrancesDiscovered[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveArray("locationsSkipped", ARRAY_COUNT(saveContext->sohStats.locationsSkipped), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.locationsSkipped[i]);
|
||||
});
|
||||
}
|
||||
|
||||
void GameplayStatsRow(const char* label, std::string value, ImVec4 color = COLOR_WHITE) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
ImGui::TableNextRow();
|
||||
@ -553,6 +654,47 @@ void DrawStatsTracker(bool& open) {
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
void InitStats(bool isDebug) {
|
||||
gSaveContext.sohStats.heartPieces = isDebug ? 8 : 0;
|
||||
gSaveContext.sohStats.heartContainers = isDebug ? 8 : 0;
|
||||
for (int dungeon = 0; dungeon < ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys); dungeon++) {
|
||||
gSaveContext.sohStats.dungeonKeys[dungeon] = isDebug ? 8 : 0;
|
||||
}
|
||||
gSaveContext.sohStats.rtaTiming = CVarGetInteger("gGameplayStats.RTATiming", 0);
|
||||
gSaveContext.sohStats.fileCreatedAt = 0;
|
||||
gSaveContext.sohStats.playTimer = 0;
|
||||
gSaveContext.sohStats.pauseTimer = 0;
|
||||
for (int timestamp = 0; timestamp < ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp); timestamp++) {
|
||||
gSaveContext.sohStats.itemTimestamp[timestamp] = 0;
|
||||
}
|
||||
for (int timestamp = 0; timestamp < ARRAY_COUNT(gSaveContext.sohStats.sceneTimestamps); timestamp++) {
|
||||
gSaveContext.sohStats.sceneTimestamps[timestamp].sceneTime = 0;
|
||||
gSaveContext.sohStats.sceneTimestamps[timestamp].roomTime = 0;
|
||||
gSaveContext.sohStats.sceneTimestamps[timestamp].scene = 254;
|
||||
gSaveContext.sohStats.sceneTimestamps[timestamp].room = 254;
|
||||
gSaveContext.sohStats.sceneTimestamps[timestamp].isRoom = 0;
|
||||
}
|
||||
gSaveContext.sohStats.tsIdx = 0;
|
||||
for (int count = 0; count < ARRAY_COUNT(gSaveContext.sohStats.count); count++) {
|
||||
gSaveContext.sohStats.count[count] = 0;
|
||||
}
|
||||
gSaveContext.sohStats.gameComplete = false;
|
||||
for (int scenesIdx = 0; scenesIdx < ARRAY_COUNT(gSaveContext.sohStats.scenesDiscovered); scenesIdx++) {
|
||||
gSaveContext.sohStats.scenesDiscovered[scenesIdx] = 0;
|
||||
}
|
||||
for (int entrancesIdx = 0; entrancesIdx < ARRAY_COUNT(gSaveContext.sohStats.entrancesDiscovered); entrancesIdx++) {
|
||||
gSaveContext.sohStats.entrancesDiscovered[entrancesIdx] = 0;
|
||||
}
|
||||
for (int rc = 0; rc < ARRAY_COUNT(gSaveContext.sohStats.locationsSkipped); rc++) {
|
||||
gSaveContext.sohStats.locationsSkipped[rc] = 0;
|
||||
}
|
||||
|
||||
strncpy(gSaveContext.sohStats.buildVersion, (const char*) gBuildVersion, sizeof(gSaveContext.sohStats.buildVersion) - 1);
|
||||
gSaveContext.sohStats.buildVersion[sizeof(gSaveContext.sohStats.buildVersion) - 1] = 0;
|
||||
gSaveContext.sohStats.buildVersionMajor = gBuildVersionMajor;
|
||||
gSaveContext.sohStats.buildVersionMinor = gBuildVersionMinor;
|
||||
gSaveContext.sohStats.buildVersionPatch = gBuildVersionPatch;
|
||||
}
|
||||
|
||||
// Entries listed here will have a timestamp shown in the stat window
|
||||
void SetupDisplayNames() {
|
||||
@ -702,4 +844,11 @@ extern "C" void InitStatTracker() {
|
||||
LUS::AddWindow("Enhancements", "Gameplay Stats", DrawStatsTracker, CVarGetInteger("gGameplayStats.Enabled", 0));
|
||||
SetupDisplayNames();
|
||||
SetupDisplayColors();
|
||||
SaveManager::Instance->AddLoadFunction("sohStats", 1, LoadStatsVersion1);
|
||||
// Add main section save, no parent
|
||||
SaveManager::Instance->AddSaveFunction("sohStats", 1, SaveStats, true, SECTION_PARENT_NONE);
|
||||
// Add subsections, parent of "sohStats". Not sure how to do this without the redundant references to "SaveStats"
|
||||
SaveManager::Instance->AddSaveFunction("entrances", 1, SaveStats, false, SECTION_ID_STATS);
|
||||
SaveManager::Instance->AddSaveFunction("scenes", 1, SaveStats, false, SECTION_ID_STATS);
|
||||
SaveManager::Instance->AddInitFunction(InitStats);
|
||||
}
|
@ -769,6 +769,8 @@ void Entrance_SetSceneDiscovered(u8 sceneNum) {
|
||||
u32 sceneBit = 1 << (sceneNum - (idx * bitsPerIndex));
|
||||
gSaveContext.sohStats.scenesDiscovered[idx] |= sceneBit;
|
||||
}
|
||||
// Save scenesDiscovered
|
||||
Save_SaveSection(SECTION_ID_SCENES);
|
||||
}
|
||||
|
||||
u8 Entrance_GetIsEntranceDiscovered(u16 entranceIndex) {
|
||||
@ -801,4 +803,6 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Save entrancesDiscovered
|
||||
Save_SaveSection(SECTION_ID_ENTRANCES);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <array>
|
||||
|
||||
extern "C" SaveContext gSaveContext;
|
||||
using namespace std::string_literals;
|
||||
|
||||
void SaveManager::WriteSaveFile(const std::filesystem::path& savePath, const uintptr_t addr, void* dramAddr,
|
||||
const size_t size) {
|
||||
@ -46,14 +47,20 @@ std::filesystem::path SaveManager::GetFileName(int fileNum) {
|
||||
}
|
||||
|
||||
SaveManager::SaveManager() {
|
||||
coreSectionIDsByName["base"] = SECTION_ID_BASE;
|
||||
coreSectionIDsByName["randomizer"] = SECTION_ID_RANDOMIZER;
|
||||
coreSectionIDsByName["sohStats"] = SECTION_ID_STATS;
|
||||
coreSectionIDsByName["entrances"] = SECTION_ID_ENTRANCES;
|
||||
coreSectionIDsByName["scenes"] = SECTION_ID_SCENES;
|
||||
AddLoadFunction("base", 1, LoadBaseVersion1);
|
||||
AddLoadFunction("base", 2, LoadBaseVersion2);
|
||||
AddLoadFunction("base", 3, LoadBaseVersion3);
|
||||
AddSaveFunction("base", 3, SaveBase);
|
||||
AddLoadFunction("base", 4, LoadBaseVersion4);
|
||||
AddSaveFunction("base", 4, SaveBase, true, SECTION_PARENT_NONE);
|
||||
|
||||
AddLoadFunction("randomizer", 1, LoadRandomizerVersion1);
|
||||
AddLoadFunction("randomizer", 2, LoadRandomizerVersion2);
|
||||
AddSaveFunction("randomizer", 2, SaveRandomizer);
|
||||
AddSaveFunction("randomizer", 2, SaveRandomizer, true, SECTION_PARENT_NONE);
|
||||
|
||||
AddInitFunction(InitFileImpl);
|
||||
|
||||
@ -261,7 +268,7 @@ void SaveManager::LoadRandomizerVersion2() {
|
||||
});
|
||||
}
|
||||
|
||||
void SaveManager::SaveRandomizer(SaveContext* saveContext) {
|
||||
void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID) {
|
||||
|
||||
if(!saveContext->n64ddFlag) return;
|
||||
|
||||
@ -394,6 +401,7 @@ void SaveManager::Init() {
|
||||
for (int fileNum = 0; fileNum < MaxFiles; fileNum++) {
|
||||
if (std::filesystem::exists(GetFileName(fileNum))) {
|
||||
LoadFile(fileNum);
|
||||
saveBlock = nlohmann::json::object();
|
||||
}
|
||||
|
||||
}
|
||||
@ -507,39 +515,6 @@ void SaveManager::InitFileNormal() {
|
||||
}
|
||||
gSaveContext.inventory.defenseHearts = 0;
|
||||
gSaveContext.inventory.gsTokens = 0;
|
||||
gSaveContext.sohStats.heartPieces = 0;
|
||||
gSaveContext.sohStats.heartContainers = 0;
|
||||
for (int dungeon = 0; dungeon < ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys); dungeon++) {
|
||||
gSaveContext.sohStats.dungeonKeys[dungeon] = 0;
|
||||
}
|
||||
gSaveContext.sohStats.rtaTiming = CVarGetInteger("gGameplayStats.RTATiming", 0);
|
||||
gSaveContext.sohStats.fileCreatedAt = 0;
|
||||
gSaveContext.sohStats.playTimer = 0;
|
||||
gSaveContext.sohStats.pauseTimer = 0;
|
||||
for (int timestamp = 0; timestamp < ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp); timestamp++) {
|
||||
gSaveContext.sohStats.itemTimestamp[timestamp] = 0;
|
||||
}
|
||||
for (int timestamp = 0; timestamp < ARRAY_COUNT(gSaveContext.sohStats.sceneTimestamps); timestamp++) {
|
||||
gSaveContext.sohStats.sceneTimestamps[timestamp].sceneTime = 0;
|
||||
gSaveContext.sohStats.sceneTimestamps[timestamp].roomTime = 0;
|
||||
gSaveContext.sohStats.sceneTimestamps[timestamp].scene = 254;
|
||||
gSaveContext.sohStats.sceneTimestamps[timestamp].room = 254;
|
||||
gSaveContext.sohStats.sceneTimestamps[timestamp].isRoom = 0;
|
||||
}
|
||||
gSaveContext.sohStats.tsIdx = 0;
|
||||
for (int count = 0; count < ARRAY_COUNT(gSaveContext.sohStats.count); count++) {
|
||||
gSaveContext.sohStats.count[count] = 0;
|
||||
}
|
||||
gSaveContext.sohStats.gameComplete = false;
|
||||
for (int scenesIdx = 0; scenesIdx < ARRAY_COUNT(gSaveContext.sohStats.scenesDiscovered); scenesIdx++) {
|
||||
gSaveContext.sohStats.scenesDiscovered[scenesIdx] = 0;
|
||||
}
|
||||
for (int entrancesIdx = 0; entrancesIdx < ARRAY_COUNT(gSaveContext.sohStats.entrancesDiscovered); entrancesIdx++) {
|
||||
gSaveContext.sohStats.entrancesDiscovered[entrancesIdx] = 0;
|
||||
}
|
||||
for (int rc = 0; rc < ARRAY_COUNT(gSaveContext.sohStats.locationsSkipped); rc++) {
|
||||
gSaveContext.sohStats.locationsSkipped[rc] = 0;
|
||||
}
|
||||
for (int scene = 0; scene < ARRAY_COUNT(gSaveContext.sceneFlags); scene++) {
|
||||
gSaveContext.sceneFlags[scene].chest = 0;
|
||||
gSaveContext.sceneFlags[scene].swch = 0;
|
||||
@ -608,12 +583,6 @@ void SaveManager::InitFileNormal() {
|
||||
gSaveContext.pendingSale = ITEM_NONE;
|
||||
gSaveContext.pendingSaleMod = MOD_NONE;
|
||||
|
||||
strncpy(gSaveContext.sohStats.buildVersion, (const char*) gBuildVersion, sizeof(gSaveContext.sohStats.buildVersion) - 1);
|
||||
gSaveContext.sohStats.buildVersion[sizeof(gSaveContext.sohStats.buildVersion) - 1] = 0;
|
||||
gSaveContext.sohStats.buildVersionMajor = gBuildVersionMajor;
|
||||
gSaveContext.sohStats.buildVersionMinor = gBuildVersionMinor;
|
||||
gSaveContext.sohStats.buildVersionPatch = gBuildVersionPatch;
|
||||
|
||||
//RANDOTODO (ADD ITEMLOCATIONS TO GSAVECONTEXT)
|
||||
}
|
||||
|
||||
@ -695,11 +664,6 @@ void SaveManager::InitFileDebug() {
|
||||
}
|
||||
gSaveContext.inventory.defenseHearts = 0;
|
||||
gSaveContext.inventory.gsTokens = 0;
|
||||
gSaveContext.sohStats.heartPieces = 8;
|
||||
gSaveContext.sohStats.heartContainers = 8;
|
||||
for (int dungeon = 0; dungeon < ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys); dungeon++) {
|
||||
gSaveContext.sohStats.dungeonKeys[dungeon] = 8;
|
||||
}
|
||||
|
||||
gSaveContext.horseData.scene = SCENE_SPOT00;
|
||||
gSaveContext.horseData.pos.x = -1840;
|
||||
@ -728,27 +692,53 @@ void SaveManager::InitFileDebug() {
|
||||
}
|
||||
|
||||
// Threaded SaveFile takes copy of gSaveContext for local unmodified storage
|
||||
void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext) {
|
||||
nlohmann::json baseBlock;
|
||||
|
||||
baseBlock["version"] = 1;
|
||||
baseBlock["sections"] = nlohmann::json::object();
|
||||
for (auto& section : sectionSaveHandlers) {
|
||||
nlohmann::json& sectionBlock = baseBlock["sections"][section.first];
|
||||
sectionBlock["version"] = section.second.first;
|
||||
void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int sectionID) {
|
||||
// Needed for first time save, hasn't changed in forever anyway
|
||||
saveBlock["version"] = 1;
|
||||
if (sectionID == SECTION_ID_BASE) {
|
||||
for (auto& sectionHandlerPair : sectionSaveHandlers) {
|
||||
auto& saveFuncInfo = sectionHandlerPair.second;
|
||||
// Don't call SaveFuncs for sections that aren't tied to game save
|
||||
if (!saveFuncInfo.saveWithBase) {
|
||||
continue;
|
||||
}
|
||||
nlohmann::json& sectionBlock = saveBlock["sections"][saveFuncInfo.name];
|
||||
sectionBlock["version"] = sectionHandlerPair.second.version;
|
||||
// If any save file is loaded for medatata, or a spoiler log is loaded (not sure which at this point), there is still data in the "randomizer" section
|
||||
// This clears the randomizer data block if and only if the section being called is "randomizer" and n64ddFlag is false.
|
||||
if (sectionHandlerPair.second.name == "randomizer" && !gSaveContext.n64ddFlag) {
|
||||
sectionBlock["data"] = nlohmann::json::object();
|
||||
continue;
|
||||
}
|
||||
|
||||
currentJsonContext = §ionBlock["data"];
|
||||
sectionHandlerPair.second.func(saveContext, sectionID);
|
||||
}
|
||||
} else {
|
||||
SaveFuncInfo svi = sectionSaveHandlers.find(sectionID)->second;
|
||||
auto& sectionName = svi.name;
|
||||
auto sectionVersion = svi.version;
|
||||
// If section has a parentSection, it is a subsection. Load parentSection version and set sectionBlock to parent string
|
||||
if (svi.parentSection != -1 && svi.parentSection < sectionIndex) {
|
||||
auto parentSvi = sectionSaveHandlers.find(svi.parentSection)->second;
|
||||
sectionName = parentSvi.name;
|
||||
sectionVersion = parentSvi.version;
|
||||
}
|
||||
nlohmann::json& sectionBlock = saveBlock["sections"][sectionName];
|
||||
sectionBlock["version"] = sectionVersion;
|
||||
currentJsonContext = §ionBlock["data"];
|
||||
section.second.second(saveContext);
|
||||
svi.func(saveContext, sectionID);
|
||||
}
|
||||
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
FILE* w = fopen(GetFileName(fileNum).c_str(), "w");
|
||||
std::string json_string = baseBlock.dump(4);
|
||||
std::string json_string = saveBlock.dump(4);
|
||||
fwrite(json_string.c_str(), sizeof(char), json_string.length(), w);
|
||||
fclose(w);
|
||||
#else
|
||||
std::ofstream output(GetFileName(fileNum));
|
||||
output << std::setw(4) << baseBlock << std::endl;
|
||||
output << std::setw(4) << saveBlock << std::endl;
|
||||
#endif
|
||||
|
||||
delete saveContext;
|
||||
@ -756,20 +746,29 @@ void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSaveFile>(fileNum);
|
||||
}
|
||||
|
||||
void SaveManager::SaveFile(int fileNum, bool threaded) {
|
||||
// SaveSection creates a copy of gSaveContext to prevent mid-save data modification, and passes its reference to SaveFileThreaded
|
||||
void SaveManager::SaveSection(int fileNum, int sectionID, bool threaded) {
|
||||
if (fileNum == 0xFF) {
|
||||
return;
|
||||
}
|
||||
// Can't think of any time the promise would be needed, so use push_task instead of submit
|
||||
// Don't save a nonexistent section
|
||||
if (sectionID >= sectionIndex) {
|
||||
SPDLOG_ERROR("SaveSection: Section ID not registered.");
|
||||
return;
|
||||
}
|
||||
auto saveContext = new SaveContext;
|
||||
memcpy(saveContext, &gSaveContext, sizeof(gSaveContext));
|
||||
if (threaded) {
|
||||
smThreadPool->push_task_back(&SaveManager::SaveFileThreaded, this, fileNum, saveContext);
|
||||
smThreadPool->push_task_back(&SaveManager::SaveFileThreaded, this, fileNum, saveContext, sectionID);
|
||||
} else {
|
||||
SaveFileThreaded(fileNum, saveContext);
|
||||
SaveFileThreaded(fileNum, saveContext, sectionID);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveManager::SaveFile(int fileNum) {
|
||||
SaveSection(fileNum, SECTION_ID_BASE, true);
|
||||
}
|
||||
|
||||
void SaveManager::SaveGlobal() {
|
||||
nlohmann::json globalBlock;
|
||||
globalBlock["version"] = 1;
|
||||
@ -790,7 +789,7 @@ void SaveManager::LoadFile(int fileNum) {
|
||||
|
||||
std::ifstream input(GetFileName(fileNum));
|
||||
|
||||
nlohmann::json saveBlock;
|
||||
saveBlock = nlohmann::json::object();
|
||||
input >> saveBlock;
|
||||
if (!saveBlock.contains("version")) {
|
||||
SPDLOG_ERROR("Save at " + GetFileName(fileNum).string() + " contains no version");
|
||||
@ -866,14 +865,22 @@ void SaveManager::AddLoadFunction(const std::string& name, int version, LoadFunc
|
||||
sectionLoadHandlers[name][version] = func;
|
||||
}
|
||||
|
||||
void SaveManager::AddSaveFunction(const std::string& name, int version, SaveFunc func) {
|
||||
if (sectionSaveHandlers.contains(name)) {
|
||||
void SaveManager::AddSaveFunction(const std::string& name, int version, SaveFunc func, bool saveWithBase, int parentSection = -1) {
|
||||
if (sectionRegistry.contains(name)) {
|
||||
SPDLOG_ERROR("Adding save function for section that already has one: " + name);
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
sectionSaveHandlers[name] = std::make_pair(version, func);
|
||||
int index = sectionIndex;
|
||||
if (coreSectionIDsByName.contains(name)) {
|
||||
index = coreSectionIDsByName.find(name)->second;
|
||||
} else {
|
||||
sectionIndex++;
|
||||
}
|
||||
SaveFuncInfo sfi = { name, version, func, saveWithBase, parentSection };
|
||||
sectionSaveHandlers.emplace(index, sfi);
|
||||
sectionRegistry.emplace(name, index);
|
||||
}
|
||||
|
||||
void SaveManager::AddPostFunction(const std::string& name, PostFunc func) {
|
||||
@ -886,6 +893,15 @@ void SaveManager::AddPostFunction(const std::string& name, PostFunc func) {
|
||||
postHandlers[name] = func;
|
||||
}
|
||||
|
||||
// Returns -1 if section name not found
|
||||
int SaveManager::GetSaveSectionID(std::string& sectionName) {
|
||||
if (sectionRegistry.contains(sectionName)) {
|
||||
return sectionRegistry.find(sectionName)->second;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveManager::CreateDefaultGlobal() {
|
||||
gSaveContext.audioSetting = 0;
|
||||
gSaveContext.zTargetSetting = 0;
|
||||
@ -1461,7 +1477,180 @@ void SaveManager::LoadBaseVersion3() {
|
||||
SaveManager::Instance->LoadData("dogParams", gSaveContext.dogParams);
|
||||
}
|
||||
|
||||
void SaveManager::SaveBase(SaveContext* saveContext) {
|
||||
void SaveManager::LoadBaseVersion4() {
|
||||
SaveManager::Instance->LoadData("entranceIndex", gSaveContext.entranceIndex);
|
||||
SaveManager::Instance->LoadData("linkAge", gSaveContext.linkAge);
|
||||
SaveManager::Instance->LoadData("cutsceneIndex", gSaveContext.cutsceneIndex);
|
||||
SaveManager::Instance->LoadData("dayTime", gSaveContext.dayTime);
|
||||
SaveManager::Instance->LoadData("nightFlag", gSaveContext.nightFlag);
|
||||
SaveManager::Instance->LoadData("totalDays", gSaveContext.totalDays);
|
||||
SaveManager::Instance->LoadData("bgsDayCount", gSaveContext.bgsDayCount);
|
||||
SaveManager::Instance->LoadData("deaths", gSaveContext.deaths);
|
||||
SaveManager::Instance->LoadArray("playerName", ARRAY_COUNT(gSaveContext.playerName), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.playerName[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadData("n64ddFlag", gSaveContext.n64ddFlag);
|
||||
SaveManager::Instance->LoadData("healthCapacity", gSaveContext.healthCapacity);
|
||||
SaveManager::Instance->LoadData("health", gSaveContext.health);
|
||||
SaveManager::Instance->LoadData("magicLevel", gSaveContext.magicLevel);
|
||||
SaveManager::Instance->LoadData("magic", gSaveContext.magic);
|
||||
SaveManager::Instance->LoadData("rupees", gSaveContext.rupees);
|
||||
SaveManager::Instance->LoadData("swordHealth", gSaveContext.swordHealth);
|
||||
SaveManager::Instance->LoadData("naviTimer", gSaveContext.naviTimer);
|
||||
SaveManager::Instance->LoadData("isMagicAcquired", gSaveContext.isMagicAcquired);
|
||||
SaveManager::Instance->LoadData("isDoubleMagicAcquired", gSaveContext.isDoubleMagicAcquired);
|
||||
SaveManager::Instance->LoadData("isDoubleDefenseAcquired", gSaveContext.isDoubleDefenseAcquired);
|
||||
SaveManager::Instance->LoadData("bgsFlag", gSaveContext.bgsFlag);
|
||||
SaveManager::Instance->LoadData("ocarinaGameRoundNum", gSaveContext.ocarinaGameRoundNum);
|
||||
SaveManager::Instance->LoadStruct("childEquips", []() {
|
||||
SaveManager::Instance->LoadArray("buttonItems", ARRAY_COUNT(gSaveContext.childEquips.buttonItems), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.childEquips.buttonItems[i],
|
||||
static_cast<uint8_t>(ITEM_NONE));
|
||||
});
|
||||
SaveManager::Instance->LoadArray("cButtonSlots", ARRAY_COUNT(gSaveContext.childEquips.cButtonSlots), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.childEquips.cButtonSlots[i],
|
||||
static_cast<uint8_t>(SLOT_NONE));
|
||||
});
|
||||
SaveManager::Instance->LoadData("equipment", gSaveContext.childEquips.equipment);
|
||||
});
|
||||
SaveManager::Instance->LoadStruct("adultEquips", []() {
|
||||
SaveManager::Instance->LoadArray("buttonItems", ARRAY_COUNT(gSaveContext.adultEquips.buttonItems), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.adultEquips.buttonItems[i],
|
||||
static_cast<uint8_t>(ITEM_NONE));
|
||||
});
|
||||
SaveManager::Instance->LoadArray("cButtonSlots", ARRAY_COUNT(gSaveContext.adultEquips.cButtonSlots), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.adultEquips.cButtonSlots[i],
|
||||
static_cast<uint8_t>(SLOT_NONE));
|
||||
});
|
||||
SaveManager::Instance->LoadData("equipment", gSaveContext.adultEquips.equipment);
|
||||
});
|
||||
SaveManager::Instance->LoadData("unk_54", gSaveContext.unk_54);
|
||||
SaveManager::Instance->LoadData("savedSceneNum", gSaveContext.savedSceneNum);
|
||||
SaveManager::Instance->LoadStruct("equips", []() {
|
||||
SaveManager::Instance->LoadArray("buttonItems", ARRAY_COUNT(gSaveContext.equips.buttonItems), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.equips.buttonItems[i], static_cast<uint8_t>(ITEM_NONE));
|
||||
});
|
||||
SaveManager::Instance->LoadArray("cButtonSlots", ARRAY_COUNT(gSaveContext.equips.cButtonSlots), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.equips.cButtonSlots[i], static_cast<uint8_t>(SLOT_NONE));
|
||||
});
|
||||
SaveManager::Instance->LoadData("equipment", gSaveContext.equips.equipment);
|
||||
});
|
||||
SaveManager::Instance->LoadStruct("inventory", []() {
|
||||
SaveManager::Instance->LoadArray("items", ARRAY_COUNT(gSaveContext.inventory.items), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.inventory.items[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("ammo", ARRAY_COUNT(gSaveContext.inventory.ammo), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.inventory.ammo[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadData("equipment", gSaveContext.inventory.equipment);
|
||||
SaveManager::Instance->LoadData("upgrades", gSaveContext.inventory.upgrades);
|
||||
SaveManager::Instance->LoadData("questItems", gSaveContext.inventory.questItems);
|
||||
SaveManager::Instance->LoadArray("dungeonItems", ARRAY_COUNT(gSaveContext.inventory.dungeonItems), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.inventory.dungeonItems[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("dungeonKeys", ARRAY_COUNT(gSaveContext.inventory.dungeonKeys), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.inventory.dungeonKeys[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadData("defenseHearts", gSaveContext.inventory.defenseHearts);
|
||||
SaveManager::Instance->LoadData("gsTokens", gSaveContext.inventory.gsTokens);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("sceneFlags", ARRAY_COUNT(gSaveContext.sceneFlags), [](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&i]() {
|
||||
SaveManager::Instance->LoadData("chest", gSaveContext.sceneFlags[i].chest);
|
||||
SaveManager::Instance->LoadData("swch", gSaveContext.sceneFlags[i].swch);
|
||||
SaveManager::Instance->LoadData("clear", gSaveContext.sceneFlags[i].clear);
|
||||
SaveManager::Instance->LoadData("collect", gSaveContext.sceneFlags[i].collect);
|
||||
SaveManager::Instance->LoadData("unk", gSaveContext.sceneFlags[i].unk);
|
||||
SaveManager::Instance->LoadData("rooms", gSaveContext.sceneFlags[i].rooms);
|
||||
SaveManager::Instance->LoadData("floors", gSaveContext.sceneFlags[i].floors);
|
||||
});
|
||||
});
|
||||
SaveManager::Instance->LoadStruct("fw", []() {
|
||||
SaveManager::Instance->LoadStruct("pos", []() {
|
||||
SaveManager::Instance->LoadData("x", gSaveContext.fw.pos.x);
|
||||
SaveManager::Instance->LoadData("y", gSaveContext.fw.pos.y);
|
||||
SaveManager::Instance->LoadData("z", gSaveContext.fw.pos.z);
|
||||
});
|
||||
SaveManager::Instance->LoadData("yaw", gSaveContext.fw.yaw);
|
||||
SaveManager::Instance->LoadData("playerParams", gSaveContext.fw.playerParams);
|
||||
SaveManager::Instance->LoadData("entranceIndex", gSaveContext.fw.entranceIndex);
|
||||
SaveManager::Instance->LoadData("roomIndex", gSaveContext.fw.roomIndex);
|
||||
SaveManager::Instance->LoadData("set", gSaveContext.fw.set);
|
||||
SaveManager::Instance->LoadData("tempSwchFlags", gSaveContext.fw.tempSwchFlags);
|
||||
SaveManager::Instance->LoadData("tempCollectFlags", gSaveContext.fw.tempCollectFlags);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("gsFlags", ARRAY_COUNT(gSaveContext.gsFlags), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.gsFlags[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("highScores", ARRAY_COUNT(gSaveContext.highScores), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.highScores[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("eventChkInf", ARRAY_COUNT(gSaveContext.eventChkInf), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.eventChkInf[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("itemGetInf", ARRAY_COUNT(gSaveContext.itemGetInf), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.itemGetInf[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadArray("infTable", ARRAY_COUNT(gSaveContext.infTable), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.infTable[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadData("worldMapAreaData", gSaveContext.worldMapAreaData);
|
||||
SaveManager::Instance->LoadData("scarecrowLongSongSet", gSaveContext.scarecrowLongSongSet);
|
||||
SaveManager::Instance->LoadArray("scarecrowLongSong", ARRAY_COUNT(gSaveContext.scarecrowLongSong), [](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&i]() {
|
||||
SaveManager::Instance->LoadData("noteIdx", gSaveContext.scarecrowLongSong[i].noteIdx);
|
||||
SaveManager::Instance->LoadData("unk_01", gSaveContext.scarecrowLongSong[i].unk_01);
|
||||
SaveManager::Instance->LoadData("unk_02", gSaveContext.scarecrowLongSong[i].unk_02);
|
||||
SaveManager::Instance->LoadData("volume", gSaveContext.scarecrowLongSong[i].volume);
|
||||
SaveManager::Instance->LoadData("vibrato", gSaveContext.scarecrowLongSong[i].vibrato);
|
||||
SaveManager::Instance->LoadData("tone", gSaveContext.scarecrowLongSong[i].tone);
|
||||
SaveManager::Instance->LoadData("semitone", gSaveContext.scarecrowLongSong[i].semitone);
|
||||
});
|
||||
});
|
||||
SaveManager::Instance->LoadData("scarecrowSpawnSongSet", gSaveContext.scarecrowSpawnSongSet);
|
||||
SaveManager::Instance->LoadArray("scarecrowSpawnSong", ARRAY_COUNT(gSaveContext.scarecrowSpawnSong), [](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&i]() {
|
||||
SaveManager::Instance->LoadData("noteIdx", gSaveContext.scarecrowSpawnSong[i].noteIdx);
|
||||
SaveManager::Instance->LoadData("unk_01", gSaveContext.scarecrowSpawnSong[i].unk_01);
|
||||
SaveManager::Instance->LoadData("unk_02", gSaveContext.scarecrowSpawnSong[i].unk_02);
|
||||
SaveManager::Instance->LoadData("volume", gSaveContext.scarecrowSpawnSong[i].volume);
|
||||
SaveManager::Instance->LoadData("vibrato", gSaveContext.scarecrowSpawnSong[i].vibrato);
|
||||
SaveManager::Instance->LoadData("tone", gSaveContext.scarecrowSpawnSong[i].tone);
|
||||
SaveManager::Instance->LoadData("semitone", gSaveContext.scarecrowSpawnSong[i].semitone);
|
||||
});
|
||||
});
|
||||
SaveManager::Instance->LoadStruct("horseData", []() {
|
||||
SaveManager::Instance->LoadData("scene", gSaveContext.horseData.scene);
|
||||
SaveManager::Instance->LoadStruct("pos", []() {
|
||||
SaveManager::Instance->LoadData("x", gSaveContext.horseData.pos.x);
|
||||
SaveManager::Instance->LoadData("y", gSaveContext.horseData.pos.y);
|
||||
SaveManager::Instance->LoadData("z", gSaveContext.horseData.pos.z);
|
||||
});
|
||||
SaveManager::Instance->LoadData("angle", gSaveContext.horseData.angle);
|
||||
});
|
||||
|
||||
SaveManager::Instance->LoadArray("randomizerInf", ARRAY_COUNT(gSaveContext.randomizerInf), [](size_t i) {
|
||||
SaveManager::Instance->LoadData("", gSaveContext.randomizerInf[i]);
|
||||
});
|
||||
SaveManager::Instance->LoadData("isMasterQuest", gSaveContext.isMasterQuest);
|
||||
SaveManager::Instance->LoadStruct("backupFW", []() {
|
||||
SaveManager::Instance->LoadStruct("pos", []() {
|
||||
SaveManager::Instance->LoadData("x", gSaveContext.backupFW.pos.x);
|
||||
SaveManager::Instance->LoadData("y", gSaveContext.backupFW.pos.y);
|
||||
SaveManager::Instance->LoadData("z", gSaveContext.backupFW.pos.z);
|
||||
});
|
||||
SaveManager::Instance->LoadData("yaw", gSaveContext.backupFW.yaw);
|
||||
SaveManager::Instance->LoadData("playerParams", gSaveContext.backupFW.playerParams);
|
||||
SaveManager::Instance->LoadData("entranceIndex", gSaveContext.backupFW.entranceIndex);
|
||||
SaveManager::Instance->LoadData("roomIndex", gSaveContext.backupFW.roomIndex);
|
||||
SaveManager::Instance->LoadData("set", gSaveContext.backupFW.set);
|
||||
SaveManager::Instance->LoadData("tempSwchFlags", gSaveContext.backupFW.tempSwchFlags);
|
||||
SaveManager::Instance->LoadData("tempCollectFlags", gSaveContext.backupFW.tempCollectFlags);
|
||||
});
|
||||
SaveManager::Instance->LoadData("dogParams", gSaveContext.dogParams);
|
||||
}
|
||||
|
||||
void SaveManager::SaveBase(SaveContext* saveContext, int sectionID) {
|
||||
SaveManager::Instance->SaveData("entranceIndex", saveContext->entranceIndex);
|
||||
SaveManager::Instance->SaveData("linkAge", saveContext->linkAge);
|
||||
SaveManager::Instance->SaveData("cutsceneIndex", saveContext->cutsceneIndex);
|
||||
@ -1534,47 +1723,6 @@ void SaveManager::SaveBase(SaveContext* saveContext) {
|
||||
SaveManager::Instance->SaveData("defenseHearts", saveContext->inventory.defenseHearts);
|
||||
SaveManager::Instance->SaveData("gsTokens", saveContext->inventory.gsTokens);
|
||||
});
|
||||
SaveManager::Instance->SaveStruct("sohStats", [&]() {
|
||||
SaveManager::Instance->SaveData("buildVersion", saveContext->sohStats.buildVersion);
|
||||
SaveManager::Instance->SaveData("buildVersionMajor", saveContext->sohStats.buildVersionMajor);
|
||||
SaveManager::Instance->SaveData("buildVersionMinor", saveContext->sohStats.buildVersionMinor);
|
||||
SaveManager::Instance->SaveData("buildVersionPatch", saveContext->sohStats.buildVersionPatch);
|
||||
|
||||
SaveManager::Instance->SaveData("heartPieces", saveContext->sohStats.heartPieces);
|
||||
SaveManager::Instance->SaveData("heartContainers", saveContext->sohStats.heartContainers);
|
||||
SaveManager::Instance->SaveArray("dungeonKeys", ARRAY_COUNT(saveContext->sohStats.dungeonKeys), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.dungeonKeys[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveData("rtaTiming", saveContext->sohStats.rtaTiming);
|
||||
SaveManager::Instance->SaveData("fileCreatedAt", saveContext->sohStats.fileCreatedAt);
|
||||
SaveManager::Instance->SaveData("playTimer", saveContext->sohStats.playTimer);
|
||||
SaveManager::Instance->SaveData("pauseTimer", saveContext->sohStats.pauseTimer);
|
||||
SaveManager::Instance->SaveArray("itemTimestamps", ARRAY_COUNT(saveContext->sohStats.itemTimestamp), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.itemTimestamp[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveArray("sceneTimestamps", ARRAY_COUNT(saveContext->sohStats.sceneTimestamps), [&](size_t i) {
|
||||
SaveManager::Instance->SaveStruct("", [&]() {
|
||||
SaveManager::Instance->SaveData("scene", saveContext->sohStats.sceneTimestamps[i].scene);
|
||||
SaveManager::Instance->SaveData("room", saveContext->sohStats.sceneTimestamps[i].room);
|
||||
SaveManager::Instance->SaveData("sceneTime", saveContext->sohStats.sceneTimestamps[i].sceneTime);
|
||||
SaveManager::Instance->SaveData("roomTime", saveContext->sohStats.sceneTimestamps[i].roomTime);
|
||||
SaveManager::Instance->SaveData("isRoom", saveContext->sohStats.sceneTimestamps[i].isRoom);
|
||||
});
|
||||
});
|
||||
SaveManager::Instance->SaveData("tsIdx", saveContext->sohStats.tsIdx);
|
||||
SaveManager::Instance->SaveArray("counts", ARRAY_COUNT(saveContext->sohStats.count), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.count[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveArray("scenesDiscovered", ARRAY_COUNT(saveContext->sohStats.scenesDiscovered), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.scenesDiscovered[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveArray("entrancesDiscovered", ARRAY_COUNT(saveContext->sohStats.entrancesDiscovered), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.entrancesDiscovered[i]);
|
||||
});
|
||||
SaveManager::Instance->SaveArray("locationsSkipped", ARRAY_COUNT(saveContext->sohStats.locationsSkipped), [&](size_t i) {
|
||||
SaveManager::Instance->SaveData("", saveContext->sohStats.locationsSkipped[i]);
|
||||
});
|
||||
});
|
||||
SaveManager::Instance->SaveArray("sceneFlags", ARRAY_COUNT(saveContext->sceneFlags), [&](size_t i) {
|
||||
SaveManager::Instance->SaveStruct("", [&]() {
|
||||
SaveManager::Instance->SaveData("chest", saveContext->sceneFlags[i].chest);
|
||||
@ -1744,8 +1892,7 @@ void SaveManager::LoadStruct(const std::string& name, LoadStructFunc func) {
|
||||
|
||||
#if defined(__WIIU__) || defined(__SWITCH__)
|
||||
// std::filesystem::copy_file doesn't work properly with the Wii U's toolchain atm
|
||||
int copy_file(const char* src, const char* dst)
|
||||
{
|
||||
int copy_file(const char* src, const char* dst) {
|
||||
alignas(0x40) uint8_t buf[4096];
|
||||
FILE* r = fopen(src, "r");
|
||||
if (!r) {
|
||||
@ -2147,7 +2294,7 @@ void SaveManager::ConvertFromUnversioned() {
|
||||
static SaveContext saveContextSave = gSaveContext;
|
||||
InitFile(false);
|
||||
CopyV0Save(*file, gSaveContext);
|
||||
SaveFile(fileNum, false);
|
||||
SaveSection(fileNum, SECTION_ID_BASE, false);
|
||||
InitMeta(fileNum);
|
||||
gSaveContext = saveContextSave;
|
||||
}
|
||||
@ -2168,7 +2315,11 @@ extern "C" void Save_InitFile(int isDebug) {
|
||||
}
|
||||
|
||||
extern "C" void Save_SaveFile(void) {
|
||||
SaveManager::Instance->SaveFile(gSaveContext.fileNum, true);
|
||||
SaveManager::Instance->SaveFile(gSaveContext.fileNum);
|
||||
}
|
||||
|
||||
extern "C" void Save_SaveSection(int sectionID) {
|
||||
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionID, true);
|
||||
}
|
||||
|
||||
extern "C" void Save_SaveGlobal(void) {
|
||||
@ -2184,8 +2335,8 @@ extern "C" void Save_AddLoadFunction(char* name, int version, SaveManager::LoadF
|
||||
SaveManager::Instance->AddLoadFunction(name, version, func);
|
||||
}
|
||||
|
||||
extern "C" void Save_AddSaveFunction(char* name, int version, SaveManager::SaveFunc func) {
|
||||
SaveManager::Instance->AddSaveFunction(name, version, func);
|
||||
extern "C" void Save_AddSaveFunction(char* name, int version, SaveManager::SaveFunc func, bool saveWithBase) {
|
||||
SaveManager::Instance->AddSaveFunction(name, version, func, saveWithBase);
|
||||
}
|
||||
|
||||
extern "C" SaveFileMetaInfo* Save_GetSaveMetaInfo(int fileNum) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <libultraship/libultra/gbi.h>
|
||||
|
||||
#define SECTION_PARENT_NONE -1
|
||||
typedef struct {
|
||||
u8 valid;
|
||||
u16 deaths;
|
||||
@ -31,28 +32,38 @@ typedef struct {
|
||||
#include "thread-pool/BS_thread_pool.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "z64save.h"
|
||||
#include "z64save.h"
|
||||
}
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
class SaveManager {
|
||||
public:
|
||||
public:
|
||||
static SaveManager* Instance;
|
||||
|
||||
static void WriteSaveFile(const std::filesystem::path& savePath, uintptr_t addr, void* dramAddr, size_t size);
|
||||
static void ReadSaveFile(std::filesystem::path savePath, uintptr_t addr, void* dramAddr, size_t size);
|
||||
|
||||
using InitFunc = void(*)(bool isDebug);
|
||||
using LoadFunc = void(*)();
|
||||
using SaveFunc = void(*)(SaveContext* saveContext);
|
||||
using PostFunc = void(*)(int version);
|
||||
using InitFunc = void (*)(bool isDebug);
|
||||
using LoadFunc = void (*)();
|
||||
using SaveFunc = void (*)(SaveContext* saveContext, int sectionID);
|
||||
using PostFunc = void (*)(int version);
|
||||
|
||||
typedef struct {
|
||||
std::string name;
|
||||
int version;
|
||||
SaveManager::SaveFunc func;
|
||||
bool saveWithBase;
|
||||
int parentSection;
|
||||
} SaveFuncInfo;
|
||||
|
||||
SaveManager();
|
||||
|
||||
void Init();
|
||||
void InitFile(bool isDebug);
|
||||
void SaveFile(int fileNum, bool threaded);
|
||||
void SaveFile(int fileNum);
|
||||
void SaveSection(int fileNum, int sectionID, bool threaded);
|
||||
int GetSaveSectionID(std::string& name);
|
||||
void SaveGlobal();
|
||||
void LoadFile(int fileNum);
|
||||
bool SaveFile_Exist(int fileNum);
|
||||
@ -64,10 +75,12 @@ public:
|
||||
// Adds a function to handling loading a section
|
||||
void AddLoadFunction(const std::string& name, int version, LoadFunc func);
|
||||
|
||||
// Adds a function that is called when saving. This should only be called once for each function, the version is filled in automatically.
|
||||
void AddSaveFunction(const std::string& name, int version, SaveFunc func);
|
||||
// Adds a function that is called when saving. This should only be called once for each function, the version is
|
||||
// filled in automatically.
|
||||
void AddSaveFunction(const std::string& name, int version, SaveFunc func, bool saveWithBase, int parentSection);
|
||||
|
||||
// Adds a function to be called after loading is complete. This is to handle any cleanup required from loading old versions.
|
||||
// Adds a function to be called after loading is complete. This is to handle any cleanup required from loading old
|
||||
// versions.
|
||||
void AddPostFunction(const std::string& name, PostFunc func);
|
||||
|
||||
void CopyZeldaFile(int from, int to);
|
||||
@ -75,8 +88,7 @@ public:
|
||||
bool IsRandoFile();
|
||||
|
||||
// Use a name of "" to save to an array. You must be in a SaveArray callback.
|
||||
template<typename T>
|
||||
void SaveData(const std::string& name, const T& data) {
|
||||
template <typename T> void SaveData(const std::string& name, const T& data) {
|
||||
if (name == "") {
|
||||
assert((*currentJsonContext).is_array());
|
||||
(*currentJsonContext).push_back(data);
|
||||
@ -84,16 +96,16 @@ public:
|
||||
(*currentJsonContext)[name.c_str()] = data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// In the SaveArrayFunc func, the name must be "" to save to the array.
|
||||
using SaveArrayFunc = std::function<void(size_t)>;
|
||||
void SaveArray(const std::string& name, const size_t size, SaveArrayFunc func);
|
||||
|
||||
|
||||
using SaveStructFunc = std::function<void()>;
|
||||
void SaveStruct(const std::string& name, SaveStructFunc func);
|
||||
|
||||
// Use a name of "" to load from an array. You must be in a LoadArray callback.
|
||||
template<typename T> void LoadData(const std::string& name, T& data, const T& defaultValue = T{}) {
|
||||
template <typename T> void LoadData(const std::string& name, T& data, const T& defaultValue = T{}) {
|
||||
if (name == "") {
|
||||
if (currentJsonArrayContext == currentJsonContext->end()) {
|
||||
// This array member is past the data in the json file. Therefore, default construct it
|
||||
@ -120,11 +132,12 @@ public:
|
||||
|
||||
private:
|
||||
std::filesystem::path GetFileName(int fileNum);
|
||||
nlohmann::json saveBlock;
|
||||
|
||||
void ConvertFromUnversioned();
|
||||
void CreateDefaultGlobal();
|
||||
|
||||
void SaveFileThreaded(int fileNum, SaveContext* saveContext);
|
||||
void SaveFileThreaded(int fileNum, SaveContext* saveContext, int sectionID);
|
||||
|
||||
void InitMeta(int slotNum);
|
||||
static void InitFileImpl(bool isDebug);
|
||||
@ -133,20 +146,23 @@ public:
|
||||
|
||||
static void LoadRandomizerVersion1();
|
||||
static void LoadRandomizerVersion2();
|
||||
static void SaveRandomizer(SaveContext* saveContext);
|
||||
static void SaveRandomizer(SaveContext* saveContext, int sectionID);
|
||||
|
||||
static void LoadBaseVersion1();
|
||||
static void LoadBaseVersion2();
|
||||
static void LoadBaseVersion3();
|
||||
static void SaveBase(SaveContext* saveContext);
|
||||
static void LoadBaseVersion4();
|
||||
static void SaveBase(SaveContext* saveContext, int sectionID);
|
||||
|
||||
std::vector<InitFunc> initFuncs;
|
||||
|
||||
using SectionLoadHandler = std::map<int, LoadFunc>;
|
||||
std::map<std::string, SectionLoadHandler> sectionLoadHandlers;
|
||||
|
||||
using SectionSaveHandler = std::pair<int, SaveFunc>;
|
||||
std::map<std::string, SectionSaveHandler> sectionSaveHandlers;
|
||||
int sectionIndex = SECTION_ID_MAX;
|
||||
std::map<std::string, int> coreSectionIDsByName;
|
||||
std::map<int, SaveFuncInfo> sectionSaveHandlers;
|
||||
std::map<std::string, int> sectionRegistry;
|
||||
|
||||
std::map<std::string, PostFunc> postHandlers;
|
||||
|
||||
@ -160,15 +176,16 @@ public:
|
||||
// TODO feature parity to the C++ interface. We need Save_AddInitFunction and Save_AddPostFunction at least
|
||||
|
||||
typedef void (*Save_LoadFunc)(void);
|
||||
typedef void (*Save_SaveFunc)(const SaveContext* saveContext);
|
||||
typedef void (*Save_SaveFunc)(const SaveContext* saveContext, int sectionID);
|
||||
|
||||
void Save_Init(void);
|
||||
void Save_InitFile(int isDebug);
|
||||
void Save_SaveFile(void);
|
||||
void Save_SaveSection(int sectionID);
|
||||
void Save_SaveGlobal(void);
|
||||
void Save_LoadGlobal(void);
|
||||
void Save_AddLoadFunction(char* name, int version, Save_LoadFunc func);
|
||||
void Save_AddSaveFunction(char* name, int version, Save_SaveFunc func);
|
||||
void Save_AddSaveFunction(char* name, int version, Save_SaveFunc func, bool saveWithBase, int parentSection);
|
||||
SaveFileMetaInfo* Save_GetSaveMetaInfo(int fileNum);
|
||||
void Save_CopyFile(int from, int to);
|
||||
void Save_DeleteFile(int fileNum);
|
||||
|
Loading…
Reference in New Issue
Block a user