mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-11 15:13:37 +02:00
Merge pull request #21169 from hrydgard/more-umd-video-detection
Make UMD_VIDEO discs with game data detect as games.
This commit is contained in:
@@ -399,9 +399,11 @@ GameRegion DetectGameRegionFromID(std::string_view id_full) {
|
||||
return GameRegion::TEST;
|
||||
} else if (id_letters == "UMDT") {
|
||||
return GameRegion::DIAGNOSTIC;
|
||||
} else if (id_letters == "STEA") {
|
||||
// The bizarre Stealth + Wipeout Pure combo.
|
||||
return GameRegion::INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
return GameRegion::HOMEBREW;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "Common/System/OSD.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Common/Data/Text/Parsers.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/File/DirListing.h"
|
||||
#include "Common/StringUtils.h"
|
||||
@@ -223,7 +224,7 @@ CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader)
|
||||
u64 lastIndexPos = index[indexSize - 1] & 0x7FFFFFFF;
|
||||
u64 expectedFileSize = lastIndexPos << indexShift;
|
||||
if (expectedFileSize > fileSize) {
|
||||
errorString_ = StringFromFormat("Expected CSO to at least be %lld bytes, but file is %lld bytes", expectedFileSize, fileSize);
|
||||
errorString_ = StringFromFormat("CSO file incomplete: expected %s, but is %s", NiceSizeFormat(expectedFileSize).c_str(), NiceSizeFormat(fileSize).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+65
-39
@@ -120,43 +120,55 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin
|
||||
// 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
|
||||
|
||||
u32_le id;
|
||||
|
||||
size_t readSize = fileLoader->ReadAt(0, 4, 1, &id);
|
||||
if (readSize != 1) {
|
||||
*errorString = "Failed to read identification bytes";
|
||||
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<BlockDevice> bd(ConstructBlockDevice(fileLoader, &error));
|
||||
std::string bdError;
|
||||
std::unique_ptr<BlockDevice> bd(ConstructBlockDevice(fileLoader, &bdError));
|
||||
if (bd) {
|
||||
u8 block16[2048]{};
|
||||
bd->ReadBlock(16, (u8 *)block16);
|
||||
PVD *pvd = (PVD *)(block16);
|
||||
if (!memcmp(pvd->identifier, "CD001", 5)) {
|
||||
PVD pvd;
|
||||
memcpy(&pvd, block16, sizeof(PVD));
|
||||
if (!memcmp(pvd.identifier, "CD001", 5)) {
|
||||
// It's a valid DVD-style ISO file. Let's see which type.
|
||||
if (!memcmp(pvd->systemId, "PSP GAME", 8)) {
|
||||
// Yes, a proper PSP game, let's get it going.
|
||||
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;
|
||||
} else if (!memcmp(pvd->systemId, "UMD VIDEO", 9) || !memcmp(pvd->systemId, "UMD AUDIO", 9)) {
|
||||
} 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());
|
||||
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";
|
||||
return IdentifiedFileType::PSP_ISO;
|
||||
}
|
||||
|
||||
// UMD AUDIO exists technically, but in reality, not really? Let's map it to VIDEO since we support neither.
|
||||
return IdentifiedFileType::PSP_UMD_VIDEO_ISO;
|
||||
} else if (!memcmp(pvd->systemId, "PS3", 3)) {
|
||||
} else if (!memcmp(pvd.systemId, "PS3", 3)) {
|
||||
*errorString = "PS3 ISO";
|
||||
return IdentifiedFileType::PS3_ISO;
|
||||
} else if (!memcmp(pvd->systemId, "PLAYSTATION", 11)) {
|
||||
*errorString = "PSX or PS2 ISO";
|
||||
} else if (!memcmp(pvd.systemId, "PLAYSTATION", 11)) {
|
||||
// Just do a size heuristic here to differentiate. There are better ways but slower.
|
||||
if (bd->GetUncompressedSize() > 800LL * 1024LL * 1024LL) {
|
||||
*errorString = "PS2 ISO";
|
||||
return IdentifiedFileType::PS2_ISO;
|
||||
}
|
||||
*errorString = "PSX ISO?";
|
||||
return IdentifiedFileType::PSX_ISO;
|
||||
} else {
|
||||
*errorString = "ISO missing PSP GAME or PSP NPU identifier";
|
||||
// Let's go check for PSP game data.
|
||||
SequentialHandleAllocator hAlloc;
|
||||
ISOFileSystem umd(&hAlloc, bd.release());
|
||||
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;
|
||||
}
|
||||
|
||||
INFO_LOG(Log::Loader, "Unknown ISO with unknown system ID: %.32s: %s", pvd.systemId, fileLoader->GetPath().c_str());
|
||||
*errorString = StringFromFormat("ISO with unknown system ID: %.32s", pvd.systemId);
|
||||
return IdentifiedFileType::UNKNOWN_ISO;
|
||||
}
|
||||
}
|
||||
@@ -169,29 +181,41 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin
|
||||
|
||||
// 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";
|
||||
*errorString = "ISO is a CD - likely PSX"; // Mode 2 CDs are used for PSX games
|
||||
return IdentifiedFileType::PSX_ISO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isDiscImage) {
|
||||
*errorString = "Not a valid PSP ISO image";
|
||||
if (!bdError.empty()) {
|
||||
*errorString = bdError;
|
||||
} else {
|
||||
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
||||
*errorString = sy->T("Not a PSP game");
|
||||
}
|
||||
return IdentifiedFileType::UNKNOWN_ISO;
|
||||
}
|
||||
}
|
||||
|
||||
u32 id;
|
||||
|
||||
size_t readSize = fileLoader->ReadAt(0, 4, 1, &id);
|
||||
if (readSize != 1) {
|
||||
*errorString = "Failed to read identification bytes";
|
||||
return IdentifiedFileType::ERROR_IDENTIFYING;
|
||||
}
|
||||
|
||||
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)) {
|
||||
if (!memcmp(&id, "PK\x03\x04", 4) || !memcmp(&id, "PK\x05\x06", 4) || !memcmp(&id, "PK\x07\x08", 4)) {
|
||||
return IdentifiedFileType::ARCHIVE_ZIP;
|
||||
} else if (!memcmp(&_id, "\x00PBP", 4)) {
|
||||
} else if (!memcmp(&id, "\x00PBP", 4)) {
|
||||
fileLoader->ReadAt(0x24, 4, 1, &psar_offset);
|
||||
fileLoader->ReadAt(psar_offset, 4, 1, &psar_id);
|
||||
// Fall through to the below if chain.
|
||||
} else if (!memcmp(&_id, "Rar!", 4)) {
|
||||
} else if (!memcmp(&id, "Rar!", 4)) {
|
||||
return IdentifiedFileType::ARCHIVE_RAR;
|
||||
} else if (!memcmp(&_id, "\x37\x7A\xBC\xAF", 4)) {
|
||||
} else if (!memcmp(&id, "\x37\x7A\xBC\xAF", 4)) {
|
||||
return IdentifiedFileType::ARCHIVE_7Z;
|
||||
}
|
||||
|
||||
@@ -253,17 +277,18 @@ IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorStrin
|
||||
return IdentifiedFileType::UNKNOWN;
|
||||
}
|
||||
|
||||
FileLoader *ResolveFileLoaderTarget(FileLoader *fileLoader) {
|
||||
std::string errorString;
|
||||
IdentifiedFileType type = Identify_File(fileLoader, &errorString);
|
||||
if (type == IdentifiedFileType::PSP_PBP_DIRECTORY) {
|
||||
FileLoader *ResolveFileLoaderTarget(FileLoader *fileLoader, IdentifiedFileType *fileType, std::string *errorString) {
|
||||
*fileType = Identify_File(fileLoader, errorString);
|
||||
if (*fileType == IdentifiedFileType::PSP_PBP_DIRECTORY) {
|
||||
const Path ebootFilename = ResolvePBPFile(fileLoader->GetPath());
|
||||
if (ebootFilename != fileLoader->GetPath()) {
|
||||
// Switch fileLoader to the actual EBOOT.
|
||||
delete fileLoader;
|
||||
fileLoader = ConstructFileLoader(ebootFilename);
|
||||
// Re-identify the file.
|
||||
*fileType = Identify_File(fileLoader, errorString);
|
||||
}
|
||||
} else if (type == IdentifiedFileType::ARCHIVE_ZIP) {
|
||||
} else if (*fileType == IdentifiedFileType::ARCHIVE_ZIP) {
|
||||
// Handle zip files, take automatic action depending on contents.
|
||||
// Can also return nullptr.
|
||||
ZipFileLoader *zipLoader = new ZipFileLoader(fileLoader);
|
||||
@@ -276,6 +301,8 @@ FileLoader *ResolveFileLoaderTarget(FileLoader *fileLoader) {
|
||||
case ZipFileContents::FRAME_DUMP:
|
||||
{
|
||||
zipLoader->Initialize(zipFileInfo.isoFileIndex);
|
||||
// Re-identify the file.
|
||||
*fileType = Identify_File(zipLoader, errorString);
|
||||
return zipLoader;
|
||||
}
|
||||
default:
|
||||
@@ -323,14 +350,13 @@ bool UmdReplace(const Path &filepath, FileLoader **fileLoader, std::string &erro
|
||||
}
|
||||
UpdateLoadedFile(loadedFile);
|
||||
|
||||
loadedFile = ResolveFileLoaderTarget(loadedFile);
|
||||
std::string errorString;
|
||||
IdentifiedFileType fileType;
|
||||
loadedFile = ResolveFileLoaderTarget(loadedFile, &fileType, &errorString);
|
||||
|
||||
*fileLoader = loadedFile;
|
||||
|
||||
std::string errorString;
|
||||
IdentifiedFileType type = Identify_File(loadedFile, &errorString);
|
||||
|
||||
switch (type) {
|
||||
switch (fileType) {
|
||||
case IdentifiedFileType::PSP_ISO:
|
||||
case IdentifiedFileType::PSP_ISO_NP:
|
||||
case IdentifiedFileType::PSP_DISC_DIRECTORY:
|
||||
@@ -340,7 +366,7 @@ bool UmdReplace(const Path &filepath, FileLoader **fileLoader, std::string &erro
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error = "Unsupported file type: " + std::to_string((int)type) + " " + errorString;
|
||||
error = "Unsupported file type: " + std::string(IdentifiedFileTypeToString(fileType)) + " " + errorString;
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
+2
-2
@@ -152,8 +152,8 @@ inline u32 operator & (const FileLoader::Flags &a, const FileLoader::Flags &b) {
|
||||
}
|
||||
|
||||
FileLoader *ConstructFileLoader(const Path &filename);
|
||||
// Resolve to the target binary, ISO, or other file (e.g. from a directory.)
|
||||
FileLoader *ResolveFileLoaderTarget(FileLoader *fileLoader);
|
||||
// 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);
|
||||
|
||||
+14
-4
@@ -134,19 +134,29 @@ namespace Reporting
|
||||
|
||||
AndroidJNIThreadContext jniContext;
|
||||
|
||||
FileLoader *fileLoader = ResolveFileLoaderTarget(ConstructFileLoader(crcFilename));
|
||||
IdentifiedFileType type;
|
||||
|
||||
std::string errorString;
|
||||
BlockDevice *blockDevice = ConstructBlockDevice(fileLoader, &errorString);
|
||||
FileLoader *fileLoader = ResolveFileLoaderTarget(ConstructFileLoader(crcFilename), &type, &errorString);
|
||||
if (!fileLoader) {
|
||||
ERROR_LOG(Log::Loader, "Failed to construct file loader for CRC: %s", errorString.c_str());
|
||||
std::lock_guard<std::mutex> guard(crcLock);
|
||||
crcResults[crcFilename] = 0;
|
||||
crcPending = false;
|
||||
crcCond.notify_one();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<BlockDevice> blockDevice(ConstructBlockDevice(fileLoader, &errorString));
|
||||
|
||||
u32 crc = 0;
|
||||
if (blockDevice) {
|
||||
crc = CalculateCRC(blockDevice, &crcCancel);
|
||||
crc = CalculateCRC(blockDevice.get(), &crcCancel);
|
||||
} else {
|
||||
ERROR_LOG(Log::Loader, "Failed to read from block device for CRC: %s", errorString.c_str());
|
||||
}
|
||||
|
||||
delete blockDevice;
|
||||
blockDevice.reset();
|
||||
delete fileLoader;
|
||||
|
||||
std::lock_guard<std::mutex> guard(crcLock);
|
||||
|
||||
+12
-12
@@ -340,7 +340,6 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
|
||||
// Trying to boot other things lands us here. We need to return a sensible error string.
|
||||
ERROR_LOG(Log::Loader, "CPU_Init didn't recognize file. %s", errorString->c_str());
|
||||
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
||||
*errorString = sy->T("Not a PSP game"); // best string we have.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -600,7 +599,7 @@ bool PSP_InitStart(const CoreParameter &coreParam) {
|
||||
}
|
||||
g_CoreParameter.errorString.clear();
|
||||
|
||||
std::string *error_string = &g_CoreParameter.errorString;
|
||||
std::string *errorString = &g_CoreParameter.errorString;
|
||||
|
||||
INFO_LOG(Log::Loader, "Starting loader thread...");
|
||||
|
||||
@@ -608,7 +607,7 @@ bool PSP_InitStart(const CoreParameter &coreParam) {
|
||||
|
||||
Core_NotifyLifecycle(CoreLifecycle::STARTING);
|
||||
|
||||
g_loadingThread = std::thread([error_string]() {
|
||||
g_loadingThread = std::thread([errorString]() {
|
||||
SetCurrentThreadName("ExecLoader");
|
||||
|
||||
AndroidJNIThreadContext jniContext;
|
||||
@@ -616,14 +615,13 @@ bool PSP_InitStart(const CoreParameter &coreParam) {
|
||||
NOTICE_LOG(Log::Boot, "PPSSPP %s", PPSSPP_GIT_VERSION);
|
||||
|
||||
Path filename = g_CoreParameter.fileToStart;
|
||||
FileLoader *loadedFile = ResolveFileLoaderTarget(ConstructFileLoader(filename));
|
||||
|
||||
IdentifiedFileType type = Identify_File(loadedFile, &g_CoreParameter.errorString);
|
||||
g_CoreParameter.fileType = type;
|
||||
IdentifiedFileType fileType;
|
||||
FileLoader *loadedFile = ResolveFileLoaderTarget(ConstructFileLoader(filename), &fileType, errorString);
|
||||
|
||||
if (System_GetPropertyBool(SYSPROP_ENOUGH_RAM_FOR_FULL_ISO)) {
|
||||
if (g_Config.bCacheFullIsoInRam) {
|
||||
switch (g_CoreParameter.fileType) {
|
||||
switch (fileType) {
|
||||
case IdentifiedFileType::PSP_ISO:
|
||||
case IdentifiedFileType::PSP_ISO_NP:
|
||||
loadedFile = new RamCachingFileLoader(loadedFile);
|
||||
@@ -635,14 +633,16 @@ bool PSP_InitStart(const CoreParameter &coreParam) {
|
||||
}
|
||||
}
|
||||
|
||||
g_CoreParameter.fileType = fileType;
|
||||
|
||||
// TODO: The reason we pass in g_CoreParameter.errorString here is that it's persistent -
|
||||
// it gets written to from the loader thread that gets spawned.
|
||||
if (!CPU_Init(loadedFile, type, &g_CoreParameter.errorString)) {
|
||||
if (!CPU_Init(loadedFile, fileType, &g_CoreParameter.errorString)) {
|
||||
CPU_Shutdown(false);
|
||||
g_CoreParameter.fileToStart.clear();
|
||||
*error_string = g_CoreParameter.errorString;
|
||||
if (error_string->empty()) {
|
||||
*error_string = "Failed initializing CPU/Memory";
|
||||
*errorString = g_CoreParameter.errorString;
|
||||
if (errorString->empty()) {
|
||||
*errorString = "Failed initializing CPU/Memory";
|
||||
}
|
||||
g_bootState = BootState::Failed;
|
||||
return;
|
||||
@@ -651,7 +651,7 @@ bool PSP_InitStart(const CoreParameter &coreParam) {
|
||||
// Initialize the GPU as far as we can here (do things like load cache files).
|
||||
_dbg_assert_(!gpu);
|
||||
#ifndef __LIBRETRO__
|
||||
InitGPU(error_string);
|
||||
InitGPU(errorString);
|
||||
#endif
|
||||
g_bootState = BootState::Complete;
|
||||
});
|
||||
|
||||
+1
-1
@@ -1457,7 +1457,7 @@ void EmuScreen::update() {
|
||||
std::string errLoadingFile = gamePath_.ToVisualString() + "\n\n";
|
||||
errLoadingFile.append(err->T("Error loading file", "Could not load game"));
|
||||
errLoadingFile.append("\n");
|
||||
errLoadingFile.append(err->T(errorMessage_.c_str()));
|
||||
errLoadingFile.append(errorMessage_);
|
||||
|
||||
screenManager()->push(new PromptScreen(gamePath_, errLoadingFile, di->T("OK"), ""));
|
||||
errorMessage_.clear();
|
||||
|
||||
@@ -121,6 +121,8 @@ struct ImConfig {
|
||||
|
||||
bool sasShowAllVoices = false;
|
||||
|
||||
float fbViewerZoom = 1.0f;
|
||||
|
||||
|
||||
// We use a separate ini file from the main PPSSPP config.
|
||||
void LoadConfig(const Path &iniFile);
|
||||
|
||||
@@ -79,10 +79,12 @@ void DrawFramebuffersWindow(ImConfig &cfg, FramebufferManagerCommon *framebuffer
|
||||
}
|
||||
|
||||
if (cfg.selectedFramebuffer != -1) {
|
||||
ImGui::SliderFloat("Scale", &cfg.fbViewerZoom, 0.5f, 16.0f, "%.2f", ImGuiSliderFlags_Logarithmic);
|
||||
|
||||
// Now, draw the image of the selected framebuffer.
|
||||
Draw::Framebuffer *fb = vfbs[cfg.selectedFramebuffer]->fbo;
|
||||
ImTextureID texId = ImGui_ImplThin3d_AddFBAsTextureTemp(fb, Draw::Aspect::COLOR_BIT, ImGuiPipeline::TexturedOpaque);
|
||||
ImGui::Image(texId, ImVec2(fb->Width(), fb->Height()));
|
||||
ImGui::Image(texId, ImVec2(fb->Width() * cfg.fbViewerZoom, fb->Height() * cfg.fbViewerZoom));
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
+2
-1
@@ -231,13 +231,14 @@ NPUH90087 = true # demo
|
||||
NPEH90042 = true # demo
|
||||
|
||||
[Force04154000Download]
|
||||
# This applies a hack to Dangan Ronpa, its demo, and its sequel.
|
||||
# This applies a hack to Dangan Ronpa, its demo, and its sequel, Super Dangan Ronpa 2.
|
||||
# The game draws solid colors to a small framebuffer, and then reads this directly in VRAM.
|
||||
# We force this framebuffer to 1x and force download it automatically.
|
||||
NPJH50631 = true
|
||||
NPJH50372 = true
|
||||
NPJH90164 = true
|
||||
NPJH50515 = true
|
||||
|
||||
# Let's also apply to Me & My Katamari.
|
||||
ULUS10094 = true
|
||||
ULES00339 = true
|
||||
|
||||
Reference in New Issue
Block a user