diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 34d6fadc9d..0adece1843 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -224,6 +224,7 @@ enum class DumpFileType { EBOOT = (1 << 0), PRX = (1 << 1), Atrac3 = (1 << 2), + PBP_ISO = (1 << 3), }; ENUM_CLASS_BITOPS(DumpFileType); diff --git a/Core/FileLoaders/LocalFileLoader.cpp b/Core/FileLoaders/LocalFileLoader.cpp index 097456ab12..6de92dabd6 100644 --- a/Core/FileLoaders/LocalFileLoader.cpp +++ b/Core/FileLoaders/LocalFileLoader.cpp @@ -78,7 +78,6 @@ LocalFileLoader::LocalFileLoader(const Path &filename) } #endif - #if defined(HAVE_LIBRETRO_VFS) file_ = File::OpenCFile(filename, "rb"); if (!file_) { diff --git a/Core/FileSystems/BlockDevices.cpp b/Core/FileSystems/BlockDevices.cpp index 43a6b50042..4512e8b1e3 100644 --- a/Core/FileSystems/BlockDevices.cpp +++ b/Core/FileSystems/BlockDevices.cpp @@ -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); } diff --git a/Core/FileSystems/FileSystem.h b/Core/FileSystems/FileSystem.h index 90cae79131..20cc3b0149 100644 --- a/Core/FileSystems/FileSystem.h +++ b/Core/FileSystems/FileSystem.h @@ -26,12 +26,15 @@ #include #include #include +#include #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 GetBlockDevice() { return std::shared_ptr(); } }; diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index dc1c640d43..89934bc6b2 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -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; hAlloc = _hAlloc; @@ -173,7 +173,6 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic } ISOFileSystem::~ISOFileSystem() { - delete blockDevice; delete treeroot; } diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index 0cc4a1f1a5..f1e68ee361 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -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); ~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 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; 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 GetBlockDevice() { return isoFileSystem_->GetBlockDevice(); } private: std::shared_ptr isoFileSystem_; diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index 5b16599f75..041070e7b9 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -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 bd(ConstructBlockDevice(fileLoader, &bdError)); + std::shared_ptr 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; + } } } diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 5f19255a06..d2ac9bdcde 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -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 + #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 bd, Path destPath, std::string title) { + NPDRMDemoBlockDevice *npdrmDemoBD = dynamic_cast(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 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 fileSystem; std::shared_ptr blockSystem; @@ -68,7 +108,7 @@ bool MountGameISO(FileLoader *fileLoader, std::string *errorString) { fileSystem = std::make_shared(&pspFileSystem, fileLoader->GetPath()); blockSystem = fileSystem; } else { - auto bd = ConstructBlockDevice(fileLoader, errorString); + std::shared_ptr 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 bd(ConstructBlockDevice(PSP_CoreParameter().mountIsoLoader, error_string)); if (bd) { auto umd2 = std::make_shared(&pspFileSystem, bd); auto blockSystem = std::make_shared(umd2); diff --git a/Core/PSPLoaders.h b/Core/PSPLoaders.h index ef200ee8ef..17e017e49f 100644 --- a/Core/PSPLoaders.h +++ b/Core/PSPLoaders.h @@ -18,8 +18,10 @@ #pragma once #include +#include 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 bd, Path destPath, std::string title); diff --git a/Core/RetroAchievements.cpp b/Core/RetroAchievements.cpp index 72e6c6866c..04ed8b0859 100644 --- a/Core/RetroAchievements.cpp +++ b/Core/RetroAchievements.cpp @@ -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) { 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(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(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; diff --git a/Core/System.cpp b/Core/System.cpp index 886cf1fa10..98818875ba 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -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 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)) { diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index 9ea4f10652..ecb0af9026 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -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 bd(ConstructBlockDevice(loader, &errorString)); if (!bd) { return ""; } diff --git a/UI/AdhocServerScreen.cpp b/UI/AdhocServerScreen.cpp index 4324ff8241..1566e22ad2 100644 --- a/UI/AdhocServerScreen.cpp +++ b/UI/AdhocServerScreen.cpp @@ -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) { diff --git a/UI/DeveloperToolsScreen.cpp b/UI/DeveloperToolsScreen.cpp index 8bba489edc..ab64fd8f7c 100644 --- a/UI/DeveloperToolsScreen.cpp +++ b/UI/DeveloperToolsScreen.cpp @@ -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) { diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 09d2f8af62..417ddbbe4f 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -813,7 +813,7 @@ handleELF: info_->MarkReadyNoLock(flags_); return; } - BlockDevice *bd = ConstructBlockDevice(info_->GetFileLoader().get(), &errorString); + std::shared_ptr 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 lock(info_->lock);