Add support for json save files (#54)

This commit is contained in:
Garrett Cox 2024-01-15 05:17:26 +00:00
parent f068674d77
commit ae257f15a2
11 changed files with 569 additions and 28 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
logs
*.nix
shipofharkinian.json
*.sav
.envrc
imgui.ini

View File

@ -0,0 +1,330 @@
#ifndef BenJsonConversions_hpp
#define BenJsonConversions_hpp
#include "z64.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
void to_json(json& j, const ItemEquips& itemEquips) {
j = json{
{ "buttonItems", itemEquips.buttonItems },
{ "cButtonSlots", itemEquips.cButtonSlots },
{ "equipment", itemEquips.equipment },
};
}
void from_json(const json& j, ItemEquips& itemEquips) {
j.at("equipment").get_to(itemEquips.equipment);
// buttonItems and cButtonSlots are arrays of arrays, so we need to manually parse them
for (int x = 0; x < 4; x++) {
for (int y = 0; y < 4; y++) {
itemEquips.buttonItems[x][y] = j.at("buttonItems")[x][y].get<u8>();
itemEquips.cButtonSlots[x][y] = j.at("cButtonSlots")[x][y].get<u8>();
}
}
}
void to_json(json& j, const Inventory& inventory) {
j = json{
{ "items", inventory.items },
{ "ammo", inventory.ammo },
{ "upgrades", inventory.upgrades },
{ "questItems", inventory.questItems },
{ "dungeonItems", inventory.dungeonItems },
{ "dungeonKeys", inventory.dungeonKeys },
{ "defenseHearts", inventory.defenseHearts },
{ "strayFairies", inventory.strayFairies },
{ "dekuPlaygroundPlayerName", inventory.dekuPlaygroundPlayerName },
};
}
void from_json(const json& j, Inventory& inventory) {
j.at("items").get_to(inventory.items);
j.at("ammo").get_to(inventory.ammo);
j.at("upgrades").get_to(inventory.upgrades);
j.at("questItems").get_to(inventory.questItems);
j.at("dungeonItems").get_to(inventory.dungeonItems);
j.at("dungeonKeys").get_to(inventory.dungeonKeys);
j.at("defenseHearts").get_to(inventory.defenseHearts);
j.at("strayFairies").get_to(inventory.strayFairies);
// dekuPlaygroundPlayerName is an array of char arrays, so we need to manually parse it
for (int i = 0; i < 3; i++) {
std::string name = j.at("dekuPlaygroundPlayerName")[i].get<std::string>();
for (int j = 0; j < 8; j++) {
inventory.dekuPlaygroundPlayerName[i][j] = name[j];
}
}
}
void to_json(json& j, const PermanentSceneFlags& permanentSceneFlags) {
j = json{
{ "chest", permanentSceneFlags.chest },
{ "switch0", permanentSceneFlags.switch0 },
{ "switch1", permanentSceneFlags.switch1 },
{ "clearedRoom", permanentSceneFlags.clearedRoom },
{ "collectible", permanentSceneFlags.collectible },
{ "unk_14", permanentSceneFlags.unk_14 },
{ "rooms", permanentSceneFlags.rooms },
};
}
void from_json(const json& j, PermanentSceneFlags& permanentSceneFlags) {
j.at("chest").get_to(permanentSceneFlags.chest);
j.at("switch0").get_to(permanentSceneFlags.switch0);
j.at("switch1").get_to(permanentSceneFlags.switch1);
j.at("clearedRoom").get_to(permanentSceneFlags.clearedRoom);
j.at("collectible").get_to(permanentSceneFlags.collectible);
j.at("unk_14").get_to(permanentSceneFlags.unk_14);
j.at("rooms").get_to(permanentSceneFlags.rooms);
}
void to_json(json& j, const SavePlayerData& savePlayerData) {
j = json{
{ "newf", savePlayerData.newf },
{ "threeDayResetCount", savePlayerData.threeDayResetCount },
{ "playerName", savePlayerData.playerName },
{ "healthCapacity", savePlayerData.healthCapacity },
{ "health", savePlayerData.health },
{ "magicLevel", savePlayerData.magicLevel },
{ "magic", savePlayerData.magic },
{ "rupees", savePlayerData.rupees },
{ "swordHealth", savePlayerData.swordHealth },
{ "tatlTimer", savePlayerData.tatlTimer },
{ "isMagicAcquired", savePlayerData.isMagicAcquired },
{ "isDoubleMagicAcquired", savePlayerData.isDoubleMagicAcquired },
{ "doubleDefense", savePlayerData.doubleDefense },
{ "unk_1F", savePlayerData.unk_1F },
{ "unk_20", savePlayerData.unk_20 },
{ "owlActivationFlags", savePlayerData.owlActivationFlags },
{ "unk_24", savePlayerData.unk_24 },
{ "savedSceneId", savePlayerData.savedSceneId },
};
}
void from_json(const json& j, SavePlayerData& savePlayerData) {
// newf is an array of chars, so we need to manually parse it
std::string newf = j.at("newf").get<std::string>();
for (int i = 0; i < 6; i++) {
savePlayerData.newf[i] = newf[i];
}
j.at("threeDayResetCount").get_to(savePlayerData.threeDayResetCount);
std::string playerName = j.at("playerName").get<std::string>();
for (int i = 0; i < 8; i++) {
savePlayerData.playerName[i] = playerName[i];
}
j.at("healthCapacity").get_to(savePlayerData.healthCapacity);
j.at("health").get_to(savePlayerData.health);
j.at("magicLevel").get_to(savePlayerData.magicLevel);
j.at("magic").get_to(savePlayerData.magic);
j.at("rupees").get_to(savePlayerData.rupees);
j.at("swordHealth").get_to(savePlayerData.swordHealth);
j.at("tatlTimer").get_to(savePlayerData.tatlTimer);
j.at("isMagicAcquired").get_to(savePlayerData.isMagicAcquired);
j.at("isDoubleMagicAcquired").get_to(savePlayerData.isDoubleMagicAcquired);
j.at("doubleDefense").get_to(savePlayerData.doubleDefense);
j.at("unk_1F").get_to(savePlayerData.unk_1F);
j.at("unk_20").get_to(savePlayerData.unk_20);
j.at("owlActivationFlags").get_to(savePlayerData.owlActivationFlags);
j.at("unk_24").get_to(savePlayerData.unk_24);
j.at("savedSceneId").get_to(savePlayerData.savedSceneId);
}
void to_json(json& j, const Vec3s& vec) {
j = json{
{ "x", vec.x },
{ "y", vec.y },
{ "z", vec.z },
};
}
void from_json(const json& j, Vec3s& vec) {
j.at("x").get_to(vec.x);
j.at("y").get_to(vec.y);
j.at("z").get_to(vec.z);
}
void to_json(json& j, const HorseData& horseData) {
j = json{
{ "sceneId", horseData.sceneId },
{ "pos", horseData.pos },
{ "yaw", horseData.yaw },
};
}
void from_json(const json& j, HorseData& horseData) {
j.at("sceneId").get_to(horseData.sceneId);
j.at("pos").get_to(horseData.pos);
j.at("yaw").get_to(horseData.yaw);
}
void to_json(json& j, const SaveInfo& saveInfo) {
j = json{
{ "playerData", saveInfo.playerData },
{ "equips", saveInfo.equips },
{ "inventory", saveInfo.inventory },
{ "permanentSceneFlags", saveInfo.permanentSceneFlags },
{ "unk_DF4", saveInfo.unk_DF4 },
{ "dekuPlaygroundHighScores", saveInfo.dekuPlaygroundHighScores },
{ "pictoFlags0", saveInfo.pictoFlags0 },
{ "pictoFlags1", saveInfo.pictoFlags1 },
{ "unk_E5C", saveInfo.unk_E5C },
{ "unk_E60", saveInfo.unk_E60 },
{ "unk_E64", saveInfo.unk_E64 },
{ "scenesVisible", saveInfo.scenesVisible },
{ "skullTokenCount", saveInfo.skullTokenCount },
{ "unk_EA0", saveInfo.unk_EA0 },
{ "unk_EA4", saveInfo.unk_EA4 },
{ "unk_EA8", saveInfo.unk_EA8 },
{ "stolenItems", saveInfo.stolenItems },
{ "unk_EB4", saveInfo.unk_EB4 },
{ "highScores", saveInfo.highScores },
{ "weekEventReg", saveInfo.weekEventReg },
{ "regionsVisited", saveInfo.regionsVisited },
{ "worldMapCloudVisibility", saveInfo.worldMapCloudVisibility },
{ "unk_F40", saveInfo.unk_F40 },
{ "scarecrowSpawnSongSet", saveInfo.scarecrowSpawnSongSet },
{ "scarecrowSpawnSong", saveInfo.scarecrowSpawnSong },
{ "bombersCaughtNum", saveInfo.bombersCaughtNum },
{ "bombersCaughtOrder", saveInfo.bombersCaughtOrder },
{ "lotteryCodes", saveInfo.lotteryCodes },
{ "spiderHouseMaskOrder", saveInfo.spiderHouseMaskOrder },
{ "bomberCode", saveInfo.bomberCode },
{ "horseData", saveInfo.horseData },
{ "checksum", saveInfo.checksum },
};
}
void from_json(const json& j, SaveInfo& saveInfo) {
j.at("playerData").get_to(saveInfo.playerData);
j.at("equips").get_to(saveInfo.equips);
j.at("inventory").get_to(saveInfo.inventory);
j.at("permanentSceneFlags").get_to(saveInfo.permanentSceneFlags);
j.at("unk_DF4").get_to(saveInfo.unk_DF4);
j.at("dekuPlaygroundHighScores").get_to(saveInfo.dekuPlaygroundHighScores);
j.at("pictoFlags0").get_to(saveInfo.pictoFlags0);
j.at("pictoFlags1").get_to(saveInfo.pictoFlags1);
j.at("unk_E5C").get_to(saveInfo.unk_E5C);
j.at("unk_E60").get_to(saveInfo.unk_E60);
j.at("unk_E64").get_to(saveInfo.unk_E64);
j.at("scenesVisible").get_to(saveInfo.scenesVisible);
j.at("skullTokenCount").get_to(saveInfo.skullTokenCount);
j.at("unk_EA0").get_to(saveInfo.unk_EA0);
j.at("unk_EA4").get_to(saveInfo.unk_EA4);
j.at("unk_EA8").get_to(saveInfo.unk_EA8);
j.at("stolenItems").get_to(saveInfo.stolenItems);
j.at("unk_EB4").get_to(saveInfo.unk_EB4);
j.at("highScores").get_to(saveInfo.highScores);
j.at("weekEventReg").get_to(saveInfo.weekEventReg);
j.at("regionsVisited").get_to(saveInfo.regionsVisited);
j.at("worldMapCloudVisibility").get_to(saveInfo.worldMapCloudVisibility);
j.at("unk_F40").get_to(saveInfo.unk_F40);
j.at("scarecrowSpawnSongSet").get_to(saveInfo.scarecrowSpawnSongSet);
j.at("scarecrowSpawnSong").get_to(saveInfo.scarecrowSpawnSong);
j.at("bombersCaughtNum").get_to(saveInfo.bombersCaughtNum);
j.at("bombersCaughtOrder").get_to(saveInfo.bombersCaughtOrder);
// lotteryCodes is an array of arrays, so we need to manually parse it
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
saveInfo.lotteryCodes[x][y] = j.at("lotteryCodes")[x][y].get<s8>();
}
}
j.at("spiderHouseMaskOrder").get_to(saveInfo.spiderHouseMaskOrder);
j.at("bomberCode").get_to(saveInfo.bomberCode);
j.at("horseData").get_to(saveInfo.horseData);
j.at("checksum").get_to(saveInfo.checksum);
}
void to_json(json& j, const Save& save) {
j = json{
{ "entrance", save.entrance },
{ "equippedMask", save.equippedMask },
{ "isFirstCycle", save.isFirstCycle },
{ "unk_06", save.unk_06 },
{ "linkAge", save.linkAge },
{ "cutsceneIndex", save.cutsceneIndex },
{ "time", save.time },
{ "owlSaveLocation", save.owlSaveLocation },
{ "isNight", save.isNight },
{ "timeSpeedOffset", save.timeSpeedOffset },
{ "day", save.day },
{ "eventDayCount", save.eventDayCount },
{ "playerForm", save.playerForm },
{ "snowheadCleared", save.snowheadCleared },
{ "hasTatl", save.hasTatl },
{ "isOwlSave", save.isOwlSave },
{ "saveInfo", save.saveInfo },
};
}
void from_json(const json& j, Save& save) {
j.at("entrance").get_to(save.entrance);
j.at("equippedMask").get_to(save.equippedMask);
j.at("isFirstCycle").get_to(save.isFirstCycle);
j.at("unk_06").get_to(save.unk_06);
j.at("linkAge").get_to(save.linkAge);
j.at("cutsceneIndex").get_to(save.cutsceneIndex);
j.at("time").get_to(save.time);
j.at("owlSaveLocation").get_to(save.owlSaveLocation);
j.at("isNight").get_to(save.isNight);
j.at("timeSpeedOffset").get_to(save.timeSpeedOffset);
j.at("day").get_to(save.day);
j.at("eventDayCount").get_to(save.eventDayCount);
j.at("playerForm").get_to(save.playerForm);
j.at("snowheadCleared").get_to(save.snowheadCleared);
j.at("hasTatl").get_to(save.hasTatl);
j.at("isOwlSave").get_to(save.isOwlSave);
j.at("saveInfo").get_to(save.saveInfo);
}
void to_json(json& j, const SaveContext& saveContext) {
j = json{
{ "save", saveContext.save },
{ "eventInf", saveContext.eventInf },
{ "unk_1014", saveContext.unk_1014 },
{ "bButtonStatus", saveContext.bButtonStatus },
{ "jinxTimer", saveContext.jinxTimer },
{ "rupeeAccumulator", saveContext.rupeeAccumulator },
{ "bottleTimerStates", saveContext.bottleTimerStates },
{ "bottleTimerStartOsTimes", saveContext.bottleTimerStartOsTimes },
{ "bottleTimerTimeLimits", saveContext.bottleTimerTimeLimits },
{ "bottleTimerCurTimes", saveContext.bottleTimerCurTimes },
{ "bottleTimerPausedOsTimes", saveContext.bottleTimerPausedOsTimes },
{ "pictoPhotoI5", saveContext.pictoPhotoI5 },
};
}
void from_json(const json& j, SaveContext& saveContext) {
j.at("save").get_to(saveContext.save);
j.at("eventInf").get_to(saveContext.eventInf);
j.at("unk_1014").get_to(saveContext.unk_1014);
j.at("bButtonStatus").get_to(saveContext.bButtonStatus);
j.at("jinxTimer").get_to(saveContext.jinxTimer);
j.at("rupeeAccumulator").get_to(saveContext.rupeeAccumulator);
j.at("bottleTimerStates").get_to(saveContext.bottleTimerStates);
j.at("bottleTimerStartOsTimes").get_to(saveContext.bottleTimerStartOsTimes);
j.at("bottleTimerTimeLimits").get_to(saveContext.bottleTimerTimeLimits);
j.at("bottleTimerCurTimes").get_to(saveContext.bottleTimerCurTimes);
j.at("bottleTimerPausedOsTimes").get_to(saveContext.bottleTimerPausedOsTimes);
j.at("pictoPhotoI5").get_to(saveContext.pictoPhotoI5);
}
void to_json(json& j, const SaveOptions& saveOptions) {
j = json{
{ "optionId", saveOptions.optionId },
{ "language", saveOptions.language },
{ "audioSetting", saveOptions.audioSetting },
{ "languageSetting", saveOptions.languageSetting },
{ "zTargetSetting", saveOptions.zTargetSetting },
};
}
void from_json(const json& j, SaveOptions& saveOptions) {
j.at("optionId").get_to(saveOptions.optionId);
j.at("language").get_to(saveOptions.language);
j.at("audioSetting").get_to(saveOptions.audioSetting);
j.at("languageSetting").get_to(saveOptions.languageSetting);
j.at("zTargetSetting").get_to(saveOptions.zTargetSetting);
}
#endif // BenJsonConversions_hpp

