diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index 549df566bc..8dd58240b6 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -451,6 +451,32 @@ u32 DetectUmdDiscType(FileLoader *fileLoader) { return DetectUmdDiscType(&umd, ""); } +bool UmdHasBootableGame(FileLoader *fileLoader) { + std::string error; + std::shared_ptr bd(ConstructBlockDevice(fileLoader, &error)); + if (!bd) { + return false; + } + SequentialHandleAllocator handles; + ISOFileSystem umd(&handles, bd); + for (const char *path : { "/PSP_GAME/SYSDIR/EBOOT.BIN", "/PSP_GAME/SYSDIR/BOOT.BIN" }) { + if (!umd.GetFileInfo(path).exists) { + continue; + } + const int handle = umd.OpenFile(path, FILEACCESS_READ); + if (handle < 0) { + continue; + } + u8 head[4]{}; + const size_t read = umd.ReadFile(handle, head, sizeof(head)); + umd.CloseFile(handle); + if (read == sizeof(head) && (memcmp(head, "~PSP", 4) == 0 || memcmp(head, "\x7F" "ELF", 4) == 0)) { + return true; + } + } + return false; +} + // Close the return value with ZipClose (if non-null, of course). ZipContainer ZipOpenPath(const Path &fileName) { ZipContainer z(fileName); diff --git a/Core/Loaders.h b/Core/Loaders.h index 3e7eabcee7..c4311629d0 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -172,6 +172,9 @@ class IFileSystem; 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, diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index e56d95ad4c..2586ec7cb0 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -126,6 +126,11 @@ static void LaunchFile(ScreenManager *screenManager, Screen *currentScreen, cons if (!fileLoader || !(DetectUmdDiscType(fileLoader.get()) & PSP_UMD_TYPE_VIDEO)) { break; } + if (!UmdHasBootableGame(fileLoader.get())) { + // The game is only a placeholder, so there's just the video. + LaunchInEmuScreen(screenManager, currentScreen, new EmuScreen(vshPath, path, false)); + return; + } auto ga = GetI18NCategory(I18NCat::GAME); const std::vector choices = { std::string(ga->T("Play game")), std::string(ga->T("Watch video")) }; UI::ListPopupScreen *popup = new UI::ListPopupScreen(ga->T("This disc has both a game and video"), choices, -1, [=](int choice) {