diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 2d1f6e68b..e61cf9223 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -47,6 +47,11 @@ std::filesystem::path SaveManager::GetFileName(int fileNum) { return sSavePath / ("file" + std::to_string(fileNum + 1) + ".sav"); } +std::filesystem::path SaveManager::GetFileTempName(int fileNum) { + const std::filesystem::path sSavePath(LUS::Context::GetPathRelativeToAppDirectory("Save")); + return sSavePath / ("file" + std::to_string(fileNum + 1) + ".temp"); +} + SaveManager::SaveManager() { coreSectionIDsByName["base"] = SECTION_ID_BASE; coreSectionIDsByName["randomizer"] = SECTION_ID_RANDOMIZER; @@ -874,6 +879,32 @@ void SaveManager::InitFileMaxed() { gSaveContext.sceneFlags[5].swch = 0x40000000; } +#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) { + alignas(0x40) uint8_t buf[4096]; + FILE* r = fopen(src, "r"); + if (!r) { + return -1; + } + FILE* w = fopen(dst, "w"); + if (!w) { + return -2; + } + + size_t res; + while ((res = fread(buf, 1, sizeof(buf), r)) > 0) { + if (fwrite(buf, 1, res, w) != res) { + break; + } + } + + fclose(r); + fclose(w); + return res >= 0 ? 0 : res; +} +#endif + // Threaded SaveFile takes copy of gSaveContext for local unmodified storage void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int sectionID) { @@ -915,19 +946,42 @@ void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int se svi.func(saveContext, sectionID, false); } + std::filesystem::path fileName = GetFileName(fileNum); + std::filesystem::path tempFile = GetFileTempName(fileNum); + + if (std::filesystem::exists(tempFile)) { + std::filesystem::remove(tempFile); + } + #if defined(__SWITCH__) || defined(__WIIU__) - FILE* w = fopen(GetFileName(fileNum).c_str(), "w"); + FILE* w = fopen(tempFile.c_str(), "w"); 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)); + std::ofstream output(tempFile); output << std::setw(4) << saveBlock << std::endl; + output.close(); #endif + if (std::filesystem::exists(fileName)) { + std::filesystem::remove(fileName); + } + +#if defined(__SWITCH__) || defined(__WIIU__) + copy_file(tempFile.c_str(), fileName.c_str()); +#else + std::filesystem::copy_file(tempFile, fileName); +#endif + + if (std::filesystem::exists(tempFile)) { + std::filesystem::remove(tempFile); + } + delete saveContext; InitMeta(fileNum); GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSaveFile>(fileNum); + SPDLOG_INFO("Save File Finish - fileNum: {}", fileNum); } // SaveSection creates a copy of gSaveContext to prevent mid-save data modification, and passes its reference to SaveFileThreaded @@ -2110,32 +2164,6 @@ 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) { - alignas(0x40) uint8_t buf[4096]; - FILE* r = fopen(src, "r"); - if (!r) { - return -1; - } - FILE* w = fopen(dst, "w"); - if (!w) { - return -2; - } - - size_t res; - while ((res = fread(buf, 1, sizeof(buf), r)) > 0) { - if (fwrite(buf, 1, res, w) != res) { - break; - } - } - - fclose(r); - fclose(w); - return res >= 0 ? 0 : res; -} -#endif - void SaveManager::CopyZeldaFile(int from, int to) { assert(std::filesystem::exists(GetFileName(from))); DeleteZeldaFile(to); diff --git a/soh/soh/SaveManager.h b/soh/soh/SaveManager.h index 892aec923..310ecda7d 100644 --- a/soh/soh/SaveManager.h +++ b/soh/soh/SaveManager.h @@ -142,6 +142,7 @@ class SaveManager { private: std::filesystem::path GetFileName(int fileNum); + std::filesystem::path GetFileTempName(int fileNum); nlohmann::json saveBlock; void ConvertFromUnversioned();