diff --git a/REUSE.toml b/REUSE.toml index dc593e9a8..a94827b3b 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -74,6 +74,7 @@ path = [ "src/images/youtube.svg", "src/images/trophy.wav", "src/images/hotkey.png", + "src/images/game_settings.png", "src/shadps4.qrc", "src/shadps4.rc", "src/qt_gui/translations/update_translation.sh", diff --git a/src/common/config.cpp b/src/common/config.cpp index 1462f2aa6..e6ccbe346 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -94,6 +94,18 @@ public: base_value = toml::get_optional(v, key).value_or(base_value); } } + void set(const T value, bool is_game_specific = false) { + is_game_specific ? game_specific_value = value : base_value = value; + } + void setTomlValue(toml::ordered_value& data, const std::string& header, const std::string& key, + bool is_game_specific = false) { + if (is_game_specific) { + data[header][key] = game_specific_value.value_or(base_value); + game_specific_value = std::nullopt; + } else { + data[header][key] = base_value; + } + } // operator T() { // return get(); // } @@ -242,8 +254,8 @@ std::filesystem::path GetSaveDataPath() { return save_data_path; } -void setVolumeSlider(int volumeValue) { - volumeSlider.base_value = volumeValue; +void setVolumeSlider(int volumeValue, bool is_game_specific) { + volumeSlider.set(volumeValue, is_game_specific); } void setLoadGameSizeEnabled(bool enable) { @@ -426,16 +438,16 @@ bool getVkGuestMarkersEnabled() { return vkGuestMarkers.get(); } -void setVkCrashDiagnosticEnabled(bool enable) { - vkCrashDiagnostic.base_value = enable; +void setVkCrashDiagnosticEnabled(bool enable, bool is_game_specific) { + vkCrashDiagnostic.set(enable, is_game_specific); } -void setVkHostMarkersEnabled(bool enable) { - vkHostMarkers.base_value = enable; +void setVkHostMarkersEnabled(bool enable, bool is_game_specific) { + vkHostMarkers.set(enable, is_game_specific); } -void setVkGuestMarkersEnabled(bool enable) { - vkGuestMarkers.base_value = enable; +void setVkGuestMarkersEnabled(bool enable, bool is_game_specific) { + vkGuestMarkers.set(enable, is_game_specific); } bool getCompatibilityEnabled() { @@ -450,16 +462,20 @@ bool getIsConnectedToNetwork() { return isConnectedToNetwork.get(); } -void setGpuId(s32 selectedGpuId) { - gpuId.base_value = selectedGpuId; +void setConnectedToNetwork(bool enable, bool is_game_specific) { + isConnectedToNetwork.set(enable, is_game_specific); } -void setWindowWidth(u32 width) { - windowWidth.base_value = width; +void setGpuId(s32 selectedGpuId, bool is_game_specific) { + gpuId.set(selectedGpuId, is_game_specific); } -void setWindowHeight(u32 height) { - windowHeight.base_value = height; +void setWindowWidth(u32 width, bool is_game_specific) { + windowWidth.set(width, is_game_specific); +} + +void setWindowHeight(u32 height, bool is_game_specific) { + windowHeight.set(height, is_game_specific); } void setInternalScreenWidth(u32 width) { @@ -470,132 +486,136 @@ void setInternalScreenHeight(u32 height) { internalScreenHeight.base_value = height; } -void setDebugDump(bool enable) { - isDebugDump.base_value = enable; +void setDebugDump(bool enable, bool is_game_specific) { + isDebugDump.set(enable, is_game_specific); } -void setLoggingEnabled(bool enable) { - logEnabled.base_value = enable; +void setLoggingEnabled(bool enable, bool is_game_specific) { + logEnabled.set(enable, is_game_specific); } -void setCollectShaderForDebug(bool enable) { - isShaderDebug.base_value = enable; +void setCollectShaderForDebug(bool enable, bool is_game_specific) { + isShaderDebug.set(enable, is_game_specific); } -void setShowSplash(bool enable) { - isShowSplash.base_value = enable; +void setShowSplash(bool enable, bool is_game_specific) { + isShowSplash.set(enable, is_game_specific); } -void setSideTrophy(string side) { - isSideTrophy.base_value = side; +void setSideTrophy(string side, bool is_game_specific) { + isSideTrophy.set(side, is_game_specific); } -void setNullGpu(bool enable) { - isNullGpu.base_value = enable; +void setNullGpu(bool enable, bool is_game_specific) { + isNullGpu.set(enable, is_game_specific); } -void setAllowHDR(bool enable) { - isHDRAllowed.base_value = enable; +void setAllowHDR(bool enable, bool is_game_specific) { + isHDRAllowed.set(enable, is_game_specific); } -void setCopyGPUCmdBuffers(bool enable) { - shouldCopyGPUBuffers.base_value = enable; +void setCopyGPUCmdBuffers(bool enable, bool is_game_specific) { + shouldCopyGPUBuffers.set(enable, is_game_specific); } -void setReadbacks(bool enable) { - readbacksEnabled.base_value = enable; +void setReadbacks(bool enable, bool is_game_specific) { + readbacksEnabled.set(enable, is_game_specific); } -void setReadbackLinearImages(bool enable) { - readbackLinearImagesEnabled.base_value = enable; +void setReadbackLinearImages(bool enable, bool is_game_specific) { + readbackLinearImagesEnabled.set(enable, is_game_specific); } -void setDirectMemoryAccess(bool enable) { - directMemoryAccessEnabled.base_value = enable; +void setDirectMemoryAccess(bool enable, bool is_game_specific) { + directMemoryAccessEnabled.set(enable, is_game_specific); } -void setDumpShaders(bool enable) { - shouldDumpShaders.base_value = enable; +void setDumpShaders(bool enable, bool is_game_specific) { + shouldDumpShaders.set(enable, is_game_specific); } -void setVkValidation(bool enable) { - vkValidation.base_value = enable; +void setVkValidation(bool enable, bool is_game_specific) { + vkValidation.set(enable, is_game_specific); } -void setVkSyncValidation(bool enable) { - vkValidationSync.base_value = enable; +void setVkSyncValidation(bool enable, bool is_game_specific) { + vkValidationSync.set(enable, is_game_specific); } -void setRdocEnabled(bool enable) { - rdocEnable.base_value = enable; +void setRdocEnabled(bool enable, bool is_game_specific) { + rdocEnable.set(enable, is_game_specific); } -void setVblankFreq(u32 value) { - vblankFrequency.base_value = value; +void setVblankFreq(u32 value, bool is_game_specific) { + vblankFrequency.set(value, is_game_specific); } -void setIsFullscreen(bool enable) { - isFullscreen.base_value = enable; +void setIsFullscreen(bool enable, bool is_game_specific) { + isFullscreen.set(enable, is_game_specific); } -void setFullscreenMode(string mode) { - fullscreenMode.base_value = mode; +void setFullscreenMode(string mode, bool is_game_specific) { + fullscreenMode.set(mode, is_game_specific); } -void setPresentMode(std::string mode) { - presentMode.base_value = mode; +void setPresentMode(std::string mode, bool is_game_specific) { + presentMode.set(mode, is_game_specific); } -void setisTrophyPopupDisabled(bool disable) { - isTrophyPopupDisabled.base_value = disable; +void setisTrophyPopupDisabled(bool disable, bool is_game_specific) { + isTrophyPopupDisabled.set(disable, is_game_specific); } void setEnableDiscordRPC(bool enable) { enableDiscordRPC = enable; } -void setCursorState(s16 newCursorState) { - cursorState.base_value = newCursorState; +void setCursorState(s16 newCursorState, bool is_game_specific) { + cursorState.set(newCursorState, is_game_specific); } -void setCursorHideTimeout(int newcursorHideTimeout) { - cursorHideTimeout.base_value = newcursorHideTimeout; +void setCursorHideTimeout(int newcursorHideTimeout, bool is_game_specific) { + cursorHideTimeout.set(newcursorHideTimeout, is_game_specific); } -void setMicDevice(string device) { - micDevice.base_value = device; +void setMicDevice(string device, bool is_game_specific) { + micDevice.set(device, is_game_specific); } -void setTrophyNotificationDuration(double newTrophyNotificationDuration) { - trophyNotificationDuration.base_value = newTrophyNotificationDuration; +void setTrophyNotificationDuration(double newTrophyNotificationDuration, bool is_game_specific) { + trophyNotificationDuration.set(newTrophyNotificationDuration, is_game_specific); } -void setLanguage(u32 language) { - m_language.base_value = language; +void setLanguage(u32 language, bool is_game_specific) { + m_language.set(language, is_game_specific); } -void setNeoMode(bool enable) { - isNeo.base_value = enable; +void setNeoMode(bool enable, bool is_game_specific) { + isNeo.set(enable, is_game_specific); } -void setLogType(const string& type) { - logType.base_value = type; +void setDevKitConsole(bool enable, bool is_game_specific) { + isDevKit.set(enable, is_game_specific); } -void setLogFilter(const string& type) { - logFilter.base_value = type; +void setLogType(const string& type, bool is_game_specific) { + logType.set(type, is_game_specific); } -void setSeparateLogFilesEnabled(bool enabled) { - isSeparateLogFilesEnabled.base_value = enabled; +void setLogFilter(const string& type, bool is_game_specific) { + logFilter.set(type, is_game_specific); } -void setUserName(const string& type) { - userName.base_value = type; +void setSeparateLogFilesEnabled(bool enabled, bool is_game_specific) { + isSeparateLogFilesEnabled.set(enabled, is_game_specific); } -void setChooseHomeTab(const string& type) { - chooseHomeTab.base_value = type; +void setUserName(const string& name, bool is_game_specific) { + userName.set(name, is_game_specific); +} + +void setChooseHomeTab(const string& type, bool is_game_specific) { + chooseHomeTab.set(type, is_game_specific); } void setUseSpecialPad(bool use) { @@ -606,8 +626,8 @@ void setSpecialPadClass(int type) { specialPadClass.base_value = type; } -void setIsMotionControlsEnabled(bool use) { - isMotionControlsEnabled.base_value = use; +void setIsMotionControlsEnabled(bool use, bool is_game_specific) { + isMotionControlsEnabled.set(use, is_game_specific); } void setCompatibilityEnabled(bool use) { @@ -703,8 +723,8 @@ bool getPSNSignedIn() { return isPSNSignedIn.get(); } -void setPSNSignedIn(bool sign) { - isPSNSignedIn.base_value = sign; +void setPSNSignedIn(bool sign, bool is_game_specific) { + isPSNSignedIn.set(sign, is_game_specific); } string getDefaultControllerID() { @@ -719,32 +739,32 @@ bool getBackgroundControllerInput() { return backgroundControllerInput.get(); } -void setBackgroundControllerInput(bool enable) { - backgroundControllerInput.base_value = enable; +void setBackgroundControllerInput(bool enable, bool is_game_specific) { + backgroundControllerInput.set(enable, is_game_specific); } bool getFsrEnabled() { return fsrEnabled.get(); } -void setFsrEnabled(bool enable) { - fsrEnabled.base_value = enable; +void setFsrEnabled(bool enable, bool is_game_specific) { + fsrEnabled.set(enable, is_game_specific); } bool getRcasEnabled() { return rcasEnabled.get(); } -void setRcasEnabled(bool enable) { - rcasEnabled.base_value = enable; +void setRcasEnabled(bool enable, bool is_game_specific) { + rcasEnabled.set(enable, is_game_specific); } int getRcasAttenuation() { return rcasAttenuation.get(); } -void setRcasAttenuation(int value) { - rcasAttenuation.base_value = value; +void setRcasAttenuation(int value, bool is_game_specific) { + rcasAttenuation.set(value, is_game_specific); } void load(const std::filesystem::path& path, bool is_game_specific) { @@ -934,7 +954,7 @@ void sortTomlSections(toml::ordered_value& data) { data = ordered_data; } -void save(const std::filesystem::path& path) { +void save(const std::filesystem::path& path, bool is_game_specific) { toml::ordered_value data; std::error_code error; @@ -956,100 +976,124 @@ void save(const std::filesystem::path& path) { fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string())); } - data["General"]["volumeSlider"] = volumeSlider.base_value; - data["General"]["isPS4Pro"] = isNeo.base_value; - data["General"]["isDevKit"] = isDevKit.base_value; - data["General"]["isPSNSignedIn"] = isPSNSignedIn.base_value; - data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled.base_value; - data["General"]["trophyNotificationDuration"] = trophyNotificationDuration.base_value; - data["General"]["logFilter"] = logFilter.base_value; - data["General"]["logType"] = logType.base_value; - data["General"]["userName"] = userName.base_value; - data["General"]["chooseHomeTab"] = chooseHomeTab.base_value; - data["General"]["showSplash"] = isShowSplash.base_value; - data["General"]["sideTrophy"] = isSideTrophy.base_value; - data["General"]["isConnectedToNetwork"] = isConnectedToNetwork.base_value; - data["General"]["defaultControllerID"] = defaultControllerID.base_value; - data["General"]["enableDiscordRPC"] = enableDiscordRPC; - data["General"]["compatibilityEnabled"] = compatibilityData; - data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; - data["Input"]["cursorState"] = cursorState.base_value; - data["Input"]["cursorHideTimeout"] = cursorHideTimeout.base_value; - data["Input"]["useSpecialPad"] = useSpecialPad.base_value; - data["Input"]["specialPadClass"] = specialPadClass.base_value; - data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled.base_value; - data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig.base_value; - data["Input"]["micDevice"] = micDevice.base_value; - data["Input"]["backgroundControllerInput"] = backgroundControllerInput.base_value; - data["GPU"]["screenWidth"] = windowWidth.base_value; - data["GPU"]["screenHeight"] = windowHeight.base_value; - data["GPU"]["internalScreenWidth"] = internalScreenWidth.base_value; - data["GPU"]["internalScreenHeight"] = internalScreenHeight.base_value; - data["GPU"]["nullGpu"] = isNullGpu.base_value; - data["GPU"]["copyGPUBuffers"] = shouldCopyGPUBuffers.base_value; - data["GPU"]["readbacks"] = readbacksEnabled.base_value; - data["GPU"]["readbackLinearImages"] = readbackLinearImagesEnabled.base_value; - data["GPU"]["directMemoryAccess"] = directMemoryAccessEnabled.base_value; - data["GPU"]["dumpShaders"] = shouldDumpShaders.base_value; - data["GPU"]["patchShaders"] = shouldPatchShaders.base_value; - data["GPU"]["vblankFrequency"] = vblankFrequency.base_value; - data["GPU"]["Fullscreen"] = isFullscreen.base_value; - data["GPU"]["FullscreenMode"] = fullscreenMode.base_value; - data["GPU"]["presentMode"] = presentMode.base_value; - data["GPU"]["allowHDR"] = isHDRAllowed.base_value; - data["GPU"]["fsrEnabled"] = fsrEnabled.base_value; - data["GPU"]["rcasEnabled"] = rcasEnabled.base_value; - data["GPU"]["rcasAttenuation"] = rcasAttenuation.base_value; - data["Vulkan"]["gpuId"] = gpuId.base_value; - data["Vulkan"]["validation"] = vkValidation.base_value; - data["Vulkan"]["validation_sync"] = vkValidationSync.base_value; - data["Vulkan"]["validation_gpu"] = vkValidationGpu.base_value; - data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic.base_value; - data["Vulkan"]["hostMarkers"] = vkHostMarkers.base_value; - data["Vulkan"]["guestMarkers"] = vkGuestMarkers.base_value; - data["Vulkan"]["rdocEnable"] = rdocEnable.base_value; - data["Debug"]["DebugDump"] = isDebugDump.base_value; - data["Debug"]["CollectShader"] = isShaderDebug.base_value; - data["Debug"]["isSeparateLogFilesEnabled"] = isSeparateLogFilesEnabled.base_value; - data["Debug"]["FPSColor"] = isFpsColor.base_value; - data["Debug"]["logEnabled"] = logEnabled.base_value; - data["Debug"]["ConfigVersion"] = config_version; - data["Keys"]["TrophyKey"] = trophyKey; + // Entries saved by the game-specific settings GUI + volumeSlider.setTomlValue(data, "General", "volumeSlider", is_game_specific); + isTrophyPopupDisabled.setTomlValue(data, "General", "isTrophyPopupDisabled", is_game_specific); + trophyNotificationDuration.setTomlValue(data, "General", "trophyNotificationDuration", + is_game_specific); + logFilter.setTomlValue(data, "General", "logFilter", is_game_specific); + logType.setTomlValue(data, "General", "logType", is_game_specific); + userName.setTomlValue(data, "General", "userName", is_game_specific); + chooseHomeTab.setTomlValue(data, "General", "chooseHomeTab", is_game_specific); + isShowSplash.setTomlValue(data, "General", "showSplash", is_game_specific); + isSideTrophy.setTomlValue(data, "General", "sideTrophy", is_game_specific); + isNeo.setTomlValue(data, "General", "isPS4Pro", is_game_specific); + isDevKit.setTomlValue(data, "General", "isDevKit", is_game_specific); + isPSNSignedIn.setTomlValue(data, "General", "isPSNSignedIn", is_game_specific); + isConnectedToNetwork.setTomlValue(data, "General", "isConnectedToNetwork", is_game_specific); - std::vector install_dirs; - std::vector install_dirs_enabled; + cursorState.setTomlValue(data, "Input", "cursorState", is_game_specific); + cursorHideTimeout.setTomlValue(data, "Input", "cursorHideTimeout", is_game_specific); + isMotionControlsEnabled.setTomlValue(data, "Input", "isMotionControlsEnabled", + is_game_specific); + micDevice.setTomlValue(data, "Input", "micDevice", is_game_specific); + backgroundControllerInput.setTomlValue(data, "Input", "backgroundControllerInput", + is_game_specific); - // temporary structure for ordering - struct DirEntry { - string path_str; - bool enabled; - }; + windowWidth.setTomlValue(data, "GPU", "screenWidth", is_game_specific); + windowHeight.setTomlValue(data, "GPU", "screenHeight", is_game_specific); + isNullGpu.setTomlValue(data, "GPU", "nullGpu", is_game_specific); + shouldCopyGPUBuffers.setTomlValue(data, "GPU", "copyGPUBuffers", is_game_specific); + readbacksEnabled.setTomlValue(data, "GPU", "readbacks", is_game_specific); + readbackLinearImagesEnabled.setTomlValue(data, "GPU", "readbackLinearImages", is_game_specific); + shouldDumpShaders.setTomlValue(data, "GPU", "dumpShaders", is_game_specific); + vblankFrequency.setTomlValue(data, "GPU", "vblankFrequency", is_game_specific); + isFullscreen.setTomlValue(data, "GPU", "Fullscreen", is_game_specific); + fullscreenMode.setTomlValue(data, "GPU", "FullscreenMode", is_game_specific); + presentMode.setTomlValue(data, "GPU", "presentMode", is_game_specific); + isHDRAllowed.setTomlValue(data, "GPU", "allowHDR", is_game_specific); + fsrEnabled.setTomlValue(data, "GPU", "fsrEnabled", is_game_specific); + rcasEnabled.setTomlValue(data, "GPU", "rcasEnabled", is_game_specific); + rcasAttenuation.setTomlValue(data, "GPU", "rcasAttenuation", is_game_specific); + directMemoryAccessEnabled.setTomlValue(data, "GPU", "directMemoryAccess", is_game_specific); - std::vector sorted_dirs; - for (const auto& dirInfo : settings_install_dirs) { - sorted_dirs.push_back({string{fmt::UTF(dirInfo.path.u8string()).data}, dirInfo.enabled}); + gpuId.setTomlValue(data, "Vulkan", "gpuId", is_game_specific); + vkValidation.setTomlValue(data, "Vulkan", "validation", is_game_specific); + vkValidationSync.setTomlValue(data, "Vulkan", "validation_sync", is_game_specific); + vkCrashDiagnostic.setTomlValue(data, "Vulkan", "crashDiagnostic", is_game_specific); + vkHostMarkers.setTomlValue(data, "Vulkan", "hostMarkers", is_game_specific); + vkGuestMarkers.setTomlValue(data, "Vulkan", "guestMarkers", is_game_specific); + rdocEnable.setTomlValue(data, "Vulkan", "rdocEnable", is_game_specific); + + isDebugDump.setTomlValue(data, "Debug", "DebugDump", is_game_specific); + isShaderDebug.setTomlValue(data, "Debug", "CollectShader", is_game_specific); + isSeparateLogFilesEnabled.setTomlValue(data, "Debug", "isSeparateLogFilesEnabled", + is_game_specific); + logEnabled.setTomlValue(data, "Debug", "logEnable", is_game_specific); + + m_language.setTomlValue(data, "Settings", "consoleLanguage", is_game_specific); + + // All other entries + if (!is_game_specific) { + std::vector install_dirs; + std::vector install_dirs_enabled; + + // temporary structure for ordering + struct DirEntry { + string path_str; + bool enabled; + }; + + std::vector sorted_dirs; + for (const auto& dirInfo : settings_install_dirs) { + sorted_dirs.push_back( + {string{fmt::UTF(dirInfo.path.u8string()).data}, dirInfo.enabled}); + } + + // Sort directories alphabetically + std::sort(sorted_dirs.begin(), sorted_dirs.end(), [](const DirEntry& a, const DirEntry& b) { + return std::lexicographical_compare( + a.path_str.begin(), a.path_str.end(), b.path_str.begin(), b.path_str.end(), + [](char a_char, char b_char) { + return std::tolower(a_char) < std::tolower(b_char); + }); + }); + + for (const auto& entry : sorted_dirs) { + install_dirs.push_back(entry.path_str); + install_dirs_enabled.push_back(entry.enabled); + } + + // Non game-specific entries + data["General"]["enableDiscordRPC"] = enableDiscordRPC; + data["General"]["compatibilityEnabled"] = compatibilityData; + data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; + data["GUI"]["installDirs"] = install_dirs; + data["GUI"]["installDirsEnabled"] = install_dirs_enabled; + data["GUI"]["saveDataPath"] = string{fmt::UTF(save_data_path.u8string()).data}; + data["GUI"]["loadGameSizeEnabled"] = load_game_size; + data["GUI"]["addonInstallDir"] = + string{fmt::UTF(settings_addon_install_dir.u8string()).data}; + + data["Debug"]["ConfigVersion"] = config_version; + data["Keys"]["TrophyKey"] = trophyKey; + + // Do not save these entries in the game-specific dialog since they are not in the GUI + data["General"]["defaultControllerID"] = defaultControllerID.base_value; + + data["Input"]["useSpecialPad"] = useSpecialPad.base_value; + data["Input"]["specialPadClass"] = specialPadClass.base_value; + data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig.base_value; + + data["GPU"]["internalScreenWidth"] = internalScreenWidth.base_value; + data["GPU"]["internalScreenHeight"] = internalScreenHeight.base_value; + data["GPU"]["patchShaders"] = shouldPatchShaders.base_value; + + data["Vulkan"]["validation_gpu"] = vkValidationGpu.base_value; + + data["Debug"]["FPSColor"] = isFpsColor.base_value; } - // Sort directories alphabetically - std::sort(sorted_dirs.begin(), sorted_dirs.end(), [](const DirEntry& a, const DirEntry& b) { - return std::lexicographical_compare( - a.path_str.begin(), a.path_str.end(), b.path_str.begin(), b.path_str.end(), - [](char a_char, char b_char) { return std::tolower(a_char) < std::tolower(b_char); }); - }); - - for (const auto& entry : sorted_dirs) { - install_dirs.push_back(entry.path_str); - install_dirs_enabled.push_back(entry.enabled); - } - - data["GUI"]["installDirs"] = install_dirs; - data["GUI"]["installDirsEnabled"] = install_dirs_enabled; - data["GUI"]["saveDataPath"] = string{fmt::UTF(save_data_path.u8string()).data}; - data["GUI"]["loadGameSizeEnabled"] = load_game_size; - - data["GUI"]["addonInstallDir"] = string{fmt::UTF(settings_addon_install_dir.u8string()).data}; - data["Settings"]["consoleLanguage"] = m_language.base_value; - // Sorting of TOML sections sortTomlSections(data); @@ -1058,82 +1102,101 @@ void save(const std::filesystem::path& path) { file.close(); } -void setDefaultValues() { - // General - volumeSlider = 100; - isNeo = false; - isDevKit = false; - isPSNSignedIn = false; - isTrophyPopupDisabled = false; - trophyNotificationDuration = 6.0; - enableDiscordRPC = false; - logFilter = ""; - logType = "sync"; - userName = "shadPS4"; - chooseHomeTab = "General"; - isShowSplash = false; - isSideTrophy = "right"; - compatibilityData = false; - checkCompatibilityOnStartup = false; - isConnectedToNetwork = false; +void setDefaultValues(bool is_game_specific) { - // 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; - micDevice = "Default Device"; - backgroundControllerInput = false; + // Entries with game-specific settings that are in the game-specific setings GUI but not in + // the global settings GUI + if (is_game_specific) { + isNeo.set(false, is_game_specific); + isDevKit.set(false, is_game_specific); + isPSNSignedIn.set(false, is_game_specific); + isConnectedToNetwork.set(false, is_game_specific); + directMemoryAccessEnabled.set(false, is_game_specific); + } - // GPU - windowWidth = 1280; - windowHeight = 720; - internalScreenWidth = 1280; - internalScreenHeight = 720; - isNullGpu = false; - shouldCopyGPUBuffers = false; - readbacksEnabled = false; - readbackLinearImagesEnabled = false; - directMemoryAccessEnabled = false; - shouldDumpShaders = false; - shouldPatchShaders = false; - vblankFrequency = 60; - isFullscreen = false; - fullscreenMode = "Windowed"; - presentMode = "Mailbox"; - isHDRAllowed = false; - fsrEnabled = true; - rcasEnabled = true; - rcasAttenuation = 250; + // Entries with game-specific settings that are in both the game-specific and global GUI + // GS - General + volumeSlider.set(100, is_game_specific); + isTrophyPopupDisabled.set(false, is_game_specific); + trophyNotificationDuration.set(6.0, is_game_specific); + logFilter.set("", is_game_specific); + logType.set("sync", is_game_specific); + userName.set("shadPS4", is_game_specific); + chooseHomeTab.set("General", is_game_specific); + isShowSplash.set(false, is_game_specific); + isSideTrophy.set("right", is_game_specific); - // Vulkan - gpuId = -1; - vkValidation = false; - vkValidationSync = false; - vkValidationGpu = false; - vkCrashDiagnostic = false; - vkHostMarkers = false; - vkGuestMarkers = false; - rdocEnable = false; + // GS - Input + cursorState.set(HideCursorState::Idle, is_game_specific); + cursorHideTimeout.set(5, is_game_specific); + isMotionControlsEnabled.set(true, is_game_specific); + micDevice.set("Default Device", is_game_specific); + backgroundControllerInput.set(false, is_game_specific); - // Debug - isDebugDump = false; - isShaderDebug = false; - isSeparateLogFilesEnabled = false; - isFpsColor = true; - logEnabled = true; + // GS - GPU + windowWidth.set(1280, is_game_specific); + windowHeight.set(720, is_game_specific); + isNullGpu.set(false, is_game_specific); + shouldCopyGPUBuffers.set(false, is_game_specific); + readbacksEnabled.set(false, is_game_specific); + readbackLinearImagesEnabled.set(false, is_game_specific); + shouldDumpShaders.set(false, is_game_specific); + vblankFrequency.set(60, is_game_specific); + isFullscreen.set(false, is_game_specific); + fullscreenMode.set("Windowed", is_game_specific); + presentMode.set("Mailbox", is_game_specific); + isHDRAllowed.set(false, is_game_specific); + fsrEnabled.set(true, is_game_specific); + rcasEnabled.set(true, is_game_specific); + rcasAttenuation.set(250, is_game_specific); - // GUI - load_game_size = true; + // GS - Vulkan + gpuId.set(-1, is_game_specific); + vkValidation.set(false, is_game_specific); + vkValidationSync.set(false, is_game_specific); + vkValidationGpu.set(false, is_game_specific); + vkCrashDiagnostic.set(false, is_game_specific); + vkHostMarkers.set(false, is_game_specific); + vkGuestMarkers.set(false, is_game_specific); + rdocEnable.set(false, is_game_specific); - // Settings - m_language = 1; + // GS - Debug + isDebugDump.set(false, is_game_specific); + isShaderDebug.set(false, is_game_specific); + isSeparateLogFilesEnabled.set(false, is_game_specific); + logEnabled.set(true, is_game_specific); + + // GS - Settings + m_language.set(1, is_game_specific); + + // All other entries + if (!is_game_specific) { + + // General + enableDiscordRPC = false; + compatibilityData = false; + checkCompatibilityOnStartup = false; + + // Input + useSpecialPad.base_value = false; + specialPadClass.base_value = 1; + useUnifiedInputConfig.base_value = true; + overrideControllerColor = false; + controllerCustomColorRGB[0] = 0; + controllerCustomColorRGB[1] = 0; + controllerCustomColorRGB[2] = 255; + + // GPU + shouldPatchShaders.base_value = false; + internalScreenWidth.base_value = 1280; + internalScreenHeight.base_value = 720; + + // GUI + load_game_size = true; + + // Debug + isFpsColor.base_value = true; + } } constexpr std::string_view GetDefaultGlobalConfig() { @@ -1272,4 +1335,10 @@ std::filesystem::path GetFoolproofInputConfigFile(const string& game_id) { return config_file; } +void resetGameSpecificValue(std::string entry) { + if (entry == "volumeSlider") { + volumeSlider.game_specific_value = std::nullopt; + } +} + } // namespace Config diff --git a/src/common/config.h b/src/common/config.h index 6631723af..1e285a13b 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -17,110 +17,118 @@ struct GameInstallDir { enum HideCursorState : int { Never, Idle, Always }; void load(const std::filesystem::path& path, bool is_game_specific = false); -void save(const std::filesystem::path& path); +void save(const std::filesystem::path& path, bool is_game_specific = false); +void resetGameSpecificValue(std::string entry); int getVolumeSlider(); -void setVolumeSlider(int volumeValue); +void setVolumeSlider(int volumeValue, bool is_game_specific = false); std::string getTrophyKey(); void setTrophyKey(std::string key); bool getIsFullscreen(); -void setIsFullscreen(bool enable); +void setIsFullscreen(bool enable, bool is_game_specific = false); std::string getFullscreenMode(); -void setFullscreenMode(std::string mode); +void setFullscreenMode(std::string mode, bool is_game_specific = false); std::string getPresentMode(); -void setPresentMode(std::string mode); +void setPresentMode(std::string mode, bool is_game_specific = false); u32 getWindowWidth(); u32 getWindowHeight(); -void setWindowWidth(u32 width); -void setWindowHeight(u32 height); +void setWindowWidth(u32 width, bool is_game_specific = false); +void setWindowHeight(u32 height, bool is_game_specific = false); u32 getInternalScreenWidth(); u32 getInternalScreenHeight(); void setInternalScreenWidth(u32 width); void setInternalScreenHeight(u32 height); bool debugDump(); -void setDebugDump(bool enable); +void setDebugDump(bool enable, bool is_game_specific = false); s32 getGpuId(); -void setGpuId(s32 selectedGpuId); +void setGpuId(s32 selectedGpuId, bool is_game_specific = false); bool allowHDR(); -void setAllowHDR(bool enable); +void setAllowHDR(bool enable, bool is_game_specific = false); bool collectShadersForDebug(); -void setCollectShaderForDebug(bool enable); +void setCollectShaderForDebug(bool enable, bool is_game_specific = false); bool showSplash(); -void setShowSplash(bool enable); +void setShowSplash(bool enable, bool is_game_specific = false); std::string sideTrophy(); -void setSideTrophy(std::string side); +void setSideTrophy(std::string side, bool is_game_specific = false); bool nullGpu(); -void setNullGpu(bool enable); +void setNullGpu(bool enable, bool is_game_specific = false); bool copyGPUCmdBuffers(); -void setCopyGPUCmdBuffers(bool enable); +void setCopyGPUCmdBuffers(bool enable, bool is_game_specific = false); bool readbacks(); -void setReadbacks(bool enable); +void setReadbacks(bool enable, bool is_game_specific = false); bool readbackLinearImages(); -void setReadbackLinearImages(bool enable); +void setReadbackLinearImages(bool enable, bool is_game_specific = false); bool directMemoryAccess(); -void setDirectMemoryAccess(bool enable); +void setDirectMemoryAccess(bool enable, bool is_game_specific = false); bool dumpShaders(); -void setDumpShaders(bool enable); +void setDumpShaders(bool enable, bool is_game_specific = false); u32 vblankFreq(); -void setVblankFreq(u32 value); +void setVblankFreq(u32 value, bool is_game_specific = false); bool getisTrophyPopupDisabled(); -void setisTrophyPopupDisabled(bool disable); +void setisTrophyPopupDisabled(bool disable, bool is_game_specific = false); s16 getCursorState(); -void setCursorState(s16 cursorState); +void setCursorState(s16 cursorState, bool is_game_specific = false); bool vkValidationEnabled(); -void setVkValidation(bool enable); +void setVkValidation(bool enable, bool is_game_specific = false); bool vkValidationSyncEnabled(); -void setVkSyncValidation(bool enable); +void setVkSyncValidation(bool enable, bool is_game_specific = false); bool getVkCrashDiagnosticEnabled(); -void setVkCrashDiagnosticEnabled(bool enable); +void setVkCrashDiagnosticEnabled(bool enable, bool is_game_specific = false); bool getVkHostMarkersEnabled(); -void setVkHostMarkersEnabled(bool enable); +void setVkHostMarkersEnabled(bool enable, bool is_game_specific = false); bool getVkGuestMarkersEnabled(); -void setVkGuestMarkersEnabled(bool enable); +void setVkGuestMarkersEnabled(bool enable, bool is_game_specific = false); bool getEnableDiscordRPC(); void setEnableDiscordRPC(bool enable); bool isRdocEnabled(); -void setRdocEnabled(bool enable); +void setRdocEnabled(bool enable, bool is_game_specific = false); std::string getLogType(); -void setLogType(const std::string& type); +void setLogType(const std::string& type, bool is_game_specific = false); std::string getLogFilter(); -void setLogFilter(const std::string& type); +void setLogFilter(const std::string& type, bool is_game_specific = false); double getTrophyNotificationDuration(); -void setTrophyNotificationDuration(double newTrophyNotificationDuration); +void setTrophyNotificationDuration(double newTrophyNotificationDuration, + bool is_game_specific = false); int getCursorHideTimeout(); std::string getMicDevice(); -void setCursorHideTimeout(int newcursorHideTimeout); -void setMicDevice(std::string device); -void setSeparateLogFilesEnabled(bool enabled); +void setCursorHideTimeout(int newcursorHideTimeout, bool is_game_specific = false); +void setMicDevice(std::string device, bool is_game_specific = false); +void setSeparateLogFilesEnabled(bool enabled, bool is_game_specific = false); bool getSeparateLogFilesEnabled(); u32 GetLanguage(); -void setLanguage(u32 language); +void setLanguage(u32 language, bool is_game_specific = false); void setUseSpecialPad(bool use); bool getUseSpecialPad(); void setSpecialPadClass(int type); int getSpecialPadClass(); bool getPSNSignedIn(); -void setPSNSignedIn(bool sign); // no ui setting -bool patchShaders(); // no set -bool fpsColor(); // no set +void setPSNSignedIn(bool sign, bool is_game_specific = false); +bool patchShaders(); // no set +bool fpsColor(); // no set bool isNeoModeConsole(); -void setNeoMode(bool enable); // no ui setting -bool isDevKitConsole(); // no set +void setNeoMode(bool enable, bool is_game_specific = false); +bool isDevKitConsole(); +void setDevKitConsole(bool enable, bool is_game_specific = false); + bool vkValidationGpuEnabled(); // no set bool getIsMotionControlsEnabled(); -void setIsMotionControlsEnabled(bool use); +void setIsMotionControlsEnabled(bool use, bool is_game_specific = false); std::string getDefaultControllerID(); void setDefaultControllerID(std::string id); bool getBackgroundControllerInput(); -void setBackgroundControllerInput(bool enable); +void setBackgroundControllerInput(bool enable, bool is_game_specific = false); bool getLoggingEnabled(); -void setLoggingEnabled(bool enable); +void setLoggingEnabled(bool enable, bool is_game_specific = false); bool getFsrEnabled(); -void setFsrEnabled(bool enable); +void setFsrEnabled(bool enable, bool is_game_specific = false); bool getRcasEnabled(); -void setRcasEnabled(bool enable); +void setRcasEnabled(bool enable, bool is_game_specific = false); int getRcasAttenuation(); -void setRcasAttenuation(int value); +void setRcasAttenuation(int value, bool is_game_specific = false); +bool getIsConnectedToNetwork(); +void setConnectedToNetwork(bool enable, bool is_game_specific = false); +void setUserName(const std::string& name, bool is_game_specific = false); +void setChooseHomeTab(const std::string& type, bool is_game_specific = false); // TODO bool GetLoadGameSizeEnabled(); @@ -128,7 +136,6 @@ std::filesystem::path GetSaveDataPath(); void setLoadGameSizeEnabled(bool enable); bool getCompatibilityEnabled(); bool getCheckCompatibilityOnStartup(); -bool getIsConnectedToNetwork(); std::string getUserName(); std::string getChooseHomeTab(); bool GetUseUnifiedInputConfig(); @@ -137,8 +144,6 @@ bool GetOverrideControllerColor(); void SetOverrideControllerColor(bool enable); int* GetControllerCustomColor(); void SetControllerCustomColor(int r, int b, int g); -void setUserName(const std::string& type); -void setChooseHomeTab(const std::string& type); void setGameInstallDirs(const std::vector& dirs_config); void setAllGameInstallDirs(const std::vector& dirs_config); void setSaveDataPath(const std::filesystem::path& path); @@ -154,7 +159,7 @@ const std::vector getGameInstallDirs(); const std::vector getGameInstallDirsEnabled(); std::filesystem::path getAddonInstallDir(); -void setDefaultValues(); +void setDefaultValues(bool is_game_specific = false); constexpr std::string_view GetDefaultGlobalConfig(); std::filesystem::path GetFoolproofInputConfigFile(const std::string& game_id = ""); diff --git a/src/emulator.cpp b/src/emulator.cpp index 1388c9027..26ff40dc1 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -136,8 +136,13 @@ void Emulator::Run(std::filesystem::path file, const std::vector ar LOG_INFO(Loader, "Description {}", Common::g_scm_desc); LOG_INFO(Loader, "Remote {}", Common::g_scm_remote_url); + const bool has_game_config = std::filesystem::exists( + Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (id + ".toml")); + LOG_INFO(Config, "Game-specific config exists: {}", has_game_config); + LOG_INFO(Config, "General LogType: {}", Config::getLogType()); LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole()); + LOG_INFO(Config, "General isDevKit: {}", Config::isDevKitConsole()); LOG_INFO(Config, "General isConnectedToNetwork: {}", Config::getIsConnectedToNetwork()); LOG_INFO(Config, "General isPsnSignedIn: {}", Config::getPSNSignedIn()); LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu()); diff --git a/src/images/game_settings.png b/src/images/game_settings.png new file mode 100644 index 000000000..614705228 Binary files /dev/null and b/src/images/game_settings.png differ diff --git a/src/qt_gui/game_grid_frame.cpp b/src/qt_gui/game_grid_frame.cpp index dda73fa17..7608e1150 100644 --- a/src/qt_gui/game_grid_frame.cpp +++ b/src/qt_gui/game_grid_frame.cpp @@ -126,6 +126,7 @@ void GameGridFrame::PopulateGameGrid(QVector m_games_search, bool from image_label->setPixmap(QPixmap::fromImage(icon)); image_label->move(0, 0); SetFavoriteIcon(image_container, m_games_, gameCounter); + SetGameConfigIcon(image_container, m_games_, gameCounter); QLabel* name_label = new QLabel(QString::fromStdString(m_games_[gameCounter].serial)); name_label->setAlignment(Qt::AlignHCenter); @@ -254,6 +255,23 @@ void GameGridFrame::SetFavoriteIcon(QWidget* parentWidget, QVector m_g label->setObjectName("favoriteIcon"); } +void GameGridFrame::SetGameConfigIcon(QWidget* parentWidget, QVector m_games_, + int gameCounter) { + std::string serialStr = m_games_[gameCounter].serial; + + bool hasGameConfig = std::filesystem::exists( + Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (serialStr + ".toml")); + + QLabel* label = new QLabel(parentWidget); + label->setPixmap(QPixmap(":images/game_settings.png") + .scaled(icon_size / 3.8, icon_size / 3.8, Qt::KeepAspectRatio, + Qt::SmoothTransformation)); + label->move(2, 2); + label->raise(); + label->setVisible(hasGameConfig); + label->setObjectName("gameConfigIcon"); +} + void GameGridFrame::SortByFavorite(QVector* game_list) { std::sort(game_list->begin(), game_list->end(), [this](const GameInfo& a, const GameInfo& b) { return this->CompareWithFavorite(a, b); diff --git a/src/qt_gui/game_grid_frame.h b/src/qt_gui/game_grid_frame.h index 0a12deb1c..4da5b9147 100644 --- a/src/qt_gui/game_grid_frame.h +++ b/src/qt_gui/game_grid_frame.h @@ -40,6 +40,7 @@ private: std::filesystem::path m_current_game_path; // Track current game path to detect changes std::shared_ptr m_gui_settings; void SetFavoriteIcon(QWidget* parentWidget, QVector m_games_, int gameCounter); + void SetGameConfigIcon(QWidget* parentWidget, QVector m_games_, int gameCounter); bool CompareWithFavorite(GameInfo a, GameInfo b); public: diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index e0995d3ef..bdbb3e81f 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -83,10 +83,7 @@ GameListFrame::GameListFrame(std::shared_ptr gui_settings, connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) { int changedFavorite = m_gui_context_menus.RequestGameMenu( pos, m_game_info->m_games, m_compat_info, m_gui_settings, this, true); - if (changedFavorite) { - last_favorite = m_game_info->m_games[this->currentRow()].serial; - PopulateGameList(false); - } + PopulateGameList(false); }); connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) { @@ -146,6 +143,12 @@ void GameListFrame::PopulateGameList(bool isInitialPopulation) { for (int i = 0; i < m_game_info->m_games.size(); i++) { SetTableItem(i, 1, QString::fromStdString(m_game_info->m_games[i].name)); + if (std::filesystem::exists(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / + (m_game_info->m_games[i].serial + ".toml"))) { + QTableWidgetItem* name_item = item(i, 1); + name_item->setIcon(QIcon(":images/game_settings.png")); + } + SetTableItem(i, 3, QString::fromStdString(m_game_info->m_games[i].serial)); SetRegionFlag(i, 4, QString::fromStdString(m_game_info->m_games[i].region)); SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].fw)); diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 110421002..f5426bd54 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -17,6 +17,7 @@ #include "compatibility_info.h" #include "game_info.h" #include "gui_settings.h" +#include "settings_dialog.h" #include "trophy_viewer.h" #ifdef Q_OS_WIN @@ -66,6 +67,24 @@ public: menu.addMenu(openFolderMenu); + QMenu* gameConfigMenu = new QMenu(tr("Game-specific Settings..."), widget); + QAction gameConfigConfigure(tr("Configure Game-specific Settings"), widget); + QAction gameConfigCreate(tr("Create Game-specific Settings from Global Settings"), widget); + QAction gameConfigDelete(tr("Delete Game-specific Settings"), widget); + + if (std::filesystem::exists(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / + (m_games[itemID].serial + ".toml"))) { + gameConfigMenu->addAction(&gameConfigConfigure); + } else { + gameConfigMenu->addAction(&gameConfigCreate); + } + + if (std::filesystem::exists(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / + (m_games[itemID].serial + ".toml"))) + gameConfigMenu->addAction(&gameConfigDelete); + + menu.addMenu(gameConfigMenu); + QString serialStr = QString::fromStdString(m_games[itemID].serial); QList list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list)); bool isFavorite = list.contains(serialStr); @@ -76,6 +95,7 @@ public: } else { toggleFavorite = new QAction(tr("Add to Favorites"), widget); } + QAction createShortcut(tr("Create Shortcut"), widget); QAction openCheats(tr("Cheats / Patches"), widget); QAction openSfoViewer(tr("SFO Viewer"), widget); @@ -123,9 +143,9 @@ public: // Compatibility submenu. QMenu* compatibilityMenu = new QMenu(tr("Compatibility..."), widget); - QAction* updateCompatibility = new QAction(tr("Update database"), widget); - QAction* viewCompatibilityReport = new QAction(tr("View report"), widget); - QAction* submitCompatibilityReport = new QAction(tr("Submit a report"), widget); + QAction* updateCompatibility = new QAction(tr("Update Database"), widget); + QAction* viewCompatibilityReport = new QAction(tr("View Report"), widget); + QAction* submitCompatibilityReport = new QAction(tr("Submit a Report"), widget); compatibilityMenu->addAction(updateCompatibility); compatibilityMenu->addAction(viewCompatibilityReport); @@ -387,6 +407,22 @@ public: [trophyViewer]() { trophyViewer->deleteLater(); }); } + if (selected == &gameConfigConfigure || selected == &gameConfigCreate) { + auto settingsWindow = new SettingsDialog(m_gui_settings, m_compat_info, widget, true, + serialStr.toStdString()); + settingsWindow->exec(); + } + + if (selected == &gameConfigDelete) { + if (QMessageBox::Yes == QMessageBox::question(widget, tr("Confirm deletion"), + tr("Delete game-specific settings?"), + QMessageBox::Yes | QMessageBox::No)) { + std::filesystem::remove( + Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / + (m_games[itemID].serial + ".toml")); + } + } + if (selected == &createShortcut) { QString targetPath; Common::FS::PathToQString(targetPath, m_games[itemID].path); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 3f53e46bd..d94470128 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -77,22 +77,48 @@ QMap micMap; int backgroundImageOpacitySlider_backup; int bgm_volume_backup; -int volume_slider_backup; static std::vector m_physical_devices; SettingsDialog::SettingsDialog(std::shared_ptr gui_settings, std::shared_ptr m_compat_info, - QWidget* parent) - : QDialog(parent), ui(new Ui::SettingsDialog), m_gui_settings(std::move(gui_settings)) { + QWidget* parent, bool is_game_specific, std::string gsc_serial) + : QDialog(parent), ui(new Ui::SettingsDialog), m_gui_settings(std::move(gui_settings)), + game_specific(is_game_specific), gs_serial(gsc_serial) { + ui->setupUi(this); ui->tabWidgetSettings->setUsesScrollButtons(false); + initialHeight = this->height(); // Add a small clear "x" button inside the Log Filter input ui->logFilterLineEdit->setClearButtonEnabled(true); - initialHeight = this->height(); - const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + if (game_specific) { + ui->tabWidgetSettings->setTabVisible(5, false); + ui->chooseHomeTabComboBox->removeItem(5); + + ui->label_Trophy->setVisible(false); + ui->trophyKeyLineEdit->setVisible(false); + ui->CompatgroupBox->setVisible(false); + ui->gameSizeCheckBox->setVisible(false); + ui->GUIBackgroundImageGroupBox->setVisible(false); + ui->GUIMusicGroupBox->setVisible(false); + ui->gameSizeCheckBox->setVisible(false); + ui->updaterGroupBox->setVisible(false); + ui->discordRPCCheckbox->setVisible(false); + ui->emulatorLanguageGroupBox->setVisible(false); + } else { + ui->dmaCheckBox->setVisible(false); + ui->devkitCheckBox->setVisible(false); + ui->neoCheckBox->setVisible(false); + ui->networkConnectedCheckBox->setVisible(false); + ui->psnSignInCheckBox->setVisible(false); + } + + std::filesystem::path config_file = + game_specific + ? Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (gs_serial + ".toml") + : Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml"; ui->buttonBox->button(QDialogButtonBox::StandardButton::Close)->setFocus(); @@ -168,25 +194,23 @@ SettingsDialog::SettingsDialog(std::shared_ptr gui_settings, connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, - [this, config_dir](QAbstractButton* button) { + [this, config_file](QAbstractButton* button) { if (button == ui->buttonBox->button(QDialogButtonBox::Save)) { is_saving = true; - UpdateSettings(); - Config::save(config_dir / "config.toml"); + UpdateSettings(game_specific); + Config::save(config_file, game_specific); QWidget::close(); } else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) { - UpdateSettings(); - Config::save(config_dir / "config.toml"); + UpdateSettings(game_specific); + Config::save(config_file, game_specific); } else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) { - Config::setDefaultValues(); setDefaultValues(); - Config::save(config_dir / "config.toml"); + Config::setDefaultValues(game_specific); + Config::save(config_file, game_specific); LoadValuesFromConfig(); } else if (button == ui->buttonBox->button(QDialogButtonBox::Close)) { ui->backgroundImageOpacitySlider->setValue(backgroundImageOpacitySlider_backup); emit BackgroundOpacityChanged(backgroundImageOpacitySlider_backup); - ui->horizontalVolumeSlider->setValue(volume_slider_backup); - Config::setVolumeSlider(volume_slider_backup); ui->BGMVolumeSlider->setValue(bgm_volume_backup); BackgroundMusicPlayer::getInstance().setVolume(bgm_volume_backup); SyncRealTimeWidgetstoConfig(); @@ -210,7 +234,7 @@ SettingsDialog::SettingsDialog(std::shared_ptr gui_settings, { connect(ui->horizontalVolumeSlider, &QSlider::valueChanged, this, [this](int value) { VolumeSliderChange(value); - Config::setVolumeSlider(value); + Config::setVolumeSlider(value, game_specific); Libraries::AudioOut::AdjustVol(); }); @@ -278,9 +302,6 @@ SettingsDialog::SettingsDialog(std::shared_ptr gui_settings, connect(ui->BGMVolumeSlider, &QSlider::valueChanged, this, [](int value) { BackgroundMusicPlayer::getInstance().setVolume(value); }); - connect(ui->chooseHomeTabComboBox, &QComboBox::currentTextChanged, this, - [](const QString& hometab) { Config::setChooseHomeTab(hometab.toStdString()); }); - #if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) connect(ui->showBackgroundImageCheckBox, &QCheckBox::stateChanged, this, [this](int state) { #else @@ -508,64 +529,125 @@ void SettingsDialog::closeEvent(QCloseEvent* event) { if (!is_saving) { ui->backgroundImageOpacitySlider->setValue(backgroundImageOpacitySlider_backup); emit BackgroundOpacityChanged(backgroundImageOpacitySlider_backup); - ui->horizontalVolumeSlider->setValue(volume_slider_backup); - Config::setVolumeSlider(volume_slider_backup); ui->BGMVolumeSlider->setValue(bgm_volume_backup); BackgroundMusicPlayer::getInstance().setVolume(bgm_volume_backup); + SyncRealTimeWidgetstoConfig(); } QDialog::closeEvent(event); } void SettingsDialog::LoadValuesFromConfig() { + std::filesystem::path config_file; + config_file = + game_specific + ? Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (gs_serial + ".toml") + : Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml"; - std::filesystem::path userdir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); std::error_code error; - if (!std::filesystem::exists(userdir / "config.toml", error)) { - Config::load(userdir / "config.toml"); - return; + bool is_newly_created = false; + if (!std::filesystem::exists(config_file, error)) { + Config::save(config_file, game_specific); + is_newly_created = true; } try { std::ifstream ifs; ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); - const toml::value data = toml::parse(userdir / "config.toml"); + const toml::value data = toml::parse(config_file); } catch (std::exception& ex) { fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what()); return; } - const toml::value data = toml::parse(userdir / "config.toml"); + const toml::value data = toml::parse(config_file); const QVector languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9, 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 30, 28}; - const auto save_data_path = Config::GetSaveDataPath(); - QString save_data_path_string; - Common::FS::PathToQString(save_data_path_string, save_data_path); - ui->currentSaveDataPath->setText(save_data_path_string); + // Entries with no game-specific settings + if (!game_specific) { + const auto save_data_path = Config::GetSaveDataPath(); + QString save_data_path_string; + Common::FS::PathToQString(save_data_path_string, save_data_path); + ui->currentSaveDataPath->setText(save_data_path_string); - const auto dlc_folder_path = Config::getAddonInstallDir(); - QString dlc_folder_path_string; - Common::FS::PathToQString(dlc_folder_path_string, dlc_folder_path); - ui->currentDLCFolder->setText(dlc_folder_path_string); + const auto dlc_folder_path = Config::getAddonInstallDir(); + QString dlc_folder_path_string; + Common::FS::PathToQString(dlc_folder_path_string, dlc_folder_path); + ui->currentDLCFolder->setText(dlc_folder_path_string); + ui->emulatorLanguageComboBox->setCurrentIndex( + languages[m_gui_settings->GetValue(gui::gen_guiLanguage).toString().toStdString()]); + + ui->playBGMCheckBox->setChecked( + m_gui_settings->GetValue(gui::gl_playBackgroundMusic).toBool()); + + ui->RCASSlider->setValue(toml::find_or(data, "GPU", "rcasAttenuation", 250)); + ui->RCASValue->setText(QString::number(ui->RCASSlider->value() / 1000.0, 'f', 3)); + + ui->BGMVolumeSlider->setValue( + m_gui_settings->GetValue(gui::gl_backgroundMusicVolume).toInt()); + ui->discordRPCCheckbox->setChecked( + toml::find_or(data, "General", "enableDiscordRPC", true)); + + ui->gameSizeCheckBox->setChecked( + toml::find_or(data, "GUI", "loadGameSizeEnabled", true)); + ui->trophyKeyLineEdit->setText( + QString::fromStdString(toml::find_or(data, "Keys", "TrophyKey", ""))); + ui->trophyKeyLineEdit->setEchoMode(QLineEdit::Password); + ui->enableCompatibilityCheckBox->setChecked( + toml::find_or(data, "General", "compatibilityEnabled", false)); + ui->checkCompatibilityOnStartupCheckBox->setChecked( + toml::find_or(data, "General", "checkCompatibilityOnStartup", false)); + + ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty()); + ui->backgroundImageOpacitySlider->setValue( + m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt()); + ui->showBackgroundImageCheckBox->setChecked( + m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool()); + + backgroundImageOpacitySlider_backup = + m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt(); + bgm_volume_backup = m_gui_settings->GetValue(gui::gl_backgroundMusicVolume).toInt(); + +#ifdef ENABLE_UPDATER + ui->updateCheckBox->setChecked(m_gui_settings->GetValue(gui::gen_checkForUpdates).toBool()); + ui->changelogCheckBox->setChecked( + m_gui_settings->GetValue(gui::gen_showChangeLog).toBool()); + + QString updateChannel = m_gui_settings->GetValue(gui::gen_updateChannel).toString(); + ui->updateComboBox->setCurrentText( + channelMap.key(updateChannel != "Release" && updateChannel != "Nightly" + ? (Common::g_is_release ? "Release" : "Nightly") + : updateChannel)); +#endif + + SyncRealTimeWidgetstoConfig(); + } + + // Entries with game-specific settings, *load these from toml file, not from Config::get* ui->consoleLanguageComboBox->setCurrentIndex( std::distance(languageIndexes.begin(), std::find(languageIndexes.begin(), languageIndexes.end(), toml::find_or(data, "Settings", "consoleLanguage", 6))) % languageIndexes.size()); - ui->emulatorLanguageComboBox->setCurrentIndex( - languages[m_gui_settings->GetValue(gui::gen_guiLanguage).toString().toStdString()]); - ui->hideCursorComboBox->setCurrentIndex(toml::find_or(data, "Input", "cursorState", 1)); - OnCursorStateChanged(toml::find_or(data, "Input", "cursorState", 1)); - ui->idleTimeoutSpinBox->setValue(toml::find_or(data, "Input", "cursorHideTimeout", 5)); - QString micValue = QString::fromStdString(Config::getMicDevice()); + std::string micDevice = + toml::find_or(data, "Input", "micDevice", "Default Device"); + QString micValue = QString::fromStdString(micDevice); int micIndex = ui->micComboBox->findData(micValue); if (micIndex != -1) { ui->micComboBox->setCurrentIndex(micIndex); } else { ui->micComboBox->setCurrentIndex(0); } + + ui->dmaCheckBox->setChecked(toml::find_or(data, "GPU", "directMemoryAccess", false)); + ui->neoCheckBox->setChecked(toml::find_or(data, "General", "isPS4Pro", false)); + ui->devkitCheckBox->setChecked(toml::find_or(data, "General", "isDevKit", false)); + ui->networkConnectedCheckBox->setChecked( + toml::find_or(data, "General", "isConnectedToNetwork", false)); + ui->psnSignInCheckBox->setChecked(toml::find_or(data, "General", "isPSNSignedIn", false)); + // First options is auto selection -1, so gpuId on the GUI will always have to subtract 1 // when setting and add 1 when getting to select the correct gpu in Qt ui->graphicsAdapterBox->setCurrentIndex(toml::find_or(data, "Vulkan", "gpuId", -1) + 1); @@ -577,35 +659,43 @@ void SettingsDialog::LoadValuesFromConfig() { ui->enableHDRCheckBox->setChecked(toml::find_or(data, "GPU", "allowHDR", false)); ui->FSRCheckBox->setChecked(toml::find_or(data, "GPU", "fsrEnabled", true)); ui->RCASCheckBox->setChecked(toml::find_or(data, "GPU", "rcasEnabled", true)); - ui->RCASSlider->setValue(toml::find_or(data, "GPU", "rcasAttenuation", 500)); + ui->RCASSlider->setValue(toml::find_or(data, "GPU", "rcasAttenuation", 250)); ui->RCASValue->setText(QString::number(ui->RCASSlider->value() / 1000.0, 'f', 3)); - - ui->playBGMCheckBox->setChecked(m_gui_settings->GetValue(gui::gl_playBackgroundMusic).toBool()); ui->disableTrophycheckBox->setChecked( toml::find_or(data, "General", "isTrophyPopupDisabled", false)); - ui->popUpDurationSpinBox->setValue(Config::getTrophyNotificationDuration()); - - QString side = QString::fromStdString(Config::sideTrophy()); + ui->popUpDurationSpinBox->setValue( + toml::find_or(data, "General", "trophyNotificationDuration", 6.0)); + ui->showSplashCheckBox->setChecked(toml::find_or(data, "General", "showSplash", false)); + ui->hideCursorComboBox->setCurrentIndex(toml::find_or(data, "Input", "cursorState", 1)); + OnCursorStateChanged(toml::find_or(data, "Input", "cursorState", 1)); + ui->idleTimeoutSpinBox->setValue(toml::find_or(data, "Input", "cursorHideTimeout", 5)); + ui->motionControlsCheckBox->setChecked( + toml::find_or(data, "Input", "isMotionControlsEnabled", true)); + ui->backgroundControllerCheckBox->setChecked( + toml::find_or(data, "Input", "backgroundControllerInput", false)); + std::string sideTrophy = toml::find_or(data, "General", "sideTrophy", "right"); + QString side = QString::fromStdString(sideTrophy); ui->radioButton_Left->setChecked(side == "left"); ui->radioButton_Right->setChecked(side == "right"); ui->radioButton_Top->setChecked(side == "top"); ui->radioButton_Bottom->setChecked(side == "bottom"); - ui->BGMVolumeSlider->setValue(m_gui_settings->GetValue(gui::gl_backgroundMusicVolume).toInt()); - ui->horizontalVolumeSlider->setValue(m_gui_settings->GetValue(gui::gl_VolumeSlider).toInt()); + ui->horizontalVolumeSlider->setValue(toml::find_or(data, "General", "volumeSlider", 100)); ui->volumeText->setText(QString::number(ui->horizontalVolumeSlider->sliderPosition()) + "%"); - ui->discordRPCCheckbox->setChecked( - toml::find_or(data, "General", "enableDiscordRPC", true)); + + std::string fullScreenMode = + toml::find_or(data, "General", "FullscreenMode", "Windowed"); QString translatedText_FullscreenMode = - screenModeMap.key(QString::fromStdString(Config::getFullscreenMode())); + screenModeMap.key(QString::fromStdString(fullScreenMode)); ui->displayModeComboBox->setCurrentText(translatedText_FullscreenMode); - QString translatedText_PresentMode = - presentModeMap.key(QString::fromStdString(Config::getPresentMode())); + + std::string presentMode = toml::find_or(data, "GPU", "presentMode", "Mailbox"); + QString translatedText_PresentMode = presentModeMap.key(QString::fromStdString(presentMode)); ui->presentModeComboBox->setCurrentText(translatedText_PresentMode); - ui->gameSizeCheckBox->setChecked(toml::find_or(data, "GUI", "loadGameSizeEnabled", true)); - ui->showSplashCheckBox->setChecked(toml::find_or(data, "General", "showSplash", false)); - QString translatedText_logType = logTypeMap.key(QString::fromStdString(Config::getLogType())); + + std::string logType = toml::find_or(data, "General", "logType", "sync"); + QString translatedText_logType = logTypeMap.key(QString::fromStdString(logType)); if (!translatedText_logType.isEmpty()) { ui->logTypeComboBox->setCurrentText(translatedText_logType); } @@ -613,9 +703,7 @@ void SettingsDialog::LoadValuesFromConfig() { QString::fromStdString(toml::find_or(data, "General", "logFilter", ""))); ui->userNameLineEdit->setText( QString::fromStdString(toml::find_or(data, "General", "userName", "shadPS4"))); - ui->trophyKeyLineEdit->setText( - QString::fromStdString(toml::find_or(data, "Keys", "TrophyKey", ""))); - ui->trophyKeyLineEdit->setEchoMode(QLineEdit::Password); + ui->debugDump->setChecked(toml::find_or(data, "Debug", "DebugDump", false)); ui->separateLogFilesCheckbox->setChecked( toml::find_or(data, "Debug", "isSeparateLogFilesEnabled", false)); @@ -636,21 +724,6 @@ void SettingsDialog::LoadValuesFromConfig() { ui->enableLoggingCheckBox->setChecked(toml::find_or(data, "Debug", "logEnabled", true)); ui->readbackLinearImagesCheckBox->setChecked( toml::find_or(data, "GPU", "readbackLinearImages", false)); - ui->enableCompatibilityCheckBox->setChecked( - toml::find_or(data, "General", "compatibilityEnabled", false)); - ui->checkCompatibilityOnStartupCheckBox->setChecked( - toml::find_or(data, "General", "checkCompatibilityOnStartup", false)); - -#ifdef ENABLE_UPDATER - ui->updateCheckBox->setChecked(m_gui_settings->GetValue(gui::gen_checkForUpdates).toBool()); - ui->changelogCheckBox->setChecked(m_gui_settings->GetValue(gui::gen_showChangeLog).toBool()); - - QString updateChannel = m_gui_settings->GetValue(gui::gen_updateChannel).toString(); - ui->updateComboBox->setCurrentText( - channelMap.key(updateChannel != "Release" && updateChannel != "Nightly" - ? (Common::g_is_release ? "Release" : "Nightly") - : updateChannel)); -#endif std::string chooseHomeTab = toml::find_or(data, "General", "chooseHomeTab", "General"); @@ -663,26 +736,9 @@ void SettingsDialog::LoadValuesFromConfig() { QStringList tabNames = {tr("General"), tr("GUI"), tr("Graphics"), tr("User"), tr("Input"), tr("Paths"), tr("Log"), tr("Debug")}; int indexTab = tabNames.indexOf(translatedText); - if (indexTab == -1) + if (indexTab == -1 || !ui->tabWidgetSettings->isTabVisible(indexTab) || is_newly_created) indexTab = 0; ui->tabWidgetSettings->setCurrentIndex(indexTab); - - ui->motionControlsCheckBox->setChecked( - toml::find_or(data, "Input", "isMotionControlsEnabled", true)); - ui->backgroundControllerCheckBox->setChecked( - toml::find_or(data, "Input", "backgroundControllerInput", false)); - - ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty()); - SyncRealTimeWidgetstoConfig(); - ui->backgroundImageOpacitySlider->setValue( - m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt()); - ui->showBackgroundImageCheckBox->setChecked( - m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool()); - - backgroundImageOpacitySlider_backup = - m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt(); - bgm_volume_backup = m_gui_settings->GetValue(gui::gl_backgroundMusicVolume).toInt(); - volume_slider_backup = m_gui_settings->GetValue(gui::gl_VolumeSlider).toInt(); } void SettingsDialog::InitializeEmulatorLanguages() { @@ -908,166 +964,203 @@ bool SettingsDialog::eventFilter(QObject* obj, QEvent* event) { return QDialog::eventFilter(obj, event); } -void SettingsDialog::UpdateSettings() { +void SettingsDialog::UpdateSettings(bool game_specific) { + // Entries that are only in the game-specific gui - Config::setIsFullscreen(screenModeMap.value(ui->displayModeComboBox->currentText()) != - "Windowed"); + if (game_specific) { + Config::setDirectMemoryAccess(ui->dmaCheckBox->isChecked(), true); + Config::setDevKitConsole(ui->devkitCheckBox->isChecked(), true); + Config::setNeoMode(ui->neoCheckBox->isChecked(), true); + Config::setConnectedToNetwork(ui->networkConnectedCheckBox->isChecked(), true); + Config::setPSNSignedIn(ui->psnSignInCheckBox->isChecked(), true); + } + + // Entries with game-specific settings, needs the game-specific arg + Config::setIsFullscreen( + screenModeMap.value(ui->displayModeComboBox->currentText()) != "Windowed", game_specific); Config::setFullscreenMode( - screenModeMap.value(ui->displayModeComboBox->currentText()).toStdString()); + screenModeMap.value(ui->displayModeComboBox->currentText()).toStdString(), game_specific); Config::setPresentMode( - presentModeMap.value(ui->presentModeComboBox->currentText()).toStdString()); - Config::setIsMotionControlsEnabled(ui->motionControlsCheckBox->isChecked()); - Config::setBackgroundControllerInput(ui->backgroundControllerCheckBox->isChecked()); - Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked()); - Config::setTrophyNotificationDuration(ui->popUpDurationSpinBox->value()); + presentModeMap.value(ui->presentModeComboBox->currentText()).toStdString(), game_specific); + Config::setIsMotionControlsEnabled(ui->motionControlsCheckBox->isChecked(), game_specific); + Config::setBackgroundControllerInput(ui->backgroundControllerCheckBox->isChecked(), + game_specific); + Config::setisTrophyPopupDisabled(ui->disableTrophycheckBox->isChecked(), game_specific); + Config::setTrophyNotificationDuration(ui->popUpDurationSpinBox->value(), game_specific); if (ui->radioButton_Top->isChecked()) { - Config::setSideTrophy("top"); + Config::setSideTrophy("top", game_specific); } else if (ui->radioButton_Left->isChecked()) { - Config::setSideTrophy("left"); + Config::setSideTrophy("left", game_specific); } else if (ui->radioButton_Right->isChecked()) { - Config::setSideTrophy("right"); + Config::setSideTrophy("right", game_specific); } else if (ui->radioButton_Bottom->isChecked()) { - Config::setSideTrophy("bottom"); + Config::setSideTrophy("bottom", game_specific); } - m_gui_settings->SetValue(gui::gl_playBackgroundMusic, ui->playBGMCheckBox->isChecked()); - Config::setLoggingEnabled(ui->enableLoggingCheckBox->isChecked()); - Config::setAllowHDR(ui->enableHDRCheckBox->isChecked()); - Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString()); - Config::setMicDevice(ui->micComboBox->currentData().toString().toStdString()); - Config::setLogFilter(ui->logFilterLineEdit->text().toStdString()); - Config::setUserName(ui->userNameLineEdit->text().toStdString()); - Config::setTrophyKey(ui->trophyKeyLineEdit->text().toStdString()); - Config::setCursorState(ui->hideCursorComboBox->currentIndex()); - Config::setCursorHideTimeout(ui->idleTimeoutSpinBox->value()); - Config::setGpuId(ui->graphicsAdapterBox->currentIndex() - 1); - m_gui_settings->SetValue(gui::gl_VolumeSlider, ui->horizontalVolumeSlider->value()); - m_gui_settings->SetValue(gui::gl_backgroundMusicVolume, ui->BGMVolumeSlider->value()); + + Config::setLoggingEnabled(ui->enableLoggingCheckBox->isChecked(), game_specific); + Config::setAllowHDR(ui->enableHDRCheckBox->isChecked(), game_specific); + Config::setLogType(logTypeMap.value(ui->logTypeComboBox->currentText()).toStdString(), + game_specific); + Config::setMicDevice(ui->micComboBox->currentData().toString().toStdString(), game_specific); + Config::setLogFilter(ui->logFilterLineEdit->text().toStdString(), game_specific); + Config::setUserName(ui->userNameLineEdit->text().toStdString(), game_specific); + Config::setCursorState(ui->hideCursorComboBox->currentIndex(), game_specific); + Config::setCursorHideTimeout(ui->hideCursorComboBox->currentIndex(), game_specific); + Config::setGpuId(ui->graphicsAdapterBox->currentIndex() - 1, game_specific); + Config::setVolumeSlider(ui->horizontalVolumeSlider->value(), game_specific); Config::setLanguage(languageIndexes[ui->consoleLanguageComboBox->currentIndex()]); - Config::setEnableDiscordRPC(ui->discordRPCCheckbox->isChecked()); - Config::setWindowWidth(ui->widthSpinBox->value()); - Config::setWindowHeight(ui->heightSpinBox->value()); - Config::setVblankFreq(ui->vblankSpinBox->value()); - Config::setDumpShaders(ui->dumpShadersCheckBox->isChecked()); - Config::setNullGpu(ui->nullGpuCheckBox->isChecked()); - Config::setFsrEnabled(ui->FSRCheckBox->isChecked()); - Config::setRcasEnabled(ui->RCASCheckBox->isChecked()); - Config::setRcasAttenuation(ui->RCASSlider->value()); - Config::setLoadGameSizeEnabled(ui->gameSizeCheckBox->isChecked()); - Config::setShowSplash(ui->showSplashCheckBox->isChecked()); - Config::setDebugDump(ui->debugDump->isChecked()); - Config::setSeparateLogFilesEnabled(ui->separateLogFilesCheckbox->isChecked()); - Config::setVkValidation(ui->vkValidationCheckBox->isChecked()); - Config::setVkSyncValidation(ui->vkSyncValidationCheckBox->isChecked()); - Config::setRdocEnabled(ui->rdocCheckBox->isChecked()); - Config::setVkHostMarkersEnabled(ui->hostMarkersCheckBox->isChecked()); - Config::setVkGuestMarkersEnabled(ui->guestMarkersCheckBox->isChecked()); - Config::setVkCrashDiagnosticEnabled(ui->crashDiagnosticsCheckBox->isChecked()); - Config::setReadbacks(ui->readbacksCheckBox->isChecked()); - Config::setReadbackLinearImages(ui->readbackLinearImagesCheckBox->isChecked()); - Config::setCollectShaderForDebug(ui->collectShaderCheckBox->isChecked()); - Config::setCopyGPUCmdBuffers(ui->copyGPUBuffersCheckBox->isChecked()); - m_gui_settings->SetValue(gui::gen_checkForUpdates, ui->updateCheckBox->isChecked()); - m_gui_settings->SetValue(gui::gen_showChangeLog, ui->changelogCheckBox->isChecked()); - m_gui_settings->SetValue(gui::gen_updateChannel, - channelMap.value(ui->updateComboBox->currentText())); + Config::setWindowWidth(ui->widthSpinBox->value(), game_specific); + Config::setWindowHeight(ui->heightSpinBox->value(), game_specific); + Config::setVblankFreq(ui->vblankSpinBox->value(), game_specific); + Config::setDumpShaders(ui->dumpShadersCheckBox->isChecked(), game_specific); + Config::setNullGpu(ui->nullGpuCheckBox->isChecked(), game_specific); + Config::setFsrEnabled(ui->FSRCheckBox->isChecked(), game_specific); + Config::setRcasEnabled(ui->RCASCheckBox->isChecked(), game_specific); + Config::setRcasAttenuation(ui->RCASSlider->value(), game_specific); + Config::setShowSplash(ui->showSplashCheckBox->isChecked(), game_specific); + Config::setDebugDump(ui->debugDump->isChecked(), game_specific); + Config::setSeparateLogFilesEnabled(ui->separateLogFilesCheckbox->isChecked(), game_specific); + Config::setVkValidation(ui->vkValidationCheckBox->isChecked(), game_specific); + Config::setVkSyncValidation(ui->vkSyncValidationCheckBox->isChecked(), game_specific); + Config::setRdocEnabled(ui->rdocCheckBox->isChecked(), game_specific); + Config::setVkHostMarkersEnabled(ui->hostMarkersCheckBox->isChecked(), game_specific); + Config::setVkGuestMarkersEnabled(ui->guestMarkersCheckBox->isChecked(), game_specific); + Config::setVkCrashDiagnosticEnabled(ui->crashDiagnosticsCheckBox->isChecked(), game_specific); + Config::setReadbacks(ui->readbacksCheckBox->isChecked(), game_specific); + Config::setReadbackLinearImages(ui->readbackLinearImagesCheckBox->isChecked(), game_specific); + Config::setCollectShaderForDebug(ui->collectShaderCheckBox->isChecked(), game_specific); + Config::setCopyGPUCmdBuffers(ui->copyGPUBuffersCheckBox->isChecked(), game_specific); Config::setChooseHomeTab( - chooseHomeTabMap.value(ui->chooseHomeTabComboBox->currentText()).toStdString()); - Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked()); - Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked()); - m_gui_settings->SetValue(gui::gl_backgroundImageOpacity, - std::clamp(ui->backgroundImageOpacitySlider->value(), 0, 100)); - emit BackgroundOpacityChanged(ui->backgroundImageOpacitySlider->value()); - m_gui_settings->SetValue(gui::gl_showBackgroundImage, - ui->showBackgroundImageCheckBox->isChecked()); + chooseHomeTabMap.value(ui->chooseHomeTabComboBox->currentText()).toStdString(), + game_specific); - std::vector dirs_with_states; - for (int i = 0; i < ui->gameFoldersListWidget->count(); i++) { - QListWidgetItem* item = ui->gameFoldersListWidget->item(i); - QString path_string = item->text(); - auto path = Common::FS::PathFromQString(path_string); - bool enabled = (item->checkState() == Qt::Checked); + // Entries with no game-specific settings + if (!game_specific) { + std::vector dirs_with_states; + for (int i = 0; i < ui->gameFoldersListWidget->count(); i++) { + QListWidgetItem* item = ui->gameFoldersListWidget->item(i); + QString path_string = item->text(); + auto path = Common::FS::PathFromQString(path_string); + bool enabled = (item->checkState() == Qt::Checked); - dirs_with_states.push_back({path, enabled}); - } - Config::setAllGameInstallDirs(dirs_with_states); + dirs_with_states.push_back({path, enabled}); + } + Config::setAllGameInstallDirs(dirs_with_states); + + BackgroundMusicPlayer::getInstance().setVolume(ui->BGMVolumeSlider->value()); #ifdef ENABLE_DISCORD_RPC - auto* rpc = Common::Singleton::Instance(); - if (Config::getEnableDiscordRPC()) { - rpc->init(); - rpc->setStatusIdling(); - } else { - rpc->shutdown(); - } + auto* rpc = Common::Singleton::Instance(); + if (Config::getEnableDiscordRPC()) { + rpc->init(); + rpc->setStatusIdling(); + } else { + rpc->shutdown(); + } #endif - BackgroundMusicPlayer::getInstance().setVolume(ui->BGMVolumeSlider->value()); - Config::setVolumeSlider(ui->horizontalVolumeSlider->value()); + Config::setLoadGameSizeEnabled(ui->gameSizeCheckBox->isChecked()); + Config::setTrophyKey(ui->trophyKeyLineEdit->text().toStdString()); + Config::setEnableDiscordRPC(ui->discordRPCCheckbox->isChecked()); + Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked()); + Config::setCheckCompatibilityOnStartup( + ui->checkCompatibilityOnStartupCheckBox->isChecked()); + m_gui_settings->SetValue(gui::gl_playBackgroundMusic, ui->playBGMCheckBox->isChecked()); + m_gui_settings->SetValue(gui::gl_backgroundMusicVolume, ui->BGMVolumeSlider->value()); + m_gui_settings->SetValue(gui::gen_checkForUpdates, ui->updateCheckBox->isChecked()); + m_gui_settings->SetValue(gui::gen_showChangeLog, ui->changelogCheckBox->isChecked()); + m_gui_settings->SetValue(gui::gen_updateChannel, + channelMap.value(ui->updateComboBox->currentText())); + m_gui_settings->SetValue(gui::gl_showBackgroundImage, + ui->showBackgroundImageCheckBox->isChecked()); + m_gui_settings->SetValue(gui::gl_backgroundImageOpacity, + std::clamp(ui->backgroundImageOpacitySlider->value(), 0, 100)); + emit BackgroundOpacityChanged(ui->backgroundImageOpacitySlider->value()); + } } void SettingsDialog::SyncRealTimeWidgetstoConfig() { - ui->gameFoldersListWidget->clear(); - std::filesystem::path userdir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); const toml::value data = toml::parse(userdir / "config.toml"); - if (data.contains("GUI")) { - const toml::value& gui = data.at("GUI"); - const auto install_dir_array = - toml::find_or>(gui, "installDirs", {}); + if (!game_specific) { + ui->gameFoldersListWidget->clear(); + if (data.contains("GUI")) { + const toml::value& gui = data.at("GUI"); + const auto install_dir_array = + toml::find_or>(gui, "installDirs", {}); - std::vector install_dirs_enabled; - try { - install_dirs_enabled = Config::getGameInstallDirsEnabled(); - } catch (...) { - // If it does not exist, assume that all are enabled. - install_dirs_enabled.resize(install_dir_array.size(), true); + std::vector install_dirs_enabled; + try { + install_dirs_enabled = Config::getGameInstallDirsEnabled(); + } catch (...) { + // If it does not exist, assume that all are enabled. + install_dirs_enabled.resize(install_dir_array.size(), true); + } + + if (install_dirs_enabled.size() < install_dir_array.size()) { + install_dirs_enabled.resize(install_dir_array.size(), true); + } + + std::vector settings_install_dirs_config; + + for (size_t i = 0; i < install_dir_array.size(); i++) { + std::filesystem::path dir = install_dir_array[i]; + bool enabled = install_dirs_enabled[i]; + + settings_install_dirs_config.push_back({dir, enabled}); + + QString path_string; + Common::FS::PathToQString(path_string, dir); + + QListWidgetItem* item = new QListWidgetItem(path_string); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked); + ui->gameFoldersListWidget->addItem(item); + } + + Config::setAllGameInstallDirs(settings_install_dirs_config); } - - if (install_dirs_enabled.size() < install_dir_array.size()) { - install_dirs_enabled.resize(install_dir_array.size(), true); - } - - std::vector settings_install_dirs_config; - - for (size_t i = 0; i < install_dir_array.size(); i++) { - std::filesystem::path dir = install_dir_array[i]; - bool enabled = install_dirs_enabled[i]; - - settings_install_dirs_config.push_back({dir, enabled}); - - QString path_string; - Common::FS::PathToQString(path_string, dir); - - QListWidgetItem* item = new QListWidgetItem(path_string); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked); - ui->gameFoldersListWidget->addItem(item); - } - - Config::setAllGameInstallDirs(settings_install_dirs_config); } + toml::value gs_data; + game_specific + ? gs_data = toml::parse(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / + (gs_serial + ".toml")) + : gs_data = data; + + int sliderValue = toml::find_or(gs_data, "General", "volumeSlider", 100); + ui->horizontalVolumeSlider->setValue(sliderValue); + + // Since config::set can be called for volume slider (connected to the widget) outside the save + // function, need to null it out if GS GUI is closed without saving + game_specific ? Config::resetGameSpecificValue("volumeSlider") + : Config::setVolumeSlider(sliderValue); + if (presenter) { - presenter->GetFsrSettingsRef().enable = Config::getFsrEnabled(); - presenter->GetFsrSettingsRef().use_rcas = Config::getRcasEnabled(); + presenter->GetFsrSettingsRef().enable = + toml::find_or(gs_data, "GPU", "fsrEnabled", true); + presenter->GetFsrSettingsRef().use_rcas = + toml::find_or(gs_data, "GPU", "rcasEnabled", true); presenter->GetFsrSettingsRef().rcas_attenuation = - static_cast(Config::getRcasAttenuation() / 1000.f); + static_cast(toml::find_or(gs_data, "GPU", "rcasAttenuation", 250) / 1000.f); } } + void SettingsDialog::setDefaultValues() { - m_gui_settings->SetValue(gui::gl_showBackgroundImage, true); - m_gui_settings->SetValue(gui::gl_backgroundImageOpacity, 50); - m_gui_settings->SetValue(gui::gl_playBackgroundMusic, false); - m_gui_settings->SetValue(gui::gl_backgroundMusicVolume, 50); - m_gui_settings->SetValue(gui::gl_VolumeSlider, 100); - m_gui_settings->SetValue(gui::gen_checkForUpdates, false); - m_gui_settings->SetValue(gui::gen_showChangeLog, false); - if (Common::g_is_release) { - m_gui_settings->SetValue(gui::gen_updateChannel, "Release"); - } else { - m_gui_settings->SetValue(gui::gen_updateChannel, "Nightly"); + if (!game_specific) { + m_gui_settings->SetValue(gui::gl_showBackgroundImage, true); + m_gui_settings->SetValue(gui::gl_backgroundImageOpacity, 50); + m_gui_settings->SetValue(gui::gl_playBackgroundMusic, false); + m_gui_settings->SetValue(gui::gl_backgroundMusicVolume, 50); + m_gui_settings->SetValue(gui::gen_checkForUpdates, false); + m_gui_settings->SetValue(gui::gen_showChangeLog, false); + if (Common::g_is_release) { + m_gui_settings->SetValue(gui::gen_updateChannel, "Release"); + } else { + m_gui_settings->SetValue(gui::gen_updateChannel, "Nightly"); + } + m_gui_settings->SetValue(gui::gen_guiLanguage, "en_US"); } - m_gui_settings->SetValue(gui::gen_guiLanguage, "en_US"); } diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index 38c4a7688..ace401258 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -23,7 +23,8 @@ class SettingsDialog : public QDialog { public: explicit SettingsDialog(std::shared_ptr gui_settings, std::shared_ptr m_compat_info, - QWidget* parent = nullptr); + QWidget* parent = nullptr, bool is_game_specific = false, + std::string gsc_serial = ""); ~SettingsDialog(); bool eventFilter(QObject* obj, QEvent* event) override; @@ -38,7 +39,7 @@ signals: private: void LoadValuesFromConfig(); - void UpdateSettings(); + void UpdateSettings(bool game_specific = false); void SyncRealTimeWidgetstoConfig(); void InitializeEmulatorLanguages(); void OnLanguageChanged(int index); @@ -55,5 +56,8 @@ private: int initialHeight; bool is_saving = false; + bool game_specific; + std::string gs_serial; + std::shared_ptr m_gui_settings; }; diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index c85230eee..64914597b 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,7 +12,7 @@ 0 0 970 - 750 + 765 @@ -59,7 +59,7 @@ - 6 + 7 @@ -74,7 +74,7 @@ 0 0 946 - 486 + 501 @@ -452,8 +452,8 @@ 0 0 - 400 - 60 + 402 + 68 @@ -539,7 +539,7 @@ 0 0 946 - 486 + 501 @@ -999,7 +999,7 @@ 0 0 946 - 486 + 501 @@ -1406,7 +1406,7 @@ 0 0 946 - 486 + 501 @@ -1648,7 +1648,7 @@ 0 0 946 - 486 + 501 @@ -1920,7 +1920,7 @@ 0 0 946 - 486 + 501 @@ -2235,7 +2235,7 @@ 0 0 946 - 382 + 495 @@ -2244,17 +2244,11 @@ 0 - + - - - 0 - - - 0 - + - + @@ -2318,10 +2312,6 @@ - - - - @@ -2389,94 +2379,136 @@ - - - - - - 0 - - + Experimental Features + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + - - - 0 + + + Enable Readbacks - - - - 0 - - - - - Enable Readbacks - - - - - - - Enable Readback Linear Images - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 11 - true - - - - WARNING: These features are experimental and should not be enabled unless you were told to, or a game requires it. Please ask in the shadPS4 Discord server if you have any questions. - - - false - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - true - - - 0 - - - -1 - - - - + + + + + + Enable Readback Linear Images + + + + + + + Enable Direct Memory Access + + + + + + + Enable Devkit Console Mode + + + + + + + Enable PS4 Pro Mode + + + + + + + Set "PSN signed-in" to True + + + + + + + Set "Network Connected" to True + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 11 + true + + + + WARNING: These features are experimental and should not be enabled unless you were told to, or a game requires it. Please ask in the shadPS4 Discord server if you have any questions. + + + false + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + true + + + 20 + + + -1 + + + + + + + Qt::Orientation::Vertical + + + + 20 + 5 + + + + diff --git a/src/shadps4.qrc b/src/shadps4.qrc index 2fd9c786a..d2cb14750 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -38,5 +38,6 @@ images/favorite_icon.png images/trophy_icon.png images/hotkey.png + images/game_settings.png