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/UI/GameSettingsScreen.cpp
Views: 1401
// Copyright (c) 2013- 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 "ppsspp_config.h"1819#include <algorithm>20#include <set>2122#include "Common/Net/Resolve.h"23#include "Common/GPU/OpenGL/GLFeatures.h"24#include "Common/Render/DrawBuffer.h"25#include "Common/UI/Root.h"26#include "Common/UI/View.h"27#include "Common/UI/ViewGroup.h"28#include "Common/UI/Context.h"29#include "Common/Render/ManagedTexture.h"30#include "Common/VR/PPSSPPVR.h"3132#include "Common/System/Display.h" // Only to check screen aspect ratio with pixel_yres/pixel_xres33#include "Common/System/Request.h"34#include "Common/System/OSD.h"35#include "Common/Battery/Battery.h"36#include "Common/System/NativeApp.h"37#include "Common/Data/Color/RGBAUtil.h"38#include "Common/Math/curves.h"39#include "Common/Data/Text/I18n.h"40#include "Common/Data/Encoding/Utf8.h"41#include "UI/EmuScreen.h"42#include "UI/DriverManagerScreen.h"43#include "UI/GameSettingsScreen.h"44#include "UI/GameInfoCache.h"45#include "UI/GamepadEmu.h"46#include "UI/MiscScreens.h"47#include "UI/ControlMappingScreen.h"48#include "UI/DevScreens.h"49#include "UI/DisplayLayoutScreen.h"50#include "UI/RemoteISOScreen.h"51#include "UI/SavedataScreen.h"52#include "UI/TouchControlLayoutScreen.h"53#include "UI/TouchControlVisibilityScreen.h"54#include "UI/TiltAnalogSettingsScreen.h"55#include "UI/GPUDriverTestScreen.h"56#include "UI/MemStickScreen.h"57#include "UI/Theme.h"58#include "UI/RetroAchievementScreens.h"59#include "UI/OnScreenDisplay.h"6061#include "Common/File/FileUtil.h"62#include "Common/File/AndroidContentURI.h"63#include "Common/OSVersion.h"64#include "Common/TimeUtil.h"65#include "Common/StringUtils.h"66#include "Core/Config.h"67#include "Core/ConfigValues.h"68#include "Core/KeyMap.h"69#include "Core/TiltEventProcessor.h"70#include "Core/Instance.h"71#include "Core/System.h"72#include "Core/Reporting.h"73#include "Core/WebServer.h"74#include "Core/HLE/sceUsbCam.h"75#include "Core/HLE/sceUsbMic.h"76#include "GPU/Common/TextureReplacer.h"77#include "GPU/Common/PostShader.h"78#include "android/jni/TestRunner.h"79#include "GPU/GPUInterface.h"80#include "GPU/Common/FramebufferManagerCommon.h"8182#include "Core/Core.h" // for Core_IsStepping83#include "Core/MIPS/MIPSTracer.h"8485#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)86#include "UI/DarwinFileSystemServices.h"87#endif8889#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)90#pragma warning(disable:4091) // workaround bug in VS2015 headers91#include "Windows/MainWindow.h"92#include <shlobj.h>93#include "Windows/W32Util/ShellUtil.h"94#endif9596#if PPSSPP_PLATFORM(ANDROID)9798#include "android/jni/AndroidAudio.h"99#include "Common/File/AndroidStorage.h"100101extern AndroidAudioState *g_audioState;102103static bool CheckKgslPresent() {104constexpr auto KgslPath{"/dev/kgsl-3d0"};105106return access(KgslPath, F_OK) == 0;107}108109static bool SupportsCustomDriver() {110return android_get_device_api_level() >= 28 && CheckKgslPresent();111}112113#else114115static bool SupportsCustomDriver() {116#ifdef _DEBUG117return false; // change to true to debug driver installation on other platforms118#else119return false;120#endif121}122123#endif124125#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)126static void SetMemStickDirDarwin(int requesterToken) {127auto initialPath = g_Config.memStickDirectory;128INFO_LOG(Log::System, "Current path: %s", initialPath.c_str());129System_BrowseForFolder(requesterToken, "", initialPath, [](const std::string &value, int) {130INFO_LOG(Log::System, "Selected path: %s", value.c_str());131DarwinFileSystemServices::setUserPreferredMemoryStickDirectory(Path(value));132});133}134#endif135136GameSettingsScreen::GameSettingsScreen(const Path &gamePath, std::string gameID, bool editThenRestore)137: TabbedUIDialogScreenWithGameBackground(gamePath), gameID_(gameID), editThenRestore_(editThenRestore) {138prevInflightFrames_ = g_Config.iInflightFrames;139analogSpeedMapped_ = KeyMap::InputMappingsFromPspButton(VIRTKEY_SPEED_ANALOG, nullptr, true);140}141142// This needs before run CheckGPUFeatures()143// TODO: Remove this if fix the issue144bool CheckSupportShaderTessellationGLES() {145#if PPSSPP_PLATFORM(UWP)146return true;147#else148// TODO: Make work with non-GL backends149int maxVertexTextureImageUnits = gl_extensions.maxVertexTextureUnits;150bool vertexTexture = maxVertexTextureImageUnits >= 3; // At least 3 for hardware tessellation151152bool textureFloat = gl_extensions.ARB_texture_float || gl_extensions.OES_texture_float;153bool hasTexelFetch = gl_extensions.GLES3 || (!gl_extensions.IsGLES && gl_extensions.VersionGEThan(3, 3, 0)) || gl_extensions.EXT_gpu_shader4;154155return vertexTexture && textureFloat && hasTexelFetch;156#endif157}158159bool DoesBackendSupportHWTess() {160switch (GetGPUBackend()) {161case GPUBackend::OPENGL:162return CheckSupportShaderTessellationGLES();163case GPUBackend::VULKAN:164case GPUBackend::DIRECT3D11:165return true;166default:167return false;168}169}170171static bool UsingHardwareTextureScaling() {172// For now, Vulkan only.173return g_Config.bTexHardwareScaling && GetGPUBackend() == GPUBackend::VULKAN && !g_Config.bSoftwareRendering;174}175176static std::string TextureTranslateName(std::string_view value) {177const TextureShaderInfo *info = GetTextureShaderInfo(value);178if (info) {179auto ts = GetI18NCategory(I18NCat::TEXTURESHADERS);180return std::string(ts->T(value, info->name.c_str()));181} else {182return std::string(value);183}184}185186static std::string *GPUDeviceNameSetting() {187if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {188return &g_Config.sVulkanDevice;189}190#ifdef _WIN32191if (g_Config.iGPUBackend == (int)GPUBackend::DIRECT3D11) {192return &g_Config.sD3D11Device;193}194#endif195return nullptr;196}197198bool PathToVisualUsbPath(Path path, std::string &outPath) {199switch (path.Type()) {200case PathType::NATIVE:201if (path.StartsWith(g_Config.memStickDirectory)) {202return g_Config.memStickDirectory.ComputePathTo(path, outPath);203}204break;205case PathType::CONTENT_URI:206#if PPSSPP_PLATFORM(ANDROID)207{208// Try to parse something sensible out of the content URI.209AndroidContentURI uri(path.ToString());210outPath = uri.RootPath();211if (startsWith(outPath, "primary:")) {212outPath = "/" + outPath.substr(8);213}214return true;215}216#endif217default:218break;219}220return false;221}222223static std::string PostShaderTranslateName(std::string_view value) {224const ShaderInfo *info = GetPostShaderInfo(value);225if (info) {226auto ps = GetI18NCategory(I18NCat::POSTSHADERS);227return std::string(ps->T(value, info->name));228} else {229return std::string(value);230}231}232233void GameSettingsScreen::PreCreateViews() {234ReloadAllPostShaderInfo(screenManager()->getDrawContext());235ReloadAllThemeInfo();236237if (editThenRestore_) {238std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);239g_Config.loadGameConfig(gameID_, info->GetTitle());240}241242iAlternateSpeedPercent1_ = g_Config.iFpsLimit1 < 0 ? -1 : (g_Config.iFpsLimit1 * 100) / 60;243iAlternateSpeedPercent2_ = g_Config.iFpsLimit2 < 0 ? -1 : (g_Config.iFpsLimit2 * 100) / 60;244iAlternateSpeedPercentAnalog_ = (g_Config.iAnalogFpsLimit * 100) / 60;245}246247void GameSettingsScreen::CreateTabs() {248using namespace UI;249auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);250251LinearLayout *graphicsSettings = AddTab("GameSettingsGraphics", ms->T("Graphics"));252CreateGraphicsSettings(graphicsSettings);253254LinearLayout *controlsSettings = AddTab("GameSettingsControls", ms->T("Controls"));255CreateControlsSettings(controlsSettings);256257LinearLayout *audioSettings = AddTab("GameSettingsAudio", ms->T("Audio"));258CreateAudioSettings(audioSettings);259260LinearLayout *networkingSettings = AddTab("GameSettingsNetworking", ms->T("Networking"));261CreateNetworkingSettings(networkingSettings);262263LinearLayout *tools = AddTab("GameSettingsTools", ms->T("Tools"));264CreateToolsSettings(tools);265266LinearLayout *systemSettings = AddTab("GameSettingsSystem", ms->T("System"));267systemSettings->SetSpacing(0);268CreateSystemSettings(systemSettings);269270int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);271if ((deviceType == DEVICE_TYPE_VR) || g_Config.bForceVR) {272LinearLayout *vrSettings = AddTab("GameSettingsVR", ms->T("VR"));273CreateVRSettings(vrSettings);274}275}276277// Graphics278void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) {279auto gr = GetI18NCategory(I18NCat::GRAPHICS);280auto vr = GetI18NCategory(I18NCat::VR);281282using namespace UI;283284graphicsSettings->Add(new ItemHeader(gr->T("Rendering Mode")));285286Draw::DrawContext *draw = screenManager()->getDrawContext();287288#if !PPSSPP_PLATFORM(UWP)289static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" };290PopupMultiChoice *renderingBackendChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, (int)GPUBackend::OPENGL, ARRAY_SIZE(renderingBackend), I18NCat::GRAPHICS, screenManager()));291if (g_Config.iGPUBackend != (int)GPUBackend::DIRECT3D9 && !draw->GetDeviceCaps().supportsD3D9) {292renderingBackendChoice->HideChoice(1);293}294renderingBackendChoice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingBackend);295296if (!g_Config.IsBackendEnabled(GPUBackend::OPENGL))297renderingBackendChoice->HideChoice((int)GPUBackend::OPENGL);298if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D9))299renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D9);300if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11))301renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D11);302if (!g_Config.IsBackendEnabled(GPUBackend::VULKAN))303renderingBackendChoice->HideChoice((int)GPUBackend::VULKAN);304305if (!IsFirstInstance()) {306// If we're not the first instance, can't save the setting, and it requires a restart, so...307renderingBackendChoice->SetEnabled(false);308}309#endif310311// Backends that don't allow a device choice will only expose one device.312if (draw->GetDeviceList().size() > 1) {313std::string *deviceNameSetting = GPUDeviceNameSetting();314if (deviceNameSetting) {315PopupMultiChoiceDynamic *deviceChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(deviceNameSetting, gr->T("Device"), draw->GetDeviceList(), I18NCat::NONE, screenManager()));316deviceChoice->OnChoice.Handle(this, &GameSettingsScreen::OnRenderingDevice);317}318}319320static const char *internalResolutions[] = { "Auto (1:1)", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP", "6x PSP", "7x PSP", "8x PSP", "9x PSP", "10x PSP" };321resolutionChoice_ = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iInternalResolution, gr->T("Rendering Resolution"), internalResolutions, 0, ARRAY_SIZE(internalResolutions), I18NCat::GRAPHICS, screenManager()));322resolutionChoice_->OnChoice.Handle(this, &GameSettingsScreen::OnResolutionChange);323resolutionChoice_->SetEnabledFunc([] {324return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects;325});326327int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);328329if (deviceType != DEVICE_TYPE_VR) {330CheckBox *softwareGPU = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareRendering, gr->T("Software Rendering", "Software Rendering (slow)")));331softwareGPU->SetEnabled(!PSP_IsInited());332}333334if (draw->GetDeviceCaps().multiSampleLevelsMask != 1) {335static const char *msaaModes[] = { "Off", "2x", "4x", "8x", "16x" };336auto msaaChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iMultiSampleLevel, gr->T("Antialiasing (MSAA)"), msaaModes, 0, ARRAY_SIZE(msaaModes), I18NCat::GRAPHICS, screenManager()));337msaaChoice->OnChoice.Add([&](UI::EventParams &) -> UI::EventReturn {338System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);339return UI::EVENT_DONE;340});341msaaChoice->SetEnabledFunc([] {342return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects;343});344if (g_Config.iMultiSampleLevel > 1 && draw->GetDeviceCaps().isTilingGPU) {345msaaChoice->SetIcon(ImageID("I_WARNING"), 0.7f);346}347msaaChoice->SetEnabledFunc([] {348return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects;349});350351// Hide unsupported levels.352for (int i = 1; i < 5; i++) {353if ((draw->GetDeviceCaps().multiSampleLevelsMask & (1 << i)) == 0) {354msaaChoice->HideChoice(i);355} else if (i > 0 && draw->GetDeviceCaps().isTilingGPU) {356msaaChoice->SetChoiceIcon(i, ImageID("I_WARNING"));357}358}359} else {360g_Config.iMultiSampleLevel = 0;361}362363#if PPSSPP_PLATFORM(ANDROID)364if ((deviceType != DEVICE_TYPE_TV) && (deviceType != DEVICE_TYPE_VR)) {365static const char *deviceResolutions[] = { "Native device resolution", "Same as Rendering resolution", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP" };366int max_res_temp = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)) / 480 + 2;367if (max_res_temp == 3)368max_res_temp = 4; // At least allow 2x369int max_res = std::min(max_res_temp, (int)ARRAY_SIZE(deviceResolutions));370UI::PopupMultiChoice *hwscale = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAndroidHwScale, gr->T("Display Resolution (HW scaler)"), deviceResolutions, 0, max_res, I18NCat::GRAPHICS, screenManager()));371hwscale->OnChoice.Add([](UI::EventParams &) {372System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);373System_PostUIMessage(UIMessage::GPU_DISPLAY_RESIZED);374System_RecreateActivity();375return UI::EVENT_DONE;376});377}378#endif379380if (deviceType != DEVICE_TYPE_VR) {381#if !defined(MOBILE_DEVICE)382graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange);383if (System_GetPropertyInt(SYSPROP_DISPLAY_COUNT) > 1) {384CheckBox *fullscreenMulti = new CheckBox(&g_Config.bFullScreenMulti, gr->T("Use all displays"));385fullscreenMulti->SetEnabledFunc([] {386return g_Config.UseFullScreen();387});388graphicsSettings->Add(fullscreenMulti)->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenMultiChange);389}390#endif391392// All backends support FIFO. Check if any immediate modes are supported, if so we can allow the user to choose.393if (draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::IMMEDIATE | Draw::PresentMode::MAILBOX)) {394CheckBox *vSync = graphicsSettings->Add(new CheckBox(&g_Config.bVSync, gr->T("VSync")));395vSync->OnClick.Add([=](EventParams &e) {396NativeResized();397return UI::EVENT_CONTINUE;398});399}400401#if PPSSPP_PLATFORM(ANDROID)402// Hide Immersive Mode on pre-kitkat Android403if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19) {404// Let's reuse the Fullscreen translation string from desktop.405graphicsSettings->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnImmersiveModeChange);406}407#endif408// Display Layout Editor: To avoid overlapping touch controls on large tablets, meet geeky demands for integer zoom/unstretched image etc.409displayEditor_ = graphicsSettings->Add(new Choice(gr->T("Display layout & effects")));410displayEditor_->OnClick.Add([&](UI::EventParams &) -> UI::EventReturn {411screenManager()->push(new DisplayLayoutScreen(gamePath_));412return UI::EVENT_DONE;413});414}415416graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control")));417static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"};418graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkip, gr->T("Frame Skipping"), frameSkip, 0, ARRAY_SIZE(frameSkip), I18NCat::GRAPHICS, screenManager()));419static const char *frameSkipType[] = {"Number of Frames", "Percent of FPS"};420graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkipType, gr->T("Frame Skipping Type"), frameSkipType, 0, ARRAY_SIZE(frameSkipType), I18NCat::GRAPHICS, screenManager()));421frameSkipAuto_ = graphicsSettings->Add(new CheckBox(&g_Config.bAutoFrameSkip, gr->T("Auto FrameSkip")));422frameSkipAuto_->OnClick.Handle(this, &GameSettingsScreen::OnAutoFrameskip);423424PopupSliderChoice *altSpeed1 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent1_, 0, 1000, NO_DEFAULT_INT, gr->T("Alternative Speed", "Alternative speed"), 5, screenManager(), gr->T("%, 0:unlimited")));425altSpeed1->SetFormat("%i%%");426altSpeed1->SetZeroLabel(gr->T("Unlimited"));427altSpeed1->SetNegativeDisable(gr->T("Disabled"));428429PopupSliderChoice *altSpeed2 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent2_, 0, 1000, NO_DEFAULT_INT, gr->T("Alternative Speed 2", "Alternative speed 2 (in %, 0 = unlimited)"), 5, screenManager(), gr->T("%, 0:unlimited")));430altSpeed2->SetFormat("%i%%");431altSpeed2->SetZeroLabel(gr->T("Unlimited"));432altSpeed2->SetNegativeDisable(gr->T("Disabled"));433434if (analogSpeedMapped_) {435PopupSliderChoice *analogSpeed = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercentAnalog_, 1, 1000, NO_DEFAULT_INT, gr->T("Analog Alternative Speed", "Analog alternative speed (in %)"), 5, screenManager(), gr->T("%")));436altSpeed2->SetFormat("%i%%");437}438439graphicsSettings->Add(new ItemHeader(gr->T("Speed Hacks", "Speed Hacks (can cause rendering errors!)")));440441CheckBox *skipBufferEffects = graphicsSettings->Add(new CheckBox(&g_Config.bSkipBufferEffects, gr->T("Skip Buffer Effects")));442skipBufferEffects->OnClick.Add([=](EventParams &e) {443if (g_Config.bSkipBufferEffects) {444settingInfo_->Show(gr->T("RenderingMode NonBuffered Tip", "Faster, but graphics may be missing in some games"), e.v);445g_Config.bAutoFrameSkip = false;446}447System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);448return UI::EVENT_DONE;449});450skipBufferEffects->SetDisabledPtr(&g_Config.bSoftwareRendering);451452CheckBox *disableCulling = graphicsSettings->Add(new CheckBox(&g_Config.bDisableRangeCulling, gr->T("Disable culling")));453disableCulling->SetDisabledPtr(&g_Config.bSoftwareRendering);454455static const char *skipGpuReadbackModes[] = { "No (default)", "Skip", "Copy to texture" };456457PopupMultiChoice *skipGPUReadbacks = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iSkipGPUReadbackMode, gr->T("Skip GPU Readbacks"), skipGpuReadbackModes, 0, ARRAY_SIZE(skipGpuReadbackModes), I18NCat::GRAPHICS, screenManager()));458skipGPUReadbacks->SetDisabledPtr(&g_Config.bSoftwareRendering);459460CheckBox *texBackoff = graphicsSettings->Add(new CheckBox(&g_Config.bTextureBackoffCache, gr->T("Lazy texture caching", "Lazy texture caching (speedup)")));461texBackoff->SetDisabledPtr(&g_Config.bSoftwareRendering);462texBackoff->OnClick.Add([=](EventParams& e) {463settingInfo_->Show(gr->T("Lazy texture caching Tip", "Faster, but can cause text problems in a few games"), e.v);464return UI::EVENT_CONTINUE;465});466467static const char *quality[] = { "Low", "Medium", "High" };468PopupMultiChoice *beziersChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iSplineBezierQuality, gr->T("LowCurves", "Spline/Bezier curves quality"), quality, 0, ARRAY_SIZE(quality), I18NCat::GRAPHICS, screenManager()));469beziersChoice->OnChoice.Add([=](EventParams &e) {470if (g_Config.iSplineBezierQuality != 0) {471settingInfo_->Show(gr->T("LowCurves Tip", "Only used by some games, controls smoothness of curves"), e.v);472}473return UI::EVENT_CONTINUE;474});475476graphicsSettings->Add(new ItemHeader(gr->T("Performance")));477CheckBox *frameDuplication = graphicsSettings->Add(new CheckBox(&g_Config.bRenderDuplicateFrames, gr->T("Render duplicate frames to 60hz")));478frameDuplication->OnClick.Add([=](EventParams &e) {479settingInfo_->Show(gr->T("RenderDuplicateFrames Tip", "Can make framerate smoother in games that run at lower framerates"), e.v);480return UI::EVENT_CONTINUE;481});482frameDuplication->SetEnabledFunc([] {483return !g_Config.bSkipBufferEffects && g_Config.iFrameSkip == 0;484});485486if (draw->GetDeviceCaps().setMaxFrameLatencySupported) {487static const char *bufferOptions[] = { "No buffer", "Up to 1", "Up to 2" };488PopupMultiChoice *inflightChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iInflightFrames, gr->T("Buffer graphics commands (faster, input lag)"), bufferOptions, 1, ARRAY_SIZE(bufferOptions), I18NCat::GRAPHICS, screenManager()));489inflightChoice->OnChoice.Handle(this, &GameSettingsScreen::OnInflightFramesChoice);490}491492if (GetGPUBackend() == GPUBackend::VULKAN) {493const bool usable = draw->GetDeviceCaps().geometryShaderSupported && !draw->GetBugs().Has(Draw::Bugs::GEOMETRY_SHADERS_SLOW_OR_BROKEN);494const bool vertexSupported = draw->GetDeviceCaps().clipDistanceSupported && draw->GetDeviceCaps().cullDistanceSupported;495if (usable && !vertexSupported) {496CheckBox *geometryCulling = graphicsSettings->Add(new CheckBox(&g_Config.bUseGeometryShader, gr->T("Geometry shader culling")));497geometryCulling->SetDisabledPtr(&g_Config.bSoftwareRendering);498}499}500501if (deviceType != DEVICE_TYPE_VR) {502CheckBox *hwTransform = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTransform, gr->T("Hardware Transform")));503hwTransform->SetDisabledPtr(&g_Config.bSoftwareRendering);504}505506CheckBox *swSkin = graphicsSettings->Add(new CheckBox(&g_Config.bSoftwareSkinning, gr->T("Software Skinning")));507swSkin->OnClick.Add([=](EventParams &e) {508settingInfo_->Show(gr->T("SoftwareSkinning Tip", "Combine skinned model draws on the CPU, faster in most games"), e.v);509return UI::EVENT_CONTINUE;510});511swSkin->SetDisabledPtr(&g_Config.bSoftwareRendering);512513CheckBox *tessellationHW = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTessellation, gr->T("Hardware Tessellation")));514tessellationHW->OnClick.Add([=](EventParams &e) {515settingInfo_->Show(gr->T("HardwareTessellation Tip", "Uses hardware to make curves"), e.v);516return UI::EVENT_CONTINUE;517});518519tessellationHW->SetEnabledFunc([]() {520return DoesBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform;521});522523// In case we're going to add few other antialiasing option like MSAA in the future.524// graphicsSettings->Add(new CheckBox(&g_Config.bFXAA, gr->T("FXAA")));525graphicsSettings->Add(new ItemHeader(gr->T("Texture Scaling")));526#ifndef MOBILE_DEVICE527static const char *texScaleLevels[] = {"Off", "2x", "3x", "4x", "5x"};528#else529static const char *texScaleLevels[] = {"Off", "2x", "3x"};530#endif531532static const char *texScaleAlgos[] = { "xBRZ", "Hybrid", "Bicubic", "Hybrid + Bicubic", };533PopupMultiChoice *texScalingType = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iTexScalingType, gr->T("Upscale Type"), texScaleAlgos, 0, ARRAY_SIZE(texScaleAlgos), I18NCat::GRAPHICS, screenManager()));534texScalingType->SetEnabledFunc([]() {535return !g_Config.bSoftwareRendering && !UsingHardwareTextureScaling();536});537PopupMultiChoice *texScalingChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iTexScalingLevel, gr->T("Upscale Level"), texScaleLevels, 1, ARRAY_SIZE(texScaleLevels), I18NCat::GRAPHICS, screenManager()));538// TODO: Better check? When it won't work, it scales down anyway.539if (!gl_extensions.OES_texture_npot && GetGPUBackend() == GPUBackend::OPENGL) {540texScalingChoice->HideChoice(3); // 3x541texScalingChoice->HideChoice(5); // 5x542}543texScalingChoice->OnChoice.Add([=](EventParams &e) {544if (g_Config.iTexScalingLevel != 1 && !UsingHardwareTextureScaling()) {545settingInfo_->Show(gr->T("UpscaleLevel Tip", "CPU heavy - some scaling may be delayed to avoid stutter"), e.v);546}547return UI::EVENT_CONTINUE;548});549texScalingChoice->SetEnabledFunc([]() {550return !g_Config.bSoftwareRendering && !UsingHardwareTextureScaling();551});552553CheckBox *deposterize = graphicsSettings->Add(new CheckBox(&g_Config.bTexDeposterize, gr->T("Deposterize")));554deposterize->OnClick.Add([=](EventParams &e) {555if (g_Config.bTexDeposterize == true) {556settingInfo_->Show(gr->T("Deposterize Tip", "Fixes visual banding glitches in upscaled textures"), e.v);557}558return UI::EVENT_CONTINUE;559});560deposterize->SetEnabledFunc([]() {561return !g_Config.bSoftwareRendering && !UsingHardwareTextureScaling();562});563564ChoiceWithValueDisplay *textureShaderChoice = graphicsSettings->Add(new ChoiceWithValueDisplay(&g_Config.sTextureShaderName, gr->T("Texture Shader"), &TextureTranslateName));565textureShaderChoice->OnClick.Handle(this, &GameSettingsScreen::OnTextureShader);566textureShaderChoice->SetEnabledFunc([]() {567return GetGPUBackend() == GPUBackend::VULKAN && !g_Config.bSoftwareRendering;568});569570graphicsSettings->Add(new ItemHeader(gr->T("Texture Filtering")));571static const char *anisoLevels[] = { "Off", "2x", "4x", "8x", "16x" };572PopupMultiChoice *anisoFiltering = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAnisotropyLevel, gr->T("Anisotropic Filtering"), anisoLevels, 0, ARRAY_SIZE(anisoLevels), I18NCat::GRAPHICS, screenManager()));573anisoFiltering->SetDisabledPtr(&g_Config.bSoftwareRendering);574575static const char *texFilters[] = { "Auto", "Nearest", "Linear", "Auto Max Quality"};576PopupMultiChoice *filters = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iTexFiltering, gr->T("Texture Filter"), texFilters, 1, ARRAY_SIZE(texFilters), I18NCat::GRAPHICS, screenManager()));577filters->SetDisabledPtr(&g_Config.bSoftwareRendering);578579CheckBox *smartFiltering = graphicsSettings->Add(new CheckBox(&g_Config.bSmart2DTexFiltering, gr->T("Smart 2D texture filtering")));580smartFiltering->SetDisabledPtr(&g_Config.bSoftwareRendering);581582#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)583bool showCardboardSettings = deviceType != DEVICE_TYPE_VR;584#else585// If you enabled it through the ini, you can see this. Useful for testing.586bool showCardboardSettings = g_Config.bEnableCardboardVR;587#endif588if (showCardboardSettings) {589graphicsSettings->Add(new ItemHeader(gr->T("Cardboard VR Settings", "Cardboard VR Settings")));590graphicsSettings->Add(new CheckBox(&g_Config.bEnableCardboardVR, gr->T("Enable Cardboard VR", "Enable Cardboard VR")));591PopupSliderChoice *cardboardScreenSize = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iCardboardScreenSize, 30, 150, 50, gr->T("Cardboard Screen Size", "Screen Size (in % of the viewport)"), 1, screenManager(), gr->T("% of viewport")));592cardboardScreenSize->SetEnabledPtr(&g_Config.bEnableCardboardVR);593PopupSliderChoice *cardboardXShift = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iCardboardXShift, -150, 150, 0, gr->T("Cardboard Screen X Shift", "X Shift (in % of the void)"), 1, screenManager(), gr->T("% of the void")));594cardboardXShift->SetEnabledPtr(&g_Config.bEnableCardboardVR);595PopupSliderChoice *cardboardYShift = graphicsSettings->Add(new PopupSliderChoice(&g_Config.iCardboardYShift, -100, 100, 0, gr->T("Cardboard Screen Y Shift", "Y Shift (in % of the void)"), 1, screenManager(), gr->T("% of the void")));596cardboardYShift->SetEnabledPtr(&g_Config.bEnableCardboardVR);597}598599std::vector<std::string> cameraList = Camera::getDeviceList();600if (cameraList.size() >= 1) {601graphicsSettings->Add(new ItemHeader(gr->T("Camera")));602PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sCameraDevice, gr->T("Camera Device"), cameraList, I18NCat::NONE, screenManager()));603cameraChoice->OnChoice.Handle(this, &GameSettingsScreen::OnCameraDeviceChange);604#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)605graphicsSettings->Add(new CheckBox(&g_Config.bCameraMirrorHorizontal, gr->T("Mirror camera image")));606#endif607}608609graphicsSettings->Add(new ItemHeader(gr->T("Hack Settings", "Hack Settings (these WILL cause glitches)")));610611static const char *bloomHackOptions[] = { "Off", "Safe", "Balanced", "Aggressive" };612PopupMultiChoice *bloomHack = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iBloomHack, gr->T("Lower resolution for effects (reduces artifacts)"), bloomHackOptions, 0, ARRAY_SIZE(bloomHackOptions), I18NCat::GRAPHICS, screenManager()));613bloomHack->SetEnabledFunc([] {614return !g_Config.bSoftwareRendering && g_Config.iInternalResolution != 1;615});616617graphicsSettings->Add(new ItemHeader(gr->T("Overlay Information")));618BitCheckBox *showFPSCtr = graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::FPS_COUNTER, gr->T("Show FPS Counter")));619BitCheckBox *showSpeed = graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::SPEED_COUNTER, gr->T("Show Speed")));620#ifdef CAN_DISPLAY_CURRENT_BATTERY_CAPACITY621BitCheckBox *showBattery = graphicsSettings->Add(new BitCheckBox(&g_Config.iShowStatusFlags, (int)ShowStatusFlags::BATTERY_PERCENT, gr->T("Show Battery %")));622#endif623AddOverlayList(graphicsSettings, screenManager());624}625626void GameSettingsScreen::CreateAudioSettings(UI::ViewGroup *audioSettings) {627using namespace UI;628629auto a = GetI18NCategory(I18NCat::AUDIO);630auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);631auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);632633audioSettings->Add(new ItemHeader(ms->T("Audio")));634CheckBox *enableSound = audioSettings->Add(new CheckBox(&g_Config.bEnableSound,a->T("Enable Sound")));635636#if PPSSPP_PLATFORM(IOS)637CheckBox *respectSilentMode = audioSettings->Add(new CheckBox(&g_Config.bAudioRespectSilentMode, a->T("Respect silent mode")));638respectSilentMode->OnClick.Add([=](EventParams &e) {639System_Notify(SystemNotification::AUDIO_MODE_CHANGED);640return UI::EVENT_DONE;641});642respectSilentMode->SetEnabledPtr(&g_Config.bEnableSound);643CheckBox *mixWithOthers = audioSettings->Add(new CheckBox(&g_Config.bAudioMixWithOthers, a->T("Mix audio with other apps")));644mixWithOthers->OnClick.Add([=](EventParams &e) {645System_Notify(SystemNotification::AUDIO_MODE_CHANGED);646return UI::EVENT_DONE;647});648mixWithOthers->SetEnabledPtr(&g_Config.bEnableSound);649#endif650651PopupSliderChoice *volume = audioSettings->Add(new PopupSliderChoice(&g_Config.iGlobalVolume, VOLUME_OFF, VOLUME_FULL, VOLUME_FULL, a->T("Global volume"), screenManager()));652volume->SetEnabledPtr(&g_Config.bEnableSound);653volume->SetZeroLabel(a->T("Mute"));654655PopupSliderChoice *altVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iAltSpeedVolume, VOLUME_OFF, VOLUME_FULL, NO_DEFAULT_INT, a->T("Alternate speed volume"), screenManager()));656altVolume->SetEnabledPtr(&g_Config.bEnableSound);657altVolume->SetZeroLabel(a->T("Mute"));658altVolume->SetNegativeDisable(a->T("Use global volume"));659660PopupSliderChoice *reverbVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iReverbVolume, VOLUME_OFF, 2 * VOLUME_FULL, VOLUME_FULL, a->T("Reverb volume"), screenManager()));661reverbVolume->SetEnabledPtr(&g_Config.bEnableSound);662reverbVolume->SetZeroLabel(a->T("Disabled"));663664PopupSliderChoice *achievementVolume = audioSettings->Add(new PopupSliderChoice(&g_Config.iAchievementSoundVolume, VOLUME_OFF, VOLUME_FULL, VOLUME_FULL, ac->T("Achievement sound volume"), screenManager()));665achievementVolume->SetEnabledPtr(&g_Config.bEnableSound);666achievementVolume->SetZeroLabel(a->T("Mute"));667668// Hide the backend selector in UWP builds (we only support XAudio2 there).669#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)670if (IsVistaOrHigher()) {671static const char *backend[] = { "Auto", "DSound (compatible)", "WASAPI (fast)" };672PopupMultiChoice *audioBackend = audioSettings->Add(new PopupMultiChoice(&g_Config.iAudioBackend, a->T("Audio backend", "Audio backend (restart req.)"), backend, 0, ARRAY_SIZE(backend), I18NCat::AUDIO, screenManager()));673audioBackend->SetEnabledPtr(&g_Config.bEnableSound);674}675#endif676677bool sdlAudio = false;678#if defined(SDL)679std::vector<std::string> audioDeviceList;680SplitString(System_GetProperty(SYSPROP_AUDIO_DEVICE_LIST), '\0', audioDeviceList);681audioDeviceList.insert(audioDeviceList.begin(), a->T_cstr("Auto"));682PopupMultiChoiceDynamic *audioDevice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sAudioDevice, a->T("Device"), audioDeviceList, I18NCat::NONE, screenManager()));683audioDevice->OnChoice.Handle(this, &GameSettingsScreen::OnAudioDevice);684sdlAudio = true;685#endif686687if (sdlAudio || g_Config.iAudioBackend == AUDIO_BACKEND_WASAPI) {688audioSettings->Add(new CheckBox(&g_Config.bAutoAudioDevice, a->T("Use new audio devices automatically")));689}690691#if PPSSPP_PLATFORM(ANDROID)692CheckBox *extraAudio = audioSettings->Add(new CheckBox(&g_Config.bExtraAudioBuffering, a->T("AudioBufferingForBluetooth", "Bluetooth-friendly buffer (slower)")));693extraAudio->SetEnabledPtr(&g_Config.bEnableSound);694695// Show OpenSL debug info696const std::string audioErrorStr = AndroidAudio_GetErrorString(g_audioState);697if (!audioErrorStr.empty()) {698audioSettings->Add(new InfoItem(a->T("Audio Error"), audioErrorStr));699}700#endif701702std::vector<std::string> micList = Microphone::getDeviceList();703if (!micList.empty()) {704audioSettings->Add(new ItemHeader(a->T("Microphone")));705PopupMultiChoiceDynamic *MicChoice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sMicDevice, a->T("Microphone Device"), micList, I18NCat::NONE, screenManager()));706MicChoice->OnChoice.Handle(this, &GameSettingsScreen::OnMicDeviceChange);707}708}709710void GameSettingsScreen::CreateControlsSettings(UI::ViewGroup *controlsSettings) {711using namespace UI;712713auto co = GetI18NCategory(I18NCat::CONTROLS);714auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);715auto di = GetI18NCategory(I18NCat::DIALOG);716717int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);718719controlsSettings->Add(new ItemHeader(ms->T("Controls")));720controlsSettings->Add(new Choice(co->T("Control Mapping")))->OnClick.Handle(this, &GameSettingsScreen::OnControlMapping);721controlsSettings->Add(new Choice(co->T("Calibrate Analog Stick")))->OnClick.Handle(this, &GameSettingsScreen::OnCalibrateAnalogs);722controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fAnalogTriggerThreshold, 0.02f, 0.98f, 0.75f, co->T("Analog trigger threshold"), screenManager()));723724#if defined(USING_WIN_UI) || (PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID))725controlsSettings->Add(new CheckBox(&g_Config.bSystemControls, co->T("Enable standard shortcut keys")));726#endif727#if defined(USING_WIN_UI)728controlsSettings->Add(new CheckBox(&g_Config.bGamepadOnlyFocused, co->T("Ignore gamepads when not focused")));729#endif730731if (System_GetPropertyBool(SYSPROP_HAS_ACCELEROMETER)) {732// Show the tilt type on the item.733Choice *customizeTilt = controlsSettings->Add(new ChoiceWithCallbackValueDisplay(co->T("Tilt control setup"), []() -> std::string {734auto co = GetI18NCategory(I18NCat::CONTROLS);735if ((u32)g_Config.iTiltInputType < (u32)g_numTiltTypes) {736return std::string(co->T(g_tiltTypes[g_Config.iTiltInputType]));737}738return "";739}));740customizeTilt->OnClick.Handle(this, &GameSettingsScreen::OnTiltCustomize);741} else if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_VR) { // TODO: This seems like a regression742controlsSettings->Add(new CheckBox(&g_Config.bHapticFeedback, co->T("HapticFeedback", "Haptic Feedback (vibration)")));743}744745// TVs don't have touch control, at least not yet.746if ((deviceType != DEVICE_TYPE_TV) && (deviceType != DEVICE_TYPE_VR)) {747controlsSettings->Add(new ItemHeader(co->T("OnScreen", "On-Screen Touch Controls")));748controlsSettings->Add(new CheckBox(&g_Config.bShowTouchControls, co->T("OnScreen", "On-Screen Touch Controls")));749layoutEditorChoice_ = controlsSettings->Add(new Choice(co->T("Customize Touch Controls")));750layoutEditorChoice_->OnClick.Handle(this, &GameSettingsScreen::OnTouchControlLayout);751layoutEditorChoice_->SetEnabledPtr(&g_Config.bShowTouchControls);752753Choice *gesture = controlsSettings->Add(new Choice(co->T("Gesture mapping")));754gesture->OnClick.Add([=](EventParams &e) {755screenManager()->push(new GestureMappingScreen(gamePath_));756return UI::EVENT_DONE;757});758gesture->SetEnabledPtr(&g_Config.bShowTouchControls);759760static const char *touchControlStyles[] = { "Classic", "Thin borders", "Glowing borders" };761View *style = controlsSettings->Add(new PopupMultiChoice(&g_Config.iTouchButtonStyle, co->T("Button style"), touchControlStyles, 0, ARRAY_SIZE(touchControlStyles), I18NCat::CONTROLS, screenManager()));762style->SetEnabledPtr(&g_Config.bShowTouchControls);763764PopupSliderChoice *opacity = controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonOpacity, 0, 100, 65, co->T("Button Opacity"), screenManager(), "%"));765opacity->SetEnabledPtr(&g_Config.bShowTouchControls);766opacity->SetFormat("%i%%");767PopupSliderChoice *autoHide = controlsSettings->Add(new PopupSliderChoice(&g_Config.iTouchButtonHideSeconds, 0, 300, 20, co->T("Auto-hide buttons after delay"), screenManager(), di->T("seconds, 0:off")));768autoHide->SetEnabledPtr(&g_Config.bShowTouchControls);769autoHide->SetFormat(di->T("%d seconds"));770autoHide->SetZeroLabel(co->T("Off"));771772// Hide stick background, useful when increasing the size773CheckBox *hideStickBackground = controlsSettings->Add(new CheckBox(&g_Config.bHideStickBackground, co->T("Hide touch analog stick background circle")));774hideStickBackground->SetEnabledPtr(&g_Config.bShowTouchControls);775776// Sticky D-pad.777CheckBox *stickyDpad = controlsSettings->Add(new CheckBox(&g_Config.bStickyTouchDPad, co->T("Sticky D-Pad (easier sweeping movements)")));778stickyDpad->SetEnabledPtr(&g_Config.bShowTouchControls);779780// Re-centers itself to the touch location on touch-down.781CheckBox *floatingAnalog = controlsSettings->Add(new CheckBox(&g_Config.bAutoCenterTouchAnalog, co->T("Auto-centering analog stick")));782floatingAnalog->SetEnabledPtr(&g_Config.bShowTouchControls);783784if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {785controlsSettings->Add(new CheckBox(&g_Config.bHapticFeedback, co->T("HapticFeedback", "Haptic Feedback (vibration)")));786}787788// On non iOS systems, offer to let the user see this button.789// Some Windows touch devices don't have a back button or other button to call up the menu.790if (System_GetPropertyBool(SYSPROP_HAS_BACK_BUTTON)) {791CheckBox *enablePauseBtn = controlsSettings->Add(new CheckBox(&g_Config.bShowTouchPause, co->T("Show Touch Pause Menu Button")));792793// Don't allow the user to disable it once in-game, so they can't lock themselves out of the menu.794if (!PSP_IsInited()) {795enablePauseBtn->SetEnabledPtr(&g_Config.bShowTouchControls);796} else {797enablePauseBtn->SetEnabled(false);798}799}800801CheckBox *disableDiags = controlsSettings->Add(new CheckBox(&g_Config.bDisableDpadDiagonals, co->T("Disable D-Pad diagonals (4-way touch)")));802disableDiags->SetEnabledPtr(&g_Config.bShowTouchControls);803}804805if (deviceType != DEVICE_TYPE_VR) {806controlsSettings->Add(new ItemHeader(co->T("Keyboard", "Keyboard Control Settings")));807#if defined(USING_WIN_UI)808controlsSettings->Add(new CheckBox(&g_Config.bIgnoreWindowsKey, co->T("Ignore Windows Key")));809#endif // #if defined(USING_WIN_UI)810auto analogLimiter = new PopupSliderChoiceFloat(&g_Config.fAnalogLimiterDeadzone, 0.0f, 1.0f, 0.6f, co->T("Analog Limiter"), 0.10f, screenManager(), "/ 1.0");811controlsSettings->Add(analogLimiter);812analogLimiter->OnChange.Add([=](EventParams &e) {813settingInfo_->Show(co->T("AnalogLimiter Tip", "When the analog limiter button is pressed"), e.v);814return UI::EVENT_CONTINUE;815});816controlsSettings->Add(new PopupSliderChoice(&g_Config.iRapidFireInterval, 1, 10, 5, co->T("Rapid fire interval"), screenManager(), "frames"));817#if defined(USING_WIN_UI) || defined(SDL) || PPSSPP_PLATFORM(ANDROID)818bool enableMouseSettings = true;819#if PPSSPP_PLATFORM(ANDROID)820if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) < 12) {821enableMouseSettings = false;822}823#endif824#else825bool enableMouseSettings = false;826#endif827if (enableMouseSettings) {828// The mousewheel button-release setting is independent of actual mouse delta control.829controlsSettings->Add(new ItemHeader(co->T("Mouse", "Mouse settings")));830auto wheelUpDelaySlider = controlsSettings->Add(new PopupSliderChoice(&g_Config.iMouseWheelUpDelayMs, 10, 300, 1, co->T("Mouse wheel button-release delay"), screenManager()));831wheelUpDelaySlider->SetFormat(di->T("%d ms"));832833CheckBox *mouseControl = controlsSettings->Add(new CheckBox(&g_Config.bMouseControl, co->T("Use Mouse Control")));834mouseControl->OnClick.Add([=](EventParams &e) {835if (g_Config.bMouseControl)836settingInfo_->Show(co->T("MouseControl Tip", "You can now map mouse in control mapping screen by pressing the 'M' icon."), e.v);837return UI::EVENT_CONTINUE;838});839#if !PPSSPP_PLATFORM(ANDROID)840controlsSettings->Add(new CheckBox(&g_Config.bMouseConfine, co->T("Confine Mouse", "Trap mouse within window/display area")))->SetEnabledPtr(&g_Config.bMouseControl);841#endif842auto sensitivitySlider = controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSensitivity, 0.01f, 1.0f, 0.1f, co->T("Mouse sensitivity"), 0.01f, screenManager(), "x"));843sensitivitySlider->SetEnabledPtr(&g_Config.bMouseControl);844sensitivitySlider->SetLiveUpdate(true);845auto smoothingSlider = controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fMouseSmoothing, 0.0f, 0.95f, 0.9f, co->T("Mouse smoothing"), 0.05f, screenManager(), "x"));846smoothingSlider->SetEnabledPtr(&g_Config.bMouseControl);847smoothingSlider->SetLiveUpdate(true);848}849}850}851852// Compound view just like the audio file choosers853class MacAddressChooser : public UI::LinearLayout {854public:855MacAddressChooser(RequesterToken token, Path gamePath, std::string *value, std::string_view title, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr);856};857858static constexpr UI::Size ITEM_HEIGHT = 64.f;859860MacAddressChooser::MacAddressChooser(RequesterToken token, Path gamePath_, std::string *value, std::string_view title, ScreenManager *screenManager, UI::LayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_HORIZONTAL, layoutParams) {861using namespace UI;862SetSpacing(5.0f);863if (!layoutParams) {864layoutParams_->width = FILL_PARENT;865layoutParams_->height = ITEM_HEIGHT;866}867auto n = GetI18NCategory(I18NCat::NETWORKING);868869std::string initialValue = *value;870Add(new PopupTextInputChoice(token, value, title, g_Config.sMACAddress, 17, screenManager, new LinearLayoutParams(1.0f)))->OnChange.Add([=](UI::EventParams &e) {871// Validate the chosen address, and restore to initialValue if bad.872if (g_Config.sMACAddress.size() != 17) {873// TODO: Alert the user874*value = initialValue;875}876return UI::EVENT_DONE;877});878Add(new Choice(n->T("Randomize"), new LinearLayoutParams(WRAP_CONTENT, ITEM_HEIGHT)))->OnClick.Add([=](UI::EventParams &) {879auto n = GetI18NCategory(I18NCat::NETWORKING);880auto di = GetI18NCategory(I18NCat::DIALOG);881882std::string_view confirmMessage = n->T("ChangeMacSaveConfirm", "Generate a new MAC address?");883std::string_view warningMessage = n->T("ChangeMacSaveWarning", "Some games verify the MAC address when loading savedata, so this may break old saves.");884std::string combined = g_Config.sMACAddress + "\n\n" + std::string(confirmMessage) + "\n\n" + std::string(warningMessage);885886auto confirmScreen = new PromptScreen(887gamePath_,888combined, di->T("Yes"), di->T("No"),889[&](bool success) {890if (success) {891g_Config.sMACAddress = CreateRandMAC();892}}893);894screenManager->push(confirmScreen);895return UI::EVENT_DONE;896});897}898899void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSettings) {900using namespace UI;901902auto n = GetI18NCategory(I18NCat::NETWORKING);903auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);904905networkingSettings->Add(new ItemHeader(ms->T("Networking")));906907networkingSettings->Add(new Choice(n->T("Open PPSSPP Multiplayer Wiki Page")))->OnClick.Handle(this, &GameSettingsScreen::OnAdhocGuides);908909networkingSettings->Add(new CheckBox(&g_Config.bEnableWlan, n->T("Enable networking", "Enable networking/wlan (beta)")));910networkingSettings->Add(new MacAddressChooser(GetRequesterToken(), gamePath_, &g_Config.sMACAddress, n->T("Change Mac Address"), screenManager()));911static const char* wlanChannels[] = { "Auto", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11" };912auto wlanChannelChoice = networkingSettings->Add(new PopupMultiChoice(&g_Config.iWlanAdhocChannel, n->T("WLAN Channel"), wlanChannels, 0, ARRAY_SIZE(wlanChannels), I18NCat::NETWORKING, screenManager()));913for (int i = 0; i < 4; i++) {914wlanChannelChoice->HideChoice(i + 2);915wlanChannelChoice->HideChoice(i + 7);916}917networkingSettings->Add(new CheckBox(&g_Config.bDiscordPresence, n->T("Send Discord Presence information")));918919networkingSettings->Add(new ItemHeader(n->T("AdHoc Server")));920networkingSettings->Add(new CheckBox(&g_Config.bEnableAdhocServer, n->T("Enable built-in PRO Adhoc Server", "Enable built-in PRO Adhoc Server")));921networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address", "Change proAdhocServer Address (localhost = multiple instance)"), I18NCat::NONE))->OnClick.Handle(this, &GameSettingsScreen::OnChangeproAdhocServerAddress);922923networkingSettings->Add(new ItemHeader(n->T("UPnP (port-forwarding)")));924networkingSettings->Add(new CheckBox(&g_Config.bEnableUPnP, n->T("Enable UPnP", "Enable UPnP (need a few seconds to detect)")));925auto useOriPort = networkingSettings->Add(new CheckBox(&g_Config.bUPnPUseOriginalPort, n->T("UPnP use original port", "UPnP use original port (Enabled = PSP compatibility)")));926useOriPort->OnClick.Add([=](EventParams& e) {927if (g_Config.bUPnPUseOriginalPort)928settingInfo_->Show(n->T("UseOriginalPort Tip", "May not work for all devices or games, see wiki."), e.v);929return UI::EVENT_CONTINUE;930});931useOriPort->SetEnabledPtr(&g_Config.bEnableUPnP);932933networkingSettings->Add(new ItemHeader(n->T("Chat")));934networkingSettings->Add(new CheckBox(&g_Config.bEnableNetworkChat, n->T("Enable network chat", "Enable network chat")));935static const char *chatButtonPositions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None" };936networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatButtonPosition, n->T("Chat Button Position"), chatButtonPositions, 0, ARRAY_SIZE(chatButtonPositions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat);937static const char *chatScreenPositions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right" };938networkingSettings->Add(new PopupMultiChoice(&g_Config.iChatScreenPosition, n->T("Chat Screen Position"), chatScreenPositions, 0, ARRAY_SIZE(chatScreenPositions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bEnableNetworkChat);939940#if (!defined(MOBILE_DEVICE) && !defined(USING_QT_UI)) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) // Missing only iOS?941networkingSettings->Add(new ItemHeader(n->T("QuickChat", "Quick Chat")));942CheckBox *qc = networkingSettings->Add(new CheckBox(&g_Config.bEnableQuickChat, n->T("EnableQuickChat", "Enable Quick Chat")));943qc->SetEnabledPtr(&g_Config.bEnableNetworkChat);944#endif945946#if !defined(MOBILE_DEVICE) && !defined(USING_QT_UI) // TODO: Add all platforms where KEY_CHAR support is added947PopupTextInputChoice *qc1 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat0, n->T("Quick Chat 1"), "", 32, screenManager()));948PopupTextInputChoice *qc2 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat1, n->T("Quick Chat 2"), "", 32, screenManager()));949PopupTextInputChoice *qc3 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat2, n->T("Quick Chat 3"), "", 32, screenManager()));950PopupTextInputChoice *qc4 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat3, n->T("Quick Chat 4"), "", 32, screenManager()));951PopupTextInputChoice *qc5 = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sQuickChat4, n->T("Quick Chat 5"), "", 32, screenManager()));952#elif defined(USING_QT_UI)953Choice *qc1 = networkingSettings->Add(new Choice(n->T("Quick Chat 1")));954Choice *qc2 = networkingSettings->Add(new Choice(n->T("Quick Chat 2")));955Choice *qc3 = networkingSettings->Add(new Choice(n->T("Quick Chat 3")));956Choice *qc4 = networkingSettings->Add(new Choice(n->T("Quick Chat 4")));957Choice *qc5 = networkingSettings->Add(new Choice(n->T("Quick Chat 5")));958#elif PPSSPP_PLATFORM(ANDROID)959ChoiceWithValueDisplay *qc1 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat0, n->T("Quick Chat 1"), I18NCat::NONE));960ChoiceWithValueDisplay *qc2 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat1, n->T("Quick Chat 2"), I18NCat::NONE));961ChoiceWithValueDisplay *qc3 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat2, n->T("Quick Chat 3"), I18NCat::NONE));962ChoiceWithValueDisplay *qc4 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat3, n->T("Quick Chat 4"), I18NCat::NONE));963ChoiceWithValueDisplay *qc5 = networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sQuickChat4, n->T("Quick Chat 5"), I18NCat::NONE));964#endif965966#if (!defined(MOBILE_DEVICE) && !defined(USING_QT_UI)) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)967qc1->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });968qc2->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });969qc3->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });970qc4->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });971qc5->SetEnabledFunc([] { return g_Config.bEnableQuickChat && g_Config.bEnableNetworkChat; });972#endif973974#if defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)975if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD)) {976qc1->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat0);977qc2->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat1);978qc3->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat2);979qc4->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat3);980qc5->OnClick.Handle(this, &GameSettingsScreen::OnChangeQuickChat4);981}982#endif983984auto di = GetI18NCategory(I18NCat::DIALOG);985986networkingSettings->Add(new ItemHeader(n->T("Misc", "Misc (default = compatibility)")));987networkingSettings->Add(new PopupSliderChoice(&g_Config.iPortOffset, 0, 60000, 10000, n->T("Port offset", "Port offset (0 = PSP compatibility)"), 100, screenManager()));988networkingSettings->Add(new PopupSliderChoice(&g_Config.iMinTimeout, 0, 15000, 0, n->T("Minimum Timeout", "Minimum Timeout (override in ms, 0 = default)"), 50, screenManager()))->SetFormat(di->T("%d ms"));989networkingSettings->Add(new CheckBox(&g_Config.bForcedFirstConnect, n->T("Forced First Connect", "Forced First Connect (faster Connect)")));990}991992void GameSettingsScreen::CreateToolsSettings(UI::ViewGroup *tools) {993using namespace UI;994995auto sa = GetI18NCategory(I18NCat::SAVEDATA);996auto sy = GetI18NCategory(I18NCat::SYSTEM);997auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);998auto dev = GetI18NCategory(I18NCat::DEVELOPER);999auto ri = GetI18NCategory(I18NCat::REMOTEISO);10001001tools->Add(new ItemHeader(ms->T("Tools")));10021003const bool showRetroAchievements = true;1004if (showRetroAchievements) {1005auto retro = tools->Add(new Choice(sy->T("RetroAchievements")));1006retro->OnClick.Add([=](UI::EventParams &) -> UI::EventReturn {1007screenManager()->push(new RetroAchievementsSettingsScreen(gamePath_));1008return UI::EVENT_DONE;1009});1010retro->SetIcon(ImageID("I_RETROACHIEVEMENTS_LOGO"));1011}10121013// These were moved here so use the wrong translation objects, to avoid having to change all inis... This isn't a sustainable situation :P1014tools->Add(new Choice(sa->T("Savedata Manager")))->OnClick.Add([=](UI::EventParams &) {1015screenManager()->push(new SavedataScreen(gamePath_));1016return UI::EVENT_DONE;1017});1018tools->Add(new Choice(dev->T("System Information")))->OnClick.Add([=](UI::EventParams &) {1019screenManager()->push(new SystemInfoScreen(gamePath_));1020return UI::EVENT_DONE;1021});1022tools->Add(new Choice(sy->T("Developer Tools")))->OnClick.Add([=](UI::EventParams &) {1023screenManager()->push(new DeveloperToolsScreen(gamePath_));1024return UI::EVENT_DONE;1025});1026tools->Add(new Choice(ri->T("Remote disc streaming")))->OnClick.Add([=](UI::EventParams &) {1027screenManager()->push(new RemoteISOScreen(gamePath_));1028return UI::EVENT_DONE;1029});1030}10311032void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) {1033using namespace UI;10341035auto sy = GetI18NCategory(I18NCat::SYSTEM);1036auto di = GetI18NCategory(I18NCat::DIALOG);1037auto vr = GetI18NCategory(I18NCat::VR);1038auto th = GetI18NCategory(I18NCat::THEMES);1039auto psps = GetI18NCategory(I18NCat::PSPSETTINGS); // TODO: Should move more into this section.10401041systemSettings->Add(new ItemHeader(sy->T("UI")));10421043auto langCodeToName = [](std::string_view value) -> std::string {1044auto &mapping = g_Config.GetLangValuesMapping();1045auto iter = mapping.find(value);1046if (iter != mapping.end()) {1047return iter->second.first;1048}1049return std::string(value);1050};10511052systemSettings->Add(new ChoiceWithValueDisplay(&g_Config.sLanguageIni, sy->T("Language"), langCodeToName))->OnClick.Add([&](UI::EventParams &e) {1053auto sy = GetI18NCategory(I18NCat::SYSTEM);1054auto langScreen = new NewLanguageScreen(sy->T("Language"));1055langScreen->OnChoice.Add([&](UI::EventParams &e) {1056screenManager()->RecreateAllViews();1057System_Notify(SystemNotification::UI);1058return UI::EVENT_DONE;1059});1060if (e.v)1061langScreen->SetPopupOrigin(e.v);1062screenManager()->push(langScreen);1063return UI::EVENT_DONE;1064});10651066#if PPSSPP_PLATFORM(IOS)1067static const char *indicator[] = {1068"Swipe once to switch app (indicator auto-hides)",1069"Swipe twice to switch app (indicator stays visible)"1070};1071PopupMultiChoice *switchMode = systemSettings->Add(new PopupMultiChoice(&g_Config.iAppSwitchMode, sy->T("App switching mode"), indicator, 0, ARRAY_SIZE(indicator), I18NCat::SYSTEM, screenManager()));1072switchMode->OnChoice.Add([](EventParams &e) {1073System_Notify(SystemNotification::APP_SWITCH_MODE_CHANGED);1074return UI::EVENT_DONE;1075});1076#endif10771078systemSettings->Add(new CheckBox(&g_Config.bUISound, sy->T("UI Sound")));1079const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png";1080const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg";1081if (File::Exists(bgPng) || File::Exists(bgJpg)) {1082backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Clear UI background")));1083} else if (System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER)) {1084backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Set UI background...")));1085} else {1086backgroundChoice_ = nullptr;1087}1088if (backgroundChoice_ != nullptr) {1089backgroundChoice_->OnClick.Handle(this, &GameSettingsScreen::OnChangeBackground);1090}10911092systemSettings->Add(new CheckBox(&g_Config.bTransparentBackground, sy->T("Transparent UI background")));10931094static const char *backgroundAnimations[] = { "No animation", "Floating symbols", "Recent games", "Waves", "Moving background" };1095systemSettings->Add(new PopupMultiChoice(&g_Config.iBackgroundAnimation, sy->T("UI background animation"), backgroundAnimations, 0, ARRAY_SIZE(backgroundAnimations), I18NCat::SYSTEM, screenManager()));10961097PopupMultiChoiceDynamic *theme = systemSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sThemeName, sy->T("Theme"), GetThemeInfoNames(), I18NCat::THEMES, screenManager()));1098theme->OnChoice.Add([=](EventParams &e) {1099UpdateTheme(screenManager()->getUIContext());1100return UI::EVENT_CONTINUE;1101});11021103Draw::DrawContext *draw = screenManager()->getDrawContext();11041105if (!draw->GetBugs().Has(Draw::Bugs::RASPBERRY_SHADER_COMP_HANG)) {1106// We use shaders without tint capability on hardware with this driver bug.1107PopupSliderChoiceFloat *tint = new PopupSliderChoiceFloat(&g_Config.fUITint, 0.0f, 1.0f, 0.0f, sy->T("Color Tint"), 0.01f, screenManager());1108tint->SetHasDropShadow(false);1109tint->SetLiveUpdate(true);1110systemSettings->Add(tint);1111PopupSliderChoiceFloat *saturation = new PopupSliderChoiceFloat(&g_Config.fUISaturation, 0.0f, 2.0f, 1.0f, sy->T("Color Saturation"), 0.01f, screenManager());1112saturation->SetHasDropShadow(false);1113saturation->SetLiveUpdate(true);1114systemSettings->Add(saturation);1115}11161117systemSettings->Add(new ItemHeader(sy->T("PSP Memory Stick")));11181119if (System_GetPropertyBool(SYSPROP_HAS_OPEN_DIRECTORY)) {1120systemSettings->Add(new Choice(sy->T("Show Memory Stick folder")))->OnClick.Add([](UI::EventParams &p) {1121System_ShowFileInFolder(g_Config.memStickDirectory);1122return UI::EVENT_DONE;1123});1124}11251126#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)1127bool showItHere = true;1128#if PPSSPP_PLATFORM(IOS_APP_STORE)1129if (g_Config.memStickDirectory == DarwinFileSystemServices::defaultMemoryStickPath()) {1130// We still keep a way to access it on the developer tools screen.1131showItHere = false;1132}1133#endif1134if (showItHere) {1135systemSettings->Add(new Choice(sy->T("Set Memory Stick folder")))->OnClick.Add(1136[=](UI::EventParams &) {1137SetMemStickDirDarwin(GetRequesterToken());1138return UI::EVENT_DONE;1139});1140}1141#endif11421143#if PPSSPP_PLATFORM(ANDROID)1144if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_VR) {1145memstickDisplay_ = g_Config.memStickDirectory.ToVisualString();1146auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&memstickDisplay_, sy->T("Memory Stick folder", "Memory Stick folder"), I18NCat::NONE));1147memstickPath->SetEnabled(!PSP_IsInited());1148memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnShowMemstickScreen);11491150// Display USB path for convenience.1151std::string usbPath;1152if (PathToVisualUsbPath(g_Config.memStickDirectory, usbPath)) {1153if (usbPath.empty()) {1154// Probably it's just the root. So let's add PSP to make it clear.1155usbPath = "/PSP";1156}1157systemSettings->Add(new InfoItem(sy->T("USB"), usbPath))->SetChoiceStyle(true);1158}1159}1160#elif defined(_WIN32)1161#if PPSSPP_PLATFORM(UWP)1162memstickDisplay_ = g_Config.memStickDirectory.ToVisualString();1163auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&memstickDisplay_, sy->T("Memory Stick folder", "Memory Stick folder"), I18NCat::NONE));1164memstickPath->SetEnabled(!PSP_IsInited());1165memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnShowMemstickScreen);1166#else1167SavePathInMyDocumentChoice = systemSettings->Add(new CheckBox(&installed_, sy->T("Save path in My Documents", "Save path in My Documents")));1168SavePathInMyDocumentChoice->SetEnabled(!PSP_IsInited());1169SavePathInMyDocumentChoice->OnClick.Handle(this, &GameSettingsScreen::OnSavePathMydoc);1170SavePathInOtherChoice = systemSettings->Add(new CheckBox(&otherinstalled_, sy->T("Save path in installed.txt", "Save path in installed.txt")));1171SavePathInOtherChoice->SetEnabled(false);1172SavePathInOtherChoice->OnClick.Handle(this, &GameSettingsScreen::OnSavePathOther);1173const bool myDocsExists = W32Util::UserDocumentsPath().size() != 0;11741175const Path &PPSSPPpath = File::GetExeDirectory();1176const Path installedFile = PPSSPPpath / "installed.txt";1177installed_ = File::Exists(installedFile);1178otherinstalled_ = false;1179if (!installed_ && myDocsExists) {1180if (File::CreateEmptyFile(PPSSPPpath / "installedTEMP.txt")) {1181// Disable the setting whether cannot create & delete file1182if (!(File::Delete(PPSSPPpath / "installedTEMP.txt")))1183SavePathInMyDocumentChoice->SetEnabled(false);1184else1185SavePathInOtherChoice->SetEnabled(!PSP_IsInited());1186} else1187SavePathInMyDocumentChoice->SetEnabled(false);1188} else {1189if (installed_ && myDocsExists) {1190FILE *testInstalled = File::OpenCFile(installedFile, "rt");1191if (testInstalled) {1192char temp[2048];1193char *tempStr = fgets(temp, sizeof(temp), testInstalled);1194// Skip UTF-8 encoding bytes if there are any. There are 3 of them.1195if (tempStr && strncmp(tempStr, "\xEF\xBB\xBF", 3) == 0) {1196tempStr += 3;1197}1198SavePathInOtherChoice->SetEnabled(!PSP_IsInited());1199if (tempStr && strlen(tempStr) != 0 && strcmp(tempStr, "\n") != 0) {1200installed_ = false;1201otherinstalled_ = true;1202}1203fclose(testInstalled);1204}1205} else if (!myDocsExists) {1206SavePathInMyDocumentChoice->SetEnabled(false);1207}1208}1209#endif1210#endif1211systemSettings->Add(new CheckBox(&g_Config.bMemStickInserted, sy->T("Memory Stick inserted")));1212UI::PopupSliderChoice *sizeChoice = systemSettings->Add(new PopupSliderChoice(&g_Config.iMemStickSizeGB, 1, 32, 16, sy->T("Memory Stick size", "Memory Stick size"), screenManager(), "GB"));1213sizeChoice->SetFormat("%d GB");12141215systemSettings->Add(new ItemHeader(sy->T("Help the PPSSPP team")));1216if (!enableReportsSet_)1217enableReports_ = Reporting::IsEnabled();1218enableReportsSet_ = true;1219enableReportsCheckbox_ = new CheckBox(&enableReports_, sy->T("Enable Compatibility Server Reports"));1220enableReportsCheckbox_->SetEnabled(Reporting::IsSupported());1221systemSettings->Add(enableReportsCheckbox_);12221223systemSettings->Add(new ItemHeader(sy->T("Emulation")));12241225systemSettings->Add(new CheckBox(&g_Config.bFastMemory, sy->T("Fast Memory", "Fast Memory")))->OnClick.Handle(this, &GameSettingsScreen::OnJitAffectingSetting);1226systemSettings->Add(new CheckBox(&g_Config.bIgnoreBadMemAccess, sy->T("Ignore bad memory accesses")));12271228static const char *ioTimingMethods[] = { "Fast (lag on slow storage)", "Host (bugs, less lag)", "Simulate UMD delays", "Simulate UMD slow reading speed"};1229View *ioTimingMethod = systemSettings->Add(new PopupMultiChoice(&g_Config.iIOTimingMethod, sy->T("IO timing method"), ioTimingMethods, 0, ARRAY_SIZE(ioTimingMethods), I18NCat::SYSTEM, screenManager()));1230systemSettings->Add(new CheckBox(&g_Config.bForceLagSync, sy->T("Force real clock sync (slower, less lag)")))->SetDisabledPtr(&g_Config.bAutoFrameSkip);1231PopupSliderChoice *lockedMhz = systemSettings->Add(new PopupSliderChoice(&g_Config.iLockedCPUSpeed, 0, 1000, 0, sy->T("Change CPU Clock", "Change CPU Clock (unstable)"), screenManager(), sy->T("MHz, 0:default")));1232lockedMhz->OnChange.Add([&](UI::EventParams &) {1233enableReportsCheckbox_->SetEnabled(Reporting::IsSupported());1234return UI::EVENT_CONTINUE;1235});1236lockedMhz->SetZeroLabel(sy->T("Auto"));1237PopupSliderChoice *rewindInterval = systemSettings->Add(new PopupSliderChoice(&g_Config.iRewindSnapshotInterval, 0, 60, 0, sy->T("Rewind Snapshot Interval"), screenManager(), di->T("seconds, 0:off")));1238rewindInterval->SetFormat(di->T("%d seconds"));1239rewindInterval->SetZeroLabel(sy->T("Off"));12401241systemSettings->Add(new ItemHeader(sy->T("General")));12421243#if PPSSPP_PLATFORM(ANDROID)1244if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {1245auto co = GetI18NCategory(I18NCat::CONTROLS);12461247static const char *screenRotation[] = { "Auto", "Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed", "Landscape Auto" };1248PopupMultiChoice *rot = systemSettings->Add(new PopupMultiChoice(&g_Config.iScreenRotation, co->T("Screen Rotation"), screenRotation, 0, ARRAY_SIZE(screenRotation), I18NCat::CONTROLS, screenManager()));1249rot->OnChoice.Handle(this, &GameSettingsScreen::OnScreenRotation);12501251if (System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE)) {1252systemSettings->Add(new CheckBox(&g_Config.bSustainedPerformanceMode, sy->T("Sustained performance mode")))->OnClick.Handle(this, &GameSettingsScreen::OnSustainedPerformanceModeChange);1253}1254}1255#endif12561257systemSettings->Add(new Choice(sy->T("Restore Default Settings")))->OnClick.Handle(this, &GameSettingsScreen::OnRestoreDefaultSettings);1258systemSettings->Add(new CheckBox(&g_Config.bEnableStateUndo, sy->T("Savestate slot backups")));1259static const char *autoLoadSaveStateChoices[] = { "Off", "Oldest Save", "Newest Save", "Slot 1", "Slot 2", "Slot 3", "Slot 4", "Slot 5" };1260systemSettings->Add(new PopupMultiChoice(&g_Config.iAutoLoadSaveState, sy->T("Auto Load Savestate"), autoLoadSaveStateChoices, 0, ARRAY_SIZE(autoLoadSaveStateChoices), I18NCat::SYSTEM, screenManager()));1261if (System_GetPropertyBool(SYSPROP_HAS_KEYBOARD))1262systemSettings->Add(new CheckBox(&g_Config.bBypassOSKWithKeyboard, sy->T("Use system native keyboard")));12631264systemSettings->Add(new CheckBox(&g_Config.bCacheFullIsoInRam, sy->T("Cache ISO in RAM", "Cache full ISO in RAM")))->SetEnabled(!PSP_IsInited());1265systemSettings->Add(new CheckBox(&g_Config.bCheckForNewVersion, sy->T("VersionCheck", "Check for new versions of PPSSPP")));1266systemSettings->Add(new CheckBox(&g_Config.bScreenshotsAsPNG, sy->T("Screenshots as PNG")));1267// TODO: Make this setting available on Mac too.1268#if PPSSPP_PLATFORM(WINDOWS)1269systemSettings->Add(new CheckBox(&g_Config.bPauseOnLostFocus, sy->T("Pause when not focused")));1270#endif12711272systemSettings->Add(new ItemHeader(sy->T("Cheats", "Cheats")));1273CheckBox *enableCheats = systemSettings->Add(new CheckBox(&g_Config.bEnableCheats, sy->T("Enable Cheats")));1274enableCheats->OnClick.Add([&](UI::EventParams &) {1275enableReportsCheckbox_->SetEnabled(Reporting::IsSupported());1276return UI::EVENT_CONTINUE;1277});1278systemSettings->Add(new CheckBox(&g_Config.bEnablePlugins, sy->T("Enable plugins")));12791280systemSettings->Add(new ItemHeader(sy->T("PSP Settings")));12811282// The ordering here is simply mapping directly to PSP_SYSTEMPARAM_LANGUAGE_*.1283static const char *defaultLanguages[] = { "Auto", "Japanese", "English", "French", "Spanish", "German", "Italian", "Dutch", "Portuguese", "Russian", "Korean", "Chinese (traditional)", "Chinese (simplified)" };1284systemSettings->Add(new PopupMultiChoice(&g_Config.iLanguage, psps->T("Game language"), defaultLanguages, -1, ARRAY_SIZE(defaultLanguages), I18NCat::PSPSETTINGS, screenManager()));1285static const char *models[] = { "PSP-1000", "PSP-2000/3000" };1286systemSettings->Add(new PopupMultiChoice(&g_Config.iPSPModel, sy->T("PSP Model"), models, 0, ARRAY_SIZE(models), I18NCat::SYSTEM, screenManager()))->SetEnabled(!PSP_IsInited());1287systemSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sNickName, sy->T("Change Nickname"), "", 32, screenManager()));1288systemSettings->Add(new CheckBox(&g_Config.bDayLightSavings, sy->T("Day Light Saving")));1289static const char *dateFormat[] = { "YYYYMMDD", "MMDDYYYY", "DDMMYYYY" };1290systemSettings->Add(new PopupMultiChoice(&g_Config.iDateFormat, sy->T("Date Format"), dateFormat, 0, ARRAY_SIZE(dateFormat), I18NCat::SYSTEM, screenManager()));1291static const char *timeFormat[] = { "24HR", "12HR" };1292systemSettings->Add(new PopupMultiChoice(&g_Config.iTimeFormat, sy->T("Time Format"), timeFormat, 0, ARRAY_SIZE(timeFormat), I18NCat::SYSTEM, screenManager()));1293static const char *buttonPref[] = { "Use O to confirm", "Use X to confirm" };1294systemSettings->Add(new PopupMultiChoice(&g_Config.iButtonPreference, sy->T("Confirmation Button"), buttonPref, 0, ARRAY_SIZE(buttonPref), I18NCat::SYSTEM, screenManager()));12951296#if defined(_WIN32) || (defined(USING_QT_UI) && !defined(MOBILE_DEVICE))1297systemSettings->Add(new ItemHeader(sy->T("Recording")));1298systemSettings->Add(new CheckBox(&g_Config.bDumpFrames, sy->T("Record Display")));1299systemSettings->Add(new CheckBox(&g_Config.bUseFFV1, sy->T("Use Lossless Video Codec (FFV1)")));1300systemSettings->Add(new CheckBox(&g_Config.bDumpVideoOutput, sy->T("Use output buffer (with overlay) for recording")));1301systemSettings->Add(new CheckBox(&g_Config.bDumpAudio, sy->T("Record Audio")));1302systemSettings->Add(new CheckBox(&g_Config.bSaveLoadResetsAVdumping, sy->T("Reset Recording on Save/Load State")));1303#endif1304}13051306void GameSettingsScreen::CreateVRSettings(UI::ViewGroup *vrSettings) {1307using namespace UI;13081309auto vr = GetI18NCategory(I18NCat::VR);1310int deviceType = System_GetPropertyInt(SYSPROP_DEVICE_TYPE);13111312if (deviceType == DEVICE_TYPE_VR) {1313vrSettings->Add(new ItemHeader(vr->T("Virtual reality")));1314vrSettings->Add(new CheckBox(&g_Config.bEnableVR, vr->T("Virtual reality")));1315vrSettings->Add(new CheckBox(&g_Config.bEnable6DoF, vr->T("6DoF movement")));1316vrSettings->Add(new CheckBox(&g_Config.bEnableStereo, vr->T("Stereoscopic vision (Experimental)")));1317vrSettings->Add(new CheckBox(&g_Config.bEnableImmersiveVR, vr->T("Enable immersive mode")));1318if (IsPassthroughSupported()) {1319vrSettings->Add(new CheckBox(&g_Config.bPassthrough, vr->T("Enable passthrough")));1320}1321vrSettings->Add(new CheckBox(&g_Config.bForce72Hz, vr->T("Force 72Hz update")));1322}13231324vrSettings->Add(new ItemHeader(vr->T("VR camera")));1325if (deviceType == DEVICE_TYPE_VR) {1326vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fCanvasDistance, 1.0f, 15.0f, 12.0f, vr->T("Distance to 2D menus and scenes"), 1.0f, screenManager(), ""));1327vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fCanvas3DDistance, 1.0f, 15.0f, 3.0f, vr->T("Distance to 3D scenes when VR disabled"), 1.0f, screenManager(), ""));1328}1329vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fFieldOfViewPercentage, 100.0f, 200.0f, 100.0f, vr->T("Field of view scale"), 10.0f, screenManager(), vr->T("% of native FoV")));1330vrSettings->Add(new CheckBox(&g_Config.bRescaleHUD, vr->T("Heads-up display detection")));1331PopupSliderChoiceFloat* vrHudScale = vrSettings->Add(new PopupSliderChoiceFloat(&g_Config.fHeadUpDisplayScale, 0.0f, 1.5f, 0.3f, vr->T("Heads-up display scale"), 0.1f, screenManager(), ""));1332vrHudScale->SetEnabledPtr(&g_Config.bRescaleHUD);1333vrSettings->Add(new CheckBox(&g_Config.bManualForceVR, vr->T("Manual switching between flat screen and VR using SCREEN key")));1334}13351336UI::EventReturn GameSettingsScreen::OnAutoFrameskip(UI::EventParams &e) {1337g_Config.UpdateAfterSettingAutoFrameSkip();1338return UI::EVENT_DONE;1339}13401341UI::EventReturn GameSettingsScreen::OnScreenRotation(UI::EventParams &e) {1342INFO_LOG(Log::System, "New display rotation: %d", g_Config.iScreenRotation);1343INFO_LOG(Log::System, "Sending rotate");1344System_Notify(SystemNotification::ROTATE_UPDATED);1345INFO_LOG(Log::System, "Got back from rotate");1346return UI::EVENT_DONE;1347}13481349UI::EventReturn GameSettingsScreen::OnAdhocGuides(UI::EventParams &e) {1350auto n = GetI18NCategory(I18NCat::NETWORKING);1351std::string url(n->T("MultiplayerHowToURL", "https://github.com/hrydgard/ppsspp/wiki/How-to-play-multiplayer-games-with-PPSSPP"));1352System_LaunchUrl(LaunchUrlType::BROWSER_URL, url.c_str());1353return UI::EVENT_DONE;1354}13551356UI::EventReturn GameSettingsScreen::OnImmersiveModeChange(UI::EventParams &e) {1357System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE);1358if (g_Config.iAndroidHwScale != 0) {1359System_RecreateActivity();1360}1361return UI::EVENT_DONE;1362}13631364UI::EventReturn GameSettingsScreen::OnSustainedPerformanceModeChange(UI::EventParams &e) {1365System_Notify(SystemNotification::SUSTAINED_PERF_CHANGE);1366return UI::EVENT_DONE;1367}13681369UI::EventReturn GameSettingsScreen::OnJitAffectingSetting(UI::EventParams &e) {1370System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);1371return UI::EVENT_DONE;1372}13731374UI::EventReturn GameSettingsScreen::OnShowMemstickScreen(UI::EventParams &e) {1375screenManager()->push(new MemStickScreen(false));1376return UI::EVENT_DONE;1377}13781379#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)13801381UI::EventReturn GameSettingsScreen::OnSavePathMydoc(UI::EventParams &e) {1382const Path &PPSSPPpath = File::GetExeDirectory();1383const Path installedFile = PPSSPPpath / "installed.txt";1384installed_ = File::Exists(installedFile);1385if (otherinstalled_) {1386File::Delete(PPSSPPpath / "installed.txt");1387File::CreateEmptyFile(PPSSPPpath / "installed.txt");1388otherinstalled_ = false;1389const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/";1390g_Config.memStickDirectory = Path(myDocsPath);1391} else if (installed_) {1392File::Delete(PPSSPPpath / "installed.txt");1393installed_ = false;1394g_Config.memStickDirectory = PPSSPPpath / "memstick";1395} else {1396FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb");1397if (f) {1398fclose(f);1399}14001401const std::string myDocsPath = W32Util::UserDocumentsPath() + "/PPSSPP/";1402g_Config.memStickDirectory = Path(myDocsPath);1403installed_ = true;1404}1405return UI::EVENT_DONE;1406}14071408UI::EventReturn GameSettingsScreen::OnSavePathOther(UI::EventParams &e) {1409const Path &PPSSPPpath = File::GetExeDirectory();1410if (otherinstalled_) {1411auto di = GetI18NCategory(I18NCat::DIALOG);1412std::string initialPath = g_Config.memStickDirectory.ToCString();1413std::string folder = W32Util::BrowseForFolder(MainWindow::GetHWND(), di->T("Choose PPSSPP save folder"), initialPath);1414if (folder.size()) {1415g_Config.memStickDirectory = Path(folder);1416FILE *f = File::OpenCFile(PPSSPPpath / "installed.txt", "wb");1417if (f) {1418std::string utfstring("\xEF\xBB\xBF");1419utfstring.append(folder);1420fwrite(utfstring.c_str(), 1, utfstring.length(), f);1421fclose(f);1422}1423installed_ = false;1424}1425else1426otherinstalled_ = false;1427}1428else {1429File::Delete(PPSSPPpath / "installed.txt");1430SavePathInMyDocumentChoice->SetEnabled(true);1431otherinstalled_ = false;1432installed_ = false;1433g_Config.memStickDirectory = PPSSPPpath / "memstick";1434}1435return UI::EVENT_DONE;1436}14371438#endif14391440UI::EventReturn GameSettingsScreen::OnChangeBackground(UI::EventParams &e) {1441const Path bgPng = GetSysDirectory(DIRECTORY_SYSTEM) / "background.png";1442const Path bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) / "background.jpg";14431444if (File::Exists(bgPng) || File::Exists(bgJpg)) {1445File::Delete(bgPng);1446File::Delete(bgJpg);1447UIBackgroundShutdown();1448RecreateViews();1449} else {1450auto sy = GetI18NCategory(I18NCat::SYSTEM);1451System_BrowseForImage(GetRequesterToken(), sy->T("Set UI background..."), [=](const std::string &value, int) {1452if (!value.empty()) {1453Path path(value);14541455// Check the file format. Don't rely on the file extension here due to scoped storage URLs.1456FILE *f = File::OpenCFile(path, "rb");1457uint8_t buffer[8];1458ImageFileType type = ImageFileType::UNKNOWN;1459if (f != nullptr && 8 == fread(buffer, 1, ARRAY_SIZE(buffer), f)) {1460type = DetectImageFileType(buffer, ARRAY_SIZE(buffer));1461}14621463std::string filename;1464switch (type) {1465case ImageFileType::JPEG:1466filename = "background.jpg";1467break;1468case ImageFileType::PNG:1469filename = "background.png";1470break;1471default:1472break;1473}14741475if (!filename.empty()) {1476Path dest = GetSysDirectory(DIRECTORY_SYSTEM) / filename;1477File::Copy(Path(value), dest);1478} else {1479g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("Only JPG and PNG images are supported"), path.GetFilename(), 5.0);1480}1481}1482// It will init again automatically. We can't init outside a frame on Vulkan.1483UIBackgroundShutdown();1484RecreateViews();1485});1486}14871488// Change to a browse or clear button.1489return UI::EVENT_DONE;1490}14911492UI::EventReturn GameSettingsScreen::OnFullscreenChange(UI::EventParams &e) {1493g_Config.iForceFullScreen = -1;1494System_ToggleFullscreenState(g_Config.UseFullScreen() ? "1" : "0");1495return UI::EVENT_DONE;1496}14971498UI::EventReturn GameSettingsScreen::OnFullscreenMultiChange(UI::EventParams &e) {1499System_ToggleFullscreenState(g_Config.UseFullScreen() ? "1" : "0");1500return UI::EVENT_DONE;1501}15021503UI::EventReturn GameSettingsScreen::OnResolutionChange(UI::EventParams &e) {1504if (g_Config.iAndroidHwScale == 1) {1505System_RecreateActivity();1506}1507Reporting::UpdateConfig();1508System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);1509return UI::EVENT_DONE;1510}15111512void GameSettingsScreen::onFinish(DialogResult result) {1513Reporting::Enable(enableReports_, "report.ppsspp.org");1514Reporting::UpdateConfig();1515if (!g_Config.Save("GameSettingsScreen::onFinish")) {1516System_Toast("Failed to save settings!\nCheck permissions, or try to restart the device.");1517}15181519if (editThenRestore_) {1520// In case we didn't have the title yet before, try again.1521std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO);1522g_Config.changeGameSpecific(gameID_, info->GetTitle());1523g_Config.unloadGameConfig();1524}15251526System_Notify(SystemNotification::UI);15271528KeyMap::UpdateNativeMenuKeys();15291530// Wipe some caches after potentially changing settings.1531// Let's not send resize messages here, handled elsewhere.1532System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);1533}15341535void GameSettingsScreen::dialogFinished(const Screen *dialog, DialogResult result) {1536if (result == DialogResult::DR_OK) {1537g_Config.iFpsLimit1 = iAlternateSpeedPercent1_ < 0 ? -1 : (iAlternateSpeedPercent1_ * 60) / 100;1538g_Config.iFpsLimit2 = iAlternateSpeedPercent2_ < 0 ? -1 : (iAlternateSpeedPercent2_ * 60) / 100;1539g_Config.iAnalogFpsLimit = (iAlternateSpeedPercentAnalog_ * 60) / 100;15401541RecreateViews();1542}15431544// Show/hide the Analog Alternative Speed as appropriate - need to recreate views if this changed.1545bool mapped = KeyMap::InputMappingsFromPspButton(VIRTKEY_SPEED_ANALOG, nullptr, true);1546if (mapped != analogSpeedMapped_) {1547analogSpeedMapped_ = mapped;1548RecreateViews();1549}1550}15511552void GameSettingsScreen::CallbackMemstickFolder(bool yes) {1553if (yes) {1554Path memstickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt";1555std::string testWriteFile = pendingMemstickFolder_ + "/.write_verify_file";15561557// Already, create away.1558if (!File::Exists(Path(pendingMemstickFolder_))) {1559File::CreateFullPath(Path(pendingMemstickFolder_));1560}1561if (!File::WriteDataToFile(true, "1", 1, Path(testWriteFile))) {1562auto sy = GetI18NCategory(I18NCat::SYSTEM);1563settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr);1564return;1565}1566File::Delete(Path(testWriteFile));15671568if (!File::WriteDataToFile(true, pendingMemstickFolder_.c_str(), pendingMemstickFolder_.size(), memstickDirFile)) {1569WARN_LOG(Log::System, "Failed to write memstick folder to '%s'", memstickDirFile.c_str());1570} else {1571// Save so the settings, at least, are transferred.1572g_Config.memStickDirectory = Path(pendingMemstickFolder_);1573g_Config.Save("MemstickPathChanged");1574}1575screenManager()->RecreateAllViews();1576}1577}15781579void TriggerRestart(const char *why, bool editThenRestore, const Path &gamePath) {1580// Extra save here to make sure the choice really gets saved even if there are shutdown bugs in1581// the GPU backend code.1582g_Config.Save(why);1583std::string param = "--gamesettings";1584if (editThenRestore) {1585// We won't pass the gameID, so don't resume back into settings.1586param.clear();1587} else if (!gamePath.empty()) {1588param += " \"" + ReplaceAll(ReplaceAll(gamePath.ToString(), "\\", "\\\\"), "\"", "\\\"") + "\"";1589}1590// Make sure the new instance is considered the first.1591ShutdownInstanceCounter();1592System_RestartApp(param);1593}15941595UI::EventReturn GameSettingsScreen::OnRenderingBackend(UI::EventParams &e) {1596// It only makes sense to show the restart prompt if the backend was actually changed.1597if (g_Config.iGPUBackend != (int)GetGPUBackend()) {1598auto di = GetI18NCategory(I18NCat::DIALOG);1599screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) {1600if (yes) {1601TriggerRestart("GameSettingsScreen::RenderingBackendYes", editThenRestore_, gamePath_);1602} else {1603g_Config.iGPUBackend = (int)GetGPUBackend();1604}1605}));1606}1607return UI::EVENT_DONE;1608}16091610UI::EventReturn GameSettingsScreen::OnRenderingDevice(UI::EventParams &e) {1611// It only makes sense to show the restart prompt if the device was actually changed.1612std::string *deviceNameSetting = GPUDeviceNameSetting();1613if (deviceNameSetting && *deviceNameSetting != GetGPUBackendDevice()) {1614auto di = GetI18NCategory(I18NCat::DIALOG);1615screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) {1616// If the user ends up deciding not to restart, set the config back to the current backend1617// so it doesn't get switched by accident.1618if (yes) {1619TriggerRestart("GameSettingsScreen::RenderingDeviceYes", editThenRestore_, gamePath_);1620} else {1621std::string *deviceNameSetting = GPUDeviceNameSetting();1622if (deviceNameSetting)1623*deviceNameSetting = GetGPUBackendDevice();1624// Needed to redraw the setting.1625RecreateViews();1626}1627}));1628}1629return UI::EVENT_DONE;1630}16311632UI::EventReturn GameSettingsScreen::OnInflightFramesChoice(UI::EventParams &e) {1633if (g_Config.iInflightFrames != prevInflightFrames_) {1634auto di = GetI18NCategory(I18NCat::DIALOG);1635screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) {1636if (yes) {1637TriggerRestart("GameSettingsScreen::InflightFramesYes", editThenRestore_, gamePath_);1638} else {1639g_Config.iInflightFrames = prevInflightFrames_;1640}1641}));1642}1643return UI::EVENT_DONE;1644}16451646UI::EventReturn GameSettingsScreen::OnCameraDeviceChange(UI::EventParams& e) {1647Camera::onCameraDeviceChange();1648return UI::EVENT_DONE;1649}16501651UI::EventReturn GameSettingsScreen::OnMicDeviceChange(UI::EventParams& e) {1652Microphone::onMicDeviceChange();1653return UI::EVENT_DONE;1654}16551656UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) {1657auto a = GetI18NCategory(I18NCat::AUDIO);1658if (g_Config.sAudioDevice == a->T("Auto")) {1659g_Config.sAudioDevice.clear();1660}1661System_Notify(SystemNotification::AUDIO_RESET_DEVICE);1662return UI::EVENT_DONE;1663}16641665UI::EventReturn GameSettingsScreen::OnChangeQuickChat0(UI::EventParams &e) {1666#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)1667auto n = GetI18NCategory(I18NCat::NETWORKING);1668System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 1"), g_Config.sQuickChat0, false, [](const std::string &value, int) {1669g_Config.sQuickChat0 = value;1670});1671#endif1672return UI::EVENT_DONE;1673}16741675UI::EventReturn GameSettingsScreen::OnChangeQuickChat1(UI::EventParams &e) {1676#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)1677auto n = GetI18NCategory(I18NCat::NETWORKING);1678System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 2"), g_Config.sQuickChat1, false, [](const std::string &value, int) {1679g_Config.sQuickChat1 = value;1680});1681#endif1682return UI::EVENT_DONE;1683}16841685UI::EventReturn GameSettingsScreen::OnChangeQuickChat2(UI::EventParams &e) {1686#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)1687auto n = GetI18NCategory(I18NCat::NETWORKING);1688System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 3"), g_Config.sQuickChat2, false, [](const std::string &value, int) {1689g_Config.sQuickChat2 = value;1690});1691#endif1692return UI::EVENT_DONE;1693}16941695UI::EventReturn GameSettingsScreen::OnChangeQuickChat3(UI::EventParams &e) {1696#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)1697auto n = GetI18NCategory(I18NCat::NETWORKING);1698System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 4"), g_Config.sQuickChat3, false, [](const std::string &value, int) {1699g_Config.sQuickChat3 = value;1700});1701#endif1702return UI::EVENT_DONE;1703}17041705UI::EventReturn GameSettingsScreen::OnChangeQuickChat4(UI::EventParams &e) {1706#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)1707auto n = GetI18NCategory(I18NCat::NETWORKING);1708System_InputBoxGetString(GetRequesterToken(), n->T("Enter Quick Chat 5"), g_Config.sQuickChat4, false, [](const std::string &value, int) {1709g_Config.sQuickChat4 = value;1710});1711#endif1712return UI::EVENT_DONE;1713}17141715UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) {1716#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)1717auto n = GetI18NCategory(I18NCat::NETWORKING);1718System_InputBoxGetString(GetRequesterToken(), n->T("Enter a new PSP nickname"), g_Config.sNickName, false, [](const std::string &value, int) {1719g_Config.sNickName = StripSpaces(value);1720});1721#endif1722return UI::EVENT_DONE;1723}17241725UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParams &e) {1726auto n = GetI18NCategory(I18NCat::NETWORKING);17271728screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, n->T("proAdhocServer Address:")));17291730return UI::EVENT_DONE;1731}17321733UI::EventReturn GameSettingsScreen::OnTextureShader(UI::EventParams &e) {1734auto gr = GetI18NCategory(I18NCat::GRAPHICS);1735auto shaderScreen = new TextureShaderScreen(gr->T("Texture Shader"));1736shaderScreen->OnChoice.Handle(this, &GameSettingsScreen::OnTextureShaderChange);1737if (e.v)1738shaderScreen->SetPopupOrigin(e.v);1739screenManager()->push(shaderScreen);1740return UI::EVENT_DONE;1741}17421743UI::EventReturn GameSettingsScreen::OnTextureShaderChange(UI::EventParams &e) {1744System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);1745RecreateViews(); // Update setting name1746g_Config.bTexHardwareScaling = g_Config.sTextureShaderName != "Off";1747return UI::EVENT_DONE;1748}17491750UI::EventReturn GameSettingsScreen::OnControlMapping(UI::EventParams &e) {1751screenManager()->push(new ControlMappingScreen(gamePath_));1752return UI::EVENT_DONE;1753}17541755UI::EventReturn GameSettingsScreen::OnCalibrateAnalogs(UI::EventParams &e) {1756screenManager()->push(new AnalogSetupScreen(gamePath_));1757return UI::EVENT_DONE;1758}17591760UI::EventReturn GameSettingsScreen::OnTouchControlLayout(UI::EventParams &e) {1761screenManager()->push(new TouchControlLayoutScreen(gamePath_));1762return UI::EVENT_DONE;1763}17641765UI::EventReturn GameSettingsScreen::OnTiltCustomize(UI::EventParams &e) {1766screenManager()->push(new TiltAnalogSettingsScreen(gamePath_));1767return UI::EVENT_DONE;1768};17691770void DeveloperToolsScreen::CreateViews() {1771using namespace UI;1772root_ = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT));1773ScrollView *settingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));1774settingsScroll->SetTag("DevToolsSettings");1775root_->Add(settingsScroll);17761777auto di = GetI18NCategory(I18NCat::DIALOG);1778auto dev = GetI18NCategory(I18NCat::DEVELOPER);1779auto gr = GetI18NCategory(I18NCat::GRAPHICS);1780auto a = GetI18NCategory(I18NCat::AUDIO);1781auto sy = GetI18NCategory(I18NCat::SYSTEM);1782auto ps = GetI18NCategory(I18NCat::POSTSHADERS);1783auto ms = GetI18NCategory(I18NCat::MEMSTICK);1784auto si = GetI18NCategory(I18NCat::SYSINFO);17851786AddStandardBack(root_);17871788LinearLayout *list = settingsScroll->Add(new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));1789list->SetSpacing(0);17901791list->Add(new ItemHeader(dev->T("Texture Replacement")));1792list->Add(new CheckBox(&g_Config.bSaveNewTextures, dev->T("Save new textures")));1793list->Add(new CheckBox(&g_Config.bReplaceTextures, dev->T("Replace textures")));17941795Choice *createTextureIni = list->Add(new Choice(dev->T("Create/Open textures.ini file for current game")));1796createTextureIni->OnClick.Handle(this, &DeveloperToolsScreen::OnOpenTexturesIniFile);1797createTextureIni->SetEnabledFunc([&] {1798if (!PSP_IsInited())1799return false;18001801// Disable the choice to Open/Create if the textures.ini file already exists, and we can't open it due to platform support limitations.1802if (!System_GetPropertyBool(SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR)) {1803if (hasTexturesIni_ == HasIni::MAYBE)1804hasTexturesIni_ = TextureReplacer::IniExists(g_paramSFO.GetDiscID()) ? HasIni::YES : HasIni::NO;1805return hasTexturesIni_ != HasIni::YES;1806}1807return true;1808});18091810list->Add(new ItemHeader(sy->T("General")));18111812bool canUseJit = System_GetPropertyBool(SYSPROP_CAN_JIT);1813// iOS can now use JIT on all modes, apparently.1814// The bool may come in handy for future non-jit platforms though (UWP XB1?)1815// In iOS App Store builds, we disable the JIT.18161817static const char *cpuCores[] = {"Interpreter", "Dynarec/JIT (recommended)", "IR Interpreter", "JIT using IR"};1818PopupMultiChoice *core = list->Add(new PopupMultiChoice(&g_Config.iCpuCore, sy->T("CPU Core"), cpuCores, 0, ARRAY_SIZE(cpuCores), I18NCat::SYSTEM, screenManager()));1819core->OnChoice.Handle(this, &DeveloperToolsScreen::OnJitAffectingSetting);1820core->OnChoice.Add([](UI::EventParams &) {1821g_Config.NotifyUpdatedCpuCore();1822return UI::EVENT_DONE;1823});1824if (!canUseJit) {1825core->HideChoice(1);1826core->HideChoice(3);1827}1828// TODO: Enable "JIT using IR" on more architectures.1829#if !PPSSPP_ARCH(X86) && !PPSSPP_ARCH(AMD64) && !PPSSPP_ARCH(ARM64)1830core->HideChoice(3);1831#endif18321833list->Add(new Choice(dev->T("JIT debug tools")))->OnClick.Handle(this, &DeveloperToolsScreen::OnJitDebugTools);1834list->Add(new CheckBox(&g_Config.bShowDeveloperMenu, dev->T("Show Developer Menu")));1835list->Add(new CheckBox(&g_Config.bDumpDecryptedEboot, dev->T("Dump Decrypted Eboot", "Dump Decrypted EBOOT.BIN (If Encrypted) When Booting Game")));18361837#if !PPSSPP_PLATFORM(UWP)1838Choice *cpuTests = new Choice(dev->T("Run CPU Tests"));1839list->Add(cpuTests)->OnClick.Handle(this, &DeveloperToolsScreen::OnRunCPUTests);18401841cpuTests->SetEnabled(TestsAvailable());1842#endif18431844list->Add(new CheckBox(&g_Config.bUseNewAtrac, dev->T("Use experimental sceAtrac")));18451846AddOverlayList(list, screenManager());18471848list->Add(new CheckBox(&g_Config.bEnableLogging, dev->T("Enable Logging")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLoggingChanged);1849list->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLogConfig);1850list->Add(new CheckBox(&g_Config.bLogFrameDrops, dev->T("Log Dropped Frame Statistics")));1851if (GetGPUBackend() == GPUBackend::VULKAN) {1852list->Add(new CheckBox(&g_Config.bGpuLogProfiler, dev->T("GPU log profiler")));1853}18541855if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {1856list->Add(new CheckBox(&g_Config.bRenderMultiThreading, dev->T("Multi-threaded rendering"), ""))->OnClick.Add([](UI::EventParams &e) {1857// TODO: Not translating yet. Will combine with other translations of settings that need restart.1858g_OSD.Show(OSDType::MESSAGE_WARNING, "Restart required");1859return UI::EVENT_DONE;1860});1861}18621863if (GetGPUBackend() == GPUBackend::VULKAN && SupportsCustomDriver()) {1864auto driverChoice = list->Add(new Choice(gr->T("Adreno Driver Manager")));1865driverChoice->OnClick.Add([=](UI::EventParams &e) {1866screenManager()->push(new DriverManagerScreen(gamePath_));1867return UI::EVENT_DONE;1868});1869}18701871// For now, we only implement GPU driver tests for Vulkan and OpenGL. This is simply1872// because the D3D drivers are generally solid enough to not need this type of investigation.1873if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) {1874list->Add(new Choice(dev->T("GPU Driver Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnGPUDriverTest);1875}1876list->Add(new CheckBox(&g_Config.bVendorBugChecksEnabled, dev->T("Enable driver bug workarounds")));1877list->Add(new Choice(dev->T("Framedump tests")))->OnClick.Handle(this, &DeveloperToolsScreen::OnFramedumpTest);1878list->Add(new Choice(dev->T("Touchscreen Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnTouchscreenTest);1879// list->Add(new Choice(dev->T("Memstick Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnMemstickTest);18801881allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);1882canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);1883CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));1884list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);1885allowDebugger->SetEnabledPtr(&canAllowDebugger_);18861887list->Add(new CheckBox(&g_Config.bShowOnScreenMessages, dev->T("Show on-screen messages")));18881889list->Add(new Choice(dev->T("GPI/GPO switches/LEDs")))->OnClick.Add([=](UI::EventParams &e) {1890screenManager()->push(new GPIGPOScreen(dev->T("GPI/GPO switches/LEDs")));1891return UI::EVENT_DONE;1892});18931894#if PPSSPP_PLATFORM(ANDROID)1895static const char *framerateModes[] = { "Default", "Request 60Hz", "Force 60Hz" };1896PopupMultiChoice *framerateMode = list->Add(new PopupMultiChoice(&g_Config.iDisplayFramerateMode, gr->T("Framerate mode"), framerateModes, 0, ARRAY_SIZE(framerateModes), I18NCat::GRAPHICS, screenManager()));1897framerateMode->SetEnabledFunc([]() { return System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30; });1898framerateMode->OnChoice.Add([this](UI::EventParams &e) {1899System_Notify(SystemNotification::FORCE_RECREATE_ACTIVITY);1900return UI::EVENT_DONE;1901});1902#endif19031904#if PPSSPP_PLATFORM(IOS_APP_STORE)1905list->Add(new NoticeView(NoticeLevel::WARN, ms->T("Moving the memstick directory is NOT recommended on iOS"), ""));1906list->Add(new Choice(sy->T("Set Memory Stick folder")))->OnClick.Add(1907[=](UI::EventParams &) {1908SetMemStickDirDarwin(GetRequesterToken());1909return UI::EVENT_DONE;1910});1911#endif19121913static const char *ffModes[] = { "Render all frames", "", "Frame Skipping" };1914PopupMultiChoice *ffMode = list->Add(new PopupMultiChoice(&g_Config.iFastForwardMode, dev->T("Fast-forward mode"), ffModes, 0, ARRAY_SIZE(ffModes), I18NCat::GRAPHICS, screenManager()));1915ffMode->SetEnabledFunc([]() { return !g_Config.bVSync; });1916ffMode->HideChoice(1); // not used19171918auto displayRefreshRate = list->Add(new PopupSliderChoice(&g_Config.iDisplayRefreshRate, 60, 1000, 60, dev->T("Display refresh rate"), 1, screenManager()));1919displayRefreshRate->SetFormat(si->T("%d Hz"));19201921#if !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(SWITCH)1922list->Add(new ItemHeader(dev->T("MIPSTracer")));19231924MIPSTracerEnabled_ = mipsTracer.tracing_enabled;1925CheckBox *MIPSTracerEnabled = new CheckBox(&MIPSTracerEnabled_, dev->T("MIPSTracer enabled"));1926list->Add(MIPSTracerEnabled)->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerEnabled);1927MIPSTracerEnabled->SetEnabledFunc([]() {1928bool temp = g_Config.iCpuCore == static_cast<int>(CPUCore::IR_INTERPRETER) && PSP_IsInited();1929return temp && Core_IsStepping() && coreState != CORE_POWERDOWN;1930});19311932Choice *TraceDumpPath = list->Add(new Choice(dev->T("Select the file path for the trace")));1933TraceDumpPath->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerPathChanged);1934TraceDumpPath->SetEnabledFunc([]() {1935if (!PSP_IsInited())1936return false;1937return true;1938});19391940MIPSTracerPath_ = mipsTracer.get_logging_path();1941MIPSTracerPath = list->Add(new InfoItem(dev->T("Current log file"), MIPSTracerPath_));19421943PopupSliderChoice* storage_capacity = list->Add(1944new PopupSliderChoice(1945&mipsTracer.in_storage_capacity, 0x4'0000, 0x40'0000, 0x10'0000, dev->T("Storage capacity"), 0x10000, screenManager()1946)1947);1948storage_capacity->SetFormat("0x%x asm opcodes");1949storage_capacity->OnChange.Add([&](UI::EventParams &) {1950INFO_LOG(Log::JIT, "User changed the tracer's storage capacity to 0x%x", mipsTracer.in_storage_capacity);1951return UI::EVENT_CONTINUE;1952});19531954PopupSliderChoice* trace_max_size = list->Add(1955new PopupSliderChoice(1956&mipsTracer.in_max_trace_size, 0x1'0000, 0x40'0000, 0x10'0000, dev->T("Max allowed trace size"), 0x10000, screenManager()1957)1958);1959trace_max_size->SetFormat("%d basic blocks");1960trace_max_size->OnChange.Add([&](UI::EventParams &) {1961INFO_LOG(Log::JIT, "User changed the tracer's max trace size to %d", mipsTracer.in_max_trace_size);1962return UI::EVENT_CONTINUE;1963});19641965Button *FlushTrace = list->Add(new Button(dev->T("Flush the trace")));1966FlushTrace->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerFlushTrace);19671968Button *InvalidateJitCache = list->Add(new Button(dev->T("Clear the JIT cache")));1969InvalidateJitCache->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearJitCache);19701971Button *ClearMIPSTracer = list->Add(new Button(dev->T("Clear the MIPSTracer")));1972ClearMIPSTracer->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearTracer);1973#endif19741975Draw::DrawContext *draw = screenManager()->getDrawContext();19761977list->Add(new ItemHeader(dev->T("Ubershaders")));1978if (draw->GetShaderLanguageDesc().bitwiseOps && !draw->GetBugs().Has(Draw::Bugs::UNIFORM_INDEXING_BROKEN)) {1979// If the above if fails, the checkbox is redundant since it'll be force disabled anyway.1980list->Add(new CheckBox(&g_Config.bUberShaderVertex, dev->T("Vertex")));1981}1982#if !PPSSPP_PLATFORM(UWP)1983if (g_Config.iGPUBackend != (int)GPUBackend::OPENGL || gl_extensions.GLES3) {1984#else1985{1986#endif1987list->Add(new CheckBox(&g_Config.bUberShaderFragment, dev->T("Fragment")));1988}19891990// Experimental, allow some VR features without OpenXR1991if (GetGPUBackend() == GPUBackend::OPENGL) {1992auto vr = GetI18NCategory(I18NCat::VR);1993list->Add(new ItemHeader(vr->T("Virtual reality")));1994list->Add(new CheckBox(&g_Config.bForceVR, vr->T("VR camera")));1995}19961997// Experimental, will move to main graphics settings later.1998bool multiViewSupported = draw->GetDeviceCaps().multiViewSupported;19992000auto enableStereo = [=]() -> bool {2001return g_Config.bStereoRendering && multiViewSupported;2002};20032004if (multiViewSupported) {2005list->Add(new ItemHeader(gr->T("Stereo rendering")));2006list->Add(new CheckBox(&g_Config.bStereoRendering, gr->T("Stereo rendering")));2007std::vector<std::string> stereoShaderNames;20082009ChoiceWithValueDisplay *stereoShaderChoice = list->Add(new ChoiceWithValueDisplay(&g_Config.sStereoToMonoShader, gr->T("Stereo display shader"), &PostShaderTranslateName));2010stereoShaderChoice->SetEnabledFunc(enableStereo);2011stereoShaderChoice->OnClick.Add([=](EventParams &e) {2012auto gr = GetI18NCategory(I18NCat::GRAPHICS);2013auto procScreen = new PostProcScreen(gr->T("Stereo display shader"), 0, true);2014if (e.v)2015procScreen->SetPopupOrigin(e.v);2016screenManager()->push(procScreen);2017return UI::EVENT_DONE;2018});2019const ShaderInfo *shaderInfo = GetPostShaderInfo(g_Config.sStereoToMonoShader);2020if (shaderInfo) {2021for (size_t i = 0; i < ARRAY_SIZE(shaderInfo->settings); ++i) {2022auto &setting = shaderInfo->settings[i];2023if (!setting.name.empty()) {2024std::string key = StringFromFormat("%sSettingCurrentValue%d", shaderInfo->section.c_str(), i + 1);2025bool keyExisted = g_Config.mPostShaderSetting.find(key) != g_Config.mPostShaderSetting.end();2026auto &value = g_Config.mPostShaderSetting[key];2027if (!keyExisted)2028value = setting.value;20292030PopupSliderChoiceFloat *settingValue = list->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, setting.value, ps->T(setting.name), setting.step, screenManager()));2031settingValue->SetEnabledFunc([=] {2032return !g_Config.bSkipBufferEffects && enableStereo();2033});2034}2035}2036}2037}20382039// Makes it easy to get savestates out of an iOS device. The file listing shown in MacOS doesn't allow2040// you to descend into directories.2041#if PPSSPP_PLATFORM(IOS)2042list->Add(new Choice(dev->T("Copy savestates to memstick root")))->OnClick.Handle(this, &DeveloperToolsScreen::OnCopyStatesToRoot);2043#endif20442045// Reconsider whenever recreating views.2046hasTexturesIni_ = HasIni::MAYBE;2047}20482049void DeveloperToolsScreen::onFinish(DialogResult result) {2050g_Config.Save("DeveloperToolsScreen::onFinish");2051System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);2052}20532054void GameSettingsScreen::CallbackRestoreDefaults(bool yes) {2055if (yes) {2056g_Config.RestoreDefaults(RestoreSettingsBits::SETTINGS);2057}2058System_Notify(SystemNotification::UI);2059}20602061UI::EventReturn GameSettingsScreen::OnRestoreDefaultSettings(UI::EventParams &e) {2062auto sy = GetI18NCategory(I18NCat::SYSTEM);2063if (g_Config.bGameSpecific) {2064auto dev = GetI18NCategory(I18NCat::DEVELOPER);2065auto di = GetI18NCategory(I18NCat::DIALOG);2066screenManager()->push(2067new PromptScreen(gamePath_, dev->T("RestoreGameDefaultSettings", "Are you sure you want to restore the game-specific settings back to the ppsspp defaults?\n"), di->T("OK"), di->T("Cancel"),2068std::bind(&GameSettingsScreen::CallbackRestoreDefaults, this, std::placeholders::_1)));2069} else {2070std::string_view title = sy->T("Restore Default Settings");2071screenManager()->push(new RestoreSettingsScreen(title));2072}2073return UI::EVENT_DONE;2074}20752076UI::EventReturn DeveloperToolsScreen::OnLoggingChanged(UI::EventParams &e) {2077System_Notify(SystemNotification::TOGGLE_DEBUG_CONSOLE);2078return UI::EVENT_DONE;2079}20802081UI::EventReturn DeveloperToolsScreen::OnRunCPUTests(UI::EventParams &e) {2082#if !PPSSPP_PLATFORM(UWP)2083RunTests();2084#endif2085return UI::EVENT_DONE;2086}20872088UI::EventReturn DeveloperToolsScreen::OnOpenTexturesIniFile(UI::EventParams &e) {2089std::string gameID = g_paramSFO.GetDiscID();2090Path generatedFilename;20912092if (TextureReplacer::GenerateIni(gameID, generatedFilename)) {2093if (System_GetPropertyBool(SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR)) {2094File::OpenFileInEditor(generatedFilename);2095} else {2096// Can't do much here, let's send a "toast" so the user sees that something happened.2097auto dev = GetI18NCategory(I18NCat::DEVELOPER);2098System_Toast((generatedFilename.ToVisualString() + ": " + dev->T_cstr("Texture ini file created")).c_str());2099}21002101hasTexturesIni_ = HasIni::YES;2102}2103return UI::EVENT_DONE;2104}21052106UI::EventReturn DeveloperToolsScreen::OnLogConfig(UI::EventParams &e) {2107screenManager()->push(new LogConfigScreen());2108return UI::EVENT_DONE;2109}21102111UI::EventReturn DeveloperToolsScreen::OnJitDebugTools(UI::EventParams &e) {2112screenManager()->push(new JitDebugScreen());2113return UI::EVENT_DONE;2114}21152116UI::EventReturn DeveloperToolsScreen::OnGPUDriverTest(UI::EventParams &e) {2117screenManager()->push(new GPUDriverTestScreen());2118return UI::EVENT_DONE;2119}21202121UI::EventReturn DeveloperToolsScreen::OnFramedumpTest(UI::EventParams &e) {2122screenManager()->push(new FrameDumpTestScreen());2123return UI::EVENT_DONE;2124}21252126UI::EventReturn DeveloperToolsScreen::OnTouchscreenTest(UI::EventParams &e) {2127screenManager()->push(new TouchTestScreen(gamePath_));2128return UI::EVENT_DONE;2129}21302131UI::EventReturn DeveloperToolsScreen::OnJitAffectingSetting(UI::EventParams &e) {2132System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);2133return UI::EVENT_DONE;2134}21352136UI::EventReturn DeveloperToolsScreen::OnCopyStatesToRoot(UI::EventParams &e) {2137Path savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE);2138Path root_dir = GetSysDirectory(DIRECTORY_MEMSTICK_ROOT);21392140std::vector<File::FileInfo> files;2141GetFilesInDir(savestate_dir, &files, nullptr, 0);21422143for (const File::FileInfo &file : files) {2144Path src = file.fullName;2145Path dst = root_dir / file.name;2146INFO_LOG(Log::System, "Copying file '%s' to '%s'", src.c_str(), dst.c_str());2147File::Copy(src, dst);2148}21492150return UI::EVENT_DONE;2151}21522153UI::EventReturn DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) {2154if (allowDebugger_) {2155StartWebServer(WebServerFlags::DEBUGGER);2156} else {2157StopWebServer(WebServerFlags::DEBUGGER);2158}2159// Persist the setting. Maybe should separate?2160g_Config.bRemoteDebuggerOnStartup = allowDebugger_;2161return UI::EVENT_DONE;2162}21632164UI::EventReturn DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) {2165if (MIPSTracerEnabled_) {2166u32 capacity = mipsTracer.in_storage_capacity;2167u32 trace_size = mipsTracer.in_max_trace_size;21682169mipsTracer.initialize(capacity, trace_size);2170mipsTracer.start_tracing();2171}2172else {2173mipsTracer.stop_tracing();2174}2175return UI::EVENT_DONE;2176}21772178UI::EventReturn DeveloperToolsScreen::OnMIPSTracerPathChanged(UI::EventParams &e) {2179auto dev = GetI18NCategory(I18NCat::DEVELOPER);2180System_BrowseForFile(GetRequesterToken(), dev->T("Select the log file"), BrowseFileType::ANY,2181[this](const std::string &value, int) {2182mipsTracer.set_logging_path(value);2183MIPSTracerPath_ = value;2184MIPSTracerPath->SetRightText(MIPSTracerPath_);2185});2186return UI::EVENT_DONE;2187}21882189UI::EventReturn DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) {2190mipsTracer.flush_to_file();2191// The error logs are emitted inside the tracer21922193return UI::EVENT_DONE;2194}21952196UI::EventReturn DeveloperToolsScreen::OnMIPSTracerClearJitCache(UI::EventParams &e) {2197INFO_LOG(Log::JIT, "Clearing the jit cache...");2198System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);2199return UI::EVENT_DONE;2200}22012202UI::EventReturn DeveloperToolsScreen::OnMIPSTracerClearTracer(UI::EventParams &e) {2203INFO_LOG(Log::JIT, "Clearing the MIPSTracer...");2204mipsTracer.clear();2205return UI::EVENT_DONE;2206}22072208void DeveloperToolsScreen::update() {2209UIDialogScreenWithBackground::update();2210allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);2211canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);2212}22132214static bool RunMemstickTest(std::string *error) {2215Path testRoot = GetSysDirectory(PSPDirectories::DIRECTORY_CACHE) / "test";22162217*error = "N/A";22182219File::CreateDir(testRoot);2220if (!File::Exists(testRoot)) {2221return false;2222}22232224Path testFilePath = testRoot / "temp.txt";2225File::CreateEmptyFile(testFilePath);22262227// Attempt to delete the test root. This should fail since it still contains files.2228File::DeleteDir(testRoot);2229if (!File::Exists(testRoot)) {2230*error = "testroot was deleted with a file in it!";2231return false;2232}22332234File::Delete(testFilePath);2235if (File::Exists(testFilePath)) {2236*error = "testfile wasn't deleted";2237return false;2238}22392240File::DeleteDir(testRoot);2241if (File::Exists(testRoot)) {2242*error = "testroot wasn't deleted, even when empty";2243return false;2244}22452246*error = "passed";2247return true;2248}22492250UI::EventReturn DeveloperToolsScreen::OnMemstickTest(UI::EventParams &e) {2251std::string error;2252if (RunMemstickTest(&error)) {2253g_OSD.Show(OSDType::MESSAGE_SUCCESS, "Memstick test passed", error, 6.0f);2254} else {2255g_OSD.Show(OSDType::MESSAGE_ERROR, "Memstick test failed", error, 6.0f);2256}22572258return UI::EVENT_DONE;2259}22602261void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) {2262using namespace UI;2263auto sy = GetI18NCategory(I18NCat::SYSTEM);2264auto di = GetI18NCategory(I18NCat::DIALOG);2265auto n = GetI18NCategory(I18NCat::NETWORKING);22662267LinearLayout *valueRow = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, Margins(0, 0, 0, 10)));22682269addrView_ = new TextEdit(*value_, n->T("Hostname"), "");2270addrView_->SetTextAlign(FLAG_DYNAMIC_ASCII);2271valueRow->Add(addrView_);2272parent->Add(valueRow);22732274LinearLayout *buttonsRow1 = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));2275LinearLayout *buttonsRow2 = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));2276parent->Add(buttonsRow1);2277parent->Add(buttonsRow2);22782279buttonsRow1->Add(new Spacer(new LinearLayoutParams(1.0, G_LEFT)));2280for (char c = '0'; c <= '9'; ++c) {2281char label[] = { c, '\0' };2282auto button = buttonsRow1->Add(new Button(label));2283button->OnClick.Handle(this, &HostnameSelectScreen::OnNumberClick);2284button->SetTag(label);2285}2286buttonsRow1->Add(new Button("."))->OnClick.Handle(this, &HostnameSelectScreen::OnPointClick);2287buttonsRow1->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT)));22882289buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_LEFT)));2290if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) {2291buttonsRow2->Add(new Button(di->T("Edit")))->OnClick.Handle(this, &HostnameSelectScreen::OnEditClick);2292}2293buttonsRow2->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteClick);2294buttonsRow2->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteAllClick);2295buttonsRow2->Add(new Button(di->T("Toggle List")))->OnClick.Handle(this, &HostnameSelectScreen::OnShowIPListClick);2296buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT)));22972298std::vector<std::string> listIP = {"socom.cc", "psp.gameplayer.club", "myneighborsushicat.com", "localhost"}; // TODO: Add some saved recent history too?2299net::GetIPList(listIP);2300ipRows_ = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0));2301ScrollView* scrollView = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));2302LinearLayout* innerView = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));2303if (listIP.size() > 0) {2304for (const auto& label : listIP) {2305// Filter out IP prefixed with "127." and "169.254." also "0." since they can be rendundant or unusable2306if (label.find("127.") != 0 && label.find("169.254.") != 0 && label.find("0.") != 0) {2307auto button = innerView->Add(new Button(label, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));2308button->OnClick.Handle(this, &HostnameSelectScreen::OnIPClick);2309button->SetTag(label);2310}2311}2312}2313scrollView->Add(innerView);2314ipRows_->Add(scrollView);2315ipRows_->SetVisibility(V_GONE);2316parent->Add(ipRows_);2317listIP.clear(); listIP.shrink_to_fit();23182319progressView_ = parent->Add(new TextView(n->T("Validating address..."), ALIGN_HCENTER, false, new LinearLayoutParams(Margins(0, 5, 0, 0))));2320progressView_->SetVisibility(UI::V_GONE);2321}23222323void HostnameSelectScreen::SendEditKey(InputKeyCode keyCode, int flags) {2324auto oldView = UI::GetFocusedView();2325UI::SetFocusedView(addrView_);2326KeyInput fakeKey{ DEVICE_ID_KEYBOARD, keyCode, KEY_DOWN | flags };2327addrView_->Key(fakeKey);2328UI::SetFocusedView(oldView);2329}23302331UI::EventReturn HostnameSelectScreen::OnNumberClick(UI::EventParams &e) {2332std::string text = e.v ? e.v->Tag() : "";2333if (text.length() == 1 && text[0] >= '0' && text[0] <= '9') {2334SendEditKey((InputKeyCode)text[0], KEY_CHAR); // ASCII for digits match keycodes.2335}2336return UI::EVENT_DONE;2337}23382339UI::EventReturn HostnameSelectScreen::OnPointClick(UI::EventParams &e) {2340SendEditKey((InputKeyCode)'.', KEY_CHAR);2341return UI::EVENT_DONE;2342}23432344UI::EventReturn HostnameSelectScreen::OnDeleteClick(UI::EventParams &e) {2345SendEditKey(NKCODE_DEL);2346return UI::EVENT_DONE;2347}23482349UI::EventReturn HostnameSelectScreen::OnDeleteAllClick(UI::EventParams &e) {2350addrView_->SetText("");2351return UI::EVENT_DONE;2352}23532354UI::EventReturn HostnameSelectScreen::OnEditClick(UI::EventParams& e) {2355auto n = GetI18NCategory(I18NCat::NETWORKING);2356System_InputBoxGetString(GetRequesterToken(), n->T("proAdhocServer Address:"), addrView_->GetText(), false, [this](const std::string& value, int) {2357addrView_->SetText(value);2358});2359return UI::EVENT_DONE;2360}23612362UI::EventReturn HostnameSelectScreen::OnShowIPListClick(UI::EventParams& e) {2363if (ipRows_->GetVisibility() == UI::V_GONE) {2364ipRows_->SetVisibility(UI::V_VISIBLE);2365}2366else {2367ipRows_->SetVisibility(UI::V_GONE);2368}2369return UI::EVENT_DONE;2370}23712372UI::EventReturn HostnameSelectScreen::OnIPClick(UI::EventParams& e) {2373std::string text = e.v ? e.v->Tag() : "";2374if (text.length() > 0) {2375addrView_->SetText(text);2376// Copy the IP to clipboard for the host to easily share their IP through chatting apps.2377System_CopyStringToClipboard(text);2378}2379return UI::EVENT_DONE;2380}23812382void HostnameSelectScreen::ResolverThread() {2383std::unique_lock<std::mutex> guard(resolverLock_);23842385while (resolverState_ != ResolverState::QUIT) {2386resolverCond_.wait(guard);23872388if (resolverState_ == ResolverState::QUEUED) {2389resolverState_ = ResolverState::PROGRESS;23902391addrinfo *resolved = nullptr;2392std::string err;2393toResolveResult_ = net::DNSResolve(toResolve_, "80", &resolved, err);2394if (resolved)2395net::DNSResolveFree(resolved);23962397resolverState_ = ResolverState::READY;2398}2399}2400}24012402bool HostnameSelectScreen::CanComplete(DialogResult result) {2403auto n = GetI18NCategory(I18NCat::NETWORKING);24042405if (result != DR_OK)2406return true;24072408std::string value = addrView_->GetText();2409if (lastResolved_ == value) {2410return true;2411}24122413// Currently running.2414if (resolverState_ == ResolverState::PROGRESS)2415return false;24162417std::lock_guard<std::mutex> guard(resolverLock_);2418switch (resolverState_) {2419case ResolverState::PROGRESS:2420case ResolverState::QUIT:2421return false;24222423case ResolverState::QUEUED:2424case ResolverState::WAITING:2425break;24262427case ResolverState::READY:2428if (toResolve_ == value) {2429// Reset the state, nothing there now.2430resolverState_ = ResolverState::WAITING;2431toResolve_.clear();2432lastResolved_ = value;2433lastResolvedResult_ = toResolveResult_;24342435if (lastResolvedResult_) {2436progressView_->SetVisibility(UI::V_GONE);2437} else {2438progressView_->SetText(n->T("Invalid IP or hostname"));2439progressView_->SetTextColor(0xFF3030FF);2440progressView_->SetVisibility(UI::V_VISIBLE);2441}2442return true;2443}24442445// Throw away that last result, it was for a different value.2446break;2447}24482449resolverState_ = ResolverState::QUEUED;2450toResolve_ = value;2451resolverCond_.notify_one();24522453progressView_->SetText(n->T("Validating address..."));2454progressView_->SetTextColor(0xFFFFFFFF);2455progressView_->SetVisibility(UI::V_VISIBLE);24562457return false;2458}24592460void HostnameSelectScreen::OnCompleted(DialogResult result) {2461if (result == DR_OK)2462*value_ = StripSpaces(addrView_->GetText());2463}24642465void GestureMappingScreen::CreateViews() {2466using namespace UI;24672468auto di = GetI18NCategory(I18NCat::DIALOG);2469auto co = GetI18NCategory(I18NCat::CONTROLS);2470auto mc = GetI18NCategory(I18NCat::MAPPABLECONTROLS);24712472root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));2473AddStandardBack(root_);2474TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 200, new AnchorLayoutParams(10, 0, 10, 0, false));2475root_->Add(tabHolder);2476ScrollView *rightPanel = new ScrollView(ORIENT_VERTICAL);2477tabHolder->AddTab(co->T("Gesture"), rightPanel);2478LinearLayout *vert = rightPanel->Add(new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT)));2479vert->SetSpacing(0);24802481static const char *gestureButton[ARRAY_SIZE(GestureKey::keyList)+1];2482gestureButton[0] = "None";2483for (int i = 1; i < ARRAY_SIZE(gestureButton); ++i) {2484gestureButton[i] = KeyMap::GetPspButtonNameCharPointer(GestureKey::keyList[i-1]);2485}24862487vert->Add(new CheckBox(&g_Config.bGestureControlEnabled, co->T("Enable gesture control")));24882489vert->Add(new ItemHeader(co->T("Swipe")));2490vert->Add(new PopupMultiChoice(&g_Config.iSwipeUp, mc->T("Swipe Up"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);2491vert->Add(new PopupMultiChoice(&g_Config.iSwipeDown, mc->T("Swipe Down"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);2492vert->Add(new PopupMultiChoice(&g_Config.iSwipeLeft, mc->T("Swipe Left"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);2493vert->Add(new PopupMultiChoice(&g_Config.iSwipeRight, mc->T("Swipe Right"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);2494vert->Add(new PopupSliderChoiceFloat(&g_Config.fSwipeSensitivity, 0.01f, 1.0f, 1.0f, co->T("Swipe sensitivity"), 0.01f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bGestureControlEnabled);2495vert->Add(new PopupSliderChoiceFloat(&g_Config.fSwipeSmoothing, 0.0f, 0.95f, 0.3f, co->T("Swipe smoothing"), 0.05f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bGestureControlEnabled);24962497vert->Add(new ItemHeader(co->T("Double tap")));2498vert->Add(new PopupMultiChoice(&g_Config.iDoubleTapGesture, mc->T("Double tap button"), gestureButton, 0, ARRAY_SIZE(gestureButton), I18NCat::MAPPABLECONTROLS, screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled);24992500vert->Add(new ItemHeader(co->T("Analog Stick")));2501vert->Add(new CheckBox(&g_Config.bAnalogGesture, co->T("Enable analog stick gesture")));2502vert->Add(new PopupSliderChoiceFloat(&g_Config.fAnalogGestureSensibility, 0.01f, 5.0f, 1.0f, co->T("Sensitivity"), 0.01f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bAnalogGesture);2503}25042505RestoreSettingsScreen::RestoreSettingsScreen(std::string_view title)2506: PopupScreen(title, "OK", "Cancel") {}25072508void RestoreSettingsScreen::CreatePopupContents(UI::ViewGroup *parent) {2509using namespace UI;2510// Carefully re-use various translations.2511auto ga = GetI18NCategory(I18NCat::GAME);2512auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);2513auto mm = GetI18NCategory(I18NCat::MAINMENU);2514auto dev = GetI18NCategory(I18NCat::DEVELOPER);25152516std::string_view text = dev->T(2517"RestoreDefaultSettings",2518"Restore these settings back to their defaults?\nYou can't undo this.\nPlease restart PPSSPP after restoring settings.");25192520TextView *textView = parent->Add(new TextView(text, FLAG_WRAP_TEXT, false));2521textView->SetPadding(10.0f);25222523parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::SETTINGS, ga->T("Game Settings")));2524parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::CONTROLS, ms->T("Controls")));2525parent->Add(new BitCheckBox(&restoreFlags_, (int)RestoreSettingsBits::RECENT, mm->T("Recent")));2526}25272528void RestoreSettingsScreen::OnCompleted(DialogResult result) {2529if (result == DialogResult::DR_OK) {2530g_Config.RestoreDefaults((RestoreSettingsBits)restoreFlags_);2531}2532}253325342535