From abdf5ad60cb595bbe39cbc546f3a973dc7f79aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 24 May 2021 22:40:59 +0200 Subject: [PATCH 01/16] compat.ini: Disable range culling for Star Wars: Lethal Alliance. See #11551 --- assets/compat.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/compat.ini b/assets/compat.ini index 2b2d0e7f00..7378c8fa08 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -223,6 +223,10 @@ ULUS10070 = true ULES00719 = true # No US release, it seems +# Star Wars: Lethal Alliance (#11551) +ULES00599 = true +ULUS10188 = true + [ClearToRAM] # SOCOM Navy Seals games require this. See issue #8973. # Navy Seals : Tactical Strike From 8e66239404f49ace9c811d0bdc9cb6f3fa4f9fda Mon Sep 17 00:00:00 2001 From: LunaMoo Date: Mon, 24 May 2021 22:54:04 +0200 Subject: [PATCH 02/16] Fix UI import cheat crash. --- UI/CwCheatScreen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index fdc0810117..b5fde7d5f6 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -211,6 +211,7 @@ UI::EventReturn CwCheatScreen::OnImportCheat(UI::EventParams ¶ms) { if (!in) { WARN_LOG(COMMON, "Unable to open %s\n", cheatFile.c_str()); + return UI::EVENT_SKIPPED; } char linebuf[2048]{}; From 79e3d1c5f9c47899a786d8e3053df8557e006b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 27 May 2021 01:02:02 +0200 Subject: [PATCH 03/16] Buildfix (debug mode) libpng isn't so happy if you force include the memory debug wrappers. We should probably not have it in Common anyway, not sure how it ended up there. --- Common/Common.vcxproj | 99 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 5e7647b71a..476039ff0f 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -551,6 +551,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -561,6 +567,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -571,6 +583,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -581,6 +599,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -591,8 +615,21 @@ NotUsing NotUsing NotUsing + + + + + + + + + + + + + + - NotUsing NotUsing @@ -602,6 +639,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -612,6 +655,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -622,6 +671,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -632,6 +687,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -642,6 +703,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -652,6 +719,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -662,6 +735,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -672,6 +751,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -682,6 +767,12 @@ NotUsing NotUsing NotUsing + + + + + + NotUsing @@ -692,6 +783,12 @@ NotUsing NotUsing NotUsing + + + + + + From 312506a41be70a9de3074bc579c9c61d5441111f Mon Sep 17 00:00:00 2001 From: kotcrab <4594081+kotcrab@users.noreply.github.com> Date: Thu, 27 May 2021 14:10:40 +0200 Subject: [PATCH 04/16] Fix crash in HTTPServer when processing invalid request --- Common/Net/HTTPServer.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Common/Net/HTTPServer.cpp b/Common/Net/HTTPServer.cpp index 882d49a965..c787c3f829 100644 --- a/Common/Net/HTTPServer.cpp +++ b/Common/Net/HTTPServer.cpp @@ -72,7 +72,9 @@ Request::Request(int fd) Request::~Request() { Close(); - _assert_(in_->Empty()); + if (!in_->Empty()) { + ERROR_LOG(IO, "Input not empty - invalid request?"); + } delete in_; if (!out_->Empty()) { ERROR_LOG(IO, "Output not empty - connection abort?"); @@ -330,6 +332,10 @@ void Server::HandleRequest(const Request &request) { } void Server::HandleRequestDefault(const Request &request) { + if (request.resource() == nullptr) { + fallback_(request); + return; + } // First, look through all handlers. If we got one, use it. auto handler = handlers_.find(request.resource()); if (handler != handlers_.end()) { From ecc2f6268842c5154342605dd51ddd5b129a4365 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 27 May 2021 23:12:40 -0700 Subject: [PATCH 05/16] Common: Zero initialize readlink buffer. See #14483. --- Common/File/FileUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index b528d21b38..f1c12ede97 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -752,7 +752,7 @@ const Path &GetExeDirectory() { ExePath = Path(program_path); #elif (defined(__APPLE__) && !PPSSPP_PLATFORM(IOS)) || defined(__linux__) || defined(KERN_PROC_PATHNAME) - char program_path[4096]; + char program_path[4096]{}; uint32_t program_path_size = sizeof(program_path) - 1; #if defined(__linux__) From 64e007c0ce2268e70311434e0bd641f5458d203f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 23 May 2021 21:23:13 -0700 Subject: [PATCH 06/16] Debugger: Add stub API for replay functionality. --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 ++ Core/Debugger/WebSocket.cpp | 2 + Core/Debugger/WebSocket/ReplaySubscriber.cpp | 60 ++++++++++++++++++++ Core/Debugger/WebSocket/ReplaySubscriber.h | 30 ++++++++++ Core/Replay.cpp | 10 ++-- Core/Replay.h | 6 +- UWP/CoreUWP/CoreUWP.vcxproj | 2 + UWP/CoreUWP/CoreUWP.vcxproj.filters | 6 ++ android/jni/Android.mk | 1 + 11 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 Core/Debugger/WebSocket/ReplaySubscriber.cpp create mode 100644 Core/Debugger/WebSocket/ReplaySubscriber.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f4661a6d8d..8dc0498dd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1580,6 +1580,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/WebSocket/MemoryInfoSubscriber.h Core/Debugger/WebSocket/MemorySubscriber.cpp Core/Debugger/WebSocket/MemorySubscriber.h + Core/Debugger/WebSocket/ReplaySubscriber.cpp + Core/Debugger/WebSocket/ReplaySubscriber.h Core/Debugger/WebSocket/SteppingBroadcaster.cpp Core/Debugger/WebSocket/SteppingBroadcaster.h Core/Debugger/WebSocket/SteppingSubscriber.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 68e918a7f0..ececc51b5f 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -525,6 +525,7 @@ + @@ -1076,6 +1077,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index c6a59d5d51..43a36fa022 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1172,6 +1172,9 @@ Ext\libzip + + Debugger\WebSocket + @@ -1886,6 +1889,9 @@ Ext\libzip + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index f3f83254af..3d85f4b6fd 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -59,6 +59,7 @@ #include "Core/Debugger/WebSocket/InputSubscriber.h" #include "Core/Debugger/WebSocket/MemoryInfoSubscriber.h" #include "Core/Debugger/WebSocket/MemorySubscriber.h" +#include "Core/Debugger/WebSocket/ReplaySubscriber.h" #include "Core/Debugger/WebSocket/SteppingSubscriber.h" typedef DebuggerSubscriber *(*SubscriberInit)(DebuggerEventHandlerMap &map); @@ -73,6 +74,7 @@ static const std::vector subscribers({ &WebSocketInputInit, &WebSocketMemoryInfoInit, &WebSocketMemoryInit, + &WebSocketReplayInit, &WebSocketSteppingInit, }); diff --git a/Core/Debugger/WebSocket/ReplaySubscriber.cpp b/Core/Debugger/WebSocket/ReplaySubscriber.cpp new file mode 100644 index 0000000000..3a33f90b38 --- /dev/null +++ b/Core/Debugger/WebSocket/ReplaySubscriber.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2021- 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 "Core/Replay.h" +#include "Core/Debugger/WebSocket/ReplaySubscriber.h" + +DebuggerSubscriber *WebSocketReplayInit(DebuggerEventHandlerMap &map) { + // No need to bind or alloc state, these are all global. + map["replay.begin"] = &WebSocketReplayBegin; + map["replay.abort"] = &WebSocketReplayAbort; + map["replay.flush"] = &WebSocketReplayFlush; + map["replay.execute"] = &WebSocketReplayExecute; + map["replay.status"] = &WebSocketReplayStatus; + map["replay.time.get"] = &WebSocketReplayTimeGet; + map["replay.time.set"] = &WebSocketReplayTimeSet; + + return nullptr; +} + +void WebSocketReplayBegin(DebuggerRequest &req) { + // TODO +} + +void WebSocketReplayAbort(DebuggerRequest &req) { + // TODO +} + +void WebSocketReplayFlush(DebuggerRequest &req) { + // TODO +} + +void WebSocketReplayExecute(DebuggerRequest &req) { + // TODO +} + +void WebSocketReplayStatus(DebuggerRequest &req) { + // TODO +} + +void WebSocketReplayTimeGet(DebuggerRequest &req) { + // TODO +} + +void WebSocketReplayTimeSet(DebuggerRequest &req) { + // TODO +} diff --git a/Core/Debugger/WebSocket/ReplaySubscriber.h b/Core/Debugger/WebSocket/ReplaySubscriber.h new file mode 100644 index 0000000000..901e54f60e --- /dev/null +++ b/Core/Debugger/WebSocket/ReplaySubscriber.h @@ -0,0 +1,30 @@ +// Copyright (c) 2021- 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/. + +#pragma once + +#include "Core/Debugger/WebSocket/WebSocketUtils.h" + +DebuggerSubscriber *WebSocketReplayInit(DebuggerEventHandlerMap &map); + +void WebSocketReplayBegin(DebuggerRequest &req); +void WebSocketReplayAbort(DebuggerRequest &req); +void WebSocketReplayFlush(DebuggerRequest &req); +void WebSocketReplayExecute(DebuggerRequest &req); +void WebSocketReplayStatus(DebuggerRequest &req); +void WebSocketReplayTimeGet(DebuggerRequest &req); +void WebSocketReplayTimeSet(DebuggerRequest &req); diff --git a/Core/Replay.cpp b/Core/Replay.cpp index 93b0968c25..bac97e2835 100644 --- a/Core/Replay.cpp +++ b/Core/Replay.cpp @@ -116,7 +116,7 @@ struct ReplayFileInfo { struct ReplayItem { ReplayItemHeader info; - std::vector data; + std::vector data; ReplayItem(ReplayItemHeader h) : info(h) { } @@ -136,7 +136,7 @@ static uint8_t lastAnalog[2][2]{}; static size_t replayDiskPos = 0; static bool diskFailed = false; -void ReplayExecuteBlob(const std::vector &data) { +void ReplayExecuteBlob(const std::vector &data) { ReplayAbort(); // Rough estimate. @@ -178,7 +178,7 @@ bool ReplayExecuteFile(const Path &filename) { return false; } - std::vector data; + std::vector data; auto loadData = [&]() { // TODO: Maybe stream instead. size_t sz = File::GetFileSize(fp); @@ -243,7 +243,7 @@ void ReplayBeginSave() { replayState = ReplayState::SAVE; } -void ReplayFlushBlob(std::vector *data) { +void ReplayFlushBlob(std::vector *data) { size_t sz = replayItems.size() * sizeof(ReplayItemHeader); // Add in any side data. for (const auto &item : replayItems) { @@ -289,7 +289,7 @@ bool ReplayFlushFile(const Path &filename) { size_t c = replayItems.size(); if (success && c != 0) { // TODO: Maybe stream instead. - std::vector data; + std::vector data; ReplayFlushBlob(&data); success = fwrite(&data[0], data.size(), 1, fp) == 1; diff --git a/Core/Replay.h b/Core/Replay.h index 2229a6dee8..3035330812 100644 --- a/Core/Replay.h +++ b/Core/Replay.h @@ -49,17 +49,17 @@ enum class ReplayAction : uint8_t { struct PSPFileInfo; // Replay from data in memory. Does not manipulate base time / RNG state. -void ReplayExecuteBlob(const std::vector &data); +void ReplayExecuteBlob(const std::vector &data); // Replay from data in a file. Returns false if invalid. bool ReplayExecuteFile(const Path &filename); -// Returns whether there are unexected events to replay. +// Returns whether there are unexecuted events to replay. bool ReplayHasMoreEvents(); // Begin recording. If currently executing, discards unexecuted events. void ReplayBeginSave(); // Flush buffered events to memory. Continues recording (next call will receive new events only.) // No header is flushed with this operation - don't mix with ReplayFlushFile(). -void ReplayFlushBlob(std::vector *data); +void ReplayFlushBlob(std::vector *data); // Flush buffered events to file. Continues recording (next call will receive new events only.) // Do not call with a different filename before ReplayAbort(). bool ReplayFlushFile(const Path &filename); diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index bd5f53edeb..f3f026c4d6 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -405,6 +405,7 @@ + @@ -636,6 +637,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 7d70fe9b42..89b790f3c4 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -700,6 +700,9 @@ Debugger\WebSocket + + Debugger\WebSocket + Debugger\WebSocket @@ -1701,6 +1704,9 @@ Debugger\WebSocket + + Debugger\WebSocket + Debugger\WebSocket diff --git a/android/jni/Android.mk b/android/jni/Android.mk index d699a76b80..1f0b6efd83 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -422,6 +422,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/MemorySubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/MemoryInfoSubscriber.cpp \ + $(SRC)/Core/Debugger/WebSocket/ReplaySubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/WebSocketUtils.cpp \ From 85c64d3ec3d9c30bdb4f0d64181c55edb511d5b4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 23 May 2021 21:37:19 -0700 Subject: [PATCH 07/16] Replay: Properly load RTC time on load file. --- Core/Debugger/WebSocket/ReplaySubscriber.cpp | 63 ++++++++++++++++++-- Core/Replay.cpp | 2 + 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/Core/Debugger/WebSocket/ReplaySubscriber.cpp b/Core/Debugger/WebSocket/ReplaySubscriber.cpp index 3a33f90b38..501f1ba256 100644 --- a/Core/Debugger/WebSocket/ReplaySubscriber.cpp +++ b/Core/Debugger/WebSocket/ReplaySubscriber.cpp @@ -15,7 +15,11 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include +#include "Common/Swap.h" +#include "Core/HLE/sceRtc.h" #include "Core/Replay.h" +#include "Core/System.h" #include "Core/Debugger/WebSocket/ReplaySubscriber.h" DebuggerSubscriber *WebSocketReplayInit(DebuggerEventHandlerMap &map) { @@ -31,19 +35,42 @@ DebuggerSubscriber *WebSocketReplayInit(DebuggerEventHandlerMap &map) { return nullptr; } +// Begin or resume recording of replay data (replay.begin) +// +// If a replay was previously being played back, this will keep any executed replay data up to +// this point for the next flush. To discard, break the CPU, abort, and then begin. +// +// No parameters. +// +// Empty response. void WebSocketReplayBegin(DebuggerRequest &req) { - // TODO + ReplayBeginSave(); + req.Respond(); } +// Abort any replay execution or recording (replay.abort) +// +// This stops executing any replay and discards any in progress recording. +// +// No parameters. +// +// Empty response. void WebSocketReplayAbort(DebuggerRequest &req) { - // TODO + ReplayAbort(); + req.Respond(); } void WebSocketReplayFlush(DebuggerRequest &req) { + if (!PSP_IsInited()) + return req.Fail("Game not running"); + // TODO } void WebSocketReplayExecute(DebuggerRequest &req) { + if (!PSP_IsInited()) + return req.Fail("Game not running"); + // TODO } @@ -51,10 +78,38 @@ void WebSocketReplayStatus(DebuggerRequest &req) { // TODO } +// Get the base RTC (real time clock) time for replay data (replay.time.get) +// +// The base time is constant during a game session, and represents the "power on" time of the +// emulated PSP. +// +// No parameters. +// +// Response (same event name): +// - value: unsigned integer, may have more than 32 integer bits. void WebSocketReplayTimeGet(DebuggerRequest &req) { - // TODO + if (!PSP_IsInited()) + return req.Fail("Game not running"); + + JsonWriter &json = req.Respond(); + json.writeUint("value", RtcBaseTime()); } +// Overwrite the base RTC time (replay.time.set) +// +// Parameters: +// - value: unsigned integer. +// +// Empty response. void WebSocketReplayTimeSet(DebuggerRequest &req) { - // TODO + if (!PSP_IsInited()) + return req.Fail("Game not running"); + + uint32_t value; + if (!req.ParamU32("value", &value, false)) { + return; + } + + RtcSetBaseTime((int32_t)value); + req.Respond(); } diff --git a/Core/Replay.cpp b/Core/Replay.cpp index bac97e2835..5fd07d7d62 100644 --- a/Core/Replay.cpp +++ b/Core/Replay.cpp @@ -206,6 +206,8 @@ bool ReplayExecuteFile(const Path &filename) { WARN_LOG(SYSTEM, "Replay version %d scary and futuristic, trying anyway", fh.version); } + RtcSetBaseTime((int32_t)fh.rtcBaseSeconds, 0); + data.resize(sz); if (fread(&data[0], sz, 1, fp) != 1) { From 032881687047251946cd2fd056de54ddd2c5cbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 30 May 2021 00:20:41 +0200 Subject: [PATCH 08/16] Rename g_Config.externalDirectory to defaultCurrentDirectory - since that's what it was. Also make currentDirectory a Path, and add support for Path config parameters. Buildfixes More Qt buildfixes Oops, didn't mean to change zstd ver --- Core/Config.cpp | 108 ++++++++++++++++++++++++++---------------- Core/Config.h | 5 +- Core/System.cpp | 2 +- Qt/QtMain.cpp | 2 +- Qt/mainwindow.cpp | 4 +- UI/DevScreens.cpp | 2 +- UI/MainScreen.cpp | 4 +- UI/NativeApp.cpp | 29 ++++++------ libretro/libretro.cpp | 4 +- 9 files changed, 95 insertions(+), 65 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 27a5b7507d..5b2779e13a 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -71,13 +71,15 @@ struct ConfigSetting { TYPE_FLOAT, TYPE_STRING, TYPE_TOUCH_POS, + TYPE_PATH, }; - union Value { + union DefaultValue { bool b; int i; uint32_t u; float f; const char *s; + const char *p; // not sure how much point.. ConfigTouchPos touchPos; }; union SettingPtr { @@ -86,6 +88,7 @@ struct ConfigSetting { uint32_t *u; float *f; std::string *s; + Path *p; ConfigTouchPos *touchPos; }; @@ -94,7 +97,8 @@ struct ConfigSetting { typedef uint32_t (*Uint32DefaultCallback)(); typedef float (*FloatDefaultCallback)(); typedef const char *(*StringDefaultCallback)(); - typedef ConfigTouchPos (*TouchPosDefaultCallback)(); + typedef ConfigTouchPos(*TouchPosDefaultCallback)(); + typedef const char *(*PathDefaultCallback)(); union Callback { BoolDefaultCallback b; @@ -102,102 +106,110 @@ struct ConfigSetting { Uint32DefaultCallback u; FloatDefaultCallback f; StringDefaultCallback s; + PathDefaultCallback p; TouchPosDefaultCallback touchPos; }; ConfigSetting(bool v) - : ini_(""), type_(TYPE_TERMINATOR), report_(false), save_(false), perGame_(false) { + : iniKey_(""), type_(TYPE_TERMINATOR), report_(false), save_(false), perGame_(false) { ptr_.b = nullptr; cb_.b = nullptr; } ConfigSetting(const char *ini, bool *v, bool def, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_BOOL), report_(false), save_(save), perGame_(perGame) { + : iniKey_(ini), type_(TYPE_BOOL), report_(false), save_(save), perGame_(perGame) { ptr_.b = v; cb_.b = nullptr; default_.b = def; } ConfigSetting(const char *ini, int *v, int def, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_INT), report_(false), save_(save), perGame_(perGame) { + : iniKey_(ini), type_(TYPE_INT), report_(false), save_(save), perGame_(perGame) { ptr_.i = v; cb_.i = nullptr; default_.i = def; } ConfigSetting(const char *ini, int *v, int def, std::function transTo, std::function transFrom, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_INT), report_(false), save_(save), perGame_(perGame), translateTo_(transTo), translateFrom_(transFrom) { + : iniKey_(ini), type_(TYPE_INT), report_(false), save_(save), perGame_(perGame), translateTo_(transTo), translateFrom_(transFrom) { ptr_.i = v; cb_.i = nullptr; default_.i = def; } ConfigSetting(const char *ini, uint32_t *v, uint32_t def, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_UINT32), report_(false), save_(save), perGame_(perGame) { + : iniKey_(ini), type_(TYPE_UINT32), report_(false), save_(save), perGame_(perGame) { ptr_.u = v; cb_.u = nullptr; default_.u = def; } ConfigSetting(const char *ini, float *v, float def, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_FLOAT), report_(false), save_(save), perGame_(perGame) { + : iniKey_(ini), type_(TYPE_FLOAT), report_(false), save_(save), perGame_(perGame) { ptr_.f = v; cb_.f = nullptr; default_.f = def; } ConfigSetting(const char *ini, std::string *v, const char *def, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_STRING), report_(false), save_(save), perGame_(perGame) { + : iniKey_(ini), type_(TYPE_STRING), report_(false), save_(save), perGame_(perGame) { ptr_.s = v; cb_.s = nullptr; default_.s = def; } + ConfigSetting(const char *ini, Path *p, const char *def, bool save = true, bool perGame = false) + : iniKey_(ini), type_(TYPE_PATH), report_(false), save_(save), perGame_(perGame) { + ptr_.p = p; + cb_.p = nullptr; + default_.p = def; + } + ConfigSetting(const char *iniX, const char *iniY, const char *iniScale, const char *iniShow, ConfigTouchPos *v, ConfigTouchPos def, bool save = true, bool perGame = false) - : ini_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(TYPE_TOUCH_POS), report_(false), save_(save), perGame_(perGame) { + : iniKey_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(TYPE_TOUCH_POS), report_(false), save_(save), perGame_(perGame) { ptr_.touchPos = v; cb_.touchPos = nullptr; default_.touchPos = def; } ConfigSetting(const char *ini, bool *v, BoolDefaultCallback def, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_BOOL), report_(false), save_(save), perGame_(perGame) { + : iniKey_(ini), type_(TYPE_BOOL), report_(false), save_(save), perGame_(perGame) { ptr_.b = v; cb_.b = def; } ConfigSetting(const char *ini, int *v, IntDefaultCallback def, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_INT), report_(false), save_(save), perGame_(perGame) { + : iniKey_(ini), type_(TYPE_INT), report_(false), save_(save), perGame_(perGame) { ptr_ .i = v; cb_.i = def; } ConfigSetting(const char *ini, int *v, IntDefaultCallback def, std::function transTo, std::function transFrom, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_INT), report_(false), save_(save), perGame_(perGame), translateTo_(transTo), translateFrom_(transFrom) { + : iniKey_(ini), type_(TYPE_INT), report_(false), save_(save), perGame_(perGame), translateTo_(transTo), translateFrom_(transFrom) { ptr_.i = v; cb_.i = def; } ConfigSetting(const char *ini, uint32_t *v, Uint32DefaultCallback def, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_UINT32), report_(false), save_(save), perGame_(perGame) { + : iniKey_(ini), type_(TYPE_UINT32), report_(false), save_(save), perGame_(perGame) { ptr_ .u = v; cb_.u = def; } ConfigSetting(const char *ini, float *v, FloatDefaultCallback def, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_FLOAT), report_(false), save_(save), perGame_(perGame) { + : iniKey_(ini), type_(TYPE_FLOAT), report_(false), save_(save), perGame_(perGame) { ptr_.f = v; cb_.f = def; } ConfigSetting(const char *ini, std::string *v, StringDefaultCallback def, bool save = true, bool perGame = false) - : ini_(ini), type_(TYPE_STRING), report_(false), save_(save), perGame_(perGame) { + : iniKey_(ini), type_(TYPE_STRING), report_(false), save_(save), perGame_(perGame) { ptr_.s = v; cb_.s = def; } ConfigSetting(const char *iniX, const char *iniY, const char *iniScale, const char *iniShow, ConfigTouchPos *v, TouchPosDefaultCallback def, bool save = true, bool perGame = false) - : ini_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(TYPE_TOUCH_POS), report_(false), save_(save), perGame_(perGame) { + : iniKey_(iniX), ini2_(iniY), ini3_(iniScale), ini4_(iniShow), type_(TYPE_TOUCH_POS), report_(false), save_(save), perGame_(perGame) { ptr_.touchPos = v; cb_.touchPos = def; } @@ -212,39 +224,39 @@ struct ConfigSetting { if (cb_.b) { default_.b = cb_.b(); } - return section->Get(ini_, ptr_.b, default_.b); + return section->Get(iniKey_, ptr_.b, default_.b); case TYPE_INT: if (cb_.i) { default_.i = cb_.i(); } if (translateFrom_) { std::string value; - if (section->Get(ini_, &value, nullptr)) { + if (section->Get(iniKey_, &value, nullptr)) { *ptr_.i = translateFrom_(value); return true; } } - return section->Get(ini_, ptr_.i, default_.i); + return section->Get(iniKey_, ptr_.i, default_.i); case TYPE_UINT32: if (cb_.u) { default_.u = cb_.u(); } - return section->Get(ini_, ptr_.u, default_.u); + return section->Get(iniKey_, ptr_.u, default_.u); case TYPE_FLOAT: if (cb_.f) { default_.f = cb_.f(); } - return section->Get(ini_, ptr_.f, default_.f); + return section->Get(iniKey_, ptr_.f, default_.f); case TYPE_STRING: if (cb_.s) { default_.s = cb_.s(); } - return section->Get(ini_, ptr_.s, default_.s); + return section->Get(iniKey_, ptr_.s, default_.s); case TYPE_TOUCH_POS: if (cb_.touchPos) { default_.touchPos = cb_.touchPos(); } - section->Get(ini_, &ptr_.touchPos->x, default_.touchPos.x); + section->Get(iniKey_, &ptr_.touchPos->x, default_.touchPos.x); section->Get(ini2_, &ptr_.touchPos->y, default_.touchPos.y); section->Get(ini3_, &ptr_.touchPos->scale, default_.touchPos.scale); if (ini4_) { @@ -253,6 +265,18 @@ struct ConfigSetting { ptr_.touchPos->show = default_.touchPos.show; } return true; + case TYPE_PATH: + { + std::string tmp; + if (cb_.p) { + default_.p = cb_.p(); + } + bool result = section->Get(iniKey_, &tmp, default_.p); + if (result) { + *ptr_.p = Path(tmp); + } + return result; + } default: _dbg_assert_msg_(false, "Unexpected ini setting type"); return false; @@ -265,21 +289,23 @@ struct ConfigSetting { switch (type_) { case TYPE_BOOL: - return section->Set(ini_, *ptr_.b); + return section->Set(iniKey_, *ptr_.b); case TYPE_INT: if (translateTo_) { std::string value = translateTo_(*ptr_.i); - return section->Set(ini_, value); + return section->Set(iniKey_, value); } - return section->Set(ini_, *ptr_.i); + return section->Set(iniKey_, *ptr_.i); case TYPE_UINT32: - return section->Set(ini_, *ptr_.u); + return section->Set(iniKey_, *ptr_.u); case TYPE_FLOAT: - return section->Set(ini_, *ptr_.f); + return section->Set(iniKey_, *ptr_.f); case TYPE_STRING: - return section->Set(ini_, *ptr_.s); + return section->Set(iniKey_, *ptr_.s); + case TYPE_PATH: + return section->Set(iniKey_, ptr_.p->ToString()); case TYPE_TOUCH_POS: - section->Set(ini_, ptr_.touchPos->x); + section->Set(iniKey_, ptr_.touchPos->x); section->Set(ini2_, ptr_.touchPos->y); section->Set(ini3_, ptr_.touchPos->scale); if (ini4_) { @@ -298,15 +324,17 @@ struct ConfigSetting { switch (type_) { case TYPE_BOOL: - return data.Add(prefix + ini_, *ptr_.b); + return data.Add(prefix + iniKey_, *ptr_.b); case TYPE_INT: - return data.Add(prefix + ini_, *ptr_.i); + return data.Add(prefix + iniKey_, *ptr_.i); case TYPE_UINT32: - return data.Add(prefix + ini_, *ptr_.u); + return data.Add(prefix + iniKey_, *ptr_.u); case TYPE_FLOAT: - return data.Add(prefix + ini_, *ptr_.f); + return data.Add(prefix + iniKey_, *ptr_.f); case TYPE_STRING: - return data.Add(prefix + ini_, *ptr_.s); + return data.Add(prefix + iniKey_, *ptr_.s); + case TYPE_PATH: + return data.Add(prefix + iniKey_, ptr_.p->ToString()); case TYPE_TOUCH_POS: // Doesn't report. return; @@ -316,7 +344,7 @@ struct ConfigSetting { } } - const char *ini_; + const char *iniKey_; const char *ini2_; const char *ini3_; const char *ini4_; @@ -325,7 +353,7 @@ struct ConfigSetting { bool save_; bool perGame_; SettingPtr ptr_; - Value default_; + DefaultValue default_; Callback cb_; // We only support transform for ints. @@ -1213,7 +1241,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { iRunCount++; if (!File::Exists(Path(currentDirectory))) - currentDirectory = ""; + currentDirectory = defaultCurrentDirectory; Section *log = iniFile.GetOrCreateSection(logSectionName); @@ -1595,7 +1623,7 @@ void Config::RestoreDefaults() { if (File::Exists(iniFilename_)) File::Delete(iniFilename_); recentIsos.clear(); - currentDirectory = ""; + currentDirectory = defaultCurrentDirectory; } Load(); } diff --git a/Core/Config.h b/Core/Config.h index be0fe9589c..d7500b7d6c 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -477,8 +477,9 @@ public: bool bShowFrameProfiler; // Various directories. Autoconfigured, not read from ini. - std::string currentDirectory; - Path externalDirectory; + Path currentDirectory; // The directory selected in the game browsing window. + Path defaultCurrentDirectory; // Platform dependent, initialized at startup. + Path memStickDirectory; Path flash0Directory; Path internalDataDirectory; diff --git a/Core/System.cpp b/Core/System.cpp index c6e510ed88..12473807b4 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -697,7 +697,7 @@ void InitSysDirectories() { File::CreateDir(GetSysDirectory(DIRECTORY_SAVESTATE)); if (g_Config.currentDirectory.empty()) { - g_Config.currentDirectory = GetSysDirectory(DIRECTORY_GAME).ToString(); + g_Config.currentDirectory = GetSysDirectory(DIRECTORY_GAME); } } #endif diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 0e7916b4bc..b642f74421 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -528,7 +528,7 @@ bool MainUI::event(QEvent *e) { QString fileName = QFileDialog::getOpenFileName(nullptr, "Load ROM", g_Config.currentDirectory.c_str(), "PSP ROMs (*.iso *.cso *.pbp *.elf *.zip *.ppdmp)"); if (QFile::exists(fileName)) { QDir newPath; - g_Config.currentDirectory = newPath.filePath(fileName).toStdString(); + g_Config.currentDirectory = Path(newPath.filePath(fileName).toStdString()); g_Config.Save("browseFileEvent"); NativeMessageReceived("boot", fileName.toStdString().c_str()); diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 53e18c23d4..8b766110db 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -122,7 +122,7 @@ void MainWindow::loadAct() if (QFile::exists(filename)) { QFileInfo info(filename); - g_Config.currentDirectory = info.absolutePath().toStdString(); + g_Config.currentDirectory = Path(info.absolutePath().toStdString()); NativeMessageReceived("boot", filename.toStdString().c_str()); } } @@ -252,7 +252,7 @@ void MainWindow::switchUMDAct() if (QFile::exists(filename)) { QFileInfo info(filename); - g_Config.currentDirectory = info.absolutePath().toStdString(); + g_Config.currentDirectory = Path(info.absolutePath().toStdString()); __UmdReplace(Path(filename.toStdString())); } } diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 337a356008..29baf2f2e9 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -592,7 +592,7 @@ void SystemInfoScreen::CreateViews() { storage->Add(new InfoItem("MemStickDirectory", g_Config.memStickDirectory.ToVisualString())); storage->Add(new InfoItem("InternalDataDirectory", g_Config.internalDataDirectory.ToVisualString())); storage->Add(new InfoItem("AppCacheDir", g_Config.appCacheDirectory.ToVisualString())); - storage->Add(new InfoItem("ExtStorageDir", g_Config.externalDirectory.ToVisualString())); + storage->Add(new InfoItem("DefaultCurrentDir", g_Config.defaultCurrentDirectory.ToVisualString())); #if PPSSPP_PLATFORM(ANDROID) storage->Add(new InfoItem("ExtFilesDir", g_extFilesDir)); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 5640fd622b..f88509276a 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -505,7 +505,7 @@ void GameBrowser::FocusGame(const Path &gamePath) { void GameBrowser::SetPath(const Path &path) { path_.SetPath(path); - g_Config.currentDirectory = path_.GetPath().ToString(); + g_Config.currentDirectory = path_.GetPath(); Refresh(); } @@ -905,7 +905,7 @@ UI::EventReturn GameBrowser::NavigateClick(UI::EventParams &e) { } else { path_.Navigate(text.ToString()); } - g_Config.currentDirectory = path_.GetPath().ToString(); + g_Config.currentDirectory = path_.GetPath(); Refresh(); return UI::EVENT_DONE; } diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 608b803e49..31dd2b3bfb 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -318,20 +318,9 @@ static bool CheckFontIsUsable(const wchar_t *fontFace) { static void PostLoadConfig() { // On Windows, we deal with currentDirectory in InitSysDirectories(). -#ifndef _WIN32 +#if !PPSSPP_PLATFORM(WINDOWS) if (g_Config.currentDirectory.empty()) { -#if defined(__ANDROID__) - g_Config.currentDirectory = g_Config.externalDirectory.ToString(); -#elif PPSSPP_PLATFORM(IOS) - g_Config.currentDirectory = g_Config.internalDataDirectory.ToString(); -#elif PPSSPP_PLATFORM(SWITCH) - g_Config.currentDirectory = "/"; -#else - if (getenv("HOME") != nullptr) - g_Config.currentDirectory = getenv("HOME"); - else - g_Config.currentDirectory = "./"; -#endif + g_Config.currentDirectory = g_Config.defaultCurrentDirectory; } #endif @@ -457,6 +446,9 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch user_data_path += "/"; #endif + // external_dir has all kinds of meanings depending on platform. + // on iOS it's even the path to bundled app assets. It's a mess. + // We want this to be FIRST. #if PPSSPP_PLATFORM(IOS) // Packed assets are included in app @@ -487,8 +479,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } #endif + g_Config.defaultCurrentDirectory = Path("/"); g_Config.internalDataDirectory = Path(savegame_dir); - g_Config.externalDirectory = Path(external_dir); #if defined(__ANDROID__) // TODO: This needs to change in Android 12. @@ -496,6 +488,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch // Maybe there should be an option to use internal memory instead, but I think // that for most people, using external memory (SDCard/USB Storage) makes the // most sense. + g_Config.defaultCurrentDirectory = Path(external_dir); g_Config.memStickDirectory = Path(external_dir); g_Config.flash0Directory = Path(external_dir) / "flash0"; @@ -511,6 +504,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } } #elif PPSSPP_PLATFORM(IOS) + g_Config.defaultCurrentDirectory = g_Config.internalDataDirectory.ToString(); g_Config.memStickDirectory = Path(user_data_path); g_Config.flash0Directory = Path(std::string(external_dir)) / "flash0"; #elif PPSSPP_PLATFORM(SWITCH) @@ -527,6 +521,13 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch g_Config.memStickDirectory = Path(config) / "ppsspp"; g_Config.flash0Directory = File::GetExeDirectory() / "assets/flash0"; + if (getenv("HOME") != nullptr) { + g_Config.defaultCurrentDirectory = Path(getenv("HOME")); + } else { + // Hm, should probably actually explicitly set the current directory.. + // Though it's not many platforms that'll land us here. + g_Config.currentDirectory = Path("."); + } #endif if (cache_dir && strlen(cache_dir)) { diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 8eb098fea0..aa2caf6633 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -617,8 +617,8 @@ bool retro_load_game(const struct retro_game_info *game) if (retro_save_dir.empty()) retro_save_dir = Path(game->path).NavigateUp(); - g_Config.currentDirectory = retro_base_dir.ToString(); - g_Config.externalDirectory = retro_base_dir; + g_Config.currentDirectory = retro_base_dir; + g_Config.defaultCurrentDirectory = retro_base_dir; g_Config.memStickDirectory = retro_save_dir; g_Config.flash0Directory = retro_base_dir / "flash0"; g_Config.internalDataDirectory = retro_base_dir; From ec35e3c11634533d432822b677ed2d373bfe6dd9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 29 May 2021 17:32:22 -0700 Subject: [PATCH 09/16] iOS: Correct Path assignment on init. --- UI/NativeApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 31dd2b3bfb..065482d4a2 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -504,7 +504,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } } #elif PPSSPP_PLATFORM(IOS) - g_Config.defaultCurrentDirectory = g_Config.internalDataDirectory.ToString(); + g_Config.defaultCurrentDirectory = g_Config.internalDataDirectory; g_Config.memStickDirectory = Path(user_data_path); g_Config.flash0Directory = Path(std::string(external_dir)) / "flash0"; #elif PPSSPP_PLATFORM(SWITCH) From c803e9fc58c2926fd8ca37f0efe526e791d61706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 30 May 2021 11:10:20 +0200 Subject: [PATCH 10/16] Remove createdPath_ from Config.cpp/h, seems pretty useless. --- Core/Config.cpp | 5 +---- Core/Config.h | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 5b2779e13a..3ac1112b20 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1607,10 +1607,7 @@ const Path Config::FindConfigFile(const std::string &baseFilename) { if (!File::Exists(filename)) { // Make sure at least the directory it's supposed to be in exists. Path path = filename.NavigateUp(); - if (createdPath_ != path) { - File::CreateFullPath(path); - createdPath_ = path; - } + File::CreateFullPath(path); } return filename; } diff --git a/Core/Config.h b/Core/Config.h index d7500b7d6c..49211f29db 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -538,7 +538,6 @@ private: Path controllerIniFilename_; std::vector searchPath_; Path defaultPath_; - Path createdPath_; }; std::map> GetLangValuesMapping(); From 72915f88c866c106be3d3661f734a21d3fa6bfb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 30 May 2021 12:39:16 +0200 Subject: [PATCH 11/16] Config: Remove the use of multiple search paths. --- Common/File/FileUtil.cpp | 4 ++-- Core/Config.cpp | 5 ++++- Core/System.h | 3 +++ UWP/PPSSPP_UWPMain.cpp | 5 +---- Windows/main.cpp | 1 - 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index f1c12ede97..2536d4bdf7 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -364,10 +364,10 @@ bool CreateFullPath(const Path &path) { std::string fullPath = path.ToString(); int panicCounter = 100; - VERBOSE_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str()); + VERBOSE_LOG(COMMON, "CreateFullPath: '%s'", fullPath.c_str()); if (File::Exists(fullPath)) { - DEBUG_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str()); + DEBUG_LOG(COMMON, "CreateFullPath: path already exists: '%s'", fullPath.c_str()); return true; } diff --git a/Core/Config.cpp b/Core/Config.cpp index 3ac1112b20..fbb22d8228 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1607,7 +1607,10 @@ const Path Config::FindConfigFile(const std::string &baseFilename) { if (!File::Exists(filename)) { // Make sure at least the directory it's supposed to be in exists. Path path = filename.NavigateUp(); - File::CreateFullPath(path); + // This check is just to avoid logging. + if (!File::Exists(path)) { + File::CreateFullPath(path); + } } return filename; } diff --git a/Core/System.h b/Core/System.h index c66e45ab67..d9e3cdb3a1 100644 --- a/Core/System.h +++ b/Core/System.h @@ -97,7 +97,10 @@ bool IsAudioInitialised(); void UpdateLoadedFile(FileLoader *fileLoader); +// NOTE: These are almost all derived from g_Config.memStickDirectory directly - +// they are not stored anywhere. Path GetSysDirectory(PSPDirectories directoryType); + #ifdef _WIN32 void InitSysDirectories(); #endif diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 949eaf0df1..a4a8852192 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -92,9 +92,6 @@ PPSSPP_UWPMain::PPSSPP_UWPMain(App ^app, const std::shared_ptrLocalFolder->Path->Data(); g_Config.memStickDirectory = Path(memstickFolderW); @@ -108,7 +105,7 @@ PPSSPP_UWPMain::PPSSPP_UWPMain(App ^app, const std::shared_ptr Date: Sun, 30 May 2021 12:45:12 +0200 Subject: [PATCH 12/16] Remove the ability to have multiple search paths for config. --- Core/Config.cpp | 29 ++++++++++------------------- Core/Config.h | 8 ++------ UI/NativeApp.cpp | 5 ++--- UWP/PPSSPP_UWPMain.cpp | 3 +-- Windows/main.cpp | 3 +-- 5 files changed, 16 insertions(+), 32 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index fbb22d8228..74be99d75e 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1577,12 +1577,8 @@ void Config::CleanRecent() { recentIsos = cleanedRecent; } -void Config::SetDefaultPath(const Path &defaultPath) { - defaultPath_ = defaultPath; -} - -void Config::AddSearchPath(const Path &path) { - searchPath_.push_back(path); +void Config::SetSearchPath(const Path &searchPath) { + searchPath_ = searchPath; } const Path Config::FindConfigFile(const std::string &baseFilename) { @@ -1596,21 +1592,16 @@ const Path Config::FindConfigFile(const std::string &baseFilename) { } #endif - for (size_t i = 0; i < searchPath_.size(); ++i) { - Path filename = searchPath_[i] / baseFilename; - if (File::Exists(filename)) { - return filename; - } + Path filename = searchPath_ / baseFilename; + if (File::Exists(filename)) { + return filename; } - const Path filename = defaultPath_ / baseFilename; - if (!File::Exists(filename)) { - // Make sure at least the directory it's supposed to be in exists. - Path path = filename.NavigateUp(); - // This check is just to avoid logging. - if (!File::Exists(path)) { - File::CreateFullPath(path); - } + // Make sure at least the directory it's supposed to be in exists. + Path path = filename.NavigateUp(); + // This check is just to avoid logging. + if (!File::Exists(path)) { + File::CreateFullPath(path); } return filename; } diff --git a/Core/Config.h b/Core/Config.h index 49211f29db..79323eefca 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -505,10 +505,7 @@ public: Path getGameConfigFile(const std::string &gameId); bool hasGameConfig(const std::string &game_id); - // Used when the file is not found in the search path. Trailing slash. - void SetDefaultPath(const Path &defaultPath); - // Use a trailing slash. - void AddSearchPath(const Path &path); + void SetSearchPath(const Path &path); const Path FindConfigFile(const std::string &baseFilename); // Utility functions for "recent" management @@ -536,8 +533,7 @@ private: std::string gameIdTitle_; Path iniFilename_; Path controllerIniFilename_; - std::vector searchPath_; - Path defaultPath_; + Path searchPath_; }; std::map> GetLangValuesMapping(); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 065482d4a2..e1edded499 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -539,14 +539,13 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch LogManager::Init(&g_Config.bEnableLogging); #if !PPSSPP_PLATFORM(WINDOWS) - g_Config.AddSearchPath(Path(user_data_path)); - g_Config.AddSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); - g_Config.SetDefaultPath(GetSysDirectory(DIRECTORY_SYSTEM)); + g_Config.SetSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); // Note that if we don't have storage permission here, loading the config will // fail and it will be set to the default. Later, we load again when we get permission. g_Config.Load(); #endif + LogManager *logman = LogManager::GetInstance(); #if PPSSPP_PLATFORM(ANDROID) diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index a4a8852192..29afda806e 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -103,8 +103,7 @@ PPSSPP_UWPMain::PPSSPP_UWPMain(App ^app, const std::shared_ptr Date: Sun, 30 May 2021 10:29:16 -0700 Subject: [PATCH 13/16] UI: Correct screenshot paths. This was preventing screenshots from saving properly. --- UI/NativeApp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index e1edded499..517321718f 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -1017,9 +1017,9 @@ void TakeScreenshot() { Path filename; while (i < 10000){ if (g_Config.bScreenshotsAsPNG) - filename = path / StringFromFormat("%s_%05d.png", path.c_str(), gameId.c_str(), i); + filename = path / StringFromFormat("%s_%05d.png", gameId.c_str(), i); else - filename = path / StringFromFormat("%s_%05d.jpg", path.c_str(), gameId.c_str(), i); + filename = path / StringFromFormat("%s_%05d.jpg", gameId.c_str(), i); File::FileInfo info; if (!File::Exists(filename)) break; From ea59fa3562ed51ac223537aa2781bd6b7c3e1ad5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 May 2021 15:25:07 -0700 Subject: [PATCH 14/16] Debugger: Add replay execute/flush APIs. --- Core/Debugger/WebSocket/ReplaySubscriber.cpp | 50 ++++++++++++++++++-- Core/Replay.cpp | 28 ++++++++++- Core/Replay.h | 8 +++- 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/Core/Debugger/WebSocket/ReplaySubscriber.cpp b/Core/Debugger/WebSocket/ReplaySubscriber.cpp index 501f1ba256..3b548a25e5 100644 --- a/Core/Debugger/WebSocket/ReplaySubscriber.cpp +++ b/Core/Debugger/WebSocket/ReplaySubscriber.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include "Common/Data/Encoding/Base64.h" #include "Common/Swap.h" #include "Core/HLE/sceRtc.h" #include "Core/Replay.h" @@ -54,28 +55,69 @@ void WebSocketReplayBegin(DebuggerRequest &req) { // // No parameters. // -// Empty response. +// Response (same event name) with no extra data. void WebSocketReplayAbort(DebuggerRequest &req) { ReplayAbort(); req.Respond(); } +// Flush current recording data (replay.flush) +// +// Flushes event data and returns it. Note when combining, you must decode first. +// +// No parameters. +// +// Response (same event name): +// - version: unsigned integer, version number of data. +// - base64: base64 encode of binary data. void WebSocketReplayFlush(DebuggerRequest &req) { if (!PSP_IsInited()) return req.Fail("Game not running"); - // TODO + std::vector data; + ReplayFlushBlob(&data); + + JsonWriter &json = req.Respond(); + json.writeInt("version", ReplayVersion()); + json.writeString("base64", Base64Encode(data.data(), data.size())); } +// Begin executing a replay (replay.execute) +// +// Parameters: +// - version: unsigned integer, same version from replay.flush. +// - base64: base64 encoded replay data. +// +// Response (same event name) with no extra data. void WebSocketReplayExecute(DebuggerRequest &req) { if (!PSP_IsInited()) return req.Fail("Game not running"); - // TODO + uint32_t version = -1; + if (!req.ParamU32("version", &version)) + return; + std::string encoded; + if (!req.ParamString("base64", &encoded)) + return; + + std::vector data = Base64Decode(encoded.data(), encoded.size()); + if (!ReplayExecuteBlob(version, data)) + return req.Fail("Invalid replay data or version"); + + req.Respond(); } +// Get replay status (replay.status) +// +// No parameters. +// +// Response (same event name): +// - executing: boolean if a replay is being executed. +// - saving: boolean if a replay is being recorded. void WebSocketReplayStatus(DebuggerRequest &req) { - // TODO + JsonWriter &json = req.Respond(); + json.writeBool("executing", ReplayIsExecuting()); + json.writeBool("saving", ReplayIsSaving()); } // Get the base RTC (real time clock) time for replay data (replay.time.get) diff --git a/Core/Replay.cpp b/Core/Replay.cpp index 5fd07d7d62..e94eebe04b 100644 --- a/Core/Replay.cpp +++ b/Core/Replay.cpp @@ -136,7 +136,16 @@ static uint8_t lastAnalog[2][2]{}; static size_t replayDiskPos = 0; static bool diskFailed = false; -void ReplayExecuteBlob(const std::vector &data) { +bool ReplayExecuteBlob(int version, const std::vector &data) { + if (version < REPLAY_VERSION_MIN || version > REPLAY_VERSION_CURRENT) { + ERROR_LOG(SYSTEM, "Bad replay data version: %d", version); + return false; + } + if (data.size() == 0) { + ERROR_LOG(SYSTEM, "Empty replay data"); + return false; + } + ReplayAbort(); // Rough estimate. @@ -167,6 +176,7 @@ void ReplayExecuteBlob(const std::vector &data) { replayState = ReplayState::EXECUTE; INFO_LOG(SYSTEM, "Executing replay with %lld items", (long long)replayItems.size()); + return true; } bool ReplayExecuteFile(const Path &filename) { @@ -178,6 +188,7 @@ bool ReplayExecuteFile(const Path &filename) { return false; } + int version = -1; std::vector data; auto loadData = [&]() { // TODO: Maybe stream instead. @@ -207,6 +218,7 @@ bool ReplayExecuteFile(const Path &filename) { } RtcSetBaseTime((int32_t)fh.rtcBaseSeconds, 0); + version = fh.version; data.resize(sz); @@ -220,7 +232,7 @@ bool ReplayExecuteFile(const Path &filename) { if (loadData()) { fclose(fp); - ReplayExecuteBlob(data); + ReplayExecuteBlob(version, data); return true; } @@ -306,6 +318,10 @@ bool ReplayFlushFile(const Path &filename) { return success; } +int ReplayVersion() { + return REPLAY_VERSION_CURRENT; +} + void ReplayAbort() { replayItems.clear(); replayExecPos = 0; @@ -321,6 +337,14 @@ void ReplayAbort() { diskFailed = false; } +bool ReplayIsExecuting() { + return replayState == ReplayState::EXECUTE; +} + +bool ReplayIsSaving() { + return replayState == ReplayState::SAVE; +} + static void ReplaySaveCtrl(uint32_t &buttons, uint8_t analog[2][2], uint64_t t) { if (lastButtons != buttons) { replayItems.push_back(ReplayItemHeader(ReplayAction::BUTTONS, t, buttons)); diff --git a/Core/Replay.h b/Core/Replay.h index 3035330812..8ed514108c 100644 --- a/Core/Replay.h +++ b/Core/Replay.h @@ -49,7 +49,7 @@ enum class ReplayAction : uint8_t { struct PSPFileInfo; // Replay from data in memory. Does not manipulate base time / RNG state. -void ReplayExecuteBlob(const std::vector &data); +bool ReplayExecuteBlob(int version, const std::vector &data); // Replay from data in a file. Returns false if invalid. bool ReplayExecuteFile(const Path &filename); // Returns whether there are unexecuted events to replay. @@ -63,10 +63,16 @@ void ReplayFlushBlob(std::vector *data); // Flush buffered events to file. Continues recording (next call will receive new events only.) // Do not call with a different filename before ReplayAbort(). bool ReplayFlushFile(const Path &filename); +// Get current replay data version. +int ReplayVersion(); // Abort any execute or record operation in progress. void ReplayAbort(); +// Check if replay data is being executed or saved. +bool ReplayIsExecuting(); +bool ReplayIsSaving(); + void ReplayApplyCtrl(uint32_t &buttons, uint8_t analog[2][2], uint64_t t); uint32_t ReplayApplyDisk(ReplayAction action, uint32_t result, uint64_t t); uint64_t ReplayApplyDisk64(ReplayAction action, uint64_t result, uint64_t t); From 2af40d07bbb062d462bbcd6458a5f8c39ba8de34 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 May 2021 15:25:41 -0700 Subject: [PATCH 15/16] Debugger: Fix websocket read silly mistake. Surprised I didn't notice this before. --- Common/Net/WebsocketServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Common/Net/WebsocketServer.cpp b/Common/Net/WebsocketServer.cpp index 3b5449b489..3b6f98ed27 100644 --- a/Common/Net/WebsocketServer.cpp +++ b/Common/Net/WebsocketServer.cpp @@ -341,8 +341,8 @@ bool WebSocketServer::ReadFrame() { mask = &header[10]; // Read from big endian. - uint64_t high = (header[2] << 24) | (header[3] << 16) || (header[4] << 8) | (header[5] << 0); - uint64_t low = (header[6] << 24) | (header[7] << 16) || (header[8] << 8) | (header[9] << 0); + uint64_t high = (header[2] << 24) | (header[3] << 16) | (header[4] << 8) | (header[5] << 0); + uint64_t low = (header[6] << 24) | (header[7] << 16) | (header[8] << 8) | (header[9] << 0); sz = (high << 32) | low; if ((sz & 0x8000000000000000ULL) != 0) { From 6e128eb5103ea122800066b5d9279e0fb5897b64 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 May 2021 21:12:30 -0700 Subject: [PATCH 16/16] Debugger: Expose API to reset game. This can be useful to put the game back to a known-good state before setting up breakpoints, overwriting memory, and processing replay data. --- Core/Debugger/WebSocket/GameSubscriber.cpp | 33 ++++++++++++++++++++++ Core/Debugger/WebSocket/GameSubscriber.h | 1 + Core/System.cpp | 3 +- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Core/Debugger/WebSocket/GameSubscriber.cpp b/Core/Debugger/WebSocket/GameSubscriber.cpp index 0acd4a9b3e..2d8bb1a116 100644 --- a/Core/Debugger/WebSocket/GameSubscriber.cpp +++ b/Core/Debugger/WebSocket/GameSubscriber.cpp @@ -18,15 +18,48 @@ #include "Core/Debugger/WebSocket/GameSubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/ELF/ParamSFO.h" +#include "Core/Host.h" #include "Core/System.h" DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map) { + map["game.reset"] = &WebSocketGameReset; map["game.status"] = &WebSocketGameStatus; map["version"] = &WebSocketVersion; return nullptr; } +// Reset emulation (game.reset) +// +// Use this if you need to break on start and do something before the game starts. +// +// Parameters: +// - break: optional boolean, true to break CPU on start. Use cpu.resume afterward. +// +// Response (same event name) with no extra data or error. +void WebSocketGameReset(DebuggerRequest &req) { + if (!PSP_IsInited()) + return req.Fail("Game not running"); + + bool needBreak = false; + if (!req.ParamBool("break", &needBreak, DebuggerParamType::OPTIONAL)) + return; + + if (needBreak) + PSP_CoreParameter().startBreak = true; + + PSP_Shutdown(); + std::string resetError; + if (!PSP_Init(PSP_CoreParameter(), &resetError)) { + ERROR_LOG(BOOT, "Error resetting: %s", resetError.c_str()); + return req.Fail("Could not reset"); + } + host->BootDone(); + host->UpdateDisassembly(); + + req.Respond(); +} + // Check game status (game.status) // // No parameters. diff --git a/Core/Debugger/WebSocket/GameSubscriber.h b/Core/Debugger/WebSocket/GameSubscriber.h index e3d08f4522..af43c692d0 100644 --- a/Core/Debugger/WebSocket/GameSubscriber.h +++ b/Core/Debugger/WebSocket/GameSubscriber.h @@ -21,5 +21,6 @@ DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map); +void WebSocketGameReset(DebuggerRequest &req); void WebSocketGameStatus(DebuggerRequest &req); void WebSocketVersion(DebuggerRequest &req); diff --git a/Core/System.cpp b/Core/System.cpp index c6e510ed88..d49eb039d7 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -468,7 +468,8 @@ bool PSP_InitUpdate(std::string *error_string) { } bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) { - PSP_InitStart(coreParam, error_string); + if (!PSP_InitStart(coreParam, error_string)) + return false; while (!PSP_InitUpdate(error_string)) sleep_ms(10);