diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index ea1e25c795..62020ee59b 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -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; diff --git a/Common/File/VFS/VFS.h b/Common/File/VFS/VFS.h index d3bb6b4685..d39442336e 100644 --- a/Common/File/VFS/VFS.h +++ b/Common/File/VFS/VFS.h @@ -2,6 +2,7 @@ #include #include +#include #include #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 *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 *listing, const char *filter = nullptr) = 0; }; diff --git a/Common/LogReporting.cpp b/Common/LogReporting.cpp index b5909be304..24ac63be66 100644 --- a/Common/LogReporting.cpp +++ b/Common/LogReporting.cpp @@ -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); diff --git a/Core/CmdLine.cpp b/Core/CmdLine.cpp index fcb8519a9c..392bf38ab3 100644 --- a/Core/CmdLine.cpp +++ b/Core/CmdLine.cpp @@ -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); diff --git a/Core/CmdLine.h b/Core/CmdLine.h index 02ae8c9105..817262b804 100644 --- a/Core/CmdLine.h +++ b/Core/CmdLine.h @@ -57,7 +57,10 @@ struct CommandLineOptions { std::optional bootVSH; std::optional appendConfig; - std::optional root; // mount root, needs more explanation + std::optional root; // This is supposed to configure host0:. + + std::optional 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. diff --git a/Core/Config.h b/Core/Config.h index d0f18f6418..2a81b0f9d1 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -706,7 +706,7 @@ public: Path defaultCurrentDirectory; // Platform dependent, initialized at startup. Path memStickDirectory; - Path flash0Directory; + Path nandRootDirectory; Path internalDataDirectory; Path appCacheDirectory; diff --git a/Core/CoreParameter.h b/Core/CoreParameter.h index f09f9d0c55..22b75f18e8 100644 --- a/Core/CoreParameter.h +++ b/Core/CoreParameter.h @@ -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; diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 1350d4a359..b9f91f55a3 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -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; diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index 12053c1851..3ba4035bb7 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -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 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 system) { +void MetaFileSystem::Mount(std::string_view prefix, std::shared_ptr system, std::string_view subDir) { std::lock_guard 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 void MetaFileSystem::UnmountAll() { fileSystems.clear(); currentDir.clear(); + host0Mapped_ = false; } void MetaFileSystem::Unmount(std::string_view prefix) { std::lock_guard guard(lock); + if (equalsNoCase(prefix, "host0:")) { + host0Mapped_ = false; + } for (auto iter = fileSystems.begin(); iter != fileSystems.end(); iter++) { if (iter->prefix == prefix) { fileSystems.erase(iter); diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index c0362be8e9..dc71263b99 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -31,21 +31,27 @@ private: struct MountPoint { std::string prefix; std::shared_ptr 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 fileSystems; - typedef std::map currentDir_t; - currentDir_t currentDir; + std::map 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 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 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 GetDirListing(std::string_view path, bool *exists = nullptr) override; int OpenFile(std::string filename, FileAccess access, const char *devicename = nullptr) override; diff --git a/Core/HLE/sceFont.cpp b/Core/HLE/sceFont.cpp index ec0d8eb2ef..ade70d0f64 100644 --- a/Core/HLE/sceFont.cpp +++ b/Core/HLE/sceFont.cpp @@ -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) { diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index f2bd0baad7..02a01c5cff 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -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(uid, error); diff --git a/Core/HLE/scePsmf.cpp b/Core/HLE/scePsmf.cpp index 762b6ea992..3ebce4666f 100644 --- a/Core/HLE/scePsmf.cpp +++ b/Core/HLE/scePsmf.cpp @@ -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); diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index b4a757dc76..86f767f3c8 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -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(&pspFileSystem, dir, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); - pspFileSystem.Mount("umd0:", fs); + auto fs = std::make_shared(&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 diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index 08025004d1..67b61949a9 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -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; } diff --git a/Core/System.cpp b/Core/System.cpp index b1b4064bef..6a97027acb 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -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(&pspFileSystem, g_Config.flash0Directory, FileSystemFlags::FLASH); -#else - auto flash0System = std::make_shared(&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(&pspFileSystem, g_Config.nandRootDirectory / "flash0", FileSystemFlags::FLASH); + auto flash1System = std::make_shared(&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(); diff --git a/Core/Util/PathUtil.cpp b/Core/Util/PathUtil.cpp index 564e8c354f..7a0b8eacfc 100644 --- a/Core/Util/PathUtil.cpp +++ b/Core/Util/PathUtil.cpp @@ -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; diff --git a/Core/Util/PathUtil.h b/Core/Util/PathUtil.h index c69ea2f7db..73e4b21264 100644 --- a/Core/Util/PathUtil.h +++ b/Core/Util/PathUtil.h @@ -11,6 +11,7 @@ enum PSPDirectories { DIRECTORY_CHEATS, DIRECTORY_SCREENSHOT, DIRECTORY_SYSTEM, + DIRECTORY_NAND, DIRECTORY_GAME, DIRECTORY_SAVEDATA, DIRECTORY_PAUTH, diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 491330d186..cbbe2c16d1 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -419,15 +419,16 @@ void DrawThreadView(ImConfig &cfg, ImControl &control) { } std::vector 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); diff --git a/UI/ImDebugger/ImGe.cpp b/UI/ImDebugger/ImGe.cpp index 4457f661e2..a86566430c 100644 --- a/UI/ImDebugger/ImGe.cpp +++ b/UI/ImDebugger/ImGe.cpp @@ -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) { diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index d715605c28..048c1a2bd2 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -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()) { diff --git a/UWP/App.cpp b/UWP/App.cpp index 62135b6817..d1d553ed63 100644 --- a/UWP/App.cpp +++ b/UWP/App.cpp @@ -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(); diff --git a/Windows/main.cpp b/Windows/main.cpp index f27468b19c..4828013d6d 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -927,12 +927,10 @@ std::vector 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; diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 0c6a927b25..68e2068e8c 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -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(); } diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index c55b2d4065..283b083953 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -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));