Merge pull request #90 from gizmo98/patch-2
[libretro-ppsspp.git] / Core / Config.cpp
blob22a9d88fdc7960b33a72f5dab199dc5db8baddd8
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/.
18 #include <cstdlib>
19 #include <ctime>
20 #include <algorithm>
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"
30 #include "net/url.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"
41 #ifndef USING_QT_UI
42 extern const char *PPSSPP_GIT_VERSION;
43 #endif
45 // TODO: Find a better place for this.
46 http::Downloader g_DownloadManager;
48 Config g_Config;
50 #ifdef IOS
51 extern bool iosCanUseJit;
52 #endif
54 struct ConfigSetting {
55 enum Type {
56 TYPE_TERMINATOR,
57 TYPE_BOOL,
58 TYPE_INT,
59 TYPE_FLOAT,
60 TYPE_STRING,
62 union Value {
63 bool b;
64 int i;
65 float f;
66 const char *s;
68 union SettingPtr {
69 bool *b;
70 int *i;
71 float *f;
72 std::string *s;
75 typedef bool (*BoolDefaultCallback)();
76 typedef int (*IntDefaultCallback)();
77 typedef float (*FloatDefaultCallback)();
78 typedef const char *(*StringDefaultCallback)();
80 union Callback {
81 BoolDefaultCallback b;
82 IntDefaultCallback i;
83 FloatDefaultCallback f;
84 StringDefaultCallback s;
87 ConfigSetting(bool v)
88 : ini_(""), type_(TYPE_TERMINATOR), report_(false), save_(false), perGame_(false) {
89 ptr_.b = NULL;
90 cb_.b = NULL;
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) {
95 ptr_.b = v;
96 cb_.b = NULL;
97 default_.b = def;
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) {
102 ptr_.i = v;
103 cb_.i = NULL;
104 default_.i = def;
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) {
109 ptr_.f = v;
110 cb_.f = NULL;
111 default_.f = def;
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) {
116 ptr_.s = v;
117 cb_.s = NULL;
118 default_.s = def;
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) {
123 ptr_.b = v;
124 cb_.b = def;
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) {
129 ptr_ .i= v;
130 cb_.i = def;
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) {
135 ptr_.f = v;
136 cb_.f = def;
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) {
141 ptr_.s = v;
142 cb_.s = def;
145 bool HasMore() const {
146 return type_ != TYPE_TERMINATOR;
149 bool Get(IniFile::Section *section) {
150 switch (type_) {
151 case TYPE_BOOL:
152 if (cb_.b) {
153 default_.b = cb_.b();
155 return section->Get(ini_, ptr_.b, default_.b);
156 case TYPE_INT:
157 if (cb_.i) {
158 default_.i = cb_.i();
160 return section->Get(ini_, ptr_.i, default_.i);
161 case TYPE_FLOAT:
162 if (cb_.f) {
163 default_.f = cb_.f();
165 return section->Get(ini_, ptr_.f, default_.f);
166 case TYPE_STRING:
167 if (cb_.s) {
168 default_.s = cb_.s();
170 return section->Get(ini_, ptr_.s, default_.s);
171 default:
172 _dbg_assert_msg_(LOADER, false, "Unexpected ini setting type");
173 return false;
177 void Set(IniFile::Section *section) {
178 if (!save_)
179 return;
181 switch (type_) {
182 case TYPE_BOOL:
183 return section->Set(ini_, *ptr_.b);
184 case TYPE_INT:
185 return section->Set(ini_, *ptr_.i);
186 case TYPE_FLOAT:
187 return section->Set(ini_, *ptr_.f);
188 case TYPE_STRING:
189 return section->Set(ini_, *ptr_.s);
190 default:
191 _dbg_assert_msg_(LOADER, false, "Unexpected ini setting type");
192 return;
196 void Report(UrlEncoder &data, const std::string &prefix) {
197 if (!report_)
198 return;
200 switch (type_) {
201 case TYPE_BOOL:
202 return data.Add(prefix + ini_, *ptr_.b);
203 case TYPE_INT:
204 return data.Add(prefix + ini_, *ptr_.i);
205 case TYPE_FLOAT:
206 return data.Add(prefix + ini_, *ptr_.f);
207 case TYPE_STRING:
208 return data.Add(prefix + ini_, *ptr_.s);
209 default:
210 _dbg_assert_msg_(LOADER, false, "Unexpected ini setting type");
211 return;
215 const char *ini_;
216 Type type_;
217 bool report_;
218 bool save_;
219 bool perGame_;
220 SettingPtr ptr_;
221 Value default_;
222 Callback cb_;
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) {
229 report_ = true;
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;
249 if (value <= 15)
250 randStream << '0' << std::hex << value;
251 else
252 randStream << std::hex << value;
253 if (i < 5) {
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() {
266 #ifdef IOS
267 return iosCanUseJit;
268 #elif defined(ARM) || defined(ARM64) || defined(_M_IX86) || defined(_M_X64)
269 return true;
270 #else
271 return false;
272 #endif
275 struct ConfigSectionSettings {
276 const char *section;
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),
311 #ifdef ANDROID
312 ConfigSetting("ScreenRotation", &g_Config.iScreenRotation, 1),
313 #endif
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),
323 #endif
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() {
330 #ifdef ARM
331 return true;
332 #else
333 return false;
334 #endif
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
357 return 1;
360 static int DefaultInternalResolution() {
361 // Auto on Windows, 2x on large screens, 1x elsewhere.
362 #if defined(USING_WIN_UI)
363 return 0;
364 #else
365 int longestDisplaySide = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES));
366 return longestDisplaySide >= 1000 ? 2 : 1;
367 #endif
370 static bool DefaultPartialStretch() {
371 #ifdef BLACKBERRY
372 return pixel_xres < 1.3 * pixel_yres;
373 #else
374 return false;
375 #endif
378 static bool DefaultTimerHack() {
379 // Has been in use on Symbian since v0.7. Preferred option.
380 #ifdef __SYMBIAN32__
381 return true;
382 #else
383 return false;
384 #endif
387 static int DefaultAndroidHwScale() {
388 #ifdef ANDROID
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);
393 if (xres < 960) {
394 // Smaller than the PSP*2, let's go native.
395 return 0;
396 } else if (xres <= 480 * 3) { // 720p xres
397 // Small-ish screen, we should default to 2x
398 return 2 + 1;
399 } else {
400 // Large or very large screen. Default to 3x psp resolution.
401 return 3 + 1;
403 return 0;
404 #else
405 return 1;
406 #endif
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),
431 #else
432 ConfigSetting("FrameSkipUnthrottle", &g_Config.bFrameSkipUnthrottle, true),
433 #endif
434 ReportedConfigSetting("ForceMaxEmulatedFPS", &g_Config.iForceMaxEmulatedFPS, 60, true, true),
435 #ifdef USING_GLES2
436 ConfigSetting("AnisotropyLevel", &g_Config.iAnisotropyLevel, 0, true, true),
437 #else
438 ConfigSetting("AnisotropyLevel", &g_Config.iAnisotropyLevel, 8, true, true),
439 #endif
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),
445 #ifdef _WIN32
446 ConfigSetting("FullScreen", &g_Config.bFullScreen, false),
447 #endif
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)) {
497 return false;
498 } else {
499 return true;
501 } else if (deviceType == DEVICE_TYPE_TV) {
502 return false;
503 } else if (deviceType == DEVICE_TYPE_DESKTOP) {
504 return false;
505 } else {
506 return false;
508 #else
509 return false;
510 #endif
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)
529 #if defined(_WIN32)
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),
533 #else
534 ConfigSetting("ShowTouchPause", &g_Config.bShowTouchPause, false, true, true),
535 #endif
536 #endif
537 #if defined(USING_WIN_UI)
538 ConfigSetting("IgnoreWindowsKey", &g_Config.bIgnoreWindowsKey, false, true, true),
539 #endif
540 ConfigSetting("ShowTouchControls", &g_Config.bShowTouchControls, &DefaultShowTouchControls, true, true),
541 // ConfigSetting("KeyMapping", &g_Config.iMappingMap, 0),
543 #ifdef MOBILE_DEVICE
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),
552 #endif
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),
589 #ifdef _WIN32
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),
599 #endif
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;
615 #else
616 return PSP_MODEL_SLIM;
617 #endif
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;
629 return defaultLang;
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),
648 #endif
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;
717 IniFile mapping;
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;
765 IniFile iniFile;
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);
778 iRunCount++;
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.
787 if (iMaxRecent == 0)
788 iMaxRecent = 30;
790 if (iMaxRecent > 0) {
791 recentIsos.clear();
792 for (int i = 0; i < iMaxRecent; i++) {
793 char keyName[64];
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");
815 float f;
816 control->Get("DPadRadius", &f, 0.0f);
817 if (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))) {
854 upgradeMessage = "";
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);
865 dl->SetHidden(true);
867 #endif
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();
876 } else {
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
884 if (bGameSpecific)
886 loadGameConfig(gameId_);
889 CleanRecent();
891 #if defined (_WIN32) && !defined (__LIBRETRO__)
892 iTempGPUBackend = iGPUBackend;
893 #endif
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_);
909 CleanRecent();
910 IniFile iniFile;
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...
916 bFirstRun = false;
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++) {
931 char keyName[64];
932 snprintf(keyName, sizeof(keyName), "FileName%d", i);
933 if (i < (int)recentIsos.size()) {
934 recent->Set(keyName, recentIsos[i]);
935 } else {
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) {
943 char keyName[64];
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());
953 return;
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());
966 return;
968 INFO_LOG(LOADER, "Controller config saved: %s", controllerIniFilename_.c_str());
970 } else {
971 INFO_LOG(LOADER, "Not saving config");
975 // Use for debugging the version check without messing with the server
976 #if 0
977 #define PPSSPP_GIT_VERSION "v0.0.1-gaaaaaaaaa"
978 #endif
980 void Config::DownloadCompletedCallback(http::Download &download) {
981 if (download.ResultCode() != 200) {
982 ERROR_LOG(LOADER, "Failed to download version.json");
983 return;
985 std::string data;
986 download.buffer().TakeAll(&data);
987 if (data.empty()) {
988 ERROR_LOG(LOADER, "Version check: Empty data from server!");
989 return;
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);
1003 return;
1005 if (!upgrade.IsValid()) {
1006 ERROR_LOG(LOADER, "Version check: Invalid server version: %s", version.c_str());
1007 return;
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 = "";
1015 return;
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)
1032 return;
1034 for (auto str = recentIsos.begin(); str != recentIsos.end(); ++str) {
1035 #ifdef _WIN32
1036 if (!strcmpIgnore((*str).c_str(), file.c_str(), "\\", "/")) {
1037 #else
1038 if (!strcmp((*str).c_str(), file.c_str())) {
1039 #endif
1040 recentIsos.erase(str);
1041 recentIsos.insert(recentIsos.begin(), file);
1042 if ((int)recentIsos.size() > iMaxRecent)
1043 recentIsos.resize(iMaxRecent);
1044 return;
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]);
1063 delete loader;
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;
1081 #ifdef _WIN32
1082 if (baseFilename.size() > 3 && baseFilename[1] == ':' && (baseFilename[2] == '/' || baseFilename[2] == '\\')) {
1083 return baseFilename;
1085 #endif
1087 for (size_t i = 0; i < searchPath_.size(); ++i) {
1088 std::string filename = searchPath_[i] + baseFilename;
1089 if (File::Exists(filename)) {
1090 return filename;
1094 const std::string filename = defaultPath_.empty() ? baseFilename : defaultPath_ + baseFilename;
1095 if (!File::Exists(filename)) {
1096 std::string path;
1097 SplitPath(filename, &path, NULL, NULL);
1098 if (createdPath_ != path) {
1099 File::CreateFullPath(path);
1100 createdPath_ = path;
1103 return filename;
1106 void Config::RestoreDefaults() {
1107 if (bGameSpecific)
1109 deleteGameConfig(gameId_);
1110 createGameConfig(gameId_);
1112 else
1114 if (File::Exists(iniFilename_))
1115 File::Delete(iniFilename_);
1116 recentIsos.clear();
1117 currentDirectory = "";
1119 Load();
1122 bool Config::hasGameConfig(const std::string &pGameId)
1124 std::string fullIniFilePath = getGameConfigFile(pGameId);
1126 IniFile existsCheck;
1127 bool exists = existsCheck.Load(fullIniFilePath);
1128 return exists;
1131 void Config::changeGameSpecific(const std::string &pGameId)
1133 Save();
1134 gameId_ = pGameId;
1135 bGameSpecific = !pGameId.empty();
1138 bool Config::createGameConfig(const std::string &pGameId)
1140 std::string fullIniFilePath = getGameConfigFile(pGameId);
1142 if (hasGameConfig(pGameId))
1144 return false;
1147 File::CreateEmptyFile(fullIniFilePath);
1149 return true;
1152 bool Config::deleteGameConfig(const std::string& pGameId)
1154 std::string fullIniFilePath = getGameConfigFile(pGameId);
1156 File::Delete(fullIniFilePath);
1157 return true;
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())
1172 return false;
1175 std::string fullIniFilePath = getGameConfigFile(pGameId);
1177 IniFile iniFile;
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);
1191 return true;
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());
1201 return false;
1204 changeGameSpecific(pGameId);
1205 IniFile iniFile;
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);
1218 return true;
1222 void Config::unloadGameConfig()
1224 if (bGameSpecific)
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);