Core: Update config files on startup (#3181)

* Organize settings and fix defaults

setDefaultValues was missing several rather important config options, and some of the defaults were inaccurate.

* Set alternative settings based on defaults

Reduces how many times we redefine the defaults for each setting.
I avoided changing behavior for installDirs and installDirsEnabled because I don't want to introduce any changes I can't easily test.

* Run save after loading to fill in missing config entries

A fix for the growing complaints about config files not updating when new settings are added.
Thanks to the prior changes, default settings are new properly defined everywhere, so running save here will properly fill in missing values with their expected defaults instead of the weird settings load would sometimes use.

* Clang

* Only update config when necessary

Instead of updating the config on each emulator boot, calculate the number of entries stored in the config and compare to a hardcoded constant.
If the config doesn't have the right number of entries, then regenerate it.

* Clang

* Clang
This commit is contained in:
Stephen Miller 2025-07-06 03:30:10 -05:00 committed by GitHub
parent 4f99f304e6
commit a63db68114
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -31,31 +31,50 @@ std::filesystem::path find_fs_path_or(const basic_value<TC>& v, const K& ky,
namespace Config { namespace Config {
// General
static bool isNeo = false; static bool isNeo = false;
static bool isDevKit = false; static bool isDevKit = false;
static bool isPSNSignedIn = false;
static bool isTrophyPopupDisabled = false; static bool isTrophyPopupDisabled = false;
static double trophyNotificationDuration = 6.0;
static bool enableDiscordRPC = false; static bool enableDiscordRPC = false;
static u32 screenWidth = 1280; static std::string logFilter = "";
static u32 screenHeight = 720;
static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
static std::string logFilter;
static std::string logType = "sync"; static std::string logType = "sync";
static std::string userName = "shadPS4"; static std::string userName = "shadPS4";
static std::string chooseHomeTab; static std::string chooseHomeTab = "General";
static bool isShowSplash = false;
static std::string isSideTrophy = "right";
static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false;
// Input
static int cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default)
static bool useSpecialPad = false; static bool useSpecialPad = false;
static int specialPadClass = 1; static int specialPadClass = 1;
static bool isMotionControlsEnabled = true; static bool isMotionControlsEnabled = true;
static bool isDebugDump = false; static bool useUnifiedInputConfig = true;
static bool isShaderDebug = false;
static bool isShowSplash = false; // These two entries aren't stored in the config
static std::string isSideTrophy = "right"; static bool overrideControllerColor = false;
static int controllerCustomColorRGB[3] = {0, 0, 255};
// GPU
static u32 screenWidth = 1280;
static u32 screenHeight = 720;
static bool isNullGpu = false; static bool isNullGpu = false;
static bool shouldCopyGPUBuffers = false; static bool shouldCopyGPUBuffers = false;
static bool readbacksEnabled = false; static bool readbacksEnabled = false;
static bool directMemoryAccessEnabled = false; static bool directMemoryAccessEnabled = false;
static bool shouldDumpShaders = false; static bool shouldDumpShaders = false;
static bool shouldPatchShaders = true; static bool shouldPatchShaders = false;
static u32 vblankDivider = 1; static u32 vblankDivider = 1;
static bool isFullscreen = false;
static std::string fullscreenMode = "Windowed";
static bool isHDRAllowed = false;
// Vulkan
static s32 gpuId = -1;
static bool vkValidation = false; static bool vkValidation = false;
static bool vkValidationSync = false; static bool vkValidationSync = false;
static bool vkValidationGpu = false; static bool vkValidationGpu = false;
@ -63,32 +82,29 @@ static bool vkCrashDiagnostic = false;
static bool vkHostMarkers = false; static bool vkHostMarkers = false;
static bool vkGuestMarkers = false; static bool vkGuestMarkers = false;
static bool rdocEnable = false; static bool rdocEnable = false;
static bool isFpsColor = true;
static bool isSeparateLogFilesEnabled = false;
static int cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default)
static double trophyNotificationDuration = 6.0;
static bool useUnifiedInputConfig = true;
static bool overrideControllerColor = false;
static int controllerCustomColorRGB[3] = {0, 0, 255};
static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false;
static std::string trophyKey;
static bool isPSNSignedIn = false;
// Gui // Debug
static bool isDebugDump = false;
static bool isShaderDebug = false;
static bool isSeparateLogFilesEnabled = false;
static bool isFpsColor = true;
// GUI
static bool load_game_size = true; static bool load_game_size = true;
static std::vector<GameInstallDir> settings_install_dirs = {}; static std::vector<GameInstallDir> settings_install_dirs = {};
std::vector<bool> install_dirs_enabled = {}; std::vector<bool> install_dirs_enabled = {};
std::filesystem::path settings_addon_install_dir = {}; std::filesystem::path settings_addon_install_dir = {};
std::filesystem::path save_data_path = {}; std::filesystem::path save_data_path = {};
static bool isFullscreen = false;
static std::string fullscreenMode = "Windowed";
static bool isHDRAllowed = false;
// Language // Settings
u32 m_language = 1; // english u32 m_language = 1; // english
// Keys
static std::string trophyKey = "";
// Expected number of items in the config file
static constexpr u64 total_entries = 51;
bool allowHDR() { bool allowHDR() {
return isHDRAllowed; return isHDRAllowed;
} }
@ -565,36 +581,46 @@ void load(const std::filesystem::path& path) {
fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what()); fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what());
return; return;
} }
u64 entry_count = 0;
if (data.contains("General")) { if (data.contains("General")) {
const toml::value& general = data.at("General"); const toml::value& general = data.at("General");
isNeo = toml::find_or<bool>(general, "isPS4Pro", false); isNeo = toml::find_or<bool>(general, "isPS4Pro", isNeo);
isDevKit = toml::find_or<bool>(general, "isDevKit", false); isDevKit = toml::find_or<bool>(general, "isDevKit", isDevKit);
isPSNSignedIn = toml::find_or<bool>(general, "isPSNSignedIn", false); isPSNSignedIn = toml::find_or<bool>(general, "isPSNSignedIn", isPSNSignedIn);
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false); isTrophyPopupDisabled =
trophyNotificationDuration = toml::find_or<bool>(general, "isTrophyPopupDisabled", isTrophyPopupDisabled);
toml::find_or<double>(general, "trophyNotificationDuration", 5.0); trophyNotificationDuration = toml::find_or<double>(general, "trophyNotificationDuration",
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true); trophyNotificationDuration);
logFilter = toml::find_or<std::string>(general, "logFilter", ""); enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", enableDiscordRPC);
logType = toml::find_or<std::string>(general, "logType", "sync"); logFilter = toml::find_or<std::string>(general, "logFilter", logFilter);
userName = toml::find_or<std::string>(general, "userName", "shadPS4"); logType = toml::find_or<std::string>(general, "logType", logType);
isShowSplash = toml::find_or<bool>(general, "showSplash", true); userName = toml::find_or<std::string>(general, "userName", userName);
isSideTrophy = toml::find_or<std::string>(general, "sideTrophy", "right"); isShowSplash = toml::find_or<bool>(general, "showSplash", isShowSplash);
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false); isSideTrophy = toml::find_or<std::string>(general, "sideTrophy", isSideTrophy);
checkCompatibilityOnStartup = compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", compatibilityData);
toml::find_or<bool>(general, "checkCompatibilityOnStartup", false); checkCompatibilityOnStartup = toml::find_or<bool>(general, "checkCompatibilityOnStartup",
chooseHomeTab = toml::find_or<std::string>(general, "chooseHomeTab", "Release"); checkCompatibilityOnStartup);
chooseHomeTab = toml::find_or<std::string>(general, "chooseHomeTab", chooseHomeTab);
entry_count += general.size();
} }
if (data.contains("Input")) { if (data.contains("Input")) {
const toml::value& input = data.at("Input"); const toml::value& input = data.at("Input");
cursorState = toml::find_or<int>(input, "cursorState", HideCursorState::Idle); cursorState = toml::find_or<int>(input, "cursorState", cursorState);
cursorHideTimeout = toml::find_or<int>(input, "cursorHideTimeout", 5); cursorHideTimeout = toml::find_or<int>(input, "cursorHideTimeout", cursorHideTimeout);
useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", false); useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", useSpecialPad);
specialPadClass = toml::find_or<int>(input, "specialPadClass", 1); specialPadClass = toml::find_or<int>(input, "specialPadClass", specialPadClass);
isMotionControlsEnabled = toml::find_or<bool>(input, "isMotionControlsEnabled", true); isMotionControlsEnabled =
useUnifiedInputConfig = toml::find_or<bool>(input, "useUnifiedInputConfig", true); toml::find_or<bool>(input, "isMotionControlsEnabled", isMotionControlsEnabled);
useUnifiedInputConfig =
toml::find_or<bool>(input, "useUnifiedInputConfig", useUnifiedInputConfig);
entry_count += input.size();
} }
if (data.contains("GPU")) { if (data.contains("GPU")) {
@ -602,44 +628,52 @@ void load(const std::filesystem::path& path) {
screenWidth = toml::find_or<int>(gpu, "screenWidth", screenWidth); screenWidth = toml::find_or<int>(gpu, "screenWidth", screenWidth);
screenHeight = toml::find_or<int>(gpu, "screenHeight", screenHeight); screenHeight = toml::find_or<int>(gpu, "screenHeight", screenHeight);
isNullGpu = toml::find_or<bool>(gpu, "nullGpu", false); isNullGpu = toml::find_or<bool>(gpu, "nullGpu", isNullGpu);
shouldCopyGPUBuffers = toml::find_or<bool>(gpu, "copyGPUBuffers", false); shouldCopyGPUBuffers = toml::find_or<bool>(gpu, "copyGPUBuffers", shouldCopyGPUBuffers);
readbacksEnabled = toml::find_or<bool>(gpu, "readbacks", false); readbacksEnabled = toml::find_or<bool>(gpu, "readbacks", readbacksEnabled);
directMemoryAccessEnabled = toml::find_or<bool>(gpu, "directMemoryAccess", false); directMemoryAccessEnabled =
shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", false); toml::find_or<bool>(gpu, "directMemoryAccess", directMemoryAccessEnabled);
shouldPatchShaders = toml::find_or<bool>(gpu, "patchShaders", true); shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", shouldDumpShaders);
vblankDivider = toml::find_or<int>(gpu, "vblankDivider", 1); shouldPatchShaders = toml::find_or<bool>(gpu, "patchShaders", shouldPatchShaders);
isFullscreen = toml::find_or<bool>(gpu, "Fullscreen", false); vblankDivider = toml::find_or<int>(gpu, "vblankDivider", vblankDivider);
fullscreenMode = toml::find_or<std::string>(gpu, "FullscreenMode", "Windowed"); isFullscreen = toml::find_or<bool>(gpu, "Fullscreen", isFullscreen);
isHDRAllowed = toml::find_or<bool>(gpu, "allowHDR", false); fullscreenMode = toml::find_or<std::string>(gpu, "FullscreenMode", fullscreenMode);
isHDRAllowed = toml::find_or<bool>(gpu, "allowHDR", isHDRAllowed);
entry_count += gpu.size();
} }
if (data.contains("Vulkan")) { if (data.contains("Vulkan")) {
const toml::value& vk = data.at("Vulkan"); const toml::value& vk = data.at("Vulkan");
gpuId = toml::find_or<int>(vk, "gpuId", -1); gpuId = toml::find_or<int>(vk, "gpuId", gpuId);
vkValidation = toml::find_or<bool>(vk, "validation", false); vkValidation = toml::find_or<bool>(vk, "validation", vkValidation);
vkValidationSync = toml::find_or<bool>(vk, "validation_sync", false); vkValidationSync = toml::find_or<bool>(vk, "validation_sync", vkValidationSync);
vkValidationGpu = toml::find_or<bool>(vk, "validation_gpu", true); vkValidationGpu = toml::find_or<bool>(vk, "validation_gpu", vkValidationGpu);
vkCrashDiagnostic = toml::find_or<bool>(vk, "crashDiagnostic", false); vkCrashDiagnostic = toml::find_or<bool>(vk, "crashDiagnostic", vkCrashDiagnostic);
vkHostMarkers = toml::find_or<bool>(vk, "hostMarkers", false); vkHostMarkers = toml::find_or<bool>(vk, "hostMarkers", vkHostMarkers);
vkGuestMarkers = toml::find_or<bool>(vk, "guestMarkers", false); vkGuestMarkers = toml::find_or<bool>(vk, "guestMarkers", vkGuestMarkers);
rdocEnable = toml::find_or<bool>(vk, "rdocEnable", false); rdocEnable = toml::find_or<bool>(vk, "rdocEnable", rdocEnable);
entry_count += vk.size();
} }
if (data.contains("Debug")) { if (data.contains("Debug")) {
const toml::value& debug = data.at("Debug"); const toml::value& debug = data.at("Debug");
isDebugDump = toml::find_or<bool>(debug, "DebugDump", false); isDebugDump = toml::find_or<bool>(debug, "DebugDump", isDebugDump);
isSeparateLogFilesEnabled = toml::find_or<bool>(debug, "isSeparateLogFilesEnabled", false); isSeparateLogFilesEnabled =
isShaderDebug = toml::find_or<bool>(debug, "CollectShader", false); toml::find_or<bool>(debug, "isSeparateLogFilesEnabled", isSeparateLogFilesEnabled);
isFpsColor = toml::find_or<bool>(debug, "FPSColor", true); isShaderDebug = toml::find_or<bool>(debug, "CollectShader", isShaderDebug);
isFpsColor = toml::find_or<bool>(debug, "FPSColor", isFpsColor);
entry_count += debug.size();
} }
if (data.contains("GUI")) { if (data.contains("GUI")) {
const toml::value& gui = data.at("GUI"); const toml::value& gui = data.at("GUI");
load_game_size = toml::find_or<bool>(gui, "loadGameSizeEnabled", true); load_game_size = toml::find_or<bool>(gui, "loadGameSizeEnabled", load_game_size);
const auto install_dir_array = const auto install_dir_array =
toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {}); toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {});
@ -661,20 +695,32 @@ void load(const std::filesystem::path& path) {
{std::filesystem::path{install_dir_array[i]}, install_dirs_enabled[i]}); {std::filesystem::path{install_dir_array[i]}, install_dirs_enabled[i]});
} }
save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {}); save_data_path = toml::find_fs_path_or(gui, "saveDataPath", save_data_path);
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); settings_addon_install_dir =
toml::find_fs_path_or(gui, "addonInstallDir", settings_addon_install_dir);
entry_count += gui.size();
} }
if (data.contains("Settings")) { if (data.contains("Settings")) {
const toml::value& settings = data.at("Settings"); const toml::value& settings = data.at("Settings");
m_language = toml::find_or<int>(settings, "consoleLanguage", m_language);
m_language = toml::find_or<int>(settings, "consoleLanguage", 1); entry_count += settings.size();
} }
if (data.contains("Keys")) { if (data.contains("Keys")) {
const toml::value& keys = data.at("Keys"); const toml::value& keys = data.at("Keys");
trophyKey = toml::find_or<std::string>(keys, "TrophyKey", ""); trophyKey = toml::find_or<std::string>(keys, "TrophyKey", trophyKey);
entry_count += keys.size();
}
// Run save after loading to generate any missing fields with default values.
if (entry_count != total_entries) {
fmt::print("Outdated config detected, updating config file.\n");
save(path);
} }
} }
@ -822,32 +868,50 @@ void save(const std::filesystem::path& path) {
} }
void setDefaultValues() { void setDefaultValues() {
isHDRAllowed = false; // General
isNeo = false; isNeo = false;
isDevKit = false; isDevKit = false;
isPSNSignedIn = false; isPSNSignedIn = false;
isFullscreen = false;
isTrophyPopupDisabled = false; isTrophyPopupDisabled = false;
enableDiscordRPC = true; trophyNotificationDuration = 6.0;
screenWidth = 1280; enableDiscordRPC = false;
screenHeight = 720;
logFilter = ""; logFilter = "";
logType = "sync"; logType = "sync";
userName = "shadPS4"; userName = "shadPS4";
chooseHomeTab = "General"; chooseHomeTab = "General";
cursorState = HideCursorState::Idle;
cursorHideTimeout = 5;
trophyNotificationDuration = 6.0;
useSpecialPad = false;
specialPadClass = 1;
isDebugDump = false;
isShaderDebug = false;
isShowSplash = false; isShowSplash = false;
isSideTrophy = "right"; isSideTrophy = "right";
compatibilityData = false;
checkCompatibilityOnStartup = false;
// Input
cursorState = HideCursorState::Idle;
cursorHideTimeout = 5;
useSpecialPad = false;
specialPadClass = 1;
isMotionControlsEnabled = true;
useUnifiedInputConfig = true;
overrideControllerColor = false;
controllerCustomColorRGB[0] = 0;
controllerCustomColorRGB[1] = 0;
controllerCustomColorRGB[2] = 255;
// GPU
screenWidth = 1280;
screenHeight = 720;
isNullGpu = false; isNullGpu = false;
shouldCopyGPUBuffers = false;
readbacksEnabled = false;
directMemoryAccessEnabled = false;
shouldDumpShaders = false; shouldDumpShaders = false;
shouldPatchShaders = false;
vblankDivider = 1; vblankDivider = 1;
isFullscreen = false;
fullscreenMode = "Windowed";
isHDRAllowed = false;
// Vulkan
gpuId = -1;
vkValidation = false; vkValidation = false;
vkValidationSync = false; vkValidationSync = false;
vkValidationGpu = false; vkValidationGpu = false;
@ -855,10 +919,18 @@ void setDefaultValues() {
vkHostMarkers = false; vkHostMarkers = false;
vkGuestMarkers = false; vkGuestMarkers = false;
rdocEnable = false; rdocEnable = false;
// Debug
isDebugDump = false;
isShaderDebug = false;
isSeparateLogFilesEnabled = false;
isFpsColor = true;
// GUI
load_game_size = true;
// Settings
m_language = 1; m_language = 1;
gpuId = -1;
compatibilityData = false;
checkCompatibilityOnStartup = false;
} }
constexpr std::string_view GetDefaultKeyboardConfig() { constexpr std::string_view GetDefaultKeyboardConfig() {