Merge pull request #21463 from hrydgard/misc

Add a new file dumping setting to dump the raw ISO file from NPDRM-packed demos
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-03-19 22:18:01 +01:00
commit 910b60defc
15 files changed
+101 -37

No files matched your search

+1
View File
@@ -224,6 +224,7 @@ enum class DumpFileType {
EBOOT = (1 << 0),
PRX = (1 << 1),
Atrac3 = (1 << 2),
PBP_ISO = (1 << 3),
};
ENUM_CLASS_BITOPS(DumpFileType);
-1
View File
@@ -78,7 +78,6 @@ LocalFileLoader::LocalFileLoader(const Path &filename)
}
#endif
#if defined(HAVE_LIBRETRO_VFS)
file_ = File::OpenCFile(filename, "rb");
if (!file_) {
+2 -1
View File
@@ -66,8 +66,9 @@ BlockDevice *ConstructBlockDevice(FileLoader *fileLoader, std::string *errorStri
} else if (!memcmp(buffer, "\x00PBP", 4)) {
uint32_t psarOffset = 0;
size = fileLoader->ReadAt(0x24, 1, 4, &psarOffset);
if (size == 4 && psarOffset < fileLoader->FileSize())
if (size == 4 && psarOffset < fileLoader->FileSize()) {
device = new NPDRMDemoBlockDevice(fileLoader);
}
} else if (!memcmp(buffer, "MComprHD", 8)) {
device = new CHDFileBlockDevice(fileLoader);
}
+4
View File
@@ -26,12 +26,15 @@
#include <cstring>
#include <cstdint>
#include <ctime>
#include <memory>
#include "Common.h"
#include "Common/File/Path.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/ErrorCodes.h"
class BlockDevice;
enum FileAccess {
FILEACCESS_NONE = 0,
FILEACCESS_READ = 1,
@@ -155,6 +158,7 @@ public:
virtual u64 FreeDiskSpace(const std::string &path) = 0;
virtual bool ComputeRecursiveDirSizeIfFast(const std::string &path, int64_t *size) = 0;
virtual void Describe(char *buf, size_t size) const = 0;
virtual std::shared_ptr<BlockDevice> GetBlockDevice() { return std::shared_ptr<BlockDevice>(); }
};
+1 -2
View File
@@ -140,7 +140,7 @@ struct VolDescriptor {
#pragma pack(pop)
ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice) {
ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, std::shared_ptr<BlockDevice> _blockDevice) {
blockDevice = _blockDevice;
hAlloc = _hAlloc;
@@ -173,7 +173,6 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic
}
ISOFileSystem::~ISOFileSystem() {
delete blockDevice;
delete treeroot;
}
+4 -2
View File
@@ -28,7 +28,7 @@ bool parseLBN(const std::string &filename, u32 *sectorStart, u32 *readSize);
class ISOFileSystem : public IFileSystem {
public:
ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice);
ISOFileSystem(IHandleAllocator *_hAlloc, std::shared_ptr<BlockDevice> _blockDevice);
~ISOFileSystem();
void DoState(PointerWrap &p) override;
@@ -57,6 +57,7 @@ public:
bool ComputeRecursiveDirSizeIfFast(const std::string &path, int64_t *size) override { return false; }
void Describe(char *buf, size_t size) const override { snprintf(buf, size, "ISO"); } // TODO: Ask the fileLoader about the origins
std::shared_ptr<BlockDevice> GetBlockDevice() override { return blockDevice; }
private:
struct TreeEntry {
~TreeEntry();
@@ -92,7 +93,7 @@ private:
EntryMap entries;
IHandleAllocator *hAlloc;
TreeEntry *treeroot;
BlockDevice *blockDevice;
std::shared_ptr<BlockDevice> blockDevice;
mutable u32 lastReadBlock_;
TreeEntry entireISO;
@@ -166,6 +167,7 @@ public:
bool ComputeRecursiveDirSizeIfFast(const std::string &path, int64_t *size) override { return false; }
void Describe(char *buf, size_t size) const override { snprintf(buf, size, "ISOBlock"); }
std::shared_ptr<BlockDevice> GetBlockDevice() { return isoFileSystem_->GetBlockDevice(); }
private:
std::shared_ptr<IFileSystem> isoFileSystem_;
+21 -21
View File
@@ -82,22 +82,8 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin
std::string extension = fileLoader->GetFileExtension();
bool isDiscImage = false;
if (extension == ".iso" || extension == ".cso" || extension == ".chd") {
isDiscImage = true;
} else if (extension == ".ppst") {
return IdentifiedFileType::PPSSPP_SAVESTATE;
} else if (extension == ".ppdmp") {
char data[8]{};
fileLoader->ReadAt(0, 8, data);
if (memcmp(data, "PPSSPPGE", 8) == 0) {
return IdentifiedFileType::PPSSPP_GE_DUMP;
}
}
// First, check if it's a directory with an EBOOT.PBP in it.
if (!isDiscImage && fileLoader->IsDirectory()) {
if (fileLoader->IsDirectory()) {
Path filename = fileLoader->GetPath();
if (filename.size() > 4) {
// Check for existence of EBOOT.PBP, as required for "Directory games".
@@ -119,13 +105,26 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin
return IdentifiedFileType::NORMAL_DIRECTORY;
}
bool isDiscImage = false;
if (extension == ".iso" || extension == ".cso" || extension == ".chd") {
isDiscImage = true;
} else if (extension == ".ppst") {
return IdentifiedFileType::PPSSPP_SAVESTATE;
} else if (extension == ".ppdmp") {
char data[8]{};
fileLoader->ReadAt(0, 8, data);
if (memcmp(data, "PPSSPPGE", 8) == 0) {
return IdentifiedFileType::PPSSPP_GE_DUMP;
}
}
// OK, quick methods of identification for common types failed. Moving on to more expensive methods,
// starting by reading the first few bytes.
// This can be necessary for weird Android content storage path types, see issue #17462
if (isDiscImage || fileLoader->FileSize() >= 0x8800) {
// Do the quick check for PSP ISOs here.
std::string bdError;
std::unique_ptr<BlockDevice> bd(ConstructBlockDevice(fileLoader, &bdError));
std::shared_ptr<BlockDevice> bd(ConstructBlockDevice(fileLoader, &bdError));
if (bd) {
u8 block16[2048]{};
bd->ReadBlock(16, (u8 *)block16);
@@ -135,12 +134,12 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin
// It's a valid DVD-style ISO file. Let's see which type.
if (!memcmp(pvd.systemId, "PSP GAME", 8) || !memcmp(pvd.systemId, "\"PSP GAME\"", 10)) {
// Yes, a known proper PSP game, let's get it going.
return IdentifiedFileType::PSP_ISO;
return bd->IsDisc() ? IdentifiedFileType::PSP_ISO : IdentifiedFileType::PSP_ISO_NP;
} else if (!memcmp(pvd.systemId, "UMD VIDEO", 9) || !memcmp(pvd.systemId, "UMD AUDIO", 9)) {
// This is rare so being slightly slow here shouldn't be a problem. Let's go check for the presence of
// actual game data.
SequentialHandleAllocator hAlloc;
ISOFileSystem umd(&hAlloc, bd.release());
ISOFileSystem umd(&hAlloc, bd);
if (umd.GetFileInfo("/PSP_GAME").exists) {
INFO_LOG(Log::Loader, "Found an UMD VIDEO disc with game data. Treating as game.");
*errorString = "UMD Video with PSP GAME data";
@@ -163,10 +162,10 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin
} else {
// Let's go check for PSP game data.
SequentialHandleAllocator hAlloc;
ISOFileSystem umd(&hAlloc, bd.release());
ISOFileSystem umd(&hAlloc, bd);
if (umd.GetFileInfo("/PSP_GAME").exists) {
INFO_LOG(Log::Loader, "PSP ISO with unknown system ID: %.32s: %s", pvd.systemId, fileLoader->GetPath().c_str());
return IdentifiedFileType::PSP_ISO;
return bd->IsDisc() ? IdentifiedFileType::PSP_ISO : IdentifiedFileType::PSP_ISO_NP;
}
INFO_LOG(Log::Loader, "Unknown ISO with unknown system ID: %.32s: %s", pvd.systemId, fileLoader->GetPath().c_str());
@@ -240,8 +239,9 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin
paramSFO.ReadSFO(sfoData);
// PS1 Eboots are supposed to use "ME" as their PARAM SFO category.
// If they don't, and they're still malformed (e.g. PSISOIMG0000 isn't found), there's nothing we can do.
if (paramSFO.GetValueString("CATEGORY") == "ME")
if (paramSFO.GetValueString("CATEGORY") == "ME") {
return IdentifiedFileType::PSP_PS1_PBP;
}
}
}
+43 -3
View File
@@ -15,6 +15,8 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <thread>
#include "Core/Core.h"
#include "Common/System/Request.h"
@@ -24,6 +26,7 @@
#ifdef _WIN32
#include "Common/CommonWindows.h"
#endif
#include "Common/System/OSD.h"
#include "Core/ELF/ParamSFO.h"
#include "Core/ELF/PBPReader.h"
@@ -38,7 +41,7 @@
#include "Core/Loaders.h"
#include "Core/MemMap.h"
#include "Core/HDRemaster.h"
#include "Core/Util/PathUtil.h"
#include "Core/Config.h"
#include "Core/ConfigValues.h"
@@ -60,6 +63,43 @@ static void UseLargeMem(int memsize) {
}
}
void DumpBlockDeviceAsync(std::shared_ptr<BlockDevice> bd, Path destPath, std::string title) {
NPDRMDemoBlockDevice *npdrmDemoBD = dynamic_cast<NPDRMDemoBlockDevice *>(bd.get());
if (npdrmDemoBD) {
INFO_LOG(Log::System, "Dumping NPDRM demo ISO... (%s)", destPath.c_str());
std::thread dumpThread([bd, title, destPath]() {
File::CreateFullPath(destPath.NavigateUp());
if (File::Exists(destPath)) {
INFO_LOG(Log::System, "Dump file already exists, skipping: %s", destPath.c_str());
return;
}
FILE *dest = File::OpenCFile(destPath, "wb");
if (!dest) {
ERROR_LOG(Log::System, "Failed to open destination for NPDRM demo ISO dump: %s", destPath.c_str());
return;
}
g_OSD.SetProgressBar("npdrm_dump", title, 0.0f, 1.0f, 0.0f, 0.0f);
int blocks = bd->GetNumBlocks();
std::vector<u8> blockBuf(bd->GetBlockSize());
for (int i = 0; i < blocks; i++) {
bd->ReadBlock(i, blockBuf.data());
fwrite(blockBuf.data(), 1, blockBuf.size(), dest);
g_OSD.SetProgressBar("npdrm_dump", title, 0.0f, 1.0f, (float)(i + 1) / blocks, 0.0f);
}
fclose(dest);
g_OSD.RemoveProgressBar("npdrm_dump", true, 1.0f);
g_OSD.Show(OSDType::MESSAGE_SUCCESS, title, GetFriendlyPath(destPath), 5.0f, "npdrm_finished");
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
g_OSD.SetClickCallback("npdrm_finished", [destPath]() {
System_ShowFileInFolder(destPath);
});
}
INFO_LOG(Log::System, "Finished dumping NPDRM demo ISO... (%s)", title.c_str());
});
dumpThread.detach();
}
}
bool MountGameISO(FileLoader *fileLoader, std::string *errorString) {
std::shared_ptr<IFileSystem> fileSystem;
std::shared_ptr<IFileSystem> blockSystem;
@@ -68,7 +108,7 @@ bool MountGameISO(FileLoader *fileLoader, std::string *errorString) {
fileSystem = std::make_shared<VirtualDiscFileSystem>(&pspFileSystem, fileLoader->GetPath());
blockSystem = fileSystem;
} else {
auto bd = ConstructBlockDevice(fileLoader, errorString);
std::shared_ptr<BlockDevice> bd(ConstructBlockDevice(fileLoader, errorString));
if (!bd) {
// Can only fail if the ISO is bad.
return false;
@@ -302,7 +342,7 @@ static Path NormalizePath(const Path &path) {
bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string_view discId, std::string *error_string) {
// This is really just for headless, might need tweaking later.
if (PSP_CoreParameter().mountIsoLoader != nullptr) {
auto bd = ConstructBlockDevice(PSP_CoreParameter().mountIsoLoader, error_string);
std::shared_ptr<BlockDevice> bd(ConstructBlockDevice(PSP_CoreParameter().mountIsoLoader, error_string));
if (bd) {
auto umd2 = std::make_shared<ISOFileSystem>(&pspFileSystem, bd);
auto blockSystem = std::make_shared<ISOBlockSystem>(umd2);
+3
View File
@@ -18,8 +18,10 @@
#pragma once
#include <string>
#include <memory>
class FileLoader;
class BlockDevice;
bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string);
bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string_view discId, std::string *error_string);
@@ -29,3 +31,4 @@ bool MountGameISO(FileLoader *fileLoader, std::string *errorString);
bool LoadParamSFOFromDisc();
bool LoadParamSFOFromPBP(FileLoader *fileLoader);
void InitMemorySizeForGame();
void DumpBlockDeviceAsync(std::shared_ptr<BlockDevice> bd, Path destPath, std::string title);
+3 -3
View File
@@ -96,7 +96,7 @@ static std::string FormatRCheevosMD5(uint8_t digest[16]) {
// Consumes the blockDevice.
// If failed, returns an empty string, otherwise a 32-character string with the hash in hex format.
static std::string ComputePSPISOHash(BlockDevice *blockDevice) {
static std::string ComputePSPISOHash(std::shared_ptr<BlockDevice> blockDevice) {
md5_context md5;
ppsspp_md5_starts(&md5);
@@ -1068,7 +1068,7 @@ void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoad
// TODO: Fish the block device out of the loading process somewhere else. Though, probably easier to just do it here,
// we need a temporary blockdevice anyway since it gets consumed by ComputePSPISOHash.
std::string errorString;
BlockDevice *blockDevice(ConstructBlockDevice(fileLoader, &errorString));
std::shared_ptr<BlockDevice> blockDevice(ConstructBlockDevice(fileLoader, &errorString));
if (!blockDevice) {
ERROR_LOG(Log::Achievements, "Failed to construct block device for '%s' - can't identify: %s", path.c_str(), errorString.c_str());
g_isIdentifying = false;
@@ -1132,7 +1132,7 @@ void ChangeUMD(const Path &path, FileLoader *fileLoader) {
}
std::string errorString;
BlockDevice *blockDevice = ConstructBlockDevice(fileLoader, &errorString);
std::shared_ptr<BlockDevice> blockDevice(ConstructBlockDevice(fileLoader, &errorString));
if (!blockDevice) {
ERROR_LOG(Log::Achievements, "Failed to construct block device for '%s' - can't identify: %s", path.c_str(), errorString.c_str());
return;
+15 -1
View File
@@ -66,6 +66,7 @@
#include "Core/FileSystems/MetaFileSystem.h"
#include "Core/Loaders.h"
#include "Core/PSPLoaders.h"
#include "Core/FileSystems/ISOFileSystem.h"
#include "Core/ELF/ParamSFO.h"
#include "Core/SaveState.h"
#include "Core/Util/RecentFiles.h"
@@ -302,7 +303,20 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
if (LoadParamSFOFromDisc()) {
InitMemorySizeForGame();
}
if (type == IdentifiedFileType::PSP_DISC_DIRECTORY) {
if (type == IdentifiedFileType::PSP_ISO_NP && ((DumpFileType)g_Config.iDumpFileTypes & DumpFileType::PBP_ISO)) {
// We have to fetch the block device again, because we want to do this after loading PARAM.SFO.
std::string title = SanitizeString(g_paramSFO.GetValueString("TITLE"), StringRestriction::AlphaNumDashUnderscore, 0, 100);
std::string filename = title + "-" + g_paramSFO.GetDiscID() + ".iso";
Path path = GetSysDirectory(DIRECTORY_DUMP) / filename;
IFileSystem *fs = pspFileSystem.GetSystem("umd0:");
if (fs) {
std::shared_ptr<BlockDevice> bd = fs->GetBlockDevice();
if (bd) {
DumpBlockDeviceAsync(bd, path, title);
}
}
} else if (type == IdentifiedFileType::PSP_DISC_DIRECTORY) {
// Check for existence of ppsspp-index.lst - if it exists, the user likely knows what they're doing.
// TODO: Better would be to check that it was loaded successfully.
if (!File::Exists(g_CoreParameter.fileToStart / INDEX_FILENAME)) {
+1 -1
View File
@@ -448,7 +448,7 @@ std::string GameManager::GetPBPGameID(FileLoader *loader) const {
std::string GameManager::GetISOGameID(FileLoader *loader) const {
SequentialHandleAllocator handles;
std::string errorString;
BlockDevice *bd = ConstructBlockDevice(loader, &errorString);
std::shared_ptr<BlockDevice> bd(ConstructBlockDevice(loader, &errorString));
if (!bd) {
return "";
}
+1 -1
View File
@@ -56,7 +56,7 @@ public:
private:
std::string editValue_;
std::string *outEditValue_;
bool hasRelay_ = true;
bool hasRelay_ = false;
};
static UI::View *CreateLinkButton(std::string url) {
+1
View File
@@ -268,6 +268,7 @@ void DeveloperToolsScreen::CreateDumpFileTab(UI::LinearLayout *list) {
list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::EBOOT, dev->T("Dump Decrypted Eboot", "Dump Decrypted EBOOT.BIN (If Encrypted) When Booting Game")));
list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::PRX, dev->T("PRX")));
list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::Atrac3, dev->T("Atrac3/3+")));
list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::PBP_ISO, dev->T("ISO from PBP")));
}
void DeveloperToolsScreen::CreateHLETab(UI::LinearLayout *list) {
+1 -1
View File
@@ -813,7 +813,7 @@ handleELF:
info_->MarkReadyNoLock(flags_);
return;
}
BlockDevice *bd = ConstructBlockDevice(info_->GetFileLoader().get(), &errorString);
std::shared_ptr<BlockDevice> bd(ConstructBlockDevice(info_->GetFileLoader().get(), &errorString));
if (!bd) {
ERROR_LOG(Log::Loader, "Failed constructing block device for ISO %s: %s", info_->GetFilePath().ToVisualString().c_str(), errorString.c_str());
std::unique_lock<std::mutex> lock(info_->lock);