From 2fd47cd7ebea5c5fe67c31fe990aa470fd7f6e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 2 Nov 2026 23:06:35 +0100 Subject: [PATCH] sceReg: Sync string keys with config, map the rest of the listed settings owner_name and time_zone_area now read from and write to sNickName and sTimeZoneArea. Also maps language, the date/time keys, summer_time and parental_level. The config values are applied in the merged category view, so reads by handle and the key info sizes see them too, not just sceRegGetKeyValueByName. language reads through GetPSPLanguage() so that the -1 'auto' setting reads as an actual language. Co-Authored-By: Claude Opus 5.5 --- Core/ConfigSettings.cpp | 61 ++++++++++++-------- Core/ConfigSettings.h | 4 +- Core/HLE/sceReg.cpp | 119 +++++++++++++++++++++++----------------- 3 files changed, 111 insertions(+), 73 deletions(-) diff --git a/Core/ConfigSettings.cpp b/Core/ConfigSettings.cpp index 57d3d875c6..ce93b83451 100644 --- a/Core/ConfigSettings.cpp +++ b/Core/ConfigSettings.cpp @@ -406,33 +406,48 @@ void ConfigSetting::ReportSetting(const ConfigBlock *configBlock, UrlEncoder &da int ConfigSettingRef::GetIntValue() const { _dbg_assert_(setting); - _dbg_assert_(setting->GetType() == ConfigSetting::Type::TYPE_INT); - int intValue = 0; - if (setting->GetType() == ConfigSetting::Type::TYPE_INT) { - void *ptr = setting->VoidPtr(block); - memcpy(&intValue, ptr, 4); - } else if (setting->GetType() == ConfigSetting::Type::TYPE_BOOL) { - void *ptr = setting->VoidPtr(block); - bool boolValue = false; - memcpy(&boolValue, ptr, 1); - intValue = boolValue ? 1 : 0; - } else { - ERROR_LOG(Log::Config, "ConfigSettingRef::GetIntValue: Setting is not an int or bool"); + const void *ptr = setting->GetVoidPtr(block); + switch (setting->GetType()) { + case ConfigSetting::Type::TYPE_INT: + return *(const int *)ptr; + case ConfigSetting::Type::TYPE_BOOL: + return *(const bool *)ptr ? 1 : 0; + default: + ERROR_LOG(Log::Config, "ConfigSettingRef::GetIntValue: %.*s is not an int or bool", STR_VIEW(setting->IniKey())); + return 0; } - return intValue; } void ConfigSettingRef::SetIntValue(int value) const { _dbg_assert_(setting); - _dbg_assert_(setting->GetType() == ConfigSetting::Type::TYPE_INT); - if (setting->GetType() == ConfigSetting::Type::TYPE_INT) { - void *ptr = setting->VoidPtr(block); - memcpy(ptr, &value, 4); - } else if (setting->GetType() == ConfigSetting::Type::TYPE_BOOL) { - void *ptr = setting->VoidPtr(block); - bool boolValue = (value != 0); - memcpy(ptr, &boolValue, 1); - } else { - ERROR_LOG(Log::Config, "ConfigSettingRef::SetIntValue: Setting is not an int or bool"); + void *ptr = setting->VoidPtr(block); + switch (setting->GetType()) { + case ConfigSetting::Type::TYPE_INT: + *(int *)ptr = value; + break; + case ConfigSetting::Type::TYPE_BOOL: + *(bool *)ptr = value != 0; + break; + default: + ERROR_LOG(Log::Config, "ConfigSettingRef::SetIntValue: %.*s is not an int or bool", STR_VIEW(setting->IniKey())); + break; } } + +const std::string *ConfigSettingRef::GetStringPtr() const { + _dbg_assert_(setting); + if (setting->GetType() != ConfigSetting::Type::TYPE_STRING) { + ERROR_LOG(Log::Config, "ConfigSettingRef::GetStringPtr: %.*s is not a string", STR_VIEW(setting->IniKey())); + return nullptr; + } + return (const std::string *)setting->GetVoidPtr(block); +} + +void ConfigSettingRef::SetStringValue(std::string_view value) const { + _dbg_assert_(setting); + if (setting->GetType() != ConfigSetting::Type::TYPE_STRING) { + ERROR_LOG(Log::Config, "ConfigSettingRef::SetStringValue: %.*s is not a string", STR_VIEW(setting->IniKey())); + return; + } + *(std::string *)setting->VoidPtr(block) = std::string(value); +} diff --git a/Core/ConfigSettings.h b/Core/ConfigSettings.h index cfe27c04a8..938caa960f 100644 --- a/Core/ConfigSettings.h +++ b/Core/ConfigSettings.h @@ -308,7 +308,9 @@ struct ConfigSettingRef { const ConfigSetting *setting; std::string_view section; - // Easy accessors. + // Easy accessors. The int ones also handle bools, as 0 or 1. int GetIntValue() const; void SetIntValue(int value) const; + const std::string *GetStringPtr() const; // nullptr if not a string setting. + void SetStringValue(std::string_view value) const; }; diff --git a/Core/HLE/sceReg.cpp b/Core/HLE/sceReg.cpp index 177dc58786..f9f6bf61e9 100644 --- a/Core/HLE/sceReg.cpp +++ b/Core/HLE/sceReg.cpp @@ -5,6 +5,7 @@ #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/ErrorCodes.h" +#include "Core/HLE/sceUtility.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeMap.h" #include "Common/StringUtils.h" @@ -19,13 +20,13 @@ // Bools are stored as 4-byte INT registry keys, 0 or 1. // // CONFIG/SYSTEM/owner_name <-> g_Config.sNickName -// CONFIG/SYSTEM/XMB/language <-> g_Config.iLanguage +// CONFIG/SYSTEM/XMB/language <-> g_Config.iLanguage (-1 means auto, and reads as what GetPSPLanguage() picks) // CONFIG/SYSTEM/umd_autoboot <-> g_Config.bVshUmdAutoboot - and in addition, auto-controlled when booting UMD VIDEO. // CONFIG/SYSTEM/umd_cache <-> (unmapped, ignore) // CONFIG/SYSTEM/usb_autoconnect <-> (unmapped, ignore) // CONFIG/DATE/date_format <-> g_Config.iDateFormat // CONFIG/DATE/time_format <-> g_Config.iTimeFormat -// CONFIG/DATE/time_zone_area <-> g_Config.iTimeZoneArea +// CONFIG/DATE/time_zone_area <-> g_Config.sTimeZoneArea // CONFIG/DATE/time_zone_offset <-> g_Config.iTimeZoneOffset // CONFIG/DATE/summer_time <-> g_Config.bDayLightSavings // CONFIG/SYSTEM/LOCK/parental_level <-> g_Config.iLockParentalLevel, int @@ -37,7 +38,7 @@ // CONFIG/SYSTEM/SOUND/dynamic_normalizer <-> (unmapped, bool) // CONFIG/SYSTEM/SOUND/operation_sound_mode <-> (unmapped, bool) (click sound when navigating the XMB) // CONFIG/RSS/download_items (unmapped, int) -// CONFIG/SYSTEM/XMB/button_assign (unmapped, int) << TODO +// CONFIG/SYSTEM/XMB/button_assign <-> g_Config.iButtonPreference // CONFIG/SYSTEM/XMB/theme_type (unmapped, int) << TODO // CONFIG/SYSTEM/XMB/THEME/color_mode (unmapped, int) << TODO // CONFIG/SYSTEM/XMB/THEME/system_color (unmapped, int) << TODO @@ -93,15 +94,17 @@ struct KeyValue { const char *strValue; // also can be used for bin. Note: can't be std::string/string_view, will cut off the length. int intValue; const KeyValue *dirContents; // intValue is the count. - // If set, an INT key reads this setting (as 0 or 1), even after the guest writes it: the VSH - // rewrites every key with its default at each boot, since category_version is below its schema. - void *configSetting; // Read/write according to config's type, we look it up by pointer (int or std::string for str/bin). - bool (*boolSetting)(); // Dynamic. Only used for UMD autoboot. + // If set, the key is synced both ways with this g_Config member (looked up by pointer): reads + // return the setting and guest writes go to it. INT keys map to int or bool settings, STR keys + // to std::string ones. + void *configSetting; + // If set, reads of an INT key return this instead, while writes still go to configSetting. + int (*readOverride)(); }; -static bool UmdAutoboot() { +static int UmdAutoboot() { // Forced on when a UMD Video disc picked from the game list boots the XMB. - return PSP_CoreParameter().umdAutoboot.value_or(g_Config.bVshUmdAutoboot); + return PSP_CoreParameter().umdAutoboot.value_or(g_Config.bVshUmdAutoboot) ? 1 : 0; } // Guest-written registry values (sceRegCreateKey / sceRegSetKeyValue), keyed by category path @@ -768,8 +771,8 @@ static const KeyValue tree_CONFIG_SYSTEM_XMB_THEME[] = { // Dump of /CONFIG/SYSTEM/XMB static const KeyValue tree_CONFIG_SYSTEM_XMB[] = { { "theme_type", ValueType::INT, "", (int)0x0 }, // decimal: 0 - { "language", ValueType::INT, "", (int)0x1 }, // decimal: 1 - { "button_assign", ValueType::INT, "", (int)0x1, nullptr, &g_Config.iButtonPreference}, + { "language", ValueType::INT, "", (int)0x1, nullptr, &g_Config.iLanguage, &GetPSPLanguage }, // decimal: 1 + { "button_assign", ValueType::INT, "", (int)0x1, nullptr, &g_Config.iButtonPreference }, { "THEME", ValueType::DIR, "", ARRAY_SIZE(tree_CONFIG_SYSTEM_XMB_THEME), tree_CONFIG_SYSTEM_XMB_THEME }, }; @@ -794,7 +797,7 @@ static const KeyValue tree_CONFIG_SYSTEM_POWER_SAVING[] = { // Dump of /CONFIG/SYSTEM/LOCK static const KeyValue tree_CONFIG_SYSTEM_LOCK[] = { { "password", ValueType::BIN, "0000", 4 }, - { "parental_level", ValueType::INT, "", (int)0x0 }, // decimal: 0 + { "parental_level", ValueType::INT, "", (int)0x0, nullptr, &g_Config.iLockParentalLevel }, // decimal: 0 { "browser_start", ValueType::INT, "", (int)0x0 }, // decimal: 0 }; @@ -806,9 +809,9 @@ static const KeyValue tree_CONFIG_SYSTEM_CHARACTER_SET[] = { // Dump of /CONFIG/SYSTEM static const KeyValue tree_CONFIG_SYSTEM[] = { - { "owner_name", ValueType::STR, "P939", 5 }, + { "owner_name", ValueType::STR, "P939", 5, nullptr, &g_Config.sNickName }, { "backlight_brightness", ValueType::INT, "", (int)0x3 }, // decimal: 3 - { "umd_autoboot", ValueType::INT, "", (int)0x1, nullptr, nullptr, &UmdAutoboot }, // decimal: 1 + { "umd_autoboot", ValueType::INT, "", (int)0x1, nullptr, &g_Config.bVshUmdAutoboot, &UmdAutoboot }, // decimal: 1 { "usb_charge", ValueType::INT, "", (int)0x1 }, // decimal: 1 { "umd_cache", ValueType::INT, "", (int)0x1 }, // decimal: 1 { "usb_auto_connect", ValueType::INT, "", (int)0x1 }, // decimal: 1 @@ -827,11 +830,11 @@ static const KeyValue tree_CONFIG_SYSTEM[] = { // Dump of /CONFIG/DATE static const KeyValue tree_CONFIG_DATE[] = { - { "time_format", ValueType::INT, "", (int)0x0 }, // decimal: 0 - { "date_format", ValueType::INT, "", (int)0x2 }, // decimal: 2 - { "summer_time", ValueType::INT, "", (int)0x0 }, // decimal: 0 - { "time_zone_offset", ValueType::INT, "", (int)0x3c }, // decimal: 60 - { "time_zone_area", ValueType::STR, "sweden", 7 }, + { "time_format", ValueType::INT, "", (int)0x0, nullptr, &g_Config.iTimeFormat }, // decimal: 0 + { "date_format", ValueType::INT, "", (int)0x2, nullptr, &g_Config.iDateFormat }, // decimal: 2 + { "summer_time", ValueType::INT, "", (int)0x0, nullptr, &g_Config.bDayLightSavings }, // decimal: 0 + { "time_zone_offset", ValueType::INT, "", (int)0x3c, nullptr, &g_Config.iTimeZoneOffset }, // decimal: 60 + { "time_zone_area", ValueType::STR, "sweden", 7, nullptr, &g_Config.sTimeZoneArea }, }; // Dump of /CONFIG/NETWORK/ADHOC @@ -1090,10 +1093,45 @@ static const KeyValue *LookupStaticCategory(const std::string &path, int *count) return curDir; } +// Points the config-mapped keys at the current g_Config values. Done on every lookup, since the +// settings can change outside the registry (in our UI, or a per-game config being loaded). +static void ApplyConfigValues(MergedCategory &merged) { + for (KeyValue &kv : merged.keyvals) { + if (kv.readOverride && kv.type == ValueType::INT) { + kv.intValue = kv.readOverride(); + continue; + } + if (!kv.configSetting) { + continue; + } + const ConfigSettingRef *ref = g_Config.FindSetting(kv.configSetting); + if (!ref) { + continue; + } + if (kv.type == ValueType::INT) { + kv.intValue = ref->GetIntValue(); + } else if (kv.type == ValueType::STR) { + const std::string *str = ref->GetStringPtr(); + if (!str) { + continue; + } + // Like the static dump, the length includes the null terminator. + const int len = (int)str->size() + 1; + if (kv.strValue && kv.intValue == len && memcmp(kv.strValue, str->c_str(), len) == 0) { + continue; + } + merged.strStorage.push_back(*str); + kv.strValue = merged.strStorage.back().c_str(); + kv.intValue = len; + } + } +} + // Merges the static dump entries for path with the overlay entries written by the guest. static const MergedCategory &GetMergedCategory(const std::string &path) { auto cached = g_mergedCache.find(path); if (cached != g_mergedCache.end()) { + ApplyConfigValues(cached->second); return cached->second; } @@ -1134,12 +1172,7 @@ static const MergedCategory &GetMergedCategory(const std::string &path) { } } - for (auto &kv : merged.keyvals) { - if (kv.boolSetting) { - kv.intValue = kv.boolSetting() ? 1 : 0; - } - } - + ApplyConfigValues(merged); return merged; } @@ -1539,20 +1572,10 @@ int sceRegGetKeyValueByName(int catHandle, const char *name, u32 bufAddr, u32 si return hleLogDebug(Log::sceReg, 0, "value: '%s'", keyval.strValue); } case ValueType::INT: - { - int intValue = keyval.intValue; // The preset. - // Check for a config mapping. Read the value. - if (keyval.configSetting) { - if (const ConfigSettingRef *ref = g_Config.FindSetting(keyval.configSetting)) { - intValue = ref->GetIntValue(); - } - } - if (size >= sizeof(u32)) { - Memory::WriteUnchecked_U32(intValue, bufAddr); + Memory::WriteUnchecked_U32(keyval.intValue, bufAddr); } - return hleLogDebug(Log::sceReg, 0, "value: %d (0x%08x)", intValue, intValue); - } + return hleLogDebug(Log::sceReg, 0, "value: %d (0x%08x)", keyval.intValue, keyval.intValue); case ValueType::DIR: case ValueType::FAIL: default: @@ -1582,9 +1605,13 @@ int sceRegSetKeyValue(int catHandle, const char *name, u32 bufAddr, u32 size) { // registry upgrade rewrites BIN keys like /CONFIG/NP/account_id, and reports the settings as // corrupted if they read back as anything else. ValueType type = ValueType::FAIL; + const ConfigSettingRef *configRef = nullptr; for (const auto &kv : GetMergedCategory(path).keyvals) { if (equals(kv.name, name)) { type = kv.type; + if (kv.configSetting) { + configRef = g_Config.FindSetting(kv.configSetting); + } break; } } @@ -1614,22 +1641,12 @@ int sceRegSetKeyValue(int catHandle, const char *name, u32 bufAddr, u32 size) { return hleLogError(Log::sceReg, -1, "bad input addr"); } value.intValue = (int)Memory::ReadUnchecked_U32(bufAddr); - - // Write to the actual Config setting, if configured. - int count = 0; - const KeyValue *keyvals = LookupCategory(path, &count); - const KeyValue *keyval = LookupCategoryKey(keyvals, name, count); - if (keyval->configSetting) { - if (const ConfigSettingRef *ref = g_Config.FindSetting(keyval->configSetting)) { - ref->SetIntValue(value.intValue); - } + if (configRef) { + configRef->SetIntValue(value.intValue); } g_overlay[path][name] = value; InvalidateMergedCache(); - - // If we have a config setting mapped, merge with that. - return hleLogInfo(Log::sceReg, 0, "set %s/%s to %d", path.c_str(), name, value.intValue); } else if (Memory::IsValidRange(bufAddr, size)) { const char *str = (const char *)Memory::GetPointerUnchecked(bufAddr); @@ -1639,6 +1656,10 @@ int sceRegSetKeyValue(int catHandle, const char *name, u32 bufAddr, u32 size) { value.str.push_back('\0'); } value.intValue = (int)value.str.size(); + if (configRef && type == ValueType::STR) { + // Up to the terminator, if the guest included one. + configRef->SetStringValue(std::string_view(value.str.c_str())); + } g_overlay[path][name] = value; InvalidateMergedCache(); if (type == ValueType::STR) {