1 // Copyright (c) 2012- PPSSPP Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
22 #include "base/display.h"
23 #include "base/NativeApp.h"
24 #include "ext/vjson/json.h"
25 #include "file/ini_file.h"
26 #include "i18n/i18n.h"
27 #include "gfx_es2/gpu_features.h"
28 #include "net/http_client.h"
29 #include "util/text/parsers.h"
32 #include "Common/CPUDetect.h"
33 #include "Common/KeyMap.h"
34 #include "Common/FileUtil.h"
35 #include "Common/StringUtils.h"
36 #include "Core/Config.h"
37 #include "Core/Loaders.h"
38 #include "GPU/Common/FramebufferCommon.h"
39 #include "HLE/sceUtility.h"
42 extern const char *PPSSPP_GIT_VERSION
;
45 // TODO: Find a better place for this.
46 http::Downloader g_DownloadManager
;
51 extern bool iosCanUseJit
;
54 struct ConfigSetting
{
75 typedef bool (*BoolDefaultCallback
)();
76 typedef int (*IntDefaultCallback
)();
77 typedef float (*FloatDefaultCallback
)();
78 typedef const char *(*StringDefaultCallback
)();
81 BoolDefaultCallback b
;
83 FloatDefaultCallback f
;
84 StringDefaultCallback s
;
88 : ini_(""), type_(TYPE_TERMINATOR
), report_(false), save_(false), perGame_(false) {
93 ConfigSetting(const char *ini
, bool *v
, bool def
, bool save
= true, bool perGame
= false)
94 : ini_(ini
), type_(TYPE_BOOL
), report_(false), save_(save
), perGame_(perGame
) {
100 ConfigSetting(const char *ini
, int *v
, int def
, bool save
= true, bool perGame
= false)
101 : ini_(ini
), type_(TYPE_INT
), report_(false), save_(save
), perGame_(perGame
) {
107 ConfigSetting(const char *ini
, float *v
, float def
, bool save
= true, bool perGame
= false)
108 : ini_(ini
), type_(TYPE_FLOAT
), report_(false), save_(save
), perGame_(perGame
) {
114 ConfigSetting(const char *ini
, std::string
*v
, const char *def
, bool save
= true, bool perGame
= false)
115 : ini_(ini
), type_(TYPE_STRING
), report_(false), save_(save
), perGame_(perGame
) {
121 ConfigSetting(const char *ini
, bool *v
, BoolDefaultCallback def
, bool save
= true, bool perGame
= false)
122 : ini_(ini
), type_(TYPE_BOOL
), report_(false), save_(save
), perGame_(perGame
) {
127 ConfigSetting(const char *ini
, int *v
, IntDefaultCallback def
, bool save
= true, bool perGame
= false)
128 : ini_(ini
), type_(TYPE_INT
), report_(false), save_(save
), perGame_(perGame
) {
133 ConfigSetting(const char *ini
, float *v
, FloatDefaultCallback def
, bool save
= true, bool perGame
= false)
134 : ini_(ini
), type_(TYPE_FLOAT
), report_(false), save_(save
), perGame_(perGame
) {
139 ConfigSetting(const char *ini
, std::string
*v
, StringDefaultCallback def
, bool save
= true, bool perGame
= false)
140 : ini_(ini
), type_(TYPE_STRING
), report_(false), save_(save
), perGame_(perGame
) {
145 bool HasMore() const {
146 return type_
!= TYPE_TERMINATOR
;
149 bool Get(IniFile::Section
*section
) {
153 default_
.b
= cb_
.b();
155 return section
->Get(ini_
, ptr_
.b
, default_
.b
);
158 default_
.i
= cb_
.i();
160 return section
->Get(ini_
, ptr_
.i
, default_
.i
);
163 default_
.f
= cb_
.f();
165 return section
->Get(ini_
, ptr_
.f
, default_
.f
);
168 default_
.s
= cb_
.s();
170 return section
->Get(ini_
, ptr_
.s
, default_
.s
);
172 _dbg_assert_msg_(LOADER
, false, "Unexpected ini setting type");
177 void Set(IniFile::Section
*section
) {
183 return section
->Set(ini_
, *ptr_
.b
);
185 return section
->Set(ini_
, *ptr_
.i
);
187 return section
->Set(ini_
, *ptr_
.f
);
189 return section
->Set(ini_
, *ptr_
.s
);
191 _dbg_assert_msg_(LOADER
, false, "Unexpected ini setting type");
196 void Report(UrlEncoder
&data
, const std::string
&prefix
) {
202 return data
.Add(prefix
+ ini_
, *ptr_
.b
);
204 return data
.Add(prefix
+ ini_
, *ptr_
.i
);
206 return data
.Add(prefix
+ ini_
, *ptr_
.f
);
208 return data
.Add(prefix
+ ini_
, *ptr_
.s
);
210 _dbg_assert_msg_(LOADER
, false, "Unexpected ini setting type");
225 struct ReportedConfigSetting
: public ConfigSetting
{
226 template <typename T1
, typename T2
>
227 ReportedConfigSetting(const char *ini
, T1
*v
, T2 def
, bool save
= true, bool perGame
= false)
228 : ConfigSetting(ini
, v
, def
, save
, perGame
) {
233 const char *DefaultLangRegion() {
234 static std::string defaultLangRegion
= "en_US";
235 if (g_Config
.bFirstRun
) {
236 std::string langRegion
= System_GetProperty(SYSPROP_LANGREGION
);
237 if (i18nrepo
.IniExists(langRegion
))
238 defaultLangRegion
= langRegion
;
241 return defaultLangRegion
.c_str();
244 const char *CreateRandMAC() {
245 std::stringstream randStream
;
246 srand(time(nullptr));
247 for (int i
= 0; i
< 6; i
++) {
248 u32 value
= rand() % 256;
250 randStream
<< '0' << std::hex
<< value
;
252 randStream
<< std::hex
<< value
;
254 randStream
<< ':'; //we need a : between every octet
257 // It's ok to strdup, this runs once and will be freed by exiting the process anyway
258 return strdup(randStream
.str().c_str());
261 static int DefaultNumWorkers() {
262 return cpu_info
.num_cores
;
265 static bool DefaultJit() {
268 #elif defined(ARM) || defined(ARM64) || defined(_M_IX86) || defined(_M_X64)
275 struct ConfigSectionSettings
{
277 ConfigSetting
*settings
;
280 static ConfigSetting generalSettings
[] = {
281 ConfigSetting("FirstRun", &g_Config
.bFirstRun
, true),
282 ConfigSetting("RunCount", &g_Config
.iRunCount
, 0),
283 ConfigSetting("Enable Logging", &g_Config
.bEnableLogging
, true),
284 ConfigSetting("AutoRun", &g_Config
.bAutoRun
, true),
285 ConfigSetting("Browse", &g_Config
.bBrowse
, false),
286 ConfigSetting("IgnoreBadMemAccess", &g_Config
.bIgnoreBadMemAccess
, true, true),
287 ConfigSetting("CurrentDirectory", &g_Config
.currentDirectory
, ""),
288 ConfigSetting("ShowDebuggerOnLoad", &g_Config
.bShowDebuggerOnLoad
, false),
289 ConfigSetting("CheckForNewVersion", &g_Config
.bCheckForNewVersion
, true),
290 ConfigSetting("Language", &g_Config
.sLanguageIni
, &DefaultLangRegion
),
291 ConfigSetting("ForceLagSync", &g_Config
.bForceLagSync
, false, true, true),
293 ReportedConfigSetting("NumWorkerThreads", &g_Config
.iNumWorkerThreads
, &DefaultNumWorkers
, true, true),
294 ConfigSetting("EnableAutoLoad", &g_Config
.bEnableAutoLoad
, false, true, true),
295 ReportedConfigSetting("EnableCheats", &g_Config
.bEnableCheats
, false, true, true),
296 ConfigSetting("CwCheatRefreshRate", &g_Config
.iCwCheatRefreshRate
, 77, true, true),
298 ConfigSetting("ScreenshotsAsPNG", &g_Config
.bScreenshotsAsPNG
, false, true, true),
299 ConfigSetting("StateSlot", &g_Config
.iCurrentStateSlot
, 0, true, true),
300 ConfigSetting("RewindFlipFrequency", &g_Config
.iRewindFlipFrequency
, 0, true, true),
302 ConfigSetting("GridView1", &g_Config
.bGridView1
, true),
303 ConfigSetting("GridView2", &g_Config
.bGridView2
, true),
304 ConfigSetting("GridView3", &g_Config
.bGridView3
, false),
306 // "default" means let emulator decide, "" means disable.
307 ConfigSetting("ReportingHost", &g_Config
.sReportHost
, "default"),
308 ConfigSetting("AutoSaveSymbolMap", &g_Config
.bAutoSaveSymbolMap
, false, true, true),
309 ConfigSetting("CacheFullIsoInRam", &g_Config
.bCacheFullIsoInRam
, false, true, true),
312 ConfigSetting("ScreenRotation", &g_Config
.iScreenRotation
, 1),
314 ConfigSetting("InternalScreenRotation", &g_Config
.iInternalScreenRotation
, 1),
316 #if defined(USING_WIN_UI)
317 ConfigSetting("TopMost", &g_Config
.bTopMost
, false),
318 ConfigSetting("WindowX", &g_Config
.iWindowX
, -1), // -1 tells us to center the window.
319 ConfigSetting("WindowY", &g_Config
.iWindowY
, -1),
320 ConfigSetting("WindowWidth", &g_Config
.iWindowWidth
, 0), // 0 will be automatically reset later (need to do the AdjustWindowRect dance).
321 ConfigSetting("WindowHeight", &g_Config
.iWindowHeight
, 0),
322 ConfigSetting("PauseOnLostFocus", &g_Config
.bPauseOnLostFocus
, false, true, true),
324 ConfigSetting("PauseWhenMinimized", &g_Config
.bPauseWhenMinimized
, false, true, true),
325 ConfigSetting("DumpDecryptedEboots", &g_Config
.bDumpDecryptedEboot
, false, true, true),
326 ConfigSetting(false),
329 static bool DefaultForceFlushToZero() {
337 static ConfigSetting cpuSettings
[] = {
338 ReportedConfigSetting("Jit", &g_Config
.bJit
, &DefaultJit
, true, true),
339 ReportedConfigSetting("SeparateCPUThread", &g_Config
.bSeparateCPUThread
, false, true, true),
340 ReportedConfigSetting("SeparateIOThread", &g_Config
.bSeparateIOThread
, true, true, true),
341 ReportedConfigSetting("IOTimingMethod", &g_Config
.iIOTimingMethod
, IOTIMING_FAST
, true, true),
342 ConfigSetting("FastMemoryAccess", &g_Config
.bFastMemory
, true, true, true),
343 ReportedConfigSetting("FuncReplacements", &g_Config
.bFuncReplacements
, true, true, true),
344 ReportedConfigSetting("CPUSpeed", &g_Config
.iLockedCPUSpeed
, 0, true, true),
345 ReportedConfigSetting("SetRoundingMode", &g_Config
.bSetRoundingMode
, true, true, true),
346 ReportedConfigSetting("ForceFlushToZero", &g_Config
.bForceFlushToZero
, &DefaultForceFlushToZero
, true, true),
348 ConfigSetting(false),
351 static int DefaultRenderingMode() {
352 // Workaround for ancient device. Can probably be removed now as we do no longer
353 // support Froyo (Android 2.2)...
354 if (System_GetProperty(SYSPROP_NAME
) == "samsung:GT-S5360") {
355 return 0; // Non-buffered
360 static int DefaultInternalResolution() {
361 // Auto on Windows, 2x on large screens, 1x elsewhere.
362 #if defined(USING_WIN_UI)
365 int longestDisplaySide
= std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES
), System_GetPropertyInt(SYSPROP_DISPLAY_YRES
));
366 return longestDisplaySide
>= 1000 ? 2 : 1;
370 static bool DefaultPartialStretch() {
372 return pixel_xres
< 1.3 * pixel_yres
;
378 static bool DefaultTimerHack() {
379 // Has been in use on Symbian since v0.7. Preferred option.
387 static int DefaultAndroidHwScale() {
389 // Get the real resolution as passed in during startup, not dp_xres and stuff
390 int xres
= System_GetPropertyInt(SYSPROP_DISPLAY_XRES
);
391 int yres
= System_GetPropertyInt(SYSPROP_DISPLAY_YRES
);
394 // Smaller than the PSP*2, let's go native.
396 } else if (xres
<= 480 * 3) { // 720p xres
397 // Small-ish screen, we should default to 2x
400 // Large or very large screen. Default to 3x psp resolution.
409 static ConfigSetting graphicsSettings
[] = {
410 ConfigSetting("EnableCardboard", &g_Config
.bEnableCardboard
, false, true, true),
411 ConfigSetting("CardboardScreenSize", &g_Config
.iCardboardScreenSize
, 50, true, true),
412 ConfigSetting("CardboardXShift", &g_Config
.iCardboardXShift
, 0, true, true),
413 ConfigSetting("CardboardYShift", &g_Config
.iCardboardXShift
, 0, true, true),
414 ConfigSetting("ShowFPSCounter", &g_Config
.iShowFPSCounter
, 0, true, true),
415 ReportedConfigSetting("GPUBackend", &g_Config
.iGPUBackend
, 0),
416 ReportedConfigSetting("RenderingMode", &g_Config
.iRenderingMode
, &DefaultRenderingMode
, true, true),
417 ConfigSetting("SoftwareRendering", &g_Config
.bSoftwareRendering
, false, true, true),
418 ReportedConfigSetting("HardwareTransform", &g_Config
.bHardwareTransform
, true, true, true),
419 ReportedConfigSetting("SoftwareSkinning", &g_Config
.bSoftwareSkinning
, true, true, true),
420 ReportedConfigSetting("TextureFiltering", &g_Config
.iTexFiltering
, 1, true, true),
421 ReportedConfigSetting("BufferFiltering", &g_Config
.iBufFilter
, 1, true, true),
422 ReportedConfigSetting("InternalResolution", &g_Config
.iInternalResolution
, &DefaultInternalResolution
, true, true),
423 ReportedConfigSetting("AndroidHwScale", &g_Config
.iAndroidHwScale
, &DefaultAndroidHwScale
),
424 ReportedConfigSetting("FrameSkip", &g_Config
.iFrameSkip
, 0, true, true),
425 ReportedConfigSetting("AutoFrameSkip", &g_Config
.bAutoFrameSkip
, false, true, true),
426 ReportedConfigSetting("FrameRate", &g_Config
.iFpsLimit
, 0, true, true),
427 #if defined(_WIN32) && !defined(__LIBRETRO__)
428 ConfigSetting("FrameSkipUnthrottle", &g_Config
.bFrameSkipUnthrottle
, false, true, true),
429 ConfigSetting("TemporaryGPUBackend", &g_Config
.iTempGPUBackend
, -1, false),
430 ConfigSetting("RestartRequired", &g_Config
.bRestartRequired
, false, false),
432 ConfigSetting("FrameSkipUnthrottle", &g_Config
.bFrameSkipUnthrottle
, true),
434 ReportedConfigSetting("ForceMaxEmulatedFPS", &g_Config
.iForceMaxEmulatedFPS
, 60, true, true),
436 ConfigSetting("AnisotropyLevel", &g_Config
.iAnisotropyLevel
, 0, true, true),
438 ConfigSetting("AnisotropyLevel", &g_Config
.iAnisotropyLevel
, 8, true, true),
440 ReportedConfigSetting("VertexCache", &g_Config
.bVertexCache
, true, true, true),
441 ReportedConfigSetting("TextureBackoffCache", &g_Config
.bTextureBackoffCache
, false, true, true),
442 ReportedConfigSetting("TextureSecondaryCache", &g_Config
.bTextureSecondaryCache
, false, true, true),
443 ReportedConfigSetting("VertexDecJit", &g_Config
.bVertexDecoderJit
, &DefaultJit
, false),
446 ConfigSetting("FullScreen", &g_Config
.bFullScreen
, false),
449 // TODO: Replace these settings with a list of options
450 ConfigSetting("PartialStretch", &g_Config
.bPartialStretch
, &DefaultPartialStretch
, true, true),
451 ConfigSetting("StretchToDisplay", &g_Config
.bStretchToDisplay
, false, true, true),
452 ConfigSetting("SmallDisplay", &g_Config
.bSmallDisplay
, false, true, true),
453 ConfigSetting("ImmersiveMode", &g_Config
.bImmersiveMode
, false, true, true),
455 ReportedConfigSetting("TrueColor", &g_Config
.bTrueColor
, true, true, true),
457 ReportedConfigSetting("MipMap", &g_Config
.bMipMap
, true, true, true),
459 ReportedConfigSetting("TexScalingLevel", &g_Config
.iTexScalingLevel
, 1, true, true),
460 ReportedConfigSetting("TexScalingType", &g_Config
.iTexScalingType
, 0, true, true),
461 ReportedConfigSetting("TexDeposterize", &g_Config
.bTexDeposterize
, false, true, true),
462 ConfigSetting("VSyncInterval", &g_Config
.bVSync
, false, true, true),
463 ReportedConfigSetting("DisableStencilTest", &g_Config
.bDisableStencilTest
, false, true, true),
464 ReportedConfigSetting("AlwaysDepthWrite", &g_Config
.bAlwaysDepthWrite
, false, true, true),
465 ReportedConfigSetting("DepthRangeHack", &g_Config
.bDepthRangeHack
, false, true, true),
466 ReportedConfigSetting("BloomHack", &g_Config
.iBloomHack
, 0, true, true),
468 // Not really a graphics setting...
469 ReportedConfigSetting("TimerHack", &g_Config
.bTimerHack
, &DefaultTimerHack
, true, true),
470 ReportedConfigSetting("AlphaMaskHack", &g_Config
.bAlphaMaskHack
, false, true, true),
471 ReportedConfigSetting("SplineBezierQuality", &g_Config
.iSplineBezierQuality
, 2, true, true),
472 ReportedConfigSetting("PostShader", &g_Config
.sPostShaderName
, "Off", true, true),
474 ReportedConfigSetting("MemBlockTransferGPU", &g_Config
.bBlockTransferGPU
, true, true, true),
475 ReportedConfigSetting("DisableSlowFramebufEffects", &g_Config
.bDisableSlowFramebufEffects
, false, true, true),
476 ReportedConfigSetting("FragmentTestCache", &g_Config
.bFragmentTestCache
, true, true, true),
478 ConfigSetting(false),
481 static ConfigSetting soundSettings
[] = {
482 ConfigSetting("Enable", &g_Config
.bEnableSound
, true, true, true),
483 ConfigSetting("AudioBackend", &g_Config
.iAudioBackend
, 0, true, true),
484 ConfigSetting("AudioLatency", &g_Config
.iAudioLatency
, 1, true, true),
485 ConfigSetting("SoundSpeedHack", &g_Config
.bSoundSpeedHack
, false, true, true),
486 ConfigSetting("AudioResampler", &g_Config
.bAudioResampler
, true, true, true),
488 ConfigSetting(false),
491 static bool DefaultShowTouchControls() {
492 #if defined(MOBILE_DEVICE)
493 int deviceType
= System_GetPropertyInt(SYSPROP_DEVICE_TYPE
);
494 if (deviceType
== DEVICE_TYPE_MOBILE
) {
495 std::string name
= System_GetProperty(SYSPROP_NAME
);
496 if (KeyMap::HasBuiltinController(name
)) {
501 } else if (deviceType
== DEVICE_TYPE_TV
) {
503 } else if (deviceType
== DEVICE_TYPE_DESKTOP
) {
513 static const float defaultControlScale
= 1.15f
;
515 static ConfigSetting controlSettings
[] = {
516 ConfigSetting("HapticFeedback", &g_Config
.bHapticFeedback
, true, true, true),
517 ConfigSetting("ShowTouchCross", &g_Config
.bShowTouchCross
, true, true, true),
518 ConfigSetting("ShowTouchCircle", &g_Config
.bShowTouchCircle
, true, true, true),
519 ConfigSetting("ShowTouchSquare", &g_Config
.bShowTouchSquare
, true, true, true),
520 ConfigSetting("ShowTouchTriangle", &g_Config
.bShowTouchTriangle
, true, true, true),
521 ConfigSetting("ShowTouchStart", &g_Config
.bShowTouchStart
, true, true, true),
522 ConfigSetting("ShowTouchSelect", &g_Config
.bShowTouchSelect
, true, true, true),
523 ConfigSetting("ShowTouchLTrigger", &g_Config
.bShowTouchLTrigger
, true, true, true),
524 ConfigSetting("ShowTouchRTrigger", &g_Config
.bShowTouchRTrigger
, true, true, true),
525 ConfigSetting("ShowAnalogStick", &g_Config
.bShowTouchAnalogStick
, true, true, true),
526 ConfigSetting("ShowTouchDpad", &g_Config
.bShowTouchDpad
, true, true, true),
527 ConfigSetting("ShowTouchUnthrottle", &g_Config
.bShowTouchUnthrottle
, true, true, true),
528 #if !defined(__SYMBIAN32__) && !defined(IOS) && !defined(MAEMO)
530 // A win32 user seeing touch controls is likely using PPSSPP on a tablet. There it makes
531 // sense to default this to on.
532 ConfigSetting("ShowTouchPause", &g_Config
.bShowTouchPause
, true, true, true),
534 ConfigSetting("ShowTouchPause", &g_Config
.bShowTouchPause
, false, true, true),
537 #if defined(USING_WIN_UI)
538 ConfigSetting("IgnoreWindowsKey", &g_Config
.bIgnoreWindowsKey
, false, true, true),
540 ConfigSetting("ShowTouchControls", &g_Config
.bShowTouchControls
, &DefaultShowTouchControls
, true, true),
541 // ConfigSetting("KeyMapping", &g_Config.iMappingMap, 0),
544 ConfigSetting("TiltBaseX", &g_Config
.fTiltBaseX
, 0.0f
, true, true),
545 ConfigSetting("TiltBaseY", &g_Config
.fTiltBaseY
, 0.0f
, true, true),
546 ConfigSetting("InvertTiltX", &g_Config
.bInvertTiltX
, false, true, true),
547 ConfigSetting("InvertTiltY", &g_Config
.bInvertTiltY
, true, true, true),
548 ConfigSetting("TiltSensitivityX", &g_Config
.iTiltSensitivityX
, 100, true, true),
549 ConfigSetting("TiltSensitivityY", &g_Config
.iTiltSensitivityY
, 100, true, true),
550 ConfigSetting("DeadzoneRadius", &g_Config
.fDeadzoneRadius
, 0.2f
, true, true),
551 ConfigSetting("TiltInputType", &g_Config
.iTiltInputType
, 0, true, true),
554 ConfigSetting("DisableDpadDiagonals", &g_Config
.bDisableDpadDiagonals
, false, true, true),
555 ConfigSetting("GamepadOnlyFocused", &g_Config
.bGamepadOnlyFocused
, false, true, true),
556 ConfigSetting("TouchButtonStyle", &g_Config
.iTouchButtonStyle
, 1, true, true),
557 ConfigSetting("TouchButtonOpacity", &g_Config
.iTouchButtonOpacity
, 65, true, true),
558 ConfigSetting("AutoCenterTouchAnalog", &g_Config
.bAutoCenterTouchAnalog
, false, true, true),
560 // -1.0f means uninitialized, set in GamepadEmu::CreatePadLayout().
561 ConfigSetting("ActionButtonSpacing2", &g_Config
.fActionButtonSpacing
, 1.0f
, true, true),
562 ConfigSetting("ActionButtonCenterX", &g_Config
.fActionButtonCenterX
, -1.0f
, true, true),
563 ConfigSetting("ActionButtonCenterY", &g_Config
.fActionButtonCenterY
, -1.0f
, true, true),
564 ConfigSetting("ActionButtonScale", &g_Config
.fActionButtonScale
, defaultControlScale
, true, true),
565 ConfigSetting("DPadX", &g_Config
.fDpadX
, -1.0f
, true, true),
566 ConfigSetting("DPadY", &g_Config
.fDpadY
, -1.0f
, true, true),
568 // Note: these will be overwritten if DPadRadius is set.
569 ConfigSetting("DPadScale", &g_Config
.fDpadScale
, defaultControlScale
, true, true),
570 ConfigSetting("DPadSpacing", &g_Config
.fDpadSpacing
, 1.0f
, true, true),
571 ConfigSetting("StartKeyX", &g_Config
.fStartKeyX
, -1.0f
, true, true),
572 ConfigSetting("StartKeyY", &g_Config
.fStartKeyY
, -1.0f
, true, true),
573 ConfigSetting("StartKeyScale", &g_Config
.fStartKeyScale
, defaultControlScale
, true, true),
574 ConfigSetting("SelectKeyX", &g_Config
.fSelectKeyX
, -1.0f
, true, true),
575 ConfigSetting("SelectKeyY", &g_Config
.fSelectKeyY
, -1.0f
, true, true),
576 ConfigSetting("SelectKeyScale", &g_Config
.fSelectKeyScale
, defaultControlScale
, true, true),
577 ConfigSetting("UnthrottleKeyX", &g_Config
.fUnthrottleKeyX
, -1.0f
, true, true),
578 ConfigSetting("UnthrottleKeyY", &g_Config
.fUnthrottleKeyY
, -1.0f
, true, true),
579 ConfigSetting("UnthrottleKeyScale", &g_Config
.fUnthrottleKeyScale
, defaultControlScale
, true, true),
580 ConfigSetting("LKeyX", &g_Config
.fLKeyX
, -1.0f
, true, true),
581 ConfigSetting("LKeyY", &g_Config
.fLKeyY
, -1.0f
, true, true),
582 ConfigSetting("LKeyScale", &g_Config
.fLKeyScale
, defaultControlScale
, true, true),
583 ConfigSetting("RKeyX", &g_Config
.fRKeyX
, -1.0f
, true, true),
584 ConfigSetting("RKeyY", &g_Config
.fRKeyY
, -1.0f
, true, true),
585 ConfigSetting("RKeyScale", &g_Config
.fRKeyScale
, defaultControlScale
, true, true),
586 ConfigSetting("AnalogStickX", &g_Config
.fAnalogStickX
, -1.0f
, true, true),
587 ConfigSetting("AnalogStickY", &g_Config
.fAnalogStickY
, -1.0f
, true, true),
588 ConfigSetting("AnalogStickScale", &g_Config
.fAnalogStickScale
, defaultControlScale
, true, true),
590 ConfigSetting("DInputAnalogDeadzone", &g_Config
.fDInputAnalogDeadzone
, 0.1f
, true, true),
591 ConfigSetting("DInputAnalogInverseMode", &g_Config
.iDInputAnalogInverseMode
, 0, true, true),
592 ConfigSetting("DInputAnalogInverseDeadzone", &g_Config
.fDInputAnalogInverseDeadzone
, 0.0f
, true, true),
593 ConfigSetting("DInputAnalogSensitivity", &g_Config
.fDInputAnalogSensitivity
, 1.0f
, true, true),
595 ConfigSetting("XInputAnalogDeadzone", &g_Config
.fXInputAnalogDeadzone
, 0.24f
, true, true),
596 ConfigSetting("XInputAnalogInverseMode", &g_Config
.iXInputAnalogInverseMode
, 0, true, true),
597 ConfigSetting("XInputAnalogInverseDeadzone", &g_Config
.fXInputAnalogInverseDeadzone
, 0.0f
, true, true),
598 ConfigSetting("XInputAnalogSensitivity", &g_Config
.fXInputAnalogSensitivity
, 1.0f
, true, true),
600 ConfigSetting("AnalogLimiterDeadzone", &g_Config
.fAnalogLimiterDeadzone
, 0.6f
, true, true),
602 ConfigSetting(false),
605 static ConfigSetting networkSettings
[] = {
606 ConfigSetting("EnableWlan", &g_Config
.bEnableWlan
, false, true, true),
607 ConfigSetting("EnableAdhocServer", &g_Config
.bEnableAdhocServer
, false, true, true),
608 ConfigSetting(false),
611 static int DefaultPSPModel() {
612 // TODO: Can probably default this on, but not sure about its memory differences.
613 #if !defined(_M_X64) && !defined(_WIN32) && !defined(__SYMBIAN32__)
614 return PSP_MODEL_FAT
;
616 return PSP_MODEL_SLIM
;
620 static int DefaultSystemParamLanguage() {
621 int defaultLang
= PSP_SYSTEMPARAM_LANGUAGE_ENGLISH
;
622 if (g_Config
.bFirstRun
) {
623 // TODO: Be smart about same language, different country
624 auto langValuesMapping
= GetLangValuesMapping();
625 if (langValuesMapping
.find(g_Config
.sLanguageIni
) != langValuesMapping
.end()) {
626 defaultLang
= langValuesMapping
[g_Config
.sLanguageIni
].second
;
632 static ConfigSetting systemParamSettings
[] = {
633 ReportedConfigSetting("PSPModel", &g_Config
.iPSPModel
, &DefaultPSPModel
, true, true),
634 ReportedConfigSetting("PSPFirmwareVersion", &g_Config
.iFirmwareVersion
, PSP_DEFAULT_FIRMWARE
, true, true),
635 ConfigSetting("NickName", &g_Config
.sNickName
, "PPSSPP", true, true),
636 ConfigSetting("proAdhocServer", &g_Config
.proAdhocServer
, "coldbird.net", true, true),
637 ConfigSetting("MacAddress", &g_Config
.sMACAddress
, &CreateRandMAC
, true, true),
638 ReportedConfigSetting("Language", &g_Config
.iLanguage
, &DefaultSystemParamLanguage
, true, true),
639 ConfigSetting("TimeFormat", &g_Config
.iTimeFormat
, PSP_SYSTEMPARAM_TIME_FORMAT_24HR
, true, true),
640 ConfigSetting("DateFormat", &g_Config
.iDateFormat
, PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD
, true, true),
641 ConfigSetting("TimeZone", &g_Config
.iTimeZone
, 0, true, true),
642 ConfigSetting("DayLightSavings", &g_Config
.bDayLightSavings
, (bool) PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_STD
, true, true),
643 ReportedConfigSetting("ButtonPreference", &g_Config
.iButtonPreference
, PSP_SYSTEMPARAM_BUTTON_CROSS
, true, true),
644 ConfigSetting("LockParentalLevel", &g_Config
.iLockParentalLevel
, 0, true, true),
645 ConfigSetting("WlanAdhocChannel", &g_Config
.iWlanAdhocChannel
, PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC
, true, true),
646 #if defined(USING_WIN_UI)
647 ConfigSetting("BypassOSKWithKeyboard", &g_Config
.bBypassOSKWithKeyboard
, false, true, true),
649 ConfigSetting("WlanPowerSave", &g_Config
.bWlanPowerSave
, (bool) PSP_SYSTEMPARAM_WLAN_POWERSAVE_OFF
, true, true),
650 ReportedConfigSetting("EncryptSave", &g_Config
.bEncryptSave
, true, true, true),
652 ConfigSetting(false),
655 static ConfigSetting debuggerSettings
[] = {
656 ConfigSetting("DisasmWindowX", &g_Config
.iDisasmWindowX
, -1),
657 ConfigSetting("DisasmWindowY", &g_Config
.iDisasmWindowY
, -1),
658 ConfigSetting("DisasmWindowW", &g_Config
.iDisasmWindowW
, -1),
659 ConfigSetting("DisasmWindowH", &g_Config
.iDisasmWindowH
, -1),
660 ConfigSetting("GEWindowX", &g_Config
.iGEWindowX
, -1),
661 ConfigSetting("GEWindowY", &g_Config
.iGEWindowY
, -1),
662 ConfigSetting("GEWindowW", &g_Config
.iGEWindowW
, -1),
663 ConfigSetting("GEWindowH", &g_Config
.iGEWindowH
, -1),
664 ConfigSetting("ConsoleWindowX", &g_Config
.iConsoleWindowX
, -1),
665 ConfigSetting("ConsoleWindowY", &g_Config
.iConsoleWindowY
, -1),
666 ConfigSetting("FontWidth", &g_Config
.iFontWidth
, 8),
667 ConfigSetting("FontHeight", &g_Config
.iFontHeight
, 12),
668 ConfigSetting("DisplayStatusBar", &g_Config
.bDisplayStatusBar
, true),
669 ConfigSetting("ShowBottomTabTitles",&g_Config
.bShowBottomTabTitles
,true),
670 ConfigSetting("ShowDeveloperMenu", &g_Config
.bShowDeveloperMenu
, false),
671 ConfigSetting("SkipDeadbeefFilling", &g_Config
.bSkipDeadbeefFilling
, false),
672 ConfigSetting("FuncHashMap", &g_Config
.bFuncHashMap
, false),
674 ConfigSetting(false),
677 static ConfigSetting speedHackSettings
[] = {
678 ReportedConfigSetting("PrescaleUV", &g_Config
.bPrescaleUV
, false, true, true),
679 ReportedConfigSetting("DisableAlphaTest", &g_Config
.bDisableAlphaTest
, false, true, true),
681 ConfigSetting(false),
684 static ConfigSetting jitSettings
[] = {
685 ReportedConfigSetting("DiscardRegsOnJRRA", &g_Config
.bDiscardRegsOnJRRA
, false, false),
687 ConfigSetting(false),
690 static ConfigSetting upgradeSettings
[] = {
691 ConfigSetting("UpgradeMessage", &g_Config
.upgradeMessage
, ""),
692 ConfigSetting("UpgradeVersion", &g_Config
.upgradeVersion
, ""),
693 ConfigSetting("DismissedVersion", &g_Config
.dismissedVersion
, ""),
695 ConfigSetting(false),
698 static ConfigSectionSettings sections
[] = {
699 {"General", generalSettings
},
700 {"CPU", cpuSettings
},
701 {"Graphics", graphicsSettings
},
702 {"Sound", soundSettings
},
703 {"Control", controlSettings
},
704 {"Network", networkSettings
},
705 {"SystemParam", systemParamSettings
},
706 {"Debugger", debuggerSettings
},
707 {"SpeedHacks", speedHackSettings
},
708 {"JIT", jitSettings
},
709 {"Upgrade", upgradeSettings
},
712 Config::Config() : bGameSpecific(false) { }
713 Config::~Config() { }
715 std::map
<std::string
, std::pair
<std::string
, int>> GetLangValuesMapping() {
716 std::map
<std::string
, std::pair
<std::string
, int>> langValuesMapping
;
718 mapping
.LoadFromVFS("langregion.ini");
719 std::vector
<std::string
> keys
;
720 mapping
.GetKeys("LangRegionNames", keys
);
723 std::map
<std::string
, int> langCodeMapping
;
724 langCodeMapping
["JAPANESE"] = PSP_SYSTEMPARAM_LANGUAGE_JAPANESE
;
725 langCodeMapping
["ENGLISH"] = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH
;
726 langCodeMapping
["FRENCH"] = PSP_SYSTEMPARAM_LANGUAGE_FRENCH
;
727 langCodeMapping
["SPANISH"] = PSP_SYSTEMPARAM_LANGUAGE_SPANISH
;
728 langCodeMapping
["GERMAN"] = PSP_SYSTEMPARAM_LANGUAGE_GERMAN
;
729 langCodeMapping
["ITALIAN"] = PSP_SYSTEMPARAM_LANGUAGE_ITALIAN
;
730 langCodeMapping
["DUTCH"] = PSP_SYSTEMPARAM_LANGUAGE_DUTCH
;
731 langCodeMapping
["PORTUGUESE"] = PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE
;
732 langCodeMapping
["RUSSIAN"] = PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN
;
733 langCodeMapping
["KOREAN"] = PSP_SYSTEMPARAM_LANGUAGE_KOREAN
;
734 langCodeMapping
["CHINESE_TRADITIONAL"] = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL
;
735 langCodeMapping
["CHINESE_SIMPLIFIED"] = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED
;
737 IniFile::Section
*langRegionNames
= mapping
.GetOrCreateSection("LangRegionNames");
738 IniFile::Section
*systemLanguage
= mapping
.GetOrCreateSection("SystemLanguage");
740 for (size_t i
= 0; i
< keys
.size(); i
++) {
741 std::string langName
;
742 langRegionNames
->Get(keys
[i
].c_str(), &langName
, "ERROR");
743 std::string langCode
;
744 systemLanguage
->Get(keys
[i
].c_str(), &langCode
, "ENGLISH");
745 int iLangCode
= PSP_SYSTEMPARAM_LANGUAGE_ENGLISH
;
746 if (langCodeMapping
.find(langCode
) != langCodeMapping
.end())
747 iLangCode
= langCodeMapping
[langCode
];
748 langValuesMapping
[keys
[i
]] = std::make_pair(langName
, iLangCode
);
750 return langValuesMapping
;
753 void Config::Load(const char *iniFileName
, const char *controllerIniFilename
) {
754 const bool useIniFilename
= iniFileName
!= nullptr && strlen(iniFileName
) > 0;
755 iniFilename_
= FindConfigFile(useIniFilename
? iniFileName
: "ppsspp.ini");
757 const bool useControllerIniFilename
= controllerIniFilename
!= nullptr && strlen(controllerIniFilename
) > 0;
758 controllerIniFilename_
= FindConfigFile(useControllerIniFilename
? controllerIniFilename
: "controls.ini");
760 INFO_LOG(LOADER
, "Loading config: %s", iniFilename_
.c_str());
761 bSaveSettings
= true;
763 bShowFrameProfiler
= true;
766 if (!iniFile
.Load(iniFilename_
)) {
767 ERROR_LOG(LOADER
, "Failed to read %s. Setting config to default.", iniFilename_
.c_str());
768 // Continue anyway to initialize the config.
771 for (size_t i
= 0; i
< ARRAY_SIZE(sections
); ++i
) {
772 IniFile::Section
*section
= iniFile
.GetOrCreateSection(sections
[i
].section
);
773 for (auto setting
= sections
[i
].settings
; setting
->HasMore(); ++setting
) {
774 setting
->Get(section
);
779 if (!File::Exists(currentDirectory
))
780 currentDirectory
= "";
782 IniFile::Section
*recent
= iniFile
.GetOrCreateSection("Recent");
783 recent
->Get("MaxRecent", &iMaxRecent
, 30);
785 // Fix issue from switching from uint (hex in .ini) to int (dec)
786 // -1 is okay, though. We'll just ignore recent stuff if it is.
790 if (iMaxRecent
> 0) {
792 for (int i
= 0; i
< iMaxRecent
; i
++) {
794 std::string fileName
;
796 snprintf(keyName
, sizeof(keyName
), "FileName%d", i
);
797 if (recent
->Get(keyName
, &fileName
, "") && !fileName
.empty()) {
798 recentIsos
.push_back(fileName
);
803 auto pinnedPaths
= iniFile
.GetOrCreateSection("PinnedPaths")->ToMap();
804 vPinnedPaths
.clear();
805 for (auto it
= pinnedPaths
.begin(), end
= pinnedPaths
.end(); it
!= end
; ++it
) {
806 vPinnedPaths
.push_back(it
->second
);
809 if (iAnisotropyLevel
> 4) {
810 iAnisotropyLevel
= 4;
813 // Check for an old dpad setting
814 IniFile::Section
*control
= iniFile
.GetOrCreateSection("Control");
816 control
->Get("DPadRadius", &f
, 0.0f
);
818 ResetControlLayout();
821 // MIGRATION: For users who had the old static touch layout, aren't I nice?
822 // We can probably kill this in 0.9.8 or something.
823 if (fDpadX
> 1.0 || fDpadY
> 1.0) { // Likely the rest are too!
824 float screen_width
= dp_xres
;
825 float screen_height
= dp_yres
;
827 fActionButtonCenterX
/= screen_width
;
828 fActionButtonCenterY
/= screen_height
;
829 fDpadX
/= screen_width
;
830 fDpadY
/= screen_height
;
831 fStartKeyX
/= screen_width
;
832 fStartKeyY
/= screen_height
;
833 fSelectKeyX
/= screen_width
;
834 fSelectKeyY
/= screen_height
;
835 fUnthrottleKeyX
/= screen_width
;
836 fUnthrottleKeyY
/= screen_height
;
837 fLKeyX
/= screen_width
;
838 fLKeyY
/= screen_height
;
839 fRKeyX
/= screen_width
;
840 fRKeyY
/= screen_height
;
841 fAnalogStickX
/= screen_width
;
842 fAnalogStickY
/= screen_height
;
845 const char *gitVer
= PPSSPP_GIT_VERSION
;
846 Version
installed(gitVer
);
847 Version
upgrade(upgradeVersion
);
848 const bool versionsValid
= installed
.IsValid() && upgrade
.IsValid();
850 // Do this regardless of iRunCount to prevent a silly bug where one might use an older
851 // build of PPSSPP, receive an upgrade notice, then start a newer version, and still receive the upgrade notice,
852 // even if said newer version is >= the upgrade found online.
853 if ((dismissedVersion
== upgradeVersion
) || (versionsValid
&& (installed
>= upgrade
))) {
857 // Check for new version on every 10 runs.
858 // Sometimes the download may not be finished when the main screen shows (if the user dismisses the
859 // splash screen quickly), but then we'll just show the notification next time instead, we store the
860 // upgrade number in the ini.
861 #if !defined(ARMEABI)
862 if (iRunCount
% 10 == 0 && bCheckForNewVersion
) {
863 std::shared_ptr
<http::Download
> dl
= g_DownloadManager
.StartDownloadWithCallback(
864 "http://www.ppsspp.org/version.json", "", &DownloadCompletedCallback
);
869 INFO_LOG(LOADER
, "Loading controller config: %s", controllerIniFilename_
.c_str());
870 bSaveSettings
= true;
872 IniFile controllerIniFile
;
873 if (!controllerIniFile
.Load(controllerIniFilename_
)) {
874 ERROR_LOG(LOADER
, "Failed to read %s. Setting controller config to default.", controllerIniFilename_
.c_str());
875 KeyMap::RestoreDefault();
877 // Continue anyway to initialize the config. It will just restore the defaults.
878 KeyMap::LoadFromIni(controllerIniFile
);
881 //so this is all the way down here to overwrite the controller settings
882 //sadly it won't benefit from all the "version conversion" going on up-above
883 //but these configs shouldn't contain older versions anyhow
886 loadGameConfig(gameId_
);
891 #if defined (_WIN32) && !defined (__LIBRETRO__)
892 iTempGPUBackend
= iGPUBackend
;
895 // Fix Wrong MAC address by old version by "Change MAC address"
896 if (sMACAddress
.length() != 17)
897 sMACAddress
= CreateRandMAC();
899 if (g_Config
.bAutoFrameSkip
&& g_Config
.iRenderingMode
== FB_NON_BUFFERED_MODE
) {
900 g_Config
.iRenderingMode
= FB_BUFFERED_MODE
;
904 void Config::Save() {
905 if (iniFilename_
.size() && g_Config
.bSaveSettings
) {
907 saveGameConfig(gameId_
);
911 if (!iniFile
.Load(iniFilename_
.c_str())) {
912 ERROR_LOG(LOADER
, "Error saving config - can't read ini %s", iniFilename_
.c_str());
915 // Need to do this somewhere...
918 for (size_t i
= 0; i
< ARRAY_SIZE(sections
); ++i
) {
919 IniFile::Section
*section
= iniFile
.GetOrCreateSection(sections
[i
].section
);
920 for (auto setting
= sections
[i
].settings
; setting
->HasMore(); ++setting
) {
921 if (!bGameSpecific
|| !setting
->perGame_
){
922 setting
->Set(section
);
927 IniFile::Section
*recent
= iniFile
.GetOrCreateSection("Recent");
928 recent
->Set("MaxRecent", iMaxRecent
);
930 for (int i
= 0; i
< iMaxRecent
; i
++) {
932 snprintf(keyName
, sizeof(keyName
), "FileName%d", i
);
933 if (i
< (int)recentIsos
.size()) {
934 recent
->Set(keyName
, recentIsos
[i
]);
936 recent
->Delete(keyName
); // delete the nonexisting FileName
940 IniFile::Section
*pinnedPaths
= iniFile
.GetOrCreateSection("PinnedPaths");
941 pinnedPaths
->Clear();
942 for (size_t i
= 0; i
< vPinnedPaths
.size(); ++i
) {
944 snprintf(keyName
, sizeof(keyName
), "Path%d", (int)i
);
945 pinnedPaths
->Set(keyName
, vPinnedPaths
[i
]);
948 IniFile::Section
*control
= iniFile
.GetOrCreateSection("Control");
949 control
->Delete("DPadRadius");
951 if (!iniFile
.Save(iniFilename_
.c_str())) {
952 ERROR_LOG(LOADER
, "Error saving config - can't write ini %s", iniFilename_
.c_str());
955 INFO_LOG(LOADER
, "Config saved: %s", iniFilename_
.c_str());
957 if (!bGameSpecific
) //otherwise we already did this in saveGameConfig()
959 IniFile controllerIniFile
;
960 if (!controllerIniFile
.Load(controllerIniFilename_
.c_str())) {
961 ERROR_LOG(LOADER
, "Error saving config - can't read ini %s", controllerIniFilename_
.c_str());
963 KeyMap::SaveToIni(controllerIniFile
);
964 if (!controllerIniFile
.Save(controllerIniFilename_
.c_str())) {
965 ERROR_LOG(LOADER
, "Error saving config - can't write ini %s", controllerIniFilename_
.c_str());
968 INFO_LOG(LOADER
, "Controller config saved: %s", controllerIniFilename_
.c_str());
971 INFO_LOG(LOADER
, "Not saving config");
975 // Use for debugging the version check without messing with the server
977 #define PPSSPP_GIT_VERSION "v0.0.1-gaaaaaaaaa"
980 void Config::DownloadCompletedCallback(http::Download
&download
) {
981 if (download
.ResultCode() != 200) {
982 ERROR_LOG(LOADER
, "Failed to download version.json");
986 download
.buffer().TakeAll(&data
);
988 ERROR_LOG(LOADER
, "Version check: Empty data from server!");
992 JsonReader
reader(data
.c_str(), data
.size());
993 const json_value
*root
= reader
.root();
994 std::string version
= root
->getString("version", "");
996 const char *gitVer
= PPSSPP_GIT_VERSION
;
997 Version
installed(gitVer
);
998 Version
upgrade(version
);
999 Version
dismissed(g_Config
.dismissedVersion
);
1001 if (!installed
.IsValid()) {
1002 ERROR_LOG(LOADER
, "Version check: Local version string invalid. Build problems? %s", PPSSPP_GIT_VERSION
);
1005 if (!upgrade
.IsValid()) {
1006 ERROR_LOG(LOADER
, "Version check: Invalid server version: %s", version
.c_str());
1010 if (installed
>= upgrade
) {
1011 INFO_LOG(LOADER
, "Version check: Already up to date, erasing any upgrade message");
1012 g_Config
.upgradeMessage
= "";
1013 g_Config
.upgradeVersion
= upgrade
.ToString();
1014 g_Config
.dismissedVersion
= "";
1018 if (installed
< upgrade
&& dismissed
!= upgrade
) {
1019 g_Config
.upgradeMessage
= "New version of PPSSPP available!";
1020 g_Config
.upgradeVersion
= upgrade
.ToString();
1021 g_Config
.dismissedVersion
= "";
1025 void Config::DismissUpgrade() {
1026 g_Config
.dismissedVersion
= g_Config
.upgradeVersion
;
1029 void Config::AddRecent(const std::string
&file
) {
1030 // Don't bother with this if the user disabled recents (it's -1).
1031 if (iMaxRecent
<= 0)
1034 for (auto str
= recentIsos
.begin(); str
!= recentIsos
.end(); ++str
) {
1036 if (!strcmpIgnore((*str
).c_str(), file
.c_str(), "\\", "/")) {
1038 if (!strcmp((*str
).c_str(), file
.c_str())) {
1040 recentIsos
.erase(str
);
1041 recentIsos
.insert(recentIsos
.begin(), file
);
1042 if ((int)recentIsos
.size() > iMaxRecent
)
1043 recentIsos
.resize(iMaxRecent
);
1047 recentIsos
.insert(recentIsos
.begin(), file
);
1048 if ((int)recentIsos
.size() > iMaxRecent
)
1049 recentIsos
.resize(iMaxRecent
);
1052 void Config::CleanRecent() {
1053 std::vector
<std::string
> cleanedRecent
;
1054 for (size_t i
= 0; i
< recentIsos
.size(); i
++) {
1055 FileLoader
*loader
= ConstructFileLoader(recentIsos
[i
]);
1056 if (loader
->Exists()) {
1057 // Make sure we don't have any redundant items.
1058 auto duplicate
= std::find(cleanedRecent
.begin(), cleanedRecent
.end(), recentIsos
[i
]);
1059 if (duplicate
== cleanedRecent
.end()) {
1060 cleanedRecent
.push_back(recentIsos
[i
]);
1065 recentIsos
= cleanedRecent
;
1068 void Config::SetDefaultPath(const std::string
&defaultPath
) {
1069 defaultPath_
= defaultPath
;
1072 void Config::AddSearchPath(const std::string
&path
) {
1073 searchPath_
.push_back(path
);
1076 const std::string
Config::FindConfigFile(const std::string
&baseFilename
) {
1077 // Don't search for an absolute path.
1078 if (baseFilename
.size() > 1 && baseFilename
[0] == '/') {
1079 return baseFilename
;
1082 if (baseFilename
.size() > 3 && baseFilename
[1] == ':' && (baseFilename
[2] == '/' || baseFilename
[2] == '\\')) {
1083 return baseFilename
;
1087 for (size_t i
= 0; i
< searchPath_
.size(); ++i
) {
1088 std::string filename
= searchPath_
[i
] + baseFilename
;
1089 if (File::Exists(filename
)) {
1094 const std::string filename
= defaultPath_
.empty() ? baseFilename
: defaultPath_
+ baseFilename
;
1095 if (!File::Exists(filename
)) {
1097 SplitPath(filename
, &path
, NULL
, NULL
);
1098 if (createdPath_
!= path
) {
1099 File::CreateFullPath(path
);
1100 createdPath_
= path
;
1106 void Config::RestoreDefaults() {
1109 deleteGameConfig(gameId_
);
1110 createGameConfig(gameId_
);
1114 if (File::Exists(iniFilename_
))
1115 File::Delete(iniFilename_
);
1117 currentDirectory
= "";
1122 bool Config::hasGameConfig(const std::string
&pGameId
)
1124 std::string fullIniFilePath
= getGameConfigFile(pGameId
);
1126 IniFile existsCheck
;
1127 bool exists
= existsCheck
.Load(fullIniFilePath
);
1131 void Config::changeGameSpecific(const std::string
&pGameId
)
1135 bGameSpecific
= !pGameId
.empty();
1138 bool Config::createGameConfig(const std::string
&pGameId
)
1140 std::string fullIniFilePath
= getGameConfigFile(pGameId
);
1142 if (hasGameConfig(pGameId
))
1147 File::CreateEmptyFile(fullIniFilePath
);
1152 bool Config::deleteGameConfig(const std::string
& pGameId
)
1154 std::string fullIniFilePath
= getGameConfigFile(pGameId
);
1156 File::Delete(fullIniFilePath
);
1160 std::string
Config::getGameConfigFile(const std::string
&pGameId
)
1162 std::string iniFileName
= pGameId
+ "_ppsspp.ini";
1163 std::string iniFileNameFull
= FindConfigFile(iniFileName
);
1165 return iniFileNameFull
;
1168 bool Config::saveGameConfig(const std::string
&pGameId
)
1170 if (pGameId
.empty())
1175 std::string fullIniFilePath
= getGameConfigFile(pGameId
);
1179 for (size_t i
= 0; i
< ARRAY_SIZE(sections
); ++i
) {
1180 IniFile::Section
*section
= iniFile
.GetOrCreateSection(sections
[i
].section
);
1181 for (auto setting
= sections
[i
].settings
; setting
->HasMore(); ++setting
) {
1182 if (setting
->perGame_
){
1183 setting
->Set(section
);
1188 KeyMap::SaveToIni(iniFile
);
1189 iniFile
.Save(fullIniFilePath
);
1194 bool Config::loadGameConfig(const std::string
&pGameId
)
1196 std::string iniFileNameFull
= getGameConfigFile(pGameId
);
1198 if (!hasGameConfig(pGameId
))
1200 INFO_LOG(LOADER
, "Failed to read %s. No game-specific settings found, using global defaults.", iniFileNameFull
.c_str());
1204 changeGameSpecific(pGameId
);
1206 iniFile
.Load(iniFileNameFull
);
1209 for (size_t i
= 0; i
< ARRAY_SIZE(sections
); ++i
) {
1210 IniFile::Section
*section
= iniFile
.GetOrCreateSection(sections
[i
].section
);
1211 for (auto setting
= sections
[i
].settings
; setting
->HasMore(); ++setting
) {
1212 if (setting
->perGame_
){
1213 setting
->Get(section
);
1217 KeyMap::LoadFromIni(iniFile
);
1222 void Config::unloadGameConfig()
1226 changeGameSpecific();
1227 Load(iniFilename_
.c_str(), controllerIniFilename_
.c_str());
1231 void Config::ResetControlLayout() {
1232 g_Config
.fActionButtonScale
= defaultControlScale
;
1233 g_Config
.fActionButtonSpacing
= 1.0f
;
1234 g_Config
.fActionButtonCenterX
= -1.0;
1235 g_Config
.fActionButtonCenterY
= -1.0;
1236 g_Config
.fDpadScale
= defaultControlScale
;
1237 g_Config
.fDpadSpacing
= 1.0f
;
1238 g_Config
.fDpadX
= -1.0;
1239 g_Config
.fDpadY
= -1.0;
1240 g_Config
.fStartKeyX
= -1.0;
1241 g_Config
.fStartKeyY
= -1.0;
1242 g_Config
.fStartKeyScale
= defaultControlScale
;
1243 g_Config
.fSelectKeyX
= -1.0;
1244 g_Config
.fSelectKeyY
= -1.0;
1245 g_Config
.fSelectKeyScale
= defaultControlScale
;
1246 g_Config
.fUnthrottleKeyX
= -1.0;
1247 g_Config
.fUnthrottleKeyY
= -1.0;
1248 g_Config
.fUnthrottleKeyScale
= defaultControlScale
;
1249 g_Config
.fLKeyX
= -1.0;
1250 g_Config
.fLKeyY
= -1.0;
1251 g_Config
.fLKeyScale
= defaultControlScale
;
1252 g_Config
.fRKeyX
= -1.0;
1253 g_Config
.fRKeyY
= -1.0;
1254 g_Config
.fRKeyScale
= defaultControlScale
;
1255 g_Config
.fAnalogStickX
= -1.0;
1256 g_Config
.fAnalogStickY
= -1.0;
1257 g_Config
.fAnalogStickScale
= defaultControlScale
;
1260 void Config::GetReportingInfo(UrlEncoder
&data
) {
1261 for (size_t i
= 0; i
< ARRAY_SIZE(sections
); ++i
) {
1262 const std::string prefix
= std::string("config.") + sections
[i
].section
;
1263 for (auto setting
= sections
[i
].settings
; setting
->HasMore(); ++setting
) {
1264 setting
->Report(data
, prefix
);