From 1fcb560fabfc8dc05ad5430a1e8ddc7eaff26d42 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 15 Mar 2025 19:01:17 +0000 Subject: [PATCH 1/5] Fix DLC info message always being displayed --- UnleashedRecomp/CMakeLists.txt | 2 + .../api/SWA/System/ApplicationDocument.h | 10 +- .../api/SWA/System/GameParameter.h | 10 +- UnleashedRecomp/main.cpp | 2 + UnleashedRecomp/patches/misc_patches.cpp | 21 +++ UnleashedRecomp/patches/resident_patches.cpp | 12 +- UnleashedRecomp/user/achievement_manager.h | 3 +- UnleashedRecomp/user/persistent_data.cpp | 18 +++ UnleashedRecomp/user/persistent_data.h | 52 ++++++++ .../user/persistent_storage_manager.cpp | 123 ++++++++++++++++++ .../user/persistent_storage_manager.h | 30 +++++ 11 files changed, 274 insertions(+), 9 deletions(-) create mode 100644 UnleashedRecomp/user/persistent_data.cpp create mode 100644 UnleashedRecomp/user/persistent_data.h create mode 100644 UnleashedRecomp/user/persistent_storage_manager.cpp create mode 100644 UnleashedRecomp/user/persistent_storage_manager.h diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 66b60a44..c7ba186f 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -183,6 +183,8 @@ set(UNLEASHED_RECOMP_USER_CXX_SOURCES "user/config.cpp" "user/registry.cpp" "user/paths.cpp" + "user/persistent_data.cpp" + "user/persistent_storage_manager.cpp" ) set(UNLEASHED_RECOMP_MOD_CXX_SOURCES diff --git a/UnleashedRecomp/api/SWA/System/ApplicationDocument.h b/UnleashedRecomp/api/SWA/System/ApplicationDocument.h index 21949711..1e6ab27f 100644 --- a/UnleashedRecomp/api/SWA/System/ApplicationDocument.h +++ b/UnleashedRecomp/api/SWA/System/ApplicationDocument.h @@ -80,9 +80,13 @@ namespace SWA boost::shared_ptr m_spRenderScene; SWA_INSERT_PADDING(0x04); boost::shared_ptr m_spGameParameter; - SWA_INSERT_PADDING(0x78); + SWA_INSERT_PADDING(0x0C); + boost::anonymous_shared_ptr m_spItemParamManager; + SWA_INSERT_PADDING(0x64); boost::shared_ptr m_spCriticalSection; - SWA_INSERT_PADDING(0x20); + SWA_INSERT_PADDING(0x14); + bool m_ShowDLCInfo; + SWA_INSERT_PADDING(0x08); }; // TODO: Hedgehog::Base::TSynchronizedPtr @@ -111,7 +115,9 @@ namespace SWA SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_Field10C, 0x10C); SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spRenderScene, 0x12C); SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spGameParameter, 0x138); + SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spItemParamManager, 0x14C); SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spCriticalSection, 0x1B8); + SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_ShowDLCInfo, 0x1D4); SWA_ASSERT_SIZEOF(CApplicationDocument::CMember, 0x1E0); SWA_ASSERT_OFFSETOF(CApplicationDocument, m_pMember, 0x04); diff --git a/UnleashedRecomp/api/SWA/System/GameParameter.h b/UnleashedRecomp/api/SWA/System/GameParameter.h index 95618144..da6b3735 100644 --- a/UnleashedRecomp/api/SWA/System/GameParameter.h +++ b/UnleashedRecomp/api/SWA/System/GameParameter.h @@ -7,11 +7,19 @@ namespace SWA class CGameParameter // : public Hedgehog::Universe::CMessageActor { public: - struct SSaveData; + struct SSaveData + { + SWA_INSERT_PADDING(0x8600); + be DLCFlags[8]; + SWA_INSERT_PADDING(0x15C); + }; + struct SStageParameter; SWA_INSERT_PADDING(0x94); xpointer m_pSaveData; xpointer m_pStageParameter; }; + + SWA_ASSERT_OFFSETOF(CGameParameter::SSaveData, DLCFlags, 0x8600); } diff --git a/UnleashedRecomp/main.cpp b/UnleashedRecomp/main.cpp index a55c0181..ccf8b81c 100644 --- a/UnleashedRecomp/main.cpp +++ b/UnleashedRecomp/main.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -212,6 +213,7 @@ int main(int argc, char *argv[]) } Config::Load(); + PersistentStorageManager::LoadBinary(); #if defined(_WIN32) && defined(UNLEASHED_RECOMP_D3D12) for (auto& dll : g_D3D12RequiredModules) diff --git a/UnleashedRecomp/patches/misc_patches.cpp b/UnleashedRecomp/patches/misc_patches.cpp index 9f318fb4..78d5e085 100644 --- a/UnleashedRecomp/patches/misc_patches.cpp +++ b/UnleashedRecomp/patches/misc_patches.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include void AchievementManagerUnlockMidAsmHook(PPCRegister& id) @@ -156,3 +157,23 @@ void DisableBoostFilterMidAsmHook(PPCRegister& r11) r11.u32 = 0; } } + +// DLC save data flag check. +// +// The DLC checks are fundamentally broken in this game, resulting in this method always +// returning true and displaying the DLC info message when it shouldn't be. +// +// The original intent here seems to have been to display the message every time new DLC +// content is installed, but the flags in the save data never get written to properly, +// causing this function to always pass in some way. +// +// We bypass the save data completely and write to external persistent storage to store +// whether we've seen the DLC info message instead. This way we can retain the original +// broken game behaviour, whilst also providing a fix for this issue that is safe. +PPC_FUNC_IMPL(__imp__sub_824EE620); +PPC_FUNC(sub_824EE620) +{ + __imp__sub_824EE620(ctx, base); + + ctx.r3.u32 = PersistentStorageManager::ShouldDisplayDLCMessage(true); +} diff --git a/UnleashedRecomp/patches/resident_patches.cpp b/UnleashedRecomp/patches/resident_patches.cpp index 64a220d7..c421946c 100644 --- a/UnleashedRecomp/patches/resident_patches.cpp +++ b/UnleashedRecomp/patches/resident_patches.cpp @@ -2,11 +2,10 @@ #include #include #include +#include #include #include -bool m_isSavedAchievementData = false; - // SWA::Message::MsgRequestStartLoading::Impl PPC_FUNC_IMPL(__imp__sub_824DCF38); PPC_FUNC(sub_824DCF38) @@ -99,20 +98,23 @@ PPC_FUNC(sub_824E5170) App::s_isSaving = pSaveIcon->m_IsVisible; + static bool isSavedExtraData = false; + if (pSaveIcon->m_IsVisible) { App::s_isSaveDataCorrupt = false; - if (!m_isSavedAchievementData) + if (!isSavedExtraData) { AchievementManager::Save(); + PersistentStorageManager::SaveBinary(); - m_isSavedAchievementData = true; + isSavedExtraData = true; } } else { - m_isSavedAchievementData = false; + isSavedExtraData = false; } } diff --git a/UnleashedRecomp/user/achievement_manager.h b/UnleashedRecomp/user/achievement_manager.h index eab9c829..57317fd5 100644 --- a/UnleashedRecomp/user/achievement_manager.h +++ b/UnleashedRecomp/user/achievement_manager.h @@ -4,6 +4,7 @@ enum class EAchStatus { + Unknown, Success, IOError, BadFileSize, @@ -16,7 +17,7 @@ class AchievementManager { public: static inline AchievementData Data{}; - static inline EAchStatus Status{}; + static inline EAchStatus Status{ EAchStatus::Unknown }; static std::filesystem::path GetDataPath(bool checkForMods) { diff --git a/UnleashedRecomp/user/persistent_data.cpp b/UnleashedRecomp/user/persistent_data.cpp new file mode 100644 index 00000000..1bdd75b8 --- /dev/null +++ b/UnleashedRecomp/user/persistent_data.cpp @@ -0,0 +1,18 @@ +#include "persistent_data.h" + +bool PersistentData::VerifySignature() const +{ + char sig[4] = EXT_SIGNATURE; + + return memcmp(Header.Signature, sig, sizeof(Header.Signature)) == 0; +} + +bool PersistentData::VerifyVersion() const +{ + return Header.Version == ExtVersion EXT_VERSION; +} + +bool PersistentData::VerifyHeader() const +{ + return Header.HeaderSize == sizeof(ExtHeader); +} diff --git a/UnleashedRecomp/user/persistent_data.h b/UnleashedRecomp/user/persistent_data.h new file mode 100644 index 00000000..5a8dd0ec --- /dev/null +++ b/UnleashedRecomp/user/persistent_data.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +#define EXT_FILENAME "EXT-DATA" +#define EXT_SIGNATURE { 'E', 'X', 'T', ' ' } +#define EXT_VERSION { 1, 0, 0 } + +enum class EDLCFlag +{ + ApotosAndShamar, + Spagonia, + Chunnan, + Mazuri, + Holoska, + EmpireCityAndAdabat, + Count +}; + +class PersistentData +{ +public: + struct ExtVersion + { + uint8_t Major; + uint8_t Minor; + uint8_t Revision; + uint8_t Reserved; + + bool operator==(const ExtVersion& other) const + { + return Major == other.Major && + Minor == other.Minor && + Revision == other.Revision; + } + }; + + struct ExtHeader + { + char Signature[4] EXT_SIGNATURE; + ExtVersion Version EXT_VERSION; + uint32_t HeaderSize{ sizeof(ExtHeader) }; + uint32_t Reserved; + }; + + ExtHeader Header; + bool DLCFlags[6]; + + bool VerifySignature() const; + bool VerifyVersion() const; + bool VerifyHeader() const; +}; diff --git a/UnleashedRecomp/user/persistent_storage_manager.cpp b/UnleashedRecomp/user/persistent_storage_manager.cpp new file mode 100644 index 00000000..8883ba6a --- /dev/null +++ b/UnleashedRecomp/user/persistent_storage_manager.cpp @@ -0,0 +1,123 @@ +#include "persistent_storage_manager.h" +#include +#include +#include + +bool PersistentStorageManager::ShouldDisplayDLCMessage(bool setOffendingDLCFlag) +{ + auto result = false; + + if (BinStatus != EBinStatus::Success) + return result; + + static std::unordered_map flags = + { + { EDLCFlag::ApotosAndShamar, DLC::ApotosShamar }, + { EDLCFlag::Spagonia, DLC::Spagonia }, + { EDLCFlag::Chunnan, DLC::Chunnan }, + { EDLCFlag::Mazuri, DLC::Mazuri }, + { EDLCFlag::Holoska, DLC::Holoska }, + { EDLCFlag::EmpireCityAndAdabat, DLC::EmpireCityAdabat } + }; + + for (auto& pair : flags) + { + if (!Data.DLCFlags[(int)pair.first] && Installer::checkDLCInstall(GetGamePath(), pair.second)) + { + if (setOffendingDLCFlag) + Data.DLCFlags[(int)pair.first] = true; + + result = true; + } + } + + return result; +} + +void PersistentStorageManager::LoadBinary() +{ + BinStatus = EBinStatus::Success; + + auto dataPath = GetDataPath(true); + + if (!std::filesystem::exists(dataPath)) + { + // Try loading base persistent data as fallback. + dataPath = GetDataPath(false); + + if (!std::filesystem::exists(dataPath)) + return; + } + + std::error_code ec; + auto fileSize = std::filesystem::file_size(dataPath, ec); + auto dataSize = sizeof(PersistentData); + + if (fileSize != dataSize) + { + BinStatus = EBinStatus::BadFileSize; + return; + } + + std::ifstream file(dataPath, std::ios::binary); + + if (!file) + { + BinStatus = EBinStatus::IOError; + return; + } + + PersistentData data{}; + + file.read((char*)&data.Header.Signature, sizeof(data.Header.Signature)); + + if (!data.VerifySignature()) + { + BinStatus = EBinStatus::BadSignature; + file.close(); + return; + } + + file.read((char*)&data.Header.Version, sizeof(data.Header.Version)); + + // TODO: upgrade in future if the version changes. + if (!data.VerifyVersion()) + { + BinStatus = EBinStatus::BadVersion; + file.close(); + return; + } + + file.read((char*)&data.Header.HeaderSize, sizeof(data.Header.HeaderSize)); + + if (!data.VerifyHeader()) + { + BinStatus = EBinStatus::BadHeader; + file.close(); + return; + } + + file.seekg(0); + file.read((char*)&data, sizeof(data)); + file.close(); + + memcpy(&Data, &data, dataSize); +} + +void PersistentStorageManager::SaveBinary() +{ + LOGN("Saving persistent storage binary..."); + + std::ofstream file(GetDataPath(true), std::ios::binary); + + if (!file) + { + LOGN_ERROR("Failed to write persistent storage binary."); + return; + } + + file.write((const char*)&Data, sizeof(PersistentData)); + file.close(); + + BinStatus = EBinStatus::Success; +} diff --git a/UnleashedRecomp/user/persistent_storage_manager.h b/UnleashedRecomp/user/persistent_storage_manager.h new file mode 100644 index 00000000..d75d24ae --- /dev/null +++ b/UnleashedRecomp/user/persistent_storage_manager.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +enum class EBinStatus +{ + Unknown, + Success, + IOError, + BadFileSize, + BadSignature, + BadVersion, + BadHeader +}; + +class PersistentStorageManager +{ +public: + static inline PersistentData Data{}; + static inline EBinStatus BinStatus{ EBinStatus::Unknown }; + + static std::filesystem::path GetDataPath(bool checkForMods) + { + return GetSavePath(checkForMods) / EXT_FILENAME; + } + + static bool ShouldDisplayDLCMessage(bool setOffendingDLCFlag); + static void LoadBinary(); + static void SaveBinary(); +}; From 3ad00fb5e3474e8be2f55cbdfd3d12f18442376d Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:28:06 +0000 Subject: [PATCH 2/5] Use uint32_t for version numbers --- UnleashedRecomp/main.cpp | 4 ++- .../patches/CTitleStateIntro_patches.cpp | 4 ++- UnleashedRecomp/user/achievement_data.cpp | 2 +- UnleashedRecomp/user/achievement_data.h | 19 ++------------ UnleashedRecomp/user/achievement_manager.cpp | 25 +++++++++++-------- UnleashedRecomp/user/achievement_manager.h | 4 +-- UnleashedRecomp/user/persistent_data.cpp | 2 +- UnleashedRecomp/user/persistent_data.h | 19 ++------------ .../user/persistent_storage_manager.cpp | 23 +++++++++-------- .../user/persistent_storage_manager.h | 4 +-- 10 files changed, 43 insertions(+), 63 deletions(-) diff --git a/UnleashedRecomp/main.cpp b/UnleashedRecomp/main.cpp index ccf8b81c..37af696e 100644 --- a/UnleashedRecomp/main.cpp +++ b/UnleashedRecomp/main.cpp @@ -213,7 +213,9 @@ int main(int argc, char *argv[]) } Config::Load(); - PersistentStorageManager::LoadBinary(); + + if (!PersistentStorageManager::LoadBinary()) + LOGFN_ERROR("Failed to load persistent storage binary... (status code {})", (int)PersistentStorageManager::BinStatus); #if defined(_WIN32) && defined(UNLEASHED_RECOMP_D3D12) for (auto& dll : g_D3D12RequiredModules) diff --git a/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp b/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp index d4a05fc5..f3dbabe6 100644 --- a/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp +++ b/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -135,7 +136,8 @@ void PressStartSaveLoadThreadMidAsmHook() g_faderBegun.wait(true); } - AchievementManager::Load(); + if (!AchievementManager::Load()) + LOGFN_ERROR("Failed to load achievement data... (status code {})", (int)AchievementManager::Status); if (AchievementManager::Status != EAchStatus::Success) { diff --git a/UnleashedRecomp/user/achievement_data.cpp b/UnleashedRecomp/user/achievement_data.cpp index 2369b0df..dd734659 100644 --- a/UnleashedRecomp/user/achievement_data.cpp +++ b/UnleashedRecomp/user/achievement_data.cpp @@ -11,7 +11,7 @@ bool AchievementData::VerifySignature() const bool AchievementData::VerifyVersion() const { - return Version == AchVersion ACH_VERSION; + return Version <= ACH_VERSION; } bool AchievementData::VerifyChecksum() diff --git a/UnleashedRecomp/user/achievement_data.h b/UnleashedRecomp/user/achievement_data.h index bd38d219..3b1e2717 100644 --- a/UnleashedRecomp/user/achievement_data.h +++ b/UnleashedRecomp/user/achievement_data.h @@ -4,27 +4,12 @@ #define ACH_FILENAME "ACH-DATA" #define ACH_SIGNATURE { 'A', 'C', 'H', ' ' } -#define ACH_VERSION { 1, 0, 0 } +#define ACH_VERSION 1 #define ACH_RECORDS 50 class AchievementData { public: - struct AchVersion - { - uint8_t Major; - uint8_t Minor; - uint8_t Revision; - uint8_t Reserved; - - bool operator==(const AchVersion& other) const - { - return Major == other.Major && - Minor == other.Minor && - Revision == other.Revision; - } - }; - #pragma pack(push, 1) struct AchRecord { @@ -35,7 +20,7 @@ class AchievementData #pragma pack(pop) char Signature[4] ACH_SIGNATURE; - AchVersion Version ACH_VERSION; + uint32_t Version{ ACH_VERSION }; uint32_t Checksum; uint32_t Reserved; AchRecord Records[ACH_RECORDS]; diff --git a/UnleashedRecomp/user/achievement_manager.cpp b/UnleashedRecomp/user/achievement_manager.cpp index 90759765..b00bd34c 100644 --- a/UnleashedRecomp/user/achievement_manager.cpp +++ b/UnleashedRecomp/user/achievement_manager.cpp @@ -86,7 +86,7 @@ void AchievementManager::Reset() Data = {}; } -void AchievementManager::Load() +bool AchievementManager::Load() { AchievementManager::Reset(); @@ -100,7 +100,7 @@ void AchievementManager::Load() dataPath = GetDataPath(false); if (!std::filesystem::exists(dataPath)) - return; + return false; } std::error_code ec; @@ -110,7 +110,7 @@ void AchievementManager::Load() if (fileSize != dataSize) { Status = EAchStatus::BadFileSize; - return; + return false; } std::ifstream file(dataPath, std::ios::binary); @@ -118,7 +118,7 @@ void AchievementManager::Load() if (!file) { Status = EAchStatus::IOError; - return; + return false; } AchievementData data{}; @@ -129,17 +129,16 @@ void AchievementManager::Load() { Status = EAchStatus::BadSignature; file.close(); - return; + return false; } file.read((char*)&data.Version, sizeof(data.Version)); - // TODO: upgrade in future if the version changes. if (!data.VerifyVersion()) { Status = EAchStatus::BadVersion; file.close(); - return; + return false; } file.seekg(0); @@ -149,20 +148,22 @@ void AchievementManager::Load() { Status = EAchStatus::BadChecksum; file.close(); - return; + return false; } file.close(); memcpy(&Data, &data, dataSize); + + return true; } -void AchievementManager::Save(bool ignoreStatus) +bool AchievementManager::Save(bool ignoreStatus) { if (!ignoreStatus && Status != EAchStatus::Success) { LOGN_WARNING("Achievement data will not be saved in this session!"); - return; + return false; } LOGN("Saving achievements..."); @@ -172,7 +173,7 @@ void AchievementManager::Save(bool ignoreStatus) if (!file) { LOGN_ERROR("Failed to write achievement data."); - return; + return false; } Data.Checksum = Data.CalculateChecksum(); @@ -181,4 +182,6 @@ void AchievementManager::Save(bool ignoreStatus) file.close(); Status = EAchStatus::Success; + + return true; } diff --git a/UnleashedRecomp/user/achievement_manager.h b/UnleashedRecomp/user/achievement_manager.h index 57317fd5..49a85c1b 100644 --- a/UnleashedRecomp/user/achievement_manager.h +++ b/UnleashedRecomp/user/achievement_manager.h @@ -30,6 +30,6 @@ class AchievementManager static void Unlock(uint16_t id); static void UnlockAll(); static void Reset(); - static void Load(); - static void Save(bool ignoreStatus = false); + static bool Load(); + static bool Save(bool ignoreStatus = false); }; diff --git a/UnleashedRecomp/user/persistent_data.cpp b/UnleashedRecomp/user/persistent_data.cpp index 1bdd75b8..2534fb03 100644 --- a/UnleashedRecomp/user/persistent_data.cpp +++ b/UnleashedRecomp/user/persistent_data.cpp @@ -9,7 +9,7 @@ bool PersistentData::VerifySignature() const bool PersistentData::VerifyVersion() const { - return Header.Version == ExtVersion EXT_VERSION; + return Header.Version <= EXT_VERSION; } bool PersistentData::VerifyHeader() const diff --git a/UnleashedRecomp/user/persistent_data.h b/UnleashedRecomp/user/persistent_data.h index 5a8dd0ec..9a9cb7d1 100644 --- a/UnleashedRecomp/user/persistent_data.h +++ b/UnleashedRecomp/user/persistent_data.h @@ -4,7 +4,7 @@ #define EXT_FILENAME "EXT-DATA" #define EXT_SIGNATURE { 'E', 'X', 'T', ' ' } -#define EXT_VERSION { 1, 0, 0 } +#define EXT_VERSION 1 enum class EDLCFlag { @@ -20,25 +20,10 @@ enum class EDLCFlag class PersistentData { public: - struct ExtVersion - { - uint8_t Major; - uint8_t Minor; - uint8_t Revision; - uint8_t Reserved; - - bool operator==(const ExtVersion& other) const - { - return Major == other.Major && - Minor == other.Minor && - Revision == other.Revision; - } - }; - struct ExtHeader { char Signature[4] EXT_SIGNATURE; - ExtVersion Version EXT_VERSION; + uint32_t Version{ EXT_VERSION }; uint32_t HeaderSize{ sizeof(ExtHeader) }; uint32_t Reserved; }; diff --git a/UnleashedRecomp/user/persistent_storage_manager.cpp b/UnleashedRecomp/user/persistent_storage_manager.cpp index 8883ba6a..412a390c 100644 --- a/UnleashedRecomp/user/persistent_storage_manager.cpp +++ b/UnleashedRecomp/user/persistent_storage_manager.cpp @@ -34,7 +34,7 @@ bool PersistentStorageManager::ShouldDisplayDLCMessage(bool setOffendingDLCFlag) return result; } -void PersistentStorageManager::LoadBinary() +bool PersistentStorageManager::LoadBinary() { BinStatus = EBinStatus::Success; @@ -46,7 +46,7 @@ void PersistentStorageManager::LoadBinary() dataPath = GetDataPath(false); if (!std::filesystem::exists(dataPath)) - return; + return false; } std::error_code ec; @@ -56,7 +56,7 @@ void PersistentStorageManager::LoadBinary() if (fileSize != dataSize) { BinStatus = EBinStatus::BadFileSize; - return; + return false; } std::ifstream file(dataPath, std::ios::binary); @@ -64,7 +64,7 @@ void PersistentStorageManager::LoadBinary() if (!file) { BinStatus = EBinStatus::IOError; - return; + return false; } PersistentData data{}; @@ -75,17 +75,16 @@ void PersistentStorageManager::LoadBinary() { BinStatus = EBinStatus::BadSignature; file.close(); - return; + return false; } file.read((char*)&data.Header.Version, sizeof(data.Header.Version)); - // TODO: upgrade in future if the version changes. if (!data.VerifyVersion()) { BinStatus = EBinStatus::BadVersion; file.close(); - return; + return false; } file.read((char*)&data.Header.HeaderSize, sizeof(data.Header.HeaderSize)); @@ -94,7 +93,7 @@ void PersistentStorageManager::LoadBinary() { BinStatus = EBinStatus::BadHeader; file.close(); - return; + return false; } file.seekg(0); @@ -102,9 +101,11 @@ void PersistentStorageManager::LoadBinary() file.close(); memcpy(&Data, &data, dataSize); + + return true; } -void PersistentStorageManager::SaveBinary() +bool PersistentStorageManager::SaveBinary() { LOGN("Saving persistent storage binary..."); @@ -113,11 +114,13 @@ void PersistentStorageManager::SaveBinary() if (!file) { LOGN_ERROR("Failed to write persistent storage binary."); - return; + return false; } file.write((const char*)&Data, sizeof(PersistentData)); file.close(); BinStatus = EBinStatus::Success; + + return true; } diff --git a/UnleashedRecomp/user/persistent_storage_manager.h b/UnleashedRecomp/user/persistent_storage_manager.h index d75d24ae..d65b89f6 100644 --- a/UnleashedRecomp/user/persistent_storage_manager.h +++ b/UnleashedRecomp/user/persistent_storage_manager.h @@ -25,6 +25,6 @@ class PersistentStorageManager } static bool ShouldDisplayDLCMessage(bool setOffendingDLCFlag); - static void LoadBinary(); - static void SaveBinary(); + static bool LoadBinary(); + static bool SaveBinary(); }; From 9b866168c44cbf05de5da78a0ae5e6d3c288e6c4 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sun, 23 Mar 2025 04:33:32 +0000 Subject: [PATCH 3/5] Make AchievementManager follow the same naming convention as PersistentStorageManager --- .../patches/CTitleStateIntro_patches.cpp | 16 +++++++-------- UnleashedRecomp/patches/resident_patches.cpp | 2 +- UnleashedRecomp/user/achievement_manager.cpp | 20 +++++++++---------- UnleashedRecomp/user/achievement_manager.h | 6 +++--- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp b/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp index f3dbabe6..42b39b43 100644 --- a/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp +++ b/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp @@ -65,16 +65,16 @@ static bool ProcessCorruptAchievementsMessage() if (!g_corruptAchievementsMessageOpen) return false; - auto message = AchievementManager::Status == EAchStatus::IOError + auto message = AchievementManager::BinStatus == EAchStatus::IOError ? Localise("Title_Message_AchievementDataIOError") : Localise("Title_Message_AchievementDataCorrupt"); if (MessageWindow::Open(message, &g_corruptAchievementsMessageResult) == MSG_CLOSED) { - // Allow user to proceed if the achievement data couldn't be loaded. - // Restarting may fix this error, so it isn't worth clearing the data for. - if (AchievementManager::Status != EAchStatus::IOError) - AchievementManager::Save(true); + // Create a new save file if the file was successfully loaded and failed validation. + // If the file couldn't be opened, restarting may fix this error, so it isn't worth clearing the data for. + if (AchievementManager::BinStatus != EAchStatus::IOError) + AchievementManager::SaveBinary(true); g_corruptAchievementsMessageOpen = false; g_corruptAchievementsMessageOpen.notify_one(); @@ -136,10 +136,10 @@ void PressStartSaveLoadThreadMidAsmHook() g_faderBegun.wait(true); } - if (!AchievementManager::Load()) - LOGFN_ERROR("Failed to load achievement data... (status code {})", (int)AchievementManager::Status); + if (!AchievementManager::LoadBinary()) + LOGFN_ERROR("Failed to load achievement data... (status code {})", (int)AchievementManager::BinStatus); - if (AchievementManager::Status != EAchStatus::Success) + if (AchievementManager::BinStatus != EAchStatus::Success) { g_corruptAchievementsMessageOpen = true; g_corruptAchievementsMessageOpen.wait(true); diff --git a/UnleashedRecomp/patches/resident_patches.cpp b/UnleashedRecomp/patches/resident_patches.cpp index c421946c..2741d0e9 100644 --- a/UnleashedRecomp/patches/resident_patches.cpp +++ b/UnleashedRecomp/patches/resident_patches.cpp @@ -106,7 +106,7 @@ PPC_FUNC(sub_824E5170) if (!isSavedExtraData) { - AchievementManager::Save(); + AchievementManager::SaveBinary(); PersistentStorageManager::SaveBinary(); isSavedExtraData = true; diff --git a/UnleashedRecomp/user/achievement_manager.cpp b/UnleashedRecomp/user/achievement_manager.cpp index b00bd34c..76593287 100644 --- a/UnleashedRecomp/user/achievement_manager.cpp +++ b/UnleashedRecomp/user/achievement_manager.cpp @@ -86,11 +86,11 @@ void AchievementManager::Reset() Data = {}; } -bool AchievementManager::Load() +bool AchievementManager::LoadBinary() { AchievementManager::Reset(); - Status = EAchStatus::Success; + BinStatus = EAchStatus::Success; auto dataPath = GetDataPath(true); @@ -109,7 +109,7 @@ bool AchievementManager::Load() if (fileSize != dataSize) { - Status = EAchStatus::BadFileSize; + BinStatus = EAchStatus::BadFileSize; return false; } @@ -117,7 +117,7 @@ bool AchievementManager::Load() if (!file) { - Status = EAchStatus::IOError; + BinStatus = EAchStatus::IOError; return false; } @@ -127,7 +127,7 @@ bool AchievementManager::Load() if (!data.VerifySignature()) { - Status = EAchStatus::BadSignature; + BinStatus = EAchStatus::BadSignature; file.close(); return false; } @@ -136,7 +136,7 @@ bool AchievementManager::Load() if (!data.VerifyVersion()) { - Status = EAchStatus::BadVersion; + BinStatus = EAchStatus::BadVersion; file.close(); return false; } @@ -146,7 +146,7 @@ bool AchievementManager::Load() if (!data.VerifyChecksum()) { - Status = EAchStatus::BadChecksum; + BinStatus = EAchStatus::BadChecksum; file.close(); return false; } @@ -158,9 +158,9 @@ bool AchievementManager::Load() return true; } -bool AchievementManager::Save(bool ignoreStatus) +bool AchievementManager::SaveBinary(bool ignoreStatus) { - if (!ignoreStatus && Status != EAchStatus::Success) + if (!ignoreStatus && BinStatus != EAchStatus::Success) { LOGN_WARNING("Achievement data will not be saved in this session!"); return false; @@ -181,7 +181,7 @@ bool AchievementManager::Save(bool ignoreStatus) file.write((const char*)&Data, sizeof(AchievementData)); file.close(); - Status = EAchStatus::Success; + BinStatus = EAchStatus::Success; return true; } diff --git a/UnleashedRecomp/user/achievement_manager.h b/UnleashedRecomp/user/achievement_manager.h index 49a85c1b..2fcb40d9 100644 --- a/UnleashedRecomp/user/achievement_manager.h +++ b/UnleashedRecomp/user/achievement_manager.h @@ -17,7 +17,7 @@ class AchievementManager { public: static inline AchievementData Data{}; - static inline EAchStatus Status{ EAchStatus::Unknown }; + static inline EAchStatus BinStatus{ EAchStatus::Unknown }; static std::filesystem::path GetDataPath(bool checkForMods) { @@ -30,6 +30,6 @@ class AchievementManager static void Unlock(uint16_t id); static void UnlockAll(); static void Reset(); - static bool Load(); - static bool Save(bool ignoreStatus = false); + static bool LoadBinary(); + static bool SaveBinary(bool ignoreStatus = false); }; From f57d0ba49a0039ca881969933ec5a99331495168 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:18:56 +0000 Subject: [PATCH 4/5] persistent_data: remove header size field --- .../patches/CTitleStateIntro_patches.cpp | 6 ++-- UnleashedRecomp/user/achievement_data.h | 6 ++-- UnleashedRecomp/user/achievement_manager.cpp | 19 +++++----- UnleashedRecomp/user/achievement_manager.h | 5 +-- UnleashedRecomp/user/persistent_data.cpp | 9 ++--- UnleashedRecomp/user/persistent_data.h | 15 +++----- .../user/persistent_storage_manager.cpp | 36 ++++++++----------- .../user/persistent_storage_manager.h | 8 ++--- 8 files changed, 45 insertions(+), 59 deletions(-) diff --git a/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp b/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp index 42b39b43..4affb80a 100644 --- a/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp +++ b/UnleashedRecomp/patches/CTitleStateIntro_patches.cpp @@ -65,7 +65,7 @@ static bool ProcessCorruptAchievementsMessage() if (!g_corruptAchievementsMessageOpen) return false; - auto message = AchievementManager::BinStatus == EAchStatus::IOError + auto message = AchievementManager::BinStatus == EAchBinStatus::IOError ? Localise("Title_Message_AchievementDataIOError") : Localise("Title_Message_AchievementDataCorrupt"); @@ -73,7 +73,7 @@ static bool ProcessCorruptAchievementsMessage() { // Create a new save file if the file was successfully loaded and failed validation. // If the file couldn't be opened, restarting may fix this error, so it isn't worth clearing the data for. - if (AchievementManager::BinStatus != EAchStatus::IOError) + if (AchievementManager::BinStatus != EAchBinStatus::IOError) AchievementManager::SaveBinary(true); g_corruptAchievementsMessageOpen = false; @@ -139,7 +139,7 @@ void PressStartSaveLoadThreadMidAsmHook() if (!AchievementManager::LoadBinary()) LOGFN_ERROR("Failed to load achievement data... (status code {})", (int)AchievementManager::BinStatus); - if (AchievementManager::BinStatus != EAchStatus::Success) + if (AchievementManager::BinStatus != EAchBinStatus::Success) { g_corruptAchievementsMessageOpen = true; g_corruptAchievementsMessageOpen.wait(true); diff --git a/UnleashedRecomp/user/achievement_data.h b/UnleashedRecomp/user/achievement_data.h index 3b1e2717..3c8c1a5a 100644 --- a/UnleashedRecomp/user/achievement_data.h +++ b/UnleashedRecomp/user/achievement_data.h @@ -21,9 +21,9 @@ class AchievementData char Signature[4] ACH_SIGNATURE; uint32_t Version{ ACH_VERSION }; - uint32_t Checksum; - uint32_t Reserved; - AchRecord Records[ACH_RECORDS]; + uint32_t Checksum{}; + uint32_t Reserved{}; + AchRecord Records[ACH_RECORDS]{}; bool VerifySignature() const; bool VerifyVersion() const; diff --git a/UnleashedRecomp/user/achievement_manager.cpp b/UnleashedRecomp/user/achievement_manager.cpp index 76593287..60ca199d 100644 --- a/UnleashedRecomp/user/achievement_manager.cpp +++ b/UnleashedRecomp/user/achievement_manager.cpp @@ -90,7 +90,7 @@ bool AchievementManager::LoadBinary() { AchievementManager::Reset(); - BinStatus = EAchStatus::Success; + BinStatus = EAchBinStatus::Success; auto dataPath = GetDataPath(true); @@ -100,7 +100,10 @@ bool AchievementManager::LoadBinary() dataPath = GetDataPath(false); if (!std::filesystem::exists(dataPath)) + { + BinStatus = EAchBinStatus::NoFile; return false; + } } std::error_code ec; @@ -109,7 +112,7 @@ bool AchievementManager::LoadBinary() if (fileSize != dataSize) { - BinStatus = EAchStatus::BadFileSize; + BinStatus = EAchBinStatus::BadFileSize; return false; } @@ -117,7 +120,7 @@ bool AchievementManager::LoadBinary() if (!file) { - BinStatus = EAchStatus::IOError; + BinStatus = EAchBinStatus::IOError; return false; } @@ -127,7 +130,7 @@ bool AchievementManager::LoadBinary() if (!data.VerifySignature()) { - BinStatus = EAchStatus::BadSignature; + BinStatus = EAchBinStatus::BadSignature; file.close(); return false; } @@ -136,7 +139,7 @@ bool AchievementManager::LoadBinary() if (!data.VerifyVersion()) { - BinStatus = EAchStatus::BadVersion; + BinStatus = EAchBinStatus::BadVersion; file.close(); return false; } @@ -146,7 +149,7 @@ bool AchievementManager::LoadBinary() if (!data.VerifyChecksum()) { - BinStatus = EAchStatus::BadChecksum; + BinStatus = EAchBinStatus::BadChecksum; file.close(); return false; } @@ -160,7 +163,7 @@ bool AchievementManager::LoadBinary() bool AchievementManager::SaveBinary(bool ignoreStatus) { - if (!ignoreStatus && BinStatus != EAchStatus::Success) + if (!ignoreStatus && BinStatus != EAchBinStatus::Success) { LOGN_WARNING("Achievement data will not be saved in this session!"); return false; @@ -181,7 +184,7 @@ bool AchievementManager::SaveBinary(bool ignoreStatus) file.write((const char*)&Data, sizeof(AchievementData)); file.close(); - BinStatus = EAchStatus::Success; + BinStatus = EAchBinStatus::Success; return true; } diff --git a/UnleashedRecomp/user/achievement_manager.h b/UnleashedRecomp/user/achievement_manager.h index 2fcb40d9..4d0bf9e1 100644 --- a/UnleashedRecomp/user/achievement_manager.h +++ b/UnleashedRecomp/user/achievement_manager.h @@ -2,10 +2,11 @@ #include -enum class EAchStatus +enum class EAchBinStatus { Unknown, Success, + NoFile, IOError, BadFileSize, BadSignature, @@ -17,7 +18,7 @@ class AchievementManager { public: static inline AchievementData Data{}; - static inline EAchStatus BinStatus{ EAchStatus::Unknown }; + static inline EAchBinStatus BinStatus{ EAchBinStatus::Unknown }; static std::filesystem::path GetDataPath(bool checkForMods) { diff --git a/UnleashedRecomp/user/persistent_data.cpp b/UnleashedRecomp/user/persistent_data.cpp index 2534fb03..f087f39e 100644 --- a/UnleashedRecomp/user/persistent_data.cpp +++ b/UnleashedRecomp/user/persistent_data.cpp @@ -4,15 +4,10 @@ bool PersistentData::VerifySignature() const { char sig[4] = EXT_SIGNATURE; - return memcmp(Header.Signature, sig, sizeof(Header.Signature)) == 0; + return memcmp(Signature, sig, sizeof(Signature)) == 0; } bool PersistentData::VerifyVersion() const { - return Header.Version <= EXT_VERSION; -} - -bool PersistentData::VerifyHeader() const -{ - return Header.HeaderSize == sizeof(ExtHeader); + return Version <= EXT_VERSION; } diff --git a/UnleashedRecomp/user/persistent_data.h b/UnleashedRecomp/user/persistent_data.h index 9a9cb7d1..fec827fa 100644 --- a/UnleashedRecomp/user/persistent_data.h +++ b/UnleashedRecomp/user/persistent_data.h @@ -20,18 +20,11 @@ enum class EDLCFlag class PersistentData { public: - struct ExtHeader - { - char Signature[4] EXT_SIGNATURE; - uint32_t Version{ EXT_VERSION }; - uint32_t HeaderSize{ sizeof(ExtHeader) }; - uint32_t Reserved; - }; - - ExtHeader Header; - bool DLCFlags[6]; + char Signature[4] EXT_SIGNATURE; + uint32_t Version{ EXT_VERSION }; + uint64_t Reserved{}; + bool DLCFlags[6]{}; bool VerifySignature() const; bool VerifyVersion() const; - bool VerifyHeader() const; }; diff --git a/UnleashedRecomp/user/persistent_storage_manager.cpp b/UnleashedRecomp/user/persistent_storage_manager.cpp index 412a390c..231ba36e 100644 --- a/UnleashedRecomp/user/persistent_storage_manager.cpp +++ b/UnleashedRecomp/user/persistent_storage_manager.cpp @@ -5,10 +5,8 @@ bool PersistentStorageManager::ShouldDisplayDLCMessage(bool setOffendingDLCFlag) { - auto result = false; - - if (BinStatus != EBinStatus::Success) - return result; + if (BinStatus != EExtBinStatus::Success) + return true; static std::unordered_map flags = { @@ -20,6 +18,8 @@ bool PersistentStorageManager::ShouldDisplayDLCMessage(bool setOffendingDLCFlag) { EDLCFlag::EmpireCityAndAdabat, DLC::EmpireCityAdabat } }; + auto result = false; + for (auto& pair : flags) { if (!Data.DLCFlags[(int)pair.first] && Installer::checkDLCInstall(GetGamePath(), pair.second)) @@ -36,7 +36,7 @@ bool PersistentStorageManager::ShouldDisplayDLCMessage(bool setOffendingDLCFlag) bool PersistentStorageManager::LoadBinary() { - BinStatus = EBinStatus::Success; + BinStatus = EExtBinStatus::Success; auto dataPath = GetDataPath(true); @@ -46,7 +46,10 @@ bool PersistentStorageManager::LoadBinary() dataPath = GetDataPath(false); if (!std::filesystem::exists(dataPath)) + { + BinStatus = EExtBinStatus::NoFile; return false; + } } std::error_code ec; @@ -55,7 +58,7 @@ bool PersistentStorageManager::LoadBinary() if (fileSize != dataSize) { - BinStatus = EBinStatus::BadFileSize; + BinStatus = EExtBinStatus::BadFileSize; return false; } @@ -63,35 +66,26 @@ bool PersistentStorageManager::LoadBinary() if (!file) { - BinStatus = EBinStatus::IOError; + BinStatus = EExtBinStatus::IOError; return false; } PersistentData data{}; - file.read((char*)&data.Header.Signature, sizeof(data.Header.Signature)); + file.read((char*)&data.Signature, sizeof(data.Signature)); if (!data.VerifySignature()) { - BinStatus = EBinStatus::BadSignature; + BinStatus = EExtBinStatus::BadSignature; file.close(); return false; } - file.read((char*)&data.Header.Version, sizeof(data.Header.Version)); + file.read((char*)&data.Version, sizeof(data.Version)); if (!data.VerifyVersion()) { - BinStatus = EBinStatus::BadVersion; - file.close(); - return false; - } - - file.read((char*)&data.Header.HeaderSize, sizeof(data.Header.HeaderSize)); - - if (!data.VerifyHeader()) - { - BinStatus = EBinStatus::BadHeader; + BinStatus = EExtBinStatus::BadVersion; file.close(); return false; } @@ -120,7 +114,7 @@ bool PersistentStorageManager::SaveBinary() file.write((const char*)&Data, sizeof(PersistentData)); file.close(); - BinStatus = EBinStatus::Success; + BinStatus = EExtBinStatus::Success; return true; } diff --git a/UnleashedRecomp/user/persistent_storage_manager.h b/UnleashedRecomp/user/persistent_storage_manager.h index d65b89f6..25f5b212 100644 --- a/UnleashedRecomp/user/persistent_storage_manager.h +++ b/UnleashedRecomp/user/persistent_storage_manager.h @@ -2,22 +2,22 @@ #include -enum class EBinStatus +enum class EExtBinStatus { Unknown, Success, + NoFile, IOError, BadFileSize, BadSignature, - BadVersion, - BadHeader + BadVersion }; class PersistentStorageManager { public: static inline PersistentData Data{}; - static inline EBinStatus BinStatus{ EBinStatus::Unknown }; + static inline EExtBinStatus BinStatus{ EExtBinStatus::Unknown }; static std::filesystem::path GetDataPath(bool checkForMods) { From f160be4bbac7c2ddd48def7de94cfff8e1d3a202 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Fri, 28 Mar 2025 12:47:18 +0000 Subject: [PATCH 5/5] Make status success by default or on file not existing --- UnleashedRecomp/user/achievement_manager.cpp | 5 +---- UnleashedRecomp/user/achievement_manager.h | 4 +--- UnleashedRecomp/user/persistent_storage_manager.cpp | 5 +---- UnleashedRecomp/user/persistent_storage_manager.h | 4 +--- 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/UnleashedRecomp/user/achievement_manager.cpp b/UnleashedRecomp/user/achievement_manager.cpp index 60ca199d..251d5bb9 100644 --- a/UnleashedRecomp/user/achievement_manager.cpp +++ b/UnleashedRecomp/user/achievement_manager.cpp @@ -100,10 +100,7 @@ bool AchievementManager::LoadBinary() dataPath = GetDataPath(false); if (!std::filesystem::exists(dataPath)) - { - BinStatus = EAchBinStatus::NoFile; - return false; - } + return true; } std::error_code ec; diff --git a/UnleashedRecomp/user/achievement_manager.h b/UnleashedRecomp/user/achievement_manager.h index 4d0bf9e1..4bff7577 100644 --- a/UnleashedRecomp/user/achievement_manager.h +++ b/UnleashedRecomp/user/achievement_manager.h @@ -4,9 +4,7 @@ enum class EAchBinStatus { - Unknown, Success, - NoFile, IOError, BadFileSize, BadSignature, @@ -18,7 +16,7 @@ class AchievementManager { public: static inline AchievementData Data{}; - static inline EAchBinStatus BinStatus{ EAchBinStatus::Unknown }; + static inline EAchBinStatus BinStatus{ EAchBinStatus::Success }; static std::filesystem::path GetDataPath(bool checkForMods) { diff --git a/UnleashedRecomp/user/persistent_storage_manager.cpp b/UnleashedRecomp/user/persistent_storage_manager.cpp index 231ba36e..6b92d239 100644 --- a/UnleashedRecomp/user/persistent_storage_manager.cpp +++ b/UnleashedRecomp/user/persistent_storage_manager.cpp @@ -46,10 +46,7 @@ bool PersistentStorageManager::LoadBinary() dataPath = GetDataPath(false); if (!std::filesystem::exists(dataPath)) - { - BinStatus = EExtBinStatus::NoFile; - return false; - } + return true; } std::error_code ec; diff --git a/UnleashedRecomp/user/persistent_storage_manager.h b/UnleashedRecomp/user/persistent_storage_manager.h index 25f5b212..4cad7b88 100644 --- a/UnleashedRecomp/user/persistent_storage_manager.h +++ b/UnleashedRecomp/user/persistent_storage_manager.h @@ -4,9 +4,7 @@ enum class EExtBinStatus { - Unknown, Success, - NoFile, IOError, BadFileSize, BadSignature, @@ -17,7 +15,7 @@ class PersistentStorageManager { public: static inline PersistentData Data{}; - static inline EExtBinStatus BinStatus{ EExtBinStatus::Unknown }; + static inline EExtBinStatus BinStatus{ EExtBinStatus::Success }; static std::filesystem::path GetDataPath(bool checkForMods) {