From 102b7b03fa7d91cb1a9469124609074a560903ba Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 23 Nov 2014 13:25:32 -0800 Subject: [PATCH] Add an interface to load files with. This allows loading from other than raw files. --- Core/Loaders.cpp | 153 +++++++++++++++++++++++++++++++------------ Core/Loaders.h | 60 ++++++++++++++++- Core/PSPLoaders.cpp | 15 +++-- Core/PSPLoaders.h | 4 +- Core/System.cpp | 11 +++- UI/GameInfoCache.cpp | 4 +- 6 files changed, 190 insertions(+), 57 deletions(-) diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index afd7418160..c6c4341c9c 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "file/file_util.h" +#include #include "Common/FileUtil.h" #include "Core/MIPS/MIPS.h" @@ -30,34 +31,106 @@ #include "Core/ELF/PBPReader.h" #include "Core/ELF/ParamSFO.h" -// TODO : improve, look in the file more -IdentifiedFileType Identify_File(std::string &filename) -{ - if (filename.size() == 0) { - ERROR_LOG(LOADER, "invalid filename %s", filename.c_str()); - return FILETYPE_ERROR; +LocalFileLoader::LocalFileLoader(const std::string &filename) { + Reopen(filename); +} + +bool LocalFileLoader::Reopen(const std::string &filename) { + fd_ = 0; + f_ = nullptr; + filesize_ = 0; + filename_ = filename; + + f_ = File::OpenCFile(filename, "rb"); + if (!f_) { + return false; } +#ifdef ANDROID + fd_ = fileno(f_); + + off64_t off = lseek64(fd_, 0, SEEK_END); + filesize_ = off; + lseek64(fd_, 0, SEEK_SET); +#else + fseek(f_, 0, SEEK_END); + filesize_ = ftello(f_); + fseek(f_, 0, SEEK_SET); +#endif + + return true; +} + +LocalFileLoader::~LocalFileLoader() { + fclose(f_); +} + +bool LocalFileLoader::Exists() { FileInfo info; - if (!getFileInfo(filename.c_str(), &info)) { + return getFileInfo(filename_.c_str(), &info); +} + +bool LocalFileLoader::IsDirectory() { + FileInfo info; + if (getFileInfo(filename_.c_str(), &info)) { + return info.isDirectory; + } + return false; +} + +s64 LocalFileLoader::FileSize() { + return filesize_; +} + +std::string LocalFileLoader::Path() const { + return filename_; +} + +void LocalFileLoader::Seek(s64 absolutePos) { +#ifdef ANDROID + lseek64(fd_, absolutePos, SEEK_SET); +#else + fseeko(f_, absolutePos, SEEK_SET); +#endif +} + +size_t LocalFileLoader::Read(size_t bytes, size_t count, void *data) { +#ifdef ANDROID + return read(fd_, data, bytes * count) / bytes; +#else + return fread(data, bytes, count, f_); +#endif +} + +size_t LocalFileLoader::ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data) { + Seek(absolutePos); + return Read(bytes, count, data); +} + +// TODO : improve, look in the file more +IdentifiedFileType Identify_File(FileLoader *fileLoader) +{ + if (fileLoader == nullptr) { + ERROR_LOG(LOADER, "Invalid fileLoader"); + return FILETYPE_ERROR; + } + if (fileLoader->Path().size() == 0) { + ERROR_LOG(LOADER, "Invalid filename %s", fileLoader->Path().c_str()); return FILETYPE_ERROR; } - std::string extension = filename.size() >= 5 ? filename.substr(filename.size() - 4) : ""; + if (!fileLoader->Exists()) { + return FILETYPE_ERROR; + } + + std::string extension = fileLoader->Extension(); if (!strcasecmp(extension.c_str(),".iso")) { // may be a psx iso, they have 2352 byte sectors. You never know what some people try to open - if ((info.size % 2352) == 0) + if ((fileLoader->FileSize() % 2352) == 0) { - FILE *f = File::OpenCFile(filename.c_str(), "rb"); - if (!f) { - // File does not exists - return FILETYPE_ERROR; - } - unsigned char sync[12]; - fread(sync,1,12,f); - fclose(f); + fileLoader->ReadAt(0, 12, sync); // each sector in a mode2 image starts with these 12 bytes if (memcmp(sync,"\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00",12) == 0) @@ -76,7 +149,8 @@ IdentifiedFileType Identify_File(std::string &filename) // First, check if it's a directory with an EBOOT.PBP in it. - if (info.isDirectory) { + if (fileLoader->IsDirectory()) { + std::string filename = fileLoader->Path(); if (filename.size() > 4) { FileInfo ebootInfo; // Check for existence of EBOOT.PBP, as required for "Directory games". @@ -97,17 +171,10 @@ IdentifiedFileType Identify_File(std::string &filename) return FILETYPE_NORMAL_DIRECTORY; } - FILE *f = File::OpenCFile(filename.c_str(), "rb"); - if (!f) { - // File does not exists - return FILETYPE_ERROR; - } - u32_le id; - size_t readSize = fread(&id, 4, 1, f); + size_t readSize = fileLoader->ReadAt(0, 4, 1, &id); if (readSize != 1) { - fclose(f); return FILETYPE_ERROR; } @@ -115,10 +182,8 @@ IdentifiedFileType Identify_File(std::string &filename) u32 _id = id; switch (_id) { case 'PBP\x00': - fseek(f, 0x24, SEEK_SET); - fread(&psar_offset, 4, 1, f); - fseek(f, psar_offset, SEEK_SET); - fread(&psar_id, 4, 1, f); + fileLoader->ReadAt(0x24, 4, 1, &psar_offset); + fileLoader->ReadAt(psar_offset, 4, 1, &psar_id); break; case '!raR': return FILETYPE_ARCHIVE_RAR; @@ -128,9 +193,8 @@ IdentifiedFileType Identify_File(std::string &filename) return FILETYPE_ARCHIVE_ZIP; } - fclose(f); - if (id == 'FLE\x7F') { + std::string filename = fileLoader->Path(); // There are a few elfs misnamed as pbp (like Trig Wars), accept that. if (!strcasecmp(extension.c_str(), ".plf") || strstr(filename.c_str(),"BOOT.BIN") || !strcasecmp(extension.c_str(), ".elf") || !strcasecmp(extension.c_str(), ".prx") || @@ -142,6 +206,8 @@ IdentifiedFileType Identify_File(std::string &filename) else if (id == 'PBP\x00') { // Do this PS1 eboot check FIRST before checking other eboot types. // It seems like some are malformed and slip through the PSAR check below. + // TODO: Change PBPReader to read FileLoader objects? + std::string filename = fileLoader->Path(); PBPReader pbp(filename.c_str()); if (pbp.IsValid()) { if (!pbp.IsELF()) { @@ -199,21 +265,22 @@ IdentifiedFileType Identify_File(std::string &filename) return FILETYPE_UNKNOWN; } -bool LoadFile(std::string &filename, std::string *error_string) { +bool LoadFile(FileLoader *fileLoader, std::string *error_string) { // Note that this can modify filename! - switch (Identify_File(filename)) { + switch (Identify_File(fileLoader)) { case FILETYPE_PSP_PBP_DIRECTORY: { + std::string filename = fileLoader->Path(); std::string ebootFilename = filename + "/EBOOT.PBP"; - FileInfo fileInfo; - getFileInfo((filename + "/EBOOT.PBP").c_str(), &fileInfo); - if (fileInfo.exists) { + // Switch fileLoader to the EBOOT. + fileLoader->Reopen(ebootFilename); + + if (fileLoader->Exists()) { INFO_LOG(LOADER, "File is a PBP in a directory!"); - std::string ebootPath = filename + "/EBOOT.PBP"; - IdentifiedFileType ebootType = Identify_File(ebootPath); + IdentifiedFileType ebootType = Identify_File(fileLoader); if (ebootType == FILETYPE_PSP_ISO_NP) { - InitMemoryForGameISO(ebootPath); + InitMemoryForGameISO(fileLoader); pspFileSystem.SetStartingDirectory("disc0:/PSP_GAME/USRDIR"); return Load_PSP_ISO(filename.c_str(), error_string); } @@ -225,7 +292,7 @@ bool LoadFile(std::string &filename, std::string *error_string) { size_t pos = path.find("/PSP/GAME/"); if (pos != std::string::npos) pspFileSystem.SetStartingDirectory("ms0:" + path.substr(pos)); - return Load_PSP_ELF_PBP(ebootFilename.c_str(), error_string); + return Load_PSP_ELF_PBP(fileLoader->Path().c_str(), error_string); } else { *error_string = "No EBOOT.PBP, misidentified game"; return false; @@ -236,14 +303,14 @@ bool LoadFile(std::string &filename, std::string *error_string) { case FILETYPE_PSP_ELF: { INFO_LOG(LOADER,"File is an ELF or loose PBP!"); - return Load_PSP_ELF_PBP(filename.c_str(), error_string); + return Load_PSP_ELF_PBP(fileLoader->Path().c_str(), error_string); } case FILETYPE_PSP_ISO: case FILETYPE_PSP_ISO_NP: case FILETYPE_PSP_DISC_DIRECTORY: // behaves the same as the mounting is already done by now pspFileSystem.SetStartingDirectory("disc0:/PSP_GAME/USRDIR"); - return Load_PSP_ISO(filename.c_str(), error_string); + return Load_PSP_ISO(fileLoader->Path().c_str(), error_string); case FILETYPE_PSP_PS1_PBP: *error_string = "PS1 EBOOTs are not supported by PPSSPP."; diff --git a/Core/Loaders.h b/Core/Loaders.h index ddf1ee5b43..c39fc471d6 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -46,9 +46,65 @@ enum IdentifiedFileType { FILETYPE_UNKNOWN }; +class FileLoader { +public: + virtual ~FileLoader() {} + + // Needed when we switch from a directory to a PBP, etc. + virtual bool Reopen(const std::string &filename) = 0; + + virtual bool Exists() = 0; + virtual bool IsDirectory() = 0; + virtual s64 FileSize() = 0; + virtual std::string Path() const = 0; + virtual std::string Extension() { + const std::string filename = Path(); + size_t pos = filename.find_last_of('.'); + if (pos == filename.npos) { + return ""; + } else { + return filename.substr(pos); + } + } + + virtual void Seek(s64 absolutePos) = 0; + virtual size_t Read(size_t bytes, size_t count, void *data) = 0; + virtual size_t Read(size_t bytes, void *data) { + return Read(1, bytes, data); + } + virtual size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data) = 0; + virtual size_t ReadAt(s64 absolutePos, size_t bytes, void *data) { + return ReadAt(absolutePos, 1, bytes, data); + } +}; + +class LocalFileLoader : public FileLoader { +public: + LocalFileLoader(const std::string &filename); + virtual ~LocalFileLoader(); + + virtual bool Reopen(const std::string &filename); + + virtual bool Exists() override; + virtual bool IsDirectory() override; + virtual s64 FileSize() override; + virtual std::string Path() const override; + + virtual void Seek(s64 absolutePos) override; + virtual size_t Read(size_t bytes, size_t count, void *data) override; + virtual size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data) override; + +private: + // First only used by Android, but we can keep it here for everyone. + int fd_; + FILE *f_; + u64 filesize_; + std::string filename_; +}; + // This can modify the string, for example for stripping off the "/EBOOT.PBP" // for a FILETYPE_PSP_PBP_DIRECTORY. -IdentifiedFileType Identify_File(std::string &str); +IdentifiedFileType Identify_File(FileLoader *fileLoader); // Can modify the string filename, as it calls IdentifyFile above. -bool LoadFile(std::string &filename, std::string *error_string); +bool LoadFile(FileLoader *fileLoader, std::string *error_string); diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 5a14cd7173..e020d4865d 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -32,6 +32,7 @@ #include "FileSystems/MetaFileSystem.h" #include "FileSystems/VirtualDiscFileSystem.h" +#include "Core/Loaders.h" #include "Core/MemMap.h" #include "Core/HDRemaster.h" @@ -53,21 +54,21 @@ // We gather the game info before actually loading/booting the ISO // to determine if the emulator should enable extra memory and // double-sized texture coordinates. -void InitMemoryForGameISO(std::string fileToStart) { +void InitMemoryForGameISO(FileLoader *fileLoader) { IFileSystem* umd2; - // check if it's a disc directory - FileInfo info; - if (!getFileInfo(fileToStart.c_str(), &info)) return; + if (!fileLoader->Exists()) { + return; + } bool actualIso = false; - if (info.isDirectory) + if (fileLoader->IsDirectory()) { - umd2 = new VirtualDiscFileSystem(&pspFileSystem, fileToStart); + umd2 = new VirtualDiscFileSystem(&pspFileSystem, fileLoader->Path()); } else { - auto bd = constructBlockDevice(fileToStart.c_str()); + auto bd = constructBlockDevice(fileLoader->Path().c_str()); // Can't init anything without a block device... if (!bd) return; diff --git a/Core/PSPLoaders.h b/Core/PSPLoaders.h index 4faa7287d9..dc788772e4 100644 --- a/Core/PSPLoaders.h +++ b/Core/PSPLoaders.h @@ -19,6 +19,8 @@ #include +class FileLoader; + bool Load_PSP_ISO(const char *filename, std::string *error_string); bool Load_PSP_ELF_PBP(const char *filename, std::string *error_string); -void InitMemoryForGameISO(std::string fileToStart); +void InitMemoryForGameISO(FileLoader *fileLoader); diff --git a/Core/System.cpp b/Core/System.cpp index 7a87f7b61d..924463c9e6 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -72,6 +72,7 @@ MetaFileSystem pspFileSystem; ParamSFOData g_paramSFO; static GlobalUIState globalUIState; static CoreParameter coreParameter; +static FileLoader *loadedFile; static PSPMixer *mixer; static std::thread *cpuThread = NULL; static std::thread::id cpuThreadID; @@ -181,7 +182,8 @@ void CPU_Init() { Memory::g_PSPModel = g_Config.iPSPModel; std::string filename = coreParameter.fileToStart; - IdentifiedFileType type = Identify_File(filename); + loadedFile = new LocalFileLoader(filename); + IdentifiedFileType type = Identify_File(loadedFile); MIPSAnalyst::Reset(); Replacement_Init(); @@ -190,7 +192,7 @@ void CPU_Init() { case FILETYPE_PSP_ISO: case FILETYPE_PSP_ISO_NP: case FILETYPE_PSP_DISC_DIRECTORY: - InitMemoryForGameISO(filename); + InitMemoryForGameISO(loadedFile); break; default: break; @@ -213,7 +215,7 @@ void CPU_Init() { // TODO: Check Game INI here for settings, patches and cheats, and modify coreParameter accordingly // Why did we check for CORE_POWERDOWN here? - if (!LoadFile(filename, &coreParameter.errorString)) { + if (!LoadFile(loadedFile, &coreParameter.errorString)) { CPU_Shutdown(); coreParameter.fileToStart = ""; CPU_SetState(CPU_THREAD_NOT_RUNNING); @@ -245,6 +247,9 @@ void CPU_Shutdown() { pspFileSystem.Shutdown(); mipsr4k.Shutdown(); Memory::Shutdown(); + + delete loadedFile; + loadedFile = 0; } void CPU_RunLoop() { diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 985950b5ff..872eeeebc6 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include "base/logging.h" @@ -246,8 +247,9 @@ public: return; std::string filename = gamePath_; + std::unique_ptr fileLoader(new LocalFileLoader(filename)); info_->path = gamePath_; - info_->fileType = Identify_File(filename); + info_->fileType = Identify_File(fileLoader.get()); // Fallback title info_->title = getFilename(info_->path);