Merge pull request #22136 from hrydgard/nand-mounts

Mount flash from PSP/NAND/flash0, flash1 etc, fix host0: to be more useful for homebrew
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-08-24 10:45:50 +02:00
commit 0e2dfad02d
25 files changed
+182 -117

No files matched your search

+1 -1
View File
@@ -1443,7 +1443,7 @@ uint8_t *ReadLocalFile(const Path &filename, size_t *size) {
return nullptr;
}
Fseek(file, 0, SEEK_SET);
// NOTE: If you find ~10 memory leaks from here, with very varying sizes, it might be the VFPU LUTs.
// NOTE: If you find up to ~10-ish memory leaks from here, with very varying sizes, it might be the VFPU LUTs.
uint8_t *contents = new uint8_t[f_size + 1];
if (fread(contents, 1, f_size, file) != f_size) {
delete[] contents;
+17
View File
@@ -2,6 +2,7 @@
#include <vector>
#include <cstdint>
#include <cstring>
#include <string_view>
#include "Common/File/DirListing.h"
@@ -37,7 +38,23 @@ public:
class VFSInterface {
public:
virtual ~VFSInterface() {}
// The return value should be delete[]-ed.
virtual uint8_t *ReadFile(std::string_view path, size_t *size) = 0;
// Slow convenience wrapper (well, we should probably change the interface).
bool ReadFileInto(std::string_view path, std::vector<uint8_t> *buf) {
size_t sz;
uint8_t *data = ReadFile(path, &sz);
if (!data) {
return false;
}
buf->resize(sz);
memcpy(buf->data(), data, sz);
delete[] data;
return true;
}
// If listing already contains files, it'll be cleared.
virtual bool GetFileListing(std::string_view path, std::vector<File::FileInfo> *listing, const char *filter = nullptr) = 0;
};
+5 -2
View File
@@ -59,21 +59,24 @@ void SetupCallbacks(AllowedCallback allowed, MessageCallback message) {
void ReportMessage(const char *message, ...) {
const int MESSAGE_BUFFER_SIZE = 65536;
char *temp = new char [MESSAGE_BUFFER_SIZE];
va_list args;
va_start(args, message);
char *temp = new char[MESSAGE_BUFFER_SIZE];
vsnprintf(temp, MESSAGE_BUFFER_SIZE - 1, message, args);
temp[MESSAGE_BUFFER_SIZE - 1] = '\0';
va_end(args);
if (!allowedCallback || !messageCallback) {
ERROR_LOG(Log::System, "Reporting not initialized, skipping: %s", temp);
delete[] temp;
return;
}
if (!allowedCallback())
if (!allowedCallback()) {
delete[] temp;
return;
}
messageCallback(message, temp);
+9 -3
View File
@@ -177,7 +177,7 @@ static const CommandLineParam g_autoParams[] = {
{POFF(escapeExit), CmdParamType::Bool, "escape-exit", '\0', "Escape key exits the application", CmdLineMode::Application},
{POFF(pauseMenuExit), CmdParamType::Bool, "pause-menu-exit", '\0', "Change \"Exit to menu\" in pause menu to \"Exit\"", CmdLineMode::Application},
{POFF(appendConfig), CmdParamType::String, "appendconfig", '\0', "Merge config FILE into the current configuration"},
{POFF(root), CmdParamType::String, "root", 'r', "Mount root directory"},
{POFF(root), CmdParamType::String, "root", 'r', "Mount directory as the root of host0:/."},
{POFF(memStick), CmdParamType::String, "memstick", '\0', "Memory stick root directory (contains PSP/GAME etc)"},
{POFF(stateToLoad), CmdParamType::String, "state", '\0', "Load state from specified file"},
{POFF(compare), CmdParamType::Bool, "compare", 'c', "Enable comparison mode", CmdLineMode::Headless},
@@ -512,7 +512,6 @@ void CommandLineOptions::ApplyToConfig() const {
if (pauseMenuExit.has_value()) {
g_Config.bPauseMenuExitsEmulator = pauseMenuExit.value();
}
if (debuggerPort.has_value()) {
g_Config.iRemoteISOPort = debuggerPort.value();
g_Config.DoNotSaveSetting(&g_Config.iRemoteISOPort);
@@ -538,7 +537,8 @@ void CommandLineOptions::ApplyToConfig() const {
}
if (root.has_value()) {
g_Config.DoNotSaveSetting(&g_Config.mountRoot);
// No DoNotSaveSetting() here - mountRoot isn't an ordinary setting and never gets
// written to ppsspp.ini, since the ini itself lives inside the memory stick directory.
g_Config.mountRoot = Path(root.value());
}
@@ -550,6 +550,12 @@ void CommandLineOptions::ApplyToConfig() const {
CreateSysDirectories();
}
if (nand.has_value()) {
// No DoNotSaveSetting() here - memStickDirectory isn't an ordinary setting and never gets
// written to ppsspp.ini, since the ini itself lives inside the memory stick directory.
g_Config.nandRootDirectory = Path(nand.value());
}
if (resolutionScale.has_value()) {
g_Config.iInternalResolution = resolutionScale.value();
g_Config.DoNotSaveSetting(&g_Config.iInternalResolution);
+4 -1
View File
@@ -57,7 +57,10 @@ struct CommandLineOptions {
std::optional<bool> bootVSH;
std::optional<std::string> appendConfig;
std::optional<std::string> root; // mount root, needs more explanation
std::optional<std::string> root; // This is supposed to configure host0:.
std::optional<std::string> nand; // Set the root nand directory (one level above flash0, ...)
// Memory stick root (the directory containing PSP/GAME, PSP/SYSTEM, ...). Mainly for headless,
// which otherwise always uses "memstick" next to the executable - so testing a real game there
// meant copying it in. Points at the same layout the app uses, so the two can share one.
+1 -1
View File
@@ -706,7 +706,7 @@ public:
Path defaultCurrentDirectory; // Platform dependent, initialized at startup.
Path memStickDirectory;
Path flash0Directory;
Path nandRootDirectory;
Path internalDataDirectory;
Path appCacheDirectory;
+5 -1
View File
@@ -59,8 +59,12 @@ struct CoreParameter {
bool enableSound = true; // there aren't multiple sound cores.
Path fileToStart;
Path mountIso; // If non-empty, and fileToStart is an ELF or PBP, will mount this ISO in the background to umd1:.
Path mountRoot; // If non-empty, and fileToStart is an ELF or PBP, mount this as host0: / umd0:.
Path mountRoot; // If non-empty, and fileToStart is an ELF or PBP, mount this as host0:.
Path nandRoot; // If non-empty, use this directory as the root above flash0:-flash3: (which are hosted in subdirectories flash0-flash3 of it).
std::string errorString;
bool loadGameConfigs = true;
+4 -2
View File
@@ -466,8 +466,10 @@ size_t DirectoryFileHandle::Seek(s32 position, FileMove type)
LARGE_INTEGER distance;
distance.QuadPart = position;
LARGE_INTEGER cursor;
SetFilePointerEx(hFile, distance, &cursor, moveMethod);
LARGE_INTEGER cursor{};
if (!SetFilePointerEx(hFile, distance, &cursor, moveMethod)) {
ERROR_LOG(Log::IO, "DirectoryFileHandle::Seek(%d, %d) failed", position, (int)type);
}
result = (size_t)cursor.QuadPart;
#else
int moveMethod = 0;
+56 -36
View File
@@ -175,6 +175,14 @@ IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle) const
return nullptr;
}
std::string MetaFileSystem::GetCurrentDirForThread(int threadID) const {
auto iter = currentDir.find(threadID);
if (iter == currentDir.end()) {
return "";
}
return iter->second;
}
int MetaFileSystem::MapFilePath(std::string_view _inpath, std::string *outpath, MountPoint **system) {
int error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;
std::lock_guard<std::recursive_mutex> guard(lock);
@@ -194,49 +202,53 @@ int MetaFileSystem::MapFilePath(std::string_view _inpath, std::string *outpath,
}
// Special handling: host0:command.txt (as seen in Super Monkey Ball Adventures, for example)
// appears to mean the current directory on the UMD. Let's just assume the current directory.
if (strncasecmp(inpath.c_str(), "host0:", strlen("host0:")) == 0) {
INFO_LOG(Log::FileSystem, "Host0 path detected, stripping: %s", inpath.c_str());
// However, this causes trouble when running tests, since our test framework uses host0:.
// Maybe it's really just supposed to map to umd0 or something?
if (PSP_CoreParameter().headLess) {
inpath = "umd0:" + inpath.substr(strlen("host0:"));
} else {
inpath = inpath.substr(strlen("host0:"));
}
// appears to mean the current directory on the UMD.
// Actually, not sure if this is still needed. It doesn't make a whole lot of sense.
if (startsWithNoCase(inpath.c_str(), "host0:") && !host0Mapped_) {
inpath = "umd0:" + inpath.substr(strlen("host0:"));
}
const std::string *currentDirectory = &startingDirectory;
// Hm, does this make sense? Doesn't each drive has its own currentDir per thread, or maybe not?
int currentThread = __KernelGetCurThread();
currentDir_t::iterator it = currentDir.find(currentThread);
if (it == currentDir.end())
{
//Attempt to emulate SCE_KERNEL_ERROR_NOCWD / 8002032C: may break things requiring fixes elsewhere
if (inpath.find(':') == std::string::npos /* means path is relative */)
{
auto it = currentDir.find(currentThread);
if (it == currentDir.end()) {
// Attempt to emulate SCE_KERNEL_ERROR_NOCWD / 8002032C: may break things requiring fixes elsewhere
if (inpath.find(':') == std::string::npos /* means path is relative */) {
error = SCE_KERNEL_ERROR_NOCWD;
WARN_LOG(Log::FileSystem, "Path is relative, but current directory not set for thread %i. returning 8002032C(SCE_KERNEL_ERROR_NOCWD) instead.", currentThread);
}
}
else
{
} else {
currentDirectory = &(it->second);
}
if (RealPath(*currentDirectory, inpath, realpath))
{
if (RealPath(*currentDirectory, inpath, realpath)) {
std::string prefix = realpath;
size_t prefixPos = realpath.find(':');
if (prefixPos != realpath.npos)
prefix = NormalizePrefix(realpath.substr(0, prefixPos + 1));
for (size_t i = 0; i < fileSystems.size(); i++)
{
size_t prefLen = fileSystems[i].prefix.size();
if (strncasecmp(fileSystems[i].prefix.c_str(), prefix.c_str(), prefLen) == 0)
{
*outpath = realpath.substr(prefixPos + 1);
for (size_t i = 0; i < fileSystems.size(); i++) {
if (equalsNoCase(fileSystems[i].prefix, prefix)) {
// Map into the underlying filesystem. If the mount specifies a subDir,
// join that with the path inside the device.
std::string basePath = realpath.substr(prefixPos + 1); // may be empty or start with '/'
const std::string &mountSub = fileSystems[i].subDir;
if (mountSub.empty()) {
*outpath = basePath;
} else {
// Normalize subDir: ensure it starts with '/' and has no trailing slash (unless it's root "/").
std::string s = mountSub;
if (s.empty()) s = "/";
if (s[0] != '/') s.insert(s.begin(), '/');
if (s.size() > 1 && s.back() == '/') s.pop_back();
if (basePath.empty()) {
*outpath = s;
} else {
*outpath = s + basePath; // basePath usually starts with '/'
}
}
*system = &(fileSystems[i]);
VERBOSE_LOG(Log::FileSystem, "MapFilePath: mapped \"%s\" to prefix: \"%s\", path: \"%s\"", inpath.c_str(), fileSystems[i].prefix.c_str(), outpath->c_str());
@@ -252,39 +264,43 @@ int MetaFileSystem::MapFilePath(std::string_view _inpath, std::string *outpath,
return error;
}
std::string MetaFileSystem::NormalizePrefix(std::string_view prefix) const {
std::string_view MetaFileSystem::NormalizePrefix(std::string_view prefix) const {
// Let's apply some mapping here since it won't break savestates.
if (prefix == "memstick:")
prefix = "ms0:";
return "ms0:";
// Seems like umd00: etc. work just fine... avoid umd1/umd for tests.
if (startsWith(prefix, "umd") && prefix != "umd1:" && prefix != "umd:")
prefix = "umd0:";
return "umd0:";
// Seems like umd00: etc. work just fine...
if (startsWith(prefix, "host"))
prefix = "host0:";
return "host0:";
// Should we simply make this case insensitive?
if (prefix == "DISC0:")
prefix = "disc0:";
return std::string(prefix);
return "disc0:";
return prefix;
}
void MetaFileSystem::Mount(std::string_view prefix, std::shared_ptr<IFileSystem> system) {
void MetaFileSystem::Mount(std::string_view prefix, std::shared_ptr<IFileSystem> system, std::string_view subDir) {
std::lock_guard<std::recursive_mutex> guard(lock);
for (auto &it : fileSystems) {
if (it.prefix == prefix) {
// Overwrite the old mount.
// shared_ptr makes sure there's no leak.
it.system = system;
it.subDir = std::string(subDir);
return;
}
}
if (equalsNoCase(prefix, "host0:")) {
host0Mapped_ = true;
}
// Prefix not yet mounted, do so.
MountPoint x;
x.prefix = prefix;
x.system = system;
x.subDir = std::string(subDir);
fileSystems.push_back(x);
}
@@ -292,10 +308,14 @@ void MetaFileSystem::Mount(std::string_view prefix, std::shared_ptr<IFileSystem>
void MetaFileSystem::UnmountAll() {
fileSystems.clear();
currentDir.clear();
host0Mapped_ = false;
}
void MetaFileSystem::Unmount(std::string_view prefix) {
std::lock_guard<std::recursive_mutex> guard(lock);
if (equalsNoCase(prefix, "host0:")) {
host0Mapped_ = false;
}
for (auto iter = fileSystems.begin(); iter != fileSystems.end(); iter++) {
if (iter->prefix == prefix) {
fileSystems.erase(iter);
+14 -7
View File
@@ -31,21 +31,27 @@ private:
struct MountPoint {
std::string prefix;
std::shared_ptr<IFileSystem> system;
// Optional subdirectory inside the filesystem to treat as the root for this mount.
// Example: mounting "ms0:" with subDir "/PSP/GAME" will map "ms0:/file"
// to the underlying filesystem path "/PSP/GAME/file".
std::string subDir;
bool operator == (const MountPoint &other) const {
return prefix == other.prefix && system == other.system;
return prefix == other.prefix && system == other.system && subDir == other.subDir;
}
};
// The order of this vector is meaningful - lookups are always a linear search from the start.
std::vector<MountPoint> fileSystems;
typedef std::map<int, std::string> currentDir_t;
currentDir_t currentDir;
std::map<int, std::string> currentDir;
std::string startingDirectory;
mutable std::recursive_mutex lock; // must be recursive. TODO: fix that
// For the UMD host0 hack.
bool host0Mapped_ = false;
// Assumes the lock is held
void Reset() {
// This used to be 6, probably an attempt to replicate PSP handles.
@@ -59,9 +65,8 @@ public:
Reset();
}
// Will replace the existing mount if already exists.
void Mount(std::string_view prefix, std::shared_ptr<IFileSystem> system);
// Will replace the existing mount if already exists. Mount with an optional sub-directory inside the filesystem to treat as the root.
void Mount(std::string_view prefix, std::shared_ptr<IFileSystem> system, std::string_view subDir = std::string_view());
void UnmountAll();
void Unmount(std::string_view prefix);
@@ -82,6 +87,8 @@ public:
void ThreadEnded(int threadID);
void Shutdown();
std::string GetCurrentDirForThread(int threadID) const;
u32 GetNewHandle() override {
u32 res = current++;
if (current < 0) {
@@ -107,7 +114,7 @@ public:
return error;
}
std::string NormalizePrefix(std::string_view prefix) const;
std::string_view NormalizePrefix(std::string_view prefix) const;
std::vector<PSPFileInfo> GetDirListing(std::string_view path, bool *exists = nullptr) override;
int OpenFile(std::string filename, FileAccess access, const char *devicename = nullptr) override;
+9 -8
View File
@@ -877,13 +877,14 @@ static void __LoadInternalFonts() {
// Fonts already loaded.
return;
}
const std::string fontPath = "flash0:/font/";
const std::string fontOverridePath = "ms0:/PSP/flash0/font/";
const std::string gameFontPath = "disc0:/PSP_GAME/USRDIR/";
if (!pspFileSystem.GetFileInfo(fontPath).exists) {
pspFileSystem.MkDir(fontPath);
const bool checkClassicOverrides = pspFileSystem.GetFileInfo(fontOverridePath).exists;
if (checkClassicOverrides) {
WARN_LOG(Log::sceFont, "Classic font overrides active, ignoring NAND: %s", fontOverridePath.c_str());
}
if ((pspFileSystem.GetFileInfo("disc0:/PSP_GAME/USRDIR/zh_gb.pgf").exists) && (pspFileSystem.GetFileInfo("disc0:/PSP_GAME/USRDIR/oldfont.prx").exists)) {
for (size_t i = 0; i < ARRAY_SIZE(fontRegistry); i++) {
const FontRegistryEntry &entry = fontRegistry[i];
@@ -908,16 +909,16 @@ static void __LoadInternalFonts() {
std::string fontFilename = gameFontPath + entry.fileName;
bufferRead = pspFileSystem.ReadEntireFile(fontFilename, buffer, true) >= 0;
if (!bufferRead) {
// No game font, let's try override path.
if (checkClassicOverrides && !bufferRead) {
// No game font, let's try classic override path. NOTE: This is not recommended - use flash0.
fontFilename = fontOverridePath + entry.fileName;
bufferRead = pspFileSystem.ReadEntireFile(fontFilename, buffer, true) >= 0;
}
if (!bufferRead) {
// No override, let's use the default path.
fontFilename = fontPath + entry.fileName;
bufferRead = pspFileSystem.ReadEntireFile(fontFilename, buffer) >= 0;
// No override, let's read fonts from assets from VFS.
fontFilename = std::string("flash0/font/") + entry.fileName;
bufferRead = g_VFS.ReadFileInto(fontFilename, &buffer);
}
if (bufferRead) {
+1 -3
View File
@@ -712,8 +712,7 @@ void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char
if (shouldHLE && GetHLEFunc(func.moduleName, func.nid)) {
if (reimporting && Memory::Read_Instruction(func.stubAddr + 4) != GetSyscallOp(func.moduleName, func.nid)) {
const char *name = GetHLEFuncName(func.moduleName, func.nid);
char temp[256];
if (temp) {
if (name) {
WARN_LOG(Log::Loader, "Reimporting updated syscall from %s: %s", func.moduleName, name);
} else {
WARN_LOG(Log::Loader, "Reimporting updated syscall from %s: zz_%08x", func.moduleName, func.nid);
@@ -1816,7 +1815,6 @@ static void __KernelStartModule(PSPModule *m, int args, const char *argp, SceKer
__KernelSetThreadRA(threadID, NID_MODULERETURN);
}
u32 __KernelGetModuleGP(SceUID uid) {
u32 error;
PSPModule *module = kernelObjects.Get<PSPModule>(uid, error);
+6 -2
View File
@@ -1243,8 +1243,12 @@ static int _PsmfPlayerSetPsmfOffset(u32 psmfPlayer, const char *filename, int of
return hleDelayResult(hleLogError(Log::ME, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid file data or does not exist"), "psmfplayer set", delayUs);
}
if (offset != 0)
pspFileSystem.SeekFile(psmfplayer->filehandle, offset, FILEMOVE_BEGIN);
if (offset < 0) {
return hleDelayResult(hleLogError(Log::ME, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid file data or does not exist"), "psmfplayer set", delayUs);
}
pspFileSystem.SeekFile(psmfplayer->filehandle, offset, FILEMOVE_BEGIN);
u8 *buf = psmfplayer->tempbuf;
int tempbufSize = (int)sizeof(psmfplayer->tempbuf);
int size = (int)pspFileSystem.ReadFile(psmfplayer->filehandle, buf, 2048);
+13 -10
View File
@@ -374,17 +374,17 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string_view discId, bool load
path = AndroidContentURI(full_path.GetDirectory()).FilePath();
}
// TODO: More robust check.
size_t pos = path.find("PSP/GAME/");
std::string ms_path;
if (pos != std::string::npos) {
ms_path = "ms0:/" + path.substr(pos) + "/";
} else {
// This is wrong, but it's better than not having a working directory at all.
// Note that umd0:/ is actually the writable containing directory, in this case.
ms_path = "umd0:/";
// We map host0: to the containing directory, see below. This will also be set as the current dir
ms_path = "host0:/";
}
Path dir;
Path host0Dir;
if (!PSP_CoreParameter().mountRoot.empty()) {
// We don't want to worry about .. and cwd and such.
const Path rootNorm = NormalizePath(PSP_CoreParameter().mountRoot);
@@ -425,28 +425,31 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string_view discId, bool load
file = filepath + "/" + file;
path = rootNorm.ToString();
pspFileSystem.SetStartingDirectory(filepath);
dir = Path(path);
host0Dir = Path(path);
} else {
pspFileSystem.SetStartingDirectory(ms_path);
dir = full_path.NavigateUp();
host0Dir = full_path.NavigateUp();
}
auto fs = std::make_shared<DirectoryFileSystem>(&pspFileSystem, dir, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD);
pspFileSystem.Mount("umd0:", fs);
auto fs = std::make_shared<DirectoryFileSystem>(&pspFileSystem, host0Dir, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD);
pspFileSystem.Mount("host0:", fs);
std::string finalName = ms_path + file;
std::string homebrewName = PSP_CoreParameter().fileToStart.ToVisualString();
std::size_t lslash = homebrewName.find_last_of('/');
std::size_t rslash = homebrewName.find_last_of('\\');
if (lslash != homebrewName.npos)
if (lslash != homebrewName.npos) {
homebrewName = homebrewName.substr(lslash + 1);
if (rslash != homebrewName.npos)
}
if (rslash != homebrewName.npos) {
homebrewName = homebrewName.substr(rslash + 1);
}
std::string discID = g_paramSFO.GetDiscID();
std::string discVersion = g_paramSFO.GetValueString("DISC_VERSION");
std::string madeUpID = g_paramSFO.GenerateFakeID(Path());
// TODO: This was long enough ago that I think this can be safely removed.
// Migrate old save states from old versions of fake game IDs.
// Ugh, this might actually be slow on Android.
// The strings here are attacker-controlled (from PARAM.SFO / filenames), so
-6
View File
@@ -558,12 +558,6 @@ namespace Reporting
if (PSP_GetBootState() == BootState::Complete && g_paramSFO.GetValueString("DISC_VERSION").empty())
return false;
// Some users run the exe from a zip or something, and don't have fonts.
// This breaks things, but let's not report it since it's confusing.
File::FileInfo fo;
if (!File::Exists(g_Config.flash0Directory / "font/jpn0.pgf") || !g_VFS.GetFileInfo("flash0/font/jpn0.pgf", &fo))
return false;
return !everUnsupported;
}
+6 -6
View File
@@ -300,12 +300,6 @@ static void ShowCompatWarnings(const Compatibility &compat) {
extern const std::string INDEX_FILENAME;
static void MountFileSystems() {
// TODO: Revisit this flash0 configurability. It was added for
#if PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(MACOS)
auto flash0System = std::make_shared<DirectoryFileSystem>(&pspFileSystem, g_Config.flash0Directory, FileSystemFlags::FLASH);
#else
auto flash0System = std::make_shared<VFSFileSystem>(&pspFileSystem, "flash0");
#endif
FileSystemFlags memstickFlags = FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD;
Path pspDir = GetSysDirectory(DIRECTORY_PSP);
@@ -322,7 +316,13 @@ static void MountFileSystems() {
pspFileSystem.Mount("fatms:", memstickSystem);
pspFileSystem.Mount("pfat0:", memstickSystem);
// TODO: These should be made "lazy" mounts.
auto flash0System = std::make_shared<DirectoryFileSystem>(&pspFileSystem, g_Config.nandRootDirectory / "flash0", FileSystemFlags::FLASH);
auto flash1System = std::make_shared<DirectoryFileSystem>(&pspFileSystem, g_Config.nandRootDirectory / "flash1", FileSystemFlags::FLASH);
pspFileSystem.Mount("flash0:", flash0System);
pspFileSystem.Mount("flash1:", flash1System);
// NOTE: We don't handle the host0: mount here, it's in Load_PSP_ELF_PBP.
if (g_RemasterMode) {
const std::string gameId = g_paramSFO.GetDiscID();
+2
View File
@@ -112,6 +112,8 @@ Path GetSysDirectory(PSPDirectories directoryType) {
return pspDirectory / "shaders";
case DIRECTORY_CUSTOM_THEMES:
return pspDirectory / "themes";
case DIRECTORY_NAND:
return pspDirectory / "NAND";
case DIRECTORY_MEMSTICK_ROOT:
return g_Config.memStickDirectory;
+1
View File
@@ -11,6 +11,7 @@ enum PSPDirectories {
DIRECTORY_CHEATS,
DIRECTORY_SCREENSHOT,
DIRECTORY_SYSTEM,
DIRECTORY_NAND,
DIRECTORY_GAME,
DIRECTORY_SAVEDATA,
DIRECTORY_PAUTH,
+7 -3
View File
@@ -419,15 +419,16 @@ void DrawThreadView(ImConfig &cfg, ImControl &control) {
}
std::vector<DebugThreadInfo> info = GetThreadsInfo();
if (ImGui::BeginTable("threads", 8, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
if (ImGui::BeginTable("threads", 9, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) {
ImGui::TableSetupColumn("Id", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("PC", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Entry", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Priority", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("State", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Wait Type", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Wait ID", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Wait Type", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Wait ID", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("Cur Dir", ImGuiTableColumnFlags_WidthStretch);
// .initialStack, .stackSize, etc
ImGui::TableHeadersRow();
@@ -459,6 +460,9 @@ void DrawThreadView(ImConfig &cfg, ImControl &control) {
char temp[64];
WaitIDToString(thread.waitType, thread.waitID, temp, sizeof(temp));
ImGui::TextUnformatted(temp);
std::string curDir = pspFileSystem.GetCurrentDirForThread(thread.id);
ImGui::TableNextColumn();
ImGui::TextUnformatted(curDir.empty() ? "N/A" : curDir.c_str());
if (ImGui::BeginPopup("threadPopup")) {
DebugThreadInfo &thread = info[i];
ImGui::Text("Thread: %s", thread.name);
+3 -4
View File
@@ -1187,15 +1187,14 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUCommon *gpuD
if (ImGui::RepeatButtonShift("Single step", fastRepeatRate)) {
gpuDebug->SetBreakNext(GPUDebug::BreakNext::OP);
}
if (disableStepButtons) {
ImGui::EndDisabled();
}
ImGui::SameLine();
if (ImGui::Button("Cancel step")) {
gpuDebug->ClearBreakNext();
}
if (disableStepButtons) {
ImGui::EndDisabled();
}
ImGui::Text("%d/%d", gpuDebug->PrimsThisFrame(), gpuDebug->PrimsLastFrame());
if (disableStepButtons) {
+4 -10
View File
@@ -595,11 +595,6 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO
g_Config.defaultCurrentDirectory = Path(external_dir);
}
// Might also add an option to move it to internal / non-visible storage, but there's
// little point, really.
g_Config.flash0Directory = Path(external_dir) / "flash0";
Path memstickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt";
if (File::Exists(memstickDirFile)) {
INFO_LOG(Log::System, "Reading '%s' to find memstick dir.", memstickDirFile.c_str());
@@ -650,13 +645,10 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO
#elif PPSSPP_PLATFORM(IOS)
g_Config.defaultCurrentDirectory = g_Config.internalDataDirectory;
g_Config.memStickDirectory = DarwinFileSystemServices::appropriateMemoryStickDirectoryToUse();
g_Config.flash0Directory = Path(external_dir) / "flash0";
#elif PPSSPP_PLATFORM(MAC)
g_Config.memStickDirectory = DarwinFileSystemServices::appropriateMemoryStickDirectoryToUse();
g_Config.flash0Directory = Path(external_dir) / "flash0";
#elif PPSSPP_PLATFORM(SWITCH)
g_Config.memStickDirectory = g_Config.internalDataDirectory / "config/ppsspp";
g_Config.flash0Directory = g_Config.internalDataDirectory / "assets/flash0";
#elif PPSSPP_PLATFORM(WINDOWS)
// ...
#else
@@ -669,7 +661,6 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO
config = "./config";
g_Config.memStickDirectory = Path(config) / "ppsspp";
g_Config.flash0Directory = File::GetExeDirectory() / "assets/flash0";
if (getenv("HOME") != nullptr) {
g_Config.defaultCurrentDirectory = Path(getenv("HOME"));
} else {
@@ -683,6 +674,9 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO
g_Config.currentDirectory = g_Config.defaultCurrentDirectory;
}
// Mount a filesystem
g_Config.nandRootDirectory = GetSysDirectory(DIRECTORY_NAND);
if (cache_dir && strlen(cache_dir)) {
g_Config.appCacheDirectory = Path(cache_dir);
DiskCachingFileLoaderCache::SetCacheDir(g_Config.appCacheDirectory);
@@ -702,7 +696,7 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO
boot_filename.clear();
if (boot_filename.empty() && cmdLineOptions.bootVSH.has_value() && cmdLineOptions.bootVSH.value()) {
boot_filename = g_Config.flash0Directory / "vsh/module/vshmain.prx";
boot_filename = g_Config.nandRootDirectory / "flash0/vsh/module/vshmain.prx";
}
if (cmdLineOptions.appendConfig.has_value()) {
+3 -3
View File
@@ -61,14 +61,14 @@ void App::InitialPPSSPP() {
g_VFS.Register("", new DirectoryReader(exePath / "Content"));
g_VFS.Register("", new DirectoryReader(exePath));
// Mount a filesystem
g_Config.flash0Directory = exePath / "assets/flash0";
// Prepare for initialization
std::wstring internalDataFolderW = std::wstring(winrt::Windows::Storage::ApplicationData::Current().LocalFolder().Path());
g_Config.internalDataDirectory = Path(internalDataFolderW);
g_Config.memStickDirectory = g_Config.internalDataDirectory;
// Mount a filesystem
g_Config.nandRootDirectory = GetSysDirectory(PSPDirectories::DIRECTORY_NAND);
// On Win32 it makes more sense to initialize the system directories here
// because the next place it was called was in the EmuThread, and it's too late by then.
CreateSysDirectories();
+1 -3
View File
@@ -927,12 +927,10 @@ std::vector<std::wstring> GetWideCmdLine() {
}
static void InitMemstickDirectory() {
if (!g_Config.memStickDirectory.empty() && !g_Config.flash0Directory.empty())
if (!g_Config.memStickDirectory.empty() && !g_Config.nandRootDirectory.empty())
return;
const Path &exePath = File::GetExeDirectory();
// Mount a filesystem
g_Config.flash0Directory = exePath / "assets/flash0";
// Caller sets this to the Documents folder.
const Path rootMyDocsPath = g_Config.internalDataDirectory;
+9 -4
View File
@@ -782,11 +782,11 @@ int main(int argc, const char* argv[]) {
coreParameter.fastForward = true;
Path exePath = File::GetExeDirectory();
g_Config.flash0Directory = exePath / "assets/flash0";
// --memstick, applied by ApplyToConfig() further up, wins. This runs after it, so without the
// check the default below would silently overwrite whatever was asked for.
if (!cmdLineOptions.memStick.has_value()) {
// TODO: Share this derivation with the main build.
#if PPSSPP_PLATFORM(WINDOWS)
// Mount a filesystem
g_Config.memStickDirectory = exePath / "memstick";
@@ -796,12 +796,14 @@ int main(int argc, const char* argv[]) {
g_Config.memStickDirectory = Path(std::string(getenv("HOME"))) / ".ppsspp";
#endif
}
g_Config.nandRootDirectory = GetSysDirectory(DIRECTORY_NAND);
coreParameter.nandRoot = g_Config.nandRootDirectory;
// Try to find the flash0 directory. Often this is from a subdirectory.
// Try to find the assets flash0 directory. Often this is from a subdirectory.
// This is needed for our fallback fonts.
Path nextPath = exePath;
for (int i = 0; i < 5; ++i) {
if (File::Exists(nextPath / "assets/flash0")) {
g_Config.flash0Directory = nextPath / "assets/flash0";
#if !PPSSPP_PLATFORM(ANDROID)
g_VFS.Register("", new DirectoryReader(nextPath / "assets"));
#endif
@@ -847,8 +849,9 @@ int main(int argc, const char* argv[]) {
UpdateUIState(UISTATE_INGAME);
if (cmdLineOptions.bootVSH.has_value() && cmdLineOptions.bootVSH.value()) {
AddToTestsByPath(&testFilenames, (g_Config.flash0Directory / "vsh/module/vshmain.prx").ToString());
AddToTestsByPath(&testFilenames, (coreParameter.nandRoot / "flash0/vsh/module/vshmain.prx").ToString());
}
if (testFilenames.empty()) {
return printUsage(cmdLineOptions, argv[0], argc <= 1 ? NULL : "No executables specified");
}
@@ -887,6 +890,8 @@ int main(int argc, const char* argv[]) {
graphicsContext->ShutdownAPI();
delete graphicsContext;
if (cmdLineOptions.debuggerPort.has_value()) {
ShutdownWebServer();
}
+1 -1
View File
@@ -1267,10 +1267,10 @@ void retro_init(void)
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;
g_Config.bEnableNetworkChat = false;
g_Config.bDiscordRichPresence = false;
g_Config.nandRootDirectory = GetSysDirectory(PSPDirectories::DIRECTORY_NAND);
g_VFS.Register("", new DirectoryReader(retro_base_dir));