mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-06 03:25:36 +00:00
Achievements: Swap RAInterface for RAIntegration via rc_client
This commit is contained in:
parent
1bb1354d4e
commit
d286b96c2d
@ -48,3 +48,14 @@ target_include_directories(rcheevos PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include
|
|||||||
target_include_directories(rcheevos INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
target_include_directories(rcheevos INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
target_compile_definitions(rcheevos PRIVATE "RCHEEVOS_URL_SSL=1" "RC_NO_THREADS=1")
|
target_compile_definitions(rcheevos PRIVATE "RCHEEVOS_URL_SSL=1" "RC_NO_THREADS=1")
|
||||||
|
|
||||||
|
# RAIntegration is not supported outside of Win32 and only on x64.
|
||||||
|
if(WIN32 AND CPU_ARCH_X64)
|
||||||
|
target_sources(rcheevos PRIVATE
|
||||||
|
src/rc_client_external.c
|
||||||
|
src/rc_client_external.h
|
||||||
|
src/rc_client_external_versions.h
|
||||||
|
src/rc_client_raintegration.c
|
||||||
|
src/rc_client_raintegration_internal.h
|
||||||
|
)
|
||||||
|
target_compile_definitions(rcheevos PUBLIC "RC_CLIENT_SUPPORTS_RAINTEGRATION=1")
|
||||||
|
endif()
|
||||||
|
@ -21,6 +21,12 @@
|
|||||||
<ClCompile Include="src\rcheevos\trigger.c" />
|
<ClCompile Include="src\rcheevos\trigger.c" />
|
||||||
<ClCompile Include="src\rcheevos\value.c" />
|
<ClCompile Include="src\rcheevos\value.c" />
|
||||||
<ClCompile Include="src\rc_client.c" />
|
<ClCompile Include="src\rc_client.c" />
|
||||||
|
<ClCompile Include="src\rc_client_external.c">
|
||||||
|
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\rc_client_raintegration.c">
|
||||||
|
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="src\rc_compat.c" />
|
<ClCompile Include="src\rc_compat.c" />
|
||||||
<ClCompile Include="src\rc_util.c" />
|
<ClCompile Include="src\rc_util.c" />
|
||||||
<ClCompile Include="src\rhash\md5.c" />
|
<ClCompile Include="src\rhash\md5.c" />
|
||||||
@ -43,7 +49,16 @@
|
|||||||
<ClInclude Include="include\rc_util.h" />
|
<ClInclude Include="include\rc_util.h" />
|
||||||
<ClInclude Include="src\rapi\rc_api_common.h" />
|
<ClInclude Include="src\rapi\rc_api_common.h" />
|
||||||
<ClInclude Include="src\rcheevos\rc_internal.h" />
|
<ClInclude Include="src\rcheevos\rc_internal.h" />
|
||||||
|
<ClInclude Include="src\rc_client_external.h">
|
||||||
|
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\rc_client_external_versions.h">
|
||||||
|
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="src\rc_client_internal.h" />
|
<ClInclude Include="src\rc_client_internal.h" />
|
||||||
|
<ClInclude Include="src\rc_client_raintegration_internal.h">
|
||||||
|
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="src\rc_compat.h" />
|
<ClInclude Include="src\rc_compat.h" />
|
||||||
<ClInclude Include="src\rc_version.h" />
|
<ClInclude Include="src\rc_version.h" />
|
||||||
<ClInclude Include="src\rhash\md5.h" />
|
<ClInclude Include="src\rhash\md5.h" />
|
||||||
@ -59,7 +74,8 @@
|
|||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||||
<PreprocessorDefinitions>RC_DISABLE_LUA=1;RCHEEVOS_URL_SSL=1;RC_NO_THREADS=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>RCHEEVOS_URL_SSL=1;RC_NO_THREADS=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">RC_CLIENT_SUPPORTS_RAINTEGRATION=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
@ -81,6 +81,8 @@
|
|||||||
<ClCompile Include="src\rc_compat.c" />
|
<ClCompile Include="src\rc_compat.c" />
|
||||||
<ClCompile Include="src\rc_util.c" />
|
<ClCompile Include="src\rc_util.c" />
|
||||||
<ClCompile Include="src\rc_client.c" />
|
<ClCompile Include="src\rc_client.c" />
|
||||||
|
<ClCompile Include="src\rc_client_external.c" />
|
||||||
|
<ClCompile Include="src\rc_client_raintegration.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="include\rc_consoles.h">
|
<ClInclude Include="include\rc_consoles.h">
|
||||||
@ -137,6 +139,9 @@
|
|||||||
<ClInclude Include="include\rc_util.h">
|
<ClInclude Include="include\rc_util.h">
|
||||||
<Filter>include</Filter>
|
<Filter>include</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\rc_client_external.h" />
|
||||||
|
<ClInclude Include="src\rc_client_external_versions.h" />
|
||||||
|
<ClInclude Include="src\rc_client_raintegration_internal.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Natvis Include="src\rc_client_types.natvis" />
|
<Natvis Include="src\rc_client_types.natvis" />
|
||||||
|
@ -62,13 +62,6 @@
|
|||||||
|
|
||||||
LOG_CHANNEL(Achievements);
|
LOG_CHANNEL(Achievements);
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
// RA_Interface ends up including windows.h, with its silly macros.
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include "common/windows_headers.h"
|
|
||||||
#endif
|
|
||||||
#include "RA_Interface.h"
|
|
||||||
#endif
|
|
||||||
namespace Achievements {
|
namespace Achievements {
|
||||||
|
|
||||||
static constexpr const char* INFO_SOUND_NAME = "sounds/achievements/message.wav";
|
static constexpr const char* INFO_SOUND_NAME = "sounds/achievements/message.wav";
|
||||||
@ -233,6 +226,13 @@ static void BuildProgressDatabase(const rc_client_all_progress_list_t* allprog);
|
|||||||
static void UpdateProgressDatabase(bool force);
|
static void UpdateProgressDatabase(bool force);
|
||||||
static void ClearProgressDatabase();
|
static void ClearProgressDatabase();
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
|
static void BeginLoadRAIntegration();
|
||||||
|
static void UnloadRAIntegration();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct PauseMenuAchievementInfo
|
struct PauseMenuAchievementInfo
|
||||||
@ -249,10 +249,6 @@ struct State
|
|||||||
bool has_leaderboards = false;
|
bool has_leaderboards = false;
|
||||||
bool has_rich_presence = false;
|
bool has_rich_presence = false;
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
bool using_raintegration = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::recursive_mutex mutex; // large
|
std::recursive_mutex mutex; // large
|
||||||
|
|
||||||
std::string rich_presence_string;
|
std::string rich_presence_string;
|
||||||
@ -297,6 +293,11 @@ struct State
|
|||||||
rc_client_hash_library_t* fetch_hash_library_result = nullptr;
|
rc_client_hash_library_t* fetch_hash_library_result = nullptr;
|
||||||
rc_client_async_handle_t* fetch_all_progress_request = nullptr;
|
rc_client_async_handle_t* fetch_all_progress_request = nullptr;
|
||||||
rc_client_all_progress_list_t* fetch_all_progress_result = nullptr;
|
rc_client_all_progress_list_t* fetch_all_progress_result = nullptr;
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
rc_client_async_handle_t* load_raintegration_request = nullptr;
|
||||||
|
bool using_raintegration = false;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -566,20 +567,11 @@ void Achievements::UpdateGlyphRanges()
|
|||||||
|
|
||||||
bool Achievements::IsActive()
|
bool Achievements::IsActive()
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
return (s_state.client != nullptr) || s_state.using_raintegration;
|
|
||||||
#else
|
|
||||||
return (s_state.client != nullptr);
|
return (s_state.client != nullptr);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Achievements::IsHardcoreModeActive()
|
bool Achievements::IsHardcoreModeActive()
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
return RA_HardcoreModeIsActive() != 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!s_state.client)
|
if (!s_state.client)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -656,6 +648,11 @@ bool Achievements::Initialize()
|
|||||||
|
|
||||||
rc_client_set_event_handler(s_state.client, ClientEventHandler);
|
rc_client_set_event_handler(s_state.client, ClientEventHandler);
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
if (g_settings.achievements_use_raintegration)
|
||||||
|
BeginLoadRAIntegration();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Hardcore starts off. We enable it on first boot.
|
// Hardcore starts off. We enable it on first boot.
|
||||||
rc_client_set_hardcore_enabled(s_state.client, false);
|
rc_client_set_hardcore_enabled(s_state.client, false);
|
||||||
rc_client_set_encore_mode_enabled(s_state.client, g_settings.achievements_encore_mode);
|
rc_client_set_encore_mode_enabled(s_state.client, g_settings.achievements_encore_mode);
|
||||||
@ -752,9 +749,6 @@ bool Achievements::TryLoggingInWithToken()
|
|||||||
|
|
||||||
void Achievements::UpdateSettings(const Settings& old_config)
|
void Achievements::UpdateSettings(const Settings& old_config)
|
||||||
{
|
{
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!g_settings.achievements_enabled)
|
if (!g_settings.achievements_enabled)
|
||||||
{
|
{
|
||||||
// we're done here
|
// we're done here
|
||||||
@ -769,6 +763,16 @@ void Achievements::UpdateSettings(const Settings& old_config)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
if (g_settings.achievements_use_raintegration != old_config.achievements_use_raintegration)
|
||||||
|
{
|
||||||
|
// RAIntegration requires a full client reload?
|
||||||
|
Shutdown();
|
||||||
|
Initialize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (g_settings.achievements_hardcore_mode != old_config.achievements_hardcore_mode)
|
if (g_settings.achievements_hardcore_mode != old_config.achievements_hardcore_mode)
|
||||||
{
|
{
|
||||||
// Enables have to wait for reset, disables can go through immediately.
|
// Enables have to wait for reset, disables can go through immediately.
|
||||||
@ -823,6 +827,11 @@ void Achievements::Shutdown()
|
|||||||
s_state.login_request = nullptr;
|
s_state.login_request = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
if (s_state.using_raintegration)
|
||||||
|
UnloadRAIntegration();
|
||||||
|
#endif
|
||||||
|
|
||||||
DestroyClient(&s_state.client, &s_state.http_downloader);
|
DestroyClient(&s_state.client, &s_state.http_downloader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -898,11 +907,6 @@ void Achievements::IdleUpdate()
|
|||||||
if (!IsActive())
|
if (!IsActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const auto lock = GetLock();
|
const auto lock = GetLock();
|
||||||
|
|
||||||
s_state.http_downloader->PollRequests();
|
s_state.http_downloader->PollRequests();
|
||||||
@ -923,14 +927,6 @@ void Achievements::FrameUpdate()
|
|||||||
if (!IsActive())
|
if (!IsActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
{
|
|
||||||
RA_DoAchievementsFrame();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto lock = GetLock();
|
auto lock = GetLock();
|
||||||
|
|
||||||
s_state.http_downloader->PollRequests();
|
s_state.http_downloader->PollRequests();
|
||||||
@ -1162,28 +1158,12 @@ void Achievements::OnSystemDestroyed()
|
|||||||
UpdateGlyphRanges();
|
UpdateGlyphRanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Achievements::OnSystemPaused(bool paused)
|
|
||||||
{
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
RA_SetPaused(paused);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Achievements::OnSystemReset()
|
void Achievements::OnSystemReset()
|
||||||
{
|
{
|
||||||
const auto lock = GetLock();
|
const auto lock = GetLock();
|
||||||
if (!IsActive())
|
if (!IsActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
{
|
|
||||||
RA_OnReset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Do we need to enable hardcore mode?
|
// Do we need to enable hardcore mode?
|
||||||
if (System::IsValid() && g_settings.achievements_hardcore_mode && !rc_client_get_hardcore_enabled(s_state.client))
|
if (System::IsValid() && g_settings.achievements_hardcore_mode && !rc_client_get_hardcore_enabled(s_state.client))
|
||||||
{
|
{
|
||||||
@ -1263,12 +1243,6 @@ bool Achievements::IdentifyGame(CDImage* image)
|
|||||||
}
|
}
|
||||||
|
|
||||||
s_state.game_hash = game_hash;
|
s_state.game_hash = game_hash;
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
RAIntegration::GameChanged();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1834,16 +1808,6 @@ void Achievements::DisableHardcoreMode(bool show_message, bool display_game_summ
|
|||||||
if (!IsActive())
|
if (!IsActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
{
|
|
||||||
if (RA_HardcoreModeIsActive())
|
|
||||||
RA_DisableHardcore();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const auto lock = GetLock();
|
const auto lock = GetLock();
|
||||||
if (!rc_client_get_hardcore_enabled(s_state.client))
|
if (!rc_client_get_hardcore_enabled(s_state.client))
|
||||||
return;
|
return;
|
||||||
@ -1919,7 +1883,7 @@ bool Achievements::DoState(StateWrapper& sw)
|
|||||||
{
|
{
|
||||||
// if we're active, make sure we've downloaded and activated all the achievements
|
// if we're active, make sure we've downloaded and activated all the achievements
|
||||||
// before deserializing, otherwise that state's going to get lost.
|
// before deserializing, otherwise that state's going to get lost.
|
||||||
if (!IsUsingRAIntegration() && s_state.load_game_request)
|
if (s_state.load_game_request)
|
||||||
{
|
{
|
||||||
// Messy because GPU-thread, but at least it looks pretty.
|
// Messy because GPU-thread, but at least it looks pretty.
|
||||||
GPUThread::RunOnThread([]() {
|
GPUThread::RunOnThread([]() {
|
||||||
@ -1938,14 +1902,7 @@ bool Achievements::DoState(StateWrapper& sw)
|
|||||||
{
|
{
|
||||||
// reset runtime, no data (state might've been created without cheevos)
|
// reset runtime, no data (state might've been created without cheevos)
|
||||||
WARNING_LOG("State is missing cheevos data, resetting runtime");
|
WARNING_LOG("State is missing cheevos data, resetting runtime");
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
RA_OnReset();
|
|
||||||
else
|
|
||||||
rc_client_reset(s_state.client);
|
rc_client_reset(s_state.client);
|
||||||
#else
|
|
||||||
rc_client_reset(s_state.client);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
@ -1954,21 +1911,12 @@ bool Achievements::DoState(StateWrapper& sw)
|
|||||||
if (sw.HasError())
|
if (sw.HasError())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
{
|
|
||||||
RA_RestoreState(reinterpret_cast<const char*>(data.data()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
const int result = rc_client_deserialize_progress_sized(s_state.client, data.data(), data_size);
|
const int result = rc_client_deserialize_progress_sized(s_state.client, data.data(), data_size);
|
||||||
if (result != RC_OK)
|
if (result != RC_OK)
|
||||||
{
|
{
|
||||||
WARNING_LOG("Failed to deserialize cheevos state ({}), resetting", result);
|
WARNING_LOG("Failed to deserialize cheevos state ({}), resetting", result);
|
||||||
rc_client_reset(s_state.client);
|
rc_client_reset(s_state.client);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1976,29 +1924,6 @@ bool Achievements::DoState(StateWrapper& sw)
|
|||||||
{
|
{
|
||||||
const size_t size_pos = sw.GetPosition();
|
const size_t size_pos = sw.GetPosition();
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
{
|
|
||||||
const int size = RA_CaptureState(nullptr, 0);
|
|
||||||
u32 write_size = static_cast<u32>(std::max(size, 0));
|
|
||||||
sw.Do(&write_size);
|
|
||||||
|
|
||||||
const std::span<u8> data = sw.GetDeferredBytes(write_size);
|
|
||||||
if (!data.empty())
|
|
||||||
{
|
|
||||||
const int result = RA_CaptureState(reinterpret_cast<char*>(data.data()), size);
|
|
||||||
if (result != static_cast<int>(size))
|
|
||||||
{
|
|
||||||
WARNING_LOG("Failed to serialize cheevos state from RAIntegration.");
|
|
||||||
write_size = 0;
|
|
||||||
sw.SetPosition(size_pos);
|
|
||||||
sw.Do(&write_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
u32 data_size = static_cast<u32>(rc_client_progress_size(s_state.client));
|
u32 data_size = static_cast<u32>(rc_client_progress_size(s_state.client));
|
||||||
sw.Do(&data_size);
|
sw.Do(&data_size);
|
||||||
|
|
||||||
@ -2018,7 +1943,6 @@ bool Achievements::DoState(StateWrapper& sw)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
@ -2281,7 +2205,7 @@ std::string Achievements::GetLoggedInUserBadgePath()
|
|||||||
|
|
||||||
u32 Achievements::GetPauseThrottleFrames()
|
u32 Achievements::GetPauseThrottleFrames()
|
||||||
{
|
{
|
||||||
if (!IsActive() || !IsHardcoreModeActive() || IsUsingRAIntegration())
|
if (!IsActive() || !IsHardcoreModeActive())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
u32 frames_remaining = 0;
|
u32 frames_remaining = 0;
|
||||||
@ -2314,23 +2238,8 @@ void Achievements::Logout()
|
|||||||
ClearProgressDatabase();
|
ClearProgressDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Achievements::ConfirmGameChange()
|
|
||||||
{
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
return RA_ConfirmLoadNewRom(false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Achievements::ConfirmHardcoreModeDisable(const char* trigger)
|
bool Achievements::ConfirmHardcoreModeDisable(const char* trigger)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
return (RA_WarnDisableHardcore(trigger) != 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// I really hope this doesn't deadlock :/
|
// I really hope this doesn't deadlock :/
|
||||||
const bool confirmed = Host::ConfirmMessage(
|
const bool confirmed = Host::ConfirmMessage(
|
||||||
TRANSLATE("Achievements", "Confirm Hardcore Mode Disable"),
|
TRANSLATE("Achievements", "Confirm Hardcore Mode Disable"),
|
||||||
@ -2346,30 +2255,19 @@ bool Achievements::ConfirmHardcoreModeDisable(const char* trigger)
|
|||||||
|
|
||||||
void Achievements::ConfirmHardcoreModeDisableAsync(const char* trigger, std::function<void(bool)> callback)
|
void Achievements::ConfirmHardcoreModeDisableAsync(const char* trigger, std::function<void(bool)> callback)
|
||||||
{
|
{
|
||||||
auto real_callback = [callback = std::move(callback)](bool res) mutable {
|
Host::ConfirmMessageAsync(
|
||||||
|
TRANSLATE_STR("Achievements", "Confirm Hardcore Mode Disable"),
|
||||||
|
fmt::format(TRANSLATE_FS("Achievements", "{0} cannot be performed while hardcore mode is active. Do you want to "
|
||||||
|
"disable hardcore mode? {0} will be cancelled if you select No."),
|
||||||
|
trigger),
|
||||||
|
[callback = std::move(callback)](bool res) mutable {
|
||||||
// don't run the callback in the middle of rendering the UI
|
// don't run the callback in the middle of rendering the UI
|
||||||
Host::RunOnCPUThread([callback = std::move(callback), res]() {
|
Host::RunOnCPUThread([callback = std::move(callback), res]() {
|
||||||
if (res)
|
if (res)
|
||||||
DisableHardcoreMode(true, true);
|
DisableHardcoreMode(true, true);
|
||||||
callback(res);
|
callback(res);
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (IsUsingRAIntegration())
|
|
||||||
{
|
|
||||||
const bool result = (RA_WarnDisableHardcore(trigger) != 0);
|
|
||||||
real_callback(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Host::ConfirmMessageAsync(
|
|
||||||
TRANSLATE_STR("Achievements", "Confirm Hardcore Mode Disable"),
|
|
||||||
fmt::format(TRANSLATE_FS("Achievements", "{0} cannot be performed while hardcore mode is active. Do you want to "
|
|
||||||
"disable hardcore mode? {0} will be cancelled if you select No."),
|
|
||||||
trigger),
|
|
||||||
std::move(real_callback));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Achievements::ClearUIState()
|
void Achievements::ClearUIState()
|
||||||
@ -4665,195 +4563,122 @@ const Achievements::ProgressDatabase::Entry* Achievements::ProgressDatabase::Loo
|
|||||||
return (iter != m_entries.end() && iter->game_id == game_id) ? &(*iter) : nullptr;
|
return (iter != m_entries.end() && iter->game_id == game_id) ? &(*iter) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
#include "RA_Consoles.h"
|
#include "common/windows_headers.h"
|
||||||
|
|
||||||
|
#include "rc_client_raintegration.h"
|
||||||
|
|
||||||
|
namespace Achievements {
|
||||||
|
|
||||||
|
static void RAIntegrationBeginLoadCallback(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 RAIntegrationWriteMemoryCallback(uint32_t address, uint8_t* buffer, uint32_t num_bytes,
|
||||||
|
rc_client_t* client);
|
||||||
|
static void RAIntegrationGetGameNameCallback(char* buffer, uint32_t buffer_size, rc_client_t* client);
|
||||||
|
|
||||||
|
} // namespace Achievements
|
||||||
|
|
||||||
bool Achievements::IsUsingRAIntegration()
|
bool Achievements::IsUsingRAIntegration()
|
||||||
{
|
{
|
||||||
return s_state.using_raintegration;
|
return s_state.using_raintegration;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Achievements::RAIntegration {
|
bool Achievements::IsRAIntegrationAvailable()
|
||||||
static void InitializeRAIntegration(void* main_window_handle);
|
|
||||||
|
|
||||||
static int RACallbackIsActive();
|
|
||||||
static void RACallbackCauseUnpause();
|
|
||||||
static void RACallbackCausePause();
|
|
||||||
static void RACallbackRebuildMenu();
|
|
||||||
static void RACallbackEstimateTitle(char* buf);
|
|
||||||
static void RACallbackResetEmulator();
|
|
||||||
static void RACallbackLoadROM(const char* unused);
|
|
||||||
static unsigned char RACallbackReadRAM(unsigned int address);
|
|
||||||
static unsigned int RACallbackReadRAMBlock(unsigned int nAddress, unsigned char* pBuffer, unsigned int nBytes);
|
|
||||||
static void RACallbackWriteRAM(unsigned int address, unsigned char value);
|
|
||||||
static unsigned char RACallbackReadScratchpad(unsigned int address);
|
|
||||||
static unsigned int RACallbackReadScratchpadBlock(unsigned int nAddress, unsigned char* pBuffer, unsigned int nBytes);
|
|
||||||
static void RACallbackWriteScratchpad(unsigned int address, unsigned char value);
|
|
||||||
|
|
||||||
static bool s_raintegration_initialized = false;
|
|
||||||
} // namespace Achievements::RAIntegration
|
|
||||||
|
|
||||||
void Achievements::SwitchToRAIntegration()
|
|
||||||
{
|
{
|
||||||
|
return (FileSystem::FileExists(Path::Combine(EmuFolders::AppRoot, "RA_Integration-x64.dll").c_str()) ||
|
||||||
|
FileSystem::FileExists(Path::Combine(EmuFolders::AppRoot, "RA_Integration.dll").c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Achievements::BeginLoadRAIntegration()
|
||||||
|
{
|
||||||
|
const std::optional<WindowInfo> wi = Host::GetTopLevelWindowInfo();
|
||||||
|
const std::wstring wapproot = StringUtil::UTF8StringToWideString(EmuFolders::AppRoot);
|
||||||
|
s_state.load_raintegration_request = rc_client_begin_load_raintegration(
|
||||||
|
s_state.client, wapproot.c_str(),
|
||||||
|
(wi.has_value() && wi->type == WindowInfo::Type::Win32) ? static_cast<HWND>(wi->window_handle) : NULL,
|
||||||
|
"DuckStation", g_scm_tag_str, RAIntegrationBeginLoadCallback, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Achievements::RAIntegrationBeginLoadCallback(int result, const char* error_message, rc_client_t* client,
|
||||||
|
void* userdata)
|
||||||
|
{
|
||||||
|
if (result != RC_OK)
|
||||||
|
{
|
||||||
|
std::string message = fmt::format("Failed to load RAIntegration:\n{}", error_message ? error_message : "");
|
||||||
|
Host::ReportErrorAsync("RAIntegration Error", message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto lock = GetLock();
|
||||||
|
|
||||||
|
rc_client_raintegration_set_write_memory_function(client, RAIntegrationWriteMemoryCallback);
|
||||||
|
rc_client_raintegration_set_console_id(client, RC_CONSOLE_PLAYSTATION);
|
||||||
|
rc_client_raintegration_set_get_game_name_function(client, RAIntegrationGetGameNameCallback);
|
||||||
|
rc_client_raintegration_set_event_handler(client, RAIntegrationEventHandler);
|
||||||
|
|
||||||
s_state.using_raintegration = true;
|
s_state.using_raintegration = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO_COLOR_LOG(StrongGreen, "RAIntegration loaded.");
|
||||||
|
|
||||||
|
Host::OnRAIntegrationMenuChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Achievements::RAIntegration::InitializeRAIntegration(void* main_window_handle)
|
void Achievements::UnloadRAIntegration()
|
||||||
{
|
{
|
||||||
RA_InitClient((HWND)main_window_handle, "DuckStation", g_scm_tag_str);
|
if (!s_state.using_raintegration)
|
||||||
RA_SetUserAgentDetail(Host::GetHTTPUserAgent().c_str());
|
|
||||||
|
|
||||||
RA_InstallSharedFunctions(RACallbackIsActive, RACallbackCauseUnpause, RACallbackCausePause, RACallbackRebuildMenu,
|
|
||||||
RACallbackEstimateTitle, RACallbackResetEmulator, RACallbackLoadROM);
|
|
||||||
RA_SetConsoleID(PlayStation);
|
|
||||||
|
|
||||||
// Apparently this has to be done early, or the memory inspector doesn't work.
|
|
||||||
// That's a bit unfortunate, because the RAM size can vary between games, and depending on the option.
|
|
||||||
RA_InstallMemoryBank(0, RACallbackReadRAM, RACallbackWriteRAM, Bus::RAM_2MB_SIZE);
|
|
||||||
RA_InstallMemoryBankBlockReader(0, RACallbackReadRAMBlock);
|
|
||||||
RA_InstallMemoryBank(1, RACallbackReadScratchpad, RACallbackWriteScratchpad, CPU::SCRATCHPAD_SIZE);
|
|
||||||
RA_InstallMemoryBankBlockReader(1, RACallbackReadScratchpadBlock);
|
|
||||||
|
|
||||||
// Fire off a login anyway. Saves going into the menu and doing it.
|
|
||||||
RA_AttemptLogin(0);
|
|
||||||
|
|
||||||
s_raintegration_initialized = true;
|
|
||||||
|
|
||||||
// this is pretty lame, but we may as well persist until we exit anyway
|
|
||||||
std::atexit(RA_Shutdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Achievements::RAIntegration::MainWindowChanged(void* new_handle)
|
|
||||||
{
|
|
||||||
if (s_raintegration_initialized)
|
|
||||||
{
|
|
||||||
RA_UpdateHWnd((HWND)new_handle);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
InitializeRAIntegration(new_handle);
|
rc_client_unload_raintegration(s_state.client);
|
||||||
|
s_state.using_raintegration = false;
|
||||||
|
Host::OnRAIntegrationMenuChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Achievements::RAIntegration::GameChanged()
|
void Achievements::RAIntegrationEventHandler(const rc_client_raintegration_event_t* event, rc_client_t* client)
|
||||||
{
|
{
|
||||||
s_state.game_id = s_state.game_hash.has_value() ? RA_IdentifyHash(GameHashToString(s_state.game_hash.value())) : 0;
|
switch (event->type)
|
||||||
RA_ActivateGame(s_state.game_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::tuple<int, std::string, bool>> Achievements::RAIntegration::GetMenuItems()
|
|
||||||
{
|
|
||||||
std::array<RA_MenuItem, 64> items;
|
|
||||||
const int num_items = RA_GetPopupMenuItems(items.data());
|
|
||||||
|
|
||||||
std::vector<std::tuple<int, std::string, bool>> ret;
|
|
||||||
ret.reserve(static_cast<u32>(num_items));
|
|
||||||
|
|
||||||
for (int i = 0; i < num_items; i++)
|
|
||||||
{
|
{
|
||||||
const RA_MenuItem& it = items[i];
|
case RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED:
|
||||||
if (!it.sLabel)
|
case RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED:
|
||||||
ret.emplace_back(0, std::string(), false);
|
{
|
||||||
else
|
Host::OnRAIntegrationMenuChanged();
|
||||||
ret.emplace_back(static_cast<int>(it.nID), StringUtil::WideStringToUTF8String(it.sLabel), it.bChecked);
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
return ret;
|
case RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED:
|
||||||
}
|
{
|
||||||
|
// Could get called from a different thread...
|
||||||
|
Host::RunOnCPUThread([]() {
|
||||||
|
const auto lock = GetLock();
|
||||||
|
OnHardcoreModeChanged(rc_client_get_hardcore_enabled(s_state.client) != 0, false, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
void Achievements::RAIntegration::ActivateMenuItem(int item)
|
case RC_CLIENT_RAINTEGRATION_EVENT_PAUSE:
|
||||||
{
|
{
|
||||||
RA_InvokeDialog(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Achievements::RAIntegration::RACallbackIsActive()
|
|
||||||
{
|
|
||||||
return static_cast<int>(HasActiveGame());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Achievements::RAIntegration::RACallbackCauseUnpause()
|
|
||||||
{
|
|
||||||
Host::RunOnCPUThread([]() { System::PauseSystem(false); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void Achievements::RAIntegration::RACallbackCausePause()
|
|
||||||
{
|
|
||||||
Host::RunOnCPUThread([]() { System::PauseSystem(true); });
|
Host::RunOnCPUThread([]() { System::PauseSystem(true); });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ERROR_LOG("Unhandled RAIntegration event {}", static_cast<u32>(event->type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Achievements::RAIntegration::RACallbackRebuildMenu()
|
void Achievements::RAIntegrationWriteMemoryCallback(uint32_t address, uint8_t* buffer, uint32_t num_bytes,
|
||||||
|
rc_client_t* client)
|
||||||
{
|
{
|
||||||
// unused, we build the menu on demand
|
// I hope this is called on the CPU thread...
|
||||||
|
CPU::SafeWriteMemoryBytes(address, buffer, num_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Achievements::RAIntegration::RACallbackEstimateTitle(char* buf)
|
void Achievements::RAIntegrationGetGameNameCallback(char* buffer, uint32_t buffer_size, rc_client_t* client)
|
||||||
{
|
{
|
||||||
StringUtil::Strlcpy(buf, System::GetGameTitle(), 256);
|
StringUtil::Strlcpy(buffer, System::GetGameTitle(), buffer_size);
|
||||||
}
|
|
||||||
|
|
||||||
void Achievements::RAIntegration::RACallbackResetEmulator()
|
|
||||||
{
|
|
||||||
if (System::IsValid())
|
|
||||||
System::ResetSystem();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Achievements::RAIntegration::RACallbackLoadROM(const char* unused)
|
|
||||||
{
|
|
||||||
// unused
|
|
||||||
UNREFERENCED_PARAMETER(unused);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char Achievements::RAIntegration::RACallbackReadRAM(unsigned int address)
|
|
||||||
{
|
|
||||||
if (!System::IsValid())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
u8 value = 0;
|
|
||||||
CPU::SafeReadMemoryByte(address, &value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Achievements::RAIntegration::RACallbackWriteRAM(unsigned int address, unsigned char value)
|
|
||||||
{
|
|
||||||
CPU::SafeWriteMemoryByte(address, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int Achievements::RAIntegration::RACallbackReadRAMBlock(unsigned int nAddress, unsigned char* pBuffer,
|
|
||||||
unsigned int nBytes)
|
|
||||||
{
|
|
||||||
if (nAddress >= Bus::g_ram_size)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
const u32 copy_size = std::min<u32>(Bus::g_ram_size - nAddress, nBytes);
|
|
||||||
std::memcpy(pBuffer, Bus::g_unprotected_ram + nAddress, copy_size);
|
|
||||||
return copy_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char Achievements::RAIntegration::RACallbackReadScratchpad(unsigned int address)
|
|
||||||
{
|
|
||||||
if (!System::IsValid() || address >= CPU::SCRATCHPAD_SIZE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return CPU::g_state.scratchpad[address];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Achievements::RAIntegration::RACallbackWriteScratchpad(unsigned int address, unsigned char value)
|
|
||||||
{
|
|
||||||
if (address >= CPU::SCRATCHPAD_SIZE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
CPU::g_state.scratchpad[address] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int Achievements::RAIntegration::RACallbackReadScratchpadBlock(unsigned int nAddress, unsigned char* pBuffer,
|
|
||||||
unsigned int nBytes)
|
|
||||||
{
|
|
||||||
if (nAddress >= CPU::SCRATCHPAD_SIZE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
const u32 copy_size = std::min<u32>(CPU::SCRATCHPAD_SIZE - nAddress, nBytes);
|
|
||||||
std::memcpy(pBuffer, &CPU::g_state.scratchpad[nAddress], copy_size);
|
|
||||||
return copy_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@ -4863,4 +4688,9 @@ bool Achievements::IsUsingRAIntegration()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Achievements::IsRAIntegrationAvailable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -89,9 +89,6 @@ void OnSystemDestroyed();
|
|||||||
/// Called when the system is being reset. Resets the internal state of all achievement tracking.
|
/// Called when the system is being reset. Resets the internal state of all achievement tracking.
|
||||||
void OnSystemReset();
|
void OnSystemReset();
|
||||||
|
|
||||||
/// Called when the system is being paused and resumed.
|
|
||||||
void OnSystemPaused(bool paused);
|
|
||||||
|
|
||||||
/// Called when the system changes game.
|
/// Called when the system changes game.
|
||||||
void GameChanged(CDImage* image);
|
void GameChanged(CDImage* image);
|
||||||
|
|
||||||
@ -126,9 +123,7 @@ bool IsHardcoreModeActive();
|
|||||||
|
|
||||||
/// RAIntegration only exists for Windows, so no point checking it on other platforms.
|
/// RAIntegration only exists for Windows, so no point checking it on other platforms.
|
||||||
bool IsUsingRAIntegration();
|
bool IsUsingRAIntegration();
|
||||||
|
bool IsRAIntegrationAvailable();
|
||||||
/// Hook for RAIntegration to confirm reset/shutdown.
|
|
||||||
bool ConfirmGameChange();
|
|
||||||
|
|
||||||
/// Returns true if the achievement system is active. Achievements can be active without a valid client.
|
/// Returns true if the achievement system is active. Achievements can be active without a valid client.
|
||||||
bool IsActive();
|
bool IsActive();
|
||||||
@ -207,22 +202,11 @@ void DrawLeaderboardsWindow();
|
|||||||
|
|
||||||
#endif // __ANDROID__
|
#endif // __ANDROID__
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
/// Prevents the internal implementation from being used. Instead, RAIntegration will be
|
|
||||||
/// called into when achievement-related events occur.
|
|
||||||
void SwitchToRAIntegration();
|
|
||||||
|
|
||||||
namespace RAIntegration {
|
|
||||||
void MainWindowChanged(void* new_handle);
|
|
||||||
void GameChanged();
|
|
||||||
std::vector<std::tuple<int, std::string, bool>> GetMenuItems();
|
|
||||||
void ActivateMenuItem(int item);
|
|
||||||
} // namespace RAIntegration
|
|
||||||
#endif
|
|
||||||
} // namespace Achievements
|
} // namespace Achievements
|
||||||
|
|
||||||
/// Functions implemented in the frontend.
|
/// Functions implemented in the frontend.
|
||||||
namespace Host {
|
namespace Host {
|
||||||
|
|
||||||
/// Called if the big picture UI requests achievements login, or token login fails.
|
/// Called if the big picture UI requests achievements login, or token login fails.
|
||||||
void OnAchievementsLoginRequested(Achievements::LoginRequestReason reason);
|
void OnAchievementsLoginRequested(Achievements::LoginRequestReason reason);
|
||||||
|
|
||||||
@ -235,4 +219,12 @@ void OnAchievementsRefreshed();
|
|||||||
|
|
||||||
/// Called whenever hardcore mode is toggled.
|
/// Called whenever hardcore mode is toggled.
|
||||||
void OnAchievementsHardcoreModeChanged(bool enabled);
|
void OnAchievementsHardcoreModeChanged(bool enabled);
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
|
/// Called when the RAIntegration menu changes.
|
||||||
|
void OnRAIntegrationMenuChanged();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace Host
|
} // namespace Host
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<PreprocessorDefinitions Condition="('$(Platform)'!='ARM64')">ENABLE_RAINTEGRATION=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM' Or '$(Platform)'=='ARM64')">ENABLE_RECOMPILER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM' Or '$(Platform)'=='ARM64')">ENABLE_RECOMPILER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM64')">ENABLE_MMAP_FASTMEM=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM64')">ENABLE_MMAP_FASTMEM=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
|
||||||
@ -17,6 +16,7 @@
|
|||||||
|
|
||||||
<AdditionalIncludeDirectories Condition="'$(Platform)'=='x64'">%(AdditionalIncludeDirectories);$(SolutionDir)dep\xbyak\xbyak</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories Condition="'$(Platform)'=='x64'">%(AdditionalIncludeDirectories);$(SolutionDir)dep\xbyak\xbyak</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">XBYAK_NO_EXCEPTION=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">XBYAK_NO_EXCEPTION=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">RC_CLIENT_SUPPORTS_RAINTEGRATION=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
|
||||||
<AdditionalIncludeDirectories Condition="'$(Platform)'=='ARM' Or '$(Platform)'=='ARM64'">%(AdditionalIncludeDirectories);$(SolutionDir)dep\vixl\include</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories Condition="'$(Platform)'=='ARM' Or '$(Platform)'=='ARM64'">%(AdditionalIncludeDirectories);$(SolutionDir)dep\vixl\include</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -6223,19 +6223,6 @@ void FullscreenUI::DrawAudioSettingsPage()
|
|||||||
|
|
||||||
void FullscreenUI::DrawAchievementsSettingsPage()
|
void FullscreenUI::DrawAchievementsSettingsPage()
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (Achievements::IsUsingRAIntegration())
|
|
||||||
{
|
|
||||||
BeginMenuButtons();
|
|
||||||
MenuButtonWithoutSummary(
|
|
||||||
FSUI_ICONSTR(ICON_FA_BAN,
|
|
||||||
FSUI_CSTR("RAIntegration is being used instead of the built-in achievements implementation.")),
|
|
||||||
false);
|
|
||||||
EndMenuButtons();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||||
|
|
||||||
BeginMenuButtons();
|
BeginMenuButtons();
|
||||||
|
@ -1551,7 +1551,7 @@ void System::UpdateInputSettingsLayer(std::string input_profile_name, std::uniqu
|
|||||||
|
|
||||||
void System::ResetSystem()
|
void System::ResetSystem()
|
||||||
{
|
{
|
||||||
if (!IsValid() || !Achievements::ConfirmGameChange())
|
if (!IsValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
InternalReset();
|
InternalReset();
|
||||||
@ -1591,8 +1591,6 @@ void System::PauseSystem(bool paused)
|
|||||||
InputManager::PauseVibration();
|
InputManager::PauseVibration();
|
||||||
InputManager::UpdateHostMouseMode();
|
InputManager::UpdateHostMouseMode();
|
||||||
|
|
||||||
Achievements::OnSystemPaused(true);
|
|
||||||
|
|
||||||
if (g_settings.inhibit_screensaver)
|
if (g_settings.inhibit_screensaver)
|
||||||
PlatformMisc::ResumeScreensaver();
|
PlatformMisc::ResumeScreensaver();
|
||||||
|
|
||||||
@ -1610,8 +1608,6 @@ void System::PauseSystem(bool paused)
|
|||||||
|
|
||||||
InputManager::UpdateHostMouseMode();
|
InputManager::UpdateHostMouseMode();
|
||||||
|
|
||||||
Achievements::OnSystemPaused(false);
|
|
||||||
|
|
||||||
if (g_settings.inhibit_screensaver)
|
if (g_settings.inhibit_screensaver)
|
||||||
PlatformMisc::SuspendScreensaver();
|
PlatformMisc::SuspendScreensaver();
|
||||||
|
|
||||||
|
@ -101,6 +101,23 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* dialog, QWi
|
|||||||
m_ui.loginBox = nullptr;
|
m_ui.loginBox = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RAIntegration is not available on non-win32/x64.
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
if (Achievements::IsRAIntegrationAvailable())
|
||||||
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useRAIntegration, "Cheevos", "UseRAIntegration", false);
|
||||||
|
else
|
||||||
|
m_ui.useRAIntegration->setEnabled(false);
|
||||||
|
|
||||||
|
dialog->registerWidgetHelp(
|
||||||
|
m_ui.useRAIntegration, tr("Enable RAIntegration (Development Only)"), tr("Unchecked"),
|
||||||
|
tr("When enabled, DuckStation will load the RAIntegration DLL which allows for achievement development.<br>The "
|
||||||
|
"RA_Integration.dll file must be placed in the same directory as the DuckStation executable."));
|
||||||
|
#else
|
||||||
|
m_ui.settingsLayout->removeWidget(m_ui.useRAIntegration);
|
||||||
|
delete m_ui.useRAIntegration;
|
||||||
|
m_ui.useRAIntegration = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
updateEnableState();
|
updateEnableState();
|
||||||
onAchievementsNotificationDurationSliderChanged();
|
onAchievementsNotificationDurationSliderChanged();
|
||||||
onLeaderboardsNotificationDurationSliderChanged();
|
onLeaderboardsNotificationDurationSliderChanged();
|
||||||
|
@ -24,11 +24,11 @@
|
|||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox">
|
<widget class="QGroupBox" name="settingsGroupBox">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Settings</string>
|
<string>Settings</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="settingsLayout">
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="spectatorMode">
|
<widget class="QCheckBox" name="spectatorMode">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -64,6 +64,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="useRAIntegration">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable RAIntegration (Development Only)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
#include "settingswindow.h"
|
#include "settingswindow.h"
|
||||||
#include "settingwidgetbinder.h"
|
#include "settingwidgetbinder.h"
|
||||||
|
|
||||||
#include "core/achievements.h"
|
|
||||||
#include "core/cheats.h"
|
#include "core/cheats.h"
|
||||||
#include "core/game_list.h"
|
#include "core/game_list.h"
|
||||||
#include "core/host.h"
|
#include "core/host.h"
|
||||||
@ -179,11 +178,6 @@ void MainWindow::initialize()
|
|||||||
switchToGameListView();
|
switchToGameListView();
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (Achievements::IsUsingRAIntegration())
|
|
||||||
Achievements::RAIntegration::MainWindowChanged((void*)winId());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
registerForDeviceNotifications();
|
registerForDeviceNotifications();
|
||||||
#endif
|
#endif
|
||||||
@ -794,6 +788,8 @@ void MainWindow::recreate()
|
|||||||
dlg->setCategoryRow(settings_window_row);
|
dlg->setCategoryRow(settings_window_row);
|
||||||
QtUtils::ShowOrRaiseWindow(dlg);
|
QtUtils::ShowOrRaiseWindow(dlg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifyRAIntegrationOfWindowChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::destroySubWindows()
|
void MainWindow::destroySubWindows()
|
||||||
@ -1697,37 +1693,6 @@ void MainWindow::setupAdditionalUi()
|
|||||||
s_disable_window_rounded_corners = Host::GetBaseBoolSettingValue("Main", "DisableWindowRoundedCorners", false);
|
s_disable_window_rounded_corners = Host::GetBaseBoolSettingValue("Main", "DisableWindowRoundedCorners", false);
|
||||||
if (s_disable_window_rounded_corners)
|
if (s_disable_window_rounded_corners)
|
||||||
PlatformMisc::SetWindowRoundedCornerState(reinterpret_cast<void*>(winId()), false);
|
PlatformMisc::SetWindowRoundedCornerState(reinterpret_cast<void*>(winId()), false);
|
||||||
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (Achievements::IsUsingRAIntegration())
|
|
||||||
{
|
|
||||||
QMenu* raMenu = new QMenu(QStringLiteral("&RAIntegration"));
|
|
||||||
m_ui.menuBar->insertMenu(m_ui.menuDebug->menuAction(), raMenu);
|
|
||||||
connect(raMenu, &QMenu::aboutToShow, this, [this, raMenu]() {
|
|
||||||
raMenu->clear();
|
|
||||||
|
|
||||||
const auto items = Achievements::RAIntegration::GetMenuItems();
|
|
||||||
for (const auto& [id, title, checked] : items)
|
|
||||||
{
|
|
||||||
if (id == 0)
|
|
||||||
{
|
|
||||||
raMenu->addSeparator();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* raAction = raMenu->addAction(QString::fromUtf8(title));
|
|
||||||
if (checked)
|
|
||||||
{
|
|
||||||
raAction->setCheckable(true);
|
|
||||||
raAction->setChecked(checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(raAction, &QAction::triggered, this,
|
|
||||||
[id = id]() { Host::RunOnCPUThread([id]() { Achievements::RAIntegration::ActivateMenuItem(id); }); });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::updateToolbarActions()
|
void MainWindow::updateToolbarActions()
|
||||||
@ -3089,3 +3054,100 @@ bool QtHost::IsSystemLocked()
|
|||||||
{
|
{
|
||||||
return (s_system_locked.load(std::memory_order_acquire) > 0);
|
return (s_system_locked.load(std::memory_order_acquire) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
|
#include "core/achievements.h"
|
||||||
|
#include "core/achievements_private.h"
|
||||||
|
|
||||||
|
#include "rc_client_raintegration.h"
|
||||||
|
|
||||||
|
void MainWindow::onRAIntegrationMenuChanged()
|
||||||
|
{
|
||||||
|
const auto lock = Achievements::GetLock();
|
||||||
|
|
||||||
|
if (!Achievements::IsUsingRAIntegration())
|
||||||
|
{
|
||||||
|
if (m_raintegration_menu)
|
||||||
|
{
|
||||||
|
m_ui.menuBar->removeAction(m_raintegration_menu->menuAction());
|
||||||
|
m_raintegration_menu->deleteLater();
|
||||||
|
m_raintegration_menu = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_raintegration_menu)
|
||||||
|
{
|
||||||
|
m_raintegration_menu = new QMenu(QStringLiteral("&RAIntegration"));
|
||||||
|
m_ui.menuBar->insertMenu(m_ui.menuDebug->menuAction(), m_raintegration_menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_raintegration_menu->clear();
|
||||||
|
|
||||||
|
const rc_client_raintegration_menu_t* menu = rc_client_raintegration_get_menu(Achievements::GetClient());
|
||||||
|
if (!menu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const rc_client_raintegration_menu_item_t& item :
|
||||||
|
std::span<const rc_client_raintegration_menu_item_t>(menu->items, menu->num_items))
|
||||||
|
{
|
||||||
|
if (item.id == 0)
|
||||||
|
{
|
||||||
|
m_raintegration_menu->addSeparator();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction* action = m_raintegration_menu->addAction(QString::fromUtf8(item.label));
|
||||||
|
action->setEnabled(item.enabled != 0);
|
||||||
|
action->setCheckable(item.checked != 0);
|
||||||
|
action->setChecked(item.checked != 0);
|
||||||
|
connect(action, &QAction::triggered, this, [id = item.id]() {
|
||||||
|
Host::RunOnCPUThread([id]() {
|
||||||
|
// not locked in case a callback fires immediately and tries to lock
|
||||||
|
// client will be safe since this is running on the main thread
|
||||||
|
if (!Achievements::IsUsingRAIntegration())
|
||||||
|
return;
|
||||||
|
|
||||||
|
rc_client_raintegration_activate_menu_item(Achievements::GetClient(), id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::notifyRAIntegrationOfWindowChange()
|
||||||
|
{
|
||||||
|
const auto lock = Achievements::GetLock();
|
||||||
|
if (!Achievements::IsUsingRAIntegration())
|
||||||
|
return;
|
||||||
|
|
||||||
|
HWND hwnd = static_cast<HWND>((void*)winId());
|
||||||
|
Host::RunOnCPUThread([hwnd]() {
|
||||||
|
const auto lock = Achievements::GetLock();
|
||||||
|
if (!Achievements::IsUsingRAIntegration())
|
||||||
|
return;
|
||||||
|
|
||||||
|
rc_client_raintegration_update_main_window_handle(Achievements::GetClient(), hwnd);
|
||||||
|
});
|
||||||
|
|
||||||
|
onRAIntegrationMenuChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Host::OnRAIntegrationMenuChanged()
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(g_main_window, "onRAIntegrationMenuChanged", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
|
||||||
|
void MainWindow::onRAIntegrationMenuChanged()
|
||||||
|
{
|
||||||
|
// has to be stubbed out because otherwise moc won't find it
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::notifyRAIntegrationOfWindowChange()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
@ -209,10 +209,12 @@ private Q_SLOTS:
|
|||||||
void onGameListEntryContextMenuRequested(const QPoint& point);
|
void onGameListEntryContextMenuRequested(const QPoint& point);
|
||||||
|
|
||||||
void onUpdateCheckComplete();
|
void onUpdateCheckComplete();
|
||||||
|
void onRAIntegrationMenuChanged();
|
||||||
|
|
||||||
void onDebugLogChannelsMenuAboutToShow();
|
void onDebugLogChannelsMenuAboutToShow();
|
||||||
void openCPUDebugger();
|
void openCPUDebugger();
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent* event) override;
|
void showEvent(QShowEvent* event) override;
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
@ -277,6 +279,7 @@ private:
|
|||||||
|
|
||||||
void registerForDeviceNotifications();
|
void registerForDeviceNotifications();
|
||||||
void unregisterForDeviceNotifications();
|
void unregisterForDeviceNotifications();
|
||||||
|
void notifyRAIntegrationOfWindowChange();
|
||||||
|
|
||||||
/// Fills menu with save state info and handlers.
|
/// Fills menu with save state info and handlers.
|
||||||
void populateGameListContextMenu(const GameList::Entry* entry, QWidget* parent_window, QMenu* menu);
|
void populateGameListContextMenu(const GameList::Entry* entry, QWidget* parent_window, QMenu* menu);
|
||||||
@ -345,6 +348,10 @@ private:
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void* m_device_notification_handle = nullptr;
|
void* m_device_notification_handle = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
|
||||||
|
QMenu* m_raintegration_menu = nullptr;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MainWindow* g_main_window;
|
extern MainWindow* g_main_window;
|
||||||
|
@ -201,12 +201,6 @@ bool QtHost::EarlyProcessStartup()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Config-based RAIntegration switch must happen before the main window is displayed.
|
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
if (!Achievements::IsUsingRAIntegration() && Host::GetBaseBoolSettingValue("Cheevos", "UseRAIntegration", false))
|
|
||||||
Achievements::SwitchToRAIntegration();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (System::ProcessStartup(&error)) [[likely]]
|
if (System::ProcessStartup(&error)) [[likely]]
|
||||||
return true;
|
return true;
|
||||||
@ -2725,9 +2719,6 @@ void QtHost::PrintCommandLineHelp(const char* progname)
|
|||||||
std::fprintf(stderr, " -nogui: Disables main window from being shown, exits on shutdown.\n");
|
std::fprintf(stderr, " -nogui: Disables main window from being shown, exits on shutdown.\n");
|
||||||
std::fprintf(stderr, " -bigpicture: Automatically starts big picture UI.\n");
|
std::fprintf(stderr, " -bigpicture: Automatically starts big picture UI.\n");
|
||||||
std::fprintf(stderr, " -earlyconsole: Creates console as early as possible, for logging.\n");
|
std::fprintf(stderr, " -earlyconsole: Creates console as early as possible, for logging.\n");
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
std::fprintf(stderr, " -raintegration: Use RAIntegration instead of built-in achievement support.\n");
|
|
||||||
#endif
|
|
||||||
std::fprintf(stderr, " --: Signals that no more arguments will follow and the remaining\n"
|
std::fprintf(stderr, " --: Signals that no more arguments will follow and the remaining\n"
|
||||||
" parameters make up the filename. Use when the filename contains\n"
|
" parameters make up the filename. Use when the filename contains\n"
|
||||||
" spaces or starts with a dash.\n");
|
" spaces or starts with a dash.\n");
|
||||||
@ -2858,13 +2849,6 @@ bool QtHost::ParseCommandLineParametersAndInitializeConfig(QApplication& app,
|
|||||||
s_cleanup_after_update = AutoUpdaterDialog::isSupported();
|
s_cleanup_after_update = AutoUpdaterDialog::isSupported();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#ifdef ENABLE_RAINTEGRATION
|
|
||||||
else if (CHECK_ARG("-raintegration"))
|
|
||||||
{
|
|
||||||
Achievements::SwitchToRAIntegration();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else if (CHECK_ARG("--"))
|
else if (CHECK_ARG("--"))
|
||||||
{
|
{
|
||||||
no_more_args = true;
|
no_more_args = true;
|
||||||
|
@ -161,21 +161,9 @@ void SettingsWindow::addPages()
|
|||||||
"<strong>Achievements</strong> from the menu. Mouse over an option for additional information, and "
|
"<strong>Achievements</strong> from the menu. Mouse over an option for additional information, and "
|
||||||
"Shift+Wheel to scroll this panel."));
|
"Shift+Wheel to scroll this panel."));
|
||||||
|
|
||||||
if (!Achievements::IsUsingRAIntegration())
|
|
||||||
{
|
|
||||||
addWidget(m_achievement_settings = new AchievementSettingsWidget(this, m_ui.settingsContainer), std::move(title),
|
addWidget(m_achievement_settings = new AchievementSettingsWidget(this, m_ui.settingsContainer), std::move(title),
|
||||||
std::move(icon_text), std::move(help_text));
|
std::move(icon_text), std::move(help_text));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
QLabel* placeholder_label =
|
|
||||||
new QLabel(QStringLiteral("RAIntegration is being used, built-in RetroAchievements support is disabled."),
|
|
||||||
m_ui.settingsContainer);
|
|
||||||
placeholder_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
|
||||||
|
|
||||||
addWidget(placeholder_label, std::move(title), std::move(icon_text), std::move(help_text));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isPerGameSettings())
|
if (!isPerGameSettings())
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user