mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Add an interface to load files with.
This allows loading from other than raw files.
This commit is contained in:
1 parent
a0461f1dcd
commit
102b7b03fa
6 files changed
+190
-57
No files matched your search
+110
-43
@@ -16,6 +16,7 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "file/file_util.h"
|
||||
#include <cstdio>
|
||||
|
||||
#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.";
|
||||
|
||||
+58
-2
@@ -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);
|
||||
+8
-7
@@ -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;
|
||||
|
||||
+3
-1
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
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);
|
||||
+8
-3
@@ -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() {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/logging.h"
|
||||
@@ -246,8 +247,9 @@ public:
|
||||
return;
|
||||
|
||||
std::string filename = gamePath_;
|
||||
std::unique_ptr<FileLoader> 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);
|
||||
|
||||
|
||||
Reference in new issue
Block a user