mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-09 13:19:00 +00:00
1432 lines
46 KiB
C++
1432 lines
46 KiB
C++
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <fstream>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <fmt/core.h>
|
|
#include <fmt/xchar.h> // for wstring support
|
|
#include <toml.hpp>
|
|
|
|
#include "common/assert.h"
|
|
#include "common/config.h"
|
|
#include "common/logging/formatter.h"
|
|
#include "common/path_util.h"
|
|
#include "common/scm_rev.h"
|
|
|
|
using std::nullopt;
|
|
using std::optional;
|
|
using std::string;
|
|
|
|
namespace toml {
|
|
template <typename TC, typename K>
|
|
std::filesystem::path find_fs_path_or(const basic_value<TC>& v, const K& ky,
|
|
std::filesystem::path opt) {
|
|
try {
|
|
auto str = find<string>(v, ky);
|
|
if (str.empty()) {
|
|
return opt;
|
|
}
|
|
std::u8string u8str{(char8_t*)&str.front(), (char8_t*)&str.back() + 1};
|
|
return std::filesystem::path{u8str};
|
|
} catch (...) {
|
|
return opt;
|
|
}
|
|
}
|
|
|
|
// why is it so hard to avoid exceptions with this library
|
|
template <typename T>
|
|
std::optional<T> get_optional(const toml::value& v, const std::string& key) {
|
|
if (!v.is_table())
|
|
return std::nullopt;
|
|
const auto& tbl = v.as_table();
|
|
auto it = tbl.find(key);
|
|
if (it == tbl.end())
|
|
return std::nullopt;
|
|
|
|
if constexpr (std::is_same_v<T, int>) {
|
|
if (it->second.is_integer()) {
|
|
return static_cast<int>(toml::get<int>(it->second));
|
|
}
|
|
} else if constexpr (std::is_same_v<T, unsigned int>) {
|
|
if (it->second.is_integer()) {
|
|
return static_cast<u32>(toml::get<unsigned int>(it->second));
|
|
}
|
|
} else if constexpr (std::is_same_v<T, double>) {
|
|
if (it->second.is_floating()) {
|
|
return toml::get<double>(it->second);
|
|
}
|
|
} else if constexpr (std::is_same_v<T, std::string>) {
|
|
if (it->second.is_string()) {
|
|
return toml::get<std::string>(it->second);
|
|
}
|
|
} else if constexpr (std::is_same_v<T, bool>) {
|
|
if (it->second.is_boolean()) {
|
|
return toml::get<bool>(it->second);
|
|
}
|
|
} else {
|
|
static_assert([] { return false; }(), "Unsupported type in get_optional<T>");
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
} // namespace toml
|
|
|
|
namespace Config {
|
|
|
|
ConfigMode config_mode = ConfigMode::Default;
|
|
|
|
void setConfigMode(ConfigMode mode) {
|
|
config_mode = mode;
|
|
}
|
|
|
|
template <typename T>
|
|
class ConfigEntry {
|
|
public:
|
|
const T default_value;
|
|
T base_value;
|
|
optional<T> game_specific_value;
|
|
ConfigEntry(const T& t = T()) : default_value(t), base_value(t), game_specific_value(nullopt) {}
|
|
ConfigEntry operator=(const T& t) {
|
|
base_value = t;
|
|
return *this;
|
|
}
|
|
const T get() const {
|
|
switch (config_mode) {
|
|
case ConfigMode::Default:
|
|
return game_specific_value.value_or(base_value);
|
|
case ConfigMode::Global:
|
|
return base_value;
|
|
case ConfigMode::Clean:
|
|
return default_value;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
void setFromToml(const toml::value& v, const std::string& key, bool is_game_specific = false) {
|
|
if (is_game_specific) {
|
|
game_specific_value = toml::get_optional<T>(v, key);
|
|
} else {
|
|
base_value = toml::get_optional<T>(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();
|
|
// }
|
|
};
|
|
|
|
// General
|
|
static ConfigEntry<int> volumeSlider(100);
|
|
static ConfigEntry<bool> isNeo(false);
|
|
static ConfigEntry<bool> isDevKit(false);
|
|
static ConfigEntry<int> extraDmemInMbytes(0);
|
|
static ConfigEntry<bool> isPSNSignedIn(false);
|
|
static ConfigEntry<bool> isTrophyPopupDisabled(false);
|
|
static ConfigEntry<double> trophyNotificationDuration(6.0);
|
|
static ConfigEntry<string> logFilter("");
|
|
static ConfigEntry<string> logType("sync");
|
|
static ConfigEntry<string> userName("shadPS4");
|
|
static ConfigEntry<string> chooseHomeTab("General");
|
|
static ConfigEntry<bool> isShowSplash(false);
|
|
static ConfigEntry<string> isSideTrophy("right");
|
|
static ConfigEntry<bool> isConnectedToNetwork(false);
|
|
static bool enableDiscordRPC = false;
|
|
static bool checkCompatibilityOnStartup = false;
|
|
static bool compatibilityData = false;
|
|
static std::filesystem::path sys_modules_path = {};
|
|
|
|
// Input
|
|
static ConfigEntry<int> cursorState(HideCursorState::Idle);
|
|
static ConfigEntry<int> cursorHideTimeout(5); // 5 seconds (default)
|
|
static ConfigEntry<bool> useSpecialPad(false);
|
|
static ConfigEntry<int> specialPadClass(1);
|
|
static ConfigEntry<bool> isMotionControlsEnabled(true);
|
|
static ConfigEntry<bool> useUnifiedInputConfig(true);
|
|
static ConfigEntry<string> defaultControllerID("");
|
|
static ConfigEntry<bool> backgroundControllerInput(false);
|
|
|
|
// Audio
|
|
static ConfigEntry<string> micDevice("Default Device");
|
|
static ConfigEntry<string> mainOutputDevice("Default Device");
|
|
static ConfigEntry<string> padSpkOutputDevice("Default Device");
|
|
|
|
// GPU
|
|
static ConfigEntry<u32> windowWidth(1280);
|
|
static ConfigEntry<u32> windowHeight(720);
|
|
static ConfigEntry<u32> internalScreenWidth(1280);
|
|
static ConfigEntry<u32> internalScreenHeight(720);
|
|
static ConfigEntry<bool> isNullGpu(false);
|
|
static ConfigEntry<bool> shouldCopyGPUBuffers(false);
|
|
static ConfigEntry<bool> readbacksEnabled(false);
|
|
static ConfigEntry<bool> readbackLinearImagesEnabled(false);
|
|
static ConfigEntry<bool> directMemoryAccessEnabled(false);
|
|
static ConfigEntry<bool> shouldDumpShaders(false);
|
|
static ConfigEntry<bool> shouldPatchShaders(false);
|
|
static ConfigEntry<u32> vblankFrequency(60);
|
|
static ConfigEntry<bool> isFullscreen(false);
|
|
static ConfigEntry<string> fullscreenMode("Windowed");
|
|
static ConfigEntry<string> presentMode("Mailbox");
|
|
static ConfigEntry<bool> isHDRAllowed(false);
|
|
static ConfigEntry<bool> fsrEnabled(true);
|
|
static ConfigEntry<bool> rcasEnabled(true);
|
|
static ConfigEntry<int> rcasAttenuation(250);
|
|
|
|
// Vulkan
|
|
static ConfigEntry<s32> gpuId(-1);
|
|
static ConfigEntry<bool> vkValidation(false);
|
|
static ConfigEntry<bool> vkValidationCore(true);
|
|
static ConfigEntry<bool> vkValidationSync(false);
|
|
static ConfigEntry<bool> vkValidationGpu(false);
|
|
static ConfigEntry<bool> vkCrashDiagnostic(false);
|
|
static ConfigEntry<bool> vkHostMarkers(false);
|
|
static ConfigEntry<bool> vkGuestMarkers(false);
|
|
static ConfigEntry<bool> rdocEnable(false);
|
|
|
|
// Debug
|
|
static ConfigEntry<bool> isDebugDump(false);
|
|
static ConfigEntry<bool> isShaderDebug(false);
|
|
static ConfigEntry<bool> isSeparateLogFilesEnabled(false);
|
|
static ConfigEntry<bool> isFpsColor(true);
|
|
static ConfigEntry<bool> logEnabled(true);
|
|
|
|
// GUI
|
|
static bool load_game_size = true;
|
|
static std::vector<GameInstallDir> settings_install_dirs = {};
|
|
std::vector<bool> install_dirs_enabled = {};
|
|
std::filesystem::path settings_addon_install_dir = {};
|
|
std::filesystem::path save_data_path = {};
|
|
|
|
// Settings
|
|
ConfigEntry<u32> m_language(1); // english
|
|
|
|
// Keys
|
|
static string trophyKey = "";
|
|
|
|
// Config version, used to determine if a user's config file is outdated.
|
|
static string config_version = Common::g_scm_rev;
|
|
|
|
// These two entries aren't stored in the config
|
|
static bool overrideControllerColor = false;
|
|
static int controllerCustomColorRGB[3] = {0, 0, 255};
|
|
|
|
std::filesystem::path getSysModulesPath() {
|
|
if (sys_modules_path.empty()) {
|
|
return Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir);
|
|
}
|
|
return sys_modules_path;
|
|
}
|
|
|
|
void setSysModulesPath(const std::filesystem::path& path) {
|
|
sys_modules_path = path;
|
|
}
|
|
|
|
int getVolumeSlider() {
|
|
return volumeSlider.get();
|
|
}
|
|
bool allowHDR() {
|
|
return isHDRAllowed.get();
|
|
}
|
|
|
|
bool GetUseUnifiedInputConfig() {
|
|
return useUnifiedInputConfig.get();
|
|
}
|
|
|
|
void SetUseUnifiedInputConfig(bool use) {
|
|
useUnifiedInputConfig.base_value = use;
|
|
}
|
|
|
|
bool GetOverrideControllerColor() {
|
|
return overrideControllerColor;
|
|
}
|
|
|
|
void SetOverrideControllerColor(bool enable) {
|
|
overrideControllerColor = enable;
|
|
}
|
|
|
|
int* GetControllerCustomColor() {
|
|
return controllerCustomColorRGB;
|
|
}
|
|
|
|
bool getLoggingEnabled() {
|
|
return logEnabled.get();
|
|
}
|
|
|
|
void SetControllerCustomColor(int r, int b, int g) {
|
|
controllerCustomColorRGB[0] = r;
|
|
controllerCustomColorRGB[1] = b;
|
|
controllerCustomColorRGB[2] = g;
|
|
}
|
|
|
|
string getTrophyKey() {
|
|
return trophyKey;
|
|
}
|
|
|
|
void setTrophyKey(string key) {
|
|
trophyKey = key;
|
|
}
|
|
|
|
bool GetLoadGameSizeEnabled() {
|
|
return load_game_size;
|
|
}
|
|
|
|
std::filesystem::path GetSaveDataPath() {
|
|
if (save_data_path.empty()) {
|
|
return Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "savedata";
|
|
}
|
|
return save_data_path;
|
|
}
|
|
|
|
void setVolumeSlider(int volumeValue, bool is_game_specific) {
|
|
volumeSlider.set(volumeValue, is_game_specific);
|
|
}
|
|
|
|
void setLoadGameSizeEnabled(bool enable) {
|
|
load_game_size = enable;
|
|
}
|
|
|
|
bool isNeoModeConsole() {
|
|
return isNeo.get();
|
|
}
|
|
|
|
bool isDevKitConsole() {
|
|
return isDevKit.get();
|
|
}
|
|
|
|
int getExtraDmemInMbytes() {
|
|
return extraDmemInMbytes.get();
|
|
}
|
|
|
|
void setExtraDmemInMbytes(int value) {
|
|
extraDmemInMbytes.base_value = 0;
|
|
}
|
|
|
|
bool getIsFullscreen() {
|
|
return isFullscreen.get();
|
|
}
|
|
|
|
string getFullscreenMode() {
|
|
return fullscreenMode.get();
|
|
}
|
|
|
|
std::string getPresentMode() {
|
|
return presentMode.get();
|
|
}
|
|
|
|
bool getisTrophyPopupDisabled() {
|
|
return isTrophyPopupDisabled.get();
|
|
}
|
|
|
|
bool getEnableDiscordRPC() {
|
|
return enableDiscordRPC;
|
|
}
|
|
|
|
s16 getCursorState() {
|
|
return cursorState.get();
|
|
}
|
|
|
|
int getCursorHideTimeout() {
|
|
return cursorHideTimeout.get();
|
|
}
|
|
|
|
string getMicDevice() {
|
|
return micDevice.get();
|
|
}
|
|
|
|
std::string getMainOutputDevice() {
|
|
return mainOutputDevice.get();
|
|
}
|
|
|
|
std::string getPadSpkOutputDevice() {
|
|
return padSpkOutputDevice.get();
|
|
}
|
|
|
|
double getTrophyNotificationDuration() {
|
|
return trophyNotificationDuration.get();
|
|
}
|
|
|
|
u32 getWindowWidth() {
|
|
return windowWidth.get();
|
|
}
|
|
|
|
u32 getWindowHeight() {
|
|
return windowHeight.get();
|
|
}
|
|
|
|
u32 getInternalScreenWidth() {
|
|
return internalScreenHeight.get();
|
|
}
|
|
|
|
u32 getInternalScreenHeight() {
|
|
return internalScreenHeight.get();
|
|
}
|
|
|
|
s32 getGpuId() {
|
|
return gpuId.get();
|
|
}
|
|
|
|
string getLogFilter() {
|
|
return logFilter.get();
|
|
}
|
|
|
|
string getLogType() {
|
|
return logType.get();
|
|
}
|
|
|
|
string getUserName() {
|
|
return userName.get();
|
|
}
|
|
|
|
string getChooseHomeTab() {
|
|
return chooseHomeTab.get();
|
|
}
|
|
|
|
bool getUseSpecialPad() {
|
|
return useSpecialPad.get();
|
|
}
|
|
|
|
int getSpecialPadClass() {
|
|
return specialPadClass.get();
|
|
}
|
|
|
|
bool getIsMotionControlsEnabled() {
|
|
return isMotionControlsEnabled.get();
|
|
}
|
|
|
|
bool debugDump() {
|
|
return isDebugDump.get();
|
|
}
|
|
|
|
bool collectShadersForDebug() {
|
|
return isShaderDebug.get();
|
|
}
|
|
|
|
bool showSplash() {
|
|
return isShowSplash.get();
|
|
}
|
|
|
|
string sideTrophy() {
|
|
return isSideTrophy.get();
|
|
}
|
|
|
|
bool nullGpu() {
|
|
return isNullGpu.get();
|
|
}
|
|
|
|
bool copyGPUCmdBuffers() {
|
|
return shouldCopyGPUBuffers.get();
|
|
}
|
|
|
|
bool readbacks() {
|
|
return readbacksEnabled.get();
|
|
}
|
|
|
|
bool readbackLinearImages() {
|
|
return readbackLinearImagesEnabled.get();
|
|
}
|
|
|
|
bool directMemoryAccess() {
|
|
return directMemoryAccessEnabled.get();
|
|
}
|
|
|
|
bool dumpShaders() {
|
|
return shouldDumpShaders.get();
|
|
}
|
|
|
|
bool patchShaders() {
|
|
return shouldPatchShaders.get();
|
|
}
|
|
|
|
bool isRdocEnabled() {
|
|
return rdocEnable.get();
|
|
}
|
|
|
|
bool fpsColor() {
|
|
return isFpsColor.get();
|
|
}
|
|
|
|
bool isLoggingEnabled() {
|
|
return logEnabled.get();
|
|
}
|
|
|
|
u32 vblankFreq() {
|
|
if (vblankFrequency.get() < 60) {
|
|
vblankFrequency = 60;
|
|
}
|
|
return vblankFrequency.get();
|
|
}
|
|
|
|
bool vkValidationEnabled() {
|
|
return vkValidation.get();
|
|
}
|
|
|
|
bool vkValidationCoreEnabled() {
|
|
return vkValidationCore.get();
|
|
}
|
|
|
|
bool vkValidationSyncEnabled() {
|
|
return vkValidationSync.get();
|
|
}
|
|
|
|
bool vkValidationGpuEnabled() {
|
|
return vkValidationGpu.get();
|
|
}
|
|
|
|
bool getVkCrashDiagnosticEnabled() {
|
|
return vkCrashDiagnostic.get();
|
|
}
|
|
|
|
bool getVkHostMarkersEnabled() {
|
|
return vkHostMarkers.get();
|
|
}
|
|
|
|
bool getVkGuestMarkersEnabled() {
|
|
return vkGuestMarkers.get();
|
|
}
|
|
|
|
void setVkCrashDiagnosticEnabled(bool enable, bool is_game_specific) {
|
|
vkCrashDiagnostic.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setVkHostMarkersEnabled(bool enable, bool is_game_specific) {
|
|
vkHostMarkers.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setVkGuestMarkersEnabled(bool enable, bool is_game_specific) {
|
|
vkGuestMarkers.set(enable, is_game_specific);
|
|
}
|
|
|
|
bool getCompatibilityEnabled() {
|
|
return compatibilityData;
|
|
}
|
|
|
|
bool getCheckCompatibilityOnStartup() {
|
|
return checkCompatibilityOnStartup;
|
|
}
|
|
|
|
bool getIsConnectedToNetwork() {
|
|
return isConnectedToNetwork.get();
|
|
}
|
|
|
|
void setConnectedToNetwork(bool enable, bool is_game_specific) {
|
|
isConnectedToNetwork.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setGpuId(s32 selectedGpuId, bool is_game_specific) {
|
|
gpuId.set(selectedGpuId, is_game_specific);
|
|
}
|
|
|
|
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) {
|
|
internalScreenWidth.base_value = width;
|
|
}
|
|
|
|
void setInternalScreenHeight(u32 height) {
|
|
internalScreenHeight.base_value = height;
|
|
}
|
|
|
|
void setDebugDump(bool enable, bool is_game_specific) {
|
|
isDebugDump.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setLoggingEnabled(bool enable, bool is_game_specific) {
|
|
logEnabled.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setCollectShaderForDebug(bool enable, bool is_game_specific) {
|
|
isShaderDebug.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setShowSplash(bool enable, bool is_game_specific) {
|
|
isShowSplash.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setSideTrophy(string side, bool is_game_specific) {
|
|
isSideTrophy.set(side, is_game_specific);
|
|
}
|
|
|
|
void setNullGpu(bool enable, bool is_game_specific) {
|
|
isNullGpu.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setAllowHDR(bool enable, bool is_game_specific) {
|
|
isHDRAllowed.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setCopyGPUCmdBuffers(bool enable, bool is_game_specific) {
|
|
shouldCopyGPUBuffers.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setReadbacks(bool enable, bool is_game_specific) {
|
|
readbacksEnabled.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setReadbackLinearImages(bool enable, bool is_game_specific) {
|
|
readbackLinearImagesEnabled.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setDirectMemoryAccess(bool enable, bool is_game_specific) {
|
|
directMemoryAccessEnabled.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setDumpShaders(bool enable, bool is_game_specific) {
|
|
shouldDumpShaders.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setVkValidation(bool enable, bool is_game_specific) {
|
|
vkValidation.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setVkSyncValidation(bool enable, bool is_game_specific) {
|
|
vkValidationSync.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setRdocEnabled(bool enable, bool is_game_specific) {
|
|
rdocEnable.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setVblankFreq(u32 value, bool is_game_specific) {
|
|
vblankFrequency.set(value, is_game_specific);
|
|
}
|
|
|
|
void setIsFullscreen(bool enable, bool is_game_specific) {
|
|
isFullscreen.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setFullscreenMode(string mode, bool is_game_specific) {
|
|
fullscreenMode.set(mode, is_game_specific);
|
|
}
|
|
|
|
void setPresentMode(std::string mode, bool is_game_specific) {
|
|
presentMode.set(mode, is_game_specific);
|
|
}
|
|
|
|
void setisTrophyPopupDisabled(bool disable, bool is_game_specific) {
|
|
isTrophyPopupDisabled.set(disable, is_game_specific);
|
|
}
|
|
|
|
void setEnableDiscordRPC(bool enable) {
|
|
enableDiscordRPC = enable;
|
|
}
|
|
|
|
void setCursorState(s16 newCursorState, bool is_game_specific) {
|
|
cursorState.set(newCursorState, is_game_specific);
|
|
}
|
|
|
|
void setCursorHideTimeout(int newcursorHideTimeout, bool is_game_specific) {
|
|
cursorHideTimeout.set(newcursorHideTimeout, is_game_specific);
|
|
}
|
|
|
|
void setMicDevice(std::string device, bool is_game_specific) {
|
|
micDevice.set(device, is_game_specific);
|
|
}
|
|
|
|
void setMainOutputDevice(std::string device, bool is_game_specific) {
|
|
mainOutputDevice.set(device, is_game_specific);
|
|
}
|
|
|
|
void setPadSpkOutputDevice(std::string device, bool is_game_specific) {
|
|
padSpkOutputDevice.set(device, is_game_specific);
|
|
}
|
|
|
|
void setTrophyNotificationDuration(double newTrophyNotificationDuration, bool is_game_specific) {
|
|
trophyNotificationDuration.set(newTrophyNotificationDuration, is_game_specific);
|
|
}
|
|
|
|
void setLanguage(u32 language, bool is_game_specific) {
|
|
m_language.set(language, is_game_specific);
|
|
}
|
|
|
|
void setNeoMode(bool enable, bool is_game_specific) {
|
|
isNeo.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setDevKitConsole(bool enable, bool is_game_specific) {
|
|
isDevKit.set(enable, is_game_specific);
|
|
}
|
|
|
|
void setLogType(const string& type, bool is_game_specific) {
|
|
logType.set(type, is_game_specific);
|
|
}
|
|
|
|
void setLogFilter(const string& type, bool is_game_specific) {
|
|
logFilter.set(type, is_game_specific);
|
|
}
|
|
|
|
void setSeparateLogFilesEnabled(bool enabled, bool is_game_specific) {
|
|
isSeparateLogFilesEnabled.set(enabled, is_game_specific);
|
|
}
|
|
|
|
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) {
|
|
useSpecialPad.base_value = use;
|
|
}
|
|
|
|
void setSpecialPadClass(int type) {
|
|
specialPadClass.base_value = type;
|
|
}
|
|
|
|
void setIsMotionControlsEnabled(bool use, bool is_game_specific) {
|
|
isMotionControlsEnabled.set(use, is_game_specific);
|
|
}
|
|
|
|
void setCompatibilityEnabled(bool use) {
|
|
compatibilityData = use;
|
|
}
|
|
|
|
void setCheckCompatibilityOnStartup(bool use) {
|
|
checkCompatibilityOnStartup = use;
|
|
}
|
|
|
|
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled) {
|
|
for (const auto& install_dir : settings_install_dirs) {
|
|
if (install_dir.path == dir) {
|
|
return false;
|
|
}
|
|
}
|
|
settings_install_dirs.push_back({dir, enabled});
|
|
return true;
|
|
}
|
|
|
|
void removeGameInstallDir(const std::filesystem::path& dir) {
|
|
auto iterator =
|
|
std::find_if(settings_install_dirs.begin(), settings_install_dirs.end(),
|
|
[&dir](const GameInstallDir& install_dir) { return install_dir.path == dir; });
|
|
if (iterator != settings_install_dirs.end()) {
|
|
settings_install_dirs.erase(iterator);
|
|
}
|
|
}
|
|
|
|
void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled) {
|
|
auto iterator =
|
|
std::find_if(settings_install_dirs.begin(), settings_install_dirs.end(),
|
|
[&dir](const GameInstallDir& install_dir) { return install_dir.path == dir; });
|
|
if (iterator != settings_install_dirs.end()) {
|
|
iterator->enabled = enabled;
|
|
}
|
|
}
|
|
|
|
void setAddonInstallDir(const std::filesystem::path& dir) {
|
|
settings_addon_install_dir = dir;
|
|
}
|
|
|
|
void setGameInstallDirs(const std::vector<std::filesystem::path>& dirs_config) {
|
|
settings_install_dirs.clear();
|
|
for (const auto& dir : dirs_config) {
|
|
settings_install_dirs.push_back({dir, true});
|
|
}
|
|
}
|
|
|
|
void setAllGameInstallDirs(const std::vector<GameInstallDir>& dirs_config) {
|
|
settings_install_dirs = dirs_config;
|
|
}
|
|
|
|
void setSaveDataPath(const std::filesystem::path& path) {
|
|
save_data_path = path;
|
|
}
|
|
|
|
const std::vector<std::filesystem::path> getGameInstallDirs() {
|
|
std::vector<std::filesystem::path> enabled_dirs;
|
|
for (const auto& dir : settings_install_dirs) {
|
|
if (dir.enabled) {
|
|
enabled_dirs.push_back(dir.path);
|
|
}
|
|
}
|
|
return enabled_dirs;
|
|
}
|
|
|
|
const std::vector<bool> getGameInstallDirsEnabled() {
|
|
std::vector<bool> enabled_dirs;
|
|
for (const auto& dir : settings_install_dirs) {
|
|
enabled_dirs.push_back(dir.enabled);
|
|
}
|
|
return enabled_dirs;
|
|
}
|
|
|
|
std::filesystem::path getAddonInstallDir() {
|
|
if (settings_addon_install_dir.empty()) {
|
|
// Default for users without a config file or a config file from before this option existed
|
|
return Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "addcont";
|
|
}
|
|
return settings_addon_install_dir;
|
|
}
|
|
|
|
u32 GetLanguage() {
|
|
return m_language.get();
|
|
}
|
|
|
|
bool getSeparateLogFilesEnabled() {
|
|
return isSeparateLogFilesEnabled.get();
|
|
}
|
|
|
|
bool getPSNSignedIn() {
|
|
return isPSNSignedIn.get();
|
|
}
|
|
|
|
void setPSNSignedIn(bool sign, bool is_game_specific) {
|
|
isPSNSignedIn.set(sign, is_game_specific);
|
|
}
|
|
|
|
string getDefaultControllerID() {
|
|
return defaultControllerID.get();
|
|
}
|
|
|
|
void setDefaultControllerID(string id) {
|
|
defaultControllerID.base_value = id;
|
|
}
|
|
|
|
bool getBackgroundControllerInput() {
|
|
return backgroundControllerInput.get();
|
|
}
|
|
|
|
void setBackgroundControllerInput(bool enable, bool is_game_specific) {
|
|
backgroundControllerInput.set(enable, is_game_specific);
|
|
}
|
|
|
|
bool getFsrEnabled() {
|
|
return fsrEnabled.get();
|
|
}
|
|
|
|
void setFsrEnabled(bool enable, bool is_game_specific) {
|
|
fsrEnabled.set(enable, is_game_specific);
|
|
}
|
|
|
|
bool getRcasEnabled() {
|
|
return rcasEnabled.get();
|
|
}
|
|
|
|
void setRcasEnabled(bool enable, bool is_game_specific) {
|
|
rcasEnabled.set(enable, is_game_specific);
|
|
}
|
|
|
|
int getRcasAttenuation() {
|
|
return rcasAttenuation.get();
|
|
}
|
|
|
|
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) {
|
|
// If the configuration file does not exist, create it and return, unless it is game specific
|
|
std::error_code error;
|
|
if (!std::filesystem::exists(path, error)) {
|
|
if (!is_game_specific) {
|
|
save(path);
|
|
}
|
|
return;
|
|
}
|
|
|
|
toml::value data;
|
|
|
|
try {
|
|
std::ifstream ifs;
|
|
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
|
ifs.open(path, std::ios_base::binary);
|
|
data = toml::parse(ifs, string{fmt::UTF(path.filename().u8string()).data});
|
|
} catch (std::exception& ex) {
|
|
fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what());
|
|
return;
|
|
}
|
|
|
|
if (data.contains("General")) {
|
|
const toml::value& general = data.at("General");
|
|
|
|
volumeSlider.setFromToml(general, "volumeSlider", is_game_specific);
|
|
isNeo.setFromToml(general, "isPS4Pro", is_game_specific);
|
|
isDevKit.setFromToml(general, "isDevKit", is_game_specific);
|
|
if (is_game_specific) { // do not get this value from the base config
|
|
extraDmemInMbytes.setFromToml(general, "extraDmemInMbytes", is_game_specific);
|
|
}
|
|
isPSNSignedIn.setFromToml(general, "isPSNSignedIn", is_game_specific);
|
|
isTrophyPopupDisabled.setFromToml(general, "isTrophyPopupDisabled", is_game_specific);
|
|
trophyNotificationDuration.setFromToml(general, "trophyNotificationDuration",
|
|
is_game_specific);
|
|
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", enableDiscordRPC);
|
|
logFilter.setFromToml(general, "logFilter", is_game_specific);
|
|
logType.setFromToml(general, "logType", is_game_specific);
|
|
userName.setFromToml(general, "userName", is_game_specific);
|
|
isShowSplash.setFromToml(general, "showSplash", is_game_specific);
|
|
isSideTrophy.setFromToml(general, "sideTrophy", is_game_specific);
|
|
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", compatibilityData);
|
|
checkCompatibilityOnStartup = toml::find_or<bool>(general, "checkCompatibilityOnStartup",
|
|
checkCompatibilityOnStartup);
|
|
|
|
isConnectedToNetwork.setFromToml(general, "isConnectedToNetwork", is_game_specific);
|
|
chooseHomeTab.setFromToml(general, "chooseHomeTab", is_game_specific);
|
|
defaultControllerID.setFromToml(general, "defaultControllerID", is_game_specific);
|
|
sys_modules_path = toml::find_fs_path_or(general, "sysModulesPath", sys_modules_path);
|
|
}
|
|
|
|
if (data.contains("Input")) {
|
|
const toml::value& input = data.at("Input");
|
|
|
|
cursorState.setFromToml(input, "cursorState", is_game_specific);
|
|
cursorHideTimeout.setFromToml(input, "cursorHideTimeout", is_game_specific);
|
|
useSpecialPad.setFromToml(input, "useSpecialPad", is_game_specific);
|
|
specialPadClass.setFromToml(input, "specialPadClass", is_game_specific);
|
|
isMotionControlsEnabled.setFromToml(input, "isMotionControlsEnabled", is_game_specific);
|
|
useUnifiedInputConfig.setFromToml(input, "useUnifiedInputConfig", is_game_specific);
|
|
backgroundControllerInput.setFromToml(input, "backgroundControllerInput", is_game_specific);
|
|
}
|
|
|
|
if (data.contains("Audio")) {
|
|
const toml::value& audio = data.at("Audio");
|
|
|
|
micDevice.setFromToml(audio, "micDevice", is_game_specific);
|
|
mainOutputDevice.setFromToml(audio, "mainOutputDevice", is_game_specific);
|
|
padSpkOutputDevice.setFromToml(audio, "padSpkOutputDevice", is_game_specific);
|
|
}
|
|
|
|
if (data.contains("GPU")) {
|
|
const toml::value& gpu = data.at("GPU");
|
|
|
|
windowWidth.setFromToml(gpu, "screenWidth", is_game_specific);
|
|
windowHeight.setFromToml(gpu, "screenHeight", is_game_specific);
|
|
internalScreenWidth.setFromToml(gpu, "internalScreenWidth", is_game_specific);
|
|
internalScreenHeight.setFromToml(gpu, "internalScreenHeight", is_game_specific);
|
|
isNullGpu.setFromToml(gpu, "nullGpu", is_game_specific);
|
|
shouldCopyGPUBuffers.setFromToml(gpu, "copyGPUBuffers", is_game_specific);
|
|
readbacksEnabled.setFromToml(gpu, "readbacks", is_game_specific);
|
|
readbackLinearImagesEnabled.setFromToml(gpu, "readbackLinearImages", is_game_specific);
|
|
directMemoryAccessEnabled.setFromToml(gpu, "directMemoryAccess", is_game_specific);
|
|
shouldDumpShaders.setFromToml(gpu, "dumpShaders", is_game_specific);
|
|
shouldPatchShaders.setFromToml(gpu, "patchShaders", is_game_specific);
|
|
vblankFrequency.setFromToml(gpu, "vblankFrequency", is_game_specific);
|
|
isFullscreen.setFromToml(gpu, "Fullscreen", is_game_specific);
|
|
fullscreenMode.setFromToml(gpu, "FullscreenMode", is_game_specific);
|
|
presentMode.setFromToml(gpu, "presentMode", is_game_specific);
|
|
isHDRAllowed.setFromToml(gpu, "allowHDR", is_game_specific);
|
|
fsrEnabled.setFromToml(gpu, "fsrEnabled", is_game_specific);
|
|
rcasEnabled.setFromToml(gpu, "rcasEnabled", is_game_specific);
|
|
rcasAttenuation.setFromToml(gpu, "rcasAttenuation", is_game_specific);
|
|
}
|
|
|
|
if (data.contains("Vulkan")) {
|
|
const toml::value& vk = data.at("Vulkan");
|
|
|
|
gpuId.setFromToml(vk, "gpuId", is_game_specific);
|
|
vkValidation.setFromToml(vk, "validation", is_game_specific);
|
|
vkValidationCore.setFromToml(vk, "validation_core", is_game_specific);
|
|
vkValidationSync.setFromToml(vk, "validation_sync", is_game_specific);
|
|
vkValidationGpu.setFromToml(vk, "validation_gpu", is_game_specific);
|
|
vkCrashDiagnostic.setFromToml(vk, "crashDiagnostic", is_game_specific);
|
|
vkHostMarkers.setFromToml(vk, "hostMarkers", is_game_specific);
|
|
vkGuestMarkers.setFromToml(vk, "guestMarkers", is_game_specific);
|
|
rdocEnable.setFromToml(vk, "rdocEnable", is_game_specific);
|
|
}
|
|
|
|
string current_version = {};
|
|
if (data.contains("Debug")) {
|
|
const toml::value& debug = data.at("Debug");
|
|
|
|
isDebugDump.setFromToml(debug, "DebugDump", is_game_specific);
|
|
isSeparateLogFilesEnabled.setFromToml(debug, "isSeparateLogFilesEnabled", is_game_specific);
|
|
isShaderDebug.setFromToml(debug, "CollectShader", is_game_specific);
|
|
isFpsColor.setFromToml(debug, "FPSColor", is_game_specific);
|
|
logEnabled.setFromToml(debug, "logEnabled", is_game_specific);
|
|
current_version = toml::find_or<std::string>(debug, "ConfigVersion", current_version);
|
|
}
|
|
|
|
if (data.contains("GUI")) {
|
|
const toml::value& gui = data.at("GUI");
|
|
|
|
load_game_size = toml::find_or<bool>(gui, "loadGameSizeEnabled", load_game_size);
|
|
|
|
const auto install_dir_array =
|
|
toml::find_or<std::vector<std::u8string>>(gui, "installDirs", {});
|
|
|
|
try {
|
|
install_dirs_enabled = toml::find<std::vector<bool>>(gui, "installDirsEnabled");
|
|
} 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);
|
|
}
|
|
|
|
settings_install_dirs.clear();
|
|
for (size_t i = 0; i < install_dir_array.size(); i++) {
|
|
settings_install_dirs.push_back(
|
|
{std::filesystem::path{install_dir_array[i]}, install_dirs_enabled[i]});
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (data.contains("Settings")) {
|
|
const toml::value& settings = data.at("Settings");
|
|
m_language.setFromToml(settings, "consoleLanguage", is_game_specific);
|
|
}
|
|
|
|
if (data.contains("Keys")) {
|
|
const toml::value& keys = data.at("Keys");
|
|
trophyKey = toml::find_or<string>(keys, "TrophyKey", trophyKey);
|
|
}
|
|
|
|
// Run save after loading to generate any missing fields with default values.
|
|
if (config_version != current_version && !is_game_specific) {
|
|
save(path);
|
|
}
|
|
}
|
|
|
|
void sortTomlSections(toml::ordered_value& data) {
|
|
toml::ordered_value ordered_data;
|
|
std::vector<string> section_order = {"General", "Input", "GPU", "Vulkan",
|
|
"Debug", "Keys", "GUI", "Settings"};
|
|
|
|
for (const auto& section : section_order) {
|
|
if (data.contains(section)) {
|
|
std::vector<string> keys;
|
|
for (const auto& item : data.at(section).as_table()) {
|
|
keys.push_back(item.first);
|
|
}
|
|
|
|
std::sort(keys.begin(), keys.end(), [](const string& a, const string& b) {
|
|
return std::lexicographical_compare(
|
|
a.begin(), a.end(), b.begin(), b.end(), [](char a_char, char b_char) {
|
|
return std::tolower(a_char) < std::tolower(b_char);
|
|
});
|
|
});
|
|
|
|
toml::ordered_value ordered_section;
|
|
for (const auto& key : keys) {
|
|
ordered_section[key] = data.at(section).at(key);
|
|
}
|
|
|
|
ordered_data[section] = ordered_section;
|
|
}
|
|
}
|
|
|
|
data = ordered_data;
|
|
}
|
|
|
|
void save(const std::filesystem::path& path, bool is_game_specific) {
|
|
toml::ordered_value data;
|
|
|
|
std::error_code error;
|
|
if (std::filesystem::exists(path, error)) {
|
|
try {
|
|
std::ifstream ifs;
|
|
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
|
ifs.open(path, std::ios_base::binary);
|
|
data = toml::parse<toml::ordered_type_config>(
|
|
ifs, string{fmt::UTF(path.filename().u8string()).data});
|
|
} catch (const std::exception& ex) {
|
|
fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what());
|
|
return;
|
|
}
|
|
} else {
|
|
if (error) {
|
|
fmt::print("Filesystem error: {}\n", error.message());
|
|
}
|
|
fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string()));
|
|
}
|
|
|
|
// 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);
|
|
if (is_game_specific) {
|
|
extraDmemInMbytes.setTomlValue(data, "General", "extraDmemInMbytes", is_game_specific);
|
|
}
|
|
isPSNSignedIn.setTomlValue(data, "General", "isPSNSignedIn", is_game_specific);
|
|
isConnectedToNetwork.setTomlValue(data, "General", "isConnectedToNetwork", is_game_specific);
|
|
|
|
cursorState.setTomlValue(data, "Input", "cursorState", is_game_specific);
|
|
cursorHideTimeout.setTomlValue(data, "Input", "cursorHideTimeout", is_game_specific);
|
|
isMotionControlsEnabled.setTomlValue(data, "Input", "isMotionControlsEnabled",
|
|
is_game_specific);
|
|
backgroundControllerInput.setTomlValue(data, "Input", "backgroundControllerInput",
|
|
is_game_specific);
|
|
|
|
micDevice.setTomlValue(data, "Audio", "micDevice", is_game_specific);
|
|
mainOutputDevice.setTomlValue(data, "Audio", "mainOutputDevice", is_game_specific);
|
|
padSpkOutputDevice.setTomlValue(data, "Audio", "padSpkOutputDevice", is_game_specific);
|
|
|
|
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);
|
|
|
|
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", "logEnabled", is_game_specific);
|
|
|
|
m_language.setTomlValue(data, "Settings", "consoleLanguage", is_game_specific);
|
|
|
|
if (!is_game_specific) {
|
|
std::vector<std::string> install_dirs;
|
|
std::vector<bool> install_dirs_enabled;
|
|
|
|
// temporary structure for ordering
|
|
struct DirEntry {
|
|
string path_str;
|
|
bool enabled;
|
|
};
|
|
|
|
std::vector<DirEntry> 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["General"]["sysModulesPath"] = string{fmt::UTF(sys_modules_path.u8string()).data};
|
|
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_core"] = vkValidationCore.base_value;
|
|
data["Vulkan"]["validation_gpu"] = vkValidationGpu.base_value;
|
|
data["Debug"]["FPSColor"] = isFpsColor.base_value;
|
|
}
|
|
|
|
// Sorting of TOML sections
|
|
sortTomlSections(data);
|
|
|
|
std::ofstream file(path, std::ios::binary);
|
|
file << data;
|
|
file.close();
|
|
}
|
|
|
|
void setDefaultValues(bool is_game_specific) {
|
|
|
|
// Entries with game-specific settings that are in the game-specific setings GUI but not in
|
|
// the global settings GUI
|
|
if (is_game_specific) {
|
|
readbacksEnabled.set(false, is_game_specific);
|
|
readbackLinearImagesEnabled.set(false, 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);
|
|
extraDmemInMbytes.set(0, is_game_specific);
|
|
}
|
|
|
|
// 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);
|
|
|
|
// GS - Input
|
|
cursorState.set(HideCursorState::Idle, is_game_specific);
|
|
cursorHideTimeout.set(5, is_game_specific);
|
|
isMotionControlsEnabled.set(true, is_game_specific);
|
|
backgroundControllerInput.set(false, is_game_specific);
|
|
|
|
// GS - Audio
|
|
micDevice.set("Default Device", is_game_specific);
|
|
|
|
// 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);
|
|
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);
|
|
|
|
// GS - Vulkan
|
|
gpuId.set(-1, is_game_specific);
|
|
vkValidation.set(false, is_game_specific);
|
|
vkValidationCore.set(true, 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);
|
|
|
|
// 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;
|
|
controllerCustomColorRGB[0] = 0;
|
|
controllerCustomColorRGB[1] = 0;
|
|
controllerCustomColorRGB[2] = 255;
|
|
|
|
// TODO: Change to be game specific
|
|
mainOutputDevice = "Default Device";
|
|
padSpkOutputDevice = "Default Device";
|
|
|
|
// 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() {
|
|
return R"(# Anything put here will be loaded for all games,
|
|
# alongside the game's config or default.ini depending on your preference.
|
|
|
|
hotkey_renderdoc_capture = f12
|
|
hotkey_fullscreen = f11
|
|
hotkey_show_fps = f10
|
|
hotkey_pause = f9
|
|
hotkey_reload_inputs = f8
|
|
hotkey_toggle_mouse_to_joystick = f7
|
|
hotkey_toggle_mouse_to_gyro = f6
|
|
hotkey_quit = lctrl, lshift, end
|
|
)";
|
|
}
|
|
|
|
constexpr std::string_view GetDefaultInputConfig() {
|
|
return R"(#Feeling lost? Check out the Help section!
|
|
|
|
# Keyboard bindings
|
|
|
|
triangle = kp8
|
|
circle = kp6
|
|
cross = kp2
|
|
square = kp4
|
|
# Alternatives for users without a keypad
|
|
triangle = c
|
|
circle = b
|
|
cross = n
|
|
square = v
|
|
|
|
l1 = q
|
|
r1 = u
|
|
l2 = e
|
|
r2 = o
|
|
l3 = x
|
|
r3 = m
|
|
|
|
options = enter
|
|
touchpad_center = space
|
|
|
|
pad_up = up
|
|
pad_down = down
|
|
pad_left = left
|
|
pad_right = right
|
|
|
|
axis_left_x_minus = a
|
|
axis_left_x_plus = d
|
|
axis_left_y_minus = w
|
|
axis_left_y_plus = s
|
|
|
|
axis_right_x_minus = j
|
|
axis_right_x_plus = l
|
|
axis_right_y_minus = i
|
|
axis_right_y_plus = k
|
|
|
|
# Controller bindings
|
|
|
|
triangle = triangle
|
|
cross = cross
|
|
square = square
|
|
circle = circle
|
|
|
|
l1 = l1
|
|
l2 = l2
|
|
l3 = l3
|
|
r1 = r1
|
|
r2 = r2
|
|
r3 = r3
|
|
|
|
options = options
|
|
touchpad_center = back
|
|
|
|
pad_up = pad_up
|
|
pad_down = pad_down
|
|
pad_left = pad_left
|
|
pad_right = pad_right
|
|
|
|
axis_left_x = axis_left_x
|
|
axis_left_y = axis_left_y
|
|
axis_right_x = axis_right_x
|
|
axis_right_y = axis_right_y
|
|
|
|
# Range of deadzones: 1 (almost none) to 127 (max)
|
|
analog_deadzone = leftjoystick, 2, 127
|
|
analog_deadzone = rightjoystick, 2, 127
|
|
|
|
override_controller_color = false, 0, 0, 255
|
|
)";
|
|
}
|
|
std::filesystem::path GetFoolproofInputConfigFile(const string& game_id) {
|
|
// Read configuration file of the game, and if it doesn't exist, generate it from default
|
|
// If that doesn't exist either, generate that from getDefaultConfig() and try again
|
|
// If even the folder is missing, we start with that.
|
|
|
|
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "input_config";
|
|
const auto config_file = config_dir / (game_id + ".ini");
|
|
const auto default_config_file = config_dir / "default.ini";
|
|
|
|
// Ensure the config directory exists
|
|
if (!std::filesystem::exists(config_dir)) {
|
|
std::filesystem::create_directories(config_dir);
|
|
}
|
|
|
|
// Check if the default config exists
|
|
if (!std::filesystem::exists(default_config_file)) {
|
|
// If the default config is also missing, create it from getDefaultConfig()
|
|
const auto default_config = GetDefaultInputConfig();
|
|
std::ofstream default_config_stream(default_config_file);
|
|
if (default_config_stream) {
|
|
default_config_stream << default_config;
|
|
}
|
|
}
|
|
|
|
// if empty, we only need to execute the function up until this point
|
|
if (game_id.empty()) {
|
|
return default_config_file;
|
|
}
|
|
|
|
// Create global config if it doesn't exist yet
|
|
if (game_id == "global" && !std::filesystem::exists(config_file)) {
|
|
if (!std::filesystem::exists(config_file)) {
|
|
const auto global_config = GetDefaultGlobalConfig();
|
|
std::ofstream global_config_stream(config_file);
|
|
if (global_config_stream) {
|
|
global_config_stream << global_config;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If game-specific config doesn't exist, create it from the default config
|
|
if (!std::filesystem::exists(config_file)) {
|
|
std::filesystem::copy(default_config_file, config_file);
|
|
}
|
|
return config_file;
|
|
}
|
|
|
|
void resetGameSpecificValue(std::string entry) {
|
|
if (entry == "volumeSlider") {
|
|
volumeSlider.game_specific_value = std::nullopt;
|
|
}
|
|
}
|
|
|
|
} // namespace Config
|