diff --git a/Core/FileSystems/BlockDevices.h b/Core/FileSystems/BlockDevices.h index a8ca18480c..e62f18622c 100644 --- a/Core/FileSystems/BlockDevices.h +++ b/Core/FileSystems/BlockDevices.h @@ -46,7 +46,7 @@ public: } return true; } - int GetBlockSize() const { return 2048;} // forced, it cannot be changed by subclasses + int GetBlockSize() const { return 2048;} // forced, it cannot be changed by subclasses. If a subclass uses bigger blocks internally, it must cache and virtualize. virtual u32 GetNumBlocks() const = 0; virtual u64 GetUncompressedSize() const { return (u64)GetNumBlocks() * (u64)GetBlockSize(); @@ -87,7 +87,6 @@ private: int ver_ = 0; }; - class FileBlockDevice : public BlockDevice { public: FileBlockDevice(FileLoader *fileLoader); diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index 231af74f87..6b60f0b387 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -28,6 +28,7 @@ #include "Core/FileLoaders/RetryingFileLoader.h" #include "Core/FileLoaders/ZipFileLoader.h" #include "Core/FileSystems/MetaFileSystem.h" +#include "Core/FileSystems/BlockDevices.h" #include "Core/PSPLoaders.h" #include "Core/MemMap.h" #include "Core/Loaders.h" @@ -37,6 +38,15 @@ #include "Core/ELF/ParamSFO.h" #include "Core/Util/GameManager.h" +struct PVD { + u8 type; + char identifier[5]; + char version; + char pad0; + char systemId[32]; // PSP GAME normally + char volumeId[32]; // In PSP games, this sometimes has the name of the game but far from always. +}; + FileLoader *ConstructFileLoader(const Path &filename) { if (filename.Type() == PathType::HTTP) { FileLoader *baseLoader = new RetryingFileLoader(new HTTPFileLoader(filename)); @@ -68,23 +78,11 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin } std::string extension = fileLoader->GetFileExtension(); - if (extension == ".iso") { - // may be a psx iso, they have 2352 byte sectors. You never know what some people try to open - if ((fileLoader->FileSize() % 2352) == 0) { - unsigned char sync[12]; - 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) { - *errorString = "ISO in Mode 2: Not a PSP game"; - return IdentifiedFileType::ISO_MODE2; - } + bool isDiscImage = false; - // maybe it also just happened to have that size, let's assume it's a PSP ISO and error out later if it's not. - } - return IdentifiedFileType::PSP_ISO; - } else if (extension == ".cso" || extension == ".chd") { - return IdentifiedFileType::PSP_ISO; + if (extension == ".iso" || extension == ".cso" || extension == ".chd") { + isDiscImage = true; } else if (extension == ".ppst") { return IdentifiedFileType::PPSSPP_SAVESTATE; } else if (extension == ".ppdmp") { @@ -96,7 +94,7 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin } // First, check if it's a directory with an EBOOT.PBP in it. - if (fileLoader->IsDirectory()) { + if (!isDiscImage && fileLoader->IsDirectory()) { Path filename = fileLoader->GetPath(); if (filename.size() > 4) { // Check for existence of EBOOT.PBP, as required for "Directory games". @@ -130,6 +128,54 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin return IdentifiedFileType::ERROR_IDENTIFYING; } + if (isDiscImage || fileLoader->FileSize() >= 0x8800) { + // All zeroes. ISO files start like this but their 16th 2048-byte sector contains metadata. + // Do the quick check for PSP ISOs here. + std::string error; + std::unique_ptr bd(ConstructBlockDevice(fileLoader, &error)); + if (bd) { + u8 block16[2048]{}; + bd->ReadBlock(16, (u8 *)block16); + PVD *pvd = (PVD *)(block16); + if (!memcmp(pvd->identifier, "CD001", 5)) { + // It's a PSP ISO file. + if (!memcmp(pvd->systemId, "PSP GAME", 8)) { + return IdentifiedFileType::PSP_ISO; + } else if (!memcmp(pvd->systemId, "PS3", 3)) { + return IdentifiedFileType::PS3_ISO; + } else if (!memcmp(pvd->systemId, "PLAYSTATION", 11)) { + *errorString = "PSX or PS2 ISO"; + // Just do a size heuristic here. + if (bd->GetUncompressedSize() > 800LL * 1024LL * 1024LL) { + return IdentifiedFileType::PS2_ISO; + } + return IdentifiedFileType::PSX_ISO; + } else { + *errorString = "ISO missing PSP GAME or PSP NPU identifier"; + return IdentifiedFileType::UNKNOWN_ISO; + } + } + + // Do extra check for PSX ISO + // may be a psx iso, they have 2352 byte sectors. You never know what some people try to open + if ((fileLoader->FileSize() % 2352) == 0) { + unsigned char sync[12]; + 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) { + *errorString = "ISO in Mode 2: Not a PSP game"; + return IdentifiedFileType::PSX_ISO; + } + } + } + + if (isDiscImage) { + *errorString = "Not a valid PSP ISO image"; + return IdentifiedFileType::UNKNOWN_ISO; + } + } + u32_le psar_offset = 0, psar_id = 0; u32 _id = id; if (!memcmp(&_id, "PK\x03\x04", 4) || !memcmp(&_id, "PK\x05\x06", 4) || !memcmp(&_id, "PK\x07\x08", 4)) { @@ -142,28 +188,6 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin return IdentifiedFileType::ARCHIVE_RAR; } else if (!memcmp(&_id, "\x37\x7A\xBC\xAF", 4)) { return IdentifiedFileType::ARCHIVE_7Z; - } else if (!memcmp(&_id, "\0\0\0\0", 4)) { - // All zeroes. ISO files start like this but their 16th 2048-byte sector contains metadata. - if (fileLoader->FileSize() > 0x8100) { - char buffer[16]; - fileLoader->ReadAt(0x8000, sizeof(buffer), buffer); - if (!memcmp(buffer + 1, "CD001", 5)) { - // It's an ISO file. - if (!memcmp(buffer + 8, "PSP GAME", 8)) { - return IdentifiedFileType::PSP_ISO; - } - return IdentifiedFileType::UNKNOWN_ISO; - } - } - } else if (!memcmp(&_id, "CISO", 4)) { - // CISO are not used for many other kinds of ISO so let's just guess it's a PSP one and let it - // fail later... - return IdentifiedFileType::PSP_ISO; - } else if (!memcmp(&_id, "MCom", 4)) { - size_t readSize = fileLoader->ReadAt(4, 4, 1, &_id); - if (!memcmp(&_id, "prHD", 4)) { - return IdentifiedFileType::PSP_ISO; // CHD file - } } if (id == 'FLE\x7F') { @@ -537,7 +561,9 @@ const char *IdentifiedFileTypeToString(IdentifiedFileType type) { case IdentifiedFileType::ARCHIVE_ZIP: return "ARCHIVE_ZIP"; case IdentifiedFileType::ARCHIVE_7Z: return "ARCHIVE_7Z"; case IdentifiedFileType::PSP_PS1_PBP: return "PSP_PS1_PBP"; - case IdentifiedFileType::ISO_MODE2: return "ISO_MODE2"; + case IdentifiedFileType::PSX_ISO: return "PSX_ISO"; + case IdentifiedFileType::PS2_ISO: return "PS2_ISO"; + case IdentifiedFileType::PS3_ISO: return "PS3_ISO"; case IdentifiedFileType::NORMAL_DIRECTORY: return "NORMAL_DIRECTORY"; case IdentifiedFileType::PSP_SAVEDATA_DIRECTORY: return "PSP_SAVEDATA_DIRECTORY"; case IdentifiedFileType::PPSSPP_SAVESTATE: return "PPSSPP_SAVESTATE"; diff --git a/Core/Loaders.h b/Core/Loaders.h index 680da1431f..1e6e87bc7f 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -36,16 +36,18 @@ enum class IdentifiedFileType { PSP_DISC_DIRECTORY, - UNKNOWN_BIN, - UNKNOWN_ELF, - UNKNOWN_ISO, - // Try to reduce support emails... ARCHIVE_RAR, ARCHIVE_ZIP, ARCHIVE_7Z, PSP_PS1_PBP, - ISO_MODE2, + PSX_ISO, + PS2_ISO, + PS3_ISO, + + UNKNOWN_BIN, + UNKNOWN_ELF, + UNKNOWN_ISO, NORMAL_DIRECTORY, diff --git a/Core/System.cpp b/Core/System.cpp index 217dc58cda..16f3e37f25 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -228,7 +228,9 @@ static void GetBootError(IdentifiedFileType type, std::string *errorString) { break; case IdentifiedFileType::ARCHIVE_7Z: *errorString = "7z file detected (Require 7-Zip)"; break; - case IdentifiedFileType::ISO_MODE2: *errorString = "PSX game image detected."; break; + case IdentifiedFileType::PSX_ISO: *errorString = "PSX game image detected."; break; + case IdentifiedFileType::PS2_ISO: *errorString = "PS2 game image detected."; break; + case IdentifiedFileType::PS3_ISO: *errorString = "PS2 game image detected."; break; case IdentifiedFileType::NORMAL_DIRECTORY: *errorString = "Just a directory."; break; case IdentifiedFileType::PPSSPP_SAVESTATE: *errorString = "This is a saved state, not a game."; break; // Actually, we could make it load it... case IdentifiedFileType::PSP_SAVEDATA_DIRECTORY: *errorString = "This is save data, not a game."; break; diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index dc9739aab5..b2d6f9d76e 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -656,6 +656,7 @@ public: case IdentifiedFileType::PSP_ELF: handleELF: + info_->title = info_->GetFilePath().GetFilename(); // An elf on its own has no usable information, no icons, no nothing. if (flags_ & GameInfoFlags::PARAM_SFO) { info_->id = g_paramSFO.GenerateFakeID(gamePath_); @@ -728,6 +729,7 @@ handleELF: case IdentifiedFileType::PPSSPP_GE_DUMP: { + info_->title = info_->GetFilePath().GetFilename(); if (flags_ & GameInfoFlags::ICON) { Path screenshotPath = gamePath_.WithReplacedExtension(".ppdmp", ".png"); // Let's use the comparison screenshot as an icon, if it exists. @@ -808,6 +810,8 @@ handleELF: // quick-update the info while we have the lock, so we don't need to wait for the image load to display the title. info_->MarkReadyNoLock(GameInfoFlags::PARAM_SFO); } + } else { + info_->title = info_->GetFilePath().GetFilename(); } } @@ -848,6 +852,7 @@ handleELF: } case IdentifiedFileType::ARCHIVE_ZIP: + info_->title = info_->GetFilePath().GetFilename(); if (flags_ & GameInfoFlags::ICON) { ReadVFSToString("zip.png", &info_->icon.data, &info_->lock); info_->icon.dataLoaded = true; @@ -855,6 +860,7 @@ handleELF: break; case IdentifiedFileType::ARCHIVE_RAR: + info_->title = info_->GetFilePath().GetFilename(); if (flags_ & GameInfoFlags::ICON) { ReadVFSToString("rargray.png", &info_->icon.data, &info_->lock); info_->icon.dataLoaded = true; @@ -862,6 +868,7 @@ handleELF: break; case IdentifiedFileType::ARCHIVE_7Z: + info_->title = info_->GetFilePath().GetFilename(); if (flags_ & GameInfoFlags::ICON) { ReadVFSToString("7z.png", &info_->icon.data, &info_->lock); info_->icon.dataLoaded = true; @@ -870,6 +877,7 @@ handleELF: case IdentifiedFileType::NORMAL_DIRECTORY: default: + info_->title = info_->GetFilePath().GetFilename(); break; } diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 71848f59f8..787e24e9b9 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -119,6 +119,37 @@ static bool FileTypeSupportsCRC(IdentifiedFileType fileType) { } } +static bool FileTypeHasIcon(IdentifiedFileType fileType) { + switch (fileType) { + case IdentifiedFileType::PSP_PBP: + case IdentifiedFileType::PSP_PBP_DIRECTORY: + case IdentifiedFileType::PSP_ISO_NP: + case IdentifiedFileType::PSP_ISO: + return true; + default: + return false; + } +} + +// Reverse logic. +static bool FileTypeIsPlayable(IdentifiedFileType fileType) { + switch (fileType) { + case IdentifiedFileType::ERROR_IDENTIFYING: + case IdentifiedFileType::UNKNOWN: + case IdentifiedFileType::PSX_ISO: + case IdentifiedFileType::PS2_ISO: + case IdentifiedFileType::PS3_ISO: + case IdentifiedFileType::UNKNOWN_BIN: + case IdentifiedFileType::UNKNOWN_ELF: + case IdentifiedFileType::UNKNOWN_ISO: + case IdentifiedFileType::NORMAL_DIRECTORY: + case IdentifiedFileType::PSP_SAVEDATA_DIRECTORY: + return false; + default: + return true; + } +} + void GameScreen::CreateContentViews(UI::ViewGroup *parent) { if (!info_) { // Shouldn't happen @@ -146,18 +177,23 @@ void GameScreen::CreateContentViews(UI::ViewGroup *parent) { parent->Add(leftScroll); const bool fileTypeSupportCRC = FileTypeSupportsCRC(info_->fileType); + const bool fileTypeHasIcon = FileTypeHasIcon(info_->fileType); // Need an explicit size here because homebrew uses screenshots as icons. LinearLayout *mainGameInfo; if (portrait) { mainGameInfo = new LinearLayout(ORIENT_VERTICAL); leftColumn->Add(new Spacer(8.0f)); - leftColumn->Add(new GameImageView(gamePath_, GameInfoFlags::ICON, 2.0f, new LinearLayoutParams(UI::Margins(0)))); + if (fileTypeHasIcon) { + leftColumn->Add(new GameImageView(gamePath_, GameInfoFlags::ICON, 2.0f, new LinearLayoutParams(UI::Margins(0)))); + } leftColumn->Add(mainGameInfo); } else { mainGameInfo = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)); ViewGroup *badgeHolder = new LinearLayout(ORIENT_HORIZONTAL); - badgeHolder->Add(new GameImageView(gamePath_, GameInfoFlags::ICON, 2.0f, new LinearLayoutParams(144 * 2, 80 * 2, UI::Margins(0)))); + if (fileTypeHasIcon) { + badgeHolder->Add(new GameImageView(gamePath_, GameInfoFlags::ICON, 2.0f, new LinearLayoutParams(144 * 2, 80 * 2, UI::Margins(0)))); + } badgeHolder->Add(mainGameInfo); leftColumn->Add(badgeHolder); } @@ -175,7 +211,9 @@ void GameScreen::CreateContentViews(UI::ViewGroup *parent) { tvTitle->SetShadow(true); std::string regionID = ReplaceAll(info_->id_version, "_", " v"); - regionID += ": "; + if (!regionID.empty()) { + regionID += ": "; + } if (info_->region != GameRegion::UNKNOWN) { regionID += GameRegionToString(info_->region); @@ -336,7 +374,7 @@ void GameScreen::CreateSettingsViews(UI::ViewGroup *rightColumn) { rightColumnItems->SetSpacing(0.0f); rightColumn->Add(rightColumnItems); - if (!inGame_) { + if (!inGame_ && FileTypeIsPlayable(info_->fileType)) { rightColumnItems->Add(new Choice(ga->T("Play"), ImageID("I_PLAY")))->OnClick.Handle(this, &GameScreen::OnPlay); } diff --git a/ext/aemu_postoffice b/ext/aemu_postoffice index 75893c3d08..098039af82 160000 --- a/ext/aemu_postoffice +++ b/ext/aemu_postoffice @@ -1 +1 @@ -Subproject commit 75893c3d08bd9f783bc19262ae3b45a5d058f3c8 +Subproject commit 098039af82636fa8c5ce10f227aa340e23c77251