From aacb9d259c3438a18183d47a9eb91d5d1b590d63 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Sat, 18 Jan 2025 19:32:37 +0000 Subject: [PATCH] Added error message for corrupted save data, removed Win32 message box on XamShowMessageBoxUI (#122) * xam: remove Win32 message box on XamShowMessageBoxUI * CTitleStateIntro_patches: display error message on corrupted save data TitleMenuRemoveContinueOnCorruptSaveMidAsmHook by @DeaTh-G Co-Authored-By: DeaTh-G <55578911+DeaTh-G@users.noreply.github.com> --------- Co-authored-by: DeaTh-G <55578911+DeaTh-G@users.noreply.github.com> --- UnleashedRecomp/app.h | 1 + UnleashedRecomp/kernel/xam.cpp | 41 +++++++-------- UnleashedRecomp/locale/locale.cpp | 14 +++++ UnleashedRecomp/patches/misc_patches.cpp | 2 - UnleashedRecomp/patches/resident_patches.cpp | 2 + .../patches/ui/CTitleStateIntro_patches.cpp | 52 ++++++++++++++++++- .../patches/ui/CTitleStateMenu_patches.cpp | 12 +++++ UnleashedRecompLib/config/SWA.toml | 5 ++ 8 files changed, 105 insertions(+), 24 deletions(-) diff --git a/UnleashedRecomp/app.h b/UnleashedRecomp/app.h index bf05692..cc61bd0 100644 --- a/UnleashedRecomp/app.h +++ b/UnleashedRecomp/app.h @@ -9,6 +9,7 @@ public: static inline bool s_isMissingDLC; static inline bool s_isLoading; static inline bool s_isWerehog; + static inline bool s_isSaveDataCorrupt; static inline ELanguage s_language; diff --git a/UnleashedRecomp/kernel/xam.cpp b/UnleashedRecomp/kernel/xam.cpp index 26aeecd..7714f1d 100644 --- a/UnleashedRecomp/kernel/xam.cpp +++ b/UnleashedRecomp/kernel/xam.cpp @@ -11,12 +11,6 @@ #include #include -#ifdef _WIN32 -#include -// Needed for commctrl -#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") -#endif - struct XamListener : KernelObject { uint32_t id{}; @@ -208,11 +202,12 @@ bool XNotifyGetNext(uint32_t hNotification, uint32_t dwMsgFilter, be* uint32_t XamShowMessageBoxUI(uint32_t dwUserIndex, be* wszTitle, be* wszText, uint32_t cButtons, xpointer>* pwszButtons, uint32_t dwFocusButton, uint32_t dwFlags, be* pResult, XXOVERLAPPED* pOverlapped) { - int button{}; + *pResult = cButtons ? cButtons - 1 : 0; -#ifdef _WIN32 +#if _DEBUG + assert("XamShowMessageBoxUI encountered!" && false); +#else std::vector texts{}; - std::vector buttons{}; texts.emplace_back(reinterpret_cast(wszTitle)); texts.emplace_back(reinterpret_cast(wszText)); @@ -226,23 +221,27 @@ uint32_t XamShowMessageBoxUI(uint32_t dwUserIndex, be* wszTitle, beget()); + wprintf(L"[XamShowMessageBoxUI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); #endif - *pResult = button; - if (pOverlapped) { pOverlapped->dwCompletionContext = GuestThread::GetCurrentThreadId(); diff --git a/UnleashedRecomp/locale/locale.cpp b/UnleashedRecomp/locale/locale.cpp index b6f9aec..b8ff79b 100644 --- a/UnleashedRecomp/locale/locale.cpp +++ b/UnleashedRecomp/locale/locale.cpp @@ -342,6 +342,20 @@ std::unordered_map> g_lo { ELanguage::Italian, "Sei sicuro di voler uscire?" } } }, + { + // Notes: message appears when the SYS-DATA is corrupted (mismatching file size). + // To make this occur, open the file in any editor and just remove a large chunk of data. + // Do not localise this unless absolutely necessary, these strings are from the XEX. + "Title_Message_SaveDataCorrupt", + { + { ELanguage::English, "The save file appears to be\ncorrupted and cannot be loaded." }, + { ELanguage::Japanese, "ゲームデータの読み込みに失敗しました。\nこのまま続けるとゲームデータをセーブすることはできません" }, + { ELanguage::German, "Diese Speicherdatei ist beschädigt\nund kann nicht geladen werden." }, + { ELanguage::French, "Le fichier de sauvegarde semble être\nendommagé et ne peut être chargé." }, + { ELanguage::Spanish, "El archivo parece estar dañado\ny no se puede cargar." }, + { ELanguage::Italian, "I file di salvataggio sembrano danneggiati\ne non possono essere caricati." } + } + }, { "Common_On", { diff --git a/UnleashedRecomp/patches/misc_patches.cpp b/UnleashedRecomp/patches/misc_patches.cpp index d6bb7d6..a18ad48 100644 --- a/UnleashedRecomp/patches/misc_patches.cpp +++ b/UnleashedRecomp/patches/misc_patches.cpp @@ -49,8 +49,6 @@ void WerehogBattleMusicMidAsmHook(PPCRegister& r11) r11.u8 = 3; } -void StorageDevicePromptMidAsmHook() {} - /* Hook function that gets the game region and force result to zero for Japanese to display the correct logos. */ diff --git a/UnleashedRecomp/patches/resident_patches.cpp b/UnleashedRecomp/patches/resident_patches.cpp index 526c2dc..31574e4 100644 --- a/UnleashedRecomp/patches/resident_patches.cpp +++ b/UnleashedRecomp/patches/resident_patches.cpp @@ -69,6 +69,8 @@ PPC_FUNC(sub_824E5170) if (pSaveIcon->m_IsVisible) { + App::s_isSaveDataCorrupt = false; + if (!m_isSavedAchievementData) { LOGN("Saving achievements..."); diff --git a/UnleashedRecomp/patches/ui/CTitleStateIntro_patches.cpp b/UnleashedRecomp/patches/ui/CTitleStateIntro_patches.cpp index 29c0b4b..bc3684c 100644 --- a/UnleashedRecomp/patches/ui/CTitleStateIntro_patches.cpp +++ b/UnleashedRecomp/patches/ui/CTitleStateIntro_patches.cpp @@ -2,14 +2,21 @@ #include #include #include +#include #include static bool g_quitMessageOpen = false; static bool g_quitMessageFaderBegun = false; static int g_quitMessageResult = -1; +static std::atomic g_corruptSaveMessageOpen = false; +static int g_corruptSaveMessageResult = -1; + static bool ProcessQuitMessage() { + if (g_corruptSaveMessageOpen) + return false; + if (!g_quitMessageOpen) return false; @@ -37,6 +44,49 @@ static bool ProcessQuitMessage() return true; } +static bool ProcessCorruptSaveMessage() +{ + if (!g_corruptSaveMessageOpen) + return false; + + if (MessageWindow::Open(Localise("Title_Message_SaveDataCorrupt"), &g_corruptSaveMessageResult) == MSG_CLOSED) + { + g_corruptSaveMessageOpen = false; + g_corruptSaveMessageOpen.notify_one(); + g_corruptSaveMessageResult = -1; + } + + return true; +} + +void StorageDevicePromptMidAsmHook() {} + +// Save data validation hook. +PPC_FUNC_IMPL(__imp__sub_822C4330); +PPC_FUNC(sub_822C4330) +{ + std::error_code ec; + auto saveFileSize = std::filesystem::file_size(GetSaveFilePath(true), ec); + + auto expectedSize = ctx.r5.u32; + auto expectedSizeAdd = *(be*)g_memory.Translate(0x83262110); + + // TODO: check for backups here and restore them? + if (saveFileSize != expectedSize + expectedSizeAdd) + { + App::s_isSaveDataCorrupt = true; + + g_corruptSaveMessageOpen = true; + g_corruptSaveMessageOpen.wait(true); + + ctx.r3.u32 = 0; + + return; + } + + ctx.r3.u32 = 1; +} + // SWA::CTitleStateIntro::Update PPC_FUNC_IMPL(__imp__sub_82587E50); PPC_FUNC(sub_82587E50) @@ -47,7 +97,7 @@ PPC_FUNC(sub_82587E50) { __imp__sub_82587E50(ctx, base); } - else + else if (!ProcessCorruptSaveMessage()) { auto pInputState = SWA::CInputState::GetInstance(); diff --git a/UnleashedRecomp/patches/ui/CTitleStateMenu_patches.cpp b/UnleashedRecomp/patches/ui/CTitleStateMenu_patches.cpp index 12cc739..aea4b65 100644 --- a/UnleashedRecomp/patches/ui/CTitleStateMenu_patches.cpp +++ b/UnleashedRecomp/patches/ui/CTitleStateMenu_patches.cpp @@ -56,6 +56,10 @@ PPC_FUNC(sub_825882B8) auto isOptionsIndex = pTitleState->m_pMember->m_pTitleMenu->m_CursorIndex == 2; auto isInstallIndex = pTitleState->m_pMember->m_pTitleMenu->m_CursorIndex == 3; + // Always default to New Game with corrupted save data. + if (App::s_isSaveDataCorrupt && pTitleState->m_pMember->m_pTitleMenu->m_CursorIndex == 1) + pTitleState->m_pMember->m_pTitleMenu->m_CursorIndex = 0; + if (!OptionsMenu::s_isVisible && isOptionsIndex) { if (OptionsMenu::s_isRestartRequired) @@ -92,6 +96,14 @@ PPC_FUNC(sub_825882B8) } } +void TitleMenuRemoveContinueOnCorruptSaveMidAsmHook(PPCRegister& r3) +{ + if (!App::s_isSaveDataCorrupt) + return; + + r3.u64 = 0; +} + void TitleMenuRemoveStorageDeviceOptionMidAsmHook(PPCRegister& r11) { r11.u32 = 0; diff --git a/UnleashedRecompLib/config/SWA.toml b/UnleashedRecompLib/config/SWA.toml index 0282337..f92595b 100644 --- a/UnleashedRecompLib/config/SWA.toml +++ b/UnleashedRecompLib/config/SWA.toml @@ -663,3 +663,8 @@ registers = ["r3"] name = "InspireSubtitleMidAsmHook" address = 0x82B949B0 registers = ["r3"] + +[[midasm_hook]] +name = "TitleMenuRemoveContinueOnCorruptSaveMidAsmHook" +address = 0x82585470 +registers = ["r3"]