Merge c55624702c
into 1ae0b23265
This commit is contained in:
commit
cd8a19c94a
|
@ -124,6 +124,11 @@ option(OPROFILING "Enable profiling" OFF)
|
|||
# TODO: Add DSPSpy
|
||||
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.
|
||||
if(NOT ANDROID)
|
||||
option(ENABLE_SDL "Enables SDL as a generic controller backend" ON)
|
||||
|
|
|
@ -43,9 +43,12 @@ add_library(rcheevos
|
|||
rcheevos/src/rhash/hash.c
|
||||
rcheevos/src/rhash/md5.c
|
||||
rcheevos/src/rhash/md5.h
|
||||
rcheevos/src/rhash/rc_hash_internal.h
|
||||
rcheevos/src/rurl/url.c
|
||||
rcheevos/src/rc_client.c
|
||||
rcheevos/src/rc_client_external.c
|
||||
rcheevos/src/rc_client_external.h
|
||||
rcheevos/src/rc_client_external_versions.h
|
||||
rcheevos/src/rc_client_internal.h
|
||||
rcheevos/src/rc_client_raintegration.c
|
||||
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_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_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")
|
||||
target_compile_definitions(rcheevos PRIVATE "RC_CLIENT_SUPPORTS_RAINTEGRATION")
|
||||
target_compile_definitions(rcheevos PRIVATE "_CRT_SECURE_NO_WARNINGS")
|
||||
endif()
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d54cf8f1059cebc90a6f5ecdf03df69259f22054
|
||||
Subproject commit 022ac70cff6cf60c8957de63d6297998904a6f05
|
|
@ -41,6 +41,7 @@
|
|||
<ClCompile Include="rcheevos\src\rhash\md5.c" />
|
||||
<ClCompile Include="rcheevos\src\rurl\url.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_compat.c" />
|
||||
<ClCompile Include="rcheevos\src\rc_util.c" />
|
||||
|
@ -68,7 +69,9 @@
|
|||
<ClInclude Include="rcheevos\src\rcheevos\rc_validate.h" />
|
||||
<ClInclude Include="rcheevos\src\rhash\aes.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_versions.h" />
|
||||
<ClInclude Include="rcheevos\src\rc_client_internal.h" />
|
||||
<ClInclude Include="rcheevos\src\rc_client_raintegration_internal.h" />
|
||||
<ClInclude Include="rcheevos\src\rc_compat.h" />
|
||||
|
@ -76,7 +79,7 @@
|
|||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<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>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "Common/Image.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "Core/ActionReplay.h"
|
||||
|
@ -33,6 +34,7 @@
|
|||
#include "Core/GeckoCode.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/PatchEngine.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
#include "Core/System.h"
|
||||
|
@ -42,6 +44,12 @@
|
|||
#include "VideoCommon/OnScreenDisplay.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 = {
|
||||
{"User-Agent", Common::GetUserAgentStr()}};
|
||||
|
||||
|
@ -51,7 +59,7 @@ AchievementManager& AchievementManager::GetInstance()
|
|||
return s_instance;
|
||||
}
|
||||
|
||||
void AchievementManager::Init()
|
||||
void AchievementManager::Init(void* hwnd)
|
||||
{
|
||||
LoadDefaultBadges();
|
||||
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_image_queue.Reset("AchievementManagerImageQueue",
|
||||
[](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())
|
||||
Login("");
|
||||
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_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));
|
||||
if (volume)
|
||||
{
|
||||
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};
|
||||
|
@ -292,15 +315,26 @@ void AchievementManager::FetchGameBadges()
|
|||
|
||||
void AchievementManager::DoFrame()
|
||||
{
|
||||
if (!IsGameLoaded() || !Core::IsCPUThread())
|
||||
if (!(IsGameLoaded() || m_dll_found) || !Core::IsCPUThread())
|
||||
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};
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
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.
|
||||
rc_client_destroy(m_client);
|
||||
m_client = nullptr;
|
||||
m_dll_found = false;
|
||||
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,
|
||||
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)
|
||||
{
|
||||
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.");
|
||||
OSD::AddMessage("Achievements are not supported for this title.", OSD::Duration::VERY_LONG,
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -949,7 +1007,6 @@ void AchievementManager::LoadGameCallback(int result, const char* error_message,
|
|||
}
|
||||
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);
|
||||
instance.m_display_welcome_message = true;
|
||||
instance.FetchGameBadges();
|
||||
|
@ -1035,6 +1092,7 @@ void AchievementManager::DisplayWelcomeMessage()
|
|||
void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t* client_event)
|
||||
{
|
||||
const auto& instance = AchievementManager::GetInstance();
|
||||
|
||||
OSD::AddMessage(fmt::format("Unlocked: {} ({})", client_event->achievement->title,
|
||||
client_event->achievement->points),
|
||||
OSD::Duration::VERY_LONG,
|
||||
|
@ -1043,6 +1101,30 @@ void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t
|
|||
&instance.GetAchievementBadge(client_event->achievement->id, false));
|
||||
AchievementManager::GetInstance().m_update_callback(
|
||||
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)
|
||||
|
@ -1229,16 +1311,33 @@ u32 AchievementManager::MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_
|
|||
{
|
||||
if (buffer == nullptr)
|
||||
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();
|
||||
if (!(Core::IsHostThread() || Core::IsCPUThread()))
|
||||
{
|
||||
ASSERT_MSG(ACHIEVEMENTS, false, "MemoryPeeker called from wrong thread");
|
||||
return 0;
|
||||
}
|
||||
Core::CPUThreadGuard threadguard(system);
|
||||
Core::CPUThreadGuard thread_guard(system);
|
||||
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);
|
||||
if (!value.has_value())
|
||||
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
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
#include "DiscIO/Volume.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
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
|
@ -113,7 +117,7 @@ public:
|
|||
using UpdateCallback = std::function<void(const UpdatedItems&)>;
|
||||
|
||||
static AchievementManager& GetInstance();
|
||||
void Init();
|
||||
void Init(void* hwnd);
|
||||
void SetUpdateCallback(UpdateCallback callback);
|
||||
void Login(const std::string& password);
|
||||
bool HasAPIToken() const;
|
||||
|
@ -161,6 +165,16 @@ public:
|
|||
const std::unordered_set<AchievementId>& GetActiveChallenges() 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 CloseGame();
|
||||
|
@ -235,6 +249,15 @@ private:
|
|||
const UpdatedItems callback_data);
|
||||
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_client_t* m_client{};
|
||||
std::atomic<Core::System*> m_system{};
|
||||
|
@ -266,6 +289,14 @@ private:
|
|||
std::unordered_set<AchievementId> m_active_challenges;
|
||||
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_image_queue;
|
||||
mutable std::recursive_mutex m_lock;
|
||||
|
|
|
@ -787,4 +787,7 @@ if(USE_RETRO_ACHIEVEMENTS)
|
|||
target_link_libraries(core PUBLIC rcheevos)
|
||||
target_compile_definitions(core PUBLIC -DUSE_RETRO_ACHIEVEMENTS)
|
||||
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()
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
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)
|
||||
{
|
||||
const auto& instance = AchievementManager::GetInstance();
|
||||
|
|
|
@ -18,7 +18,7 @@ class AchievementBox final : public QGroupBox
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AchievementBox(QWidget* parent, rc_client_achievement_t* achievement);
|
||||
explicit AchievementBox(QWidget* parent, const rc_client_achievement_t* achievement);
|
||||
void UpdateData();
|
||||
void UpdateProgress();
|
||||
|
||||
|
@ -28,7 +28,7 @@ private:
|
|||
QProgressBar* m_progress_bar;
|
||||
QLabel* m_progress_label;
|
||||
|
||||
rc_client_achievement_t* m_achievement;
|
||||
const rc_client_achievement_t* m_achievement;
|
||||
};
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -34,10 +34,6 @@ AchievementSettingsWidget::AchievementSettingsWidget(QWidget* parent) : QWidget(
|
|||
|
||||
connect(&Settings::Instance(), &Settings::ConfigChanged, this,
|
||||
&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)
|
||||
|
@ -256,10 +252,9 @@ void AchievementSettingsWidget::ToggleRAIntegration()
|
|||
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
if (Config::Get(Config::RA_ENABLED))
|
||||
instance.Init();
|
||||
instance.Init(reinterpret_cast<void*>(winId()));
|
||||
else
|
||||
instance.Shutdown();
|
||||
UpdateHardcoreMode();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::Login()
|
||||
|
@ -297,7 +292,6 @@ void AchievementSettingsWidget::ToggleHardcore()
|
|||
}
|
||||
}
|
||||
SaveSettings();
|
||||
UpdateHardcoreMode();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleUnofficial()
|
||||
|
@ -327,14 +321,4 @@ void AchievementSettingsWidget::ToggleProgress()
|
|||
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
|
||||
|
|
|
@ -39,8 +39,6 @@ private:
|
|||
void ToggleDiscordPresence();
|
||||
void ToggleProgress();
|
||||
|
||||
void UpdateHardcoreMode();
|
||||
|
||||
QGroupBox* m_common_box;
|
||||
QVBoxLayout* m_common_layout;
|
||||
ToolTipCheckBox* m_common_integration_enabled_input;
|
||||
|
|
|
@ -39,8 +39,6 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
|
|||
});
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||
[this] { m_settings_widget->UpdateData(RC_OK); });
|
||||
connect(&Settings::Instance(), &Settings::HardcoreStateChanged, this,
|
||||
[this] { AchievementsWindow::UpdateData({.all = true}); });
|
||||
}
|
||||
|
||||
void AchievementsWindow::showEvent(QShowEvent* event)
|
||||
|
|
|
@ -677,4 +677,7 @@ endif()
|
|||
if(USE_RETRO_ACHIEVEMENTS)
|
||||
target_link_libraries(dolphin-emu PRIVATE rcheevos)
|
||||
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()
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "Core/BootManager.h"
|
||||
#include "Core/CommonTitles.h"
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/Config/FreeLookSettings.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/NetplaySettings.h"
|
||||
#include "Core/Config/UISettings.h"
|
||||
|
@ -272,9 +273,15 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr<BootParameters> boo
|
|||
NetPlayInit();
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().Init();
|
||||
AchievementManager::GetInstance().Init(reinterpret_cast<void*>(winId()));
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
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
|
||||
|
||||
#if defined(__unix__) || defined(__unix) || defined(__APPLE__)
|
||||
|
@ -935,7 +942,11 @@ bool MainWindow::RequestStop()
|
|||
else
|
||||
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))
|
||||
return true;
|
||||
|
@ -960,13 +971,27 @@ bool MainWindow::RequestStop()
|
|||
// 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
|
||||
m_render_widget->SetWaitingForMessageBox(true);
|
||||
auto confirm = ModalMessageBox::question(
|
||||
confirm_parent, tr("Confirm"),
|
||||
m_stop_requested ? tr("A shutdown is already in progress. Unsaved data "
|
||||
"may be lost if you stop the current emulation "
|
||||
"before it completes. Force stop?") :
|
||||
tr("Do you want to stop the current emulation?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::NoButton, Qt::ApplicationModal);
|
||||
QString message;
|
||||
if (m_stop_requested)
|
||||
{
|
||||
message = tr("A shutdown is already in progress. Unsaved data "
|
||||
"may be lost if you stop the current emulation "
|
||||
"before it completes. Force stop?");
|
||||
}
|
||||
#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,
|
||||
// even if the render widget will stay alive for a while.
|
||||
|
@ -1992,6 +2017,13 @@ void MainWindow::ShowAchievementSettings()
|
|||
ShowAchievementsWindow();
|
||||
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
|
||||
|
||||
void MainWindow::ShowMemcardManager()
|
||||
|
|
|
@ -181,6 +181,7 @@ private:
|
|||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
void ShowAchievementsWindow();
|
||||
void ShowAchievementSettings();
|
||||
void OnHardcoreChanged();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
void NetPlayInit();
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
#include "DolphinQt/QtUtils/DolphinFileDialog.h"
|
||||
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
||||
#include "DolphinQt/QtUtils/ParallelProgressDialog.h"
|
||||
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
||||
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
#include "DolphinQt/Updater.h"
|
||||
|
@ -71,6 +72,10 @@
|
|||
#include "UICommon/AutoUpdate.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;
|
||||
|
||||
QString MenuBar::GetSignatureSelector() const
|
||||
|
@ -284,8 +289,14 @@ void MenuBar::AddToolsMenu()
|
|||
tools_menu->addSeparator();
|
||||
|
||||
#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();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
|
@ -1123,6 +1134,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()
|
||||
{
|
||||
QString wad_file = DolphinFileDialog::getOpenFileName(this, tr("Select Title to Install to NAND"),
|
||||
|
|
|
@ -44,6 +44,9 @@ public:
|
|||
explicit MenuBar(QWidget* parent = nullptr);
|
||||
|
||||
void UpdateToolsMenu(Core::State state);
|
||||
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||
void UpdateAchievementDevelopmentMenu();
|
||||
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||
|
||||
QMenu* GetListColumnsMenu() const { return m_cols_menu; }
|
||||
|
||||
|
@ -205,6 +208,10 @@ private:
|
|||
QAction* m_wad_install_action;
|
||||
QMenu* m_perform_online_update_menu;
|
||||
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_ntscu_ipl;
|
||||
QAction* m_pal_ipl;
|
||||
|
|
|
@ -222,7 +222,6 @@ signals:
|
|||
void SDCardInsertionChanged(bool inserted);
|
||||
void USBKeyboardConnectionChanged(bool connected);
|
||||
void EnableGfxModsChanged(bool enabled);
|
||||
void HardcoreStateChanged();
|
||||
|
||||
private:
|
||||
Settings();
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
<PreprocessorDefinitions>HAVE_SDL2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>USE_RETRO_ACHIEVEMENTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>RC_CLIENT_SUPPORTS_HASH;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>RC_CLIENT_SUPPORTS_RAINTEGRATION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>HAVE_CUBEB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
|
||||
<!-- Warnings one may want to ignore when using Level4.
|
||||
|
|
Loading…
Reference in New Issue