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/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..b6012a52f9 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}, @@ -538,7 +538,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()); } diff --git a/Core/CmdLine.h b/Core/CmdLine.h index 02ae8c9105..1fa39e0206 100644 --- a/Core/CmdLine.h +++ b/Core/CmdLine.h @@ -57,7 +57,8 @@ 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:. + // 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/CoreParameter.h b/Core/CoreParameter.h index f09f9d0c55..790fec463b 100644 --- a/Core/CoreParameter.h +++ b/Core/CoreParameter.h @@ -60,7 +60,8 @@ struct CoreParameter { 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:. + 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 06292eb2d5..3bcde05700 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -194,48 +194,34 @@ 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. + 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 */) - { + 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) - { + 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 '/' @@ -297,6 +283,10 @@ void MetaFileSystem::Mount(std::string_view prefix, std::shared_ptr } } + if (equalsNoCase(prefix, "host0:")) { + host0Mapped_ = true; + } + // Prefix not yet mounted, do so. MountPoint x; x.prefix = prefix; @@ -309,10 +299,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 59fd96c134..9c1fca6fad 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -50,6 +50,9 @@ private: 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. diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index f2bd0baad7..c9d638eb85 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1816,7 +1816,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/System.cpp b/Core/System.cpp index b1b4064bef..edbc119fd4 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -324,6 +324,9 @@ static void MountFileSystems() { pspFileSystem.Mount("flash0:", flash0System); + // NOTE: We don't handle the host0: mount here, it's in Load_PSP_ELF_PBP. + // Additionally, host0: is remapped to umd0: if there is an UMD inserted (old hack in MetaFileSystem). + if (g_RemasterMode) { const std::string gameId = g_paramSFO.GetDiscID(); const Path exdataPath = GetSysDirectory(DIRECTORY_EXDATA) / gameId; diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 0c6a927b25..a33a6260d2 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -887,6 +887,8 @@ int main(int argc, const char* argv[]) { graphicsContext->ShutdownAPI(); + delete graphicsContext; + if (cmdLineOptions.debuggerPort.has_value()) { ShutdownWebServer(); }