CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/Config.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>18#include <cstdlib>19#include <ctime>20#include <mutex>21#include <set>22#include <sstream>23#include <thread>2425#include "ppsspp_config.h"2627#include "Common/GPU/OpenGL/GLFeatures.h"28#include "Common/Net/HTTPClient.h"29#include "Common/Net/URL.h"3031#include "Common/Log.h"32#include "Common/TimeUtil.h"33#include "Common/Thread/ThreadUtil.h"34#include "Common/Data/Format/IniFile.h"35#include "Common/Data/Format/JSONReader.h"36#include "Common/Data/Text/I18n.h"37#include "Common/Data/Text/Parsers.h"38#include "Common/CPUDetect.h"39#include "Common/File/FileUtil.h"40#include "Common/File/VFS/VFS.h"41#include "Common/Log/LogManager.h"42#include "Common/OSVersion.h"43#include "Common/System/Display.h"44#include "Common/System/System.h"45#include "Common/StringUtils.h"46#include "Common/Thread/ThreadUtil.h"47#include "Common/GPU/Vulkan/VulkanLoader.h"48#include "Common/VR/PPSSPPVR.h"49#include "Common/System/OSD.h"50#include "Core/Config.h"51#include "Core/ConfigSettings.h"52#include "Core/ConfigValues.h"53#include "Core/Loaders.h"54#include "Core/KeyMap.h"55#include "Core/HLE/sceUtility.h"56#include "Core/Instance.h"57#include "GPU/Common/FramebufferManagerCommon.h"5859// TODO: Find a better place for this.60http::RequestManager g_DownloadManager;6162Config g_Config;6364static bool jitForcedOff;6566// Not in Config.h because it's #included a lot.67struct ConfigPrivate {68std::mutex recentIsosLock;69std::mutex recentIsosThreadLock;70std::thread recentIsosThread;71bool recentIsosThreadPending = false;7273void ResetRecentIsosThread();74void SetRecentIsosThread(std::function<void()> f);75};7677#ifdef _DEBUG78static const char * const logSectionName = "LogDebug";79#else80static const char * const logSectionName = "Log";81#endif8283static bool TryUpdateSavedPath(Path *path);8485std::string GPUBackendToString(GPUBackend backend) {86switch (backend) {87case GPUBackend::OPENGL:88return "OPENGL";89case GPUBackend::DIRECT3D9:90return "DIRECT3D9";91case GPUBackend::DIRECT3D11:92return "DIRECT3D11";93case GPUBackend::VULKAN:94return "VULKAN";95}96// Intentionally not a default so we get a warning.97return "INVALID";98}99100GPUBackend GPUBackendFromString(std::string_view backend) {101if (!equalsNoCase(backend, "OPENGL") || backend == "0")102return GPUBackend::OPENGL;103if (!equalsNoCase(backend, "DIRECT3D9") || backend == "1")104return GPUBackend::DIRECT3D9;105if (!equalsNoCase(backend, "DIRECT3D11") || backend == "2")106return GPUBackend::DIRECT3D11;107if (!equalsNoCase(backend, "VULKAN") || backend == "3")108return GPUBackend::VULKAN;109return GPUBackend::OPENGL;110}111112const char *DefaultLangRegion() {113// Unfortunate default. There's no need to use bFirstRun, since this is only a default.114static std::string defaultLangRegion = "en_US";115std::string langRegion = System_GetProperty(SYSPROP_LANGREGION);116if (g_i18nrepo.IniExists(langRegion)) {117defaultLangRegion = langRegion;118} else if (langRegion.length() >= 3) {119// Don't give up. Let's try a fuzzy match - so nl_BE can match nl_NL.120IniFile mapping;121mapping.LoadFromVFS(g_VFS, "langregion.ini");122std::vector<std::string> keys;123mapping.GetKeys("LangRegionNames", keys);124125for (std::string key : keys) {126if (startsWithNoCase(key, langRegion)) {127// Exact submatch, or different case. Let's use it.128defaultLangRegion = key;129break;130} else if (startsWithNoCase(key, langRegion.substr(0, 3))) {131// Best so far.132defaultLangRegion = key;133}134}135}136137return defaultLangRegion.c_str();138}139140std::string CreateRandMAC() {141std::stringstream randStream;142srand(time(nullptr));143for (int i = 0; i < 6; i++) {144u32 value = rand() % 256;145if (i == 0) {146// Making sure the 1st 2-bits on the 1st byte of OUI are zero to prevent issue with some games (ie. Gran Turismo)147value &= 0xfc;148}149if (value <= 15)150randStream << '0' << std::hex << value;151else152randStream << std::hex << value;153if (i < 5) {154randStream << ':'; //we need a : between every octet155}156}157return randStream.str();158}159160static int DefaultCpuCore() {161#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(RISCV64)162if (System_GetPropertyBool(SYSPROP_CAN_JIT))163return (int)CPUCore::JIT;164return (int)CPUCore::IR_INTERPRETER;165#else166return (int)CPUCore::IR_INTERPRETER;167#endif168}169170static bool DefaultCodeGen() {171#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(RISCV64)172return true;173#else174return false;175#endif176}177178static bool DefaultVSync() {179#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(UWP)180// Previously we didn't allow turning off vsync/FIFO on Android. Let's set the default accordingly.181return true;182#else183return false;184#endif185}186187static bool DefaultEnableStateUndo() {188#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)189// Off on mobile to save disk space.190return false;191#endif192return true;193}194195static float DefaultUISaturation() {196return IsVREnabled() ? 1.5f : 1.0f;197}198199static const ConfigSetting generalSettings[] = {200ConfigSetting("FirstRun", &g_Config.bFirstRun, true, CfgFlag::DEFAULT),201ConfigSetting("RunCount", &g_Config.iRunCount, 0, CfgFlag::DEFAULT),202ConfigSetting("Enable Logging", &g_Config.bEnableLogging, true, CfgFlag::DEFAULT),203ConfigSetting("AutoRun", &g_Config.bAutoRun, true, CfgFlag::DEFAULT),204ConfigSetting("Browse", &g_Config.bBrowse, false, CfgFlag::DEFAULT),205ConfigSetting("IgnoreBadMemAccess", &g_Config.bIgnoreBadMemAccess, true, CfgFlag::DEFAULT),206ConfigSetting("CurrentDirectory", &g_Config.currentDirectory, "", CfgFlag::DEFAULT),207ConfigSetting("ShowDebuggerOnLoad", &g_Config.bShowDebuggerOnLoad, false, CfgFlag::DEFAULT),208ConfigSetting("CheckForNewVersion", &g_Config.bCheckForNewVersion, true, CfgFlag::DEFAULT),209ConfigSetting("Language", &g_Config.sLanguageIni, &DefaultLangRegion, CfgFlag::DEFAULT),210ConfigSetting("ForceLagSync2", &g_Config.bForceLagSync, false, CfgFlag::PER_GAME),211ConfigSetting("DiscordPresence", &g_Config.bDiscordPresence, true, CfgFlag::DEFAULT), // Or maybe it makes sense to have it per-game? Race conditions abound...212ConfigSetting("UISound", &g_Config.bUISound, false, CfgFlag::DEFAULT),213214ConfigSetting("DisableHTTPS", &g_Config.bDisableHTTPS, false, CfgFlag::DONT_SAVE),215ConfigSetting("AutoLoadSaveState", &g_Config.iAutoLoadSaveState, 0, CfgFlag::PER_GAME),216ConfigSetting("EnableCheats", &g_Config.bEnableCheats, false, CfgFlag::PER_GAME | CfgFlag::REPORT),217ConfigSetting("EnablePlugins", &g_Config.bEnablePlugins, true, CfgFlag::PER_GAME | CfgFlag::REPORT),218ConfigSetting("CwCheatRefreshRate", &g_Config.iCwCheatRefreshIntervalMs, 77, CfgFlag::PER_GAME),219ConfigSetting("CwCheatScrollPosition", &g_Config.fCwCheatScrollPosition, 0.0f, CfgFlag::PER_GAME),220ConfigSetting("GameListScrollPosition", &g_Config.fGameListScrollPosition, 0.0f, CfgFlag::DEFAULT),221ConfigSetting("DebugOverlay", &g_Config.iDebugOverlay, 0, CfgFlag::DONT_SAVE),222223ConfigSetting("ScreenshotsAsPNG", &g_Config.bScreenshotsAsPNG, false, CfgFlag::PER_GAME),224ConfigSetting("UseFFV1", &g_Config.bUseFFV1, false, CfgFlag::DEFAULT),225ConfigSetting("DumpFrames", &g_Config.bDumpFrames, false, CfgFlag::DEFAULT),226ConfigSetting("DumpVideoOutput", &g_Config.bDumpVideoOutput, false, CfgFlag::DEFAULT),227ConfigSetting("DumpAudio", &g_Config.bDumpAudio, false, CfgFlag::DEFAULT),228ConfigSetting("SaveLoadResetsAVdumping", &g_Config.bSaveLoadResetsAVdumping, false, CfgFlag::DEFAULT),229ConfigSetting("StateSlot", &g_Config.iCurrentStateSlot, 0, CfgFlag::PER_GAME),230ConfigSetting("EnableStateUndo", &g_Config.bEnableStateUndo, &DefaultEnableStateUndo, CfgFlag::PER_GAME),231ConfigSetting("StateLoadUndoGame", &g_Config.sStateLoadUndoGame, "NA", CfgFlag::DEFAULT),232ConfigSetting("StateUndoLastSaveGame", &g_Config.sStateUndoLastSaveGame, "NA", CfgFlag::DEFAULT),233ConfigSetting("StateUndoLastSaveSlot", &g_Config.iStateUndoLastSaveSlot, -5, CfgFlag::DEFAULT), // Start with an "invalid" value234ConfigSetting("RewindSnapshotInterval", &g_Config.iRewindSnapshotInterval, 0, CfgFlag::PER_GAME),235236ConfigSetting("ShowOnScreenMessage", &g_Config.bShowOnScreenMessages, true, CfgFlag::DEFAULT),237ConfigSetting("ShowRegionOnGameIcon", &g_Config.bShowRegionOnGameIcon, false, CfgFlag::DEFAULT),238ConfigSetting("ShowIDOnGameIcon", &g_Config.bShowIDOnGameIcon, false, CfgFlag::DEFAULT),239ConfigSetting("GameGridScale", &g_Config.fGameGridScale, 1.0, CfgFlag::DEFAULT),240ConfigSetting("GridView1", &g_Config.bGridView1, true, CfgFlag::DEFAULT),241ConfigSetting("GridView2", &g_Config.bGridView2, true, CfgFlag::DEFAULT),242ConfigSetting("GridView3", &g_Config.bGridView3, false, CfgFlag::DEFAULT),243ConfigSetting("RightAnalogUp", &g_Config.iRightAnalogUp, 0, CfgFlag::PER_GAME),244ConfigSetting("RightAnalogDown", &g_Config.iRightAnalogDown, 0, CfgFlag::PER_GAME),245ConfigSetting("RightAnalogLeft", &g_Config.iRightAnalogLeft, 0, CfgFlag::PER_GAME),246ConfigSetting("RightAnalogRight", &g_Config.iRightAnalogRight, 0, CfgFlag::PER_GAME),247ConfigSetting("RightAnalogPress", &g_Config.iRightAnalogPress, 0, CfgFlag::PER_GAME),248ConfigSetting("RightAnalogCustom", &g_Config.bRightAnalogCustom, false, CfgFlag::PER_GAME),249ConfigSetting("RightAnalogDisableDiagonal", &g_Config.bRightAnalogDisableDiagonal, false, CfgFlag::PER_GAME),250ConfigSetting("SwipeUp", &g_Config.iSwipeUp, 0, CfgFlag::PER_GAME),251ConfigSetting("SwipeDown", &g_Config.iSwipeDown, 0, CfgFlag::PER_GAME),252ConfigSetting("SwipeLeft", &g_Config.iSwipeLeft, 0, CfgFlag::PER_GAME),253ConfigSetting("SwipeRight", &g_Config.iSwipeRight, 0, CfgFlag::PER_GAME),254ConfigSetting("SwipeSensitivity", &g_Config.fSwipeSensitivity, 1.0f, CfgFlag::PER_GAME),255ConfigSetting("SwipeSmoothing", &g_Config.fSwipeSmoothing, 0.3f, CfgFlag::PER_GAME),256ConfigSetting("DoubleTapGesture", &g_Config.iDoubleTapGesture, 0, CfgFlag::PER_GAME),257ConfigSetting("GestureControlEnabled", &g_Config.bGestureControlEnabled, false, CfgFlag::PER_GAME),258259// "default" means let emulator decide, "" means disable.260ConfigSetting("ReportingHost", &g_Config.sReportHost, "default", CfgFlag::DEFAULT),261ConfigSetting("AutoSaveSymbolMap", &g_Config.bAutoSaveSymbolMap, false, CfgFlag::PER_GAME),262ConfigSetting("CacheFullIsoInRam", &g_Config.bCacheFullIsoInRam, false, CfgFlag::PER_GAME),263ConfigSetting("RemoteISOPort", &g_Config.iRemoteISOPort, 0, CfgFlag::DEFAULT),264ConfigSetting("LastRemoteISOServer", &g_Config.sLastRemoteISOServer, "", CfgFlag::DEFAULT),265ConfigSetting("LastRemoteISOPort", &g_Config.iLastRemoteISOPort, 0, CfgFlag::DEFAULT),266ConfigSetting("RemoteISOManualConfig", &g_Config.bRemoteISOManual, false, CfgFlag::DEFAULT),267ConfigSetting("RemoteShareOnStartup", &g_Config.bRemoteShareOnStartup, false, CfgFlag::DEFAULT),268ConfigSetting("RemoteISOSubdir", &g_Config.sRemoteISOSubdir, "/", CfgFlag::DEFAULT),269ConfigSetting("RemoteDebuggerOnStartup", &g_Config.bRemoteDebuggerOnStartup, false, CfgFlag::DEFAULT),270ConfigSetting("RemoteTab", &g_Config.bRemoteTab, false, CfgFlag::DEFAULT),271ConfigSetting("RemoteISOSharedDir", &g_Config.sRemoteISOSharedDir, "", CfgFlag::DEFAULT),272ConfigSetting("RemoteISOShareType", &g_Config.iRemoteISOShareType, (int)RemoteISOShareType::RECENT, CfgFlag::DEFAULT),273274#ifdef __ANDROID__275ConfigSetting("ScreenRotation", &g_Config.iScreenRotation, ROTATION_AUTO_HORIZONTAL),276#endif277ConfigSetting("InternalScreenRotation", &g_Config.iInternalScreenRotation, ROTATION_LOCKED_HORIZONTAL, CfgFlag::PER_GAME),278279ConfigSetting("BackgroundAnimation", &g_Config.iBackgroundAnimation, 1, CfgFlag::DEFAULT),280ConfigSetting("TransparentBackground", &g_Config.bTransparentBackground, true, CfgFlag::DEFAULT),281ConfigSetting("UITint", &g_Config.fUITint, 0.0, CfgFlag::DEFAULT),282ConfigSetting("UISaturation", &g_Config.fUISaturation, &DefaultUISaturation, CfgFlag::DEFAULT),283284#if defined(USING_WIN_UI)285ConfigSetting("TopMost", &g_Config.bTopMost, false, CfgFlag::DEFAULT),286ConfigSetting("PauseOnLostFocus", &g_Config.bPauseOnLostFocus, false, CfgFlag::PER_GAME),287#endif288289#if !defined(MOBILE_DEVICE)290ConfigSetting("WindowX", &g_Config.iWindowX, -1, CfgFlag::DEFAULT), // -1 tells us to center the window.291ConfigSetting("WindowY", &g_Config.iWindowY, -1, CfgFlag::DEFAULT),292ConfigSetting("WindowWidth", &g_Config.iWindowWidth, 0, CfgFlag::DEFAULT), // 0 will be automatically reset later (need to do the AdjustWindowRect dance).293ConfigSetting("WindowHeight", &g_Config.iWindowHeight, 0, CfgFlag::DEFAULT),294#endif295296ConfigSetting("PauseWhenMinimized", &g_Config.bPauseWhenMinimized, false, CfgFlag::PER_GAME),297ConfigSetting("PauseExitsEmulator", &g_Config.bPauseExitsEmulator, false, CfgFlag::DONT_SAVE),298ConfigSetting("PauseMenuExitsEmulator", &g_Config.bPauseMenuExitsEmulator, false, CfgFlag::DONT_SAVE),299300ConfigSetting("DumpDecryptedEboots", &g_Config.bDumpDecryptedEboot, false, CfgFlag::PER_GAME),301ConfigSetting("FullscreenOnDoubleclick", &g_Config.bFullscreenOnDoubleclick, true, CfgFlag::DONT_SAVE),302ConfigSetting("ShowMenuBar", &g_Config.bShowMenuBar, true, CfgFlag::DEFAULT),303304ConfigSetting("MemStickInserted", &g_Config.bMemStickInserted, true, CfgFlag::PER_GAME | CfgFlag::REPORT),305ConfigSetting("LoadPlugins", &g_Config.bLoadPlugins, true, CfgFlag::PER_GAME),306307ConfigSetting("IgnoreCompatSettings", &g_Config.sIgnoreCompatSettings, "", CfgFlag::PER_GAME | CfgFlag::REPORT),308309ConfigSetting("RunBehindPauseMenu", &g_Config.bRunBehindPauseMenu, false, CfgFlag::DEFAULT),310311ConfigSetting("ShowGPOLEDs", &g_Config.bShowGPOLEDs, false, CfgFlag::PER_GAME),312};313314static bool DefaultSasThread() {315return cpu_info.num_cores > 1;316}317318static const ConfigSetting achievementSettings[] = {319// Core settings320ConfigSetting("AchievementsEnable", &g_Config.bAchievementsEnable, true, CfgFlag::DEFAULT),321ConfigSetting("AchievementsEnableRAIntegration", &g_Config.bAchievementsEnableRAIntegration, false, CfgFlag::DEFAULT),322ConfigSetting("AchievementsChallengeMode", &g_Config.bAchievementsHardcoreMode, true, CfgFlag::PER_GAME | CfgFlag::DEFAULT),323ConfigSetting("AchievementsEncoreMode", &g_Config.bAchievementsEncoreMode, false, CfgFlag::PER_GAME | CfgFlag::DEFAULT),324ConfigSetting("AchievementsUnofficial", &g_Config.bAchievementsUnofficial, false, CfgFlag::PER_GAME | CfgFlag::DEFAULT),325ConfigSetting("AchievementsLogBadMemReads", &g_Config.bAchievementsLogBadMemReads, false, CfgFlag::DEFAULT),326ConfigSetting("AchievementsSaveStateInHardcoreMode", &g_Config.bAchievementsSaveStateInHardcoreMode, false, CfgFlag::DEFAULT),327328// Achievements login info. Note that password is NOT stored, only a login token.329// And that login token is stored separately from the ini, see NativeSaveSecret, but it can also be loaded330// from the ini if manually entered (useful when testing various builds on Android).331ConfigSetting("AchievementsToken", &g_Config.sAchievementsToken, "", CfgFlag::DONT_SAVE),332ConfigSetting("AchievementsUserName", &g_Config.sAchievementsUserName, "", CfgFlag::DEFAULT),333334// Customizations335ConfigSetting("AchievementsSoundEffects", &g_Config.bAchievementsSoundEffects, true, CfgFlag::DEFAULT),336ConfigSetting("AchievementsUnlockAudioFile", &g_Config.sAchievementsUnlockAudioFile, "", CfgFlag::DEFAULT),337ConfigSetting("AchievementsLeaderboardSubmitAudioFile", &g_Config.sAchievementsLeaderboardSubmitAudioFile, "", CfgFlag::DEFAULT),338339ConfigSetting("AchievementsLeaderboardTrackerPos", &g_Config.iAchievementsLeaderboardTrackerPos, (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::PER_GAME | CfgFlag::DEFAULT),340ConfigSetting("AchievementsLeaderboardStartedOrFailedPos", &g_Config.iAchievementsLeaderboardStartedOrFailedPos, (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::PER_GAME | CfgFlag::DEFAULT),341ConfigSetting("AchievementsLeaderboardSubmittedPos", &g_Config.iAchievementsLeaderboardSubmittedPos, (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::PER_GAME | CfgFlag::DEFAULT),342ConfigSetting("AchievementsProgressPos", &g_Config.iAchievementsProgressPos, (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::PER_GAME | CfgFlag::DEFAULT),343ConfigSetting("AchievementsChallengePos", &g_Config.iAchievementsChallengePos, (int)ScreenEdgePosition::TOP_LEFT, CfgFlag::PER_GAME | CfgFlag::DEFAULT),344ConfigSetting("AchievementsUnlockedPos", &g_Config.iAchievementsUnlockedPos, (int)ScreenEdgePosition::TOP_CENTER, CfgFlag::PER_GAME | CfgFlag::DEFAULT),345};346347static const ConfigSetting cpuSettings[] = {348ConfigSetting("CPUCore", &g_Config.iCpuCore, &DefaultCpuCore, CfgFlag::PER_GAME | CfgFlag::REPORT),349ConfigSetting("SeparateSASThread", &g_Config.bSeparateSASThread, &DefaultSasThread, CfgFlag::PER_GAME | CfgFlag::REPORT),350ConfigSetting("IOTimingMethod", &g_Config.iIOTimingMethod, IOTIMING_FAST, CfgFlag::PER_GAME | CfgFlag::REPORT),351ConfigSetting("FastMemoryAccess", &g_Config.bFastMemory, true, CfgFlag::PER_GAME),352ConfigSetting("FunctionReplacements", &g_Config.bFuncReplacements, true, CfgFlag::PER_GAME | CfgFlag::REPORT),353ConfigSetting("HideSlowWarnings", &g_Config.bHideSlowWarnings, false, CfgFlag::DEFAULT),354ConfigSetting("HideStateWarnings", &g_Config.bHideStateWarnings, false, CfgFlag::DEFAULT),355ConfigSetting("PreloadFunctions", &g_Config.bPreloadFunctions, false, CfgFlag::PER_GAME),356ConfigSetting("JitDisableFlags", &g_Config.uJitDisableFlags, (uint32_t)0, CfgFlag::PER_GAME),357ConfigSetting("CPUSpeed", &g_Config.iLockedCPUSpeed, 0, CfgFlag::PER_GAME | CfgFlag::REPORT),358};359360static int DefaultInternalResolution() {361// Auto on Windows and Linux, 2x on large screens and iOS, 1x elsewhere.362#if defined(USING_WIN_UI) || defined(USING_QT_UI)363return 0;364#elif PPSSPP_PLATFORM(IOS)365return 2;366#else367if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_VR) {368return 4;369}370int longestDisplaySide = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES));371int scale = longestDisplaySide >= 1000 ? 2 : 1;372INFO_LOG(Log::G3D, "Longest display side: %d pixels. Choosing scale %d", longestDisplaySide, scale);373return scale;374#endif375}376377static int DefaultFastForwardMode() {378#if PPSSPP_PLATFORM(ANDROID) || defined(USING_QT_UI) || PPSSPP_PLATFORM(UWP) || PPSSPP_PLATFORM(IOS)379return (int)FastForwardMode::SKIP_FLIP;380#else381return (int)FastForwardMode::CONTINUOUS;382#endif383}384385static int DefaultAndroidHwScale() {386#if PPSSPP_PLATFORM(ANDROID)387if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19 || System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_TV) {388// Arbitrary cutoff at Kitkat - modern devices are usually powerful enough that hw scaling389// doesn't really help very much and mostly causes problems. See #11151390return 0;391}392393// Get the real resolution as passed in during startup, not dp_xres and stuff394int xres = System_GetPropertyInt(SYSPROP_DISPLAY_XRES);395int yres = System_GetPropertyInt(SYSPROP_DISPLAY_YRES);396397if (xres <= 960) {398// Smaller than the PSP*2, let's go native.399return 0;400} else if (xres <= 480 * 3) { // 720p xres401// Small-ish screen, we should default to 2x402return 2 + 1;403} else {404// Large or very large screen. Default to 3x psp resolution.405return 3 + 1;406}407return 0;408#else409return 1;410#endif411}412413// See issue 14439. Should possibly even block these devices from selecting VK.414const char * const vulkanDefaultBlacklist[] = {415"Sony:BRAVIA VH1",416};417418static int DefaultGPUBackend() {419if (IsVREnabled()) {420return (int)GPUBackend::OPENGL;421}422423#if PPSSPP_PLATFORM(WINDOWS)424// If no Vulkan, use Direct3D 11 on Windows 8+ (most importantly 10.)425if (IsWin8OrHigher()) {426return (int)GPUBackend::DIRECT3D11;427}428#elif PPSSPP_PLATFORM(ANDROID)429// Check blacklist.430for (size_t i = 0; i < ARRAY_SIZE(vulkanDefaultBlacklist); i++) {431if (System_GetProperty(SYSPROP_NAME) == vulkanDefaultBlacklist[i]) {432return (int)GPUBackend::OPENGL;433}434}435436// Default to Vulkan only on Oreo 8.1 (level 27) devices or newer, and only437// on ARM64 and x86-64. Drivers before, and on other archs, are generally too438// unreliable to default to (with some exceptions, of course).439#if PPSSPP_ARCH(64BIT)440if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 27) {441return (int)GPUBackend::VULKAN;442}443#else444// There are some newer devices that benefit from Vulkan as default, but are 32-bit. Example: Redmi 9A.445// Let's only allow the very newest generation though.446if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30) {447return (int)GPUBackend::VULKAN;448}449#endif450451#elif PPSSPP_PLATFORM(MAC)452453#if PPSSPP_ARCH(ARM64)454return (int)GPUBackend::VULKAN;455#else456// On Intel (generally older Macs) default to OpenGL.457return (int)GPUBackend::OPENGL;458#endif459460#elif PPSSPP_PLATFORM(IOS_APP_STORE)461return (int)GPUBackend::VULKAN;462#endif463464// TODO: On some additional Linux platforms, we should also default to Vulkan.465return (int)GPUBackend::OPENGL;466}467468int Config::NextValidBackend() {469std::vector<std::string> split;470std::set<GPUBackend> failed;471472SplitString(sFailedGPUBackends, ',', split);473for (const auto &str : split) {474if (!str.empty() && str != "ALL") {475failed.insert(GPUBackendFromString(str));476}477}478479// Count these as "failed" too so we don't pick them.480SplitString(sDisabledGPUBackends, ',', split);481for (const auto &str : split) {482if (!str.empty()) {483failed.insert(GPUBackendFromString(str));484}485}486487if (failed.count((GPUBackend)iGPUBackend)) {488ERROR_LOG(Log::Loader, "Graphics backend failed for %d, trying another", iGPUBackend);489490#if !PPSSPP_PLATFORM(UWP)491if (!failed.count(GPUBackend::VULKAN) && VulkanMayBeAvailable()) {492return (int)GPUBackend::VULKAN;493}494#endif495#if PPSSPP_PLATFORM(WINDOWS)496if (!failed.count(GPUBackend::DIRECT3D11) && IsWin7OrHigher()) {497return (int)GPUBackend::DIRECT3D11;498}499#endif500#if PPSSPP_API(ANY_GL)501if (!failed.count(GPUBackend::OPENGL)) {502return (int)GPUBackend::OPENGL;503}504#endif505#if PPSSPP_API(D3D9)506if (!failed.count(GPUBackend::DIRECT3D9)) {507return (int)GPUBackend::DIRECT3D9;508}509#endif510511// They've all failed. Let them try the default - or on Android, OpenGL.512sFailedGPUBackends += ",ALL";513ERROR_LOG(Log::Loader, "All graphics backends failed");514#if PPSSPP_PLATFORM(ANDROID)515return (int)GPUBackend::OPENGL;516#else517return DefaultGPUBackend();518#endif519}520521return iGPUBackend;522}523524bool Config::IsBackendEnabled(GPUBackend backend) {525std::vector<std::string> split;526527SplitString(sDisabledGPUBackends, ',', split);528for (const auto &str : split) {529if (str.empty())530continue;531auto match = GPUBackendFromString(str);532if (match == backend)533return false;534}535536#if PPSSPP_PLATFORM(UWP)537if (backend != GPUBackend::DIRECT3D11)538return false;539#elif PPSSPP_PLATFORM(SWITCH)540if (backend != GPUBackend::OPENGL)541return false;542#elif PPSSPP_PLATFORM(WINDOWS)543if (backend == GPUBackend::DIRECT3D11 && !IsVistaOrHigher())544return false;545#else546if (backend == GPUBackend::DIRECT3D11 || backend == GPUBackend::DIRECT3D9)547return false;548#endif549550#if !PPSSPP_API(ANY_GL)551if (backend == GPUBackend::OPENGL)552return false;553#endif554if (backend == GPUBackend::VULKAN && !VulkanMayBeAvailable())555return false;556return true;557}558559template <typename T, std::string (*FTo)(T), T (*FFrom)(std::string_view)>560struct ConfigTranslator {561static std::string To(int v) {562return StringFromInt(v) + " (" + FTo(T(v)) + ")";563}564565static int From(const std::string &v) {566int result;567if (TryParse(v, &result)) {568return result;569}570return (int)FFrom(v);571}572};573574typedef ConfigTranslator<GPUBackend, GPUBackendToString, GPUBackendFromString> GPUBackendTranslator;575576static int FastForwardModeFromString(const std::string &s) {577if (!strcasecmp(s.c_str(), "CONTINUOUS"))578return (int)FastForwardMode::CONTINUOUS;579if (!strcasecmp(s.c_str(), "SKIP_FLIP"))580return (int)FastForwardMode::SKIP_FLIP;581return DefaultFastForwardMode();582}583584static std::string FastForwardModeToString(int v) {585switch (FastForwardMode(v)) {586case FastForwardMode::CONTINUOUS:587return "CONTINUOUS";588case FastForwardMode::SKIP_FLIP:589return "SKIP_FLIP";590}591return "CONTINUOUS";592}593594static const ConfigSetting graphicsSettings[] = {595ConfigSetting("EnableCardboardVR", &g_Config.bEnableCardboardVR, false, CfgFlag::PER_GAME),596ConfigSetting("CardboardScreenSize", &g_Config.iCardboardScreenSize, 50, CfgFlag::PER_GAME),597ConfigSetting("CardboardXShift", &g_Config.iCardboardXShift, 0, CfgFlag::PER_GAME),598ConfigSetting("CardboardYShift", &g_Config.iCardboardYShift, 0, CfgFlag::PER_GAME),599ConfigSetting("iShowStatusFlags", &g_Config.iShowStatusFlags, 0, CfgFlag::PER_GAME),600ConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend, &GPUBackendTranslator::To, &GPUBackendTranslator::From, CfgFlag::DEFAULT | CfgFlag::REPORT),601#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64)602ConfigSetting("CustomDriver", &g_Config.sCustomDriver, "", CfgFlag::DEFAULT),603#endif604ConfigSetting("FailedGraphicsBackends", &g_Config.sFailedGPUBackends, "", CfgFlag::DEFAULT),605ConfigSetting("DisabledGraphicsBackends", &g_Config.sDisabledGPUBackends, "", CfgFlag::DEFAULT),606ConfigSetting("VulkanDevice", &g_Config.sVulkanDevice, "", CfgFlag::DEFAULT),607#ifdef _WIN32608ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", CfgFlag::DEFAULT),609#endif610ConfigSetting("CameraDevice", &g_Config.sCameraDevice, "", CfgFlag::DEFAULT),611ConfigSetting("CameraMirrorHorizontal", &g_Config.bCameraMirrorHorizontal, false, CfgFlag::DEFAULT),612ConfigSetting("AndroidFramerateMode", &g_Config.iDisplayFramerateMode, 1, CfgFlag::DEFAULT),613ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, CfgFlag::DONT_SAVE),614ConfigSetting("UseGeometryShader", &g_Config.bUseGeometryShader, false, CfgFlag::PER_GAME),615ConfigSetting("SkipBufferEffects", &g_Config.bSkipBufferEffects, false, CfgFlag::PER_GAME | CfgFlag::REPORT),616ConfigSetting("DisableRangeCulling", &g_Config.bDisableRangeCulling, false, CfgFlag::PER_GAME | CfgFlag::REPORT),617ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, CfgFlag::PER_GAME),618ConfigSetting("SoftwareRendererJit", &g_Config.bSoftwareRenderingJit, true, CfgFlag::PER_GAME),619ConfigSetting("HardwareTransform", &g_Config.bHardwareTransform, true, CfgFlag::PER_GAME | CfgFlag::REPORT),620ConfigSetting("SoftwareSkinning", &g_Config.bSoftwareSkinning, true, CfgFlag::PER_GAME | CfgFlag::REPORT),621ConfigSetting("TextureFiltering", &g_Config.iTexFiltering, 1, CfgFlag::PER_GAME | CfgFlag::REPORT),622ConfigSetting("Smart2DTexFiltering", &g_Config.bSmart2DTexFiltering, false, CfgFlag::PER_GAME | CfgFlag::REPORT),623ConfigSetting("InternalResolution", &g_Config.iInternalResolution, &DefaultInternalResolution, CfgFlag::PER_GAME | CfgFlag::REPORT),624ConfigSetting("AndroidHwScale", &g_Config.iAndroidHwScale, &DefaultAndroidHwScale, CfgFlag::DEFAULT),625ConfigSetting("HighQualityDepth", &g_Config.bHighQualityDepth, true, CfgFlag::PER_GAME | CfgFlag::REPORT),626ConfigSetting("FrameSkip", &g_Config.iFrameSkip, 0, CfgFlag::PER_GAME | CfgFlag::REPORT),627ConfigSetting("FrameSkipType", &g_Config.iFrameSkipType, 0, CfgFlag::PER_GAME | CfgFlag::REPORT),628ConfigSetting("AutoFrameSkip", &g_Config.bAutoFrameSkip, IsVREnabled(), CfgFlag::PER_GAME | CfgFlag::REPORT),629ConfigSetting("StereoRendering", &g_Config.bStereoRendering, false, CfgFlag::PER_GAME),630ConfigSetting("StereoToMonoShader", &g_Config.sStereoToMonoShader, "RedBlue", CfgFlag::PER_GAME),631ConfigSetting("FrameRate", &g_Config.iFpsLimit1, 0, CfgFlag::PER_GAME),632ConfigSetting("FrameRate2", &g_Config.iFpsLimit2, -1, CfgFlag::PER_GAME),633ConfigSetting("AnalogFrameRate", &g_Config.iAnalogFpsLimit, 240, CfgFlag::PER_GAME),634ConfigSetting("UnthrottlingMode", &g_Config.iFastForwardMode, &DefaultFastForwardMode, &FastForwardModeToString, &FastForwardModeFromString, CfgFlag::PER_GAME),635#if defined(USING_WIN_UI)636ConfigSetting("RestartRequired", &g_Config.bRestartRequired, false, CfgFlag::DONT_SAVE),637#endif638639// Most low-performance (and many high performance) mobile GPUs do not support aniso anyway so defaulting to 4 is fine.640ConfigSetting("AnisotropyLevel", &g_Config.iAnisotropyLevel, 4, CfgFlag::PER_GAME),641ConfigSetting("MultiSampleLevel", &g_Config.iMultiSampleLevel, 0, CfgFlag::PER_GAME), // Number of samples is 1 << iMultiSampleLevel642643ConfigSetting("TextureBackoffCache", &g_Config.bTextureBackoffCache, false, CfgFlag::PER_GAME | CfgFlag::REPORT),644ConfigSetting("VertexDecJit", &g_Config.bVertexDecoderJit, &DefaultCodeGen, CfgFlag::DONT_SAVE | CfgFlag::REPORT),645646#ifndef MOBILE_DEVICE647ConfigSetting("FullScreen", &g_Config.bFullScreen, false, CfgFlag::DEFAULT),648ConfigSetting("FullScreenMulti", &g_Config.bFullScreenMulti, false, CfgFlag::DEFAULT),649#endif650651#if PPSSPP_PLATFORM(IOS)652ConfigSetting("AppSwitchMode", &g_Config.iAppSwitchMode, (int)AppSwitchMode::DOUBLE_SWIPE_INDICATOR, CfgFlag::DEFAULT),653#endif654655ConfigSetting("BufferFiltering", &g_Config.iDisplayFilter, SCALE_LINEAR, CfgFlag::PER_GAME),656ConfigSetting("DisplayOffsetX", &g_Config.fDisplayOffsetX, 0.5f, CfgFlag::PER_GAME),657ConfigSetting("DisplayOffsetY", &g_Config.fDisplayOffsetY, 0.5f, CfgFlag::PER_GAME),658ConfigSetting("DisplayScale", &g_Config.fDisplayScale, 1.0f, CfgFlag::PER_GAME),659ConfigSetting("DisplayIntegerScale", &g_Config.bDisplayIntegerScale, false, CfgFlag::PER_GAME),660ConfigSetting("DisplayAspectRatio", &g_Config.fDisplayAspectRatio, 1.0f, CfgFlag::PER_GAME),661ConfigSetting("DisplayStretch", &g_Config.bDisplayStretch, false, CfgFlag::PER_GAME),662ConfigSetting("DisplayCropTo16x9", &g_Config.bDisplayCropTo16x9, true, CfgFlag::PER_GAME),663664ConfigSetting("ImmersiveMode", &g_Config.bImmersiveMode, true, CfgFlag::PER_GAME),665ConfigSetting("SustainedPerformanceMode", &g_Config.bSustainedPerformanceMode, false, CfgFlag::PER_GAME),666ConfigSetting("IgnoreScreenInsets", &g_Config.bIgnoreScreenInsets, true, CfgFlag::DEFAULT),667668ConfigSetting("ReplaceTextures", &g_Config.bReplaceTextures, true, CfgFlag::PER_GAME | CfgFlag::REPORT),669ConfigSetting("SaveNewTextures", &g_Config.bSaveNewTextures, false, CfgFlag::PER_GAME | CfgFlag::REPORT),670ConfigSetting("IgnoreTextureFilenames", &g_Config.bIgnoreTextureFilenames, false, CfgFlag::PER_GAME),671672ConfigSetting("TexScalingLevel", &g_Config.iTexScalingLevel, 1, CfgFlag::PER_GAME | CfgFlag::REPORT),673ConfigSetting("TexScalingType", &g_Config.iTexScalingType, 0, CfgFlag::PER_GAME | CfgFlag::REPORT),674ConfigSetting("TexDeposterize", &g_Config.bTexDeposterize, false, CfgFlag::PER_GAME | CfgFlag::REPORT),675ConfigSetting("TexHardwareScaling", &g_Config.bTexHardwareScaling, false, CfgFlag::PER_GAME | CfgFlag::REPORT),676ConfigSetting("VSync", &g_Config.bVSync, &DefaultVSync, CfgFlag::PER_GAME),677ConfigSetting("BloomHack", &g_Config.iBloomHack, 0, CfgFlag::PER_GAME | CfgFlag::REPORT),678679// Not really a graphics setting...680ConfigSetting("SplineBezierQuality", &g_Config.iSplineBezierQuality, 2, CfgFlag::PER_GAME | CfgFlag::REPORT),681ConfigSetting("HardwareTessellation", &g_Config.bHardwareTessellation, false, CfgFlag::PER_GAME | CfgFlag::REPORT),682ConfigSetting("TextureShader", &g_Config.sTextureShaderName, "Off", CfgFlag::PER_GAME),683ConfigSetting("ShaderChainRequires60FPS", &g_Config.bShaderChainRequires60FPS, false, CfgFlag::PER_GAME),684685ConfigSetting("SkipGPUReadbackMode", &g_Config.iSkipGPUReadbackMode, false, CfgFlag::PER_GAME | CfgFlag::REPORT),686687ConfigSetting("GfxDebugOutput", &g_Config.bGfxDebugOutput, false, CfgFlag::DONT_SAVE),688ConfigSetting("LogFrameDrops", &g_Config.bLogFrameDrops, false, CfgFlag::DEFAULT),689690ConfigSetting("InflightFrames", &g_Config.iInflightFrames, 3, CfgFlag::DEFAULT),691ConfigSetting("RenderDuplicateFrames", &g_Config.bRenderDuplicateFrames, false, CfgFlag::PER_GAME),692693ConfigSetting("MultiThreading", &g_Config.bRenderMultiThreading, true, CfgFlag::DEFAULT),694695ConfigSetting("ShaderCache", &g_Config.bShaderCache, true, CfgFlag::DONT_SAVE), // Doesn't save. Ini-only.696ConfigSetting("GpuLogProfiler", &g_Config.bGpuLogProfiler, false, CfgFlag::DEFAULT),697698ConfigSetting("UberShaderVertex", &g_Config.bUberShaderVertex, true, CfgFlag::DEFAULT),699ConfigSetting("UberShaderFragment", &g_Config.bUberShaderFragment, true, CfgFlag::DEFAULT),700701ConfigSetting("DisplayRefreshRate", &g_Config.iDisplayRefreshRate, g_Config.iDisplayRefreshRate, CfgFlag::PER_GAME),702};703704static const ConfigSetting soundSettings[] = {705ConfigSetting("Enable", &g_Config.bEnableSound, true, CfgFlag::PER_GAME),706ConfigSetting("AudioBackend", &g_Config.iAudioBackend, 0, CfgFlag::PER_GAME),707ConfigSetting("ExtraAudioBuffering", &g_Config.bExtraAudioBuffering, false, CfgFlag::DEFAULT),708ConfigSetting("GlobalVolume", &g_Config.iGlobalVolume, VOLUME_FULL, CfgFlag::PER_GAME),709ConfigSetting("ReverbVolume", &g_Config.iReverbVolume, VOLUME_FULL, CfgFlag::PER_GAME),710ConfigSetting("AltSpeedVolume", &g_Config.iAltSpeedVolume, -1, CfgFlag::PER_GAME),711ConfigSetting("AchievementSoundVolume", &g_Config.iAchievementSoundVolume, 6, CfgFlag::PER_GAME),712ConfigSetting("AudioDevice", &g_Config.sAudioDevice, "", CfgFlag::DEFAULT),713ConfigSetting("AutoAudioDevice", &g_Config.bAutoAudioDevice, true, CfgFlag::DEFAULT),714ConfigSetting("AudioMixWithOthers", &g_Config.bAudioMixWithOthers, true, CfgFlag::DEFAULT),715ConfigSetting("AudioRespectSilentMode", &g_Config.bAudioRespectSilentMode, false, CfgFlag::DEFAULT),716ConfigSetting("UseNewAtrac", &g_Config.bUseNewAtrac, false, CfgFlag::DEFAULT),717};718719static bool DefaultShowTouchControls() {720int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);721if (deviceType == DEVICE_TYPE_MOBILE) {722std::string name = System_GetProperty(SYSPROP_NAME);723if (KeyMap::HasBuiltinController(name)) {724return false;725} else {726return true;727}728} else if (deviceType == DEVICE_TYPE_TV) {729return false;730} else if (deviceType == DEVICE_TYPE_DESKTOP) {731return false;732} else if (deviceType == DEVICE_TYPE_VR) {733return false;734} else {735return false;736}737}738739static const float defaultControlScale = 1.15f;740static const ConfigTouchPos defaultTouchPosShow = { -1.0f, -1.0f, defaultControlScale, true };741static const ConfigTouchPos defaultTouchPosHide = { -1.0f, -1.0f, defaultControlScale, false };742743static const ConfigSetting controlSettings[] = {744ConfigSetting("HapticFeedback", &g_Config.bHapticFeedback, false, CfgFlag::PER_GAME),745ConfigSetting("ShowTouchCross", &g_Config.bShowTouchCross, true, CfgFlag::PER_GAME),746ConfigSetting("ShowTouchCircle", &g_Config.bShowTouchCircle, true, CfgFlag::PER_GAME),747ConfigSetting("ShowTouchSquare", &g_Config.bShowTouchSquare, true, CfgFlag::PER_GAME),748ConfigSetting("ShowTouchTriangle", &g_Config.bShowTouchTriangle, true, CfgFlag::PER_GAME),749750ConfigSetting("Custom0Mapping", "Custom0Image", "Custom0Shape", "Custom0Toggle", "Custom0Repeat", &g_Config.CustomButton[0], {0, 0, 0, false, false}, CfgFlag::PER_GAME),751ConfigSetting("Custom1Mapping", "Custom1Image", "Custom1Shape", "Custom1Toggle", "Custom1Repeat", &g_Config.CustomButton[1], {0, 1, 0, false, false}, CfgFlag::PER_GAME),752ConfigSetting("Custom2Mapping", "Custom2Image", "Custom2Shape", "Custom2Toggle", "Custom2Repeat", &g_Config.CustomButton[2], {0, 2, 0, false, false}, CfgFlag::PER_GAME),753ConfigSetting("Custom3Mapping", "Custom3Image", "Custom3Shape", "Custom3Toggle", "Custom3Repeat", &g_Config.CustomButton[3], {0, 3, 0, false, false}, CfgFlag::PER_GAME),754ConfigSetting("Custom4Mapping", "Custom4Image", "Custom4Shape", "Custom4Toggle", "Custom4Repeat", &g_Config.CustomButton[4], {0, 4, 0, false, false}, CfgFlag::PER_GAME),755ConfigSetting("Custom5Mapping", "Custom5Image", "Custom5Shape", "Custom5Toggle", "Custom5Repeat", &g_Config.CustomButton[5], {0, 0, 1, false, false}, CfgFlag::PER_GAME),756ConfigSetting("Custom6Mapping", "Custom6Image", "Custom6Shape", "Custom6Toggle", "Custom6Repeat", &g_Config.CustomButton[6], {0, 1, 1, false, false}, CfgFlag::PER_GAME),757ConfigSetting("Custom7Mapping", "Custom7Image", "Custom7Shape", "Custom7Toggle", "Custom7Repeat", &g_Config.CustomButton[7], {0, 2, 1, false, false}, CfgFlag::PER_GAME),758ConfigSetting("Custom8Mapping", "Custom8Image", "Custom8Shape", "Custom8Toggle", "Custom8Repeat", &g_Config.CustomButton[8], {0, 3, 1, false, false}, CfgFlag::PER_GAME),759ConfigSetting("Custom9Mapping", "Custom9Image", "Custom9Shape", "Custom9Toggle", "Custom9Repeat", &g_Config.CustomButton[9], {0, 4, 1, false, false}, CfgFlag::PER_GAME),760ConfigSetting("Custom10Mapping", "Custom10Image", "Custom10Shape", "Custom10Toggle", "Custom10Repeat", &g_Config.CustomButton[10], {0, 0, 2, false, false}, CfgFlag::PER_GAME),761ConfigSetting("Custom11Mapping", "Custom11Image", "Custom11Shape", "Custom11Toggle", "Custom11Repeat", &g_Config.CustomButton[11], {0, 1, 2, false, false}, CfgFlag::PER_GAME),762ConfigSetting("Custom12Mapping", "Custom12Image", "Custom12Shape", "Custom12Toggle", "Custom12Repeat", &g_Config.CustomButton[12], {0, 2, 2, false, false}, CfgFlag::PER_GAME),763ConfigSetting("Custom13Mapping", "Custom13Image", "Custom13Shape", "Custom13Toggle", "Custom13Repeat", &g_Config.CustomButton[13], {0, 3, 2, false, false}, CfgFlag::PER_GAME),764ConfigSetting("Custom14Mapping", "Custom14Image", "Custom14Shape", "Custom14Toggle", "Custom14Repeat", &g_Config.CustomButton[14], {0, 4, 2, false, false}, CfgFlag::PER_GAME),765ConfigSetting("Custom15Mapping", "Custom15Image", "Custom15Shape", "Custom15Toggle", "Custom15Repeat", &g_Config.CustomButton[15], {0, 0, 9, false, false}, CfgFlag::PER_GAME),766ConfigSetting("Custom16Mapping", "Custom16Image", "Custom16Shape", "Custom16Toggle", "Custom16Repeat", &g_Config.CustomButton[16], {0, 1, 9, false, false}, CfgFlag::PER_GAME),767ConfigSetting("Custom17Mapping", "Custom17Image", "Custom17Shape", "Custom17Toggle", "Custom17Repeat", &g_Config.CustomButton[17], {0, 2, 9, false, false}, CfgFlag::PER_GAME),768ConfigSetting("Custom18Mapping", "Custom18Image", "Custom18Shape", "Custom18Toggle", "Custom18Repeat", &g_Config.CustomButton[18], {0, 3, 9, false, false}, CfgFlag::PER_GAME),769ConfigSetting("Custom19Mapping", "Custom19Image", "Custom19Shape", "Custom19Toggle", "Custom19Repeat", &g_Config.CustomButton[19], {0, 4, 9, false, false}, CfgFlag::PER_GAME),770// Combo keys are something else, but I don't want to break the config backwards compatibility so these will stay wrongly named.771ConfigSetting("fcombo0X", "fcombo0Y", "comboKeyScale0", "ShowComboKey0", &g_Config.touchCustom[0], defaultTouchPosHide, CfgFlag::PER_GAME),772ConfigSetting("fcombo1X", "fcombo1Y", "comboKeyScale1", "ShowComboKey1", &g_Config.touchCustom[1], defaultTouchPosHide, CfgFlag::PER_GAME),773ConfigSetting("fcombo2X", "fcombo2Y", "comboKeyScale2", "ShowComboKey2", &g_Config.touchCustom[2], defaultTouchPosHide, CfgFlag::PER_GAME),774ConfigSetting("fcombo3X", "fcombo3Y", "comboKeyScale3", "ShowComboKey3", &g_Config.touchCustom[3], defaultTouchPosHide, CfgFlag::PER_GAME),775ConfigSetting("fcombo4X", "fcombo4Y", "comboKeyScale4", "ShowComboKey4", &g_Config.touchCustom[4], defaultTouchPosHide, CfgFlag::PER_GAME),776ConfigSetting("fcombo5X", "fcombo5Y", "comboKeyScale5", "ShowComboKey5", &g_Config.touchCustom[5], defaultTouchPosHide, CfgFlag::PER_GAME),777ConfigSetting("fcombo6X", "fcombo6Y", "comboKeyScale6", "ShowComboKey6", &g_Config.touchCustom[6], defaultTouchPosHide, CfgFlag::PER_GAME),778ConfigSetting("fcombo7X", "fcombo7Y", "comboKeyScale7", "ShowComboKey7", &g_Config.touchCustom[7], defaultTouchPosHide, CfgFlag::PER_GAME),779ConfigSetting("fcombo8X", "fcombo8Y", "comboKeyScale8", "ShowComboKey8", &g_Config.touchCustom[8], defaultTouchPosHide, CfgFlag::PER_GAME),780ConfigSetting("fcombo9X", "fcombo9Y", "comboKeyScale9", "ShowComboKey9", &g_Config.touchCustom[9], defaultTouchPosHide, CfgFlag::PER_GAME),781ConfigSetting("fcombo10X", "fcombo10Y", "comboKeyScale10", "ShowComboKey10", &g_Config.touchCustom[10], defaultTouchPosHide, CfgFlag::PER_GAME),782ConfigSetting("fcombo11X", "fcombo11Y", "comboKeyScale11", "ShowComboKey11", &g_Config.touchCustom[11], defaultTouchPosHide, CfgFlag::PER_GAME),783ConfigSetting("fcombo12X", "fcombo12Y", "comboKeyScale12", "ShowComboKey12", &g_Config.touchCustom[12], defaultTouchPosHide, CfgFlag::PER_GAME),784ConfigSetting("fcombo13X", "fcombo13Y", "comboKeyScale13", "ShowComboKey13", &g_Config.touchCustom[13], defaultTouchPosHide, CfgFlag::PER_GAME),785ConfigSetting("fcombo14X", "fcombo14Y", "comboKeyScale14", "ShowComboKey14", &g_Config.touchCustom[14], defaultTouchPosHide, CfgFlag::PER_GAME),786ConfigSetting("fcombo15X", "fcombo15Y", "comboKeyScale15", "ShowComboKey15", &g_Config.touchCustom[15], defaultTouchPosHide, CfgFlag::PER_GAME),787ConfigSetting("fcombo16X", "fcombo16Y", "comboKeyScale16", "ShowComboKey16", &g_Config.touchCustom[16], defaultTouchPosHide, CfgFlag::PER_GAME),788ConfigSetting("fcombo17X", "fcombo17Y", "comboKeyScale17", "ShowComboKey17", &g_Config.touchCustom[17], defaultTouchPosHide, CfgFlag::PER_GAME),789ConfigSetting("fcombo18X", "fcombo18Y", "comboKeyScale18", "ShowComboKey18", &g_Config.touchCustom[18], defaultTouchPosHide, CfgFlag::PER_GAME),790ConfigSetting("fcombo19X", "fcombo19Y", "comboKeyScale19", "ShowComboKey19", &g_Config.touchCustom[19], defaultTouchPosHide, CfgFlag::PER_GAME),791792#if defined(_WIN32)793// A win32 user seeing touch controls is likely using PPSSPP on a tablet. There it makes794// sense to default this to on.795ConfigSetting("ShowTouchPause", &g_Config.bShowTouchPause, true, CfgFlag::DEFAULT),796#else797ConfigSetting("ShowTouchPause", &g_Config.bShowTouchPause, false, CfgFlag::DEFAULT),798#endif799#if defined(USING_WIN_UI)800ConfigSetting("IgnoreWindowsKey", &g_Config.bIgnoreWindowsKey, false, CfgFlag::PER_GAME),801#endif802803ConfigSetting("ShowTouchControls", &g_Config.bShowTouchControls, &DefaultShowTouchControls, CfgFlag::PER_GAME),804805// ConfigSetting("KeyMapping", &g_Config.iMappingMap, 0),806807#ifdef MOBILE_DEVICE808ConfigSetting("TiltBaseAngleY", &g_Config.fTiltBaseAngleY, 0.9f, CfgFlag::PER_GAME),809ConfigSetting("TiltInvertX", &g_Config.bInvertTiltX, false, CfgFlag::PER_GAME),810ConfigSetting("TiltInvertY", &g_Config.bInvertTiltY, false, CfgFlag::PER_GAME),811ConfigSetting("TiltSensitivityX", &g_Config.iTiltSensitivityX, 60, CfgFlag::PER_GAME),812ConfigSetting("TiltSensitivityY", &g_Config.iTiltSensitivityY, 60, CfgFlag::PER_GAME),813ConfigSetting("TiltAnalogDeadzoneRadius", &g_Config.fTiltAnalogDeadzoneRadius, 0.0f, CfgFlag::PER_GAME),814ConfigSetting("TiltInverseDeadzone", &g_Config.fTiltInverseDeadzone, 0.0f, CfgFlag::PER_GAME),815ConfigSetting("TiltCircularDeadzone", &g_Config.bTiltCircularDeadzone, true, CfgFlag::PER_GAME),816ConfigSetting("TiltInputType", &g_Config.iTiltInputType, 0, CfgFlag::PER_GAME),817#endif818819ConfigSetting("DisableDpadDiagonals", &g_Config.bDisableDpadDiagonals, false, CfgFlag::PER_GAME),820ConfigSetting("GamepadOnlyFocused", &g_Config.bGamepadOnlyFocused, false, CfgFlag::PER_GAME),821ConfigSetting("TouchButtonStyle", &g_Config.iTouchButtonStyle, 1, CfgFlag::PER_GAME),822ConfigSetting("TouchButtonOpacity", &g_Config.iTouchButtonOpacity, 65, CfgFlag::PER_GAME),823ConfigSetting("TouchButtonHideSeconds", &g_Config.iTouchButtonHideSeconds, 20, CfgFlag::PER_GAME),824ConfigSetting("AutoCenterTouchAnalog", &g_Config.bAutoCenterTouchAnalog, false, CfgFlag::PER_GAME),825ConfigSetting("StickyTouchDPad", &g_Config.bStickyTouchDPad, false, CfgFlag::PER_GAME),826827// Snap touch control position828ConfigSetting("TouchSnapToGrid", &g_Config.bTouchSnapToGrid, false, CfgFlag::PER_GAME),829ConfigSetting("TouchSnapGridSize", &g_Config.iTouchSnapGridSize, 64, CfgFlag::PER_GAME),830831// -1.0f means uninitialized, set in GamepadEmu::CreatePadLayout().832ConfigSetting("ActionButtonSpacing2", &g_Config.fActionButtonSpacing, 1.0f, CfgFlag::PER_GAME),833ConfigSetting("ActionButtonCenterX", "ActionButtonCenterY", "ActionButtonScale", nullptr, &g_Config.touchActionButtonCenter, defaultTouchPosShow, CfgFlag::PER_GAME),834ConfigSetting("DPadX", "DPadY", "DPadScale", "ShowTouchDpad", &g_Config.touchDpad, defaultTouchPosShow, CfgFlag::PER_GAME),835836// Note: these will be overwritten if DPadRadius is set.837ConfigSetting("DPadSpacing", &g_Config.fDpadSpacing, 1.0f, CfgFlag::PER_GAME),838ConfigSetting("StartKeyX", "StartKeyY", "StartKeyScale", "ShowTouchStart", &g_Config.touchStartKey, defaultTouchPosShow, CfgFlag::PER_GAME),839ConfigSetting("SelectKeyX", "SelectKeyY", "SelectKeyScale", "ShowTouchSelect", &g_Config.touchSelectKey, defaultTouchPosShow, CfgFlag::PER_GAME),840ConfigSetting("UnthrottleKeyX", "UnthrottleKeyY", "UnthrottleKeyScale", "ShowTouchUnthrottle", &g_Config.touchFastForwardKey, defaultTouchPosShow, CfgFlag::PER_GAME),841ConfigSetting("LKeyX", "LKeyY", "LKeyScale", "ShowTouchLTrigger", &g_Config.touchLKey, defaultTouchPosShow, CfgFlag::PER_GAME),842ConfigSetting("RKeyX", "RKeyY", "RKeyScale", "ShowTouchRTrigger", &g_Config.touchRKey, defaultTouchPosShow, CfgFlag::PER_GAME),843ConfigSetting("AnalogStickX", "AnalogStickY", "AnalogStickScale", "ShowAnalogStick", &g_Config.touchAnalogStick, defaultTouchPosShow, CfgFlag::PER_GAME),844ConfigSetting("RightAnalogStickX", "RightAnalogStickY", "RightAnalogStickScale", "ShowRightAnalogStick", &g_Config.touchRightAnalogStick, defaultTouchPosHide, CfgFlag::PER_GAME),845846ConfigSetting("AnalogDeadzone", &g_Config.fAnalogDeadzone, 0.15f, CfgFlag::PER_GAME),847ConfigSetting("AnalogInverseDeadzone", &g_Config.fAnalogInverseDeadzone, 0.0f, CfgFlag::PER_GAME),848ConfigSetting("AnalogSensitivity", &g_Config.fAnalogSensitivity, 1.1f, CfgFlag::PER_GAME),849ConfigSetting("AnalogIsCircular", &g_Config.bAnalogIsCircular, false, CfgFlag::PER_GAME),850ConfigSetting("AnalogAutoRotSpeed", &g_Config.fAnalogAutoRotSpeed, 8.0f, CfgFlag::PER_GAME),851852ConfigSetting("AnalogLimiterDeadzone", &g_Config.fAnalogLimiterDeadzone, 0.6f, CfgFlag::DEFAULT),853ConfigSetting("AnalogTriggerThreshold", &g_Config.fAnalogTriggerThreshold, 0.75f, CfgFlag::DEFAULT),854855ConfigSetting("AllowMappingCombos", &g_Config.bAllowMappingCombos, false, CfgFlag::DEFAULT),856ConfigSetting("StrictComboOrder", &g_Config.bStrictComboOrder, false, CfgFlag::DEFAULT),857858ConfigSetting("LeftStickHeadScale", &g_Config.fLeftStickHeadScale, 1.0f, CfgFlag::PER_GAME),859ConfigSetting("RightStickHeadScale", &g_Config.fRightStickHeadScale, 1.0f, CfgFlag::PER_GAME),860ConfigSetting("HideStickBackground", &g_Config.bHideStickBackground, false, CfgFlag::PER_GAME),861862ConfigSetting("UseMouse", &g_Config.bMouseControl, false, CfgFlag::PER_GAME),863ConfigSetting("MapMouse", &g_Config.bMapMouse, false, CfgFlag::PER_GAME),864ConfigSetting("ConfineMap", &g_Config.bMouseConfine, false, CfgFlag::PER_GAME),865ConfigSetting("MouseSensitivity", &g_Config.fMouseSensitivity, 0.1f, CfgFlag::PER_GAME),866ConfigSetting("MouseSmoothing", &g_Config.fMouseSmoothing, 0.9f, CfgFlag::PER_GAME),867ConfigSetting("MouseWheelUpDelayMs", &g_Config.iMouseWheelUpDelayMs, 80, CfgFlag::PER_GAME),868869ConfigSetting("SystemControls", &g_Config.bSystemControls, true, CfgFlag::DEFAULT),870ConfigSetting("RapidFileInterval", &g_Config.iRapidFireInterval, 5, CfgFlag::DEFAULT),871872ConfigSetting("AnalogGesture", &g_Config.bAnalogGesture, false, CfgFlag::PER_GAME),873ConfigSetting("AnalogGestureSensibility", &g_Config.fAnalogGestureSensibility, 1.0f, CfgFlag::PER_GAME),874};875876static const ConfigSetting networkSettings[] = {877ConfigSetting("EnableWlan", &g_Config.bEnableWlan, false, CfgFlag::PER_GAME),878ConfigSetting("EnableAdhocServer", &g_Config.bEnableAdhocServer, false, CfgFlag::PER_GAME),879ConfigSetting("proAdhocServer", &g_Config.proAdhocServer, "socom.cc", CfgFlag::PER_GAME),880ConfigSetting("PortOffset", &g_Config.iPortOffset, 10000, CfgFlag::PER_GAME),881ConfigSetting("MinTimeout", &g_Config.iMinTimeout, 0, CfgFlag::PER_GAME),882ConfigSetting("ForcedFirstConnect", &g_Config.bForcedFirstConnect, false, CfgFlag::PER_GAME),883ConfigSetting("EnableUPnP", &g_Config.bEnableUPnP, false, CfgFlag::PER_GAME),884ConfigSetting("UPnPUseOriginalPort", &g_Config.bUPnPUseOriginalPort, false, CfgFlag::PER_GAME),885886ConfigSetting("EnableNetworkChat", &g_Config.bEnableNetworkChat, false, CfgFlag::PER_GAME),887ConfigSetting("ChatButtonPosition", &g_Config.iChatButtonPosition, (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME),888ConfigSetting("ChatScreenPosition", &g_Config.iChatScreenPosition, (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME),889ConfigSetting("EnableQuickChat", &g_Config.bEnableQuickChat, true, CfgFlag::PER_GAME),890ConfigSetting("QuickChat1", &g_Config.sQuickChat0, "Quick Chat 1", CfgFlag::PER_GAME),891ConfigSetting("QuickChat2", &g_Config.sQuickChat1, "Quick Chat 2", CfgFlag::PER_GAME),892ConfigSetting("QuickChat3", &g_Config.sQuickChat2, "Quick Chat 3", CfgFlag::PER_GAME),893ConfigSetting("QuickChat4", &g_Config.sQuickChat3, "Quick Chat 4", CfgFlag::PER_GAME),894ConfigSetting("QuickChat5", &g_Config.sQuickChat4, "Quick Chat 5", CfgFlag::PER_GAME),895};896897static const ConfigSetting systemParamSettings[] = {898ConfigSetting("PSPModel", &g_Config.iPSPModel, PSP_MODEL_SLIM, CfgFlag::PER_GAME | CfgFlag::REPORT),899ConfigSetting("PSPFirmwareVersion", &g_Config.iFirmwareVersion, PSP_DEFAULT_FIRMWARE, CfgFlag::PER_GAME | CfgFlag::REPORT),900ConfigSetting("NickName", &g_Config.sNickName, "PPSSPP", CfgFlag::PER_GAME),901ConfigSetting("MacAddress", &g_Config.sMACAddress, "", CfgFlag::PER_GAME),902ConfigSetting("GameLanguage", &g_Config.iLanguage, -1, CfgFlag::PER_GAME | CfgFlag::REPORT),903ConfigSetting("ParamTimeFormat", &g_Config.iTimeFormat, PSP_SYSTEMPARAM_TIME_FORMAT_24HR, CfgFlag::PER_GAME),904ConfigSetting("ParamDateFormat", &g_Config.iDateFormat, PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD, CfgFlag::PER_GAME),905ConfigSetting("TimeZone", &g_Config.iTimeZone, 0, CfgFlag::PER_GAME),906ConfigSetting("DayLightSavings", &g_Config.bDayLightSavings, (bool) PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_STD, CfgFlag::PER_GAME),907ConfigSetting("ButtonPreference", &g_Config.iButtonPreference, PSP_SYSTEMPARAM_BUTTON_CROSS, CfgFlag::PER_GAME | CfgFlag::REPORT),908ConfigSetting("LockParentalLevel", &g_Config.iLockParentalLevel, 0, CfgFlag::PER_GAME),909ConfigSetting("WlanAdhocChannel", &g_Config.iWlanAdhocChannel, PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC, CfgFlag::PER_GAME),910#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)911ConfigSetting("BypassOSKWithKeyboard", &g_Config.bBypassOSKWithKeyboard, false, CfgFlag::PER_GAME),912#endif913ConfigSetting("WlanPowerSave", &g_Config.bWlanPowerSave, (bool) PSP_SYSTEMPARAM_WLAN_POWERSAVE_OFF, CfgFlag::PER_GAME),914ConfigSetting("EncryptSave", &g_Config.bEncryptSave, true, CfgFlag::PER_GAME | CfgFlag::REPORT),915ConfigSetting("SavedataUpgradeVersion", &g_Config.bSavedataUpgrade, true, CfgFlag::DEFAULT),916ConfigSetting("MemStickSize", &g_Config.iMemStickSizeGB, 16, CfgFlag::DEFAULT),917};918919static const ConfigSetting debuggerSettings[] = {920ConfigSetting("DisasmWindowX", &g_Config.iDisasmWindowX, -1, CfgFlag::DEFAULT),921ConfigSetting("DisasmWindowY", &g_Config.iDisasmWindowY, -1, CfgFlag::DEFAULT),922ConfigSetting("DisasmWindowW", &g_Config.iDisasmWindowW, -1, CfgFlag::DEFAULT),923ConfigSetting("DisasmWindowH", &g_Config.iDisasmWindowH, -1, CfgFlag::DEFAULT),924ConfigSetting("GEWindowX", &g_Config.iGEWindowX, -1, CfgFlag::DEFAULT),925ConfigSetting("GEWindowY", &g_Config.iGEWindowY, -1, CfgFlag::DEFAULT),926ConfigSetting("GEWindowW", &g_Config.iGEWindowW, -1, CfgFlag::DEFAULT),927ConfigSetting("GEWindowH", &g_Config.iGEWindowH, -1, CfgFlag::DEFAULT),928ConfigSetting("GEWindowTabsBL", &g_Config.uGETabsLeft, (uint32_t)0, CfgFlag::DEFAULT),929ConfigSetting("GEWindowTabsBR", &g_Config.uGETabsRight, (uint32_t)0, CfgFlag::DEFAULT),930ConfigSetting("GEWindowTabsTR", &g_Config.uGETabsTopRight, (uint32_t)0, CfgFlag::DEFAULT),931ConfigSetting("ConsoleWindowX", &g_Config.iConsoleWindowX, -1, CfgFlag::DEFAULT),932ConfigSetting("ConsoleWindowY", &g_Config.iConsoleWindowY, -1, CfgFlag::DEFAULT),933ConfigSetting("FontWidth", &g_Config.iFontWidth, 8, CfgFlag::DEFAULT),934ConfigSetting("FontHeight", &g_Config.iFontHeight, 12, CfgFlag::DEFAULT),935ConfigSetting("DisplayStatusBar", &g_Config.bDisplayStatusBar, true, CfgFlag::DEFAULT),936ConfigSetting("ShowBottomTabTitles",&g_Config.bShowBottomTabTitles, true, CfgFlag::DEFAULT),937ConfigSetting("ShowDeveloperMenu", &g_Config.bShowDeveloperMenu, false, CfgFlag::DEFAULT),938ConfigSetting("SkipDeadbeefFilling", &g_Config.bSkipDeadbeefFilling, false, CfgFlag::DEFAULT),939ConfigSetting("FuncHashMap", &g_Config.bFuncHashMap, false, CfgFlag::DEFAULT),940ConfigSetting("SkipFuncHashMap", &g_Config.sSkipFuncHashMap, "", CfgFlag::DEFAULT),941ConfigSetting("MemInfoDetailed", &g_Config.bDebugMemInfoDetailed, false, CfgFlag::DEFAULT),942};943944static const ConfigSetting jitSettings[] = {945ConfigSetting("DiscardRegsOnJRRA", &g_Config.bDiscardRegsOnJRRA, false, CfgFlag::DONT_SAVE | CfgFlag::REPORT),946};947948static const ConfigSetting upgradeSettings[] = {949ConfigSetting("UpgradeMessage", &g_Config.upgradeMessage, "", CfgFlag::DEFAULT),950ConfigSetting("UpgradeVersion", &g_Config.upgradeVersion, "", CfgFlag::DEFAULT),951ConfigSetting("DismissedVersion", &g_Config.dismissedVersion, "", CfgFlag::DEFAULT),952};953954static const ConfigSetting themeSettings[] = {955ConfigSetting("ThemeName", &g_Config.sThemeName, "Default", CfgFlag::DEFAULT),956};957958959static const ConfigSetting vrSettings[] = {960ConfigSetting("VREnable", &g_Config.bEnableVR, true, CfgFlag::PER_GAME),961ConfigSetting("VREnable6DoF", &g_Config.bEnable6DoF, false, CfgFlag::PER_GAME),962ConfigSetting("VREnableStereo", &g_Config.bEnableStereo, false, CfgFlag::PER_GAME),963ConfigSetting("VRForce72Hz", &g_Config.bForce72Hz, true, CfgFlag::PER_GAME),964ConfigSetting("VRForce", &g_Config.bForceVR, false, CfgFlag::DEFAULT),965ConfigSetting("VRImmersiveMode", &g_Config.bEnableImmersiveVR, true, CfgFlag::PER_GAME),966ConfigSetting("VRManualForceVR", &g_Config.bManualForceVR, false, CfgFlag::PER_GAME),967ConfigSetting("VRPassthrough", &g_Config.bPassthrough, false, CfgFlag::PER_GAME),968ConfigSetting("VRRescaleHUD", &g_Config.bRescaleHUD, true, CfgFlag::PER_GAME),969ConfigSetting("VRCameraDistance", &g_Config.fCameraDistance, 0.0f, CfgFlag::PER_GAME),970ConfigSetting("VRCameraHeight", &g_Config.fCameraHeight, 0.0f, CfgFlag::PER_GAME),971ConfigSetting("VRCameraSide", &g_Config.fCameraSide, 0.0f, CfgFlag::PER_GAME),972ConfigSetting("VRCameraPitch", &g_Config.fCameraPitch, 0.0f, CfgFlag::PER_GAME),973ConfigSetting("VRCanvasDistance", &g_Config.fCanvasDistance, 12.0f, CfgFlag::DEFAULT),974ConfigSetting("VRCanvas3DDistance", &g_Config.fCanvas3DDistance, 3.0f, CfgFlag::DEFAULT),975ConfigSetting("VRFieldOfView", &g_Config.fFieldOfViewPercentage, 100.0f, CfgFlag::PER_GAME),976ConfigSetting("VRHeadUpDisplayScale", &g_Config.fHeadUpDisplayScale, 0.3f, CfgFlag::PER_GAME),977};978979static const ConfigSectionSettings sections[] = {980{"General", generalSettings, ARRAY_SIZE(generalSettings)},981{"CPU", cpuSettings, ARRAY_SIZE(cpuSettings)},982{"Graphics", graphicsSettings, ARRAY_SIZE(graphicsSettings)},983{"Sound", soundSettings, ARRAY_SIZE(soundSettings)},984{"Control", controlSettings, ARRAY_SIZE(controlSettings)},985{"Network", networkSettings, ARRAY_SIZE(networkSettings)},986{"SystemParam", systemParamSettings, ARRAY_SIZE(systemParamSettings)},987{"Debugger", debuggerSettings, ARRAY_SIZE(debuggerSettings)},988{"JIT", jitSettings, ARRAY_SIZE(jitSettings)},989{"Upgrade", upgradeSettings, ARRAY_SIZE(upgradeSettings)},990{"Theme", themeSettings, ARRAY_SIZE(themeSettings)},991{"VR", vrSettings, ARRAY_SIZE(vrSettings)},992{"Achievements", achievementSettings, ARRAY_SIZE(achievementSettings)},993};994995const size_t numSections = ARRAY_SIZE(sections);996997static void IterateSettings(IniFile &iniFile, std::function<void(Section *section, const ConfigSetting &setting)> func) {998for (size_t i = 0; i < numSections; ++i) {999Section *section = iniFile.GetOrCreateSection(sections[i].section);1000for (size_t j = 0; j < sections[i].settingsCount; j++) {1001func(section, sections[i].settings[j]);1002}1003}1004}10051006static void IterateSettings(std::function<void(const ConfigSetting &setting)> func) {1007for (size_t i = 0; i < numSections; ++i) {1008for (size_t j = 0; j < sections[i].settingsCount; j++) {1009func(sections[i].settings[j]);1010}1011}1012}10131014void ConfigPrivate::ResetRecentIsosThread() {1015std::lock_guard<std::mutex> guard(recentIsosThreadLock);1016if (recentIsosThreadPending && recentIsosThread.joinable())1017recentIsosThread.join();1018}10191020void ConfigPrivate::SetRecentIsosThread(std::function<void()> f) {1021std::lock_guard<std::mutex> guard(recentIsosThreadLock);1022if (recentIsosThreadPending && recentIsosThread.joinable())1023recentIsosThread.join();1024recentIsosThread = std::thread(f);1025recentIsosThreadPending = true;1026}10271028Config::Config() {1029private_ = new ConfigPrivate();1030}10311032Config::~Config() {1033if (bUpdatedInstanceCounter) {1034ShutdownInstanceCounter();1035}1036private_->ResetRecentIsosThread();1037delete private_;1038}10391040void Config::LoadLangValuesMapping() {1041IniFile mapping;1042mapping.LoadFromVFS(g_VFS, "langregion.ini");1043std::vector<std::string> keys;1044mapping.GetKeys("LangRegionNames", keys);10451046std::map<std::string, int> langCodeMapping;1047langCodeMapping["JAPANESE"] = PSP_SYSTEMPARAM_LANGUAGE_JAPANESE;1048langCodeMapping["ENGLISH"] = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;1049langCodeMapping["FRENCH"] = PSP_SYSTEMPARAM_LANGUAGE_FRENCH;1050langCodeMapping["SPANISH"] = PSP_SYSTEMPARAM_LANGUAGE_SPANISH;1051langCodeMapping["GERMAN"] = PSP_SYSTEMPARAM_LANGUAGE_GERMAN;1052langCodeMapping["ITALIAN"] = PSP_SYSTEMPARAM_LANGUAGE_ITALIAN;1053langCodeMapping["DUTCH"] = PSP_SYSTEMPARAM_LANGUAGE_DUTCH;1054langCodeMapping["PORTUGUESE"] = PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE;1055langCodeMapping["RUSSIAN"] = PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN;1056langCodeMapping["KOREAN"] = PSP_SYSTEMPARAM_LANGUAGE_KOREAN;1057langCodeMapping["CHINESE_TRADITIONAL"] = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL;1058langCodeMapping["CHINESE_SIMPLIFIED"] = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED;10591060const Section *langRegionNames = mapping.GetOrCreateSection("LangRegionNames");1061const Section *systemLanguage = mapping.GetOrCreateSection("SystemLanguage");10621063for (size_t i = 0; i < keys.size(); i++) {1064std::string langName;1065langRegionNames->Get(keys[i].c_str(), &langName, "ERROR");1066std::string langCode;1067systemLanguage->Get(keys[i].c_str(), &langCode, "ENGLISH");1068int iLangCode = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;1069if (langCodeMapping.find(langCode) != langCodeMapping.end())1070iLangCode = langCodeMapping[langCode];1071langValuesMapping_[keys[i]] = std::make_pair(langName, iLangCode);1072}1073}10741075const std::map<std::string, std::pair<std::string, int>, std::less<>> &Config::GetLangValuesMapping() {1076if (langValuesMapping_.empty()) {1077LoadLangValuesMapping();1078}1079return langValuesMapping_;1080}10811082void Config::Reload() {1083reload_ = true;1084Load();1085reload_ = false;1086}10871088// Call this if you change the search path (such as when changing memstick directory. can't1089// really think of any other legit uses).1090void Config::UpdateIniLocation(const char *iniFileName, const char *controllerIniFilename) {1091const bool useIniFilename = iniFileName != nullptr && strlen(iniFileName) > 0;1092const char *ppssppIniFilename = IsVREnabled() ? "ppssppvr.ini" : "ppsspp.ini";1093iniFilename_ = FindConfigFile(useIniFilename ? iniFileName : ppssppIniFilename);1094const bool useControllerIniFilename = controllerIniFilename != nullptr && strlen(controllerIniFilename) > 0;1095const char *controlsIniFilename = IsVREnabled() ? "controlsvr.ini" : "controls.ini";1096controllerIniFilename_ = FindConfigFile(useControllerIniFilename ? controllerIniFilename : controlsIniFilename);1097}10981099bool Config::LoadAppendedConfig() {1100IniFile iniFile;1101if (!iniFile.Load(appendedConfigFileName_)) {1102ERROR_LOG(Log::Loader, "Failed to read appended config '%s'.", appendedConfigFileName_.c_str());1103return false;1104}11051106IterateSettings(iniFile, [&iniFile](Section *section, const ConfigSetting &setting) {1107if (iniFile.Exists(section->name().c_str(), setting.iniKey_))1108setting.Get(section);1109});11101111INFO_LOG(Log::Loader, "Loaded appended config '%s'.", appendedConfigFileName_.c_str());11121113Save("Loaded appended config"); // Let's prevent reset1114return true;1115}11161117void Config::SetAppendedConfigIni(const Path &path) {1118appendedConfigFileName_ = path;1119}11201121void Config::UpdateAfterSettingAutoFrameSkip() {1122if (bAutoFrameSkip && iFrameSkip == 0) {1123iFrameSkip = 1;1124}11251126if (bAutoFrameSkip && bSkipBufferEffects) {1127bSkipBufferEffects = false;1128}1129}11301131void Config::Load(const char *iniFileName, const char *controllerIniFilename) {1132double startTime = time_now_d();11331134if (!bUpdatedInstanceCounter) {1135InitInstanceCounter();1136bUpdatedInstanceCounter = true;1137}11381139g_DownloadManager.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION));11401141UpdateIniLocation(iniFileName, controllerIniFilename);11421143INFO_LOG(Log::Loader, "Loading config: %s", iniFilename_.c_str());1144bSaveSettings = true;11451146IniFile iniFile;1147if (!iniFile.Load(iniFilename_)) {1148ERROR_LOG(Log::Loader, "Failed to read '%s'. Setting config to default.", iniFilename_.c_str());1149// Continue anyway to initialize the config.1150}11511152IterateSettings(iniFile, [](Section *section, const ConfigSetting &setting) {1153setting.Get(section);1154});11551156iRunCount++;11571158// For iOS, issue #192111159TryUpdateSavedPath(¤tDirectory);11601161// This check is probably not really necessary here anyway, you can always1162// press Home or Browse if you're in a bad directory.1163if (!File::Exists(currentDirectory))1164currentDirectory = defaultCurrentDirectory;11651166Section *log = iniFile.GetOrCreateSection(logSectionName);11671168bool debugDefaults = false;1169#ifdef _DEBUG1170debugDefaults = true;1171#endif1172LogManager::GetInstance()->LoadConfig(log, debugDefaults);11731174Section *recent = iniFile.GetOrCreateSection("Recent");1175recent->Get("MaxRecent", &iMaxRecent, 60);11761177// Fix issue from switching from uint (hex in .ini) to int (dec)1178// -1 is okay, though. We'll just ignore recent stuff if it is.1179if (iMaxRecent == 0)1180iMaxRecent = 60;11811182// Fix JIT setting if no longer available.1183if (!System_GetPropertyBool(SYSPROP_CAN_JIT)) {1184if (iCpuCore == (int)CPUCore::JIT || iCpuCore == (int)CPUCore::JIT_IR) {1185WARN_LOG(Log::Loader, "Forcing JIT off due to unavailablility");1186iCpuCore = (int)CPUCore::IR_INTERPRETER;1187}1188}11891190if (iMaxRecent > 0) {1191private_->ResetRecentIsosThread();1192std::lock_guard<std::mutex> guard(private_->recentIsosLock);1193recentIsos.clear();1194for (int i = 0; i < iMaxRecent; i++) {1195char keyName[64];1196std::string fileName;11971198snprintf(keyName, sizeof(keyName), "FileName%d", i);1199if (recent->Get(keyName, &fileName, "") && !fileName.empty()) {1200recentIsos.push_back(fileName);1201}1202}1203}12041205// Time tracking1206Section *playTime = iniFile.GetOrCreateSection("PlayTime");1207playTimeTracker_.Load(playTime);12081209auto pinnedPaths = iniFile.GetOrCreateSection("PinnedPaths")->ToMap();1210vPinnedPaths.clear();1211for (const auto &[_, value] : pinnedPaths) {1212// Unpin paths that are deleted automatically.1213const std::string &path = value;1214if (startsWith(path, "http://") || startsWith(path, "https://") || File::Exists(Path(path))) {1215vPinnedPaths.push_back(File::ResolvePath(path));1216}1217}12181219// Default values for post process shaders1220bool postShadersInitialized = iniFile.HasSection("PostShaderList");1221Section *postShaderChain = iniFile.GetOrCreateSection("PostShaderList");1222Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting");1223if (IsVREnabled() && !postShadersInitialized) {1224postShaderChain->Set("PostShader1", "ColorCorrection");1225postShaderSetting->Set("ColorCorrectionSettingCurrentValue1", 1.0f);1226postShaderSetting->Set("ColorCorrectionSettingCurrentValue2", 1.5f);1227postShaderSetting->Set("ColorCorrectionSettingCurrentValue3", 1.1f);1228postShaderSetting->Set("ColorCorrectionSettingCurrentValue4", 1.0f);1229}12301231// Load post process shader values1232mPostShaderSetting.clear();1233for (const auto &[key, value] : postShaderSetting->ToMap()) {1234mPostShaderSetting[key] = std::stof(value);1235}12361237// Load post process shader names1238vPostShaderNames.clear();1239for (const auto& it : postShaderChain->ToMap()) {1240if (it.second != "Off")1241vPostShaderNames.push_back(it.second);1242}12431244// Check for an old dpad setting1245Section *control = iniFile.GetOrCreateSection("Control");1246float f;1247control->Get("DPadRadius", &f, 0.0f);1248if (f > 0.0f) {1249ResetControlLayout();1250}12511252// Force JIT setting to a valid value for the current system configuration.1253if (!System_GetPropertyBool(SYSPROP_CAN_JIT)) {1254if (g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR) {1255g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER;1256}1257}12581259const char *gitVer = PPSSPP_GIT_VERSION;1260Version installed(gitVer);1261Version upgrade(upgradeVersion);1262const bool versionsValid = installed.IsValid() && upgrade.IsValid();12631264// Do this regardless of iRunCount to prevent a silly bug where one might use an older1265// build of PPSSPP, receive an upgrade notice, then start a newer version, and still receive the upgrade notice,1266// even if said newer version is >= the upgrade found online.1267if ((dismissedVersion == upgradeVersion) || (versionsValid && (installed >= upgrade))) {1268upgradeMessage.clear();1269}12701271// Check for new version on every 10 runs.1272// Sometimes the download may not be finished when the main screen shows (if the user dismisses the1273// splash screen quickly), but then we'll just show the notification next time instead, we store the1274// upgrade number in the ini.1275if (iRunCount % 10 == 0 && bCheckForNewVersion) {1276const char *versionUrl = "http://www.ppsspp.org/version.json";1277const char *acceptMime = "application/json, text/*; q=0.9, */*; q=0.8";1278g_DownloadManager.StartDownloadWithCallback(versionUrl, Path(), http::ProgressBarMode::NONE, &DownloadCompletedCallback, "version", acceptMime);1279}12801281INFO_LOG(Log::Loader, "Loading controller config: %s", controllerIniFilename_.c_str());1282bSaveSettings = true;12831284LoadStandardControllerIni();12851286//so this is all the way down here to overwrite the controller settings1287//sadly it won't benefit from all the "version conversion" going on up-above1288//but these configs shouldn't contain older versions anyhow1289if (bGameSpecific) {1290loadGameConfig(gameId_, gameIdTitle_);1291}12921293CleanRecent();12941295PostLoadCleanup(false);12961297INFO_LOG(Log::Loader, "Config loaded: '%s' (%0.1f ms)", iniFilename_.c_str(), (time_now_d() - startTime) * 1000.0);1298}12991300bool Config::Save(const char *saveReason) {1301double startTime = time_now_d();1302if (!IsFirstInstance()) {1303// TODO: Should we allow saving config if started from a different directory?1304// How do we tell?1305WARN_LOG(Log::Loader, "Not saving config - secondary instances don't.");13061307// Don't want to retry or something.1308return true;1309}13101311if (!iniFilename_.empty() && g_Config.bSaveSettings) {1312saveGameConfig(gameId_, gameIdTitle_);13131314PreSaveCleanup(false);13151316CleanRecent();1317IniFile iniFile;1318if (!iniFile.Load(iniFilename_)) {1319WARN_LOG(Log::Loader, "Likely saving config for first time - couldn't read ini '%s'", iniFilename_.c_str());1320}13211322// Need to do this somewhere...1323bFirstRun = false;13241325IterateSettings(iniFile, [&](Section *section, const ConfigSetting &setting) {1326if (!bGameSpecific || !setting.PerGame()) {1327setting.Set(section);1328}1329});13301331Section *recent = iniFile.GetOrCreateSection("Recent");1332recent->Set("MaxRecent", iMaxRecent);13331334private_->ResetRecentIsosThread();1335for (int i = 0; i < iMaxRecent; i++) {1336char keyName[64];1337snprintf(keyName, sizeof(keyName), "FileName%d", i);1338std::lock_guard<std::mutex> guard(private_->recentIsosLock);1339if (i < (int)recentIsos.size()) {1340recent->Set(keyName, recentIsos[i]);1341} else {1342recent->Delete(keyName); // delete the nonexisting FileName1343}1344}13451346Section *pinnedPaths = iniFile.GetOrCreateSection("PinnedPaths");1347pinnedPaths->Clear();1348for (size_t i = 0; i < vPinnedPaths.size(); ++i) {1349char keyName[64];1350snprintf(keyName, sizeof(keyName), "Path%d", (int)i);1351pinnedPaths->Set(keyName, vPinnedPaths[i]);1352}13531354if (!bGameSpecific) {1355Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting");1356postShaderSetting->Clear();1357for (const auto &[k, v] : mPostShaderSetting) {1358postShaderSetting->Set(k.c_str(), v);1359}1360Section *postShaderChain = iniFile.GetOrCreateSection("PostShaderList");1361postShaderChain->Clear();1362for (size_t i = 0; i < vPostShaderNames.size(); ++i) {1363char keyName[64];1364snprintf(keyName, sizeof(keyName), "PostShader%d", (int)i+1);1365postShaderChain->Set(keyName, vPostShaderNames[i]);1366}1367}13681369Section *control = iniFile.GetOrCreateSection("Control");1370control->Delete("DPadRadius");13711372Section *log = iniFile.GetOrCreateSection(logSectionName);1373if (LogManager::GetInstance())1374LogManager::GetInstance()->SaveConfig(log);13751376// Time tracking1377Section *playTime = iniFile.GetOrCreateSection("PlayTime");1378playTimeTracker_.Save(playTime);13791380if (!iniFile.Save(iniFilename_)) {1381ERROR_LOG(Log::Loader, "Error saving config (%s) - can't write ini '%s'", saveReason, iniFilename_.c_str());1382return false;1383}1384INFO_LOG(Log::Loader, "Config saved (%s): '%s' (%0.1f ms)", saveReason, iniFilename_.c_str(), (time_now_d() - startTime) * 1000.0);13851386if (!bGameSpecific) //otherwise we already did this in saveGameConfig()1387{1388IniFile controllerIniFile;1389if (!controllerIniFile.Load(controllerIniFilename_)) {1390ERROR_LOG(Log::Loader, "Error saving controller config - can't read ini first '%s'", controllerIniFilename_.c_str());1391}1392KeyMap::SaveToIni(controllerIniFile);1393if (!controllerIniFile.Save(controllerIniFilename_)) {1394ERROR_LOG(Log::Loader, "Error saving config - can't write ini '%s'", controllerIniFilename_.c_str());1395return false;1396}1397INFO_LOG(Log::Loader, "Controller config saved: %s", controllerIniFilename_.c_str());1398}13991400PostSaveCleanup(false);1401} else {1402INFO_LOG(Log::Loader, "Not saving config");1403}14041405return true;1406}14071408void Config::PostLoadCleanup(bool gameSpecific) {1409// Override ppsspp.ini JIT value to prevent crashing1410jitForcedOff = DefaultCpuCore() != (int)CPUCore::JIT && (g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR);1411if (jitForcedOff) {1412g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER;1413}14141415// This caps the exponent 4 (so 16x.)1416if (iAnisotropyLevel > 4) {1417iAnisotropyLevel = 4;1418}14191420// Set a default MAC, and correct if it's an old format.1421if (sMACAddress.length() != 17)1422sMACAddress = CreateRandMAC();14231424if (g_Config.bAutoFrameSkip && g_Config.bSkipBufferEffects) {1425g_Config.bSkipBufferEffects = false;1426}14271428// Automatically silence secondary instances. Could be an option I guess, but meh.1429if (PPSSPP_ID > 1) {1430g_Config.iGlobalVolume = 0;1431}14321433// Automatically switch away from deprecated setting value.1434if (iTexScalingLevel <= 0) {1435iTexScalingLevel = 1;1436}14371438// Remove a legacy value.1439if (g_Config.sCustomDriver == "Default") {1440g_Config.sCustomDriver = "";1441}1442}14431444void Config::PreSaveCleanup(bool gameSpecific) {1445if (jitForcedOff) {1446// If we forced jit off and it's still set to IR, change it back to jit.1447if (g_Config.iCpuCore == (int)CPUCore::IR_INTERPRETER)1448g_Config.iCpuCore = (int)CPUCore::JIT;1449}1450}14511452void Config::PostSaveCleanup(bool gameSpecific) {1453if (jitForcedOff) {1454// Force JIT off again just in case Config::Save() is called without exiting PPSSPP.1455if (g_Config.iCpuCore == (int)CPUCore::JIT)1456g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER;1457}1458}14591460void Config::NotifyUpdatedCpuCore() {1461if (jitForcedOff && g_Config.iCpuCore == (int)CPUCore::IR_INTERPRETER) {1462// No longer forced off, the user set it to IR jit.1463jitForcedOff = false;1464}1465}14661467// Use for debugging the version check without messing with the server1468#if 01469#define PPSSPP_GIT_VERSION "v0.0.1-gaaaaaaaaa"1470#endif14711472void Config::DownloadCompletedCallback(http::Request &download) {1473if (download.ResultCode() != 200) {1474ERROR_LOG(Log::Loader, "Failed to download %s: %d", download.url().c_str(), download.ResultCode());1475return;1476}1477std::string data;1478download.buffer().TakeAll(&data);1479if (data.empty()) {1480ERROR_LOG(Log::Loader, "Version check: Empty data from server!");1481return;1482}14831484json::JsonReader reader(data.c_str(), data.size());1485const json::JsonGet root = reader.root();1486if (!root) {1487ERROR_LOG(Log::Loader, "Failed to parse json");1488return;1489}14901491std::string version;1492root.getString("version", &version);14931494const char *gitVer = PPSSPP_GIT_VERSION;1495Version installed(gitVer);1496Version upgrade(version);1497Version dismissed(g_Config.dismissedVersion);14981499if (!installed.IsValid()) {1500ERROR_LOG(Log::Loader, "Version check: Local version string invalid. Build problems? %s", PPSSPP_GIT_VERSION);1501return;1502}1503if (!upgrade.IsValid()) {1504ERROR_LOG(Log::Loader, "Version check: Invalid server version: %s", version.c_str());1505return;1506}15071508if (installed >= upgrade) {1509INFO_LOG(Log::Loader, "Version check: Already up to date, erasing any upgrade message");1510g_Config.upgradeMessage.clear();1511g_Config.upgradeVersion = upgrade.ToString();1512g_Config.dismissedVersion.clear();1513return;1514}15151516if (installed < upgrade && dismissed != upgrade) {1517g_Config.upgradeMessage = "New version of PPSSPP available!";1518g_Config.upgradeVersion = upgrade.ToString();1519g_Config.dismissedVersion.clear();1520}1521}15221523void Config::DismissUpgrade() {1524g_Config.dismissedVersion = g_Config.upgradeVersion;1525}15261527void Config::AddRecent(const std::string &file) {1528// Don't bother with this if the user disabled recents (it's -1).1529if (iMaxRecent <= 0)1530return;15311532// We'll add it back below. This makes sure it's at the front, and only once.1533RemoveRecent(file);15341535private_->ResetRecentIsosThread();1536std::lock_guard<std::mutex> guard(private_->recentIsosLock);1537const std::string filename = File::ResolvePath(file);1538recentIsos.insert(recentIsos.begin(), filename);1539if ((int)recentIsos.size() > iMaxRecent)1540recentIsos.resize(iMaxRecent);1541}15421543void Config::RemoveRecent(const std::string &file) {1544// Don't bother with this if the user disabled recents (it's -1).1545if (iMaxRecent <= 0)1546return;15471548private_->ResetRecentIsosThread();1549std::lock_guard<std::mutex> guard(private_->recentIsosLock);15501551const std::string filename = File::ResolvePath(file);1552auto iter = std::remove_if(recentIsos.begin(), recentIsos.end(), [filename](const auto &str) {1553const std::string recent = File::ResolvePath(str);1554return filename == recent;1555});1556// remove_if is weird.1557recentIsos.erase(iter, recentIsos.end());1558}15591560// On iOS, the path to the app documents directory changes on each launch.1561// Example path:1562// /var/mobile/Containers/Data/Application/0E0E89DE-8D8E-485A-860C-700D8BC87B86/Documents/PSP/GAME/SuicideBarbie1563// The GUID part changes on each launch.1564static bool TryUpdateSavedPath(Path *path) {1565#if PPSSPP_PLATFORM(IOS)1566INFO_LOG(Log::Loader, "Original path: %s", path->c_str());1567std::string pathStr = path->ToString();15681569const std::string_view applicationRoot = "/var/mobile/Containers/Data/Application/";1570if (startsWith(pathStr, applicationRoot)) {1571size_t documentsPos = pathStr.find("/Documents/");1572if (documentsPos == std::string::npos) {1573return false;1574}1575std::string memstick = g_Config.memStickDirectory.ToString();1576size_t memstickDocumentsPos = memstick.find("/Documents"); // Note: No trailing slash, or we won't find it.1577*path = Path(memstick.substr(0, memstickDocumentsPos) + pathStr.substr(documentsPos));1578return true;1579} else {1580// Path can't be auto-updated.1581return false;1582}1583#else1584return false;1585#endif1586}15871588void Config::CleanRecent() {1589private_->SetRecentIsosThread([this] {1590SetCurrentThreadName("RecentISOs");15911592AndroidJNIThreadContext jniContext; // destructor detaches15931594double startTime = time_now_d();15951596std::lock_guard<std::mutex> guard(private_->recentIsosLock);1597std::vector<std::string> cleanedRecent;1598if (recentIsos.empty()) {1599INFO_LOG(Log::Loader, "No recents list found.");1600}16011602for (size_t i = 0; i < recentIsos.size(); i++) {1603bool exists = false;1604Path path = Path(recentIsos[i]);1605switch (path.Type()) {1606case PathType::CONTENT_URI:1607case PathType::NATIVE:1608exists = File::Exists(path);1609if (!exists) {1610if (TryUpdateSavedPath(&path)) {1611exists = File::Exists(path);1612INFO_LOG(Log::Loader, "Exists=%d when checking updated path: %s", exists, path.c_str());1613}1614}1615break;1616default:1617FileLoader *loader = ConstructFileLoader(path);1618exists = loader->ExistsFast();1619delete loader;1620break;1621}16221623if (exists) {1624std::string pathStr = path.ToString();1625// Make sure we don't have any redundant items.1626auto duplicate = std::find(cleanedRecent.begin(), cleanedRecent.end(), pathStr);1627if (duplicate == cleanedRecent.end()) {1628cleanedRecent.push_back(pathStr);1629}1630} else {1631DEBUG_LOG(Log::Loader, "Removed %s from recent. errno=%d", path.c_str(), errno);1632}1633}16341635double recentTime = time_now_d() - startTime;1636if (recentTime > 0.1) {1637INFO_LOG(Log::System, "CleanRecent took %0.2f", recentTime);1638}1639recentIsos = cleanedRecent;1640});1641}16421643std::vector<std::string> Config::RecentIsos() const {1644std::lock_guard<std::mutex> guard(private_->recentIsosLock);1645return recentIsos;1646}16471648bool Config::HasRecentIsos() const {1649std::lock_guard<std::mutex> guard(private_->recentIsosLock);1650return !recentIsos.empty();1651}16521653void Config::ClearRecentIsos() {1654private_->ResetRecentIsosThread();1655std::lock_guard<std::mutex> guard(private_->recentIsosLock);1656recentIsos.clear();1657}16581659void Config::SetSearchPath(const Path &searchPath) {1660searchPath_ = searchPath;1661}16621663const Path Config::FindConfigFile(const std::string &baseFilename) {1664// Don't search for an absolute path.1665if (baseFilename.size() > 1 && baseFilename[0] == '/') {1666return Path(baseFilename);1667}1668#ifdef _WIN321669if (baseFilename.size() > 3 && baseFilename[1] == ':' && (baseFilename[2] == '/' || baseFilename[2] == '\\')) {1670return Path(baseFilename);1671}1672#endif16731674Path filename = searchPath_ / baseFilename;1675if (File::Exists(filename)) {1676return filename;1677}16781679// Make sure at least the directory it's supposed to be in exists.1680Path path = filename.NavigateUp();1681// This check is just to avoid logging.1682if (!File::Exists(path)) {1683File::CreateFullPath(path);1684}1685return filename;1686}16871688void Config::RestoreDefaults(RestoreSettingsBits whatToRestore) {1689if (bGameSpecific) {1690// TODO: This should be possible to do in a cleaner way.1691deleteGameConfig(gameId_);1692createGameConfig(gameId_);1693Load();1694} else {1695if (whatToRestore & RestoreSettingsBits::SETTINGS) {1696IterateSettings([](const ConfigSetting &setting) {1697setting.RestoreToDefault();1698});1699}17001701if (whatToRestore & RestoreSettingsBits::CONTROLS) {1702KeyMap::RestoreDefault();1703}17041705if (whatToRestore & RestoreSettingsBits::RECENT) {1706ClearRecentIsos();1707currentDirectory = defaultCurrentDirectory;1708}1709}1710}17111712bool Config::hasGameConfig(const std::string &pGameId) {1713Path fullIniFilePath = getGameConfigFile(pGameId);1714return File::Exists(fullIniFilePath);1715}17161717void Config::changeGameSpecific(const std::string &pGameId, const std::string &title) {1718if (!reload_)1719Save("changeGameSpecific");1720gameId_ = pGameId;1721gameIdTitle_ = title;1722bGameSpecific = !pGameId.empty();1723}17241725bool Config::createGameConfig(const std::string &pGameId) {1726Path fullIniFilePath = getGameConfigFile(pGameId);17271728if (hasGameConfig(pGameId)) {1729return false;1730}17311732File::CreateEmptyFile(fullIniFilePath);1733return true;1734}17351736bool Config::deleteGameConfig(const std::string& pGameId) {1737Path fullIniFilePath = Path(getGameConfigFile(pGameId));17381739File::Delete(fullIniFilePath);1740return true;1741}17421743Path Config::getGameConfigFile(const std::string &pGameId) {1744const char *ppssppIniFilename = IsVREnabled() ? "_ppssppvr.ini" : "_ppsspp.ini";1745std::string iniFileName = pGameId + ppssppIniFilename;1746Path iniFileNameFull = FindConfigFile(iniFileName);17471748return iniFileNameFull;1749}17501751bool Config::saveGameConfig(const std::string &pGameId, const std::string &title) {1752if (pGameId.empty()) {1753return false;1754}17551756Path fullIniFilePath = getGameConfigFile(pGameId);17571758IniFile iniFile;17591760Section *top = iniFile.GetOrCreateSection("");1761top->AddComment(StringFromFormat("Game config for %s - %s", pGameId.c_str(), title.c_str()));17621763PreSaveCleanup(true);17641765IterateSettings(iniFile, [](Section *section, const ConfigSetting &setting) {1766if (setting.PerGame()) {1767setting.Set(section);1768}1769});17701771Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting");1772postShaderSetting->Clear();1773for (const auto &[k, v] : mPostShaderSetting) {1774postShaderSetting->Set(k.c_str(), v);1775}17761777Section *postShaderChain = iniFile.GetOrCreateSection("PostShaderList");1778postShaderChain->Clear();1779for (size_t i = 0; i < vPostShaderNames.size(); ++i) {1780char keyName[64];1781snprintf(keyName, sizeof(keyName), "PostShader%d", (int)i+1);1782postShaderChain->Set(keyName, vPostShaderNames[i]);1783}17841785KeyMap::SaveToIni(iniFile);1786iniFile.Save(fullIniFilePath);17871788PostSaveCleanup(true);1789return true;1790}17911792bool Config::loadGameConfig(const std::string &pGameId, const std::string &title) {1793Path iniFileNameFull = getGameConfigFile(pGameId);17941795if (!hasGameConfig(pGameId)) {1796DEBUG_LOG(Log::Loader, "No game-specific settings found in %s. Using global defaults.", iniFileNameFull.c_str());1797return false;1798}17991800changeGameSpecific(pGameId, title);1801IniFile iniFile;1802iniFile.Load(iniFileNameFull);18031804auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap();1805mPostShaderSetting.clear();1806for (const auto &[k, v] : postShaderSetting) {1807float value = 0.0f;1808if (sscanf(v.c_str(), "%f", &value)) {1809mPostShaderSetting[k] = value;1810} else {1811WARN_LOG(Log::Loader, "Invalid float value string for param %s: '%s'", k.c_str(), v.c_str());1812}1813}18141815auto postShaderChain = iniFile.GetOrCreateSection("PostShaderList")->ToMap();1816vPostShaderNames.clear();1817for (const auto &[_, v] : postShaderChain) {1818if (v != "Off")1819vPostShaderNames.push_back(v);1820}18211822IterateSettings(iniFile, [](Section *section, const ConfigSetting &setting) {1823if (setting.PerGame()) {1824setting.Get(section);1825}1826});18271828KeyMap::LoadFromIni(iniFile);18291830if (!appendedConfigFileName_.ToString().empty() &&1831std::find(appendedConfigUpdatedGames_.begin(), appendedConfigUpdatedGames_.end(), pGameId) == appendedConfigUpdatedGames_.end()) {18321833LoadAppendedConfig();1834appendedConfigUpdatedGames_.push_back(pGameId);1835}18361837PostLoadCleanup(true);1838return true;1839}18401841void Config::unloadGameConfig() {1842if (bGameSpecific) {1843changeGameSpecific();18441845IniFile iniFile;1846iniFile.Load(iniFilename_);18471848// Reload game specific settings back to standard.1849IterateSettings(iniFile, [](Section *section, const ConfigSetting &setting) {1850if (setting.PerGame()) {1851setting.Get(section);1852}1853});18541855auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap();1856mPostShaderSetting.clear();1857for (const auto &[k, v] : postShaderSetting) {1858mPostShaderSetting[k] = std::stof(v);1859}18601861auto postShaderChain = iniFile.GetOrCreateSection("PostShaderList")->ToMap();1862vPostShaderNames.clear();1863for (const auto &[k, v] : postShaderChain) {1864if (v != "Off")1865vPostShaderNames.push_back(v);1866}18671868LoadStandardControllerIni();1869PostLoadCleanup(true);1870}1871}18721873void Config::LoadStandardControllerIni() {1874IniFile controllerIniFile;1875if (!controllerIniFile.Load(controllerIniFilename_)) {1876ERROR_LOG(Log::Loader, "Failed to read %s. Setting controller config to default.", controllerIniFilename_.c_str());1877KeyMap::RestoreDefault();1878} else {1879// Continue anyway to initialize the config. It will just restore the defaults.1880KeyMap::LoadFromIni(controllerIniFile);1881}1882}18831884void Config::ResetControlLayout() {1885auto reset = [](ConfigTouchPos &pos) {1886pos.x = defaultTouchPosShow.x;1887pos.y = defaultTouchPosShow.y;1888pos.scale = defaultTouchPosShow.scale;1889};1890reset(g_Config.touchActionButtonCenter);1891g_Config.fActionButtonSpacing = 1.0f;1892reset(g_Config.touchDpad);1893g_Config.fDpadSpacing = 1.0f;1894reset(g_Config.touchStartKey);1895reset(g_Config.touchSelectKey);1896reset(g_Config.touchFastForwardKey);1897reset(g_Config.touchLKey);1898reset(g_Config.touchRKey);1899reset(g_Config.touchAnalogStick);1900reset(g_Config.touchRightAnalogStick);1901for (int i = 0; i < CUSTOM_BUTTON_COUNT; i++) {1902reset(g_Config.touchCustom[i]);1903}1904g_Config.fLeftStickHeadScale = 1.0f;1905g_Config.fRightStickHeadScale = 1.0f;1906}19071908void Config::GetReportingInfo(UrlEncoder &data) {1909for (size_t i = 0; i < numSections; ++i) {1910const std::string prefix = std::string("config.") + sections[i].section;1911for (size_t j = 0; j < sections[i].settingsCount; j++) {1912sections[i].settings[j].ReportSetting(data, prefix);1913}1914}1915}19161917bool Config::IsPortrait() const {1918return (iInternalScreenRotation == ROTATION_LOCKED_VERTICAL || iInternalScreenRotation == ROTATION_LOCKED_VERTICAL180) && !bSkipBufferEffects;1919}19201921int Config::GetPSPLanguage() {1922if (g_Config.iLanguage == -1) {1923const auto &langValuesMapping = GetLangValuesMapping();1924auto iter = langValuesMapping.find(g_Config.sLanguageIni);1925if (iter != langValuesMapping.end()) {1926return iter->second.second;1927} else {1928// Fallback to English1929return PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;1930}1931} else {1932return g_Config.iLanguage;1933}1934}19351936void PlayTimeTracker::Start(const std::string &gameId) {1937if (gameId.empty()) {1938return;1939}1940INFO_LOG(Log::System, "GameTimeTracker::Start(%s)", gameId.c_str());19411942auto iter = tracker_.find(std::string(gameId));1943if (iter != tracker_.end()) {1944if (iter->second.startTime == 0.0) {1945iter->second.lastTimePlayed = time_now_unix_utc();1946iter->second.startTime = time_now_d();1947}1948return;1949}19501951PlayTime playTime;1952playTime.lastTimePlayed = time_now_unix_utc();1953playTime.totalTimePlayed = 0.0;1954playTime.startTime = time_now_d();1955tracker_[gameId] = playTime;1956}19571958void PlayTimeTracker::Stop(const std::string &gameId) {1959if (gameId.empty()) {1960return;1961}19621963INFO_LOG(Log::System, "GameTimeTracker::Stop(%s)", gameId.c_str());19641965auto iter = tracker_.find(std::string(gameId));1966if (iter != tracker_.end()) {1967if (iter->second.startTime != 0.0) {1968iter->second.totalTimePlayed += time_now_d() - iter->second.startTime;1969iter->second.startTime = 0.0;1970}1971iter->second.lastTimePlayed = time_now_unix_utc();1972return;1973}19741975// Shouldn't happen, ignore this case.1976WARN_LOG(Log::System, "GameTimeTracker::Stop called without corresponding GameTimeTracker::Start");1977}19781979void PlayTimeTracker::Load(const Section *section) {1980tracker_.clear();19811982auto map = section->ToMap();19831984for (const auto &iter : map) {1985const std::string &value = iter.second;1986// Parse the string.1987PlayTime gameTime{};1988if (2 == sscanf(value.c_str(), "%d,%llu", &gameTime.totalTimePlayed, (long long *)&gameTime.lastTimePlayed)) {1989tracker_[iter.first.c_str()] = gameTime;1990}1991}1992}19931994void PlayTimeTracker::Save(Section *section) {1995for (auto iter : tracker_) {1996std::string formatted = StringFromFormat("%d,%llu", iter.second.totalTimePlayed, iter.second.lastTimePlayed);1997section->Set(iter.first.c_str(), formatted);1998}1999}20002001bool PlayTimeTracker::GetPlayedTimeString(const std::string &gameId, std::string *str) const {2002auto ga = GetI18NCategory(I18NCat::GAME);20032004auto iter = tracker_.find(gameId);2005if (iter == tracker_.end()) {2006return false;2007}20082009int totalSeconds = iter->second.totalTimePlayed;2010int seconds = totalSeconds % 60;2011totalSeconds /= 60;2012int minutes = totalSeconds % 60;2013totalSeconds /= 60;2014int hours = totalSeconds;20152016*str = ApplySafeSubstitutions(ga->T("Time Played: %1h %2m %3s"), hours, minutes, seconds);2017return true;2018}201920202021