mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-01 12:07:31 +01:00
ChoiceGroups no longer have hardcoded margins. A new ChoiceGroupList holds them, with padding and spacing that give the same look as before. Settings tabs, developer tools, the two-pane settings columns, the pause menu and the rest put their groups in one, and a few screens adjust its padding and spacing to their layout. Also add the two groups in the remote disc streaming settings that were created but never added, and make the settings search look inside groups: it only checked a tab's direct children, so with ChoiceGroups a filter hid everything. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
870 lines
37 KiB
C++
870 lines
37 KiB
C++
// Copyright (c) 2013- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#include <string>
|
|
|
|
#include "android/jni/app-android.h"
|
|
#include "Common/UI/View.h"
|
|
#include "Common/UI/ViewGroup.h"
|
|
#include "Common/System/OSD.h"
|
|
#include "Common/System/Request.h"
|
|
#include "Common/GPU/OpenGL/GLFeatures.h"
|
|
#include "Common/Data/Text/Parsers.h"
|
|
#include "Common/Data/Encoding/Utf8.h"
|
|
#include "Common/File/FileUtil.h"
|
|
#include "Common/Render/Text/draw_text.h"
|
|
#include "Common/StringUtils.h"
|
|
#include "Common/UI/ScreenManager.h"
|
|
#include "GPU/Common/TextureReplacer.h"
|
|
#include "GPU/Common/PostShader.h"
|
|
#include "Core/MIPS/MIPSTracer.h"
|
|
#include "Core/ELF/ParamSFO.h"
|
|
#include "Core/Config.h"
|
|
#include "Core/HLE/HLE.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/System.h"
|
|
#include "Core/WebServer.h"
|
|
#include "Core/Util/PathUtil.h"
|
|
#include "Core/FileSystems/VirtualDiscFileSystem.h"
|
|
#include "UI/GPUDriverTestScreen.h"
|
|
#include "UI/DeveloperToolsScreen.h"
|
|
#include "UI/DevScreens.h"
|
|
#include "UI/DriverManagerScreen.h"
|
|
#include "UI/DisplayLayoutScreen.h"
|
|
#include "UI/GameSettingsScreen.h"
|
|
#include "UI/OnScreenDisplay.h"
|
|
#include "UI/IconCache.h"
|
|
#include "UI/MiscViews.h"
|
|
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
|
|
|
static bool CheckKgslPresent() {
|
|
constexpr auto KgslPath{ "/dev/kgsl-3d0" };
|
|
|
|
return access(KgslPath, F_OK) == 0;
|
|
}
|
|
|
|
static bool SupportsCustomDriver() {
|
|
return android_get_device_api_level() >= 28 && CheckKgslPresent();
|
|
}
|
|
|
|
#else
|
|
|
|
static bool SupportsCustomDriver() {
|
|
#ifdef _DEBUG
|
|
return false; // change to true to debug driver installation on other platforms
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
static std::string PostShaderTranslateName(std::string_view value) {
|
|
const ShaderInfo *info = GetPostShaderInfo(value);
|
|
if (info) {
|
|
auto ps = GetI18NCategory(I18NCat::POSTSHADERS);
|
|
return std::string(ps->T(value, info->name));
|
|
} else {
|
|
return std::string(value);
|
|
}
|
|
}
|
|
|
|
DeveloperToolsScreen::DeveloperToolsScreen(const Path &gamePath)
|
|
: UITabbedBaseDialogScreen(gamePath, &g_Config.iDeveloperSettingsCurrentTab, TabDialogFlags::AddAutoTitles) {}
|
|
|
|
void DeveloperToolsScreen::CreateTextureReplacementTab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
auto di = GetI18NCategory(I18NCat::DIALOG);
|
|
|
|
ChoiceGroup *textureReplacement = list->Add(new ChoiceGroup(dev->T("Texture Replacement")));
|
|
textureReplacement->Add(new CheckBox(&g_Config.bSaveNewTextures, dev->T("Save new textures")));
|
|
textureReplacement->Add(new CheckBox(&g_Config.bReplaceTextures, dev->T("Replace textures")));
|
|
|
|
Choice *createTextureIni = textureReplacement->Add(new Choice(dev->T("Create/Open textures.ini file for current game")));
|
|
createTextureIni->OnClick.Handle(this, &DeveloperToolsScreen::OnOpenTexturesIniFile);
|
|
createTextureIni->SetEnabledFunc([&] {
|
|
if (!PSP_IsInited())
|
|
return false;
|
|
|
|
// Disable the choice to Open/Create if the textures.ini file already exists, and we can't open it due to platform support limitations.
|
|
if (!System_GetPropertyBool(SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR)) {
|
|
if (hasTexturesIni_ == HasIni::MAYBE)
|
|
hasTexturesIni_ = TextureReplacer::IniExists(g_paramSFO.GetDiscID()) ? HasIni::YES : HasIni::NO;
|
|
return hasTexturesIni_ != HasIni::YES;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
|
|
// Best string we have
|
|
textureReplacement->Add(new Choice(di->T("Show in folder")))->OnClick.Add([=](UI::EventParams &) {
|
|
Path path;
|
|
if (PSP_IsInited()) {
|
|
std::string gameID = g_paramSFO.GetDiscID();
|
|
path = GetSysDirectory(DIRECTORY_TEXTURES) / gameID;
|
|
} else {
|
|
// Just show the root textures directory.
|
|
path = GetSysDirectory(DIRECTORY_TEXTURES);
|
|
}
|
|
System_ShowFileInFolder(path);
|
|
});
|
|
}
|
|
|
|
static const char *texLoadSpeeds[] = { "Slow (smooth)", "Medium", "Fast", "Instant (may stutter)" };
|
|
PopupMultiChoice *texLoadSpeed = textureReplacement->Add(new PopupMultiChoice(&g_Config.iReplacementTextureLoadSpeed, dev->T("Replacement texture load speed"), texLoadSpeeds, 0, ARRAY_SIZE(texLoadSpeeds), I18NCat::DEVELOPER, screenManager()));
|
|
texLoadSpeed->SetChoiceIcon(3, ImageID("I_WARNING"));
|
|
}
|
|
|
|
void DeveloperToolsScreen::CreateGeneralTab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
|
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
|
|
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
|
|
|
|
ChoiceGroup *cpuSettings = list->Add(new ChoiceGroup(sy->T("CPU Core")));
|
|
|
|
bool canUseJit = System_GetPropertyBool(SYSPROP_CAN_JIT);
|
|
// iOS can now use JIT on all modes, apparently.
|
|
// The bool may come in handy for future non-jit platforms though (UWP XB1?)
|
|
// In iOS App Store builds, we disable the JIT.
|
|
|
|
static const char *cpuCores[] = { "Interpreter", "Dynarec/JIT (recommended)", "IR Interpreter", "JIT using IR" };
|
|
PopupMultiChoice *core = cpuSettings->Add(new PopupMultiChoice(&g_Config.iCpuCore, sy->T("CPU Core"), cpuCores, 0, ARRAY_SIZE(cpuCores), I18NCat::SYSTEM, screenManager()));
|
|
core->OnChoice.Add([=](UI::EventParams &e) {
|
|
OnJitAffectingSetting(e);
|
|
g_Config.NotifyUpdatedCpuCore();
|
|
});
|
|
if (!canUseJit) {
|
|
core->HideChoice(1);
|
|
}
|
|
// Every native JIT is IR-based, so "JIT using IR" is the same as "JIT".
|
|
core->HideChoice(3);
|
|
|
|
// Only the interpreter raises these, but don't hide it behind the CPU core choice - it'd just
|
|
// look like the setting had vanished.
|
|
cpuSettings->Add(new CheckBox(&g_Config.bEnableFPUExceptionTraps, dev->T("Enable FPU exceptions")));
|
|
// Experimental, so not translated yet.
|
|
cpuSettings->Add(new CheckBox(&g_Config.bEmulateMediaEngine, dev->T("Enable ME emulation for homebrew")));
|
|
|
|
cpuSettings->Add(new Choice(dev->T("JIT debug tools")))->OnClick.Handle(this, &DeveloperToolsScreen::OnJitDebugTools);
|
|
cpuSettings->Add(new CheckBox(&g_Config.bShowDeveloperMenu, dev->T("Show in-game developer menu")));
|
|
|
|
AddOverlayList(cpuSettings, screenManager());
|
|
|
|
ChoiceGroup *generalSettings = list->Add(new ChoiceGroup(sy->T("General")));
|
|
|
|
generalSettings->Add(new CheckBox(&g_Config.bEnableLogging, dev->T("Enable Logging")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLoggingChanged);
|
|
generalSettings->Add(new Choice(dev->T("Logging Channels")))->OnClick.Add([this](UI::EventParams &e) {
|
|
screenManager()->push(new LogConfigScreen());
|
|
});
|
|
generalSettings->Add(new CheckBox(&g_Config.bEnableFileLogging, dev->T("Log to file")))->SetEnabledPtr(&g_Config.bEnableLogging);
|
|
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_DESKTOP) {
|
|
generalSettings->Add(new Choice(dev->T("Show log file in folder")))->OnClick.Add([](UI::EventParams &e) {
|
|
Path logFilePath = g_logManager.GetLogFilePath();
|
|
if (logFilePath.empty()) {
|
|
ERROR_LOG(Log::System, "No log file path configured.");
|
|
return;
|
|
}
|
|
if (File::Exists(logFilePath)) {
|
|
System_ShowFileInFolder(logFilePath);
|
|
} else {
|
|
System_LaunchUrl(LaunchUrlType::LOCAL_FILE, logFilePath.NavigateUp().ToString());
|
|
}
|
|
});
|
|
}
|
|
|
|
generalSettings->Add(new CheckBox(&g_Config.bLogFrameDrops, dev->T("Log Dropped Frame Statistics")));
|
|
if (GetGPUBackend() == GPUBackend::VULKAN) {
|
|
generalSettings->Add(new CheckBox(&g_Config.bGpuLogProfiler, dev->T("GPU log profiler")));
|
|
}
|
|
|
|
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
|
|
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
|
|
CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));
|
|
generalSettings->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);
|
|
allowDebugger->SetEnabledPtr(&canAllowDebugger_);
|
|
|
|
CheckBox *localDebugger = generalSettings->Add(new CheckBox(&g_Config.bRemoteDebuggerLocal, dev->T("Use locally hosted remote debugger")));
|
|
localDebugger->SetEnabledPtr(&allowDebugger_);
|
|
|
|
generalSettings->Add(new CheckBox(&g_Config.bAutoSaveLoadSymbols, dev->T("Auto save/load symbols")));
|
|
|
|
generalSettings->Add(new Choice(dev->T("GPI/GPO switches/LEDs")))->OnClick.Add([=](UI::EventParams &e) {
|
|
screenManager()->push(new GPIGPOScreen(dev->T("GPI/GPO switches/LEDs")));
|
|
});
|
|
|
|
generalSettings->Add(new CheckBox(&g_Config.bShowSaveLoadIndicator, dev->T("Show indicator when saving/loading")));
|
|
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
|
static const char *framerateModes[] = { "Default", "Request 60 Hz", "Force 60Hz" };
|
|
PopupMultiChoice *framerateMode = generalSettings->Add(new PopupMultiChoice(&g_Config.iDisplayFramerateMode, gr->T("Framerate mode"), framerateModes, 0, ARRAY_SIZE(framerateModes), I18NCat::GRAPHICS, screenManager()));
|
|
framerateMode->SetEnabledFunc([]() { return System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30; });
|
|
framerateMode->OnChoice.Add([](UI::EventParams &e) {
|
|
System_Notify(SystemNotification::FORCE_RECREATE_ACTIVITY);
|
|
});
|
|
#endif
|
|
|
|
#if PPSSPP_PLATFORM(IOS)
|
|
generalSettings->Add(new NoticeView(NoticeLevel::WARN, ms->T("Moving the memstick directory is NOT recommended on iOS"), ""))->SetWrapText(true);
|
|
generalSettings->Add(new Choice(sy->T("Set Memory Stick folder")))->OnClick.Add(
|
|
[=](UI::EventParams &) {
|
|
SetMemStickDirDarwin(GetRequesterToken());
|
|
});
|
|
#endif
|
|
|
|
// Makes it easy to get savestates out of an iOS device. The file listing shown in MacOS doesn't allow
|
|
// you to descend into directories.
|
|
#if PPSSPP_PLATFORM(IOS)
|
|
generalSettings->Add(new Choice(dev->T("Copy savestates to memstick root")))->OnClick.Handle(this, &DeveloperToolsScreen::OnCopyStatesToRoot);
|
|
#endif
|
|
|
|
auto di = GetI18NCategory(I18NCat::DIALOG);
|
|
// Reuse strings to the max, heh.
|
|
generalSettings->Add(new Choice(ApplySafeSubstitutions("%1: ppsspp.ini", di->T("Copy to clipboard"))))->OnClick.Add([=](UI::EventParams &) {
|
|
auto di = GetI18NCategory(I18NCat::DIALOG);
|
|
std::string configStr = g_Config.GetConfigAsString();
|
|
if (!configStr.empty()) {
|
|
System_CopyStringToClipboard(configStr);
|
|
g_OSD.Show(OSDType::MESSAGE_INFO, ApplySafeSubstitutions(di->T("Copied to clipboard: %1"), "ppsspp.ini"), 0.0f, "copyToClip");
|
|
}
|
|
});
|
|
|
|
#if PLATFORM_SUPPORTS_FILE_HANDLER_PLUGINS
|
|
generalSettings->Add(new CheckBox(&g_Config.bEnableFileHandlerPlugins, dev->T("Enable file handler plugins (insecure)")));
|
|
#endif
|
|
}
|
|
|
|
void DeveloperToolsScreen::CreateTestsTab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
|
|
ChoiceGroup *tests = list->Add(new ChoiceGroup(""));
|
|
tests->Add(new Choice(dev->T("Touchscreen Test")))->OnClick.Add([this](UI::EventParams &e) {
|
|
screenManager()->push(new TouchTestScreen(gamePath_));
|
|
// Handle touchscreen test event
|
|
});
|
|
// tests->Add(new Choice(dev->T("Memstick Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnMemstickTest);
|
|
Choice *frameDumpTests = tests->Add(new Choice(dev->T("Framedump tests")));
|
|
frameDumpTests->OnClick.Add([this](UI::EventParams &e) {
|
|
screenManager()->push(new FrameDumpTestScreen());
|
|
});
|
|
frameDumpTests->SetEnabled(!PSP_IsInited());
|
|
// For now, we only implement GPU driver tests for Vulkan and OpenGL. This is simply
|
|
// because the D3D drivers are generally solid enough to not need this type of investigation.
|
|
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) {
|
|
tests->Add(new Choice(dev->T("GPU Driver Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnGPUDriverTest);
|
|
}
|
|
|
|
// Not useful enough to be made visible.
|
|
/*
|
|
auto memmapTest = tests->Add(new Choice(dev->T("Memory map test")));
|
|
memmapTest->OnClick.Add([this](UI::EventParams &e) {
|
|
MemoryMapTest();
|
|
});
|
|
memmapTest->SetEnabled(PSP_IsInited());
|
|
*/
|
|
}
|
|
|
|
void DeveloperToolsScreen::CreateDumpFileTab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
|
|
ChoiceGroup *dumpSettings = list->Add(new ChoiceGroup(dev->T("Dump files")));
|
|
dumpSettings->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::EBOOT, dev->T("Dump Decrypted Eboot", "Dump Decrypted EBOOT.BIN (If Encrypted) When Booting Game")));
|
|
dumpSettings->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::PRX, dev->T("PRX")));
|
|
dumpSettings->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::Atrac3, dev->T("Atrac3/3+")));
|
|
dumpSettings->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::PBP_ISO, dev->T("ISO from PBP")));
|
|
}
|
|
|
|
void DeveloperToolsScreen::CreateHLETab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
|
|
ChoiceGroup *atracSettings = list->Add(new ChoiceGroup(""));
|
|
atracSettings->Add(new CheckBox(&g_Config.bUseOldAtrac, dev->T("Use the old sceAtrac implementation")));
|
|
|
|
ChoiceGroup *disableHLE = list->Add(new ChoiceGroup(dev->T("Disable HLE")));
|
|
|
|
for (int i = 0; i < (int)DisableHLEFlags::Count; i++) {
|
|
DisableHLEFlags flag = (DisableHLEFlags)(1 << i);
|
|
|
|
// Show a checkbox, unless the setting has graduated to always disabled.
|
|
if (!(flag & AlwaysDisableHLEFlags())) {
|
|
const HLEModuleMeta *meta = GetHLEModuleMetaByFlag(flag);
|
|
if (meta) {
|
|
BitCheckBox *checkBox = disableHLE->Add(new BitCheckBox(&g_Config.iDisableHLE, (int)flag, meta->modname));
|
|
checkBox->SetEnabled(!PSP_IsInited());
|
|
}
|
|
}
|
|
}
|
|
|
|
ChoiceGroup *forceEnableHLE = list->Add(new ChoiceGroup(dev->T("Force-enable HLE")));
|
|
|
|
for (int i = 0; i < (int)DisableHLEFlags::Count; i++) {
|
|
DisableHLEFlags flag = (DisableHLEFlags)(1 << i);
|
|
|
|
// Show a checkbox, only if the setting has graduated to always disabled (and thus it makes sense to force-enable it).
|
|
if (flag & AlwaysDisableHLEFlags()) {
|
|
const HLEModuleMeta *meta = GetHLEModuleMetaByFlag(flag);
|
|
if (meta) {
|
|
BitCheckBox *checkBox = forceEnableHLE->Add(new BitCheckBox(&g_Config.iForceEnableHLE, (int)flag, meta->modname));
|
|
checkBox->SetEnabled(!PSP_IsInited());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeveloperToolsScreen::CreateMIPSTracerTab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
ChoiceGroup *tracerSettings = list->Add(new ChoiceGroup(dev->T("MIPSTracer")));
|
|
|
|
MIPSTracerEnabled_ = mipsTracer.tracing_enabled;
|
|
CheckBox *MIPSTracerEnabled = new CheckBox(&MIPSTracerEnabled_, dev->T("MIPSTracer enabled"));
|
|
tracerSettings->Add(MIPSTracerEnabled)->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerEnabled);
|
|
MIPSTracerEnabled->SetEnabledFunc([]() {
|
|
bool temp = g_Config.iCpuCore == static_cast<int>(CPUCore::IR_INTERPRETER) && PSP_IsInited();
|
|
return temp && Core_IsStepping() && coreState != CORE_POWERDOWN;
|
|
});
|
|
|
|
Choice *TraceDumpPath = tracerSettings->Add(new Choice(dev->T("Select the file path for the trace")));
|
|
TraceDumpPath->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerPathChanged);
|
|
TraceDumpPath->SetEnabledFunc([]() {
|
|
if (!PSP_IsInited())
|
|
return false;
|
|
return true;
|
|
});
|
|
|
|
tracerSettings->Add(new InfoItem(dev->T("Current log file"), mipsTracer.get_logging_path()));
|
|
|
|
PopupSliderChoice* storage_capacity = tracerSettings->Add(
|
|
new PopupSliderChoice(
|
|
&mipsTracer.in_storage_capacity, 0x4'0000, 0x40'0000, 0x10'0000, dev->T("Storage capacity"), 0x10000, screenManager()
|
|
)
|
|
);
|
|
storage_capacity->SetFormat("0x%x asm opcodes");
|
|
storage_capacity->OnChange.Add([&](UI::EventParams &) {
|
|
INFO_LOG(Log::JIT, "User changed the tracer's storage capacity to 0x%x", mipsTracer.in_storage_capacity);
|
|
});
|
|
|
|
PopupSliderChoice* trace_max_size = tracerSettings->Add(
|
|
new PopupSliderChoice(
|
|
&mipsTracer.in_max_trace_size, 0x1'0000, 0x40'0000, 0x10'0000, dev->T("Max allowed trace size"), 0x10000, screenManager()
|
|
)
|
|
);
|
|
trace_max_size->SetFormat("%d basic blocks");
|
|
trace_max_size->OnChange.Add([&](UI::EventParams &) {
|
|
INFO_LOG(Log::JIT, "User changed the tracer's max trace size to %d", mipsTracer.in_max_trace_size);
|
|
});
|
|
|
|
ChoiceGroup *tracerActions = list->Add(new ChoiceGroup(dev->T("MIPSTracer actions")));
|
|
Choice *FlushTrace = tracerActions->Add(new Choice(dev->T("Flush the trace")));
|
|
FlushTrace->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerFlushTrace);
|
|
|
|
Choice *InvalidateJitCache = tracerActions->Add(new Choice(dev->T("Clear the JIT cache")));
|
|
InvalidateJitCache->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearJitCache);
|
|
|
|
Choice *ClearMIPSTracer = tracerActions->Add(new Choice(dev->T("Clear the MIPSTracer")));
|
|
ClearMIPSTracer->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearTracer);
|
|
}
|
|
|
|
void DeveloperToolsScreen::CreateAudioTab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
ChoiceGroup *audioSettings = list->Add(new ChoiceGroup(""));
|
|
audioSettings->Add(new CheckBox(&g_Config.bForceFfmpegForAudioDec, dev->T("Use FFMPEG for all compressed audio")));
|
|
}
|
|
|
|
void DeveloperToolsScreen::CreateUITab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
UIContext *uiContext = screenManager()->getUIContext();
|
|
ChoiceGroup *atlasGroup = list->Add(new ChoiceGroup(""));
|
|
atlasGroup->Add(new Choice(dev->T("Reload UI atlas")))->OnClick.Add([uiContext](UI::EventParams &) {
|
|
uiContext->InvalidateAtlas();
|
|
});
|
|
|
|
auto di = GetI18NCategory(I18NCat::DIALOG);
|
|
auto si = GetI18NCategory(I18NCat::SYSINFO);
|
|
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
|
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
|
|
|
|
// TODO: Move most of these strings out of the SysInfo category.
|
|
|
|
ChoiceGroup *iconCache = list->Add(new ChoiceGroup(si->T("Icon cache")));
|
|
IconCacheStats iconStats = g_iconCache.GetStats();
|
|
iconCache->Add(new InfoItem(si->T("Image data count"), StringFromFormat("%d", iconStats.cachedCount)));
|
|
iconCache->Add(new InfoItem(si->T("Texture count"), StringFromFormat("%d", iconStats.textureCount)));
|
|
iconCache->Add(new InfoItem(si->T("Data size"), NiceSizeFormat(iconStats.dataSize)));
|
|
iconCache->Add(new Choice(di->T("Clear")))->OnClick.Add([&](UI::EventParams &) {
|
|
g_iconCache.ClearData();
|
|
RecreateViews();
|
|
});
|
|
|
|
ChoiceGroup *fontCache = list->Add(new ChoiceGroup(si->T("Font cache")));
|
|
const TextDrawer *text = screenManager()->getUIContext()->Text();
|
|
if (text) {
|
|
fontCache->Add(new InfoItem(si->T("Texture count"), StringFromFormat("%d", text->GetStringCacheSize())));
|
|
fontCache->Add(new InfoItem(si->T("Data size"), NiceSizeFormat(text->GetCacheDataSize())));
|
|
}
|
|
|
|
ChoiceGroup *sliderTest = list->Add(new ChoiceGroup(si->T("Slider test")));
|
|
sliderTest->Add(new Slider(&testSliderValue_, 0, 100, 1, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
|
|
|
|
// Same encoding as in GameSettingsScreen: None is -1.
|
|
static const char *positions[] = { "None", "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right" };
|
|
|
|
ChoiceGroup *notificationTests = list->Add(new ChoiceGroup(si->T("Notification tests")));
|
|
notificationTests->Add(new Choice(si->T("Error")))->OnClick.Add([&](UI::EventParams &) {
|
|
std::string str = "Error " + CodepointToUTF8(0x1F41B) + CodepointToUTF8(0x1F41C) + CodepointToUTF8(0x1F914);
|
|
g_OSD.Show(OSDType::MESSAGE_ERROR, str);
|
|
});
|
|
notificationTests->Add(new Choice(si->T("Warning")))->OnClick.Add([&](UI::EventParams &) {
|
|
g_OSD.Show(OSDType::MESSAGE_WARNING, "Warning, a pretty long warning heading", "Some\nAdditional\nDetail, some of which is very, very long and wide and will need line wrapping on most screens.");
|
|
});
|
|
notificationTests->Add(new Choice(si->T("Info")))->OnClick.Add([&](UI::EventParams &) {
|
|
g_OSD.Show(OSDType::MESSAGE_INFO, "Info, info info info info info info info info info info");
|
|
});
|
|
// This one is clickable
|
|
notificationTests->Add(new Choice(si->T("Success")))->OnClick.Add([&](UI::EventParams &) {
|
|
g_OSD.Show(OSDType::MESSAGE_SUCCESS, "Success", 0.0f, "clickable");
|
|
g_OSD.SetClickCallback("clickable", []() {
|
|
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.google.com/");
|
|
});
|
|
});
|
|
notificationTests->Add(new Choice(sy->T("RetroAchievements")))->OnClick.Add([&](UI::EventParams &) {
|
|
g_OSD.Show(OSDType::MESSAGE_WARNING, "RetroAchievements warning", "", "I_RETROACHIEVEMENTS_LOGO");
|
|
});
|
|
ChoiceGroup *progressTests = list->Add(new ChoiceGroup(si->T("Progress tests")));
|
|
progressTests->Add(new Choice(si->T("30%")))->OnClick.Add([&](UI::EventParams &) {
|
|
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 30, 0.0f);
|
|
});
|
|
progressTests->Add(new Choice(si->T("100%")))->OnClick.Add([&](UI::EventParams &) {
|
|
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 100, 1.0f);
|
|
});
|
|
progressTests->Add(new Choice(si->T("N/A%")))->OnClick.Add([&](UI::EventParams &) {
|
|
g_OSD.SetProgressBar("testprogress", "Test Progress", 0, 0, 0, 0.0f);
|
|
});
|
|
progressTests->Add(new Choice(si->T("Success")))->OnClick.Add([&](UI::EventParams &) {
|
|
g_OSD.RemoveProgressBar("testprogress", true, 0.5f);
|
|
});
|
|
progressTests->Add(new Choice(si->T("Failure")))->OnClick.Add([&](UI::EventParams &) {
|
|
g_OSD.RemoveProgressBar("testprogress", false, 0.5f);
|
|
});
|
|
ChoiceGroup *achievementTests = list->Add(new ChoiceGroup(si->T("Achievement tests")));
|
|
achievementTests->Add(new Choice(si->T("Leaderboard tracker: Show")))->OnClick.Add([=](UI::EventParams &) {
|
|
g_OSD.ShowLeaderboardTracker(1, "My leaderboard tracker", true);
|
|
});
|
|
achievementTests->Add(new Choice(si->T("Leaderboard tracker: Update")))->OnClick.Add([=](UI::EventParams &) {
|
|
g_OSD.ShowLeaderboardTracker(1, "Updated tracker", true);
|
|
});
|
|
achievementTests->Add(new Choice(si->T("Leaderboard tracker: Hide")))->OnClick.Add([=](UI::EventParams &) {
|
|
g_OSD.ShowLeaderboardTracker(1, "", false);
|
|
});
|
|
|
|
ChoiceGroup *notificationSettings = list->Add(new ChoiceGroup(ac->T("Notifications")));
|
|
notificationSettings->Add(new PopupMultiChoice(&g_Config.iNotificationPos, sy->T("Notification screen position"), positions, -1, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()));
|
|
notificationSettings->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardTrackerPos, ac->T("Leaderboard tracker"), positions, -1, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()));
|
|
|
|
#ifdef _DEBUG
|
|
notificationSettings->Add(new CheckBox(&pretendIngame_, ac->T("Pretend to be in-game (for testing)")));
|
|
#endif
|
|
|
|
#if defined(_DEBUG) || PPSSPP_PLATFORM(ANDROID)
|
|
ChoiceGroup *crashTests = list->Add(new ChoiceGroup(ac->T("Assert")));
|
|
#endif
|
|
#ifdef _DEBUG
|
|
// Untranslated string because this is debug mode only, only for PPSSPP developers.
|
|
crashTests->Add(new Choice("Assert"))->OnClick.Add([=](UI::EventParams &) {
|
|
_dbg_assert_msg_(false, "Test assert message");
|
|
});
|
|
#endif
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
|
crashTests->Add(new Choice(si->T("Exception")))->OnClick.Add([&](UI::EventParams &) {
|
|
System_Notify(SystemNotification::TEST_JAVA_EXCEPTION);
|
|
});
|
|
#endif
|
|
}
|
|
|
|
void DeveloperToolsScreen::CreateNetworkTab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
|
|
auto ri = GetI18NCategory(I18NCat::REMOTEISO);
|
|
auto nw = GetI18NCategory(I18NCat::NETWORKING);
|
|
ChoiceGroup *networkSettings = list->Add(new ChoiceGroup(ms->T("Networking")));
|
|
networkSettings->Add(new CheckBox(&g_Config.bDontDownloadInfraJson, dev->T("Don't download infra-dns.json")));
|
|
// This is shared between RemoteISO and the remote debugger.
|
|
networkSettings->Add(new PopupSliderChoice(&g_Config.iRemoteISOPort, 0, 65535, 0, ri->T("Local Server Port", "Local Server Port"), 100, screenManager()));
|
|
ChoiceGroup *adhocSettings = list->Add(new ChoiceGroup(nw->T("AdHoc server")));
|
|
adhocSettings->Add(new CheckBox(&g_Config.bAdhocServerShowPlayerPorts, nw->T("Show player port numbers")));
|
|
}
|
|
|
|
// TODO: Make this generic
|
|
extern int DefaultDepthRaster();
|
|
|
|
void DeveloperToolsScreen::CreateGraphicsTab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
|
|
auto ps = GetI18NCategory(I18NCat::POSTSHADERS);
|
|
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
|
auto si = GetI18NCategory(I18NCat::SYSINFO);
|
|
|
|
Draw::DrawContext *draw = screenManager()->getDrawContext();
|
|
|
|
ChoiceGroup *generalSettings = list->Add(new ChoiceGroup(sy->T("General")));
|
|
generalSettings->Add(new CheckBox(&g_Config.bVendorBugChecksEnabled, dev->T("Enable driver bug workarounds")));
|
|
generalSettings->Add(new CheckBox(&g_Config.bShaderCache, dev->T("Enable shader cache")));
|
|
generalSettings->Add(new CheckBox(&g_Config.bHardwareTransform, gr->T("Hardware Transform")));
|
|
|
|
auto displayRefreshRate = generalSettings->Add(new PopupSliderChoice(&g_Config.iDisplayRefreshRate, 60, 1000, 60, dev->T("Display refresh rate"), 1, screenManager()));
|
|
displayRefreshRate->SetFormat(si->T("%d Hz"));
|
|
|
|
ChoiceGroup *vulkanSettings = list->Add(new ChoiceGroup(dev->T("Vulkan")));
|
|
vulkanSettings->Add(new CheckBox(&g_Config.bVulkanDisableImplicitLayers, dev->T("Prevent loading overlays")));
|
|
|
|
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {
|
|
vulkanSettings->Add(new CheckBox(&g_Config.bRenderMultiThreading, dev->T("Multi-threaded rendering"), ""))->OnClick.Add([](UI::EventParams &e) {
|
|
// TODO: Not translating yet. Will combine with other translations of settings that need restart.
|
|
g_OSD.Show(OSDType::MESSAGE_WARNING, "Restart required");
|
|
});
|
|
}
|
|
|
|
if (GetGPUBackend() == GPUBackend::VULKAN && SupportsCustomDriver()) {
|
|
auto driverChoice = vulkanSettings->Add(new Choice(gr->T("AdrenoTools driver manager")));
|
|
driverChoice->OnClick.Add([=](UI::EventParams &e) {
|
|
screenManager()->push(new DriverManagerScreen(gamePath_));
|
|
});
|
|
}
|
|
|
|
static const char *depthRasterModes[] = { "Auto", "Low", "Off", "Always on" };
|
|
|
|
PopupMultiChoice *depthRasterMode = vulkanSettings->Add(new PopupMultiChoice(&g_Config.iDepthRasterMode, gr->T("Lens flare occlusion"), depthRasterModes, 0, ARRAY_SIZE(depthRasterModes), I18NCat::GRAPHICS, screenManager()));
|
|
depthRasterMode->SetDisabledPtr(&g_Config.bSoftwareRendering);
|
|
depthRasterMode->SetDefault(DefaultDepthRaster());
|
|
depthRasterMode->SetChoiceIcon(3, ImageID("I_WARNING")); // It's a performance trap.
|
|
|
|
ChoiceGroup *ubershaderSettings = list->Add(new ChoiceGroup(dev->T("Ubershaders")));
|
|
if (draw->GetShaderLanguageDesc().bitwiseOps && !draw->GetBugs().Has(Draw::Bugs::UNIFORM_INDEXING_BROKEN)) {
|
|
// If the above if fails, the checkbox is redundant since it'll be force disabled anyway.
|
|
ubershaderSettings->Add(new CheckBox(&g_Config.bUberShaderVertex, dev->T("Vertex")));
|
|
}
|
|
|
|
// Experimental, allow some VR features without OpenXR
|
|
if (GetGPUBackend() == GPUBackend::OPENGL) {
|
|
auto vr = GetI18NCategory(I18NCat::VR);
|
|
ChoiceGroup *vrSettings = list->Add(new ChoiceGroup(vr->T("Virtual reality")));
|
|
vrSettings->Add(new CheckBox(&g_Config.bForceVR, vr->T("VR camera")));
|
|
}
|
|
|
|
// Experimental, will move to main graphics settings later.
|
|
bool multiViewSupported = draw->GetDeviceCaps().multiViewSupported;
|
|
|
|
auto enableStereo = [=]() -> bool {
|
|
return g_Config.bStereoRendering && multiViewSupported;
|
|
};
|
|
|
|
if (multiViewSupported) {
|
|
ChoiceGroup *stereoSettings = list->Add(new ChoiceGroup(gr->T("Stereo rendering")));
|
|
stereoSettings->Add(new CheckBox(&g_Config.bStereoRendering, gr->T("Stereo rendering")));
|
|
|
|
ChoiceWithValueDisplay *stereoShaderChoice = stereoSettings->Add(new ChoiceWithValueDisplay(&g_Config.sStereoToMonoShader, gr->T("Stereo display shader"), &PostShaderTranslateName));
|
|
stereoShaderChoice->SetEnabledFunc(enableStereo);
|
|
stereoShaderChoice->OnClick.Add([=](EventParams &e) {
|
|
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
|
|
auto procScreen = new PostProcScreen(gr->T("Stereo display shader"), 0, true);
|
|
procScreen->OnChoice.Add([this](EventParams &) {
|
|
RecreateViews();
|
|
});
|
|
if (e.v)
|
|
procScreen->SetPopupOrigin(e.v);
|
|
screenManager()->push(procScreen);
|
|
});
|
|
|
|
const ShaderInfo *shaderInfo = GetPostShaderInfo(g_Config.sStereoToMonoShader);
|
|
if (shaderInfo) {
|
|
for (size_t i = 0; i < ARRAY_SIZE(shaderInfo->settings); ++i) {
|
|
auto &setting = shaderInfo->settings[i];
|
|
if (!setting.name.empty()) {
|
|
std::string key = StringFromFormat("%sSettingCurrentValue%d", shaderInfo->section.c_str(), i + 1);
|
|
bool keyExisted = g_Config.mPostShaderSetting.find(key) != g_Config.mPostShaderSetting.end();
|
|
auto &value = g_Config.mPostShaderSetting[key];
|
|
if (!keyExisted)
|
|
value = setting.value;
|
|
|
|
PopupSliderChoiceFloat *settingValue = stereoSettings->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, setting.value, ps->T(setting.name), setting.step, screenManager()));
|
|
settingValue->SetEnabledFunc([=] {
|
|
return !g_Config.bSkipBufferEffects && enableStereo();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeveloperToolsScreen::CreateCrashHistoryTab(UI::LinearLayout *list) {
|
|
using namespace UI;
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
auto di = GetI18NCategory(I18NCat::DIALOG);
|
|
std::vector<std::string> reports = Android_GetNativeCrashHistory(20);
|
|
ChoiceGroup *crashHistory = list->Add(new ChoiceGroup(dev->T("Crash history")));
|
|
if (reports.empty()) {
|
|
crashHistory->Add(new TextView(di->T("None")));
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < reports.size(); i++) {
|
|
std::string name = StringFromFormat("Crash %d", (int)i);
|
|
CollapsibleSection *section = crashHistory->Add(new CollapsibleSection(name));
|
|
const std::string report = reports[i];
|
|
if (report.size() > 150) {
|
|
section->Add(new Choice(di->T("Copy to clipboard"), ImageID("I_FILE_COPY")))->OnClick.Add([report](UI::EventParams&) {
|
|
System_CopyStringToClipboard(report);
|
|
});
|
|
}
|
|
section->Add(new TextView(report, FLAG_WRAP_TEXT | FLAG_DYNAMIC_ASCII, true));
|
|
}
|
|
}
|
|
|
|
void DeveloperToolsScreen::CreateTabs() {
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
|
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
|
|
|
|
AddTab("General", sy->T("General"), [this](UI::LinearLayout *parent) {
|
|
CreateGeneralTab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
AddTab("TextureReplacement", dev->T("Texture Replacement"), [this](UI::LinearLayout *parent) {
|
|
CreateTextureReplacementTab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
AddTab("Graphics", ms->T("Graphics"), [this](UI::LinearLayout *parent) {
|
|
CreateGraphicsTab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
AddTab("Networking", ms->T("Networking"), [this](UI::LinearLayout *parent) {
|
|
CreateNetworkTab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
AddTab("Audio", ms->T("Audio"), [this](UI::LinearLayout *parent) {
|
|
CreateAudioTab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
AddTab("Tests", dev->T("Tests"), [this](UI::LinearLayout *parent) {
|
|
CreateTestsTab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
AddTab("UI", dev->T("UI"), [this](UI::LinearLayout *parent) {
|
|
CreateUITab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
AddTab("DumpFiles", dev->T("Dump files"), [this](UI::LinearLayout *parent) {
|
|
CreateDumpFileTab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
AddTab("HLE", dev->T("HLE"), [this](UI::LinearLayout *parent) {
|
|
CreateHLETab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
#if !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(SWITCH)
|
|
AddTab("MIPSTracer", dev->T("MIPSTracer"), [this](UI::LinearLayout *parent) {
|
|
CreateMIPSTracerTab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
#endif
|
|
//#if PPSSPP_PLATFORM(ANDROID)
|
|
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30) {
|
|
AddTab("Crash history", dev->T("Crash history"), [this](UI::LinearLayout *parent) {
|
|
CreateCrashHistoryTab(parent->Add(new UI::ChoiceGroupList()));
|
|
});
|
|
}
|
|
//#endif
|
|
|
|
// Reconsider whenever recreating views.
|
|
hasTexturesIni_ = HasIni::MAYBE;
|
|
}
|
|
|
|
void DeveloperToolsScreen::onFinish(DialogResult result) {
|
|
UIScreen::onFinish(result);
|
|
g_Config.Save("DeveloperToolsScreen::onFinish");
|
|
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnLoggingChanged(UI::EventParams &e) {
|
|
System_Notify(SystemNotification::TOGGLE_DEBUG_CONSOLE);
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnOpenTexturesIniFile(UI::EventParams &e) {
|
|
std::string gameID = g_paramSFO.GetDiscID();
|
|
Path generatedFilename;
|
|
|
|
if (TextureReplacer::GenerateIni(gameID, generatedFilename)) {
|
|
if (System_GetPropertyBool(SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR)) {
|
|
File::OpenFileInEditor(generatedFilename);
|
|
} else {
|
|
// Can't do much here, let's send a "toast" so the user sees that something happened.
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
System_Toast((GetFriendlyPath(generatedFilename) + ": " + dev->T_cstr("Texture ini file created")).c_str());
|
|
}
|
|
|
|
hasTexturesIni_ = HasIni::YES;
|
|
}
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnJitDebugTools(UI::EventParams &e) {
|
|
screenManager()->push(new JitDebugScreen());
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnGPUDriverTest(UI::EventParams &e) {
|
|
screenManager()->push(new GPUDriverTestScreen());
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnJitAffectingSetting(UI::EventParams &e) {
|
|
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnCopyStatesToRoot(UI::EventParams &e) {
|
|
Path savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE);
|
|
Path root_dir = GetSysDirectory(DIRECTORY_MEMSTICK_ROOT);
|
|
|
|
std::vector<File::FileInfo> files;
|
|
GetFilesInDir(savestate_dir, &files, nullptr, 0);
|
|
|
|
for (const File::FileInfo &file : files) {
|
|
Path src = file.fullName;
|
|
Path dst = root_dir / file.name;
|
|
INFO_LOG(Log::System, "Copying file '%s' to '%s'", src.c_str(), dst.c_str());
|
|
File::Copy(src, dst);
|
|
}
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) {
|
|
if (allowDebugger_) {
|
|
// Only needed for connecting from another device, but that's the usual case.
|
|
System_RequestLocalNetworkPermission();
|
|
StartWebServer(WebServerFlags::DEBUGGER);
|
|
} else {
|
|
StopWebServer(WebServerFlags::DEBUGGER);
|
|
}
|
|
// Persist the setting. Maybe should separate?
|
|
g_Config.bRemoteDebuggerOnStartup = allowDebugger_;
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) {
|
|
if (MIPSTracerEnabled_) {
|
|
u32 capacity = mipsTracer.in_storage_capacity;
|
|
u32 trace_size = mipsTracer.in_max_trace_size;
|
|
|
|
mipsTracer.initialize(capacity, trace_size);
|
|
mipsTracer.start_tracing();
|
|
} else {
|
|
mipsTracer.stop_tracing();
|
|
}
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnMIPSTracerPathChanged(UI::EventParams &e) {
|
|
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
|
|
System_BrowseForFileSave(
|
|
GetRequesterToken(),
|
|
dev->T("Select the log file"),
|
|
"trace.txt",
|
|
BrowseFileType::ANY,
|
|
[this](std::string_view value, int) {
|
|
mipsTracer.set_logging_path(std::string(value));
|
|
// The views may have been recreated while the dialog was open, so don't keep a pointer to the path view.
|
|
RecreateViews();
|
|
}
|
|
);
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) {
|
|
mipsTracer.flush_to_file();
|
|
// The error logs are emitted inside the tracer
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnMIPSTracerClearJitCache(UI::EventParams &e) {
|
|
INFO_LOG(Log::JIT, "Clearing the jit cache...");
|
|
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnMIPSTracerClearTracer(UI::EventParams &e) {
|
|
INFO_LOG(Log::JIT, "Clearing the MIPSTracer...");
|
|
mipsTracer.clear();
|
|
}
|
|
|
|
void DeveloperToolsScreen::update() {
|
|
UIBaseDialogScreen::update();
|
|
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
|
|
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
|
|
|
|
// For the UI tab's notification tests.
|
|
if (pretendIngame_) {
|
|
g_OSD.NudgeIngameNotifications();
|
|
}
|
|
}
|
|
|
|
void DeveloperToolsScreen::MemoryMapTest() {
|
|
int sum = 0;
|
|
for (uint64_t addr = 0; addr < 0x100000000ULL; addr += 0x1000) {
|
|
const u32 addr32 = (u32)addr;
|
|
if (Memory::IsValidAddress(addr32)) {
|
|
sum += Memory::ReadUnchecked_U32(addr32);
|
|
}
|
|
}
|
|
// Just to force the compiler to do things properly.
|
|
INFO_LOG(Log::JIT, "Total sum: %08x", sum);
|
|
}
|
|
|
|
static bool RunMemstickTest(std::string *error) {
|
|
Path testRoot = GetSysDirectory(PSPDirectories::DIRECTORY_CACHE) / "test";
|
|
|
|
*error = "N/A";
|
|
|
|
File::CreateDir(testRoot);
|
|
if (!File::Exists(testRoot)) {
|
|
return false;
|
|
}
|
|
|
|
Path testFilePath = testRoot / "temp.txt";
|
|
File::CreateEmptyFile(testFilePath);
|
|
|
|
// Attempt to delete the test root. This should fail since it still contains files.
|
|
File::DeleteDir(testRoot);
|
|
if (!File::Exists(testRoot)) {
|
|
*error = "testroot was deleted with a file in it!";
|
|
return false;
|
|
}
|
|
|
|
File::Delete(testFilePath);
|
|
if (File::Exists(testFilePath)) {
|
|
*error = "testfile wasn't deleted";
|
|
return false;
|
|
}
|
|
|
|
File::DeleteDir(testRoot);
|
|
if (File::Exists(testRoot)) {
|
|
*error = "testroot wasn't deleted, even when empty";
|
|
return false;
|
|
}
|
|
|
|
*error = "passed";
|
|
return true;
|
|
}
|
|
|
|
void DeveloperToolsScreen::OnMemstickTest(UI::EventParams &e) {
|
|
std::string error;
|
|
if (RunMemstickTest(&error)) {
|
|
g_OSD.Show(OSDType::MESSAGE_SUCCESS, "Memstick test passed", error, 6.0f);
|
|
} else {
|
|
g_OSD.Show(OSDType::MESSAGE_ERROR, "Memstick test failed", error, 6.0f);
|
|
}
|
|
}
|