diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index 6450eff1bc..9c49c9f7a0 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -23,7 +23,7 @@ static void TextToImage(std::string_view buttonText, ImageID *image) { if (buttonText == di->T("Delete") || buttonText == di->T("Move to trash")) { *image = ImageID("I_TRASHCAN"); } else if (buttonText == di->T("Back")) { - *image = ImageID("I_NAVIGATE_BACK"); + *image = ImageID("I_CHEVRON_LEFT"); } else if (buttonText == di->T("Add")) { *image = ImageID("I_PLUS"); } else if (buttonText == di->T("OK")) { diff --git a/Core/Config.cpp b/Core/Config.cpp index 17fad32ebc..4151f2ff4e 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1470,10 +1470,12 @@ static constexpr const char *paramDateFormatNames[] = { "YYYYMMDD", "MMDDYYYY", static constexpr const char *paramTimeFormatNames[] = { "24HR", "12HR" }; +// Most of this is just registry settings, stored in our ini format. sceReg reads/writes here. static constexpr ConfigSetting systemParamSettings[] = { ConfigSetting("PSPModel", SETTING(g_Config, iPSPModel), PSP_MODEL_SLIM, CfgFlag::PER_GAME | CfgFlag::REPORT) .Label(I18NCat::SYSTEM, "PSP Model").Choices(pSPModelNames), ConfigSetting("PSPFirmwareVersion", SETTING(g_Config, iFirmwareVersion), PSP_DEFAULT_FIRMWARE, CfgFlag::PER_GAME | CfgFlag::REPORT), + ConfigSetting("SceRegCategoryVersion", SETTING(g_Config, iSceRegCategoryVersion), 1, CfgFlag::DEFAULT), ConfigSetting("NickName", SETTING(g_Config, sNickName), "PPSSPP", CfgFlag::PER_GAME), ConfigSetting("MacAddress", SETTING(g_Config, sMACAddress), "", CfgFlag::PER_GAME), ConfigSetting("GameLanguage", SETTING(g_Config, iLanguage), -1, CfgFlag::PER_GAME | CfgFlag::REPORT) @@ -1482,7 +1484,8 @@ static constexpr ConfigSetting systemParamSettings[] = { .Label(I18NCat::SYSTEM, "Time Format").Choices(paramTimeFormatNames), ConfigSetting("ParamDateFormat", SETTING(g_Config, iDateFormat), PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD, CfgFlag::PER_GAME) .Label(I18NCat::SYSTEM, "Date Format").Choices(paramDateFormatNames), - ConfigSetting("TimeZone", SETTING(g_Config, iTimeZone), 0, CfgFlag::PER_GAME), + ConfigSetting("TimeZone", SETTING(g_Config, iTimeZoneOffset), 0, CfgFlag::PER_GAME), + ConfigSetting("TimeZoneArea", SETTING(g_Config, sTimeZoneArea), "sweden", CfgFlag::PER_GAME), // This doesn't really do much. Just cosmetic in the XMB ConfigSetting("DayLightSavings", SETTING(g_Config, bDayLightSavings), (bool) PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_STD, CfgFlag::PER_GAME) .Label(I18NCat::SYSTEM, "Daylight savings"), ConfigSetting("ButtonPreference", SETTING(g_Config, iButtonPreference), PSP_SYSTEMPARAM_BUTTON_CROSS, CfgFlag::PER_GAME | CfgFlag::REPORT) @@ -1495,6 +1498,11 @@ static constexpr ConfigSetting systemParamSettings[] = { ConfigSetting("WlanPowerSave", SETTING(g_Config, bWlanPowerSave), (bool) PSP_SYSTEMPARAM_WLAN_POWERSAVE_OFF, CfgFlag::PER_GAME), ConfigSetting("EncryptSave", SETTING(g_Config, bEncryptSave), true, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("VshUmdAutoboot", SETTING(g_Config, bVshUmdAutoboot), false, CfgFlag::DEFAULT), + ConfigSetting("XmbThemeType", SETTING(g_Config, iXmbThemeType), 0, CfgFlag::DEFAULT), + ConfigSetting("XmbThemeColorMode", SETTING(g_Config, iXmbThemeColorMode), 0, CfgFlag::DEFAULT), + ConfigSetting("XmbThemeWallpaperMode", SETTING(g_Config, iXmbThemeWallpaperMode), 0, CfgFlag::DEFAULT), + ConfigSetting("XmbThemeSystemColor", SETTING(g_Config, iXmbThemeSystemColor), 0, CfgFlag::DEFAULT), + ConfigSetting("XmbThemeCustomThemeMode", SETTING(g_Config, iXmbThemeCustomThemeMode), 0, CfgFlag::DEFAULT), ConfigSetting("MemStickSize", SETTING(g_Config, iMemStickSizeGB), 16, CfgFlag::DEFAULT) .Label(I18NCat::SYSTEM, "Memory Stick size").Range(1, 32).Units("GB"), ConfigSetting("FuseID", SETTING(g_Config, sFuseID), "", CfgFlag::DEFAULT), diff --git a/Core/Config.h b/Core/Config.h index afa427572e..ea5f82caa9 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -601,7 +601,10 @@ public: // Risky JIT optimizations bool bDiscardRegsOnJRRA; - // SystemParam + // SystemParam settings - well, they are queries by systemparam but they're actually stored in the registry. + // So now, we mirror these out into the actual registry. + int iSceRegCategoryVersion; // The version of the registry. Resets to 1 if you install an older firmware. The system will reject too-new regs and rewrite too-old ones. + std::string sNickName; // AdHoc and system nickname std::string sMACAddress; // The Fuse ID of the PSP a game patch was installed on (12 hex digits), to decrypt it with. @@ -610,13 +613,20 @@ public: int iLanguage; int iTimeFormat; int iDateFormat; - int iTimeZone; + int iTimeZoneOffset; + std::string sTimeZoneArea; bool bDayLightSavings; int iButtonPreference; int iLockParentalLevel; bool bEncryptSave; // /CONFIG/SYSTEM/umd_autoboot: whether the VSH starts an inserted disc by itself. bool bVshUmdAutoboot; + // /CONFIG/SYSTEM/XMB (language and button_assign are iLanguage and iButtonPreference). + int iXmbThemeType; + int iXmbThemeColorMode; + int iXmbThemeWallpaperMode; + int iXmbThemeSystemColor; + int iXmbThemeCustomThemeMode; // Networking bool bEnableAdhocServer; diff --git a/Core/ConfigSettings.cpp b/Core/ConfigSettings.cpp index d8a03e0ed1..ce93b83451 100644 --- a/Core/ConfigSettings.cpp +++ b/Core/ConfigSettings.cpp @@ -403,3 +403,51 @@ void ConfigSetting::ReportSetting(const ConfigBlock *configBlock, UrlEncoder &da return; } } + +int ConfigSettingRef::GetIntValue() const { + _dbg_assert_(setting); + 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; + } +} + +void ConfigSettingRef::SetIntValue(int value) const { + _dbg_assert_(setting); + 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 8e59599558..938caa960f 100644 --- a/Core/ConfigSettings.h +++ b/Core/ConfigSettings.h @@ -251,6 +251,9 @@ struct ConfigSetting { _dbg_assert_(type_ == Type::TYPE_FLOAT); return defaultCallback_.f ? defaultCallback_.f() : default_.f; } + Type GetType() const { + return type_; + } constexpr const ConfigSettingInfo &Info() const { return info_; @@ -274,6 +277,10 @@ struct ConfigSetting { const char *configBlockBase = (const char *)configBlock; return (const void *)(configBlockBase + offset_); } + void *VoidPtr(const ConfigBlock *configBlock) const { + const char *configBlockBase = (const char *)configBlock; + return (void *)(configBlockBase + offset_); + } private: static constexpr void Check(bool ok) { @@ -300,4 +307,10 @@ struct ConfigSettingRef { ConfigBlock *block; const ConfigSetting *setting; std::string_view section; + + // 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/ELF/PrxDecrypter.cpp b/Core/ELF/PrxDecrypter.cpp index ebe91b6cb1..6fb0c69668 100644 --- a/Core/ELF/PrxDecrypter.cpp +++ b/Core/ELF/PrxDecrypter.cpp @@ -923,7 +923,7 @@ static bool KirkOutputFits(const KIRK_CMD1_ECDSA_HEADER *header, u32 size) { } static int pspDecryptType0(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size) { - DEBUG_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]); + DEBUG_LOG(Log::Loader, "Decrypting tag %02X (type 0)", (u32)*(u32_le *)&inbuf[0xD0]); const auto decryptSize = *(s32_le*)&inbuf[0xB0]; const auto pti = GetTagInfo((u32)*(u32_le *)&inbuf[0xD0]); @@ -985,9 +985,8 @@ static int pspDecryptType0(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 siz return decryptSize; } -static int pspDecryptType1(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size) -{ - INFO_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]); +static int pspDecryptType1(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size) { + DEBUG_LOG(Log::Loader, "Decrypting tag %02X (type 1)", (u32)*(u32_le *)&inbuf[0xD0]); const auto decryptSize = *(s32_le*)&inbuf[0xB0]; const auto pti = GetTagInfo((u32)*(u32_le *)&inbuf[0xD0]); @@ -1045,9 +1044,8 @@ static int pspDecryptType1(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 siz return decryptSize; } -static int pspDecryptType2(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size) -{ - INFO_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]); +static int pspDecryptType2(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size) { + DEBUG_LOG(Log::Loader, "Decrypting tag %02X (type 2)", (u32)*(u32_le *)&inbuf[0xD0]); const auto decryptSize = *(s32_le*)&inbuf[0xB0]; const auto pti = GetTagInfo2((u32)*(u32_le *)&inbuf[0xD0]); @@ -1165,9 +1163,8 @@ static int pspFinishType5(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size return decryptSize; } -static int pspDecryptType5(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size, const u8 *seed) - { - INFO_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]); +static int pspDecryptType5(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size, const u8 *seed) { + DEBUG_LOG(Log::Loader, "Decrypting tag %02X (type 5)", (u32)*(u32_le *)&inbuf[0xD0]); const auto pti = GetTagInfo2((u32)*(u32_le *)&inbuf[0xD0]); if (!pti) @@ -1206,7 +1203,7 @@ static int pspDecryptType3(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 siz { return -1; } - INFO_LOG(Log::Loader, "Decrypting tag %08X", (u32)*(u32_le *)&inbuf[0xD0]); + DEBUG_LOG(Log::Loader, "Decrypting tag %08X (type 3)", (u32)*(u32_le *)&inbuf[0xD0]); auto xorbuf = expandSeed(pti->key, pti->code, secureId); PRXType5 type5(inbuf); @@ -1221,7 +1218,7 @@ static int pspDecryptType3(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 siz static int pspDecryptType6(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size) { - INFO_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]); + DEBUG_LOG(Log::Loader, "Decrypting tag %02X (type 6)", (u32)*(u32_le *)&inbuf[0xD0]); const auto decryptSize = *(s32_le*)&inbuf[0xB0]; const auto pti = GetTagInfo2((u32)*(u32_le *)&inbuf[0xD0]); @@ -1297,7 +1294,7 @@ static int pspDecryptType6(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 siz // type 6 writes its tail there. static int pspDecryptType9(KirkState *kirk, const u8 *inbuf, u8 *outbuf, u32 size) { - INFO_LOG(Log::Loader, "Decrypting tag %02X", (u32)*(u32_le *)&inbuf[0xD0]); + DEBUG_LOG(Log::Loader, "Decrypting tag %02X (type 9)", (u32)*(u32_le *)&inbuf[0xD0]); const auto decryptSize = *(s32_le*)&inbuf[0xB0]; const auto pti = GetTagInfo2((u32)*(u32_le *)&inbuf[0xD0]); diff --git a/Core/HLE/sceImpose.cpp b/Core/HLE/sceImpose.cpp index 3faec2b55d..d03b1bc4bf 100644 --- a/Core/HLE/sceImpose.cpp +++ b/Core/HLE/sceImpose.cpp @@ -293,6 +293,10 @@ const HLEFunction sceImpose_driver[] = { {0X01EF0650, &WrapI_I, "sceImposeSetStatus", 'i', "i" }, {0X2462EFE4, &WrapI_I, "sceImposeSetStatus", 'i', "i" }, {0XC10C3D39, &WrapI_I, "sceImposeSetStatus", 'i', "i" }, + + // Unknown, spams in older firmwares (3.80). Probably aliases for Get something. + {0x21d3ea22, 0, "sceImpose_21D3EA22", '?', ""}, + {0xd4fda428, 0, "sceImpose_d4fda428", '?', ""}, }; void Register_sceImpose_driver() { diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 6b21faed0d..4c1c8bc701 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -850,7 +850,7 @@ void ImportVarSymbol(WriteVarSymbolState &state, const VarSymbolImport &var) { } // It hasn't been exported yet, but hopefully it will later. - INFO_LOG(Log::Loader, "Variable (%s,%08x) unresolved, storing for later resolving", var.moduleName, var.nid); + DEBUG_LOG(Log::Loader, "Variable (%s,%08x) unresolved, storing for later resolving", var.moduleName, var.nid); } void ExportVarSymbol(const VarSymbolExport &var) { @@ -945,7 +945,7 @@ void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char // We used to report this, but I don't think it's very interesting anymore. WARN_LOG(Log::Loader, "Unknown syscall from known HLE module '%s': 0x%08x (import for '%s')", func.moduleName, func.nid, importingModule); } else { - INFO_LOG(Log::Loader, "Function (%s,%08x) unresolved in '%s', storing for later resolving", func.moduleName, func.nid, importingModule); + DEBUG_LOG(Log::Loader, "Function (%s,%08x) unresolved in '%s', storing for later resolving", func.moduleName, func.nid, importingModule); } if (shouldHLE || !reimporting) { diff --git a/Core/HLE/sceReg.cpp b/Core/HLE/sceReg.cpp index 415b13f5ba..cf559ea990 100644 --- a/Core/HLE/sceReg.cpp +++ b/Core/HLE/sceReg.cpp @@ -5,21 +5,54 @@ #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" #include "Core/Config.h" +#include "Core/ConfigSettings.h" #include "Core/System.h" #define SYSTEM_REGISTRY "/system" #define REG_KEYNAME_SIZE 27 -enum RegKeyTypes { - REG_TYPE_DIR = 1, - REG_TYPE_INT = 2, - REG_TYPE_STR = 3, - REG_TYPE_BIN = 4, -}; +// Mapping to PPSSPP settings +// 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 (-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.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 +// CONFIG/DISPLAY/screensaver_start_time <-> (unmapped, int seconds) +// CONFIG/SYSTEM/POWER_SAVING/backlight_off_interval <-> (unmapped, int seconds) +// CONFIG/SYSTEM/POWER_SAVING/suspend_interval_interval <-> (unmapped, int seconds) +// CONFIG/SYSTEM/POWER_SAVING/wlan_mode <-> (unmapped, bool, "WLAN power save") +// CONFIG/SYSTEM/SOUND/avls <-> (unmapped, bool) +// 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 <-> g_Config.iButtonPreference +// CONFIG/SYSTEM/XMB/theme_type <-> g_Config.iXmbThemeType +// CONFIG/SYSTEM/XMB/THEME/color_mode <-> g_Config.iXmbThemeColorMode +// CONFIG/SYSTEM/XMB/THEME/system_color <-> g_Config.iXmbThemeSystemColor +// CONFIG/SYSTEM/XMB/THEME/wallpaper_mode <-> g_Config.iXmbThemeWallpaperMode +// CONFIG/SYSTEM/XMB/THEME/custom_theme_mode <-> g_Config.iXmbThemeCustomThemeMode +// +// UMD video stuff +// CONFIG/VIDEO/appended_volume (unmapped, int, 0-2) << TODO +// CONFIG/VIDEO/menu_language (unmapped, 2 byte binary) << TODO +// CONFIG/VIDEO/sound_language (unmapped, 2 byte binary) << TODO +// CONFIG/VIDEO/subtitle_language (unmapped, 2 byte binary) << TODO +// CONFIG/VIDEO/lr_button_enable (unmapped, bool) << TODO +// CONFIG/VIDEO/list_play_mode (unmapped, bool) << TODO +// CONFIG/VIDEO/title_display_mode (int, 0-1) << TODO typedef unsigned int REGHANDLE; @@ -57,17 +90,21 @@ enum class ValueType { struct KeyValue { std::string name; ValueType type; + // Fixed values. 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. - bool (*boolSetting)() = nullptr; + // 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 @@ -664,7 +701,7 @@ static const KeyValue tree_CONFIG_PREMO[] = { { "custom_video_buffer1", ValueType::INT, "", (int)0x0 }, // decimal: 0 { "custom_video_buffer2", ValueType::INT, "", (int)0x0 }, // decimal: 0 { "setting_internet", ValueType::INT, "", (int)0x0 }, // decimal: 0 - { "button_assign", ValueType::INT, "", (int)0x0 }, // decimal: 0 + { "button_assign", ValueType::INT, "", (int)0x0 }, // decimal: 0 NOTE: This is not the "real" button_assign, this is something else. { "flags", ValueType::INT, "", (int)0x0 }, // decimal: 0 { "account_id", ValueType::BIN, "", 16 }, // (all zero) { "login_id", ValueType::STR, "", 1 }, @@ -725,17 +762,17 @@ static const KeyValue tree_CONFIG_ONESEG[] = { // Dump of /CONFIG/SYSTEM/XMB/THEME static const KeyValue tree_CONFIG_SYSTEM_XMB_THEME[] = { - { "color_mode", ValueType::INT, "", (int)0x0 }, // decimal: 0 - { "wallpaper_mode", ValueType::INT, "", (int)0x0 }, // decimal: 0 - { "system_color", ValueType::INT, "", (int)0x0 }, // decimal: 0 - { "custom_theme_mode", ValueType::INT, "", (int)0x0 }, // decimal: 0 + { "color_mode", ValueType::INT, "", (int)0x0, nullptr, &g_Config.iXmbThemeColorMode }, // decimal: 0 + { "wallpaper_mode", ValueType::INT, "", (int)0x0, nullptr, &g_Config.iXmbThemeWallpaperMode }, // decimal: 0 + { "system_color", ValueType::INT, "", (int)0x0, nullptr, &g_Config.iXmbThemeSystemColor }, // decimal: 0 + { "custom_theme_mode", ValueType::INT, "", (int)0x0, nullptr, &g_Config.iXmbThemeCustomThemeMode }, // decimal: 0 }; // 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 }, // decimal: 1 + { "theme_type", ValueType::INT, "", (int)0x0, nullptr, &g_Config.iXmbThemeType }, // decimal: 0 + { "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 }, }; @@ -760,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 }; @@ -772,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, &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 @@ -793,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 @@ -990,7 +1027,7 @@ static const KeyValue tree_REGISTRY[] = { // A real 6.6x PSP has 0x66 here, which is what this tree was dumped from - but the VSH treats a // category_version higher than the schema it knows as a corrupt registry and offers to reset your // settings instead of booting. So we go very low. - { "category_version", ValueType::INT, "", 1 }, + { "category_version", ValueType::INT, "", 1, nullptr, &g_Config.iSceRegCategoryVersion }, }; // There might be more categories. @@ -1056,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; } @@ -1100,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; } @@ -1143,6 +1210,21 @@ static const KeyValue *LookupCategory(std::string_view path, int *count) { return cur->keyvals.data(); } +static const KeyValue *LookupCategoryKey(const KeyValue *keyvals, const char *name, int count, int *index = nullptr) { + if (!keyvals || !count) { + return nullptr; + } + for (int i = 0; i < count; i++) { + if (equals(keyvals[i].name, name)) { + if (index) { + *index = i; + } + return &keyvals[i]; + } + }; + return nullptr; +} + void __RegDoState(PointerWrap &p) { auto s = p.Section("sceReg", 0, 4); if (!s) { @@ -1347,31 +1429,31 @@ int sceRegGetKeyInfo(int catHandle, const char *name, u32 outKeyHandleAddr, u32 return hleLogWarning(Log::sceReg, SCE_REG_ERROR_CATEGORY_NOT_FOUND); } - for (int i = 0; i < count; i++) { - if (equals(keyvals[i].name, name)) { - // Found it! - if (Memory::IsValid4AlignedAddress(outKeyHandleAddr)) { - // Let's just make the index the key handle. - Memory::WriteUnchecked_U32(i, outKeyHandleAddr); - } - if (Memory::IsValid4AlignedAddress(outTypeAddr)) { - // Let's just make the index the key handle. - Memory::WriteUnchecked_U32((int)keyvals[i].type, outTypeAddr); - } - int size = 0; - if (Memory::IsValid4AlignedAddress(outSizeAddr)) { - switch (keyvals[i].type) { - case ValueType::BIN: size = (int)keyvals[i].intValue; break; - case ValueType::STR: size = (int)keyvals[i].intValue; break; - case ValueType::DIR: size = 0; break; - case ValueType::INT: size = 4; break; - default: break; - } - // Let's just make the index the key handle. - Memory::WriteUnchecked_U32(size, outSizeAddr); - } - return hleLogDebug(Log::sceReg, 0, "handle: %d type: %d size: %d", i, (int)keyvals[i].type, size); + int index = 0; + const KeyValue *keyval = LookupCategoryKey(keyvals, name, count, &index); + if (keyval) { + // Found it! + if (Memory::IsValid4AlignedAddress(outKeyHandleAddr)) { + // Let's just make the index the key handle. + Memory::WriteUnchecked_U32(index, outKeyHandleAddr); } + if (Memory::IsValid4AlignedAddress(outTypeAddr)) { + // Let's just make the index the key handle. + Memory::WriteUnchecked_U32((int)keyval->type, outTypeAddr); + } + int size = 0; + if (Memory::IsValid4AlignedAddress(outSizeAddr)) { + switch (keyval->type) { + case ValueType::BIN: size = (int)keyval->intValue; break; + case ValueType::STR: size = (int)keyval->intValue; break; + case ValueType::DIR: size = 0; break; + case ValueType::INT: size = 4; break; + default: break; + } + // Let's just make the index the key handle. + Memory::WriteUnchecked_U32(size, outSizeAddr); + } + return hleLogDebug(Log::sceReg, 0, "type: %d size: %d", (int)keyval->type, size); } return hleLogWarning(Log::sceReg, -1, "key with name '%s' not found", name); @@ -1393,24 +1475,23 @@ int sceRegGetKeyInfoByName(int catHandle, const char *name, u32 typeAddr, u32 si return hleLogWarning(Log::sceReg, SCE_REG_ERROR_CATEGORY_NOT_FOUND); } - for (int i = 0; i < count; i++) { - if (equals(keyvals[i].name, name)) { - int size = 0; - if (Memory::IsValid4AlignedAddress(typeAddr)) { - Memory::WriteUnchecked_U32((int)keyvals[i].type, typeAddr); - } - if (Memory::IsValid4AlignedAddress(sizeAddr)) { - switch (keyvals[i].type) { - case ValueType::BIN: size = (int)keyvals[i].intValue; break; - case ValueType::STR: size = (int)keyvals[i].intValue; break; - case ValueType::DIR: size = 0; break; - case ValueType::INT: size = 4; break; - default: break; - } - Memory::WriteUnchecked_U32(size, sizeAddr); - } - return hleLogDebug(Log::sceReg, 0, "type: %d size: %d", (int)keyvals[i].type, size); + const KeyValue *keyval = LookupCategoryKey(keyvals, name, count); + if (keyval) { + int size = 0; + if (Memory::IsValid4AlignedAddress(typeAddr)) { + Memory::WriteUnchecked_U32((int)keyval->type, typeAddr); } + if (Memory::IsValid4AlignedAddress(sizeAddr)) { + switch (keyval->type) { + case ValueType::BIN: size = (int)keyval->intValue; break; + case ValueType::STR: size = (int)keyval->intValue; break; + case ValueType::DIR: size = 0; break; + case ValueType::INT: size = 4; break; + default: break; + } + Memory::WriteUnchecked_U32(size, sizeAddr); + } + return hleLogDebug(Log::sceReg, 0, "type: %d size: %d", (int)keyval->type, size); } return hleLogWarning(Log::sceReg, -1, "key with name '%s' not found", name); @@ -1421,7 +1502,7 @@ int sceRegGetKeyValue(int catHandle, int keyHandle, u32 bufAddr, u32 size) { return -1; } - auto iter = g_openCategories.find(catHandle); + const auto iter = g_openCategories.find(catHandle); if (iter == g_openCategories.end()) { return hleLogError(Log::sceReg, 0, "Not found"); } @@ -1440,14 +1521,14 @@ int sceRegGetKeyValue(int catHandle, int keyHandle, u32 bufAddr, u32 size) { switch (keyval.type) { case ValueType::BIN: Memory::MemcpyUnchecked(bufAddr, keyval.strValue, std::min(size, (u32)keyval.intValue)); - return hleLogDebug(Log::sceReg, 0); + return hleLogDebug(Log::sceReg, 0, "%s/%s: value: ", iter->second.path.c_str(), keyval.name.c_str()); case ValueType::STR: Memory::MemcpyUnchecked(bufAddr, keyval.strValue, std::min(size, (u32)keyval.intValue)); - return hleLogDebug(Log::sceReg, 0, "value: '%s'", keyval.strValue); + return hleLogDebug(Log::sceReg, 0, "%s/%s: value: '%s'", iter->second.path.c_str(), keyval.name.c_str(), keyval.strValue); case ValueType::INT: if (size >= sizeof(u32)) Memory::WriteUnchecked_U32(keyval.intValue, bufAddr); - return hleLogDebug(Log::sceReg, 0, "value: %d (0x%08x)", keyval.intValue, keyval.intValue); + return hleLogInfo(Log::sceReg, 0, "%s/%s: value: %d (0x%08x)", iter->second.path.c_str(), keyval.name.c_str(), keyval.intValue, keyval.intValue); case ValueType::DIR: case ValueType::FAIL: default: @@ -1456,6 +1537,7 @@ int sceRegGetKeyValue(int catHandle, int keyHandle, u32 bufAddr, u32 size) { } } +// TODO: This one seems to ignore overlays? int sceRegGetKeyValueByName(int catHandle, const char *name, u32 bufAddr, u32 size) { if (!name) { return hleLogError(Log::sceReg, -1, "Invalid name pointer"); @@ -1482,14 +1564,17 @@ int sceRegGetKeyValueByName(int catHandle, const char *name, u32 bufAddr, u32 si const KeyValue &keyval = keyvals[i]; switch (keyval.type) { case ValueType::BIN: - Memory::MemcpyUnchecked(bufAddr, keyval.strValue, std::min(size, (u32)keyval.intValue)); - return hleLogDebug(Log::sceReg, 0); case ValueType::STR: Memory::MemcpyUnchecked(bufAddr, keyval.strValue, std::min(size, (u32)keyval.intValue)); - return hleLogDebug(Log::sceReg, 0, "value: '%s'", keyval.strValue); + if (keyval.type == ValueType::BIN) { + return hleLogDebug(Log::sceReg, 0, "value: "); + } else { + return hleLogDebug(Log::sceReg, 0, "value: '%s'", keyval.strValue); + } case ValueType::INT: - if (size >= sizeof(u32)) + if (size >= sizeof(u32)) { Memory::WriteUnchecked_U32(keyval.intValue, bufAddr); + } return hleLogDebug(Log::sceReg, 0, "value: %d (0x%08x)", keyval.intValue, keyval.intValue); case ValueType::DIR: case ValueType::FAIL: @@ -1520,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; } } @@ -1552,6 +1641,13 @@ 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); + if (configRef) { + configRef->SetIntValue(value.intValue); + } + + g_overlay[path][name] = value; + InvalidateMergedCache(); + 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); value.str.assign(str, size); @@ -1560,13 +1656,25 @@ 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) { + return hleLogInfo(Log::sceReg, 0, "set %s/%s to '%.*s'", path.c_str(), name, (int)value.str.size(), value.str.c_str()); + } else { + if (value.str.size() == 2) { + // This is probably one of the languages. + return hleLogInfo(Log::sceReg, 0, "set %s/%s to binary data of size 2: '%c%c'", path.c_str(), name, value.str[0], value.str[1]); + } else { + return hleLogInfo(Log::sceReg, 0, "set %s/%s to binary data of size %d", path.c_str(), name, (int)value.str.size()); + } + } } else { return hleLogError(Log::sceReg, -1, "bad input addr"); } - - g_overlay[path][name] = value; - InvalidateMergedCache(); - return hleLogDebug(Log::sceReg, 0, "set %s/%s", path.c_str(), name); } int sceRegCreateKey(int catHandle, const char *name, int type, u32 size) { @@ -1580,19 +1688,15 @@ int sceRegCreateKey(int catHandle, const char *name, int type, u32 size) { } OverlayValue value; - switch (type) { - case REG_TYPE_DIR: - value.isDir = true; + switch ((ValueType)type) { + case ValueType::DIR: value.type = ValueType::DIR; + value.isDir = true; break; - case REG_TYPE_INT: - value.type = ValueType::INT; - break; - case REG_TYPE_STR: - value.type = ValueType::STR; - break; - case REG_TYPE_BIN: - value.type = ValueType::BIN; + case ValueType::INT: + case ValueType::STR: + case ValueType::BIN: + value.type = (ValueType)type; break; default: return hleLogError(Log::sceReg, -1, "bad type %d", type); @@ -1612,8 +1716,10 @@ int sceRegCreateKey(int catHandle, const char *name, int type, u32 size) { g_overlay[path][name] = value; InvalidateMergedCache(); - return hleLogDebug(Log::sceReg, 0, "create %s/%s (type %d)", path.c_str(), name, type); + + return hleLogInfo(Log::sceReg, 0, "create %s/%s (type %d)", path.c_str(), name, type); } + // Speculated signature int sceRegRemoveKey(int catHandle, int key) { return hleLogError(Log::sceReg, 0); diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index 9a080eaf64..e5f07e84d2 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -1577,7 +1577,7 @@ static u32 sceUtilityGetSystemParamInt(u32 id, u32 destaddr) { param = PSP_SYSTEMPARAM_TIME_FORMAT_24HR; break; case PSP_SYSTEMPARAM_ID_INT_TIMEZONE: - param = g_Config.iTimeZone; + param = g_Config.iTimeZoneOffset; break; case PSP_SYSTEMPARAM_ID_INT_DAYLIGHTSAVINGS: param = g_Config.bDayLightSavings?PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_SAVING:PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_STD; diff --git a/Core/Util/PSARUnpack.cpp b/Core/Util/PSARUnpack.cpp index 61d009f781..320bcc4cf3 100644 --- a/Core/Util/PSARUnpack.cpp +++ b/Core/Util/PSARUnpack.cpp @@ -1337,9 +1337,39 @@ bool EraseInstalledFirmware(const Path &nandRoot, std::string *error) { success = false; } } + + // Also reset the category version. + g_Config.iSceRegCategoryVersion = 1; return success; } +int SceRegCategoryVersionForFirmware(std::string_view version) { + // The default of /REGISTRY/category_version in each firmware's registry schema, a table of + // { name, type, size, default } entries in vsh/module/vshmain.prx (common_util.prx before 2.00). + // When the stored version is below this, the shell writes the default of every key added since + // that version (from 1, that includes umd_autoboot). One above it means a corrupt registry. + static const struct { + int firmware; // The first firmware with this version, as FirmwareVersionToInt() gives it. + int categoryVersion; + } versions[] = { + { 100, 0x00 }, { 150, 0x01 }, { 200, 0x06 }, { 250, 0x13 }, { 260, 0x14 }, { 270, 0x1b }, + { 280, 0x20 }, { 300, 0x26 }, { 310, 0x2c }, { 330, 0x31 }, { 350, 0x35 }, { 360, 0x36 }, + { 370, 0x3b }, { 380, 0x3e }, { 395, 0x40 }, { 405, 0x43 }, { 420, 0x46 }, { 500, 0x4c }, + { 550, 0x58 }, { 570, 0x61 }, { 600, 0x63 }, { 620, 0x64 }, { 630, 0x66 }, + }; + const int fw = FirmwareVersionToInt(version); + if (fw < versions[0].firmware) { + return 1; + } + int categoryVersion = versions[0].categoryVersion; + for (const auto &v : versions) { + if (fw >= v.firmware) { + categoryVersion = v.categoryVersion; + } + } + return categoryVersion; +} + int FirmwareVersionToInt(std::string_view version) { const size_t dot = version.find('.'); if (dot == std::string_view::npos || dot == 0 || dot + 1 >= version.size()) { @@ -1671,6 +1701,11 @@ bool InstallFirmware(const Path &updater, const Path &nandRoot, const PSARUnpack if (ok) { ok = SwapStagingIntoPlace(nandRoot, staging, error); } + if (ok) { + // Match the registry to the new firmware's schema, so its shell doesn't reset the + // settings (which sceReg maps to our config) to its defaults at first boot. + g_Config.iSceRegCategoryVersion = SceRegCategoryVersionForFirmware(stats->firmwareVersion); + } // Whether it worked or not, don't leave the staging copy behind. if (File::Exists(staging) && !File::DeleteDirRecursively(staging)) { diff --git a/Core/Util/PSARUnpack.h b/Core/Util/PSARUnpack.h index 4663fb71b8..ab9abe471d 100644 --- a/Core/Util/PSARUnpack.h +++ b/Core/Util/PSARUnpack.h @@ -181,6 +181,9 @@ bool EraseInstalledFirmware(const Path &nandRoot, std::string *error); // makes the result safe to compare with: an unknown version is older than every real one. int FirmwareVersionToInt(std::string_view version); +// The /REGISTRY/category_version the shell of this firmware expects. 1 for an unknown version. +int SceRegCategoryVersionForFirmware(std::string_view version); + // The firmware versions we can actually boot the VSH (XMB) on. Every other version loads, but // the module patches it needs are version-specific, so it won't get anywhere. bool FirmwareVersionSupportsVSH(std::string_view version); diff --git a/UI/GameButton.cpp b/UI/GameButton.cpp index 452bcd2e5f..567ad237af 100644 --- a/UI/GameButton.cpp +++ b/UI/GameButton.cpp @@ -204,7 +204,6 @@ void GameButton::Draw(UIContext &dc) { } } - u32 color = 0, shadowColor = 0; using namespace UI; UI::Style style = dc.GetTheme().itemStyle; @@ -232,71 +231,79 @@ void GameButton::Draw(UIContext &dc) { } const float textX = ListTextX(viewMode_); - if (texture && ginfo) { - color = whiteAlpha(ease((time_now_d() - ginfo->icon.timeLoaded) * 2)); - shadowColor = blackAlpha(ease((time_now_d() - ginfo->icon.timeLoaded) * 2)); - float tw = texture->Width(); - float th = texture->Height(); - - // Adjust position so we don't stretch the image vertically or horizontally. - // Make sure it's not wider than 144 (like Doom Legacy homebrew), ugly in the grid mode. - float nw = std::min(iconBounds.h * tw / th, (float)iconBounds.w); - iconBounds.x += (iconBounds.w - nw) / 2.0f; - iconBounds.w = nw; - } - - int txOffset = down_ ? 4 : 0; - if (viewMode_ != GameBrowserViewMode::GRID) { - txOffset = 0; + int iconYOffset = 0; + if (viewMode_ == GameBrowserViewMode::GRID && down_) { + iconYOffset = 4; } Bounds overlayBounds = bounds_; // Render the icon, all modes have this. - int dropsize = 10; if (texture) { + float alpha = 1.0f; + float dropsize = 10; + + if (ginfo) { + float loadPhase = ease((time_now_d() - ginfo->icon.timeLoaded) * 2); + + loadPhase = 0.0f; + + alpha = loadPhase; + + float tw = texture->Width(); + float th = texture->Height(); + // Adjust position so we don't stretch the image vertically or horizontally. + // Make sure it's not wider than 144 (like Doom Legacy homebrew), ugly in the grid mode. + float nw = std::min(iconBounds.h * tw / th, (float)iconBounds.w); + iconBounds.x += (iconBounds.w - nw) / 2.0f; + iconBounds.w = nw; + } + if (viewMode_ == GameBrowserViewMode::LIST) { iconBounds.x += 4; } - if (txOffset) { + + if (iconYOffset) { + // Button pressed, move the icon down a bit and reduce the shadow. dropsize = 3; - iconBounds.y += txOffset * 2; - overlayBounds.y += txOffset * 2; + iconBounds.y += iconYOffset * 2; + overlayBounds.y += iconYOffset * 2; } + if (HasFocus()) { dc.Draw()->Flush(); dc.RebindTexture(); float pulse = sin(time_now_d() * 7.0) * 0.25 + 0.8; - dc.DrawRectDropShadow(iconBounds, 10.0f, pulse, alphaMul(color, pulse)); + dc.DrawRectDropShadow(iconBounds, 10.0f, pulse, style.fgColor); dc.Draw()->Flush(); } else { dc.Draw()->Flush(); dc.RebindTexture(); - dc.DrawRectDropShadow(iconBounds, 8.0f, 0.7f, shadowColor); - // dc.Draw()->DrawImage4Grid(dc.GetTheme().dropShadow4Grid, x - dropsize, y - dropsize * 0.5f, x + w + dropsize, y + h + dropsize * 1.5, alphaMul(shadowColor, 0.5f), 1.0f); + dc.DrawRectDropShadow(iconBounds, 8.0f, 0.7f, 0); dc.Draw()->Flush(); } dc.Draw()->Flush(); dc.GetDrawContext()->BindTexture(0, texture); - dc.Draw()->DrawTexRect(iconBounds, 0, 0, 1, 1, color); + dc.Draw()->DrawTexRect(iconBounds, 0, 0, 1, 1, 0xFFFFFFFF); dc.Draw()->Flush(); } - if (viewMode_ == GameBrowserViewMode::GRID && g_Config.bShowIDOnGameIcon && fileType != IdentifiedFileType::PSP_ELF && !id_version.empty()) { - std::string_view idStr = id_version; - if (fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) { - auto ga = GetI18NCategory(I18NCat::GAME); - idStr = ga->T("SaveData"); - } - dc.SetFontScale(0.5f * g_Config.fGameGridScale, 0.5f * g_Config.fGameGridScale); - dc.DrawText(idStr, bounds_.x + 5, iconBounds.y + 1, 0xFF000000, ALIGN_TOPLEFT); - dc.DrawText(idStr, bounds_.x + 4, iconBounds.y, dc.GetTheme().infoStyle.fgColor, ALIGN_TOPLEFT); - dc.SetFontScale(1.0f, 1.0f); - } if (viewMode_ == GameBrowserViewMode::GRID) { + if (g_Config.bShowIDOnGameIcon && fileType != IdentifiedFileType::PSP_ELF && !id_version.empty()) { + std::string_view idStr = id_version; + if (fileType == IdentifiedFileType::PSP_SAVEDATA_DIRECTORY) { + auto ga = GetI18NCategory(I18NCat::GAME); + idStr = ga->T("SaveData"); + } + dc.SetFontScale(0.5f * g_Config.fGameGridScale, 0.5f * g_Config.fGameGridScale); + dc.DrawText(idStr, bounds_.x + 5, iconBounds.y + 1, 0xFF000000, ALIGN_TOPLEFT); + dc.DrawText(idStr, bounds_.x + 4, iconBounds.y, dc.GetTheme().infoStyle.fgColor, ALIGN_TOPLEFT); + dc.SetFontScale(1.0f, 1.0f); + } + if (imageIcon.isValid()) { Bounds bounds = bounds_; diff --git a/headless/Headless.cpp b/headless/Headless.cpp index f4d4b1f4b6..7847602ca9 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -941,6 +941,8 @@ int main(int argc, const char* argv[]) { #endif PROFILE_INIT(); TimeInit(); + // Builds the pointer to setting lookup, which sceReg's registry mapping uses. + g_Config.Init(); #if PPSSPP_PLATFORM(WINDOWS) if (!IsDebuggerPresent()) { SetCleanExitOnAssert(); @@ -1180,7 +1182,7 @@ int main(int argc, const char* argv[]) { g_Config.iTimeFormat = PSP_SYSTEMPARAM_TIME_FORMAT_24HR; g_Config.bEncryptSave = true; g_Config.sNickName = "shadow"; - g_Config.iTimeZone = 60; + g_Config.iTimeZoneOffset = 60; g_Config.iDateFormat = PSP_SYSTEMPARAM_DATE_FORMAT_DDMMYYYY; g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CROSS; g_Config.iLockParentalLevel = 9; @@ -1445,6 +1447,14 @@ int main(int argc, const char* argv[]) { coreParameter.nandRoot = nand; } + // Headless doesn't keep a config, so set the registry version that installing this firmware + // in the app would have stored. + { + InstalledFirmwareInfo firmwareInfo; + ReadInstalledFirmwareInfo(g_Config.nandRootDirectory, &firmwareInfo, false); + g_Config.iSceRegCategoryVersion = SceRegCategoryVersionForFirmware(firmwareInfo.version); + } + // Placed here rather than with the other early-exit subcommands above, because resolving a // "flash0:/kd/foo.prx" module path needs nandRootDirectory, which is only settled just above. if (cmdLineOptions.reDecrypt.has_value()) { diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index b6f3fa5c76..95052bd89f 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -1225,6 +1225,8 @@ void retro_init(void) if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL)) libretro_supports_bitmasks = true; + // Builds the pointer to setting lookup, which sceReg's registry mapping uses. + g_Config.Init(); g_Config.Load("", ""); g_Config.iInternalResolution = 0;