diff --git a/Core/FileLoaders/PS1EbootFileLoader.cpp b/Core/FileLoaders/PS1EbootFileLoader.cpp index 2d9efef95a..bbfdbda011 100644 --- a/Core/FileLoaders/PS1EbootFileLoader.cpp +++ b/Core/FileLoaders/PS1EbootFileLoader.cpp @@ -1108,8 +1108,8 @@ void PS1EbootFileLoader::BuildHeaders() { if (multiDisc) { // The fields the emulator reads (pops +1B2F0 in 6.60): the disc offsets, the first disc's ID, and the // title. The end of the last disc and its +0x2D31 are where a STARTDAT would follow, as in a PSISOIMG. - // The MACs a store game's header has, and the key its disc switching strings are descrambled with, - // stay zero: PopsBoot sees to both. + // The MACs a store game's header has stay zero (PopsBoot passes them), and so does the key its disc + // switching strings are descrambled with, which then only reverses PopsBoot's strings. u8 *title = pbp + psarOffset_; memcpy(title, "PSTITLEIMG000000", 16); *(u32_le *)(title + 0x10) = (u32)end; diff --git a/Core/HLE/PopsBoot.cpp b/Core/HLE/PopsBoot.cpp index c65ef33714..867ed12ffd 100644 --- a/Core/HLE/PopsBoot.cpp +++ b/Core/HLE/PopsBoot.cpp @@ -423,8 +423,8 @@ static bool SetsBlockSizeBefore(u32 addr, u32 textAddr) { // The emulator's multi-disc strings, 14 for each of its 12 languages, as {length, UTF-16 pointer}: the HOME // menu's item, then three dialogs of a line each. A store game's PSTITLEIMG header has the key they're -// descrambled with when a game has several discs (pops +29xxx in 6.60). A converted one has none, so the -// descrambling is skipped and they're ours, from our translations. Each dialog's lines are left-aligned and +// descrambled with when a game has several discs (pops +29xxx in 6.60). A converted one's is zeros, which +// only reverses them, and they're ours, from our translations. Each dialog's lines are left-aligned and // centred as a block. Its disc number is drawn over a gap in a line, at a position (one each for the // switch dialog, the disc picker's label and the confirmation) from a table of its own. enum { @@ -629,9 +629,10 @@ static bool InstallDiscSwitchText(u32 stringTable, u32 positionTable, u32 resetT u32 ptr = strings; u32 entries[DISC_STRING_COUNT][2]; for (int i = 0; i < DISC_STRING_COUNT; i++) { + // Backwards: the descrambling reverses them, and with our header's key of zeros does nothing else. const std::u16string &s = text.strings[i]; for (size_t c = 0; c < s.size(); c++) { - Memory::WriteUnchecked_U16(s[c], ptr + (u32)c * 2); + Memory::WriteUnchecked_U16(s[s.size() - 1 - c], ptr + (u32)c * 2); } entries[i][0] = (u32)s.size(); entries[i][1] = s.empty() ? 0 : ptr; @@ -659,22 +660,15 @@ static u32 LuiAddiuAddress(u32 addr) { return (Memory::ReadUnchecked_U32(addr) << 16) + (s16)(Memory::ReadUnchecked_U32(addr + 4) & 0xFFFF); } -// Finds the multi-disc strings' code in the emulator, and skips their descrambling for ours. +// Finds the multi-disc strings' tables in the emulator, and puts ours there. static void PatchDiscSwitchText(u32 textAddr, u32 textSize) { - u32 stringTable = 0, positionTable = 0, resetTable = 0, decode = 0, decodeEnd = 0; + u32 stringTable = 0, positionTable = 0, resetTable = 0; for (u32 addr = textAddr; addr + 0x20 < textAddr + textSize; addr += 4) { auto at = [&](u32 offset) { return Memory::ReadUnchecked_U32(addr + offset); }; - // The language's row: lang * 0x70 + table, into s7. - if (at(0) == 0x000219C0 && at(4) == 0x00021100 && at(8) == 0x00621823 && (at(12) >> 16) == 0x3C02 && (at(16) >> 16) == 0x2442 && at(20) == 0x0062B821) { + // The language's row: lang * 0x70 + table (into s7 in 6.x, k0 in 5.x, the stack in 3.x). + if (!stringTable && at(0) == 0x000219C0 && at(4) == 0x00021100 && at(8) == 0x00621823 && (at(12) >> 16) == 0x3C02 && (at(16) >> 16) == 0x2442 && (at(20) & 0xFFFF07FF) == 0x00620021) { stringTable = LuiAddiuAddress(addr + 12); } - // beq s7, zero, past the text; li s5, 0: then the descrambling, until bne t1, s0; addiu a1, a1, 8. - if (stringTable && !decode && (at(0) >> 16) == 0x12E0 && at(4) == 0x0000A821) { - decode = addr + 8; - } - if (decode && !decodeEnd && addr > decode && (at(0) >> 16) == 0x1530 && at(4) == 0x24A50008) { - decodeEnd = addr + 8; - } // The number position, (lang * 3 + i) * 4 + table. if (at(0) == 0x00031040 && at(4) == 0x00431021 && at(8) == 0x00501021 && (at(12) >> 16) == 0x3C03 && (at(16) >> 16) == 0x2463 && at(20) == 0x00021080) { positionTable = LuiAddiuAddress(addr + 12); @@ -684,17 +678,13 @@ static void PatchDiscSwitchText(u32 textAddr, u32 textSize) { resetTable = LuiAddiuAddress(addr); } } - if (!stringTable || !decode || !decodeEnd || !positionTable || !resetTable || decodeEnd - decode > 0x400) { - WARN_LOG(Log::sceKernel, "POPS: didn't find the disc switching text (%08x %08x-%08x %08x %08x)", stringTable, decode, decodeEnd, positionTable, resetTable); + if (!stringTable || !positionTable || !resetTable) { + WARN_LOG(Log::sceKernel, "POPS: didn't find the disc switching text (%08x %08x %08x)", stringTable, positionTable, resetTable); return; } - if (!InstallDiscSwitchText(stringTable, positionTable, resetTable)) { - return; + if (InstallDiscSwitchText(stringTable, positionTable, resetTable)) { + INFO_LOG(Log::sceKernel, "POPS: disc switching text at %08x, numbers at %08x", stringTable, positionTable); } - Memory::WriteUnchecked_U32(0x10000000 | (((decodeEnd - (decode + 4)) >> 2) & 0xFFFF), decode); // b decodeEnd - Memory::WriteUnchecked_U32(MIPS_MAKE_NOP(), decode + 4); - currentMIPS->InvalidateICacheRangeDeferred(decode, 8); - INFO_LOG(Log::sceKernel, "POPS: disc switching text at %08x, numbers at %08x", stringTable, positionTable); } void __PopsBootPatchEmulator(u32 textAddr, u32 textSize, u32 decodeStub) { diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 01eed5e891..0700708137 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -672,23 +672,10 @@ bool Load_PSP_POPS(FileLoader *fileLoader, std::string *error_string) { return false; } - // Several discs from a playlist need the disc switching text PopsBoot puts in the emulator, which it - // only finds in 6.20 and later. - const PS1EbootFileLoader *discImage = dynamic_cast(fileLoader); - if (discImage && discImage->NumDiscs() > 1) { - InstalledFirmwareInfo info; - ReadInstalledFirmwareInfo(g_Config.nandRootDirectory, &info, false); - if (FirmwareVersionToInt(info.version) < 620) { - auto po = GetI18NCategory(I18NCat::POPS); - *error_string = po->T("PS1MultiDiscFirmware", "So far, PS1 games of several discs only run with firmware 6.20 or later. Install one of those from an official PSP update file in Settings > Tools > PSP Firmware."); - return false; - } - } - // Its directory as host0:, as for a homebrew EBOOT. An EBOOT made from a disc image is the only file there. const Path fullPath = fileLoader->GetPath(); std::string gamePath; - if (discImage) { + if (dynamic_cast(fileLoader)) { pspFileSystem.Mount("host0:", std::make_shared(&pspFileSystem, fileLoader, "EBOOT.PBP")); gamePath = "host0:/EBOOT.PBP"; } else { diff --git a/docs/POPS.md b/docs/POPS.md index b24ecac4d3..44954cb696 100644 --- a/docs/POPS.md +++ b/docs/POPS.md @@ -88,13 +88,13 @@ Reset Game change it. A store game's PSTITLEIMG header also has a MAC for each disc header, which the manager's disc header check compares when there are several discs (by calling sceDrmBBMacFinal2 itself, in the export that is 6.60's sceMeAudio_14447BA0), and at +0x28C the key the emulator's disc switching strings are descrambled with. -A converted one has neither: every MAC the manager compares passes, and PopsBoot skips the descrambling and -puts our strings, from our translations, in the emulator's table (14 for each of its 12 languages, as -{length, UTF-16 pointer}). Each dialog's lines are left-aligned and centred as a block, in ltn0.pgf; the -disc number is drawn over a gap, at a position from a table per language (one each for the switch dialog, -the disc picker's label and the confirmation, and the reset dialog's as a move from the label's), which -PopsBoot computes for our text. That code is found by its instructions in 6.20 to 6.61, so several discs -need one of those; earlier versions have the same descrambling, compiled differently. +A converted one has neither: every MAC the manager compares passes, and PopsBoot puts our strings, from our +translations, in the emulator's table (14 for each of its 12 languages, as {length, UTF-16 pointer}). They +go in backwards: with a key of zeros, the descrambling only reverses each string. Each dialog's lines are +left-aligned and centred as a block, in ltn0.pgf; the disc number is drawn over a gap, at a position from a +table per language (one each for the switch dialog, the disc picker's label and the confirmation, and the +reset dialog's as a move from the label's), which PopsBoot computes for our text. The tables are found by +the code that indexes them, which is the same from 3.95 to 6.61. On Android, a game of several files (a cue sheet, `.ccd`, `.mds` or playlist) only loads through a folder PPSSPP has access to, not as a single picked file. @@ -119,6 +119,6 @@ The HOME button opens the emulator's menu. Savestates work. - The anti-LibCrypt word some PAL games need in a converted image (PSISOIMG + 0x12B0). ARK-4's popcorn has a table of them by disc ID, switched off. Converting tools can patch the image instead. - ARK-4's per-game CONFIG.BIN next to the EBOOT, copied over the image's config (PSISOIMG + 0x420). -- Multi-disc games on firmware before 6.20, and booting an image straight from an archive. +- Booting an image straight from an archive. - A byte popcorn changes in reads of converted images (0x41B, from 0x27 to 0x55 when 0x41B-0x41E read 27 19 22 41) without knowing why.