Files

446 lines
16 KiB
C++
Raw Permalink Normal View History

// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
2024-07-30 13:42:36 +02:00
// SPDX-License-Identifier: GPL-3.0+
2023-05-13 14:30:41 +10:00
#include "BuildVersion.h"
2023-05-13 14:30:41 +10:00
#include "GS.h"
#include "GS/Renderers/HW/GSTextureReplacements.h"
#include "Host.h"
2023-05-13 14:30:41 +10:00
#include "LayeredSettingsInterface.h"
#include "VMManager.h"
#include "common/Assertions.h"
#include "common/CrashHandler.h"
#include "common/FileSystem.h"
2023-06-19 20:26:38 +10:00
#include "common/HeterogeneousContainers.h"
2023-05-13 14:30:41 +10:00
#include "common/Path.h"
#include "common/StringUtil.h"
2023-05-13 14:30:41 +10:00
#include "fmt/format.h"
#include <cstdarg>
2023-06-19 20:26:38 +10:00
#include <shared_mutex>
#if defined(__APPLE__)
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#include <sys/sysctl.h>
#endif
#endif
2023-06-19 20:26:38 +10:00
namespace Host
{
static std::pair<const char*, u32> LookupTranslationString(
const std::string_view context, const std::string_view msg);
2023-06-19 20:26:38 +10:00
static std::mutex s_settings_mutex;
2025-08-03 17:24:01 -04:00
static std::mutex s_secrets_settings_mutex;
2023-06-19 20:26:38 +10:00
static LayeredSettingsInterface s_layered_settings_interface;
static constexpr u32 TRANSLATION_STRING_CACHE_SIZE = 4 * 1024 * 1024;
using TranslationStringMap = UnorderedStringMap<std::pair<u32, u32>>;
using TranslationStringContextMap = UnorderedStringMap<TranslationStringMap>;
static std::shared_mutex s_translation_string_mutex;
static TranslationStringContextMap s_translation_string_map;
static std::vector<char> s_translation_string_cache;
static u32 s_translation_string_cache_pos;
} // namespace Host
std::pair<const char*, u32> Host::LookupTranslationString(const std::string_view context, const std::string_view msg)
2023-06-19 20:26:38 +10:00
{
// TODO: TranslatableString, compile-time hashing.
TranslationStringContextMap::iterator ctx_it;
TranslationStringMap::iterator msg_it;
std::pair<const char*, u32> ret;
s32 len;
// Shouldn't happen, but just in case someone tries to translate an empty string.
if (msg.empty()) [[unlikely]]
2023-06-19 20:26:38 +10:00
{
ret.first = &s_translation_string_cache[0];
ret.second = 0;
return ret;
}
s_translation_string_mutex.lock_shared();
2023-07-22 13:15:26 +10:00
ctx_it = s_translation_string_map.find(context);
2023-06-19 20:26:38 +10:00
if (ctx_it == s_translation_string_map.end()) [[unlikely]]
2023-06-19 20:26:38 +10:00
goto add_string;
2023-07-22 13:15:26 +10:00
msg_it = ctx_it->second.find(msg);
if (msg_it == ctx_it->second.end()) [[unlikely]]
2023-06-19 20:26:38 +10:00
goto add_string;
ret.first = &s_translation_string_cache[msg_it->second.first];
ret.second = msg_it->second.second;
s_translation_string_mutex.unlock_shared();
return ret;
add_string:
s_translation_string_mutex.unlock_shared();
2024-08-21 17:51:57 +02:00
std::lock_guard lock(s_translation_string_mutex);
2023-06-19 20:26:38 +10:00
if (s_translation_string_cache.empty()) [[unlikely]]
2023-06-19 20:26:38 +10:00
{
// First element is always an empty string.
s_translation_string_cache.resize(TRANSLATION_STRING_CACHE_SIZE);
s_translation_string_cache[0] = '\0';
s_translation_string_cache_pos = 0;
}
if ((len = Internal::GetTranslatedStringImpl(context, msg,
&s_translation_string_cache[s_translation_string_cache_pos],
TRANSLATION_STRING_CACHE_SIZE - 1 - s_translation_string_cache_pos)) < 0)
{
Console.Error("WARNING: Clearing translation string cache, it might need to be larger.");
s_translation_string_cache_pos = 0;
if ((len = Internal::GetTranslatedStringImpl(context, msg,
&s_translation_string_cache[s_translation_string_cache_pos],
TRANSLATION_STRING_CACHE_SIZE - 1 - s_translation_string_cache_pos)) < 0)
{
pxFailRel("Failed to get translated string after clearing cache.");
len = 0;
}
}
// New context?
if (ctx_it == s_translation_string_map.end())
ctx_it = s_translation_string_map.emplace(context, TranslationStringMap()).first;
// Impl doesn't null terminate, we need that for C strings.
// TODO: do we want to consider aligning the buffer?
const u32 insert_pos = s_translation_string_cache_pos;
s_translation_string_cache[insert_pos + static_cast<u32>(len)] = 0;
ctx_it->second.emplace(msg, std::pair<u32, u32>(insert_pos, static_cast<u32>(len)));
s_translation_string_cache_pos = insert_pos + static_cast<u32>(len) + 1;
ret.first = &s_translation_string_cache[insert_pos];
ret.second = static_cast<u32>(len);
return ret;
}
const char* Host::TranslateToCString(const std::string_view context, const std::string_view msg)
2023-06-19 20:26:38 +10:00
{
return LookupTranslationString(context, msg).first;
}
std::string_view Host::TranslateToStringView(const std::string_view context, const std::string_view msg)
2023-06-19 20:26:38 +10:00
{
const auto mp = LookupTranslationString(context, msg);
return std::string_view(mp.first, mp.second);
}
std::string Host::TranslateToString(const std::string_view context, const std::string_view msg)
2023-06-19 20:26:38 +10:00
{
return std::string(TranslateToStringView(context, msg));
}
2023-05-13 14:30:41 +10:00
2023-06-24 15:13:36 +10:00
void Host::ClearTranslationCache()
{
2024-08-21 17:51:57 +02:00
std::lock_guard lock(s_translation_string_mutex);
2023-06-24 15:13:36 +10:00
s_translation_string_map.clear();
s_translation_string_cache_pos = 0;
}
2025-03-02 23:04:19 +00:00
void Host::ReportFormattedInfoAsync(const std::string_view title, const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
std::string message(StringUtil::StdStringFromFormatV(format, ap));
va_end(ap);
ReportInfoAsync(title, message);
}
void Host::ReportFormattedErrorAsync(const std::string_view title, const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
std::string message(StringUtil::StdStringFromFormatV(format, ap));
va_end(ap);
ReportErrorAsync(title, message);
2022-08-20 20:20:36 +10:00
}
// The RetroAchievements client versions are injected from a gitignored header
// (ra_ua_secret.h) so the exact strings RA pins are not present in public source —
// third parties were spoofing our clients by copying the User-Agent verbatim. A fork or
// code-lift without the secret gets a stock UA that RA treats as unknown (no hardcore).
#if defined(__has_include)
# if __has_include("ra_ua_secret.h")
# include "ra_ua_secret.h"
# endif
#endif
#if defined(__APPLE__) && TARGET_OS_IPHONE
// Human-readable iOS version for the RA User-Agent detail (e.g. "iOS 17.5").
static std::string GetIOSVersionForUserAgent()
{
char version[64] = {};
size_t version_len = sizeof(version);
if (sysctlbyname("kern.osproductversion", version, &version_len, nullptr, 0) == 0 && version[0] != '\0')
return fmt::format("iOS {}", version);
return GetOSVersionString();
}
#endif
std::string Host::GetHTTPUserAgent()
{
// RetroAchievements identifies the client by the leading Name/Version token of this
// User-Agent. iOS ships the ARMSX2-iOS client, everything else the ARMSX2 client; the
// version comes from the gitignored ra_ua_secret.h. A build without the secret sends a
// token RA doesn't recognise (no hardcore) rather than leaking a real, still-valid one.
#if defined(__APPLE__) && TARGET_OS_IPHONE
#if defined(ARMSX2_IOS_RA_UA_VERSION)
const char* core_version = (BuildVersion::GitTag && BuildVersion::GitTag[0]) ? BuildVersion::GitTag : BuildVersion::GitRev;
return fmt::format("ARMSX2-iOS/v" ARMSX2_IOS_RA_UA_VERSION " ({}) pcsx2/{}", GetIOSVersionForUserAgent(), core_version);
#else
return fmt::format("PCSX2 {} ({})", BuildVersion::GitRev, GetOSVersionString());
#endif
#else
#if defined(ARMSX2_RA_UA_VERSION)
return fmt::format("ARMSX2/" ARMSX2_RA_UA_VERSION " ({})", GetOSVersionString());
#else
return fmt::format("PCSX2-community ({})", GetOSVersionString());
#endif
#endif
}
2023-05-13 14:30:41 +10:00
std::unique_lock<std::mutex> Host::GetSettingsLock()
{
return std::unique_lock<std::mutex>(s_settings_mutex);
}
2025-08-03 17:24:01 -04:00
std::unique_lock<std::mutex> Host::GetSecretsSettingsLock()
{
return std::unique_lock<std::mutex>(s_secrets_settings_mutex);
}
2023-05-13 14:30:41 +10:00
SettingsInterface* Host::GetSettingsInterface()
{
return &s_layered_settings_interface;
}
std::string Host::GetBaseStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetStringValue(section, key, default_value);
}
SmallString Host::GetBaseSmallStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetSmallStringValue(section, key, default_value);
}
TinyString Host::GetBaseTinyStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetTinyStringValue(section, key, default_value);
}
2023-05-13 14:30:41 +10:00
bool Host::GetBaseBoolSettingValue(const char* section, const char* key, bool default_value /*= false*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetBoolValue(section, key, default_value);
}
int Host::GetBaseIntSettingValue(const char* section, const char* key, int default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetIntValue(section, key, default_value);
}
uint Host::GetBaseUIntSettingValue(const char* section, const char* key, uint default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetUIntValue(section, key, default_value);
}
float Host::GetBaseFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetFloatValue(section, key, default_value);
}
double Host::GetBaseDoubleSettingValue(const char* section, const char* key, double default_value /* = 0.0f */)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->GetDoubleValue(section, key, default_value);
}
std::vector<std::string> Host::GetBaseStringListSetting(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->GetStringList(section, key);
}
void Host::SetBaseBoolSettingValue(const char* section, const char* key, bool value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetBoolValue(section, key, value);
}
void Host::SetBaseIntSettingValue(const char* section, const char* key, int value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetIntValue(section, key, value);
}
void Host::SetBaseUIntSettingValue(const char* section, const char* key, uint value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetUIntValue(section, key, value);
}
void Host::SetBaseFloatSettingValue(const char* section, const char* key, float value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetFloatValue(section, key, value);
}
void Host::SetBaseStringSettingValue(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringValue(section, key, value);
}
void Host::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringList(section, key, values);
}
bool Host::AddBaseValueToStringList(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->AddToStringList(section, key, value);
}
bool Host::RemoveBaseValueFromStringList(const char* section, const char* key, const char* value)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
->RemoveFromStringList(section, key, value);
}
2023-05-29 20:42:46 +10:00
bool Host::ContainsBaseSettingValue(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->ContainsValue(section, key);
}
2023-05-13 14:30:41 +10:00
void Host::RemoveBaseSettingValue(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->DeleteValue(section, key);
}
SmallString Host::GetSmallStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetSmallStringValue(section, key, default_value);
}
TinyString Host::GetTinyStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetTinyStringValue(section, key, default_value);
}
2023-05-13 14:30:41 +10:00
std::string Host::GetStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetStringValue(section, key, default_value);
}
bool Host::GetBoolSettingValue(const char* section, const char* key, bool default_value /*= false*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetBoolValue(section, key, default_value);
}
int Host::GetIntSettingValue(const char* section, const char* key, int default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetIntValue(section, key, default_value);
}
uint Host::GetUIntSettingValue(const char* section, const char* key, uint default_value /*= 0*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetUIntValue(section, key, default_value);
}
float Host::GetFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetFloatValue(section, key, default_value);
}
double Host::GetDoubleSettingValue(const char* section, const char* key, double default_value /*= 0.0f*/)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetDoubleValue(section, key, default_value);
}
std::vector<std::string> Host::GetStringListSetting(const char* section, const char* key)
{
std::unique_lock lock(s_settings_mutex);
return s_layered_settings_interface.GetStringList(section, key);
}
SettingsInterface* Host::Internal::GetBaseSettingsLayer()
{
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE);
}
2025-08-03 17:24:01 -04:00
SettingsInterface* Host::Internal::GetSecretsSettingsLayer()
{
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_SECRETS);
}
2023-05-13 14:30:41 +10:00
SettingsInterface* Host::Internal::GetGameSettingsLayer()
{
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_GAME);
}
SettingsInterface* Host::Internal::GetInputSettingsLayer()
{
return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_INPUT);
}
void Host::Internal::SetBaseSettingsLayer(SettingsInterface* sif)
{
pxAssertRel(s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE) == nullptr,
2025-08-03 17:24:01 -04:00
"Base layer has already been set");
2023-05-13 14:30:41 +10:00
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_BASE, sif);
}
2025-08-03 17:24:01 -04:00
void Host::Internal::SetSecretsSettingsLayer(SettingsInterface* sif)
{
pxAssertRel(s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_SECRETS) == nullptr,
"Secrets layer has already been set");
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_SECRETS, sif);
}
void Host::Internal::SetGameSettingsLayer(SettingsInterface* sif, std::unique_lock<std::mutex>& settings_lock)
2023-05-13 14:30:41 +10:00
{
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_GAME, sif);
}
void Host::Internal::SetInputSettingsLayer(SettingsInterface* sif, std::unique_lock<std::mutex>& settings_lock)
2023-05-13 14:30:41 +10:00
{
s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_INPUT, sif);
}