mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-24 14:24:54 +00:00
Merge pull request #12949 from LillyJadeKatrin/retroachievements-new-dev-branch
RetroAchievements - Dev Branch Refactor
This commit is contained in:
commit
258fc1b209
19 changed files with 359 additions and 51 deletions
|
@ -124,6 +124,11 @@ option(OPROFILING "Enable profiling" OFF)
|
||||||
# TODO: Add DSPSpy
|
# TODO: Add DSPSpy
|
||||||
option(DSPTOOL "Build dsptool" OFF)
|
option(DSPTOOL "Build dsptool" OFF)
|
||||||
|
|
||||||
|
# RetroAchievements developer tools require Windows hooks
|
||||||
|
if(WIN32)
|
||||||
|
option(RC_CLIENT_SUPPORTS_RAINTEGRATION "Enables RetroAchievements developer tools" ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Enable SDL by default on operating systems that aren't Android.
|
# Enable SDL by default on operating systems that aren't Android.
|
||||||
if(NOT ANDROID)
|
if(NOT ANDROID)
|
||||||
option(ENABLE_SDL "Enables SDL as a generic controller backend" ON)
|
option(ENABLE_SDL "Enables SDL as a generic controller backend" ON)
|
||||||
|
|
8
Externals/rcheevos/CMakeLists.txt
vendored
8
Externals/rcheevos/CMakeLists.txt
vendored
|
@ -43,9 +43,12 @@ add_library(rcheevos
|
||||||
rcheevos/src/rhash/hash.c
|
rcheevos/src/rhash/hash.c
|
||||||
rcheevos/src/rhash/md5.c
|
rcheevos/src/rhash/md5.c
|
||||||
rcheevos/src/rhash/md5.h
|
rcheevos/src/rhash/md5.h
|
||||||
|
rcheevos/src/rhash/rc_hash_internal.h
|
||||||
rcheevos/src/rurl/url.c
|
rcheevos/src/rurl/url.c
|
||||||
rcheevos/src/rc_client.c
|
rcheevos/src/rc_client.c
|
||||||
|
rcheevos/src/rc_client_external.c
|
||||||
rcheevos/src/rc_client_external.h
|
rcheevos/src/rc_client_external.h
|
||||||
|
rcheevos/src/rc_client_external_versions.h
|
||||||
rcheevos/src/rc_client_internal.h
|
rcheevos/src/rc_client_internal.h
|
||||||
rcheevos/src/rc_client_raintegration.c
|
rcheevos/src/rc_client_raintegration.c
|
||||||
rcheevos/src/rc_client_raintegration_internal.h
|
rcheevos/src/rc_client_raintegration_internal.h
|
||||||
|
@ -61,6 +64,11 @@ target_include_directories(rcheevos PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/rcheevo
|
||||||
target_include_directories(rcheevos INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
|
target_include_directories(rcheevos INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
target_compile_definitions(rcheevos PRIVATE "RC_DISABLE_LUA=1" "RCHEEVOS_URL_SSL")
|
target_compile_definitions(rcheevos PRIVATE "RC_DISABLE_LUA=1" "RCHEEVOS_URL_SSL")
|
||||||
target_compile_definitions(rcheevos PRIVATE "RC_CLIENT_SUPPORTS_HASH")
|
target_compile_definitions(rcheevos PRIVATE "RC_CLIENT_SUPPORTS_HASH")
|
||||||
|
target_compile_definitions(rcheevos PRIVATE "RC_CLIENT_SUPPORTS_EXTERNAL")
|
||||||
|
target_compile_definitions(rcheevos PRIVATE "RC_HASH_NO_ENCRYPTED")
|
||||||
|
target_compile_definitions(rcheevos PRIVATE "RC_HASH_NO_ROM")
|
||||||
|
target_compile_definitions(rcheevos PRIVATE "RC_HASH_NO_ZIP")
|
||||||
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||||
|
target_compile_definitions(rcheevos PRIVATE "RC_CLIENT_SUPPORTS_RAINTEGRATION")
|
||||||
target_compile_definitions(rcheevos PRIVATE "_CRT_SECURE_NO_WARNINGS")
|
target_compile_definitions(rcheevos PRIVATE "_CRT_SECURE_NO_WARNINGS")
|
||||||
endif()
|
endif()
|
||||||
|
|
2
Externals/rcheevos/rcheevos
vendored
2
Externals/rcheevos/rcheevos
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit d54cf8f1059cebc90a6f5ecdf03df69259f22054
|
Subproject commit 022ac70cff6cf60c8957de63d6297998904a6f05
|
5
Externals/rcheevos/rcheevos.vcxproj
vendored
5
Externals/rcheevos/rcheevos.vcxproj
vendored
|
@ -41,6 +41,7 @@
|
||||||
<ClCompile Include="rcheevos\src\rhash\md5.c" />
|
<ClCompile Include="rcheevos\src\rhash\md5.c" />
|
||||||
<ClCompile Include="rcheevos\src\rurl\url.c" />
|
<ClCompile Include="rcheevos\src\rurl\url.c" />
|
||||||
<ClCompile Include="rcheevos\src\rc_client.c" />
|
<ClCompile Include="rcheevos\src\rc_client.c" />
|
||||||
|
<ClCompile Include="rcheevos\src\rc_client_external.c" />
|
||||||
<ClCompile Include="rcheevos\src\rc_client_raintegration.c" />
|
<ClCompile Include="rcheevos\src\rc_client_raintegration.c" />
|
||||||
<ClCompile Include="rcheevos\src\rc_compat.c" />
|
<ClCompile Include="rcheevos\src\rc_compat.c" />
|
||||||
<ClCompile Include="rcheevos\src\rc_util.c" />
|
<ClCompile Include="rcheevos\src\rc_util.c" />
|
||||||
|
@ -68,7 +69,9 @@
|
||||||
<ClInclude Include="rcheevos\src\rcheevos\rc_validate.h" />
|
<ClInclude Include="rcheevos\src\rcheevos\rc_validate.h" />
|
||||||
<ClInclude Include="rcheevos\src\rhash\aes.h" />
|
<ClInclude Include="rcheevos\src\rhash\aes.h" />
|
||||||
<ClInclude Include="rcheevos\src\rhash\md5.h" />
|
<ClInclude Include="rcheevos\src\rhash\md5.h" />
|
||||||
|
<ClInclude Include="rcheevos\src\rhash\rc_hash_internal.h" />
|
||||||
<ClInclude Include="rcheevos\src\rc_client_external.h" />
|
<ClInclude Include="rcheevos\src\rc_client_external.h" />
|
||||||
|
<ClInclude Include="rcheevos\src\rc_client_external_versions.h" />
|
||||||
<ClInclude Include="rcheevos\src\rc_client_internal.h" />
|
<ClInclude Include="rcheevos\src\rc_client_internal.h" />
|
||||||
<ClInclude Include="rcheevos\src\rc_client_raintegration_internal.h" />
|
<ClInclude Include="rcheevos\src\rc_client_raintegration_internal.h" />
|
||||||
<ClInclude Include="rcheevos\src\rc_compat.h" />
|
<ClInclude Include="rcheevos\src\rc_compat.h" />
|
||||||
|
@ -76,7 +79,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<PreprocessorDefinitions>RC_DISABLE_LUA;RCHEEVOS_URL_SSL;RC_CLIENT_SUPPORTS_HASH;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>RC_DISABLE_LUA;RCHEEVOS_URL_SSL;RC_CLIENT_SUPPORTS_HASH;RC_CLIENT_SUPPORTS_EXTERNAL;RC_CLIENT_SUPPORTS_RAINTEGRATION;RC_HASH_NO_ENCRYPTED;RC_HASH_NO_ROM;RC_HASH_NO_ZIP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)rcheevos\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)rcheevos\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "Common/Image.h"
|
#include "Common/Image.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/ScopeGuard.h"
|
#include "Common/ScopeGuard.h"
|
||||||
|
#include "Common/StringUtil.h"
|
||||||
#include "Common/Version.h"
|
#include "Common/Version.h"
|
||||||
#include "Common/WorkQueueThread.h"
|
#include "Common/WorkQueueThread.h"
|
||||||
#include "Core/ActionReplay.h"
|
#include "Core/ActionReplay.h"
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
#include "Core/GeckoCode.h"
|
#include "Core/GeckoCode.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/HW/VideoInterface.h"
|
#include "Core/HW/VideoInterface.h"
|
||||||
|
#include "Core/Host.h"
|
||||||
#include "Core/PatchEngine.h"
|
#include "Core/PatchEngine.h"
|
||||||
#include "Core/PowerPC/MMU.h"
|
#include "Core/PowerPC/MMU.h"
|
||||||
#include "Core/System.h"
|
#include "Core/System.h"
|
||||||
|
@ -42,6 +44,12 @@
|
||||||
#include "VideoCommon/OnScreenDisplay.h"
|
#include "VideoCommon/OnScreenDisplay.h"
|
||||||
#include "VideoCommon/VideoEvents.h"
|
#include "VideoCommon/VideoEvents.h"
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
#include <libloaderapi.h>
|
||||||
|
#include <rcheevos/include/rc_client_raintegration.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
static const Common::HttpRequest::Headers USER_AGENT_HEADER = {
|
static const Common::HttpRequest::Headers USER_AGENT_HEADER = {
|
||||||
{"User-Agent", Common::GetUserAgentStr()}};
|
{"User-Agent", Common::GetUserAgentStr()}};
|
||||||
|
|
||||||
|
@ -51,7 +59,7 @@ AchievementManager& AchievementManager::GetInstance()
|
||||||
return s_instance;
|
return s_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementManager::Init()
|
void AchievementManager::Init(void* hwnd)
|
||||||
{
|
{
|
||||||
LoadDefaultBadges();
|
LoadDefaultBadges();
|
||||||
if (!m_client && Config::Get(Config::RA_ENABLED))
|
if (!m_client && Config::Get(Config::RA_ENABLED))
|
||||||
|
@ -73,9 +81,19 @@ void AchievementManager::Init()
|
||||||
m_queue.Reset("AchievementManagerQueue", [](const std::function<void()>& func) { func(); });
|
m_queue.Reset("AchievementManagerQueue", [](const std::function<void()>& func) { func(); });
|
||||||
m_image_queue.Reset("AchievementManagerImageQueue",
|
m_image_queue.Reset("AchievementManagerImageQueue",
|
||||||
[](const std::function<void()>& func) { func(); });
|
[](const std::function<void()>& func) { func(); });
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
// Attempt to load the integration DLL from the directory containing the main client executable.
|
||||||
|
// In x64 build, will look for RA_Integration-x64.dll, then RA_Integration.dll.
|
||||||
|
// In non-x64 build, will only look for RA_Integration.dll.
|
||||||
|
rc_client_begin_load_raintegration(
|
||||||
|
m_client, UTF8ToWString(File::GetExeDirectory()).c_str(), reinterpret_cast<HWND>(hwnd),
|
||||||
|
"Dolphin", Common::GetScmDescStr().c_str(), LoadIntegrationCallback, NULL);
|
||||||
|
#else // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
if (HasAPIToken())
|
if (HasAPIToken())
|
||||||
Login("");
|
Login("");
|
||||||
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager Initialized");
|
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager Initialized");
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,12 +181,17 @@ void AchievementManager::LoadGame(const std::string& file_path, const DiscIO::Vo
|
||||||
rc_client_set_unofficial_enabled(m_client, Config::Get(Config::RA_UNOFFICIAL_ENABLED));
|
rc_client_set_unofficial_enabled(m_client, Config::Get(Config::RA_UNOFFICIAL_ENABLED));
|
||||||
rc_client_set_encore_mode_enabled(m_client, Config::Get(Config::RA_ENCORE_ENABLED));
|
rc_client_set_encore_mode_enabled(m_client, Config::Get(Config::RA_ENCORE_ENABLED));
|
||||||
rc_client_set_spectator_mode_enabled(m_client, Config::Get(Config::RA_SPECTATOR_ENABLED));
|
rc_client_set_spectator_mode_enabled(m_client, Config::Get(Config::RA_SPECTATOR_ENABLED));
|
||||||
if (volume)
|
|
||||||
{
|
{
|
||||||
std::lock_guard lg{m_lock};
|
std::lock_guard lg{m_lock};
|
||||||
if (!m_loading_volume)
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
SplitPath(file_path, nullptr, &m_title_estimate, nullptr);
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
if (volume)
|
||||||
{
|
{
|
||||||
m_loading_volume = DiscIO::CreateVolume(volume->GetBlobReader().CopyReader());
|
if (!m_loading_volume)
|
||||||
|
{
|
||||||
|
m_loading_volume = DiscIO::CreateVolume(volume->GetBlobReader().CopyReader());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::lock_guard lg{m_filereader_lock};
|
std::lock_guard lg{m_filereader_lock};
|
||||||
|
@ -292,15 +315,26 @@ void AchievementManager::FetchGameBadges()
|
||||||
|
|
||||||
void AchievementManager::DoFrame()
|
void AchievementManager::DoFrame()
|
||||||
{
|
{
|
||||||
if (!IsGameLoaded() || !Core::IsCPUThread())
|
if (!(IsGameLoaded() || m_dll_found) || !Core::IsCPUThread())
|
||||||
return;
|
return;
|
||||||
{
|
{
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
if (m_dll_found)
|
||||||
|
{
|
||||||
|
std::lock_guard lg{m_memory_lock};
|
||||||
|
Core::System* system = m_system.load(std::memory_order_acquire);
|
||||||
|
if (!system)
|
||||||
|
return;
|
||||||
|
Core::CPUThreadGuard thread_guard(*system);
|
||||||
|
u32 ram_size = system->GetMemory().GetRamSizeReal();
|
||||||
|
if (m_cloned_memory.size() != ram_size)
|
||||||
|
m_cloned_memory.resize(ram_size);
|
||||||
|
system->GetMemory().CopyFromEmu(m_cloned_memory.data(), 0, m_cloned_memory.size());
|
||||||
|
}
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
std::lock_guard lg{m_lock};
|
std::lock_guard lg{m_lock};
|
||||||
rc_client_do_frame(m_client);
|
rc_client_do_frame(m_client);
|
||||||
}
|
}
|
||||||
Core::System* system = m_system.load(std::memory_order_acquire);
|
|
||||||
if (!system)
|
|
||||||
return;
|
|
||||||
auto current_time = std::chrono::steady_clock::now();
|
auto current_time = std::chrono::steady_clock::now();
|
||||||
if (current_time - m_last_rp_time > std::chrono::seconds{10})
|
if (current_time - m_last_rp_time > std::chrono::seconds{10})
|
||||||
{
|
{
|
||||||
|
@ -621,6 +655,22 @@ std::vector<std::string> AchievementManager::GetActiveLeaderboards() const
|
||||||
return display_values;
|
return display_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
const rc_client_raintegration_menu_t* AchievementManager::GetDevelopmentMenu()
|
||||||
|
{
|
||||||
|
if (!m_dll_found)
|
||||||
|
return nullptr;
|
||||||
|
return rc_client_raintegration_get_menu(m_client);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 AchievementManager::ActivateDevMenuItem(u32 menu_item_id)
|
||||||
|
{
|
||||||
|
if (!m_dll_found)
|
||||||
|
return 0;
|
||||||
|
return rc_client_raintegration_activate_menu_item(m_client, menu_item_id);
|
||||||
|
}
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
void AchievementManager::DoState(PointerWrap& p)
|
void AchievementManager::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
if (!m_client || !Config::Get(Config::RA_ENABLED))
|
if (!m_client || !Config::Get(Config::RA_ENABLED))
|
||||||
|
@ -716,6 +766,7 @@ void AchievementManager::Shutdown()
|
||||||
// DON'T log out - keep those credentials for next run.
|
// DON'T log out - keep those credentials for next run.
|
||||||
rc_client_destroy(m_client);
|
rc_client_destroy(m_client);
|
||||||
m_client = nullptr;
|
m_client = nullptr;
|
||||||
|
m_dll_found = false;
|
||||||
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager shut down.");
|
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager shut down.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -921,7 +972,8 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro
|
||||||
void AchievementManager::LoadGameCallback(int result, const char* error_message,
|
void AchievementManager::LoadGameCallback(int result, const char* error_message,
|
||||||
rc_client_t* client, void* userdata)
|
rc_client_t* client, void* userdata)
|
||||||
{
|
{
|
||||||
AchievementManager::GetInstance().m_loading_volume.reset(nullptr);
|
auto& instance = AchievementManager::GetInstance();
|
||||||
|
instance.m_loading_volume.reset(nullptr);
|
||||||
if (result == RC_API_FAILURE)
|
if (result == RC_API_FAILURE)
|
||||||
{
|
{
|
||||||
WARN_LOG_FMT(ACHIEVEMENTS, "Load data request rejected for old Dolphin version.");
|
WARN_LOG_FMT(ACHIEVEMENTS, "Load data request rejected for old Dolphin version.");
|
||||||
|
@ -936,6 +988,12 @@ void AchievementManager::LoadGameCallback(int result, const char* error_message,
|
||||||
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to load data for current game.");
|
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to load data for current game.");
|
||||||
OSD::AddMessage("Achievements are not supported for this title.", OSD::Duration::VERY_LONG,
|
OSD::AddMessage("Achievements are not supported for this title.", OSD::Duration::VERY_LONG,
|
||||||
OSD::Color::RED);
|
OSD::Color::RED);
|
||||||
|
if (instance.m_dll_found && result == RC_NO_GAME_LOADED)
|
||||||
|
{
|
||||||
|
// Allow developer tools for unidentified games
|
||||||
|
rc_client_set_read_memory_function(instance.m_client, MemoryPeeker);
|
||||||
|
instance.m_system.store(&Core::System::GetInstance(), std::memory_order_release);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -949,7 +1007,6 @@ void AchievementManager::LoadGameCallback(int result, const char* error_message,
|
||||||
}
|
}
|
||||||
INFO_LOG_FMT(ACHIEVEMENTS, "Loaded data for game ID {}.", game->id);
|
INFO_LOG_FMT(ACHIEVEMENTS, "Loaded data for game ID {}.", game->id);
|
||||||
|
|
||||||
auto& instance = AchievementManager::GetInstance();
|
|
||||||
rc_client_set_read_memory_function(instance.m_client, MemoryPeeker);
|
rc_client_set_read_memory_function(instance.m_client, MemoryPeeker);
|
||||||
instance.m_display_welcome_message = true;
|
instance.m_display_welcome_message = true;
|
||||||
instance.FetchGameBadges();
|
instance.FetchGameBadges();
|
||||||
|
@ -1035,6 +1092,7 @@ void AchievementManager::DisplayWelcomeMessage()
|
||||||
void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t* client_event)
|
void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t* client_event)
|
||||||
{
|
{
|
||||||
const auto& instance = AchievementManager::GetInstance();
|
const auto& instance = AchievementManager::GetInstance();
|
||||||
|
|
||||||
OSD::AddMessage(fmt::format("Unlocked: {} ({})", client_event->achievement->title,
|
OSD::AddMessage(fmt::format("Unlocked: {} ({})", client_event->achievement->title,
|
||||||
client_event->achievement->points),
|
client_event->achievement->points),
|
||||||
OSD::Duration::VERY_LONG,
|
OSD::Duration::VERY_LONG,
|
||||||
|
@ -1043,6 +1101,30 @@ void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t
|
||||||
&instance.GetAchievementBadge(client_event->achievement->id, false));
|
&instance.GetAchievementBadge(client_event->achievement->id, false));
|
||||||
AchievementManager::GetInstance().m_update_callback(
|
AchievementManager::GetInstance().m_update_callback(
|
||||||
UpdatedItems{.achievements = {client_event->achievement->id}});
|
UpdatedItems{.achievements = {client_event->achievement->id}});
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
switch (rc_client_raintegration_get_achievement_state(instance.m_client,
|
||||||
|
client_event->achievement->id))
|
||||||
|
{
|
||||||
|
case RC_CLIENT_RAINTEGRATION_ACHIEVEMENT_STATE_LOCAL:
|
||||||
|
// Achievement only exists locally and has not been uploaded.
|
||||||
|
OSD::AddMessage("Local achievement; not submitted to site.", OSD::Duration::VERY_LONG,
|
||||||
|
OSD::Color::GREEN);
|
||||||
|
break;
|
||||||
|
case RC_CLIENT_RAINTEGRATION_ACHIEVEMENT_STATE_MODIFIED:
|
||||||
|
// Achievement has been modified locally and differs from the one on the site.
|
||||||
|
OSD::AddMessage("Modified achievement; not submitted to site.", OSD::Duration::VERY_LONG,
|
||||||
|
OSD::Color::GREEN);
|
||||||
|
break;
|
||||||
|
case RC_CLIENT_RAINTEGRATION_ACHIEVEMENT_STATE_INSECURE:
|
||||||
|
// The player has done something that we consider cheating like modifying the RAM while playing.
|
||||||
|
// Just indicate that the achievement was only unlocked locally, but don't clarify why.
|
||||||
|
OSD::AddMessage("Achievement not submitted to site.", OSD::Duration::VERY_LONG,
|
||||||
|
OSD::Color::GREEN);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementManager::HandleLeaderboardStartedEvent(const rc_client_event_t* client_event)
|
void AchievementManager::HandleLeaderboardStartedEvent(const rc_client_event_t* client_event)
|
||||||
|
@ -1229,16 +1311,33 @@ u32 AchievementManager::MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_
|
||||||
{
|
{
|
||||||
if (buffer == nullptr)
|
if (buffer == nullptr)
|
||||||
return 0u;
|
return 0u;
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
auto& instance = AchievementManager::GetInstance();
|
||||||
|
if (instance.m_dll_found)
|
||||||
|
{
|
||||||
|
std::lock_guard lg{instance.m_memory_lock};
|
||||||
|
if (u64(address) + num_bytes >= instance.m_cloned_memory.size())
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(ACHIEVEMENTS,
|
||||||
|
"Attempt to read past memory size: size {} address {} write length {}",
|
||||||
|
instance.m_cloned_memory.size(), address, num_bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::copy(instance.m_cloned_memory.begin() + address,
|
||||||
|
instance.m_cloned_memory.begin() + address + num_bytes, buffer);
|
||||||
|
return num_bytes;
|
||||||
|
}
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
auto& system = Core::System::GetInstance();
|
auto& system = Core::System::GetInstance();
|
||||||
if (!(Core::IsHostThread() || Core::IsCPUThread()))
|
if (!(Core::IsHostThread() || Core::IsCPUThread()))
|
||||||
{
|
{
|
||||||
ASSERT_MSG(ACHIEVEMENTS, false, "MemoryPeeker called from wrong thread");
|
ASSERT_MSG(ACHIEVEMENTS, false, "MemoryPeeker called from wrong thread");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Core::CPUThreadGuard threadguard(system);
|
Core::CPUThreadGuard thread_guard(system);
|
||||||
for (u32 num_read = 0; num_read < num_bytes; num_read++)
|
for (u32 num_read = 0; num_read < num_bytes; num_read++)
|
||||||
{
|
{
|
||||||
auto value = system.GetMMU().HostTryReadU8(threadguard, address + num_read,
|
auto value = system.GetMMU().HostTryReadU8(thread_guard, address + num_read,
|
||||||
PowerPC::RequestedAddressSpace::Physical);
|
PowerPC::RequestedAddressSpace::Physical);
|
||||||
if (!value.has_value())
|
if (!value.has_value())
|
||||||
return num_read;
|
return num_read;
|
||||||
|
@ -1395,4 +1494,97 @@ void AchievementManager::EventHandler(const rc_client_event_t* event, rc_client_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
void AchievementManager::LoadIntegrationCallback(int result, const char* error_message,
|
||||||
|
rc_client_t* client, void* userdata)
|
||||||
|
{
|
||||||
|
auto& instance = AchievementManager::GetInstance();
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case RC_OK:
|
||||||
|
INFO_LOG_FMT(ACHIEVEMENTS, "RAIntegration.dll found.");
|
||||||
|
instance.m_dll_found = true;
|
||||||
|
rc_client_raintegration_set_event_handler(instance.m_client, RAIntegrationEventHandler);
|
||||||
|
rc_client_raintegration_set_write_memory_function(instance.m_client, MemoryPoker);
|
||||||
|
rc_client_raintegration_set_get_game_name_function(instance.m_client, GameTitleEstimateHandler);
|
||||||
|
instance.m_dev_menu_callback();
|
||||||
|
// TODO: hook up menu and dll event handlers
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RC_MISSING_VALUE:
|
||||||
|
INFO_LOG_FMT(ACHIEVEMENTS, "RAIntegration.dll not found.");
|
||||||
|
// DLL is not present; do nothing.
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to load RAIntegration.dll. {}", error_message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance.HasAPIToken())
|
||||||
|
instance.Login("");
|
||||||
|
INFO_LOG_FMT(ACHIEVEMENTS, "Achievement Manager Initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AchievementManager::RAIntegrationEventHandler(const rc_client_raintegration_event_t* event,
|
||||||
|
rc_client_t* client)
|
||||||
|
{
|
||||||
|
auto& instance = AchievementManager::GetInstance();
|
||||||
|
switch (event->type)
|
||||||
|
{
|
||||||
|
case RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED:
|
||||||
|
case RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED:
|
||||||
|
instance.m_dev_menu_callback();
|
||||||
|
break;
|
||||||
|
case RC_CLIENT_RAINTEGRATION_EVENT_PAUSE:
|
||||||
|
{
|
||||||
|
Core::QueueHostJob([](Core::System& system) { Core::SetState(system, Core::State::Paused); });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED:
|
||||||
|
Config::SetBaseOrCurrent(Config::RA_HARDCORE_ENABLED,
|
||||||
|
!Config::Get(Config::RA_HARDCORE_ENABLED));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WARN_LOG_FMT(ACHIEVEMENTS, "Unsupported raintegration event. {}", event->type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AchievementManager::MemoryPoker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client)
|
||||||
|
{
|
||||||
|
if (buffer == nullptr)
|
||||||
|
return;
|
||||||
|
if (!(Core::IsHostThread() || Core::IsCPUThread()))
|
||||||
|
{
|
||||||
|
Core::QueueHostJob([address, buffer, num_bytes, client](Core::System& system) {
|
||||||
|
MemoryPoker(address, buffer, num_bytes, client);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& instance = AchievementManager::GetInstance();
|
||||||
|
if (u64(address) + num_bytes >= instance.m_cloned_memory.size())
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(ACHIEVEMENTS,
|
||||||
|
"Attempt to write past memory size: size {} address {} write length {}",
|
||||||
|
instance.m_cloned_memory.size(), address, num_bytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Core::System* system = instance.m_system.load(std::memory_order_acquire);
|
||||||
|
if (!system)
|
||||||
|
return;
|
||||||
|
Core::CPUThreadGuard thread_guard(*system);
|
||||||
|
std::lock_guard lg{instance.m_memory_lock};
|
||||||
|
system->GetMemory().CopyToEmu(address, buffer, num_bytes);
|
||||||
|
std::copy(buffer, buffer + num_bytes, instance.m_cloned_memory.begin() + address);
|
||||||
|
}
|
||||||
|
void AchievementManager::GameTitleEstimateHandler(char* buffer, u32 buffer_size,
|
||||||
|
rc_client_t* client)
|
||||||
|
{
|
||||||
|
auto& instance = AchievementManager::GetInstance();
|
||||||
|
std::lock_guard lg{instance.m_lock};
|
||||||
|
strncpy(buffer, instance.m_title_estimate.c_str(), static_cast<size_t>(buffer_size));
|
||||||
|
}
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
#endif // USE_RETRO_ACHIEVEMENTS
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
|
@ -34,6 +34,10 @@
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
#include "VideoCommon/Assets/CustomTextureData.h"
|
#include "VideoCommon/Assets/CustomTextureData.h"
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
#include <rcheevos/include/rc_client_raintegration.h>
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
class CPUThreadGuard;
|
class CPUThreadGuard;
|
||||||
|
@ -113,7 +117,7 @@ public:
|
||||||
using UpdateCallback = std::function<void(const UpdatedItems&)>;
|
using UpdateCallback = std::function<void(const UpdatedItems&)>;
|
||||||
|
|
||||||
static AchievementManager& GetInstance();
|
static AchievementManager& GetInstance();
|
||||||
void Init();
|
void Init(void* hwnd);
|
||||||
void SetUpdateCallback(UpdateCallback callback);
|
void SetUpdateCallback(UpdateCallback callback);
|
||||||
void Login(const std::string& password);
|
void Login(const std::string& password);
|
||||||
bool HasAPIToken() const;
|
bool HasAPIToken() const;
|
||||||
|
@ -161,6 +165,16 @@ public:
|
||||||
const std::unordered_set<AchievementId>& GetActiveChallenges() const;
|
const std::unordered_set<AchievementId>& GetActiveChallenges() const;
|
||||||
std::vector<std::string> GetActiveLeaderboards() const;
|
std::vector<std::string> GetActiveLeaderboards() const;
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
const rc_client_raintegration_menu_t* GetDevelopmentMenu();
|
||||||
|
u32 ActivateDevMenuItem(u32 menu_item_id);
|
||||||
|
void SetDevMenuUpdateCallback(std::function<void(void)> callback)
|
||||||
|
{
|
||||||
|
m_dev_menu_callback = callback;
|
||||||
|
};
|
||||||
|
bool CheckForModifications() { return rc_client_raintegration_has_modifications(m_client); };
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
void DoState(PointerWrap& p);
|
void DoState(PointerWrap& p);
|
||||||
|
|
||||||
void CloseGame();
|
void CloseGame();
|
||||||
|
@ -235,6 +249,15 @@ private:
|
||||||
const UpdatedItems callback_data);
|
const UpdatedItems callback_data);
|
||||||
static void EventHandler(const rc_client_event_t* event, rc_client_t* client);
|
static void EventHandler(const rc_client_event_t* event, rc_client_t* client);
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
static void LoadIntegrationCallback(int result, const char* error_message, rc_client_t* client,
|
||||||
|
void* userdata);
|
||||||
|
static void RAIntegrationEventHandler(const rc_client_raintegration_event_t* event,
|
||||||
|
rc_client_t* client);
|
||||||
|
static void MemoryPoker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client);
|
||||||
|
static void GameTitleEstimateHandler(char* buffer, u32 buffer_size, rc_client_t* client);
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
rc_runtime_t m_runtime{};
|
rc_runtime_t m_runtime{};
|
||||||
rc_client_t* m_client{};
|
rc_client_t* m_client{};
|
||||||
std::atomic<Core::System*> m_system{};
|
std::atomic<Core::System*> m_system{};
|
||||||
|
@ -266,6 +289,14 @@ private:
|
||||||
std::unordered_set<AchievementId> m_active_challenges;
|
std::unordered_set<AchievementId> m_active_challenges;
|
||||||
std::vector<rc_client_leaderboard_tracker_t> m_active_leaderboards;
|
std::vector<rc_client_leaderboard_tracker_t> m_active_leaderboards;
|
||||||
|
|
||||||
|
bool m_dll_found = false;
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
std::function<void(void)> m_dev_menu_callback;
|
||||||
|
std::vector<u8> m_cloned_memory;
|
||||||
|
std::recursive_mutex m_memory_lock;
|
||||||
|
std::string m_title_estimate;
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
Common::WorkQueueThread<std::function<void()>> m_queue;
|
Common::WorkQueueThread<std::function<void()>> m_queue;
|
||||||
Common::WorkQueueThread<std::function<void()>> m_image_queue;
|
Common::WorkQueueThread<std::function<void()>> m_image_queue;
|
||||||
mutable std::recursive_mutex m_lock;
|
mutable std::recursive_mutex m_lock;
|
||||||
|
|
|
@ -789,4 +789,7 @@ if(USE_RETRO_ACHIEVEMENTS)
|
||||||
target_link_libraries(core PUBLIC rcheevos)
|
target_link_libraries(core PUBLIC rcheevos)
|
||||||
target_compile_definitions(core PUBLIC -DUSE_RETRO_ACHIEVEMENTS)
|
target_compile_definitions(core PUBLIC -DUSE_RETRO_ACHIEVEMENTS)
|
||||||
target_compile_definitions(core PUBLIC -DRC_CLIENT_SUPPORTS_HASH)
|
target_compile_definitions(core PUBLIC -DRC_CLIENT_SUPPORTS_HASH)
|
||||||
|
if(RC_CLIENT_SUPPORTS_RAINTEGRATION)
|
||||||
|
target_compile_definitions(core PUBLIC -DRC_CLIENT_SUPPORTS_RAINTEGRATION)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
static constexpr size_t PROGRESS_LENGTH = 24;
|
static constexpr size_t PROGRESS_LENGTH = 24;
|
||||||
|
|
||||||
AchievementBox::AchievementBox(QWidget* parent, rc_client_achievement_t* achievement)
|
AchievementBox::AchievementBox(QWidget* parent, const rc_client_achievement_t* achievement)
|
||||||
: QGroupBox(parent), m_achievement(achievement)
|
: QGroupBox(parent), m_achievement(achievement)
|
||||||
{
|
{
|
||||||
const auto& instance = AchievementManager::GetInstance();
|
const auto& instance = AchievementManager::GetInstance();
|
||||||
|
|
|
@ -18,7 +18,7 @@ class AchievementBox final : public QGroupBox
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit AchievementBox(QWidget* parent, rc_client_achievement_t* achievement);
|
explicit AchievementBox(QWidget* parent, const rc_client_achievement_t* achievement);
|
||||||
void UpdateData();
|
void UpdateData();
|
||||||
void UpdateProgress();
|
void UpdateProgress();
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ private:
|
||||||
QProgressBar* m_progress_bar;
|
QProgressBar* m_progress_bar;
|
||||||
QLabel* m_progress_label;
|
QLabel* m_progress_label;
|
||||||
|
|
||||||
rc_client_achievement_t* m_achievement;
|
const rc_client_achievement_t* m_achievement;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // USE_RETRO_ACHIEVEMENTS
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
|
@ -34,10 +34,6 @@ AchievementSettingsWidget::AchievementSettingsWidget(QWidget* parent) : QWidget(
|
||||||
|
|
||||||
connect(&Settings::Instance(), &Settings::ConfigChanged, this,
|
connect(&Settings::Instance(), &Settings::ConfigChanged, this,
|
||||||
&AchievementSettingsWidget::LoadSettings);
|
&AchievementSettingsWidget::LoadSettings);
|
||||||
|
|
||||||
// If hardcore is enabled when the emulator starts, make sure it turns off what it needs to
|
|
||||||
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
|
||||||
UpdateHardcoreMode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementSettingsWidget::UpdateData(int login_failed_code)
|
void AchievementSettingsWidget::UpdateData(int login_failed_code)
|
||||||
|
@ -256,10 +252,9 @@ void AchievementSettingsWidget::ToggleRAIntegration()
|
||||||
|
|
||||||
auto& instance = AchievementManager::GetInstance();
|
auto& instance = AchievementManager::GetInstance();
|
||||||
if (Config::Get(Config::RA_ENABLED))
|
if (Config::Get(Config::RA_ENABLED))
|
||||||
instance.Init();
|
instance.Init(reinterpret_cast<void*>(winId()));
|
||||||
else
|
else
|
||||||
instance.Shutdown();
|
instance.Shutdown();
|
||||||
UpdateHardcoreMode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementSettingsWidget::Login()
|
void AchievementSettingsWidget::Login()
|
||||||
|
@ -297,7 +292,6 @@ void AchievementSettingsWidget::ToggleHardcore()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
UpdateHardcoreMode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementSettingsWidget::ToggleUnofficial()
|
void AchievementSettingsWidget::ToggleUnofficial()
|
||||||
|
@ -327,14 +321,4 @@ void AchievementSettingsWidget::ToggleProgress()
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementSettingsWidget::UpdateHardcoreMode()
|
|
||||||
{
|
|
||||||
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
|
||||||
{
|
|
||||||
Settings::Instance().SetDebugModeEnabled(false);
|
|
||||||
}
|
|
||||||
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
|
|
||||||
emit Settings::Instance().HardcoreStateChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // USE_RETRO_ACHIEVEMENTS
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
|
@ -39,8 +39,6 @@ private:
|
||||||
void ToggleDiscordPresence();
|
void ToggleDiscordPresence();
|
||||||
void ToggleProgress();
|
void ToggleProgress();
|
||||||
|
|
||||||
void UpdateHardcoreMode();
|
|
||||||
|
|
||||||
QGroupBox* m_common_box;
|
QGroupBox* m_common_box;
|
||||||
QVBoxLayout* m_common_layout;
|
QVBoxLayout* m_common_layout;
|
||||||
ToolTipCheckBox* m_common_integration_enabled_input;
|
ToolTipCheckBox* m_common_integration_enabled_input;
|
||||||
|
|
|
@ -39,8 +39,6 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
|
||||||
});
|
});
|
||||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||||
[this] { m_settings_widget->UpdateData(RC_OK); });
|
[this] { m_settings_widget->UpdateData(RC_OK); });
|
||||||
connect(&Settings::Instance(), &Settings::HardcoreStateChanged, this,
|
|
||||||
[this] { AchievementsWindow::UpdateData({.all = true}); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AchievementsWindow::showEvent(QShowEvent* event)
|
void AchievementsWindow::showEvent(QShowEvent* event)
|
||||||
|
|
|
@ -677,4 +677,7 @@ endif()
|
||||||
if(USE_RETRO_ACHIEVEMENTS)
|
if(USE_RETRO_ACHIEVEMENTS)
|
||||||
target_link_libraries(dolphin-emu PRIVATE rcheevos)
|
target_link_libraries(dolphin-emu PRIVATE rcheevos)
|
||||||
target_compile_definitions(dolphin-emu PRIVATE -DUSE_RETRO_ACHIEVEMENTS)
|
target_compile_definitions(dolphin-emu PRIVATE -DUSE_RETRO_ACHIEVEMENTS)
|
||||||
|
if(RC_CLIENT_SUPPORTS_RAINTEGRATION)
|
||||||
|
target_compile_definitions(dolphin-emu PRIVATE -DRC_CLIENT_SUPPORTS_RAINTEGRATION)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "Core/BootManager.h"
|
#include "Core/BootManager.h"
|
||||||
#include "Core/CommonTitles.h"
|
#include "Core/CommonTitles.h"
|
||||||
#include "Core/Config/AchievementSettings.h"
|
#include "Core/Config/AchievementSettings.h"
|
||||||
|
#include "Core/Config/FreeLookSettings.h"
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/Config/NetplaySettings.h"
|
#include "Core/Config/NetplaySettings.h"
|
||||||
#include "Core/Config/UISettings.h"
|
#include "Core/Config/UISettings.h"
|
||||||
|
@ -272,9 +273,15 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr<BootParameters> boo
|
||||||
NetPlayInit();
|
NetPlayInit();
|
||||||
|
|
||||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
AchievementManager::GetInstance().Init();
|
AchievementManager::GetInstance().Init(reinterpret_cast<void*>(winId()));
|
||||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||||
Settings::Instance().SetDebugModeEnabled(false);
|
Settings::Instance().SetDebugModeEnabled(false);
|
||||||
|
// This needs to trigger on both RA_HARDCORE_ENABLED and RA_ENABLED
|
||||||
|
Config::AddConfigChangedCallback(
|
||||||
|
[this]() { QueueOnObject(this, [this] { this->OnHardcoreChanged(); }); });
|
||||||
|
// If hardcore is enabled when the emulator starts, make sure it turns off what it needs to
|
||||||
|
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
||||||
|
OnHardcoreChanged();
|
||||||
#endif // USE_RETRO_ACHIEVEMENTS
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
||||||
#if defined(__unix__) || defined(__unix) || defined(__APPLE__)
|
#if defined(__unix__) || defined(__unix) || defined(__APPLE__)
|
||||||
|
@ -935,7 +942,11 @@ bool MainWindow::RequestStop()
|
||||||
else
|
else
|
||||||
FullScreen();
|
FullScreen();
|
||||||
|
|
||||||
if (Config::Get(Config::MAIN_CONFIRM_ON_STOP))
|
bool confirm_on_stop = Config::Get(Config::MAIN_CONFIRM_ON_STOP);
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
confirm_on_stop = confirm_on_stop || AchievementManager::GetInstance().CheckForModifications();
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
if (confirm_on_stop)
|
||||||
{
|
{
|
||||||
if (std::exchange(m_stop_confirm_showing, true))
|
if (std::exchange(m_stop_confirm_showing, true))
|
||||||
return true;
|
return true;
|
||||||
|
@ -960,13 +971,27 @@ bool MainWindow::RequestStop()
|
||||||
// This is to avoid any "race conditions" between the "Window Activate" message and the
|
// This is to avoid any "race conditions" between the "Window Activate" message and the
|
||||||
// message box returning, which could break cursor locking depending on the order
|
// message box returning, which could break cursor locking depending on the order
|
||||||
m_render_widget->SetWaitingForMessageBox(true);
|
m_render_widget->SetWaitingForMessageBox(true);
|
||||||
auto confirm = ModalMessageBox::question(
|
QString message;
|
||||||
confirm_parent, tr("Confirm"),
|
if (m_stop_requested)
|
||||||
m_stop_requested ? tr("A shutdown is already in progress. Unsaved data "
|
{
|
||||||
"may be lost if you stop the current emulation "
|
message = tr("A shutdown is already in progress. Unsaved data "
|
||||||
"before it completes. Force stop?") :
|
"may be lost if you stop the current emulation "
|
||||||
tr("Do you want to stop the current emulation?"),
|
"before it completes. Force stop?");
|
||||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton, Qt::ApplicationModal);
|
}
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
else if (AchievementManager::GetInstance().CheckForModifications())
|
||||||
|
{
|
||||||
|
message = tr(
|
||||||
|
"Do you want to stop the current emulation? Unsaved achievement modifications detected.");
|
||||||
|
}
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message = tr("Do you want to stop the current emulation?");
|
||||||
|
}
|
||||||
|
auto confirm = ModalMessageBox::question(confirm_parent, tr("Confirm"), message,
|
||||||
|
QMessageBox::Yes | QMessageBox::No,
|
||||||
|
QMessageBox::NoButton, Qt::ApplicationModal);
|
||||||
|
|
||||||
// If a user confirmed stopping the emulation, we do not capture the cursor again,
|
// If a user confirmed stopping the emulation, we do not capture the cursor again,
|
||||||
// even if the render widget will stay alive for a while.
|
// even if the render widget will stay alive for a while.
|
||||||
|
@ -1992,6 +2017,13 @@ void MainWindow::ShowAchievementSettings()
|
||||||
ShowAchievementsWindow();
|
ShowAchievementsWindow();
|
||||||
m_achievements_window->ForceSettingsTab();
|
m_achievements_window->ForceSettingsTab();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::OnHardcoreChanged()
|
||||||
|
{
|
||||||
|
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
||||||
|
Settings::Instance().SetDebugModeEnabled(false);
|
||||||
|
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
|
||||||
|
}
|
||||||
#endif // USE_RETRO_ACHIEVEMENTS
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
||||||
void MainWindow::ShowMemcardManager()
|
void MainWindow::ShowMemcardManager()
|
||||||
|
|
|
@ -181,6 +181,7 @@ private:
|
||||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
void ShowAchievementsWindow();
|
void ShowAchievementsWindow();
|
||||||
void ShowAchievementSettings();
|
void ShowAchievementSettings();
|
||||||
|
void OnHardcoreChanged();
|
||||||
#endif // USE_RETRO_ACHIEVEMENTS
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
||||||
void NetPlayInit();
|
void NetPlayInit();
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
#include "DolphinQt/QtUtils/DolphinFileDialog.h"
|
#include "DolphinQt/QtUtils/DolphinFileDialog.h"
|
||||||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||||
#include "DolphinQt/QtUtils/ParallelProgressDialog.h"
|
#include "DolphinQt/QtUtils/ParallelProgressDialog.h"
|
||||||
|
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
||||||
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
|
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
|
||||||
#include "DolphinQt/Settings.h"
|
#include "DolphinQt/Settings.h"
|
||||||
#include "DolphinQt/Updater.h"
|
#include "DolphinQt/Updater.h"
|
||||||
|
@ -71,6 +72,10 @@
|
||||||
#include "UICommon/AutoUpdate.h"
|
#include "UICommon/AutoUpdate.h"
|
||||||
#include "UICommon/GameFile.h"
|
#include "UICommon/GameFile.h"
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
#include <rcheevos/include/rc_client_raintegration.h>
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
QPointer<MenuBar> MenuBar::s_menu_bar;
|
QPointer<MenuBar> MenuBar::s_menu_bar;
|
||||||
|
|
||||||
QString MenuBar::GetSignatureSelector() const
|
QString MenuBar::GetSignatureSelector() const
|
||||||
|
@ -284,8 +289,14 @@ void MenuBar::AddToolsMenu()
|
||||||
tools_menu->addSeparator();
|
tools_menu->addSeparator();
|
||||||
|
|
||||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); });
|
m_achievements_action =
|
||||||
|
tools_menu->addAction(tr("Achievements"), this, [this] { emit ShowAchievementsWindow(); });
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
m_achievements_dev_menu = tools_menu->addMenu(tr("RetroAchievements Development"));
|
||||||
|
AchievementManager::GetInstance().SetDevMenuUpdateCallback(
|
||||||
|
[this]() { QueueOnObject(this, [this] { this->UpdateAchievementDevelopmentMenu(); }); });
|
||||||
|
m_achievements_dev_menu->menuAction()->setVisible(false);
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
tools_menu->addSeparator();
|
tools_menu->addSeparator();
|
||||||
#endif // USE_RETRO_ACHIEVEMENTS
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
||||||
|
@ -1124,6 +1135,38 @@ void MenuBar::UpdateToolsMenu(const Core::State state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
void MenuBar::UpdateAchievementDevelopmentMenu()
|
||||||
|
{
|
||||||
|
auto* dev_menu = AchievementManager::GetInstance().GetDevelopmentMenu();
|
||||||
|
if (dev_menu)
|
||||||
|
{
|
||||||
|
m_achievements_dev_menu->menuAction()->setVisible(true);
|
||||||
|
m_achievements_dev_menu->clear();
|
||||||
|
for (u32 i = 0; i < dev_menu->num_items; i++)
|
||||||
|
{
|
||||||
|
const auto& menu_item = dev_menu->items[i];
|
||||||
|
if (menu_item.label == nullptr)
|
||||||
|
{
|
||||||
|
m_achievements_dev_menu->addSeparator();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto* ra_dev_menu_item = m_achievements_dev_menu->addAction(
|
||||||
|
QString::fromStdString(menu_item.label), this,
|
||||||
|
[menu_item]() { AchievementManager::GetInstance().ActivateDevMenuItem(menu_item.id); });
|
||||||
|
ra_dev_menu_item->setEnabled(menu_item.enabled);
|
||||||
|
// Recommended hardcode by RAIntegration.dll developer Jamiras
|
||||||
|
ra_dev_menu_item->setCheckable(i < 2);
|
||||||
|
ra_dev_menu_item->setChecked(menu_item.checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_achievements_dev_menu->menuAction()->setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
void MenuBar::InstallWAD()
|
void MenuBar::InstallWAD()
|
||||||
{
|
{
|
||||||
QString wad_file = DolphinFileDialog::getOpenFileName(this, tr("Select Title to Install to NAND"),
|
QString wad_file = DolphinFileDialog::getOpenFileName(this, tr("Select Title to Install to NAND"),
|
||||||
|
|
|
@ -44,6 +44,9 @@ public:
|
||||||
explicit MenuBar(QWidget* parent = nullptr);
|
explicit MenuBar(QWidget* parent = nullptr);
|
||||||
|
|
||||||
void UpdateToolsMenu(Core::State state);
|
void UpdateToolsMenu(Core::State state);
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
void UpdateAchievementDevelopmentMenu();
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
QMenu* GetListColumnsMenu() const { return m_cols_menu; }
|
QMenu* GetListColumnsMenu() const { return m_cols_menu; }
|
||||||
|
|
||||||
|
@ -205,6 +208,10 @@ private:
|
||||||
QAction* m_wad_install_action;
|
QAction* m_wad_install_action;
|
||||||
QMenu* m_perform_online_update_menu;
|
QMenu* m_perform_online_update_menu;
|
||||||
QAction* m_perform_online_update_for_current_region;
|
QAction* m_perform_online_update_for_current_region;
|
||||||
|
QAction* m_achievements_action;
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
QMenu* m_achievements_dev_menu;
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
QAction* m_ntscj_ipl;
|
QAction* m_ntscj_ipl;
|
||||||
QAction* m_ntscu_ipl;
|
QAction* m_ntscu_ipl;
|
||||||
QAction* m_pal_ipl;
|
QAction* m_pal_ipl;
|
||||||
|
|
|
@ -222,7 +222,6 @@ signals:
|
||||||
void SDCardInsertionChanged(bool inserted);
|
void SDCardInsertionChanged(bool inserted);
|
||||||
void USBKeyboardConnectionChanged(bool connected);
|
void USBKeyboardConnectionChanged(bool connected);
|
||||||
void EnableGfxModsChanged(bool enabled);
|
void EnableGfxModsChanged(bool enabled);
|
||||||
void HardcoreStateChanged();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Settings();
|
Settings();
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
<PreprocessorDefinitions>HAVE_SDL2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>HAVE_SDL2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions>USE_RETRO_ACHIEVEMENTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>USE_RETRO_ACHIEVEMENTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions>RC_CLIENT_SUPPORTS_HASH;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>RC_CLIENT_SUPPORTS_HASH;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<PreprocessorDefinitions>RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions>HAVE_CUBEB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>HAVE_CUBEB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
|
||||||
<!-- Warnings one may want to ignore when using Level4.
|
<!-- Warnings one may want to ignore when using Level4.
|
||||||
|
|
Loading…
Add table
Reference in a new issue