From 317a06f2f62822ea459558411317ccdfd2ca36cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 3 Aug 2026 11:05:38 +0200 Subject: [PATCH] Revert "Delete the ability to load file handler plugins from extracted game folders" This reverts commit e80619215472680a423179268fff87402c35388a. --- Core/FileSystems/VirtualDiscFileSystem.cpp | 156 ++++++++++++++++++++- Core/FileSystems/VirtualDiscFileSystem.h | 95 ++++++++++++- 2 files changed, 243 insertions(+), 8 deletions(-) diff --git a/Core/FileSystems/VirtualDiscFileSystem.cpp b/Core/FileSystems/VirtualDiscFileSystem.cpp index be43ef48fc..baa5dcdacc 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.cpp +++ b/Core/FileSystems/VirtualDiscFileSystem.cpp @@ -40,6 +40,9 @@ #include #include #include +#if !PPSSPP_PLATFORM(SWITCH) +#include +#endif #endif const std::string INDEX_FILENAME = ".ppsspp-index.lst"; @@ -56,6 +59,9 @@ VirtualDiscFileSystem::~VirtualDiscFileSystem() { iter->second.Close(); } } + for (auto iter = handlers.begin(), end = handlers.end(); iter != end; ++iter) { + delete iter->second; + } } void VirtualDiscFileSystem::LoadFileListIndex() { @@ -101,13 +107,39 @@ void VirtualDiscFileSystem::LoadFileListIndex() { filename_pos++; } - entry.fileName = line.substr(filename_pos); + // Check if there's a handler specified. + size_t handler_pos = line.find(':', filename_pos); + if (handler_pos != line.npos) { + entry.fileName = line.substr(filename_pos, handler_pos - filename_pos); + + std::string handler = line.substr(handler_pos + 1); + size_t trunc = handler.find_last_not_of("\r\n"); + if (trunc != handler.npos && trunc != handler.size()) + handler.resize(trunc + 1); + + if (handlers.find(handler) == handlers.end()) + handlers[handler] = new Handler(handler.c_str(), this); + if (handlers[handler]->IsValid()) + entry.handler = handlers[handler]; + } else { + entry.fileName = line.substr(filename_pos); + } size_t trunc = entry.fileName.find_last_not_of("\r\n"); if (trunc != entry.fileName.npos && trunc != entry.fileName.size()) entry.fileName.resize(trunc + 1); entry.firstBlock = (u32)strtol(line.c_str(), NULL, 16); - entry.totalSize = File::GetFileSize(GetLocalPath(entry.fileName)); + if (entry.handler != NULL && entry.handler->IsValid()) { + HandlerFileHandle temp = entry.handler; + if (temp.Open(basePath.ToString(), entry.fileName, FILEACCESS_READ)) { + entry.totalSize = (u32)temp.Seek(0, FILEMOVE_END); + temp.Close(); + } else { + ERROR_LOG(Log::FileSystem, "Unable to open virtual file: %s", entry.fileName.c_str()); + } + } else { + entry.totalSize = File::GetFileSize(GetLocalPath(entry.fileName)); + } // Try to keep currentBlockIndex sane, in case there are other files. u32 nextBlock = entry.firstBlock + (entry.totalSize + 2047) / 2048; @@ -162,6 +194,10 @@ void VirtualDiscFileSystem::DoState(PointerWrap &p) // open file if (of.type != VFILETYPE_ISO) { + if (fileList[of.fileIndex].handler != NULL) { + of.handler = fileList[of.fileIndex].handler; + } + bool success = of.Open(basePath, fileList[of.fileIndex].fileName, FILEACCESS_READ); if (!success) { ERROR_LOG(Log::FileSystem, "Failed to create file handle for %s.", fileList[of.fileIndex].fileName.c_str()); @@ -296,15 +332,19 @@ int VirtualDiscFileSystem::OpenFile(std::string filename, FileAccess access, con entry.size = readSize; int fileIndex = getFileListIndex(sectorStart,readSize); - if (fileIndex == -1) { + if (fileIndex == -1) + { ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem: sce_lbn used without calling fileinfo."); return 0; } entry.fileIndex = (u32)fileIndex; - entry.startOffset = (sectorStart-fileList[entry.fileIndex].firstBlock) * 2048; + entry.startOffset = (sectorStart-fileList[entry.fileIndex].firstBlock)*2048; // now we just need an actual file handle + if (fileList[entry.fileIndex].handler != NULL) { + entry.handler = fileList[entry.fileIndex].handler; + } bool success = entry.Open(basePath, fileList[entry.fileIndex].fileName, FILEACCESS_READ); if (!success) { @@ -330,6 +370,9 @@ int VirtualDiscFileSystem::OpenFile(std::string filename, FileAccess access, con entry.type = VFILETYPE_NORMAL; entry.fileIndex = getFileListIndex(filename); + if (entry.fileIndex != (u32)-1 && fileList[entry.fileIndex].handler != NULL) { + entry.handler = fileList[entry.fileIndex].handler; + } bool success = entry.Open(basePath, filename, (FileAccess)(access & FILEACCESS_PSP_FLAGS)); if (!success) { @@ -418,6 +461,9 @@ size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &u } OpenFileEntry temp(Flags()); + if (fileList[fileIndex].handler != NULL) { + temp.handler = fileList[fileIndex].handler; + } bool success = temp.Open(basePath, fileList[fileIndex].fileName, FILEACCESS_READ); if (!success) @@ -525,6 +571,22 @@ PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) { } int fileIndex = getFileListIndex(filename); + if (fileIndex != -1 && fileList[fileIndex].handler != NULL) { + x.type = FILETYPE_NORMAL; + x.isOnSectorSystem = true; + x.startSector = fileList[fileIndex].firstBlock; + x.access = 0555; + + HandlerFileHandle temp = fileList[fileIndex].handler; + if (temp.Open(basePath.ToString(), filename, FILEACCESS_READ)) { + x.exists = true; + x.size = temp.Seek(0, FILEMOVE_END); + temp.Close(); + } + + // TODO: Probably should include dates or something... + return x; + } Path fullName = GetLocalPath(filename); if (!File::Exists(fullName)) { @@ -737,3 +799,89 @@ bool VirtualDiscFileSystem::RemoveFile(const std::string &filename) ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot remove file on virtual disc"); return false; } + +void VirtualDiscFileSystem::HandlerLogger(void *arg, HandlerHandle handle, LogLevel level, const char *msg) { + VirtualDiscFileSystem *sys = static_cast(arg); + + // TODO: Probably could do this smarter / use a lookup. + const char *filename = NULL; + for (auto it = sys->entries.begin(), end = sys->entries.end(); it != end; ++it) { + if (it->second.fileIndex != (u32)-1 && it->second.handler.handle == handle) { + filename = sys->fileList[it->second.fileIndex].fileName.c_str(); + break; + } + } + + if (filename != NULL) { + GENERIC_LOG(Log::FileSystem, level, "%s: %s", filename, msg); + } else { + GENERIC_LOG(Log::FileSystem, level, "%s", msg); + } +} + +VirtualDiscFileSystem::Handler::Handler(const char *filename, VirtualDiscFileSystem *const sys) +: sys_(sys) { +#if !PPSSPP_PLATFORM(SWITCH) +#ifdef _WIN32 +#if PPSSPP_PLATFORM(UWP) +#define dlopen(name, ignore) (void *)LoadPackagedLibrary(ConvertUTF8ToWString(name).c_str(), 0) +#define dlsym(mod, name) GetProcAddress((HMODULE)mod, name) +#define dlclose(mod) FreeLibrary((HMODULE)mod) +#else +#define dlopen(name, ignore) (void *)LoadLibrary(ConvertUTF8ToWString(name).c_str()) +#define dlsym(mod, name) GetProcAddress((HMODULE)mod, name) +#define dlclose(mod) FreeLibrary((HMODULE)mod) +#endif +#endif + + library = dlopen(filename, RTLD_LOCAL | RTLD_NOW); + if (library != NULL) { + Init = (InitFunc)dlsym(library, "Init"); + Shutdown = (ShutdownFunc)dlsym(library, "Shutdown"); + Open = (OpenFunc)dlsym(library, "Open"); + Seek = (SeekFunc)dlsym(library, "Seek"); + Read = (ReadFunc)dlsym(library, "Read"); + Close = (CloseFunc)dlsym(library, "Close"); + + VersionFunc Version = (VersionFunc)dlsym(library, "Version"); + if (Version && Version() >= 2) { + ShutdownV2 = (ShutdownV2Func)Shutdown; + } + + if (!Init || !Shutdown || !Open || !Seek || !Read || !Close) { + ERROR_LOG(Log::FileSystem, "Unable to find all handler functions: %s", filename); + dlclose(library); + library = NULL; + } else if (!Init(&HandlerLogger, sys)) { + ERROR_LOG(Log::FileSystem, "Unable to initialize handler: %s", filename); + dlclose(library); + library = NULL; + } + } else { + ERROR_LOG(Log::FileSystem, "Unable to load handler '%s': %s", filename, GetLastErrorMsg().c_str()); + } +#ifdef _WIN32 +#undef dlopen +#undef dlsym +#undef dlclose +#endif +#endif +} + +VirtualDiscFileSystem::Handler::~Handler() { + if (library != NULL) { + if (ShutdownV2) + ShutdownV2(sys_); + else + Shutdown(); + +#if !PPSSPP_PLATFORM(UWP) && !PPSSPP_PLATFORM(SWITCH) +#ifdef _WIN32 + FreeLibrary((HMODULE)library); +#else + dlclose(library); +#endif +#endif + } +} + diff --git a/Core/FileSystems/VirtualDiscFileSystem.h b/Core/FileSystems/VirtualDiscFileSystem.h index 202b338e37..00fb4ada5d 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.h +++ b/Core/FileSystems/VirtualDiscFileSystem.h @@ -66,6 +66,73 @@ private: int getFileListIndex(u32 accessBlock, u32 accessSize, bool blockMode = false) const; Path GetLocalPath(std::string_view localpath) const; + typedef void *HandlerLibrary; + typedef int HandlerHandle; + typedef s64 HandlerOffset; + typedef void (*HandlerLogFunc)(void *arg, HandlerHandle handle, LogLevel level, const char *msg); + + static void HandlerLogger(void *arg, HandlerHandle handle, LogLevel level, const char *msg); + + // The primary purpose of handlers is to make it easier to work with large archives. + // However, they have other uses as well, such as patching individual files. + struct Handler { + Handler(const char *filename, VirtualDiscFileSystem *const sys); + ~Handler(); + + typedef bool (*InitFunc)(HandlerLogFunc logger, void *loggerArg); + typedef void (*ShutdownFunc)(); + typedef void (*ShutdownV2Func)(void *loggerArg); + typedef HandlerHandle (*OpenFunc)(const char *basePath, const char *filename); + typedef HandlerOffset (*SeekFunc)(HandlerHandle handle, HandlerOffset offset, FileMove origin); + typedef HandlerOffset (*ReadFunc)(HandlerHandle handle, void *data, HandlerOffset size); + typedef void (*CloseFunc)(HandlerHandle handle); + typedef int (*VersionFunc)(); + + HandlerLibrary library; + VirtualDiscFileSystem *const sys_; + InitFunc Init; + ShutdownFunc Shutdown; + ShutdownV2Func ShutdownV2; + OpenFunc Open; + SeekFunc Seek; + ReadFunc Read; + CloseFunc Close; + + bool IsValid() const { return library != nullptr; } + }; + + struct HandlerFileHandle { + Handler *handler; + HandlerHandle handle; + + HandlerFileHandle() : handler(nullptr), handle(0) {} + HandlerFileHandle(Handler *handler_) : handler(handler_), handle(-1) {} + + bool Open(const std::string& basePath, const std::string& fileName, FileAccess access) { + // Ignore access, read only. + handle = handler->Open(basePath.c_str(), fileName.c_str()); + return handle > 0; + } + size_t Read(u8 *data, s64 size) { + return (size_t)handler->Read(handle, data, size); + } + size_t Seek(s32 position, FileMove type) { + return (size_t)handler->Seek(handle, position, type); + } + void Close() { + handler->Close(handle); + } + + bool IsValid() { + return handler != nullptr && handler->IsValid(); + } + + HandlerFileHandle &operator =(Handler *_handler) { + handler = _handler; + return *this; + } + }; + typedef enum { VFILETYPE_NORMAL, VFILETYPE_LBN, VFILETYPE_ISO } VirtualFileType; struct OpenFileEntry { @@ -75,6 +142,7 @@ private: } DirectoryFileHandle hFile; + HandlerFileHandle handler; VirtualFileType type = VFILETYPE_NORMAL; u32 fileIndex = 0; u64 curOffset = 0; @@ -84,16 +152,32 @@ private: bool Open(const Path &basePath, std::string& fileName, FileAccess access) { // Ignored, we're read only. u32 err; - return hFile.Open(basePath, fileName, access, err); + if (handler.IsValid()) { + return handler.Open(basePath.ToString(), fileName, access); + } else { + return hFile.Open(basePath, fileName, access, err); + } } size_t Read(u8 *data, s64 size) { - return hFile.Read(data, size); + if (handler.IsValid()) { + return handler.Read(data, size); + } else { + return hFile.Read(data, size); + } } size_t Seek(s32 position, FileMove type) { - return hFile.Seek(position, type); + if (handler.IsValid()) { + return handler.Seek(position, type); + } else { + return hFile.Seek(position, type); + } } void Close() { - return hFile.Close(); + if (handler.IsValid()) { + return handler.Close(); + } else { + return hFile.Close(); + } } }; @@ -107,9 +191,12 @@ private: std::string fileName; u32 firstBlock; u32 totalSize; + Handler *handler; }; std::vector fileList; u32 currentBlockIndex; u32 lastReadBlock_; + + std::map handlers; };