View File

@ -30,6 +30,7 @@
#include "z64.h"
#include "macros.h"
#include <Utils/StringHelper.h>
#include <nlohmann/json.hpp>
#include <Fast3D/gfx_pc.h>
#include <Fast3D/gfx_rendering_api.h>
@ -51,6 +52,7 @@ CrowdControl* CrowdControl::Instance;
#include <libultraship/libultraship.h>
#include <BenGui.hpp>
#include "BenJsonConversions.hpp"
#include "Enhancements/controls/SohInputEditorWindow.h"
@ -1384,3 +1386,193 @@ extern "C" int Controller_ShouldRumble(size_t slot) {
return 0;
}
// This entire thing is temporary until we have a more robust save system that
// supports backwards compatability, migrations, threaded saving, save sections, etc.
typedef enum FlashSlotFile {
/* -1 */ FLASH_SLOT_FILE_UNAVAILABLE = -1,
/* 0 */ FLASH_SLOT_FILE_1_NEW_CYCLE,
/* 1 */ FLASH_SLOT_FILE_1_NEW_CYCLE_BACKUP,
/* 2 */ FLASH_SLOT_FILE_2_NEW_CYCLE,
/* 3 */ FLASH_SLOT_FILE_2_NEW_CYCLE_BACKUP,
/* 4 */ FLASH_SLOT_FILE_1_OWL_SAVE,
/* 5 */ FLASH_SLOT_FILE_1_OWL_SAVE_BACKUP,
/* 6 */ FLASH_SLOT_FILE_2_OWL_SAVE,
/* 7 */ FLASH_SLOT_FILE_2_OWL_SAVE_BACKUP,
/* 8 */ FLASH_SLOT_FILE_SRAM_HEADER,
/* 9 */ FLASH_SLOT_FILE_SRAM_HEADER_BACKUP,
} FlashSlotFile;
#define GET_NEWF(save, index) (save.saveInfo.playerData.newf[index])
#define IS_VALID_FILE(save) \
((GET_NEWF(save, 0) == 'Z') && (GET_NEWF(save, 1) == 'E') && \
(GET_NEWF(save, 2) == 'L') && (GET_NEWF(save, 3) == 'D') && \
(GET_NEWF(save, 4) == 'A') && (GET_NEWF(save, 5) == '3'))
const std::filesystem::path savesFolderPath(LUS::Context::GetPathRelativeToAppDirectory("Save"));
void WriteSaveFile(std::filesystem::path fileName, nlohmann::json j) {
const std::filesystem::path filePath = savesFolderPath / fileName;
if (!std::filesystem::exists(savesFolderPath)) {
std::filesystem::create_directory(savesFolderPath);
}
std::ofstream o(filePath);
o << std::setw(4) << j << std::endl;
o.close();
}
void DeleteSaveFile(std::filesystem::path fileName) {
const std::filesystem::path filePath = savesFolderPath / fileName;
if (std::filesystem::exists(filePath)) {
std::filesystem::remove(filePath);
}
}
int ReadSaveFile(std::filesystem::path fileName, nlohmann::json& j) {
const std::filesystem::path filePath = savesFolderPath / fileName;
if (!std::filesystem::exists(filePath)) {
return -1;
}
std::ifstream i(filePath);
i >> j;
i.close();
return 0;
}
extern "C" void BenSysFlashrom_WriteData(u8* saveBuffer, u32 pageNum, u32 pageCount) {
FlashSlotFile flashSlotFile = FLASH_SLOT_FILE_UNAVAILABLE;
bool isBackup = false;
for (u32 i = 0; i < ARRAY_COUNT(gFlashSaveStartPages) - 1; i++) {
if (pageNum == gFlashSaveStartPages[i]) {
flashSlotFile = static_cast<FlashSlotFile>(i);
break;
}
}
if (flashSlotFile == FLASH_SLOT_FILE_UNAVAILABLE) {
return;
}
switch (flashSlotFile) {
case FLASH_SLOT_FILE_1_NEW_CYCLE_BACKUP:
case FLASH_SLOT_FILE_2_NEW_CYCLE_BACKUP:
isBackup = true;
// fallthrough
case FLASH_SLOT_FILE_1_NEW_CYCLE:
case FLASH_SLOT_FILE_2_NEW_CYCLE: {
Save save;
memcpy(&save, saveBuffer, sizeof(Save));
std::string fileName = "save_" + std::to_string(flashSlotFile) + ".sav";
if (isBackup) fileName += ".bak";
if (IS_VALID_FILE(save)) {
WriteSaveFile(fileName, save);
} else {
DeleteSaveFile(fileName);
}
break;
}
case FLASH_SLOT_FILE_1_OWL_SAVE_BACKUP:
case FLASH_SLOT_FILE_2_OWL_SAVE_BACKUP:
isBackup = true;
// fallthrough
case FLASH_SLOT_FILE_1_OWL_SAVE:
case FLASH_SLOT_FILE_2_OWL_SAVE: {
SaveContext saveContext;
memcpy(&saveContext, saveBuffer, sizeof(SaveContext));
std::string fileName = "save_" + std::to_string(flashSlotFile) + ".sav";
if (isBackup) fileName += ".bak";
if (IS_VALID_FILE(saveContext.save)) {
WriteSaveFile(fileName, saveContext);
} else {
DeleteSaveFile(fileName);
}
break;
}
case FLASH_SLOT_FILE_SRAM_HEADER_BACKUP:
case FLASH_SLOT_FILE_SRAM_HEADER: {
SaveOptions saveOptions;
memcpy(&saveOptions, saveBuffer, sizeof(SaveOptions));
std::string fileName = "global.sav";
WriteSaveFile(fileName, saveOptions);
break;
}
}
}
extern "C" s32 BenSysFlashrom_ReadData(void* saveBuffer, u32 pageNum, u32 pageCount) {
FlashSlotFile flashSlotFile = FLASH_SLOT_FILE_UNAVAILABLE;
bool isBackup = false;
for (u32 i = 0; i < ARRAY_COUNT(gFlashSaveStartPages) - 1; i++) {
if (pageNum == gFlashSaveStartPages[i]) {
flashSlotFile = static_cast<FlashSlotFile>(i);
break;
}
}
if (flashSlotFile == FLASH_SLOT_FILE_UNAVAILABLE) {
return -1;
}
switch (flashSlotFile) {
case FLASH_SLOT_FILE_1_NEW_CYCLE_BACKUP:
case FLASH_SLOT_FILE_2_NEW_CYCLE_BACKUP:
isBackup = true;
// fallthrough
case FLASH_SLOT_FILE_1_NEW_CYCLE:
case FLASH_SLOT_FILE_2_NEW_CYCLE: {
std::string fileName = "save_" + std::to_string(flashSlotFile) + ".sav";
if (isBackup) fileName += ".bak";
nlohmann::json j;
int result = ReadSaveFile(fileName, j);
if (result != 0) return result;
Save save = j;
memcpy(saveBuffer, &save, sizeof(Save));
return result;
}
case FLASH_SLOT_FILE_1_OWL_SAVE_BACKUP:
case FLASH_SLOT_FILE_2_OWL_SAVE_BACKUP:
isBackup = true;
// fallthrough
case FLASH_SLOT_FILE_1_OWL_SAVE:
case FLASH_SLOT_FILE_2_OWL_SAVE: {
std::string fileName = "save_" + std::to_string(flashSlotFile) + ".sav";
if (isBackup) fileName += ".bak";
nlohmann::json j;
int result = ReadSaveFile(fileName, j);
if (result != 0) return result;
SaveContext saveContext = j;
memcpy(saveBuffer, &saveContext, sizeof(SaveContext));
return result;
}
case FLASH_SLOT_FILE_SRAM_HEADER:
case FLASH_SLOT_FILE_SRAM_HEADER_BACKUP: {
std::string fileName = "global.sav";
nlohmann::json j;
int result = ReadSaveFile(fileName, j);
if (result != 0) return result;
SaveOptions saveOptions = j;
memcpy(saveBuffer, &saveOptions, sizeof(SaveOptions));
return 0;
break;
}
}
}

