mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
4413c7b10f
This option was Windows exclusive and this commit implements it on SDL as well. It is hidden on mobile devices because it makes no sense there.
2112 lines
98 KiB
C++
2112 lines
98 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <mutex>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <thread>
|
|
|
|
#include "ppsspp_config.h"
|
|
|
|
#include "Common/GPU/OpenGL/GLFeatures.h"
|
|
#include "Common/Net/HTTPClient.h"
|
|
#include "Common/Net/URL.h"
|
|
|
|
#include "Common/Log.h"
|
|
#include "Common/TimeUtil.h"
|
|
#include "Common/Thread/ThreadUtil.h"
|
|
#include "Common/Data/Format/JSONReader.h"
|
|
#include "Common/Data/Format/IniFile.h"
|
|
#include "Common/Data/Text/I18n.h"
|
|
#include "Common/Data/Text/Parsers.h"
|
|
#include "Common/CPUDetect.h"
|
|
#include "Common/File/FileUtil.h"
|
|
#include "Common/File/VFS/VFS.h"
|
|
#include "Common/Log/LogManager.h"
|
|
#include "Common/Math/CrossSIMD.h"
|
|
#include "Common/OSVersion.h"
|
|
#include "Common/System/Display.h"
|
|
#include "Common/System/System.h"
|
|
#include "Common/StringUtils.h"
|
|
#include "Common/GPU/Vulkan/VulkanLoader.h"
|
|
#include "Common/VR/PPSSPPVR.h"
|
|
#include "Common/System/Request.h"
|
|
#include "Core/Config.h"
|
|
#include "Core/ConfigSettings.h"
|
|
#include "Core/ConfigValues.h"
|
|
#include "Core/KeyMap.h"
|
|
#include "Core/HLE/sceUtility.h"
|
|
#include "Core/Instance.h"
|
|
#include "Core/Util/RecentFiles.h"
|
|
#include "Core/Util/PathUtil.h"
|
|
|
|
#include "GPU/Common/FramebufferManagerCommon.h"
|
|
|
|
// TODO: Find a better place for this.
|
|
http::RequestManager g_DownloadManager;
|
|
|
|
Config g_Config;
|
|
|
|
static bool jitForcedOff;
|
|
|
|
#ifdef _DEBUG
|
|
static const std::string_view logSectionName = "LogDebug";
|
|
#else
|
|
static const std::string_view logSectionName = "Log";
|
|
#endif
|
|
|
|
std::string GPUBackendToString(GPUBackend backend) {
|
|
switch (backend) {
|
|
case GPUBackend::OPENGL:
|
|
return "OPENGL";
|
|
case GPUBackend::DIRECT3D11:
|
|
return "DIRECT3D11";
|
|
case GPUBackend::VULKAN:
|
|
return "VULKAN";
|
|
}
|
|
// Intentionally not a default so we get a warning.
|
|
return "INVALID";
|
|
}
|
|
|
|
GPUBackend GPUBackendFromString(std::string_view backend) {
|
|
if (equalsNoCase(backend, "OPENGL") || backend == "0")
|
|
return GPUBackend::OPENGL;
|
|
if (equalsNoCase(backend, "DIRECT3D11") || backend == "2")
|
|
return GPUBackend::DIRECT3D11;
|
|
if (equalsNoCase(backend, "VULKAN") || backend == "3")
|
|
return GPUBackend::VULKAN;
|
|
return GPUBackend::OPENGL;
|
|
}
|
|
|
|
std::string DefaultLangRegion() {
|
|
// Unfortunate default. There's no need to use bFirstRun, since this is only a default.
|
|
static std::string defaultLangRegion = "en_US";
|
|
std::string langRegion = System_GetProperty(SYSPROP_LANGREGION);
|
|
if (g_i18nrepo.IniExists(langRegion)) {
|
|
defaultLangRegion = langRegion;
|
|
} else if (langRegion.length() >= 3) {
|
|
// Don't give up. Let's try a fuzzy match - so nl_BE can match nl_NL.
|
|
IniFile mapping;
|
|
mapping.LoadFromVFS(g_VFS, "langregion.ini");
|
|
std::vector<std::string> keys;
|
|
Section *section = mapping.GetSection("LangRegionNames");
|
|
if (section) {
|
|
section->GetKeys(&keys);
|
|
}
|
|
for (const std::string &key : keys) {
|
|
if (startsWithNoCase(key, langRegion)) {
|
|
// Exact submatch, or different case. Let's use it.
|
|
defaultLangRegion = key;
|
|
break;
|
|
} else if (startsWithNoCase(key, langRegion.substr(0, 3))) {
|
|
// Best so far.
|
|
defaultLangRegion = key;
|
|
}
|
|
}
|
|
}
|
|
|
|
return defaultLangRegion;
|
|
}
|
|
|
|
int DefaultDepthRaster() {
|
|
#ifdef CROSSSIMD_SLOW
|
|
// No SIMD acceleration for the depth rasterizer.
|
|
// Default to off.
|
|
return (int)DepthRasterMode::OFF;
|
|
#endif
|
|
|
|
// For 64-bit ARM and x86 with SIMD, enable depth raster.
|
|
#if PPSSPP_ARCH(ARM64_NEON) || PPSSPP_ARCH(SSE2)
|
|
|
|
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
|
|
return (int)DepthRasterMode::LOW_QUALITY;
|
|
#else
|
|
return (int)DepthRasterMode::DEFAULT;
|
|
#endif
|
|
|
|
#else
|
|
|
|
// 32-bit ARM or no SIMD, the depth raster will be too slow.
|
|
return (int)DepthRasterMode::OFF;
|
|
|
|
#endif
|
|
}
|
|
|
|
std::string CreateRandMAC() {
|
|
std::stringstream randStream;
|
|
srand(time(nullptr));
|
|
for (int i = 0; i < 6; i++) {
|
|
u32 value = rand() % 256;
|
|
if (i == 0) {
|
|
// Making sure the 1st 2-bits on the 1st byte of OUI are zero to prevent issue with some games (ie. Gran Turismo)
|
|
value &= 0xfc;
|
|
}
|
|
if (value <= 15)
|
|
randStream << '0' << std::hex << value;
|
|
else
|
|
randStream << std::hex << value;
|
|
if (i < 5) {
|
|
randStream << ':'; //we need a : between every octet
|
|
}
|
|
}
|
|
return randStream.str();
|
|
}
|
|
|
|
static int DefaultCpuCore() {
|
|
#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(RISCV64) || PPSSPP_ARCH(LOONGARCH64)
|
|
if (System_GetPropertyBool(SYSPROP_CAN_JIT))
|
|
return (int)CPUCore::JIT;
|
|
return (int)CPUCore::IR_INTERPRETER;
|
|
#else
|
|
return (int)CPUCore::IR_INTERPRETER;
|
|
#endif
|
|
}
|
|
|
|
static bool DefaultCodeGen() {
|
|
#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(RISCV64) || PPSSPP_ARCH(LOONGARCH64)
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
static bool DefaultEnableStateUndo() {
|
|
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
|
|
// Off on mobile to save disk space.
|
|
return false;
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
static float DefaultGameGridScale() {
|
|
#if PPSSPP_PLATFORM(IOS)
|
|
return 1.25f;
|
|
#else
|
|
return 1.0f;
|
|
#endif
|
|
}
|
|
|
|
static float DefaultUISaturation() {
|
|
return IsVREnabled() ? 1.5f : 1.0f;
|
|
}
|
|
|
|
static int DefaultUIScaleFactor() {
|
|
#if PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(LINUX) || PPSSPP_PLATFORM(MAC)
|
|
return -1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int DefaultScreenRotation() {
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
|
return ROTATION_LOCKED_HORIZONTAL;
|
|
#else
|
|
return ROTATION_AUTO;
|
|
#endif
|
|
}
|
|
|
|
#define SETTING(a, x) &a, &a.x
|
|
#define SETTING_IDX(a, x, i) &a, &a.x[i]
|
|
|
|
// All relative to g_Config.
|
|
static const ConfigSetting generalSettings[] = {
|
|
ConfigSetting("FirstRun", SETTING(g_Config, bFirstRun), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("RunCount", SETTING(g_Config, iRunCount), 0, CfgFlag::DEFAULT),
|
|
ConfigSetting("Enable Logging", SETTING(g_Config, bEnableLogging), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("FileLogging", SETTING(g_Config, bEnableFileLogging), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("AutoRun", SETTING(g_Config, bAutoRun), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("IgnoreBadMemAccess", SETTING(g_Config, bIgnoreBadMemAccess), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("CurrentDirectory", SETTING(g_Config, currentDirectory), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("ShowDebuggerOnLoad", SETTING(g_Config, bShowDebuggerOnLoad), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("ShowImDebugger", SETTING(g_Config, bShowImDebugger), false, CfgFlag::DONT_SAVE),
|
|
ConfigSetting("CheckForNewVersion", SETTING(g_Config, bCheckForNewVersion), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("Language", SETTING(g_Config, sLanguageIni), &DefaultLangRegion, CfgFlag::DEFAULT),
|
|
ConfigSetting("ForceLagSync2", SETTING(g_Config, bForceLagSync), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("DiscordRichPresence", SETTING(g_Config, bDiscordRichPresence), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("UISound", SETTING(g_Config, bUISound), false, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("DisableHTTPS", SETTING(g_Config, bDisableHTTPS), false, CfgFlag::DONT_SAVE),
|
|
ConfigSetting("AutoLoadSaveState", SETTING(g_Config, iAutoLoadSaveState), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("EnableCheats", SETTING(g_Config, bEnableCheats), false, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("EnablePlugins", SETTING(g_Config, bEnablePlugins), true, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("CwCheatRefreshRate", SETTING(g_Config, iCwCheatRefreshIntervalMs), 77, CfgFlag::PER_GAME),
|
|
ConfigSetting("CwCheatScrollPosition", SETTING(g_Config, fCwCheatScrollPosition), 0.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("GameListScrollPosition", SETTING(g_Config, fGameListScrollPosition), 0.0f, CfgFlag::DEFAULT),
|
|
ConfigSetting("HomebrewScrollPosition", SETTING(g_Config, fHomebrewScrollPosition), 0.0f, CfgFlag::DEFAULT),
|
|
ConfigSetting("RemoteScrollPosition", SETTING(g_Config, fRemoteScrollPosition), 0.0f, CfgFlag::DEFAULT),
|
|
ConfigSetting("DebugOverlay", SETTING(g_Config, iDebugOverlay), 0, CfgFlag::DONT_SAVE),
|
|
ConfigSetting("DefaultTab", SETTING(g_Config, iDefaultTab), 0, CfgFlag::DEFAULT),
|
|
ConfigSetting("DisableHLEFlags", SETTING(g_Config, iDisableHLE), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("ForceEnableHLEFlags", SETTING(g_Config, iForceEnableHLE), 0, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("ScreenshotMode", SETTING(g_Config, iScreenshotMode), 0, CfgFlag::DEFAULT),
|
|
ConfigSetting("ScreenshotsAsPNG", SETTING(g_Config, bScreenshotsAsPNG), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("UseFFV1", SETTING(g_Config, bUseFFV1), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("DumpFrames", SETTING(g_Config, bDumpFrames), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("DumpVideoOutput", SETTING(g_Config, bDumpVideoOutput), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("DumpAudio", SETTING(g_Config, bDumpAudio), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("SaveLoadResetsAVdumping", SETTING(g_Config, bSaveLoadResetsAVdumping), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("StateSlot", SETTING(g_Config, iCurrentStateSlot), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("EnableStateUndo", SETTING(g_Config, bEnableStateUndo), &DefaultEnableStateUndo, CfgFlag::PER_GAME),
|
|
ConfigSetting("StateLoadUndoGame", SETTING(g_Config, sStateLoadUndoGame), "NA", CfgFlag::DEFAULT),
|
|
ConfigSetting("StateUndoLastSaveGame", SETTING(g_Config, sStateUndoLastSaveGame), "NA", CfgFlag::DEFAULT),
|
|
ConfigSetting("StateUndoLastSaveSlot", SETTING(g_Config, iStateUndoLastSaveSlot), -5, CfgFlag::DEFAULT), // Start with an "invalid" value
|
|
ConfigSetting("RewindSnapshotInterval", SETTING(g_Config, iRewindSnapshotInterval), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("SaveStateSlotCount", SETTING(g_Config, iSaveStateSlotCount), 5, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("ShowRegionOnGameIcon", SETTING(g_Config, bShowRegionOnGameIcon), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("ShowIDOnGameIcon", SETTING(g_Config, bShowIDOnGameIcon), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("GameGridScale", SETTING(g_Config, fGameGridScale), &DefaultGameGridScale, CfgFlag::DEFAULT),
|
|
ConfigSetting("GridView1", SETTING(g_Config, bGridView1), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("GridView2", SETTING(g_Config, bGridView2), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("GridView3", SETTING(g_Config, bGridView3), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("GridView4", SETTING(g_Config, bGridView4), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("RightAnalogUp", SETTING(g_Config, iRightAnalogUp), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("RightAnalogDown", SETTING(g_Config, iRightAnalogDown), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("RightAnalogLeft", SETTING(g_Config, iRightAnalogLeft), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("RightAnalogRight", SETTING(g_Config, iRightAnalogRight), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("RightAnalogPress", SETTING(g_Config, iRightAnalogPress), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("RightAnalogCustom", SETTING(g_Config, bRightAnalogCustom), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("RightAnalogDisableDiagonal", SETTING(g_Config, bRightAnalogDisableDiagonal), false, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("TouchGliding", SETTING(g_Config, bTouchGliding), false, CfgFlag::PER_GAME),
|
|
|
|
// "default" means let emulator decide, "" means disable.
|
|
ConfigSetting("ReportingHost", SETTING(g_Config, sReportHost), "default", CfgFlag::DEFAULT),
|
|
ConfigSetting("AutoSaveSymbolMap", SETTING(g_Config, bAutoSaveSymbolMap), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("CompressSymbols", SETTING(g_Config, bCompressSymbols), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("CacheFullIsoInRam", SETTING(g_Config, bCacheFullIsoInRam), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("RemoteISOPort", SETTING(g_Config, iRemoteISOPort), 0, CfgFlag::DEFAULT),
|
|
ConfigSetting("LastRemoteISOServer", SETTING(g_Config, sLastRemoteISOServer), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("LastRemoteISOPort", SETTING(g_Config, iLastRemoteISOPort), 0, CfgFlag::DEFAULT),
|
|
ConfigSetting("RemoteISOManualConfig", SETTING(g_Config, bRemoteISOManual), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("RemoteShareOnStartup", SETTING(g_Config, bRemoteShareOnStartup), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("RemoteISOSubdir", SETTING(g_Config, sRemoteISOSubdir), "/", CfgFlag::DEFAULT),
|
|
ConfigSetting("RemoteDebuggerOnStartup", SETTING(g_Config, bRemoteDebuggerOnStartup), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("RemoteDebuggerLocal", SETTING(g_Config, bRemoteDebuggerLocal), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("RemoteTab", SETTING(g_Config, bRemoteTab), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("RemoteISOSharedDir", SETTING(g_Config, sRemoteISOSharedDir), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("RemoteISOShareType", SETTING(g_Config, iRemoteISOShareType), (int)RemoteISOShareType::RECENT, CfgFlag::DEFAULT),
|
|
ConfigSetting("AskForExitConfirmationAfterSeconds", SETTING(g_Config, iAskForExitConfirmationAfterSeconds), 300, CfgFlag::PER_GAME),
|
|
|
|
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
|
|
ConfigSetting("ScreenRotation", SETTING(g_Config, iScreenRotation), &DefaultScreenRotation, CfgFlag::DEFAULT),
|
|
#endif
|
|
|
|
ConfigSetting("BackgroundAnimation", SETTING(g_Config, iBackgroundAnimation), 1, CfgFlag::DEFAULT),
|
|
ConfigSetting("TransparentBackground", SETTING(g_Config, bTransparentBackground), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("UITint", SETTING(g_Config, fUITint), 0.0, CfgFlag::DEFAULT),
|
|
ConfigSetting("UISaturation", SETTING(g_Config, fUISaturation), &DefaultUISaturation, CfgFlag::DEFAULT),
|
|
|
|
#if defined(USING_WIN_UI)
|
|
ConfigSetting("TopMost", SETTING(g_Config, bTopMost), false, CfgFlag::DEFAULT),
|
|
#endif
|
|
|
|
#if defined(USING_WIN_UI) || (defined (SDL) && !defined(MOBILE_DEVICE))
|
|
ConfigSetting("PauseOnLostFocus", SETTING(g_Config, bPauseOnLostFocus), false, CfgFlag::PER_GAME),
|
|
#endif
|
|
|
|
#if !defined(MOBILE_DEVICE)
|
|
ConfigSetting("WindowX", SETTING(g_Config, iWindowX), -1, CfgFlag::DEFAULT), // -1 tells us to center the window.
|
|
ConfigSetting("WindowY", SETTING(g_Config, iWindowY), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("WindowWidth", SETTING(g_Config, iWindowWidth), 0, CfgFlag::DEFAULT), // 0 will be automatically reset later (need to do the AdjustWindowRect dance).
|
|
ConfigSetting("WindowHeight", SETTING(g_Config, iWindowHeight), 0, CfgFlag::DEFAULT),
|
|
ConfigSetting("WindowSizeState", SETTING(g_Config, iWindowSizeState), (int)WindowSizeState::Normal, CfgFlag::DEFAULT),
|
|
ConfigSetting("ShrinkIfWindowSmall", SETTING(g_Config, bShrinkIfWindowSmall), false, CfgFlag::DEFAULT),
|
|
#endif
|
|
|
|
ConfigSetting("PauseWhenMinimized", SETTING(g_Config, bPauseWhenMinimized), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("PauseExitsEmulator", SETTING(g_Config, bPauseExitsEmulator), false, CfgFlag::DONT_SAVE),
|
|
ConfigSetting("PauseMenuExitsEmulator", SETTING(g_Config, bPauseMenuExitsEmulator), false, CfgFlag::DONT_SAVE),
|
|
|
|
ConfigSetting("DumpFileTypes", SETTING(g_Config, iDumpFileTypes), 0, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("FullscreenOnDoubleclick", SETTING(g_Config, bFullscreenOnDoubleclick), true, CfgFlag::DONT_SAVE),
|
|
ConfigSetting("ShowMenuBar", SETTING(g_Config, bShowMenuBar), true, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("MemStickInserted", SETTING(g_Config, bMemStickInserted), true, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("LoadPlugins", SETTING(g_Config, bLoadPlugins), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("NotificationPos", SETTING(g_Config, iNotificationPos), (int)ScreenEdgePosition::TOP_CENTER, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("IgnoreCompatSettings", SETTING(g_Config, sIgnoreCompatSettings), "", CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
|
|
ConfigSetting("RunBehindPauseMenu", SETTING(g_Config, bRunBehindPauseMenu), false, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("ShowGPOLEDs", SETTING(g_Config, bShowGPOLEDs), false, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("UIScaleFactor", SETTING(g_Config, iUIScaleFactor), &DefaultUIScaleFactor, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("VulkanDisableImplicitLayers", SETTING(g_Config, bVulkanDisableImplicitLayers), false, CfgFlag::DEFAULT),
|
|
};
|
|
|
|
static bool DefaultSasThread() {
|
|
return cpu_info.num_cores > 1;
|
|
}
|
|
|
|
static const ConfigSetting achievementSettings[] = {
|
|
// Core settings
|
|
ConfigSetting("AchievementsEnable", SETTING(g_Config, bAchievementsEnable), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("AchievementsEnableRAIntegration", SETTING(g_Config, bAchievementsEnableRAIntegration), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsChallengeMode", SETTING(g_Config, bAchievementsHardcoreMode), true, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsEncoreMode", SETTING(g_Config, bAchievementsEncoreMode), false, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsUnofficial", SETTING(g_Config, bAchievementsUnofficial), false, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsLogBadMemReads", SETTING(g_Config, bAchievementsLogBadMemReads), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsSaveStateInHardcoreMode", SETTING(g_Config, bAchievementsSaveStateInHardcoreMode), false, CfgFlag::DEFAULT),
|
|
|
|
// Achievements login info. Note that password is NOT stored, only a login token.
|
|
// And that login token is stored separately from the ini, see NativeSaveSecret, but it can also be loaded
|
|
// from the ini if manually entered (useful when testing various builds on Android).
|
|
ConfigSetting("AchievementsToken", SETTING(g_Config, sAchievementsToken), "", CfgFlag::DONT_SAVE),
|
|
ConfigSetting("AchievementsUserName", SETTING(g_Config, sAchievementsUserName), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsHost", SETTING(g_Config, sAchievementsHost), "", CfgFlag::DEFAULT),
|
|
|
|
// Customizations
|
|
ConfigSetting("AchievementsSoundEffects", SETTING(g_Config, bAchievementsSoundEffects), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsUnlockAudioFile", SETTING(g_Config, sAchievementsUnlockAudioFile), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsLeaderboardSubmitAudioFile", SETTING(g_Config, sAchievementsLeaderboardSubmitAudioFile), "", CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("AchievementsLeaderboardTrackerPos", SETTING(g_Config, iAchievementsLeaderboardTrackerPos), (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsLeaderboardStartedOrFailedPos", SETTING(g_Config, iAchievementsLeaderboardStartedOrFailedPos), (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsLeaderboardSubmittedPos", SETTING(g_Config, iAchievementsLeaderboardSubmittedPos), (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsProgressPos", SETTING(g_Config, iAchievementsProgressPos), (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsChallengePos", SETTING(g_Config, iAchievementsChallengePos), (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
|
|
ConfigSetting("AchievementsUnlockedPos", SETTING(g_Config, iAchievementsUnlockedPos), (int)ScreenEdgePosition::TOP_CENTER, CfgFlag::PER_GAME | CfgFlag::DEFAULT),
|
|
};
|
|
|
|
static const ConfigSetting cpuSettings[] = {
|
|
ConfigSetting("CPUCore", SETTING(g_Config, iCpuCore), &DefaultCpuCore, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("SeparateSASThread", SETTING(g_Config, bSeparateSASThread), &DefaultSasThread, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("IOTimingMethod", SETTING(g_Config, iIOTimingMethod), IOTIMING_FAST, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("FastMemoryAccess", SETTING(g_Config, bFastMemory), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("FunctionReplacements", SETTING(g_Config, bFuncReplacements), true, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("HideSlowWarnings", SETTING(g_Config, bHideSlowWarnings), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("HideStateWarnings", SETTING(g_Config, bHideStateWarnings), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("JitDisableFlags", SETTING(g_Config, uJitDisableFlags), (uint32_t)0, CfgFlag::PER_GAME),
|
|
ConfigSetting("CPUSpeed", SETTING(g_Config, iLockedCPUSpeed), 0, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
};
|
|
|
|
static int DefaultInternalResolution() {
|
|
// Auto on Windows and Linux, 2x on large screens and iOS, 1x elsewhere.
|
|
#if defined(USING_WIN_UI) || defined(USING_QT_UI)
|
|
return 0;
|
|
#elif PPSSPP_PLATFORM(IOS)
|
|
return 2;
|
|
#else
|
|
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_VR) {
|
|
return 4;
|
|
}
|
|
int longestDisplaySide = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES));
|
|
int scale = longestDisplaySide >= 1000 ? 2 : 1;
|
|
INFO_LOG(Log::G3D, "Longest display side: %d pixels. Choosing scale %d", longestDisplaySide, scale);
|
|
return scale;
|
|
#endif
|
|
}
|
|
|
|
static int DefaultFastForwardMode() {
|
|
#if PPSSPP_PLATFORM(ANDROID) || defined(USING_QT_UI) || PPSSPP_PLATFORM(UWP) || PPSSPP_PLATFORM(IOS)
|
|
return (int)FastForwardMode::SKIP_FLIP;
|
|
#else
|
|
return (int)FastForwardMode::CONTINUOUS;
|
|
#endif
|
|
}
|
|
|
|
static int DefaultAndroidHwScale() {
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
|
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19 || System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_TV) {
|
|
// Arbitrary cutoff at Kitkat - modern devices are usually powerful enough that hw scaling
|
|
// doesn't really help very much and mostly causes problems. See #11151
|
|
return 0;
|
|
}
|
|
|
|
// Get the real resolution as passed in during startup, not dp_xres and stuff
|
|
int xres = System_GetPropertyInt(SYSPROP_DISPLAY_XRES);
|
|
int yres = System_GetPropertyInt(SYSPROP_DISPLAY_YRES);
|
|
|
|
if (xres <= 960) {
|
|
// Smaller than the PSP*2, let's go native.
|
|
return 0;
|
|
} else if (xres <= 480 * 3) { // 720p xres
|
|
// Small-ish screen, we should default to 2x
|
|
return 2 + 1;
|
|
} else {
|
|
// Large or very large screen. Default to 3x psp resolution.
|
|
return 3 + 1;
|
|
}
|
|
return 0;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
// See issue 14439. Should possibly even block these devices from selecting VK.
|
|
const char * const vulkanDefaultBlacklist[] = {
|
|
"Sony:BRAVIA VH1",
|
|
};
|
|
|
|
static int DefaultGPUBackend() {
|
|
if (IsVREnabled()) {
|
|
return (int)GPUBackend::OPENGL;
|
|
}
|
|
|
|
#if PPSSPP_PLATFORM(UWP)
|
|
return (int)GPUBackend::DIRECT3D11;
|
|
#elif PPSSPP_PLATFORM(WINDOWS)
|
|
// On Win10, there's a good chance Vulkan will work by default.
|
|
if (IsWin10OrHigher()) {
|
|
return (int)GPUBackend::VULKAN;
|
|
}
|
|
// On older Windows, to be safe, use Direct3D 11.
|
|
if (IsWin8OrHigher()) {
|
|
return (int)GPUBackend::DIRECT3D11;
|
|
}
|
|
#elif PPSSPP_PLATFORM(ANDROID)
|
|
// Check blacklist.
|
|
for (size_t i = 0; i < ARRAY_SIZE(vulkanDefaultBlacklist); i++) {
|
|
if (System_GetProperty(SYSPROP_NAME) == vulkanDefaultBlacklist[i]) {
|
|
return (int)GPUBackend::OPENGL;
|
|
}
|
|
}
|
|
|
|
// Default to Vulkan only on Oreo 8.1 (level 27) devices or newer, and only
|
|
// on ARM64 and x86-64. Drivers before, and on other archs, are generally too
|
|
// unreliable to default to (with some exceptions, of course).
|
|
#if PPSSPP_ARCH(64BIT)
|
|
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 27) {
|
|
return (int)GPUBackend::VULKAN;
|
|
}
|
|
#else
|
|
// There are some newer devices that benefit from Vulkan as default, but are 32-bit. Example: Redmi 9A.
|
|
// Let's only allow the very newest generation though.
|
|
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30) {
|
|
return (int)GPUBackend::VULKAN;
|
|
}
|
|
#endif
|
|
|
|
#elif PPSSPP_PLATFORM(MAC)
|
|
|
|
#if PPSSPP_ARCH(ARM64)
|
|
return (int)GPUBackend::VULKAN;
|
|
#else
|
|
// On Intel (generally older Macs) default to OpenGL.
|
|
return (int)GPUBackend::OPENGL;
|
|
#endif
|
|
|
|
#elif PPSSPP_PLATFORM(IOS_APP_STORE)
|
|
return (int)GPUBackend::VULKAN;
|
|
#endif
|
|
|
|
// TODO: On some additional Linux platforms, we should also default to Vulkan.
|
|
return (int)GPUBackend::OPENGL;
|
|
}
|
|
|
|
int Config::NextValidBackend() {
|
|
std::vector<std::string> split;
|
|
std::set<GPUBackend> failed;
|
|
|
|
SplitString(sFailedGPUBackends, ',', split);
|
|
for (const auto &str : split) {
|
|
if (!str.empty() && str != "ALL") {
|
|
failed.insert(GPUBackendFromString(str));
|
|
}
|
|
}
|
|
|
|
// Count these as "failed" too so we don't pick them.
|
|
SplitString(sDisabledGPUBackends, ',', split);
|
|
for (const auto &str : split) {
|
|
if (!str.empty()) {
|
|
failed.insert(GPUBackendFromString(str));
|
|
}
|
|
}
|
|
|
|
if (failed.count((GPUBackend)iGPUBackend)) {
|
|
ERROR_LOG(Log::Loader, "Graphics backend failed for %d, trying another", iGPUBackend);
|
|
|
|
#if !PPSSPP_PLATFORM(UWP)
|
|
if (!failed.count(GPUBackend::VULKAN) && VulkanMayBeAvailable()) {
|
|
return (int)GPUBackend::VULKAN;
|
|
}
|
|
#endif
|
|
#if PPSSPP_PLATFORM(WINDOWS)
|
|
if (!failed.count(GPUBackend::DIRECT3D11) && IsWin7OrHigher()) {
|
|
return (int)GPUBackend::DIRECT3D11;
|
|
}
|
|
#endif
|
|
#if PPSSPP_API(ANY_GL)
|
|
if (!failed.count(GPUBackend::OPENGL)) {
|
|
return (int)GPUBackend::OPENGL;
|
|
}
|
|
#endif
|
|
|
|
// They've all failed. Let them try the default - or on Android, OpenGL.
|
|
if (sFailedGPUBackends.find(",ALL") == std::string::npos) {
|
|
sFailedGPUBackends += ",ALL";
|
|
}
|
|
ERROR_LOG(Log::Loader, "All graphics backends failed");
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
|
return (int)GPUBackend::OPENGL;
|
|
#else
|
|
return DefaultGPUBackend();
|
|
#endif
|
|
}
|
|
|
|
return iGPUBackend;
|
|
}
|
|
|
|
bool Config::IsBackendEnabled(GPUBackend backend) {
|
|
std::vector<std::string> split;
|
|
|
|
SplitString(sDisabledGPUBackends, ',', split);
|
|
for (const auto &str : split) {
|
|
if (str.empty())
|
|
continue;
|
|
auto match = GPUBackendFromString(str);
|
|
if (match == backend)
|
|
return false;
|
|
}
|
|
|
|
#if PPSSPP_PLATFORM(UWP)
|
|
if (backend != GPUBackend::DIRECT3D11)
|
|
return false;
|
|
#elif PPSSPP_PLATFORM(SWITCH)
|
|
if (backend != GPUBackend::OPENGL)
|
|
return false;
|
|
#elif PPSSPP_PLATFORM(WINDOWS)
|
|
if (backend == GPUBackend::DIRECT3D11 && !IsVistaOrHigher())
|
|
return false;
|
|
#else
|
|
if (backend == GPUBackend::DIRECT3D11)
|
|
return false;
|
|
#endif
|
|
|
|
#if !PPSSPP_API(ANY_GL)
|
|
if (backend == GPUBackend::OPENGL)
|
|
return false;
|
|
#endif
|
|
if (backend == GPUBackend::VULKAN && !VulkanMayBeAvailable())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
template <typename T, std::string (*FTo)(T), T (*FFrom)(std::string_view)>
|
|
struct ConfigTranslator {
|
|
static std::string To(int v) {
|
|
return StringFromInt(v) + " (" + FTo(T(v)) + ")";
|
|
}
|
|
|
|
static int From(const std::string &v) {
|
|
int result;
|
|
if (TryParse(v, &result)) {
|
|
return result;
|
|
}
|
|
return (int)FFrom(v);
|
|
}
|
|
};
|
|
|
|
typedef ConfigTranslator<GPUBackend, GPUBackendToString, GPUBackendFromString> GPUBackendTranslator;
|
|
|
|
static std::string DefaultInfrastructureUsername() {
|
|
// If the user has already picked a Nickname that satisfies the rules and is not "PPSSPP",
|
|
// let's use that.
|
|
// NOTE: This type of dependency means that network settings must be AFTER system settings in sections[].
|
|
if (g_Config.sNickName != "PPSSPP" &&
|
|
!g_Config.sNickName.empty() &&
|
|
g_Config.sNickName == SanitizeString(g_Config.sNickName, StringRestriction::AlphaNumDashUnderscore, 3, 16)) {
|
|
return g_Config.sNickName;
|
|
}
|
|
|
|
// Otherwise let's leave it empty, which will result in login failure and a warning.
|
|
return std::string();
|
|
}
|
|
|
|
bool DisplayLayoutConfig::InternalRotationIsPortrait() const {
|
|
return (iInternalScreenRotation == ROTATION_LOCKED_VERTICAL || iInternalScreenRotation == ROTATION_LOCKED_VERTICAL180) && !g_Config.bSkipBufferEffects;
|
|
}
|
|
|
|
bool DisplayLayoutConfig::ResetToDefault(std::string_view blockName) {
|
|
static const DisplayLayoutConfig defaultLayout = DisplayLayoutConfig();
|
|
*this = defaultLayout;
|
|
if (endsWith(blockName, ".Portrait")) {
|
|
// TODO: On mobile, where the aspect is fixed, we should use the screen size to compute this properly,
|
|
// so the screen almost touches the top edge.
|
|
fDisplayOffsetY = 0.25f;
|
|
bImmersiveMode = false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// These were previously part of Graphics.
|
|
// It's instantiated into g_Config.displayLayoutLandscape and g_Config.displayLayoutPortrait.
|
|
// Defaults are set directly on the struct declaration (and adjusted per instance in ResetToDefault above).
|
|
static const ConfigSetting displayLayoutSettings[] = {
|
|
ConfigSetting("BufferFiltering", SETTING(g_Config.displayLayoutLandscape, iDisplayFilter), CfgFlag::PER_GAME),
|
|
ConfigSetting("DisplayStretch", SETTING(g_Config.displayLayoutLandscape, bDisplayStretch), CfgFlag::PER_GAME),
|
|
ConfigSetting("DisplayOffsetX", SETTING(g_Config.displayLayoutLandscape, fDisplayOffsetX), CfgFlag::PER_GAME),
|
|
ConfigSetting("DisplayOffsetY", SETTING(g_Config.displayLayoutLandscape, fDisplayOffsetY), CfgFlag::PER_GAME),
|
|
ConfigSetting("DisplayScale", SETTING(g_Config.displayLayoutLandscape, fDisplayScale), CfgFlag::PER_GAME),
|
|
ConfigSetting("DisplayIntegerScale", SETTING(g_Config.displayLayoutLandscape, bDisplayIntegerScale), CfgFlag::PER_GAME),
|
|
ConfigSetting("DisplayAspectRatio", SETTING(g_Config.displayLayoutLandscape, fDisplayAspectRatio), CfgFlag::PER_GAME),
|
|
ConfigSetting("IgnoreScreenInsets", SETTING(g_Config.displayLayoutLandscape, bIgnoreScreenInsets), CfgFlag::PER_GAME),
|
|
ConfigSetting("InternalScreenRotation", SETTING(g_Config.displayLayoutLandscape, iInternalScreenRotation), CfgFlag::PER_GAME),
|
|
ConfigSetting("RotateControlsWithScreen", SETTING(g_Config.displayLayoutLandscape, bRotateControlsWithScreen), CfgFlag::PER_GAME),
|
|
ConfigSetting("EnableCardboardVR", SETTING(g_Config.displayLayoutLandscape, bEnableCardboardVR), CfgFlag::PER_GAME),
|
|
ConfigSetting("CardboardScreenSize", SETTING(g_Config.displayLayoutLandscape, iCardboardScreenSize), CfgFlag::PER_GAME),
|
|
ConfigSetting("CardboardXShift", SETTING(g_Config.displayLayoutLandscape, iCardboardXShift), CfgFlag::PER_GAME),
|
|
ConfigSetting("CardboardYShift", SETTING(g_Config.displayLayoutLandscape, iCardboardYShift), CfgFlag::PER_GAME),
|
|
ConfigSetting("ImmersiveMode", SETTING(g_Config.displayLayoutLandscape, bImmersiveMode), CfgFlag::PER_GAME),
|
|
};
|
|
|
|
bool GestureControlConfig::ResetToDefault(std::string_view blockName) {
|
|
static const GestureControlConfig defaultLayout;
|
|
*this = defaultLayout;
|
|
// TODO: Check blockname to set defaults differently for secondary zone(s).
|
|
return true;
|
|
}
|
|
|
|
// These were previously mostly part of General (which was wrong).
|
|
static const ConfigSetting gestureControlSettings[] = {
|
|
ConfigSetting("SwipeUp", SETTING(g_Config.gestureControls[0], iSwipeUp), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("SwipeDown", SETTING(g_Config.gestureControls[0], iSwipeDown), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("SwipeLeft", SETTING(g_Config.gestureControls[0], iSwipeLeft), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("SwipeRight", SETTING(g_Config.gestureControls[0], iSwipeRight), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("SwipeSensitivity", SETTING(g_Config.gestureControls[0], fSwipeSensitivity), 1.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("SwipeSmoothing", SETTING(g_Config.gestureControls[0], fSwipeSmoothing), 0.3f, CfgFlag::PER_GAME),
|
|
ConfigSetting("DoubleTapGesture", SETTING(g_Config.gestureControls[0], iDoubleTapGesture), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("GestureControlEnabled", SETTING(g_Config.gestureControls[0], bGestureControlEnabled), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("AnalogGesture", SETTING(g_Config.gestureControls[0], bAnalogGesture), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("AnalogGestureSensibility", SETTING(g_Config.gestureControls[0], fAnalogGestureSensitivity), 1.0f, CfgFlag::PER_GAME),
|
|
};
|
|
|
|
static const ConfigSetting graphicsSettings[] = {
|
|
ConfigSetting("iShowStatusFlags", SETTING(g_Config, iShowStatusFlags), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("GraphicsBackend", SETTING(g_Config, iGPUBackend), &DefaultGPUBackend, &GPUBackendTranslator::To, &GPUBackendTranslator::From, CfgFlag::DEFAULT | CfgFlag::REPORT),
|
|
#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64)
|
|
ConfigSetting("CustomDriver", SETTING(g_Config, sCustomDriver), "", CfgFlag::DEFAULT),
|
|
#endif
|
|
ConfigSetting("DisabledGraphicsBackends", SETTING(g_Config, sDisabledGPUBackends), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("VulkanDevice", SETTING(g_Config, sVulkanDevice), "", CfgFlag::DEFAULT),
|
|
#ifdef _WIN32
|
|
ConfigSetting("D3D11Device", SETTING(g_Config, sD3D11Device), "", CfgFlag::DEFAULT),
|
|
#endif
|
|
ConfigSetting("CameraDevice", SETTING(g_Config, sCameraDevice), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("CameraMirrorHorizontal", SETTING(g_Config, bCameraMirrorHorizontal), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("AndroidFramerateMode", SETTING(g_Config, iDisplayFramerateMode), 1, CfgFlag::DEFAULT),
|
|
ConfigSetting("VendorBugChecksEnabled", SETTING(g_Config, bVendorBugChecksEnabled), true, CfgFlag::DONT_SAVE),
|
|
ConfigSetting("UseGeometryShader", SETTING(g_Config, bUseGeometryShader), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("SkipBufferEffects", SETTING(g_Config, bSkipBufferEffects), false, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("DisableRangeCulling", SETTING(g_Config, bDisableRangeCulling), false, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("DepthRasterMode", SETTING(g_Config, iDepthRasterMode), &DefaultDepthRaster, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("SoftwareRenderer", SETTING(g_Config, bSoftwareRendering), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("SoftwareRendererJit", SETTING(g_Config, bSoftwareRenderingJit), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("HardwareTransform", SETTING(g_Config, bHardwareTransform), true, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("SoftwareSkinning", SETTING(g_Config, bSoftwareSkinning), true, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("TextureFiltering", SETTING(g_Config, iTexFiltering), 1, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("Smart2DTexFiltering", SETTING(g_Config, bSmart2DTexFiltering), false, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("InternalResolution", SETTING(g_Config, iInternalResolution), &DefaultInternalResolution, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("AndroidHwScale", SETTING(g_Config, iAndroidHwScale), &DefaultAndroidHwScale, CfgFlag::DEFAULT),
|
|
ConfigSetting("HighQualityDepth", SETTING(g_Config, bHighQualityDepth), true, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("FrameSkip", SETTING(g_Config, iFrameSkip), 0, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("AutoFrameSkip", SETTING(g_Config, bAutoFrameSkip), IsVREnabled(), CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("StereoRendering", SETTING(g_Config, bStereoRendering), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("StereoToMonoShader", SETTING(g_Config, sStereoToMonoShader), "RedBlue", CfgFlag::PER_GAME),
|
|
ConfigSetting("FrameRate", SETTING(g_Config, iFpsLimit1), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("FrameRate2", SETTING(g_Config, iFpsLimit2), -1, CfgFlag::PER_GAME),
|
|
ConfigSetting("AnalogFrameRate", SETTING(g_Config, iAnalogFpsLimit), 240, CfgFlag::PER_GAME),
|
|
#if defined(USING_WIN_UI)
|
|
ConfigSetting("RestartRequired", SETTING(g_Config, bRestartRequired), false, CfgFlag::DONT_SAVE),
|
|
#endif
|
|
|
|
// Most low-performance (and many high performance) mobile GPUs do not support aniso anyway so defaulting to 4 is fine.
|
|
ConfigSetting("AnisotropyLevel", SETTING(g_Config, iAnisotropyLevel), 4, CfgFlag::PER_GAME),
|
|
ConfigSetting("MultiSampleLevel", SETTING(g_Config, iMultiSampleLevel), 0, CfgFlag::PER_GAME), // Number of samples is 1 << iMultiSampleLevel
|
|
|
|
ConfigSetting("TextureBackoffCache", SETTING(g_Config, bTextureBackoffCache), false, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("VertexDecJit", SETTING(g_Config, bVertexDecoderJit), &DefaultCodeGen, CfgFlag::DONT_SAVE | CfgFlag::REPORT),
|
|
|
|
#ifndef MOBILE_DEVICE
|
|
ConfigSetting("FullScreen", SETTING(g_Config, bFullScreen), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("FullScreenMulti", SETTING(g_Config, bFullScreenMulti), false, CfgFlag::DEFAULT),
|
|
#endif
|
|
|
|
#if PPSSPP_PLATFORM(IOS)
|
|
ConfigSetting("AppSwitchMode", SETTING(g_Config, iAppSwitchMode), (int)AppSwitchMode::DOUBLE_SWIPE_INDICATOR, CfgFlag::DEFAULT),
|
|
#endif
|
|
|
|
ConfigSetting("DisplayCropTo16x9", SETTING(g_Config, bDisplayCropTo16x9), true, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("SustainedPerformanceMode", SETTING(g_Config, bSustainedPerformanceMode), false, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("ReplaceTextures", SETTING(g_Config, bReplaceTextures), true, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("SaveNewTextures", SETTING(g_Config, bSaveNewTextures), false, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("IgnoreTextureFilenames", SETTING(g_Config, bIgnoreTextureFilenames), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("ReplacementTextureLoadSpeed", SETTING(g_Config, iReplacementTextureLoadSpeed), 0, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("TexScalingLevel", SETTING(g_Config, iTexScalingLevel), 1, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("TexScalingType", SETTING(g_Config, iTexScalingType), 0, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("TexDeposterize", SETTING(g_Config, bTexDeposterize), false, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("TexHardwareScaling", SETTING(g_Config, bTexHardwareScaling), false, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("VerticalSync", SETTING(g_Config, bVSync), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("LowLatencyPresent", SETTING(g_Config, bLowLatencyPresent), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("BloomHack", SETTING(g_Config, iBloomHack), 0, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
|
|
// Not really a graphics setting...
|
|
ConfigSetting("SplineBezierQuality", SETTING(g_Config, iSplineBezierQuality), 2, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("HardwareTessellation", SETTING(g_Config, bHardwareTessellation), false, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("TextureShader", SETTING(g_Config, sTextureShaderName), "Off", CfgFlag::PER_GAME),
|
|
ConfigSetting("ShaderChainRequires60FPS", SETTING(g_Config, bShaderChainRequires60FPS), false, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("SkipGPUReadbackMode", SETTING(g_Config, iSkipGPUReadbackMode), false, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
|
|
ConfigSetting("GfxDebugOutput", SETTING(g_Config, bGfxDebugOutput), false, CfgFlag::DONT_SAVE),
|
|
ConfigSetting("LogFrameDrops", SETTING(g_Config, bLogFrameDrops), false, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("InflightFrames", SETTING(g_Config, iInflightFrames), 3, CfgFlag::DEFAULT),
|
|
ConfigSetting("RenderDuplicateFrames", SETTING(g_Config, bRenderDuplicateFrames), false, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("MultiThreading", SETTING(g_Config, bRenderMultiThreading), true, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("ShaderCache", SETTING(g_Config, bShaderCache), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("GpuLogProfiler", SETTING(g_Config, bGpuLogProfiler), false, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("UberShaderVertex", SETTING(g_Config, bUberShaderVertex), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("UberShaderFragment", SETTING(g_Config, bUberShaderFragment), true, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("DisplayRefreshRate", SETTING(g_Config, iDisplayRefreshRate), g_Config.iDisplayRefreshRate, CfgFlag::PER_GAME),
|
|
};
|
|
|
|
static int LegacyVolumeToNewVolume(int legacy, int max) {
|
|
float multiplier = Volume10ToMultiplier(legacy);
|
|
return std::clamp(MultiplierToVolume100(multiplier), 0, max);
|
|
}
|
|
|
|
static int DefaultGameVolume() {
|
|
return LegacyVolumeToNewVolume(g_Config.iLegacyGameVolume, 100);
|
|
}
|
|
|
|
static int DefaultReverbVolume() {
|
|
return LegacyVolumeToNewVolume(g_Config.iLegacyReverbVolume, 200);
|
|
}
|
|
|
|
static int DefaultAchievementVolume() {
|
|
// NOTE: The old achievemnt volume was a straight percentage so it doesn't convert
|
|
// the same as the others.
|
|
return MultiplierToVolume100((float)g_Config.iLegacyAchievementVolume / 10.0f);
|
|
}
|
|
|
|
static int DefaultGamePreviewVolume() {
|
|
// This was previously controlled by the UI volume, so transfer the default value over.
|
|
return g_Config.iUIVolume;
|
|
}
|
|
|
|
std::string DefaultProAdhocServer() {
|
|
return "socom.cc";
|
|
}
|
|
|
|
static const ConfigSetting soundSettings[] = {
|
|
ConfigSetting("Enable", SETTING(g_Config, bEnableSound), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("ExtraAudioBuffering", SETTING(g_Config, bExtraAudioBuffering), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("AudioBufferSize", SETTING(g_Config, iSDLAudioBufferSize), 256, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("FillAudioGaps", SETTING(g_Config, bFillAudioGaps), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("AudioSyncMode", SETTING(g_Config, iAudioPlaybackMode), (int)AudioSyncMode::CLASSIC_PITCH, CfgFlag::DEFAULT),
|
|
|
|
// Legacy volume settings, these get auto upgraded through default handlers on the new settings. NOTE: Must be before the new ones in the order here.
|
|
// The default settings here are still relevant, they will get propagated into the new ones.
|
|
// In 1.21 or so, let's get rid of these.
|
|
ConfigSetting("GlobalVolume", SETTING(g_Config, iLegacyGameVolume), VOLUME_FULL, CfgFlag::PER_GAME | CfgFlag::DONT_SAVE),
|
|
ConfigSetting("ReverbVolume", SETTING(g_Config, iLegacyReverbVolume), VOLUME_FULL, CfgFlag::PER_GAME | CfgFlag::DONT_SAVE),
|
|
ConfigSetting("AchievementSoundVolume", SETTING(g_Config, iLegacyAchievementVolume), 6, CfgFlag::PER_GAME | CfgFlag::DONT_SAVE),
|
|
|
|
// Current volume settings.
|
|
ConfigSetting("GameVolume", SETTING(g_Config, iGameVolume), &DefaultGameVolume, CfgFlag::PER_GAME),
|
|
ConfigSetting("ReverbRelativeVolume", SETTING(g_Config, iReverbVolume), &DefaultReverbVolume, CfgFlag::PER_GAME),
|
|
ConfigSetting("AltSpeedRelativeVolume", SETTING(g_Config, iAltSpeedVolume), VOLUMEHI_FULL, CfgFlag::PER_GAME),
|
|
ConfigSetting("AchievementVolume", SETTING(g_Config, iAchievementVolume), &DefaultAchievementVolume, CfgFlag::PER_GAME),
|
|
ConfigSetting("UIVolume", SETTING(g_Config, iUIVolume), 75, CfgFlag::DEFAULT),
|
|
ConfigSetting("GamePreviewVolume", SETTING(g_Config, iGamePreviewVolume), &DefaultGamePreviewVolume, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("AudioDevice", SETTING(g_Config, sAudioDevice), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("AutoAudioDevice", SETTING(g_Config, bAutoSwitchAudioDevice), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("AudioMixWithOthers", SETTING(g_Config, bAudioMixWithOthers), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("AudioRespectSilentMode", SETTING(g_Config, bAudioRespectSilentMode), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("UseOldAtrac", SETTING(g_Config, bUseOldAtrac), false, CfgFlag::DEFAULT),
|
|
};
|
|
|
|
static bool DefaultShowTouchControls() {
|
|
switch (System_GetPropertyInt(SYSPROP_DEVICE_TYPE)) {
|
|
case DEVICE_TYPE_MOBILE:
|
|
return !KeyMap::HasBuiltinController(System_GetProperty(SYSPROP_NAME));
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static const float defaultControlScale = 1.15f;
|
|
static const ConfigTouchPos defaultTouchPosShow = { -1.0f, -1.0f, defaultControlScale, true };
|
|
static const ConfigTouchPos defaultTouchPosHide = { -1.0f, -1.0f, defaultControlScale, false };
|
|
|
|
void TouchControlConfig::ResetLayout() {
|
|
// reset puts the settings in a state so they'll then get properly reinitialized in InitPadLayout.
|
|
// Intentionally don't modify 'show' here, this is only done in ResetToDefault.
|
|
auto reset = [](ConfigTouchPos *pos) {
|
|
pos->x = defaultTouchPosShow.x;
|
|
pos->y = defaultTouchPosShow.y;
|
|
pos->scale = defaultTouchPosShow.scale;
|
|
};
|
|
reset(&touchActionButtonCenter);
|
|
fActionButtonSpacing = 1.0f;
|
|
reset(&touchDpad);
|
|
fDpadSpacing = 1.0f;
|
|
reset(&touchStartKey);
|
|
reset(&touchSelectKey);
|
|
reset(&touchFastForwardKey);
|
|
reset(&touchLKey);
|
|
reset(&touchRKey);
|
|
reset(&touchAnalogStick);
|
|
reset(&touchRightAnalogStick);
|
|
reset(&touchPauseKey);
|
|
|
|
for (int i = 0; i < CUSTOM_BUTTON_COUNT; i++) {
|
|
reset(&touchCustom[i]);
|
|
}
|
|
fLeftStickHeadScale = 1.0f;
|
|
fRightStickHeadScale = 1.0f;
|
|
}
|
|
|
|
bool TouchControlConfig::ResetToDefault(std::string_view blockName) {
|
|
static const TouchControlConfig defaults = TouchControlConfig();
|
|
*this = defaults;
|
|
|
|
switch (System_GetPropertyInt(SYSPROP_DEVICE_TYPE)) {
|
|
case DEVICE_TYPE_MOBILE:
|
|
case DEVICE_TYPE_DESKTOP:
|
|
touchPauseKey.show = true;
|
|
break;
|
|
case DEVICE_TYPE_VR:
|
|
case DEVICE_TYPE_TV:
|
|
touchPauseKey.show = false;
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static const ConfigSetting touchControlSettings[] = {
|
|
ConfigSetting("ShowTouchCross", SETTING(g_Config.touchControlsLandscape, bShowTouchCross), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("ShowTouchCircle", SETTING(g_Config.touchControlsLandscape, bShowTouchCircle), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("ShowTouchSquare", SETTING(g_Config.touchControlsLandscape, bShowTouchSquare), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("ShowTouchTriangle", SETTING(g_Config.touchControlsLandscape, bShowTouchTriangle), true, CfgFlag::PER_GAME),
|
|
|
|
// Combo keys are something else, but I don't want to break the config backwards compatibility so these will stay wrongly named.
|
|
ConfigSetting("fcombo0X", "fcombo0Y", "comboKeyScale0", "ShowComboKey0", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 0), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo1X", "fcombo1Y", "comboKeyScale1", "ShowComboKey1", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 1), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo2X", "fcombo2Y", "comboKeyScale2", "ShowComboKey2", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 2), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo3X", "fcombo3Y", "comboKeyScale3", "ShowComboKey3", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 3), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo4X", "fcombo4Y", "comboKeyScale4", "ShowComboKey4", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 4), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo5X", "fcombo5Y", "comboKeyScale5", "ShowComboKey5", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 5), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo6X", "fcombo6Y", "comboKeyScale6", "ShowComboKey6", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 6), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo7X", "fcombo7Y", "comboKeyScale7", "ShowComboKey7", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 7), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo8X", "fcombo8Y", "comboKeyScale8", "ShowComboKey8", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 8), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo9X", "fcombo9Y", "comboKeyScale9", "ShowComboKey9", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 9), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo10X", "fcombo10Y", "comboKeyScale10", "ShowComboKey10", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 10), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo11X", "fcombo11Y", "comboKeyScale11", "ShowComboKey11", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 11), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo12X", "fcombo12Y", "comboKeyScale12", "ShowComboKey12", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 12), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo13X", "fcombo13Y", "comboKeyScale13", "ShowComboKey13", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 13), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo14X", "fcombo14Y", "comboKeyScale14", "ShowComboKey14", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 14), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo15X", "fcombo15Y", "comboKeyScale15", "ShowComboKey15", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 15), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo16X", "fcombo16Y", "comboKeyScale16", "ShowComboKey16", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 16), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo17X", "fcombo17Y", "comboKeyScale17", "ShowComboKey17", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 17), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo18X", "fcombo18Y", "comboKeyScale18", "ShowComboKey18", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 18), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
ConfigSetting("fcombo19X", "fcombo19Y", "comboKeyScale19", "ShowComboKey19", SETTING_IDX(g_Config.touchControlsLandscape, touchCustom, 19), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
|
|
// -1.0f means uninitialized, set in GamepadEmu::CreatePadLayout().
|
|
ConfigSetting("ActionButtonSpacing2", SETTING(g_Config.touchControlsLandscape, fActionButtonSpacing), 1.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("ActionButtonCenterX", "ActionButtonCenterY", "ActionButtonScale", nullptr, SETTING(g_Config.touchControlsLandscape, touchActionButtonCenter), defaultTouchPosShow, CfgFlag::PER_GAME),
|
|
ConfigSetting("DPadX", "DPadY", "DPadScale", "ShowTouchDpad", SETTING(g_Config.touchControlsLandscape, touchDpad), defaultTouchPosShow, CfgFlag::PER_GAME),
|
|
|
|
// Note: these will be overwritten if DPadRadius is set.
|
|
ConfigSetting("DPadSpacing", SETTING(g_Config.touchControlsLandscape, fDpadSpacing), 1.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("StartKeyX", "StartKeyY", "StartKeyScale", "ShowTouchStart", SETTING(g_Config.touchControlsLandscape, touchStartKey), defaultTouchPosShow, CfgFlag::PER_GAME),
|
|
ConfigSetting("SelectKeyX", "SelectKeyY", "SelectKeyScale", "ShowTouchSelect", SETTING(g_Config.touchControlsLandscape, touchSelectKey), defaultTouchPosShow, CfgFlag::PER_GAME),
|
|
ConfigSetting("UnthrottleKeyX", "UnthrottleKeyY", "UnthrottleKeyScale", "ShowTouchUnthrottle", SETTING(g_Config.touchControlsLandscape, touchFastForwardKey), defaultTouchPosShow, CfgFlag::PER_GAME),
|
|
ConfigSetting("LKeyX", "LKeyY", "LKeyScale", "ShowTouchLTrigger", SETTING(g_Config.touchControlsLandscape, touchLKey), defaultTouchPosShow, CfgFlag::PER_GAME),
|
|
ConfigSetting("RKeyX", "RKeyY", "RKeyScale", "ShowTouchRTrigger", SETTING(g_Config.touchControlsLandscape, touchRKey), defaultTouchPosShow, CfgFlag::PER_GAME),
|
|
ConfigSetting("PauseKeyX", "PauseKeyY", "PauseKeyScale", "ShowTouchPause", SETTING(g_Config.touchControlsLandscape, touchPauseKey), defaultTouchPosShow, CfgFlag::PER_GAME),
|
|
ConfigSetting("AnalogStickX", "AnalogStickY", "AnalogStickScale", "ShowAnalogStick", SETTING(g_Config.touchControlsLandscape, touchAnalogStick), defaultTouchPosShow, CfgFlag::PER_GAME),
|
|
ConfigSetting("RightAnalogStickX", "RightAnalogStickY", "RightAnalogStickScale", "ShowRightAnalogStick", SETTING(g_Config.touchControlsLandscape, touchRightAnalogStick), defaultTouchPosHide, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("LeftStickHeadScale", SETTING(g_Config.touchControlsLandscape, fLeftStickHeadScale), CfgFlag::PER_GAME),
|
|
ConfigSetting("RightStickHeadScale", SETTING(g_Config.touchControlsLandscape, fRightStickHeadScale), CfgFlag::PER_GAME),
|
|
ConfigSetting("HideStickBackground", SETTING(g_Config.touchControlsLandscape, bHideStickBackground), CfgFlag::PER_GAME),
|
|
};
|
|
|
|
static const ConfigSetting controlSettings[] = {
|
|
ConfigSetting("HapticFeedback", SETTING(g_Config, bHapticFeedback), false, CfgFlag::PER_GAME),
|
|
|
|
#if defined(USING_WIN_UI)
|
|
ConfigSetting("IgnoreWindowsKey", SETTING(g_Config, bIgnoreWindowsKey), false, CfgFlag::PER_GAME),
|
|
#endif
|
|
|
|
ConfigSetting("ShowTouchControls", SETTING(g_Config, bShowTouchControls), &DefaultShowTouchControls, CfgFlag::PER_GAME),
|
|
|
|
// ConfigSetting("KeyMapping", SETTING(g_Config, iMappingMap), 0),
|
|
ConfigSetting("Custom0Mapping", "Custom0Image", "Custom0Shape", "Custom0Toggle", "Custom0Repeat", SETTING_IDX(g_Config, CustomButton, 0), {0, 0, 0, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom1Mapping", "Custom1Image", "Custom1Shape", "Custom1Toggle", "Custom1Repeat", SETTING_IDX(g_Config, CustomButton, 1), {0, 1, 0, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom2Mapping", "Custom2Image", "Custom2Shape", "Custom2Toggle", "Custom2Repeat", SETTING_IDX(g_Config, CustomButton, 2), {0, 2, 0, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom3Mapping", "Custom3Image", "Custom3Shape", "Custom3Toggle", "Custom3Repeat", SETTING_IDX(g_Config, CustomButton, 3), {0, 3, 0, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom4Mapping", "Custom4Image", "Custom4Shape", "Custom4Toggle", "Custom4Repeat", SETTING_IDX(g_Config, CustomButton, 4), {0, 4, 0, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom5Mapping", "Custom5Image", "Custom5Shape", "Custom5Toggle", "Custom5Repeat", SETTING_IDX(g_Config, CustomButton, 5), {0, 0, 1, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom6Mapping", "Custom6Image", "Custom6Shape", "Custom6Toggle", "Custom6Repeat", SETTING_IDX(g_Config, CustomButton, 6), {0, 1, 1, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom7Mapping", "Custom7Image", "Custom7Shape", "Custom7Toggle", "Custom7Repeat", SETTING_IDX(g_Config, CustomButton, 7), {0, 2, 1, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom8Mapping", "Custom8Image", "Custom8Shape", "Custom8Toggle", "Custom8Repeat", SETTING_IDX(g_Config, CustomButton, 8), {0, 3, 1, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom9Mapping", "Custom9Image", "Custom9Shape", "Custom9Toggle", "Custom9Repeat", SETTING_IDX(g_Config, CustomButton, 9), {0, 4, 1, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom10Mapping", "Custom10Image", "Custom10Shape", "Custom10Toggle", "Custom10Repeat", SETTING_IDX(g_Config, CustomButton, 10), {0, 0, 2, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom11Mapping", "Custom11Image", "Custom11Shape", "Custom11Toggle", "Custom11Repeat", SETTING_IDX(g_Config, CustomButton, 11), {0, 1, 2, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom12Mapping", "Custom12Image", "Custom12Shape", "Custom12Toggle", "Custom12Repeat", SETTING_IDX(g_Config, CustomButton, 12), {0, 2, 2, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom13Mapping", "Custom13Image", "Custom13Shape", "Custom13Toggle", "Custom13Repeat", SETTING_IDX(g_Config, CustomButton, 13), {0, 3, 2, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom14Mapping", "Custom14Image", "Custom14Shape", "Custom14Toggle", "Custom14Repeat", SETTING_IDX(g_Config, CustomButton, 14), {0, 4, 2, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom15Mapping", "Custom15Image", "Custom15Shape", "Custom15Toggle", "Custom15Repeat", SETTING_IDX(g_Config, CustomButton, 15), {0, 0, 9, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom16Mapping", "Custom16Image", "Custom16Shape", "Custom16Toggle", "Custom16Repeat", SETTING_IDX(g_Config, CustomButton, 16), {0, 1, 9, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom17Mapping", "Custom17Image", "Custom17Shape", "Custom17Toggle", "Custom17Repeat", SETTING_IDX(g_Config, CustomButton, 17), {0, 2, 9, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom18Mapping", "Custom18Image", "Custom18Shape", "Custom18Toggle", "Custom18Repeat", SETTING_IDX(g_Config, CustomButton, 18), {0, 3, 9, false, false}, CfgFlag::PER_GAME),
|
|
ConfigSetting("Custom19Mapping", "Custom19Image", "Custom19Shape", "Custom19Toggle", "Custom19Repeat", SETTING_IDX(g_Config, CustomButton, 19), {0, 4, 9, false, false}, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("TiltBaseAngleY", SETTING(g_Config, fTiltBaseAngleY), 0.9f, CfgFlag::PER_GAME),
|
|
ConfigSetting("TiltInvertX", SETTING(g_Config, bInvertTiltX), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("TiltInvertY", SETTING(g_Config, bInvertTiltY), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("TiltSensitivityX", SETTING(g_Config, iTiltSensitivityX), 60, CfgFlag::PER_GAME),
|
|
ConfigSetting("TiltSensitivityY", SETTING(g_Config, iTiltSensitivityY), 60, CfgFlag::PER_GAME),
|
|
ConfigSetting("TiltAnalogDeadzoneRadius", SETTING(g_Config, fTiltAnalogDeadzoneRadius), 0.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("TiltInverseDeadzone", SETTING(g_Config, fTiltInverseDeadzone), 0.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("TiltCircularDeadzone", SETTING(g_Config, bTiltCircularDeadzone), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("TiltInputType", SETTING(g_Config, iTiltInputType), 1, CfgFlag::PER_GAME),
|
|
ConfigSetting("TiltInputEnabled", SETTING(g_Config, bTiltInputEnabled), false, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("DisableDpadDiagonals", SETTING(g_Config, bDisableDpadDiagonals), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("GamepadOnlyFocused", SETTING(g_Config, bGamepadOnlyFocused), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("TouchButtonStyle", SETTING(g_Config, iTouchButtonStyle), 1, CfgFlag::PER_GAME),
|
|
ConfigSetting("TouchButtonOpacity", SETTING(g_Config, iTouchButtonOpacity), 65, CfgFlag::PER_GAME),
|
|
ConfigSetting("TouchButtonHideSeconds", SETTING(g_Config, iTouchButtonHideSeconds), 8, CfgFlag::PER_GAME),
|
|
ConfigSetting("AutoCenterTouchAnalog", SETTING(g_Config, bAutoCenterTouchAnalog), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("StickyTouchDPad", SETTING(g_Config, bStickyTouchDPad), false, CfgFlag::PER_GAME),
|
|
|
|
// Snap touch control position
|
|
ConfigSetting("TouchSnapToGrid", SETTING(g_Config, bTouchSnapToGrid), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("TouchSnapGridSize", SETTING(g_Config, iTouchSnapGridSize), 64, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("AnalogDeadzone", SETTING(g_Config, fAnalogDeadzone), 0.15f, CfgFlag::PER_GAME),
|
|
ConfigSetting("AnalogInverseDeadzone", SETTING(g_Config, fAnalogInverseDeadzone), 0.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("AnalogSensitivity", SETTING(g_Config, fAnalogSensitivity), 1.1f, CfgFlag::PER_GAME),
|
|
ConfigSetting("AnalogIsCircular", SETTING(g_Config, bAnalogIsCircular), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("AnalogAutoRotSpeed", SETTING(g_Config, fAnalogAutoRotSpeed), 8.0f, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("AnalogLimiterDeadzone", SETTING(g_Config, fAnalogLimiterDeadzone), 0.6f, CfgFlag::DEFAULT),
|
|
ConfigSetting("AnalogTriggerThreshold", SETTING(g_Config, fAnalogTriggerThreshold), 0.75f, CfgFlag::DEFAULT),
|
|
ConfigSetting("AnalogStickThreshold", SETTING(g_Config, fAnalogStickThreshold), 0.75f, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("AllowMappingCombos", SETTING(g_Config, bAllowMappingCombos), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("StrictComboOrder", SETTING(g_Config, bStrictComboOrder), false, CfgFlag::DEFAULT),
|
|
|
|
ConfigSetting("UseMouse", SETTING(g_Config, bMouseControl), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("ConfineMap", SETTING(g_Config, bMouseConfine), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("MouseSensitivity", SETTING(g_Config, fMouseSensitivity), 0.1f, CfgFlag::PER_GAME),
|
|
ConfigSetting("MouseSmoothing", SETTING(g_Config, fMouseSmoothing), 0.9f, CfgFlag::PER_GAME),
|
|
ConfigSetting("MouseWheelUpDelayMs", SETTING(g_Config, iMouseWheelUpDelayMs), 80, CfgFlag::PER_GAME),
|
|
|
|
ConfigSetting("SystemControls", SETTING(g_Config, bSystemControls), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("RapidFileInterval", SETTING(g_Config, iRapidFireInterval), 5, CfgFlag::DEFAULT),
|
|
};
|
|
|
|
static const std::vector<std::string_view> emptyList;
|
|
|
|
static const ConfigSetting networkSettings[] = {
|
|
ConfigSetting("EnableWlan", SETTING(g_Config, bEnableWlan), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("EnableAdhocServer", SETTING(g_Config, bEnableAdhocServer), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("proAdhocServer", SETTING(g_Config, sProAdhocServer), &DefaultProAdhocServer, CfgFlag::PER_GAME),
|
|
ConfigSetting("AdhocServerRelayMode", SETTING(g_Config, iAdhocServerRelayMode), (int)AdhocServerRelayMode::Auto, CfgFlag::PER_GAME),
|
|
ConfigSetting("PortOffset", SETTING(g_Config, iPortOffset), 10000, CfgFlag::PER_GAME),
|
|
ConfigSetting("PrimaryDNSServer", SETTING(g_Config, sInfrastructureDNSServer), "67.222.156.250", CfgFlag::PER_GAME),
|
|
ConfigSetting("MinTimeout", SETTING(g_Config, iMinTimeout), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("ForcedFirstConnect", SETTING(g_Config, bForcedFirstConnect), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("EnableUPnP", SETTING(g_Config, bEnableUPnP), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("UPnPUseOriginalPort", SETTING(g_Config, bUPnPUseOriginalPort), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("InfrastructureUsername", SETTING(g_Config, sInfrastructureUsername), &DefaultInfrastructureUsername, CfgFlag::PER_GAME),
|
|
ConfigSetting("InfrastructureAutoDNS", SETTING(g_Config, bInfrastructureAutoDNS), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("AllowSavestateWhileConnected", SETTING(g_Config, bAllowSavestateWhileConnected), false, CfgFlag::DONT_SAVE),
|
|
ConfigSetting("AllowSpeedControlWhileConnected", SETTING(g_Config, bAllowSpeedControlWhileConnected), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("DontDownloadInfraJson", SETTING(g_Config, bDontDownloadInfraJson), false, CfgFlag::DONT_SAVE),
|
|
ConfigSetting("proAdhocServerList", SETTING(g_Config, vCustomAdhocServerList), &emptyList, CfgFlag::DEFAULT), // Customizable server list.
|
|
ConfigSetting("RelayAdhocServerList", SETTING(g_Config, vCustomAdhocServerListWithRelay), &emptyList, CfgFlag::DEFAULT), // Customizable server list.
|
|
ConfigSetting("AdhocServerListUrl", SETTING(g_Config, sAdhocServerListUrl), "http://metadata.ppsspp.org/adhoc-servers.json", CfgFlag::DEFAULT), // URL for the server list. Can be set to a local path too.
|
|
ConfigSetting("EnableNetworkChat", SETTING(g_Config, bEnableNetworkChat), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("ChatButtonPosition", SETTING(g_Config, iChatButtonPosition), (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME),
|
|
ConfigSetting("ChatScreenPosition", SETTING(g_Config, iChatScreenPosition), (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME),
|
|
ConfigSetting("EnableQuickChat", SETTING(g_Config, bEnableQuickChat), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("QuickChat1", SETTING(g_Config, sQuickChat[0]), "Quick Chat 1", CfgFlag::PER_GAME),
|
|
ConfigSetting("QuickChat2", SETTING(g_Config, sQuickChat[1]), "Quick Chat 2", CfgFlag::PER_GAME),
|
|
ConfigSetting("QuickChat3", SETTING(g_Config, sQuickChat[2]), "Quick Chat 3", CfgFlag::PER_GAME),
|
|
ConfigSetting("QuickChat4", SETTING(g_Config, sQuickChat[3]), "Quick Chat 4", CfgFlag::PER_GAME),
|
|
ConfigSetting("QuickChat5", SETTING(g_Config, sQuickChat[4]), "Quick Chat 5", CfgFlag::PER_GAME),
|
|
};
|
|
|
|
static const ConfigSetting systemParamSettings[] = {
|
|
ConfigSetting("PSPModel", SETTING(g_Config, iPSPModel), PSP_MODEL_SLIM, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("PSPFirmwareVersion", SETTING(g_Config, iFirmwareVersion), PSP_DEFAULT_FIRMWARE, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("NickName", SETTING(g_Config, sNickName), "PPSSPP", CfgFlag::PER_GAME),
|
|
ConfigSetting("MacAddress", SETTING(g_Config, sMACAddress), "", CfgFlag::PER_GAME),
|
|
ConfigSetting("GameLanguage", SETTING(g_Config, iLanguage), -1, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("ParamTimeFormat", SETTING(g_Config, iTimeFormat), PSP_SYSTEMPARAM_TIME_FORMAT_24HR, CfgFlag::PER_GAME),
|
|
ConfigSetting("ParamDateFormat", SETTING(g_Config, iDateFormat), PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD, CfgFlag::PER_GAME),
|
|
ConfigSetting("TimeZone", SETTING(g_Config, iTimeZone), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("DayLightSavings", SETTING(g_Config, bDayLightSavings), (bool) PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_STD, CfgFlag::PER_GAME),
|
|
ConfigSetting("ButtonPreference", SETTING(g_Config, iButtonPreference), PSP_SYSTEMPARAM_BUTTON_CROSS, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("LockParentalLevel", SETTING(g_Config, iLockParentalLevel), 0, CfgFlag::PER_GAME),
|
|
ConfigSetting("WlanAdhocChannel", SETTING(g_Config, iWlanAdhocChannel), PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC, CfgFlag::PER_GAME),
|
|
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
|
|
ConfigSetting("BypassOSKWithKeyboard", SETTING(g_Config, bBypassOSKWithKeyboard), false, CfgFlag::PER_GAME),
|
|
#endif
|
|
ConfigSetting("WlanPowerSave", SETTING(g_Config, bWlanPowerSave), (bool) PSP_SYSTEMPARAM_WLAN_POWERSAVE_OFF, CfgFlag::PER_GAME),
|
|
ConfigSetting("EncryptSave", SETTING(g_Config, bEncryptSave), true, CfgFlag::PER_GAME | CfgFlag::REPORT),
|
|
ConfigSetting("MemStickSize", SETTING(g_Config, iMemStickSizeGB), 16, CfgFlag::DEFAULT),
|
|
};
|
|
|
|
static const ConfigSetting debuggerSettings[] = {
|
|
ConfigSetting("DisasmWindowX", SETTING(g_Config, iDisasmWindowX), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("DisasmWindowY", SETTING(g_Config, iDisasmWindowY), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("DisasmWindowW", SETTING(g_Config, iDisasmWindowW), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("DisasmWindowH", SETTING(g_Config, iDisasmWindowH), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("GEWindowX", SETTING(g_Config, iGEWindowX), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("GEWindowY", SETTING(g_Config, iGEWindowY), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("GEWindowW", SETTING(g_Config, iGEWindowW), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("GEWindowH", SETTING(g_Config, iGEWindowH), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("GEWindowTabsBL", SETTING(g_Config, uGETabsLeft), (uint32_t)0, CfgFlag::DEFAULT),
|
|
ConfigSetting("GEWindowTabsBR", SETTING(g_Config, uGETabsRight), (uint32_t)0, CfgFlag::DEFAULT),
|
|
ConfigSetting("GEWindowTabsTR", SETTING(g_Config, uGETabsTopRight), (uint32_t)0, CfgFlag::DEFAULT),
|
|
ConfigSetting("ConsoleWindowX", SETTING(g_Config, iConsoleWindowX), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("ConsoleWindowY", SETTING(g_Config, iConsoleWindowY), -1, CfgFlag::DEFAULT),
|
|
ConfigSetting("FontWidth", SETTING(g_Config, iFontWidth), 8, CfgFlag::DEFAULT),
|
|
ConfigSetting("FontHeight", SETTING(g_Config, iFontHeight), 12, CfgFlag::DEFAULT),
|
|
ConfigSetting("ShowBottomTabTitles",SETTING(g_Config, bShowBottomTabTitles), true, CfgFlag::DEFAULT),
|
|
ConfigSetting("ShowDeveloperMenu", SETTING(g_Config, bShowDeveloperMenu), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("SkipDeadbeefFilling", SETTING(g_Config, bSkipDeadbeefFilling), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("FuncHashMap", SETTING(g_Config, bFuncHashMap), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("SkipFuncHashMap", SETTING(g_Config, sSkipFuncHashMap), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("MemInfoDetailed", SETTING(g_Config, bDebugMemInfoDetailed), false, CfgFlag::DEFAULT),
|
|
};
|
|
|
|
static const ConfigSetting jitSettings[] = {
|
|
ConfigSetting("DiscardRegsOnJRRA", SETTING(g_Config, bDiscardRegsOnJRRA), false, CfgFlag::DONT_SAVE | CfgFlag::REPORT),
|
|
};
|
|
|
|
static const ConfigSetting upgradeSettings[] = {
|
|
ConfigSetting("UpgradeMessage", SETTING(g_Config, sUpgradeMessage), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("UpgradeVersion", SETTING(g_Config, sUpgradeVersion), "", CfgFlag::DEFAULT),
|
|
ConfigSetting("DismissedVersion", SETTING(g_Config, sDismissedVersion), "", CfgFlag::DEFAULT),
|
|
};
|
|
|
|
static const ConfigSetting themeSettings[] = {
|
|
ConfigSetting("ThemeName", SETTING(g_Config, sThemeName), "Default", CfgFlag::DEFAULT),
|
|
};
|
|
|
|
static const ConfigSetting vrSettings[] = {
|
|
ConfigSetting("VREnable", SETTING(g_Config, bEnableVR), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("VREnable6DoF", SETTING(g_Config, bEnable6DoF), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("VREnableStereo", SETTING(g_Config, bEnableStereo), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRForce72Hz", SETTING(g_Config, bForce72Hz), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRForce", SETTING(g_Config, bForceVR), false, CfgFlag::DEFAULT),
|
|
ConfigSetting("VRImmersiveMode", SETTING(g_Config, bEnableImmersiveVR), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRManualForceVR", SETTING(g_Config, bManualForceVR), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRPassthrough", SETTING(g_Config, bPassthrough), false, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRRescaleHUD", SETTING(g_Config, bRescaleHUD), true, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRCameraDistance", SETTING(g_Config, fCameraDistance), 0.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRCameraHeight", SETTING(g_Config, fCameraHeight), 0.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRCameraSide", SETTING(g_Config, fCameraSide), 0.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRCameraPitch", SETTING(g_Config, fCameraPitch), 0.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRCanvasDistance", SETTING(g_Config, fCanvasDistance), 12.0f, CfgFlag::DEFAULT),
|
|
ConfigSetting("VRCanvas3DDistance", SETTING(g_Config, fCanvas3DDistance), 3.0f, CfgFlag::DEFAULT),
|
|
ConfigSetting("VRFieldOfView", SETTING(g_Config, fFieldOfViewPercentage), 100.0f, CfgFlag::PER_GAME),
|
|
ConfigSetting("VRHeadUpDisplayScale", SETTING(g_Config, fHeadUpDisplayScale), 0.3f, CfgFlag::PER_GAME),
|
|
};
|
|
|
|
// The first column says what structure the parameters are relative to.
|
|
static const ConfigSectionMeta g_sectionMeta[] = {
|
|
{ &g_Config, generalSettings, ARRAY_SIZE(generalSettings), "General" },
|
|
{ &g_Config, cpuSettings, ARRAY_SIZE(cpuSettings), "CPU" },
|
|
{ &g_Config, graphicsSettings, ARRAY_SIZE(graphicsSettings), "Graphics" },
|
|
{ &g_Config, soundSettings, ARRAY_SIZE(soundSettings), "Sound" },
|
|
{ &g_Config, controlSettings, ARRAY_SIZE(controlSettings), "Control" },
|
|
{ &g_Config, systemParamSettings, ARRAY_SIZE(systemParamSettings), "SystemParam" },
|
|
{ &g_Config, networkSettings, ARRAY_SIZE(networkSettings), "Network" },
|
|
{ &g_Config, debuggerSettings, ARRAY_SIZE(debuggerSettings), "Debugger" },
|
|
{ &g_Config, jitSettings, ARRAY_SIZE(jitSettings), "JIT" },
|
|
{ &g_Config, themeSettings, ARRAY_SIZE(themeSettings), "Theme" },
|
|
{ &g_Config, vrSettings, ARRAY_SIZE(vrSettings), "VR" },
|
|
{ &g_Config, achievementSettings, ARRAY_SIZE(achievementSettings), "Achievements" },
|
|
{ &g_Config, upgradeSettings, ARRAY_SIZE(upgradeSettings), "Upgrade" },
|
|
{ &g_Config.displayLayoutLandscape, displayLayoutSettings, ARRAY_SIZE(displayLayoutSettings), "DisplayLayout.Landscape", "Graphics" }, // We read the old settings from [Graphics], since most people played in landscape before.
|
|
{ &g_Config.displayLayoutPortrait, displayLayoutSettings, ARRAY_SIZE(displayLayoutSettings), "DisplayLayout.Portrait"}, // These we don't want to read from the old settings, since for most people, those settings will be bad.
|
|
{ &g_Config.touchControlsLandscape, touchControlSettings, ARRAY_SIZE(touchControlSettings), "TouchControls.Landscape", "Control" }, // We read the old settings from [Control], since most people played in landscape before.
|
|
{ &g_Config.touchControlsPortrait, touchControlSettings, ARRAY_SIZE(touchControlSettings), "TouchControls.Portrait"}, // These we don't want to read from the old settings, since for most people, those settings will be bad.
|
|
{ &g_Config.gestureControls[0], gestureControlSettings, ARRAY_SIZE(gestureControlSettings), "GestureControls.Left", "General"}, // We read the old settings from [General], since most of them used to be there (except the analog stuff).
|
|
{ &g_Config.gestureControls[1], gestureControlSettings, ARRAY_SIZE(gestureControlSettings), "GestureControls.Right", "General"}, // We read the old settings from [General], since most of them used to be there (except the analog stuff).
|
|
};
|
|
|
|
ConfigBlock *GetConfigBlockForSection(std::string_view sectionName) {
|
|
for (const ConfigSectionMeta &meta : g_sectionMeta) {
|
|
if (equals(meta.section, sectionName)) {
|
|
return meta.configBlock;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const size_t numSections = ARRAY_SIZE(g_sectionMeta);
|
|
|
|
std::map<const void *, std::pair<const ConfigBlock *, const ConfigSetting *>> &Config::getPtrLUT() {
|
|
static std::map<const void *, std::pair<const ConfigBlock *, const ConfigSetting *>> lut;
|
|
return lut;
|
|
}
|
|
|
|
int Config::GetDefaultValueInt(int *configSetting) {
|
|
const auto &lut = getPtrLUT();
|
|
auto it = lut.find(configSetting);
|
|
if (it != lut.end()) {
|
|
const ConfigBlock *block = it->second.first;
|
|
if (block->CanResetToDefault()) {
|
|
// TODO: Support this.
|
|
_dbg_assert_(false);
|
|
return NO_DEFAULT_INT;
|
|
}
|
|
const ConfigSetting *setting = it->second.second;
|
|
return setting->GetDefaultInt();
|
|
}
|
|
return NO_DEFAULT_INT;
|
|
}
|
|
|
|
void Config::Init() {
|
|
// Initialize the pointer->setting lookup map.
|
|
auto &ref = getPtrLUT();
|
|
for (size_t i = 0; i < numSections; ++i) {
|
|
ConfigBlock *configBlock = g_sectionMeta[i].configBlock;
|
|
for (size_t j = 0; j < g_sectionMeta[i].settingsCount; j++) {
|
|
const void *ptr = g_sectionMeta[i].settings[j].GetVoidPtr(configBlock);
|
|
ref[ptr] = std::make_pair(configBlock, &g_sectionMeta[i].settings[j]);
|
|
}
|
|
}
|
|
_dbg_assert_(ref.size() > 1);
|
|
}
|
|
|
|
Config::~Config() {
|
|
if (bUpdatedInstanceCounter) {
|
|
ShutdownInstanceCounter();
|
|
}
|
|
}
|
|
|
|
void Config::Reload() {
|
|
inReload_ = true;
|
|
Load();
|
|
inReload_ = false;
|
|
}
|
|
|
|
// Call this if you change the search path (such as when changing memstick directory. can't
|
|
// really think of any other legit uses).
|
|
void Config::UpdateIniLocation(const char *iniFileName, const char *controllerIniFilename) {
|
|
const bool useIniFilename = iniFileName != nullptr && strlen(iniFileName) > 0;
|
|
const char *ppssppIniFilename = IsVREnabled() ? "ppssppvr.ini" : "ppsspp.ini";
|
|
bool exists;
|
|
iniFilename_ = FindConfigFile(searchPath_, useIniFilename ? iniFileName : ppssppIniFilename, &exists);
|
|
const bool useControllerIniFilename = controllerIniFilename != nullptr && strlen(controllerIniFilename) > 0;
|
|
const char *controlsIniFilename = IsVREnabled() ? "controlsvr.ini" : "controls.ini";
|
|
controllerIniFilename_ = FindConfigFile(searchPath_, useControllerIniFilename ? controllerIniFilename : controlsIniFilename, &exists);
|
|
}
|
|
|
|
bool Config::LoadAppendedConfig() {
|
|
IniFile iniFile;
|
|
if (!iniFile.Load(appendedConfigFileName_)) {
|
|
ERROR_LOG(Log::Loader, "Failed to read appended config '%s'.", appendedConfigFileName_.c_str());
|
|
return false;
|
|
}
|
|
|
|
for (const ConfigSectionMeta &meta : g_sectionMeta) {
|
|
Section *section = iniFile.GetSection(meta.section);
|
|
if (!section) {
|
|
continue;
|
|
}
|
|
for (size_t j = 0; j < meta.settingsCount; j++) {
|
|
meta.settings[j].ReadFromIniSection(meta.configBlock, section, false);
|
|
}
|
|
}
|
|
|
|
INFO_LOG(Log::Loader, "Loaded appended config '%s'.", appendedConfigFileName_.c_str());
|
|
|
|
Save("Loaded appended config"); // Let's prevent reset
|
|
return true;
|
|
}
|
|
|
|
void Config::UpdateAfterSettingAutoFrameSkip() {
|
|
if (bAutoFrameSkip && bSkipBufferEffects) {
|
|
bSkipBufferEffects = false;
|
|
}
|
|
}
|
|
|
|
void Config::ReadAllSettings(const IniFile &iniFile) {
|
|
// Read settings. Note, configblocks can now support their own defaulting mechanism.
|
|
for (const ConfigSectionMeta &meta : g_sectionMeta) {
|
|
const Section *section = iniFile.GetSection(meta.section);
|
|
ConfigBlock *configBlock = meta.configBlock;
|
|
// Not found? Try the fallback (to upgrade settings that have been moved from old sections).
|
|
if (!section && !meta.fallbackSectionName.empty()) {
|
|
section = iniFile.GetSection(meta.fallbackSectionName);
|
|
// NOTE: it's tempting to update the configBlock here, but that's not what we want to do!
|
|
// We just want to read from a different section in the ini file, we still want to read into
|
|
// the same configBlock.
|
|
}
|
|
// If section is still null, we'll handle that gracefully by resetting to defaults.
|
|
bool applyDefaultPerSetting = true;
|
|
if (configBlock->ResetToDefault(meta.section)) {
|
|
applyDefaultPerSetting = false;
|
|
}
|
|
for (size_t j = 0; j < meta.settingsCount; j++) {
|
|
const ConfigSetting &setting = meta.settings[j];
|
|
setting.ReadFromIniSection(configBlock, section, applyDefaultPerSetting);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string Config::GetConfigAsString() {
|
|
Config::Save("beforecopy");
|
|
std::string temp;
|
|
if (File::ReadTextFileToString(iniFilename_, &temp)) {
|
|
return temp;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
|
|
double startTime = time_now_d();
|
|
|
|
if (!bUpdatedInstanceCounter) {
|
|
InitInstanceCounter();
|
|
bUpdatedInstanceCounter = true;
|
|
}
|
|
|
|
g_DownloadManager.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION));
|
|
|
|
UpdateIniLocation(iniFileName, controllerIniFilename);
|
|
|
|
INFO_LOG(Log::Loader, "Loading config: %s", iniFilename_.c_str());
|
|
bSaveSettings = true;
|
|
|
|
IniFile iniFile;
|
|
if (!iniFile.Load(iniFilename_)) {
|
|
WARN_LOG(Log::Loader, "Failed to read '%s'. Setting main config to default.", iniFilename_.c_str());
|
|
// Continue anyway to initialize the config.
|
|
}
|
|
|
|
ReadAllSettings(iniFile);
|
|
|
|
iRunCount++;
|
|
|
|
// For iOS, issue #19211
|
|
TryUpdateSavedPath(¤tDirectory);
|
|
|
|
Section *log = iniFile.GetOrCreateSection(logSectionName);
|
|
|
|
g_logManager.LoadConfig(log);
|
|
|
|
Section *recent = iniFile.GetOrCreateSection("Recent");
|
|
iMaxRecent = 60;
|
|
recent->Get("MaxRecent", &iMaxRecent);
|
|
|
|
// Fix issue from switching from uint (hex in .ini) to int (dec)
|
|
// -1 is okay, though. We'll just ignore recent stuff if it is.
|
|
if (iMaxRecent == 0)
|
|
iMaxRecent = 60;
|
|
|
|
// Fix JIT setting if no longer available.
|
|
if (!System_GetPropertyBool(SYSPROP_CAN_JIT)) {
|
|
if (iCpuCore == (int)CPUCore::JIT || iCpuCore == (int)CPUCore::JIT_IR) {
|
|
WARN_LOG(Log::Loader, "Forcing JIT off due to unavailablility");
|
|
iCpuCore = (int)CPUCore::IR_INTERPRETER;
|
|
}
|
|
}
|
|
|
|
if (iMaxRecent > 0) {
|
|
g_recentFiles.Load(recent, iMaxRecent);
|
|
g_recentFiles.Clean();
|
|
}
|
|
|
|
// Time tracking
|
|
Section *playTime = iniFile.GetOrCreateSection("PlayTime");
|
|
playTimeTracker_.Load(playTime);
|
|
|
|
auto pinnedPaths = iniFile.GetOrCreateSection("PinnedPaths")->ToMap();
|
|
vPinnedPaths.clear();
|
|
for (const auto &[_, value] : pinnedPaths) {
|
|
// Unpin paths that are deleted automatically.
|
|
const std::string &path = value;
|
|
if (startsWith(path, "http://") || startsWith(path, "https://") || File::Exists(Path(path))) {
|
|
vPinnedPaths.push_back(File::ResolvePath(path));
|
|
}
|
|
}
|
|
|
|
// Default values for post process shaders
|
|
bool postShadersInitialized = iniFile.HasSection("PostShaderList");
|
|
Section *postShaderChain = iniFile.GetOrCreateSection("PostShaderList");
|
|
Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting");
|
|
if (IsVREnabled() && !postShadersInitialized) {
|
|
postShaderChain->Set("PostShader1", "ColorCorrection");
|
|
postShaderSetting->Set("ColorCorrectionSettingCurrentValue1", 1.0f);
|
|
postShaderSetting->Set("ColorCorrectionSettingCurrentValue2", 1.5f);
|
|
postShaderSetting->Set("ColorCorrectionSettingCurrentValue3", 1.1f);
|
|
postShaderSetting->Set("ColorCorrectionSettingCurrentValue4", 1.0f);
|
|
}
|
|
|
|
// Load post process shader values
|
|
mPostShaderSetting.clear();
|
|
for (const auto &[key, value] : postShaderSetting->ToMap()) {
|
|
mPostShaderSetting[key] = std::stof(value);
|
|
}
|
|
|
|
const Section *hostOverrideSetting = iniFile.GetOrCreateSection("HostAliases");
|
|
// TODO: relocate me before PR
|
|
mHostToAlias = hostOverrideSetting->ToMap();
|
|
|
|
// Load post process shader names
|
|
vPostShaderNames.clear();
|
|
for (const auto& it : postShaderChain->ToMap()) {
|
|
if (it.second != "Off")
|
|
vPostShaderNames.push_back(it.second);
|
|
}
|
|
|
|
// Force JIT setting to a valid value for the current system configuration.
|
|
if (!System_GetPropertyBool(SYSPROP_CAN_JIT)) {
|
|
if (g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR) {
|
|
g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER;
|
|
}
|
|
}
|
|
|
|
CheckForUpdate();
|
|
|
|
INFO_LOG(Log::Loader, "Loading controller config: %s", controllerIniFilename_.c_str());
|
|
bSaveSettings = true;
|
|
|
|
LoadStandardControllerIni();
|
|
|
|
// So this is all the way down here to overwrite the controller settings
|
|
// sadly it won't benefit from all the "version conversion" going on up-above
|
|
// but these configs shouldn't contain older versions anyhow
|
|
_dbg_assert_(!IsGameSpecific());
|
|
|
|
PostLoadCleanup();
|
|
|
|
INFO_LOG(Log::Loader, "Config loaded: '%s' (%0.1f ms)", iniFilename_.c_str(), (time_now_d() - startTime) * 1000.0);
|
|
}
|
|
|
|
bool Config::ShouldSaveSetting(const void *ptr) const {
|
|
return std::find(settingsNotToSave_.begin(), settingsNotToSave_.end(), ptr) == settingsNotToSave_.end();
|
|
}
|
|
|
|
// If we're in game specific mode, we need to:
|
|
// * Save the game-specific settings to the game-specific ini file.
|
|
// * Then, save the NON-game-specific settings ONLY to the regular ini file!
|
|
bool Config::Save(const char *saveReason) {
|
|
double startTime = time_now_d();
|
|
if (!IsFirstInstance()) {
|
|
// TODO: Should we allow saving config if started from a different directory?
|
|
// How do we tell?
|
|
WARN_LOG(Log::Loader, "Not saving config - secondary instances don't.");
|
|
// Don't want to retry or something.
|
|
return true;
|
|
}
|
|
|
|
if (!iniFilename_.empty() && g_Config.bSaveSettings) {
|
|
if (IsGameSpecific()) {
|
|
// Save just the game-specific settings to the game-specific ini.
|
|
SaveGameConfig(gameId_, ""); // we don't pass a title, it was stored in the ini the first time.
|
|
}
|
|
|
|
PreSaveCleanup();
|
|
|
|
g_recentFiles.Clean();
|
|
IniFile iniFile;
|
|
if (!iniFile.Load(iniFilename_)) {
|
|
WARN_LOG(Log::Loader, "Likely saving config for first time - couldn't read ini '%s'", iniFilename_.c_str());
|
|
}
|
|
|
|
// Need to do this somewhere...
|
|
bFirstRun = false;
|
|
|
|
// Do the writing.
|
|
for (const ConfigSectionMeta &meta : g_sectionMeta) {
|
|
Section *section = iniFile.GetOrCreateSection(meta.section);
|
|
ConfigBlock *configBlock = meta.configBlock;
|
|
for (size_t j = 0; j < meta.settingsCount; j++) {
|
|
if (IsGameSpecific() && (meta.settings[j].Flags() & CfgFlag::PER_GAME)) {
|
|
// Skip per-game settings in non-game-specific ini.
|
|
continue;
|
|
}
|
|
if (!ShouldSaveSetting(meta.settings[j].GetVoidPtr(configBlock))) {
|
|
// Skip settings marked as "don't save".
|
|
INFO_LOG(Log::System, "Not saving setting '%.*s' as marked as don't save.", STR_VIEW(meta.settings[j].IniKey()));
|
|
continue;
|
|
}
|
|
meta.settings[j].WriteToIniSection(configBlock, section);
|
|
}
|
|
}
|
|
|
|
Section *recent = iniFile.GetOrCreateSection("Recent");
|
|
recent->Set("MaxRecent", iMaxRecent);
|
|
g_recentFiles.Save(recent, iMaxRecent);
|
|
|
|
Section *pinnedPaths = iniFile.GetOrCreateSection("PinnedPaths");
|
|
pinnedPaths->Clear();
|
|
for (size_t i = 0; i < vPinnedPaths.size(); ++i) {
|
|
char keyName[64];
|
|
snprintf(keyName, sizeof(keyName), "Path%d", (int)i);
|
|
pinnedPaths->Set(keyName, vPinnedPaths[i]);
|
|
}
|
|
|
|
if (!IsGameSpecific()) {
|
|
// These settings can be game specific, and so are handled in SaveGameConfig().
|
|
|
|
Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting");
|
|
postShaderSetting->Clear();
|
|
for (const auto &[k, v] : mPostShaderSetting) {
|
|
postShaderSetting->Set(k, v);
|
|
}
|
|
Section *postShaderChain = iniFile.GetOrCreateSection("PostShaderList");
|
|
postShaderChain->Clear();
|
|
for (size_t i = 0; i < vPostShaderNames.size(); ++i) {
|
|
char keyName[64];
|
|
snprintf(keyName, sizeof(keyName), "PostShader%d", (int)i+1);
|
|
postShaderChain->Set(keyName, vPostShaderNames[i]);
|
|
}
|
|
}
|
|
|
|
// TODO: relocate me before PR
|
|
Section *hostOverrideSetting = iniFile.GetOrCreateSection("HostAliases");
|
|
hostOverrideSetting->Clear();
|
|
for (auto& it : mHostToAlias) {
|
|
hostOverrideSetting->Set(it.first.c_str(), it.second.c_str());
|
|
}
|
|
|
|
Section *control = iniFile.GetOrCreateSection("Control");
|
|
control->Delete("DPadRadius");
|
|
|
|
Section *log = iniFile.GetOrCreateSection(logSectionName);
|
|
g_logManager.SaveConfig(log);
|
|
|
|
// Time tracking
|
|
Section *playTime = iniFile.GetOrCreateSection("PlayTime");
|
|
playTimeTracker_.Save(playTime);
|
|
|
|
if (!iniFile.Save(iniFilename_)) {
|
|
ERROR_LOG(Log::Loader, "Error saving config (%s) - can't write ini '%s'", saveReason, iniFilename_.c_str());
|
|
return false;
|
|
}
|
|
INFO_LOG(Log::Loader, "Config saved (%s): '%s' (%0.1f ms)", saveReason, iniFilename_.c_str(), (time_now_d() - startTime) * 1000.0);
|
|
|
|
if (!IsGameSpecific()) {
|
|
// These settings can be game specific, and so are handled in SaveGameConfig().
|
|
IniFile controllerIniFile;
|
|
if (!controllerIniFile.Load(controllerIniFilename_)) {
|
|
ERROR_LOG(Log::Loader, "Error saving controller config - can't read ini first '%s'", controllerIniFilename_.c_str());
|
|
}
|
|
KeyMap::SaveToIni(controllerIniFile);
|
|
if (!controllerIniFile.Save(controllerIniFilename_)) {
|
|
ERROR_LOG(Log::Loader, "Error saving config - can't write ini '%s'", controllerIniFilename_.c_str());
|
|
return false;
|
|
}
|
|
INFO_LOG(Log::Loader, "Controller config saved: %s", controllerIniFilename_.c_str());
|
|
}
|
|
|
|
PostSaveCleanup();
|
|
} else {
|
|
INFO_LOG(Log::Loader, "Not saving config");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// A lot more cleanup tasks should be moved into here, and some of these are severely outdated.
|
|
void Config::PostLoadCleanup() {
|
|
// Override ppsspp.ini JIT value to prevent crashing
|
|
jitForcedOff = DefaultCpuCore() != (int)CPUCore::JIT && (g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR);
|
|
if (jitForcedOff) {
|
|
g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER;
|
|
}
|
|
|
|
// This caps the aniso level exponent to 4 (so 16x.). No hardware supports more anyway.
|
|
iAnisotropyLevel = std::clamp(iAnisotropyLevel, 0, 4);
|
|
|
|
if (iGPUBackend == 1) { // d3d9, no longer supported. Fall back to D3D11.
|
|
iGPUBackend = (int)GPUBackend::DIRECT3D11;
|
|
} else if (iGPUBackend < 0 || iGPUBackend > 3) {
|
|
iGPUBackend = (int)DefaultGPUBackend();
|
|
}
|
|
|
|
#if PPSSPP_PLATFORM(UWP)
|
|
// Enforce D3D11.
|
|
iGPUBackend = (int)GPUBackend::DIRECT3D11;
|
|
#endif
|
|
|
|
// Set a default MAC, and correct if it's an old format.
|
|
if (sMACAddress.length() != 17)
|
|
sMACAddress = CreateRandMAC();
|
|
|
|
if (g_Config.bAutoFrameSkip && g_Config.bSkipBufferEffects) {
|
|
g_Config.bSkipBufferEffects = false;
|
|
}
|
|
|
|
// Automatically silence secondary instances. Could be an option I guess, but meh.
|
|
if (PPSSPP_ID > 1) {
|
|
NOTICE_LOG(Log::Audio, "Secondary instance %d - silencing audio", (int)PPSSPP_ID);
|
|
g_Config.iGameVolume = 0;
|
|
}
|
|
|
|
// Automatically switch away from deprecated setting value.
|
|
if (iTexScalingLevel <= 0) {
|
|
iTexScalingLevel = 1;
|
|
}
|
|
|
|
// Remove a legacy value.
|
|
if (g_Config.sCustomDriver == "Default") {
|
|
g_Config.sCustomDriver = "";
|
|
}
|
|
|
|
// Squash unsupported screen rotations.
|
|
if (g_Config.iScreenRotation == ROTATION_LOCKED_VERTICAL180) {
|
|
g_Config.iScreenRotation = ROTATION_LOCKED_VERTICAL;
|
|
}
|
|
|
|
// Clamp save state slot count to somewhat sane limits.
|
|
g_Config.iSaveStateSlotCount = std::clamp(g_Config.iSaveStateSlotCount, 1, 100);
|
|
}
|
|
|
|
void Config::PreSaveCleanup() {
|
|
if (jitForcedOff) {
|
|
// If we forced jit off and it's still set to IR, change it back to jit.
|
|
if (g_Config.iCpuCore == (int)CPUCore::IR_INTERPRETER)
|
|
g_Config.iCpuCore = (int)CPUCore::JIT;
|
|
}
|
|
}
|
|
|
|
void Config::PostSaveCleanup() {
|
|
if (jitForcedOff) {
|
|
// Force JIT off again just in case Config::Save() is called without exiting PPSSPP.
|
|
if (g_Config.iCpuCore == (int)CPUCore::JIT)
|
|
g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER;
|
|
}
|
|
}
|
|
|
|
void Config::NotifyUpdatedCpuCore() {
|
|
if (jitForcedOff && g_Config.iCpuCore == (int)CPUCore::IR_INTERPRETER) {
|
|
// No longer forced off, the user set it to IR jit.
|
|
jitForcedOff = false;
|
|
}
|
|
}
|
|
|
|
bool Config::SupportsUpgradeCheck() const {
|
|
#if PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(LINUX) || PPSSPP_PLATFORM(MACOS) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS_APP_STORE)
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
// Use for debugging the version check without messing with the server
|
|
#define NEW_VERSION_OVERRIDE "v1.100.3-gaaaaaaaaa"
|
|
constexpr int UPDATE_CHECK_FREQ = 1;
|
|
#else
|
|
constexpr int UPDATE_CHECK_FREQ = 5;
|
|
#endif
|
|
|
|
void Config::CheckForUpdate() {
|
|
if (!bCheckForNewVersion || !SupportsUpgradeCheck()) {
|
|
return;
|
|
}
|
|
|
|
const char *gitVer = PPSSPP_GIT_VERSION;
|
|
Version installed(gitVer);
|
|
Version upgrade(sUpgradeVersion);
|
|
const bool versionsValid = installed.IsValid() && upgrade.IsValid();
|
|
|
|
// Do this regardless of iRunCount to prevent a silly bug where one might use an older
|
|
// build of PPSSPP, receive an upgrade notice, then start a newer version, and still receive the upgrade notice,
|
|
// even if said newer version is >= the upgrade found online.
|
|
if ((sDismissedVersion == sUpgradeVersion) || (versionsValid && (installed >= upgrade))) {
|
|
sUpgradeMessage.clear();
|
|
}
|
|
|
|
// Check for new version on every 10 runs.
|
|
// Sometimes the download may not be finished when the main screen shows (if the user dismisses the
|
|
// splash screen quickly), but then we'll just show the notification next time instead, we store the
|
|
// upgrade number in the ini.
|
|
|
|
const bool checkThisTime = iRunCount % UPDATE_CHECK_FREQ == 0;
|
|
if (checkThisTime) {
|
|
const char *versionUrl = "http://www.ppsspp.org/version.json";
|
|
const char *acceptMime = "application/json, text/*; q=0.9, */*; q=0.8";
|
|
g_DownloadManager.StartDownload(versionUrl, Path(), http::RequestFlags::Default, acceptMime, "version", [this](http::Request &download) { VersionJsonDownloadCompleted(download); });
|
|
}
|
|
}
|
|
|
|
void Config::VersionJsonDownloadCompleted(http::Request &download) {
|
|
if (download.ResultCode() != 200) {
|
|
ERROR_LOG(Log::Loader, "Failed to download %s: %d", download.url().c_str(), download.ResultCode());
|
|
return;
|
|
}
|
|
std::string data;
|
|
download.buffer().TakeAll(&data);
|
|
if (data.empty()) {
|
|
ERROR_LOG(Log::Loader, "Version check: Empty data from server!");
|
|
return;
|
|
}
|
|
|
|
json::JsonReader reader(data.c_str(), data.size());
|
|
const json::JsonGet root = reader.root();
|
|
if (!root) {
|
|
ERROR_LOG(Log::Loader, "Failed to parse json");
|
|
return;
|
|
}
|
|
|
|
std::string version;
|
|
root.getString("version", &version);
|
|
|
|
#ifdef NEW_VERSION_OVERRIDE
|
|
version = NEW_VERSION_OVERRIDE;
|
|
#endif
|
|
|
|
const char *gitVer = PPSSPP_GIT_VERSION;
|
|
Version installed(gitVer);
|
|
Version upgrade(version);
|
|
Version dismissed(g_Config.sDismissedVersion);
|
|
|
|
if (!installed.IsValid()) {
|
|
ERROR_LOG(Log::Loader, "Version check: Local version string invalid. Build problems? %s", PPSSPP_GIT_VERSION);
|
|
return;
|
|
}
|
|
if (!upgrade.IsValid()) {
|
|
ERROR_LOG(Log::Loader, "Version check: Invalid server version: %s", version.c_str());
|
|
return;
|
|
}
|
|
|
|
if (installed >= upgrade) {
|
|
INFO_LOG(Log::Loader, "Version check: Already up to date, erasing any upgrade message");
|
|
g_Config.sUpgradeMessage.clear();
|
|
g_Config.sUpgradeVersion = upgrade.ToString();
|
|
g_Config.sDismissedVersion.clear();
|
|
return;
|
|
}
|
|
|
|
if (installed < upgrade && dismissed != upgrade) {
|
|
g_Config.sUpgradeMessage = "New version of PPSSPP available!";
|
|
g_Config.sUpgradeVersion = upgrade.ToString();
|
|
g_Config.sDismissedVersion.clear();
|
|
}
|
|
}
|
|
|
|
bool Config::ShowUpgradeReminder() {
|
|
return !sUpgradeMessage.empty() && !sUpgradeVersion.empty() && sUpgradeVersion != sDismissedVersion;
|
|
}
|
|
|
|
void Config::DismissUpgrade() {
|
|
INFO_LOG(Log::Loader, "Upgrade dismissed for version %s", sUpgradeVersion.c_str());
|
|
sDismissedVersion = sUpgradeVersion;
|
|
sUpgradeMessage.clear();
|
|
}
|
|
|
|
void Config::SetSearchPath(const Path &searchPath) {
|
|
searchPath_ = searchPath;
|
|
}
|
|
|
|
void Config::RestoreDefaults(RestoreSettingsBits whatToRestore, bool log) {
|
|
if (IsGameSpecific()) {
|
|
// TODO: This could be done in a cleaner way.
|
|
DeleteGameConfig(gameId_);
|
|
CreateGameConfig(gameId_);
|
|
Load();
|
|
} else {
|
|
if (whatToRestore & RestoreSettingsBits::SETTINGS) {
|
|
// Read settings. Note, ConfigBlocks can now support their own defaulting mechanism.
|
|
for (const auto &meta : g_sectionMeta) {
|
|
ConfigBlock *configBlock = meta.configBlock;
|
|
bool applyDefaultPerSetting = true;
|
|
if (!configBlock->ResetToDefault(meta.section)) {
|
|
// Reset the settings one by one.
|
|
for (size_t j = 0; j < meta.settingsCount; j++) {
|
|
meta.settings[j].RestoreToDefault(configBlock, log);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (whatToRestore & RestoreSettingsBits::CONTROLS) {
|
|
KeyMap::RestoreDefault();
|
|
}
|
|
|
|
if (whatToRestore & RestoreSettingsBits::RECENT) {
|
|
g_recentFiles.Clear();
|
|
currentDirectory = defaultCurrentDirectory;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Config::HasGameConfig(std::string_view gameId) {
|
|
if (gameId.empty()) {
|
|
return false;
|
|
}
|
|
bool exists = false;
|
|
Path fullIniFilePath = GetGameConfigFilePath(searchPath_, gameId, &exists);
|
|
return exists;
|
|
}
|
|
|
|
bool Config::CreateGameConfig(std::string_view gameId) {
|
|
if (gameId.empty()) {
|
|
return false;
|
|
}
|
|
bool exists;
|
|
Path fullIniFilePath = GetGameConfigFilePath(searchPath_, gameId, &exists);
|
|
|
|
if (exists) {
|
|
INFO_LOG(Log::System, "Game config already exists");
|
|
return false;
|
|
}
|
|
|
|
File::CreateEmptyFile(fullIniFilePath);
|
|
return true;
|
|
}
|
|
|
|
bool Config::DeleteGameConfig(std::string_view gameId) {
|
|
bool exists = false;
|
|
Path fullIniFilePath = GetGameConfigFilePath(searchPath_, gameId, &exists);
|
|
|
|
if (exists) {
|
|
if (System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN)) {
|
|
System_MoveToTrash(fullIniFilePath);
|
|
} else {
|
|
File::Delete(fullIniFilePath);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Config::SaveGameConfig(const std::string &gameId, std::string_view titleForComment) {
|
|
if (gameId.empty()) {
|
|
return false;
|
|
}
|
|
|
|
if (gameId_.empty()) {
|
|
INFO_LOG(Log::G3D, "Switching to game-specific mode for saving config: %s", gameId.c_str());
|
|
gameId_ = gameId;
|
|
}
|
|
|
|
bool exists;
|
|
Path fullIniFilePath = GetGameConfigFilePath(searchPath_, gameId, &exists);
|
|
|
|
IniFile iniFile;
|
|
|
|
// Just like regular configs, we should load and save, in order to preserve things like comments.
|
|
iniFile.Load(fullIniFilePath);
|
|
|
|
Section *top = iniFile.GetOrCreateSection("");
|
|
if (top->Lines().empty() && !titleForComment.empty()) {
|
|
top->AddComment(StringFromFormat("Game config for %s - %.*s", gameId.c_str(), STR_VIEW(titleForComment)));
|
|
}
|
|
|
|
PreSaveCleanup();
|
|
|
|
// Do all the actual saving.
|
|
for (const ConfigSectionMeta &meta : g_sectionMeta) {
|
|
Section *section = iniFile.GetOrCreateSection(meta.section);
|
|
ConfigBlock *configBlock = meta.configBlock;
|
|
for (size_t j = 0; j < meta.settingsCount; j++) {
|
|
if (meta.settings[j].PerGame()) {
|
|
meta.settings[j].WriteToIniSection(configBlock, section);
|
|
}
|
|
}
|
|
}
|
|
|
|
Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting");
|
|
postShaderSetting->Clear();
|
|
for (const auto &[k, v] : mPostShaderSetting) {
|
|
postShaderSetting->Set(k, v);
|
|
}
|
|
|
|
Section *postShaderChain = iniFile.GetOrCreateSection("PostShaderList");
|
|
postShaderChain->Clear();
|
|
for (size_t i = 0; i < vPostShaderNames.size(); ++i) {
|
|
char keyName[64];
|
|
snprintf(keyName, sizeof(keyName), "PostShader%d", (int)i+1);
|
|
postShaderChain->Set(keyName, vPostShaderNames[i]);
|
|
}
|
|
|
|
KeyMap::SaveToIni(iniFile);
|
|
iniFile.Save(fullIniFilePath);
|
|
|
|
INFO_LOG(Log::Loader, "Game-specific config saved: '%s'", fullIniFilePath.c_str());
|
|
|
|
PostSaveCleanup();
|
|
return true;
|
|
}
|
|
|
|
bool Config::LoadGameConfig(const std::string &gameId) {
|
|
bool exists;
|
|
Path iniFileNameFull = GetGameConfigFilePath(searchPath_, gameId, &exists);
|
|
if (!exists) {
|
|
// Bail if there's no game-specific config.
|
|
DEBUG_LOG(Log::Loader, "No game-specific settings found in %s. Using global defaults.", iniFileNameFull.c_str());
|
|
return false;
|
|
}
|
|
|
|
// Switch to game specific mode, if we're not in it.
|
|
if (gameId_.empty()) {
|
|
INFO_LOG(Log::Loader, "Switching to game specific mode before load: %s", gameId.c_str());
|
|
gameId_ = gameId;
|
|
}
|
|
|
|
IniFile iniFile;
|
|
iniFile.Load(iniFileNameFull);
|
|
|
|
auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap();
|
|
mPostShaderSetting.clear();
|
|
for (const auto &[k, v] : postShaderSetting) {
|
|
float value = 0.0f;
|
|
if (sscanf(v.c_str(), "%f", &value)) {
|
|
mPostShaderSetting[k] = value;
|
|
} else {
|
|
WARN_LOG(Log::Loader, "Invalid float value string for param %s: '%s'", k.c_str(), v.c_str());
|
|
}
|
|
}
|
|
|
|
auto postShaderChain = iniFile.GetOrCreateSection("PostShaderList")->ToMap();
|
|
vPostShaderNames.clear();
|
|
for (const auto &[_, v] : postShaderChain) {
|
|
if (v != "Off")
|
|
vPostShaderNames.push_back(v);
|
|
}
|
|
|
|
for (const ConfigSectionMeta &meta : g_sectionMeta) {
|
|
Section *section = iniFile.GetSection(meta.section);
|
|
ConfigBlock *configBlock = meta.configBlock;
|
|
// Not found? Try the fallback (to upgrade settings that have been moved from old sections).
|
|
if (!section && !meta.fallbackSectionName.empty()) {
|
|
section = iniFile.GetSection(meta.fallbackSectionName);
|
|
// NOTE: it's tempting to update the configBlock here, but that's not what we want to do!
|
|
// We just want to read from a different section in the ini file, we still want to read into
|
|
// the same configBlock.
|
|
}
|
|
for (size_t j = 0; j < meta.settingsCount; j++) {
|
|
meta.settings[j].ReadFromIniSection(configBlock, section, false);
|
|
}
|
|
}
|
|
|
|
KeyMap::LoadFromIni(iniFile);
|
|
|
|
if (!appendedConfigFileName_.ToString().empty() &&
|
|
std::find(appendedConfigUpdatedGames_.begin(), appendedConfigUpdatedGames_.end(), gameId) == appendedConfigUpdatedGames_.end()) {
|
|
|
|
LoadAppendedConfig();
|
|
appendedConfigUpdatedGames_.push_back(gameId);
|
|
}
|
|
|
|
PostLoadCleanup();
|
|
|
|
DEBUG_LOG(Log::Loader, "Game-specific config loaded: %s", gameId_.c_str());
|
|
return true;
|
|
}
|
|
|
|
void Config::UnloadGameConfig() {
|
|
_dbg_assert_(IsGameSpecific());
|
|
|
|
// Leave game-specific mode.
|
|
gameId_.clear();
|
|
|
|
// Reload all settings from the main ini file.
|
|
IniFile iniFile;
|
|
iniFile.Load(iniFilename_);
|
|
|
|
ReadAllSettings(iniFile);
|
|
|
|
auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap();
|
|
mPostShaderSetting.clear();
|
|
for (const auto &[k, v] : postShaderSetting) {
|
|
mPostShaderSetting[k] = std::stof(v);
|
|
}
|
|
|
|
auto postShaderChain = iniFile.GetOrCreateSection("PostShaderList")->ToMap();
|
|
vPostShaderNames.clear();
|
|
for (const auto &[k, v] : postShaderChain) {
|
|
if (v != "Off")
|
|
vPostShaderNames.push_back(v);
|
|
}
|
|
|
|
LoadStandardControllerIni();
|
|
PostLoadCleanup();
|
|
}
|
|
|
|
void Config::LoadStandardControllerIni() {
|
|
IniFile controllerIniFile;
|
|
if (!controllerIniFile.Load(controllerIniFilename_)) {
|
|
WARN_LOG(Log::Loader, "Failed to read '%s'. Setting controller config to default.", controllerIniFilename_.c_str());
|
|
KeyMap::RestoreDefault();
|
|
} else {
|
|
// Continue anyway to initialize the config. It will just restore the defaults.
|
|
KeyMap::LoadFromIni(controllerIniFile);
|
|
}
|
|
}
|
|
|
|
void Config::GetReportingInfo(UrlEncoder &data) const {
|
|
for (const ConfigSectionMeta &meta : g_sectionMeta) {
|
|
const std::string prefix = join("config.", meta.section);
|
|
ConfigBlock *configBlock = meta.configBlock;
|
|
for (size_t j = 0; j < meta.settingsCount; j++) {
|
|
meta.settings[j].ReportSetting(configBlock, data, prefix);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlayTimeTracker::Start(std::string_view gameId) {
|
|
if (gameId.empty()) {
|
|
return;
|
|
}
|
|
VERBOSE_LOG(Log::System, "GameTimeTracker::Start(%.*s)", STR_VIEW(gameId));
|
|
|
|
auto iter = tracker_.find(gameId);
|
|
if (iter != tracker_.end()) {
|
|
if (iter->second.startTime == 0.0) {
|
|
iter->second.lastTimePlayed = time_now_unix_utc();
|
|
iter->second.startTime = time_now_d();
|
|
}
|
|
return;
|
|
}
|
|
|
|
PlayTime playTime;
|
|
playTime.lastTimePlayed = time_now_unix_utc();
|
|
playTime.totalTimePlayed = 0.0;
|
|
playTime.startTime = time_now_d();
|
|
tracker_[std::string(gameId)] = playTime;
|
|
}
|
|
|
|
void PlayTimeTracker::Stop(std::string_view gameId) {
|
|
if (gameId.empty()) {
|
|
return;
|
|
}
|
|
|
|
VERBOSE_LOG(Log::System, "GameTimeTracker::Stop(%.*s)", STR_VIEW(gameId));
|
|
|
|
auto iter = tracker_.find(gameId);
|
|
if (iter != tracker_.end()) {
|
|
if (iter->second.startTime != 0.0) {
|
|
iter->second.totalTimePlayed += time_now_d() - iter->second.startTime;
|
|
iter->second.startTime = 0.0;
|
|
}
|
|
iter->second.lastTimePlayed = time_now_unix_utc();
|
|
return;
|
|
}
|
|
|
|
// Can happen if boot gets cancelled. Not worth warn-logging.
|
|
DEBUG_LOG(Log::System, "GameTimeTracker::Stop called without corresponding GameTimeTracker::Start");
|
|
}
|
|
|
|
void PlayTimeTracker::Reset(std::string_view gameId) {
|
|
auto iter = tracker_.find(gameId);
|
|
if (iter != tracker_.end()) {
|
|
iter->second.lastTimePlayed = 0;
|
|
iter->second.totalTimePlayed = 0;
|
|
iter->second.startTime = 0.0;
|
|
}
|
|
}
|
|
|
|
void PlayTimeTracker::Load(const Section *section) {
|
|
tracker_.clear();
|
|
|
|
const auto map = section->ToMap();
|
|
|
|
for (const auto &iter : map) {
|
|
const std::string &value = iter.second;
|
|
// Parse the string.
|
|
PlayTime gameTime{};
|
|
if (2 == sscanf(value.c_str(), "%d,%llu", &gameTime.totalTimePlayed, (long long *)&gameTime.lastTimePlayed)) {
|
|
tracker_[iter.first] = gameTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlayTimeTracker::Save(Section *section) {
|
|
for (auto &iter : tracker_) {
|
|
std::string formatted = StringFromFormat("%d,%llu", iter.second.totalTimePlayed, iter.second.lastTimePlayed);
|
|
section->Set(iter.first, formatted);
|
|
}
|
|
}
|
|
|
|
bool PlayTimeTracker::GetPlayedTimeString(std::string_view gameId, std::string *str) const {
|
|
auto ga = GetI18NCategory(I18NCat::GAME);
|
|
|
|
auto iter = tracker_.find(gameId);
|
|
if (iter == tracker_.end()) {
|
|
return false;
|
|
}
|
|
|
|
int totalSeconds = iter->second.totalTimePlayed;
|
|
const int seconds = totalSeconds % 60;
|
|
totalSeconds /= 60;
|
|
const int minutes = totalSeconds % 60;
|
|
totalSeconds /= 60;
|
|
const int hours = totalSeconds;
|
|
|
|
*str = ApplySafeSubstitutions(ga->T("Time Played: %1h %2m %3s"), hours, minutes, seconds);
|
|
return true;
|
|
}
|
|
|
|
// This matches exactly the old shift-based curve.
|
|
float Volume10ToMultiplier(int volume) {
|
|
// Allow muting entirely.
|
|
if (volume <= 0) {
|
|
return 0.0f;
|
|
}
|
|
return powf(2.0f, (float)(volume - 10));
|
|
}
|
|
|
|
// NOTE: This is used for new volume parameters.
|
|
// It uses a more intuitive-feeling curve.
|
|
float Volume100ToMultiplier(int volume) {
|
|
// Switch to linear above the 1.0f point.
|
|
if (volume > 100) {
|
|
return volume / 100.0f;
|
|
}
|
|
return powf(volume * 0.01f, 1.75f);
|
|
}
|
|
|
|
// Used for migration from the old settings.
|
|
int MultiplierToVolume100(float multiplier) {
|
|
// Switch to linear above the 1.0f point.
|
|
if (multiplier > 1.0f) {
|
|
return multiplier * 100;
|
|
}
|
|
return (int)(powf(multiplier, 1.0f / 1.75f) * 100.f + 0.5f);
|
|
}
|
|
|
|
float UIScaleFactorToMultiplier(int factor) {
|
|
return powf(2.0f, (float)factor / 8.0f);
|
|
}
|