diff --git a/Common/Serialize/Serializer.h b/Common/Serialize/Serializer.h index a7fa964a0c..808967b518 100644 --- a/Common/Serialize/Serializer.h +++ b/Common/Serialize/Serializer.h @@ -147,6 +147,11 @@ public: void DoMarker(const char *prevName, u32 arbitraryNumber = 0x42); + void SkipBytes(size_t bytes) { + // Should work in all modes. + *ptr += bytes; + } + size_t Offset() const { return *ptr - ptrStart_; } private: diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 5ff26c0461..60317157d9 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -85,8 +85,7 @@ #include "sceDmac.h" #include "sceMp4.h" #include "sceOpenPSID.h" - -#include "../Util/PPGeDraw.h" +#include "Core/Util/PPGeDraw.h" /* 17: [MIPS32 R4K 00000000 ]: Loader: Type: 1 Vaddr: 00000000 Filesz: 2856816 Memsz: 2856816 diff --git a/Core/RetroAchievements.cpp b/Core/RetroAchievements.cpp index e4c9300ec5..7b7d01960b 100644 --- a/Core/RetroAchievements.cpp +++ b/Core/RetroAchievements.cpp @@ -354,6 +354,11 @@ static void event_handler_callback(const rc_client_event_t *event, rc_client_t * } void Initialize() { + if (!g_Config.bAchievementsEnable) { + _dbg_assert_(!g_rcClient); + INFO_LOG(ACHIEVEMENTS, "Achievements are disabled, not initializing."); + return; + } _assert_msg_(g_Config.bAchievementsEnable, "Achievements are enabled"); g_challengeMode = true; // the default @@ -453,14 +458,27 @@ void Idle() { } void DoState(PointerWrap &p) { - auto sw = p.Section("Achievements", 1); + auto sw = p.Section("Achievements", 0, 1); if (!sw) { // Save state is missing the section. // Reset the runtime. + if (HasAchievementsOrLeaderboards()) { + auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS); + g_OSD.Show(OSDType::MESSAGE_WARNING, ac->T("Save state loaded without achievement data"), 5.0f); + } + rc_client_reset(g_rcClient); return; } uint32_t data_size = 0; + + if (!g_activeGame) { + WARN_LOG(ACHIEVEMENTS, "Save state contained achievement data, but achievements are not active. Ignore."); + Do(p, data_size); + p.SkipBytes(data_size); + return; + } + if (p.mode == PointerWrap::MODE_MEASURE || p.mode == PointerWrap::MODE_WRITE || p.mode == PointerWrap::MODE_VERIFY || p.mode == PointerWrap::MODE_NOOP) { data_size = (uint32_t)(g_rcClient ? rc_client_progress_size(g_rcClient) : 0); } @@ -473,18 +491,35 @@ void DoState(PointerWrap &p) { case PointerWrap::MODE_MEASURE: case PointerWrap::MODE_WRITE: case PointerWrap::MODE_VERIFY: - rc_client_serialize_progress(g_rcClient, buffer); + { + int retval = rc_client_serialize_progress(g_rcClient, buffer); + if (retval != RC_OK) { + ERROR_LOG(ACHIEVEMENTS, "Error %d serializing achievement data. Ignoring.", retval); + } break; } + } DoArray(p, buffer, data_size); switch (p.mode) { case PointerWrap::MODE_READ: - rc_client_deserialize_progress(g_rcClient, buffer); + { + int retval = rc_client_deserialize_progress(g_rcClient, buffer); + if (retval != RC_OK) { + // TODO: What should we really do here? + ERROR_LOG(ACHIEVEMENTS, "Error %d deserializing achievement data. Ignoring.", retval); + } break; } + } delete[] buffer; + } else { + if (HasAchievementsOrLeaderboards()) { + auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS); + g_OSD.Show(OSDType::MESSAGE_WARNING, ac->T("Save state loaded without achievement data"), 5.0f); + } + rc_client_reset(g_rcClient); } } @@ -610,6 +645,7 @@ void UnloadGame() { if (g_rcClient) { rc_client_unload_game(g_rcClient); } + g_activeGame = false; } void change_media_callback(int result, const char *error_message, rc_client_t *client, void *userdata) { diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index bc68cebd40..4d689460b4 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -47,6 +47,7 @@ #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" +#include "Core/RetroAchievements.h" #include "HW/MemoryStick.h" #include "GPU/GPUState.h" @@ -391,6 +392,7 @@ namespace SaveState currentMIPS->DoState(p); HLEDoState(p); __KernelDoState(p); + Achievements::DoState(p); // Kernel object destructors might close open files, so do the filesystem last. pspFileSystem.DoState(p); } diff --git a/UI/RetroAchievementScreens.cpp b/UI/RetroAchievementScreens.cpp index 3a04ed6d29..81ab01ee64 100644 --- a/UI/RetroAchievementScreens.cpp +++ b/UI/RetroAchievementScreens.cpp @@ -196,7 +196,9 @@ void RetroAchievementsSettingsScreen::CreateAccountTab(UI::ViewGroup *viewGroup) using namespace UI; - if (Achievements::IsLoggedIn()) { + if (!g_Config.bAchievementsEnable) { + viewGroup->Add(new TextView(ac->T("Achievements are disabled"))); + } else if (Achievements::IsLoggedIn()) { const rc_client_user_t *info = rc_client_get_user_info(Achievements::GetClient()); // In the future, RetroAchievements will support display names. Prepare for that. @@ -255,9 +257,14 @@ void RetroAchievementsSettingsScreen::CreateSettingsTab(UI::ViewGroup *viewGroup using namespace UI; viewGroup->Add(new ItemHeader(ac->T("Settings"))); - viewGroup->Add(new CheckBox(&g_Config.bAchievementsRichPresence, ac->T("Rich Presence"))); - viewGroup->Add(new CheckBox(&g_Config.bAchievementsSoundEffects, ac->T("Sound Effects"))); // not yet implemented - viewGroup->Add(new CheckBox(&g_Config.bAchievementsLogBadMemReads, ac->T("Log bad memory accesses"))); + viewGroup->Add(new CheckBox(&g_Config.bAchievementsEnable, ac->T("Achievements enabled")))->OnClick.Add([&](UI::EventParams &e) -> UI::EventReturn { + Achievements::UpdateSettings(); + RecreateViews(); + return UI::EVENT_DONE; + }); + viewGroup->Add(new CheckBox(&g_Config.bAchievementsRichPresence, ac->T("Rich Presence")))->SetEnabledPtr(&g_Config.bAchievementsEnable); + viewGroup->Add(new CheckBox(&g_Config.bAchievementsSoundEffects, ac->T("Sound Effects")))->SetEnabledPtr(&g_Config.bAchievementsEnable); // not yet implemented + viewGroup->Add(new CheckBox(&g_Config.bAchievementsLogBadMemReads, ac->T("Log bad memory accesses")))->SetEnabledPtr(&g_Config.bAchievementsEnable); // Not yet fully implemented // viewGroup->Add(new CheckBox(&g_Config.bAchievementsChallengeMode, ac->T("Challenge Mode (no savestates)"))); diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index 7cac1f3c79..68cf5bac73 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -106,6 +106,9 @@ + + {533f1d30-d04d-47cc-ad71-20f658907e36} + {beb0a821-3c7f-410f-a525-63afbc69bf8f} diff --git a/assets/lang/en_US.ini b/assets/lang/en_US.ini index 1800175eb9..93180ad81c 100644 --- a/assets/lang/en_US.ini +++ b/assets/lang/en_US.ini @@ -26,6 +26,7 @@ %d achievements, %d points = %d achievements, %d points Account = Account Achievements = Achievements +Achievements are disabled = Achievements are disabled Challenge Mode = Challenge Mode Challenge Mode (no savestates) = Challenge Mode (no savestates) Earned = You have earned %d of %d achievements, and %d of %d points @@ -33,7 +34,6 @@ How to use RetroAchievements = How to use RetroAchievements Leaderboard submission is enabled = Leaderboard submission is enabled Leaderboards = Leaderboards Links = Links -Local = Local Locked achievements = Locked achievements Log bad memory accesses = Log bad memory accesses Mastered %1 = Mastered %1 @@ -41,6 +41,7 @@ Register on www.retroachievements.org = Register on www.retroachievements.org RetroAchievements are not available for this game = RetroAchievements are not available for this game RetroAchievements website = RetroAchievements website Rich Presence = Rich Presence +Save state loaded without achievement data = Save state loaded without achievement data Sound Effects = Sound Effects Statistics = Statistics Submitted Score = Submitted Score diff --git a/headless/Headless.cpp b/headless/Headless.cpp index a59ebafc45..4a79bad5d8 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -143,6 +143,12 @@ void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\ void System_AudioClear() {} void System_AudioPushSamples(const s32 *audio, int numSamples) {} +// TODO: To avoid having to define these here, these should probably be turned into system "requests". +void NativeSaveSecret(const char *nameOfSecret, const std::string &data) {} +std::string NativeLoadSecret(const char *nameOfSecret) { + return ""; +} + int printUsage(const char *progname, const char *reason) { if (reason != NULL) diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index 7329860ba5..695f20dbe4 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -508,6 +508,9 @@ {8bfd8150-94d5-4bf9-8a50-7bd9929a0850} + + {31694510-a8c0-40f6-b09b-e8df825adefa} + {457f45d2-556f-47bc-a31d-aff0d15beaed} diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index ca588ea6c4..66e7fe5a15 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -103,6 +103,12 @@ void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\ void System_AudioClear() {} void System_AudioPushSamples(const s32 *audio, int numSamples) {} +// TODO: To avoid having to define these here, these should probably be turned into system "requests". +void NativeSaveSecret(const char *nameOfSecret, const std::string &data) {} +std::string NativeLoadSecret(const char *nameOfSecret) { + return ""; +} + #if PPSSPP_PLATFORM(ANDROID) JNIEnv *getEnv() { return nullptr; diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj index 687314e43d..e96bfd7892 100644 --- a/unittest/UnitTests.vcxproj +++ b/unittest/UnitTests.vcxproj @@ -426,15 +426,15 @@ {8bfd8150-94d5-4bf9-8a50-7bd9929a0850} + + {31694510-a8c0-40f6-b09b-e8df825adefa} + {f761046e-6c38-4428-a5f1-38391a37bb34} {457f45d2-556f-47bc-a31d-aff0d15beaed} - - {004b8d11-2be3-4bd9-ab40-2be04cf2096f} -