View File

@ -125,6 +125,8 @@ void Overlay_DisplayText_Seconds(int seconds, const char* text);
void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
void CheckTracker_OnMessageClose();
void BenSysFlashrom_WriteData(u8* addr, u32 pageNum, u32 pageCount);
s32 BenSysFlashrom_ReadData(void* addr, u32 pageNum, u32 pageCount);
int32_t GetGIID(uint32_t itemID);
#endif

View File

@ -124,7 +124,7 @@ source_group("include" FILES ${Header_Files__include})
# }}}
# m (root)
file(GLOB soh__ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "2s2h/*.c" "2s2h/*.cpp" "2s2h/*.h")
file(GLOB soh__ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "2s2h/*.c" "2s2h/*.cpp" "2s2h/*.h" "2s2h/*.hpp")
source_group("2s2h" FILES ${soh__})
file(GLOB_RECURSE libultra_headers RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}

View File

@ -1685,7 +1685,7 @@ void Sram_UpdateWriteToFlashOwlSave(SramContext* sramCtx);
extern u32 gSramSlotOffsets[];
extern u8 gAmmoItems[];
extern s32 gFlashSaveStartPages[];
extern s32 gFlashSaveStartPages[10];
extern s32 gFlashSaveNumPages[];
extern s32 gFlashSpecialSaveNumPages[];
extern s32 gFlashOwlSaveStartPages[];

