Shipwright/soh/soh/SaveManager.h
Christopher Leggett 7b08f98b8c
Dual OTR MQ and Vanilla Support (#1694)
* Changes OTR Extraction to have specific mq and nonmq paths.

Also updates the game to load resources according to whether or not
Master Quest or Vanilla is loaded.

* Removes unneeded code from the last commit.

* Fixes some weird formatting in ZRom.c

* Loads oot-mq.otr and patches oot.otr on top, if both are present.

If only one or the other are present, it becomes the only and main OTR.

* Adds ImGui Logic for whether or an MQ Checkbox.

Checkbox checked only specifies whether new saves should be MQ or not.
Checkbox is disabled or force-enabled according to which OTRs are loaded.
Also as a necessity includes tracking what game versions have been loaded
from the OTRs.

* Adds MQ settings logic for Randomizer's ImGui menu.

* Writes Master Quest dungeons to the spoiler log

* Loads MQ Dungeons from spoiler, persists in save, and loads when appropriate.

* Adds logic to prevent loading or creating incompatible rando saves.

* Fixdes some linux build issues and new rando save issues

* Makes appimage create both vanilla and mq otrs

If either rom is present, it makes the corresponding OTR. If both are present,
it will make both. If one OTR is present but both roms are present, it will
create the missing OTR.

* Makes it so a randomized save file will not be marked as MQ.

* Refactors to load all OTRs from MainPath or a specific list.

Also adds the ability to take a std::unordered_set of hashes to
validate each OTR's version file against.

* Fixes a syntax error

* Makes ExtractAssets output Vanilla and MQ OTRs if both roms are present

* Fixes asset generation bug.

* Partially working fix for dual OTR extract_assets

Currently the cmake ExtractAssets target will return with a 1 if you
only end up exporting one type of OTR isntead of both. Haven't found
a great way to only attempt to copy a file if it exists from within
cmake. It does actually correctly copy the OTR that is generated,
despite the error from copying the other one.

Pushing as is for now but will keep investigating.

* Adds oot-mq.otr to the gitignore.

* Makes ExtractAssets not fail on only one rom/OTR.

* Removes PatchesPath from the constructors requiring OTRFiles vector.

* Renames OOT_UNKNOWN to just UNKNOWN to remove OOT specific reference.

* Removes randomizing MQ Dungeons and re-disables MQ rando.

Doing this so the PR can get merged quicker with just the Dual OTR
support and won't need to wait on rando logic to be updated. That
will happen in another PR directly after the merge.

* Update mac startup script for dual otr

* Update soh/macosx/soh-macos.sh

* Update soh/macosx/soh-macos.sh

* Update soh/macosx/soh-macos.sh

* Implements new BinaryReader to fix Linux build issue.

BinaryReader itself comes from https://github.com/Moneyl/BinaryTools
I added a wrapper to adapt it to the ABI from ZAPD's Binary Reader and
add Endianness checking. I also had to copy a handful of other bits and
pieces from ZAPD to make it all function as expected.

* A few edits to the updatream BinaryReader to compile it on Linux.

* Adds the Endianness to the first byte of the version file.

* Fixes Jenkins

* Addresses some of Kenix's comments

* Renames `ReadNullTerminatedString` to `ReadCString`

* Refactors Archive::LoadFile into a private method with more arguments.

* Removes BitConverter and extends existing endianness.h instead.

* Fixes an endianness issue with the version file.

Co-authored-by: Garrett Cox <garrettjcox@gmail.com>
2022-10-16 23:07:35 -04:00

160 lines
5.0 KiB
C++

#pragma once
#include <ultra64/gbi.h>
typedef struct {
u8 valid;
u16 deaths;
char playerName[8];
u16 healthCapacity;
u32 questItems;
s8 defense;
u16 health;
u32 requiresMasterQuest;
u32 requiresOriginal;
u8 seedHash[5];
u8 randoSave;
} SaveFileMetaInfo;
#ifdef __cplusplus
#include <map>
#include <string>
#include <tuple>
#include <functional>
#include <vector>
#include <filesystem>
#include <nlohmann/json.hpp>
class SaveManager {
public:
static SaveManager* Instance;
using InitFunc = void(*)(bool isDebug);
using LoadFunc = void(*)();
using SaveFunc = void(*)();
using PostFunc = void(*)(int version);
SaveManager();
void Init();
void InitFile(bool isDebug);
void SaveFile(int fileNum);
void SaveGlobal();
void LoadFile(int fileNum);
bool SaveFile_Exist(int fileNum);
// Adds a function that is called when we are intializing a save, including when we are loading a save.
void AddInitFunction(InitFunc func);
// 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 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);
void DeleteZeldaFile(int fileNum);
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) {
if (name == "") {
assert((*currentJsonContext).is_array());
(*currentJsonContext).push_back(data);
} else {
(*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{}) {
if (name == "") {
if (currentJsonArrayContext == currentJsonContext->end()) {
// This array member is past the data in the json file. Therefore, default construct it
data = defaultValue;
} else {
currentJsonArrayContext.value().get_to(data);
}
} else if (!currentJsonContext->contains(name.c_str())) {
data = defaultValue;
} else {
(*currentJsonContext)[name.c_str()].get_to(data);
}
}
// In the LoadArrayFunc func, the name must be "" to load from the array.
using LoadArrayFunc = std::function<void(size_t)>;
void LoadArray(const std::string& name, const size_t size, LoadArrayFunc func);
using LoadStructFunc = std::function<void()>;
void LoadStruct(const std::string& name, LoadStructFunc func);
static const int MaxFiles = 3;
std::array<SaveFileMetaInfo, MaxFiles> fileMetaInfo;
private:
std::filesystem::path GetFileName(int fileNum);
void ConvertFromUnversioned();
void CreateDefaultGlobal();
void InitMeta(int slotNum);
static void InitFileImpl(bool isDebug);
static void InitFileNormal();
static void InitFileDebug();
static void LoadRandomizerVersion1();
static void LoadRandomizerVersion2();
static void SaveRandomizer();
static void LoadBaseVersion1();
static void LoadBaseVersion2();
static void SaveBase();
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;
std::map<std::string, PostFunc> postHandlers;
nlohmann::json* currentJsonContext = nullptr;
nlohmann::json::iterator currentJsonArrayContext;
};
#else
// 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)(void);
void Save_Init(void);
void Save_InitFile(int isDebug);
void Save_SaveFile(void);
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);
SaveFileMetaInfo* Save_GetSaveMetaInfo(int fileNum);
void Save_CopyFile(int from, int to);
void Save_DeleteFile(int fileNum);
bool Save_Exist(int fileNum);
#endif