Files
ppsspp/Core/Loaders.h
Henrik RydgårdandClaude Opus 5.5 34a0090a18 Install PS1 discs from zip and 7z archives, and show them as playable only with a firmware that runs them
A zip or 7z with a PS1 disc in it (a cue sheet and the files it names, a
.ccd with its .img and .sub, or a lone raw .bin or .img) is now installed
like a zipped ISO. A disc of several files goes into a folder of its own,
named after the cue sheet or .ccd. An ISO in the archive still takes
priority, and a lone .bin or .img only counts when nothing else does.

The game screen offers Play for a PS1 disc image only when the installed
firmware's POPS can run it: 3.95 to 6.61, with popsman.prx.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:01:22 +02:00

228 lines
6.5 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <string>
#include <memory>
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/File/Path.h"
#include "Common/File/VFS/ZipFileReader.h"
enum class IdentifiedFileType {
ERROR_IDENTIFYING,
PSP_PBP_DIRECTORY,
PSP_PBP,
PSP_ELF,
PSP_ISO,
PSP_ISO_NP,
PSP_DISC_DIRECTORY,
// Try to reduce support emails...
ARCHIVE_RAR,
ARCHIVE_ZIP,
ARCHIVE_7Z,
// A .pkg game update - a container we install from rather than boot. See Core/Util/PkgUnpack.h.
PSP_PKG,
PSP_PS1_PBP,
PSX_ISO,
PS2_ISO,
PS3_ISO,
PSP_UMD_VIDEO_ISO,
UNKNOWN_BIN,
UNKNOWN_ELF,
UNKNOWN_ISO,
NORMAL_DIRECTORY,
PSP_SAVEDATA_DIRECTORY,
PPSSPP_SAVESTATE,
PPSSPP_GE_DUMP,
UNKNOWN,
};
const char *IdentifiedFileTypeToString(IdentifiedFileType type);
// NB: It is a REQUIREMENT that implementations of this class are entirely thread safe!
// TODO: actually, is it really?
class FileLoader {
public:
enum class Flags {
NONE,
// Not necessary to read from / store into cache.
HINT_UNCACHED,
};
virtual ~FileLoader() {}
virtual bool IsRemote() {
return false;
}
virtual bool Exists() = 0; // This is permitted to essentially be "IsReadable" and not actually report existence (let's say the permissions are wrong, for example...)
virtual bool ExistsFast() {
return Exists();
}
virtual bool IsDirectory() = 0;
virtual s64 FileSize() = 0;
virtual Path GetPath() const = 0;
virtual std::string GetFileExtension() const {
return GetPath().GetFileExtension();
}
virtual size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) = 0;
virtual size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) {
return ReadAt(absolutePos, 1, bytes, data, flags);
}
// Cancel any operations that might block, if possible.
virtual void Cancel() {}
virtual std::string LatestError() const {
return "";
}
};
class ProxiedFileLoader : public FileLoader {
public:
ProxiedFileLoader(FileLoader *backend) : backend_(backend) {}
~ProxiedFileLoader() {
// Takes ownership.
delete backend_;
}
bool IsRemote() override {
return backend_->IsRemote();
}
bool Exists() override {
return backend_->Exists();
}
bool ExistsFast() override {
return backend_->ExistsFast();
}
bool IsDirectory() override {
return backend_->IsDirectory();
}
s64 FileSize() override {
return backend_->FileSize();
}
Path GetPath() const override {
return backend_->GetPath();
}
void Cancel() override {
backend_->Cancel();
}
std::string LatestError() const override {
return backend_->LatestError();
}
size_t ReadAt(s64 absolutePos, size_t bytes, size_t count, void *data, Flags flags = Flags::NONE) override {
return backend_->ReadAt(absolutePos, bytes, count, data, flags);
}
size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override {
return backend_->ReadAt(absolutePos, bytes, data, flags);
}
FileLoader *Steal() {
FileLoader *backend = backend_;
backend_ = nullptr;
return backend;
}
protected:
FileLoader *backend_;
};
inline u32 operator & (const FileLoader::Flags &a, const FileLoader::Flags &b) {
return (u32)a & (u32)b;
}
FileLoader *ConstructFileLoader(const Path &filename);
// Identifies the file and resolves to the target binary, ISO, or other file (e.g. from a directory.)
FileLoader *ResolveFileLoaderTarget(FileLoader *fileLoader, IdentifiedFileType *fileType, std::string *errorString);
Path ResolvePBPDirectory(const Path &filename);
Path ResolvePBPFile(const Path &filename);
IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorString);
bool UmdReplace(const Path &filepath, FileLoader **fileLoader, std::string &error);
// Mounts a disc image at umd0:, umd1:, umd: and disc0:, whether or not one was there.
bool UmdMount(const Path &filepath, FileLoader **fileLoader, std::string &error);
class IFileSystem;
// What a disc holds, as PSP_UMD_TYPE_* bits (game, video, audio), or 0 if it's none of those.
// root is prepended to the disc's paths, so "disc0:" reads the mounted disc through pspFileSystem.
u32 DetectUmdDiscType(IFileSystem *fs, std::string_view root);
// The same for a disc image file. 0 if it can't be opened as one.
u32 DetectUmdDiscType(FileLoader *fileLoader);
// Whether the image's PSP_GAME has an actual executable. The EUR Video Demo Disc lists a game
// (UMD_DATA.BIN says GVA) whose EBOOT.BIN and BOOT.BIN are all zeroes.
bool UmdHasBootableGame(FileLoader *fileLoader);
enum class ArchiveType {
ZIP,
SevenZ,
};
enum class ZipFileContents {
NOT_A_ZIP_FILE = 0,
UNKNOWN,
PSP_GAME_DIR,
ISO_FILE,
TEXTURE_PACK,
SAVE_DATA,
FRAME_DUMP,
EXTRACTED_GAME,
SAVE_STATES,
PRX_PLUGIN,
// A PS1 disc image, which can be one file or several (a cue sheet and what it names).
PSX_DISC,
};
// TODO: Rename to ArchiveContentsInfo or something.
struct ZipFileInfo {
ArchiveType archiveType;
ZipFileContents contents = ZipFileContents::NOT_A_ZIP_FILE;
int numFiles = 0;
int stripChars = 0; // for PSP game - how much to strip from the path.
int isoFileIndex = -1; // for ISO
int textureIniIndex = -1; // for textures
bool ignoreMetaFiles = false;
bool saveDataInRoot = false; // for SAVE_DATA - the files of a single save are in the root, extract into savedataDir.
std::string gameTitle; // from PARAM.SFO if available
std::string savedataTitle;
std::string savedataDetails;
std::string savedataDir;
std::string mTime;
std::string iniContents;
std::string isoFilename; // Used by SevenZ only right now.
std::vector<std::string> discFiles; // For PSX_DISC, the disc's files in the archive.
s64 totalFileSize = 0;
std::string contentName;
};
ZipContainer ZipOpenPath(const Path &fileName);
void ZipClose(ZipContainer &z);
void DetectZipFileContents(zip_t *z, ZipFileInfo *info);
bool DetectArchiveContents(VFSInterface *vfs, ZipFileInfo *info);