View File

@ -386,6 +386,9 @@ void* osViGetCurrentFramebuffer(void) {
OSPiHandle* osFlashInit(void) {
}
void osFlashReadId(u32* t, u32* v) {
// We're faking these so the flashrom system will continue to work
*t = 0x11118001; // FLASH_TYPE_MAGIC
*v = 0x00C20000; // FLASH_VERSION_MX_PROTO_A
}
s32 osFlashSectorErase(u32 page) {
}

View File

@ -7,6 +7,7 @@
#include "z64thread.h"
#include "sys_flashrom.h"
#include "PR/os_internal_flash.h"
#include "BenPort.h"
OSMesgQueue sFlashromMesgQueue;
OSMesg sFlashromMesg[1];
@ -78,6 +79,10 @@ s32 SysFlashrom_InitFlash(void) {
}
s32 SysFlashrom_ReadData(void* addr, u32 pageNum, u32 pageCount) {
// #region 2S2H [Port] Redirect to our own read function
return BenSysFlashrom_ReadData(addr, pageNum, pageCount);
// #endregion
OSIoMesg msg;
if (!SysFlashrom_IsInit()) {
@ -201,25 +206,27 @@ s32 SysFlashrom_WriteData(void* addr, u32 pageNum, u32 pageCount) {
}
void SysFlashrom_ThreadEntry(void* arg) {
#if 0
FlashromRequest* req = (FlashromRequest*)arg;
switch (req->requestType) {
case FLASHROM_REQUEST_WRITE:
req->response = SysFlashrom_WriteData(req->addr, req->pageNum, req->pageCount);
osSendMesg(&req->messageQueue, (OSMesg)req->response, OS_MESG_BLOCK);
osSendMesg(&req->messageQueue, OS_MESG_32(req->response), OS_MESG_BLOCK);
break;
case FLASHROM_REQUEST_READ:
req->response = SysFlashrom_ReadData(req->addr, req->pageNum, req->pageCount);
osSendMesg(&req->messageQueue, (OSMesg)req->response, OS_MESG_BLOCK);
osSendMesg(&req->messageQueue, OS_MESG_32(req->response), OS_MESG_BLOCK);
break;
}
#endif
}
void SysFlashrom_WriteDataAsync(u8* addr, u32 pageNum, u32 pageCount) {
#if 0
// #region 2S2H [Port] Redirect to our own write function
BenSysFlashrom_WriteData(addr, pageNum, pageCount);
return;
// #endregion
FlashromRequest* req = &sFlashromRequest;
if (SysFlashrom_IsInit()) {
req->requestType = FLASHROM_REQUEST_WRITE;
@ -233,28 +240,34 @@ void SysFlashrom_WriteDataAsync(u8* addr, u32 pageNum, u32 pageCount) {
STACK_TOP(sSysFlashromStack), Z_PRIORITY_FLASHROM);
osStartThread(&sSysFlashromThread);
}
#endif
}
s32 SysFlashrom_IsBusy(void) {
return 0;
//OSMesgQueue* queue = &sFlashromRequest.messageQueue;
//
//if (!SysFlashrom_IsInit()) {
// return -1;
//}
//return MQ_IS_FULL(queue);
// #region 2S2H [Port] This is only checked in 3 places in which we always want to tell it we've "started working"
// Eventually we should probably have this call into BenPort and check if a save is in progress
return 1;
// #endregion
OSMesgQueue* queue = &sFlashromRequest.messageQueue;
if (!SysFlashrom_IsInit()) {
return -1;
}
return MQ_IS_FULL(queue);
}
s32 SysFlashrom_AwaitResult(void) {
// #region 2S2H [Port] Currently all of our save writes/reads are synchronous, so we don't need to wait for anything
return 0;
//if (!SysFlashrom_IsInit()) {
// return -1;
//}
//osRecvMesg(&sFlashromRequest.messageQueue, NULL, OS_MESG_BLOCK);
//osDestroyThread(&sSysFlashromThread);
//StackCheck_Cleanup(&sSysFlashromStackInfo);
//return sFlashromRequest.response;
// #endregion
if (!SysFlashrom_IsInit()) {
return -1;
}
osRecvMesg(&sFlashromRequest.messageQueue, NULL, OS_MESG_BLOCK);
osDestroyThread(&sSysFlashromThread);
StackCheck_Cleanup(&sSysFlashromStackInfo);
return sFlashromRequest.response;
}
void SysFlashrom_WriteDataSync(void* addr, u32 pageNum, u32 pageCount) {

View File

@ -49,8 +49,7 @@ void Setup_SetRegs(void) {
void Setup_InitImpl(SetupState* this) {
SysFlashrom_InitFlash();
// BENTODO: this doesn't crash but was stubbed in minibuild? probably just for debug purposes
// SaveContext_Init();
SaveContext_Init();
Setup_SetRegs();
STOP_GAMESTATE(&this->state);

View File

@ -300,7 +300,7 @@ u8 gAmmoItems[ITEM_NUM_SLOTS] = {
};
// Stores flash start page number
s32 gFlashSaveStartPages[] = {
s32 gFlashSaveStartPages[10] = {
0, // File 1 New Cycle Save
0x40, // File 1 New Cycle Save Backup
0x80, // File 2 New Cycle Save
@ -667,6 +667,10 @@ void Sram_IncrementDay(void) {
}
u16 Sram_CalcChecksum(void* data, size_t count) {
// #region 2S2H [Port] I'm not really sure what this is doing or how to port it, for now always return the same
return 1;
// #endregion
u8* dataPtr = data;
u16 chkSum = 0;

View File

@ -145,9 +145,6 @@ void ConsoleLogo_Main(GameState* thisx) {
// #region 2S2H [Debug] Eventually we'll get rid of this
if (CVarGetInteger("gDebugEnabled", 0)) {
gSaveContext.gameMode = GAMEMODE_NORMAL;
gSaveContext.nextDayTime = NEXT_TIME_NONE;
gSaveContext.nextTransitionType = TRANS_NEXT_TYPE_DEFAULT;
gSaveContext.prevHudVisibility = HUD_VISIBILITY_ALL;
SET_NEXT_GAMESTATE(&this->state, MapSelect_Init, sizeof(MapSelectState));
// #endregion
} else {