From 9e0842e03b24d9ee2fbb6d99ccf2e4ef5c0cd29b Mon Sep 17 00:00:00 2001 From: Henrique Gemignani Passos Lima Date: Wed, 12 May 2021 21:55:55 +0300 Subject: [PATCH] Update quickplay module (#38) * Migrate to use the REL loading dol patch from aprilwade's randomprime. * Migrate Quickplay module to use the Pwootage's PrimeAPI2 * Add Quickplay module for Echoes NTSC --- .gitignore | 3 + resources/quickplay/MP1/v1.088.bin | Bin 256 -> 339 bytes resources/quickplay/MP1/v1.088.map | 2 + resources/quickplay/MP1/v1.088.rel | Bin 3024 -> 2400 bytes resources/quickplay/MP1/v1.110.bin | Bin 0 -> 339 bytes resources/quickplay/MP1/v1.110.map | 2 + resources/quickplay/MP1/v1.111.bin | Bin 0 -> 339 bytes resources/quickplay/MP1/v1.111.map | 2 + resources/quickplay/MP2/v1.028.bin | Bin 0 -> 343 bytes resources/quickplay/MP2/v1.028.map | 2 + resources/quickplay/MP2/v1.028.rel | Bin 0 -> 3904 bytes src/Editor/NDolphinIntegration.cpp | 84 +++++++++++++++++------ src/Editor/SDolHeader.h | 106 +++++++++++++++++++++++++++++ 13 files changed, 179 insertions(+), 22 deletions(-) create mode 100644 resources/quickplay/MP1/v1.088.map create mode 100644 resources/quickplay/MP1/v1.110.bin create mode 100644 resources/quickplay/MP1/v1.110.map create mode 100644 resources/quickplay/MP1/v1.111.bin create mode 100644 resources/quickplay/MP1/v1.111.map create mode 100644 resources/quickplay/MP2/v1.028.bin create mode 100644 resources/quickplay/MP2/v1.028.map create mode 100644 resources/quickplay/MP2/v1.028.rel create mode 100644 src/Editor/SDolHeader.h diff --git a/.gitignore b/.gitignore index c1b9ffcd..c4e4f46f 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,9 @@ CMakeLists.txt.user .idea/vcs.xml .idea/workspace.xml +#Visual Studio +.vs + #Dew .dew/* diff --git a/resources/quickplay/MP1/v1.088.bin b/resources/quickplay/MP1/v1.088.bin index 87bf7551845d045ef58cdb6a65e92dbc61ff3329..23704ab955937fab4fd0b6df916f2785a1a1c4f5 100644 GIT binary patch literal 339 zcmZojW?*P&W?;}@VPJ4*`v1ePfkB~$gK60WMh2ECivK^@G&C?+v?vDDv?^D47+N?s zFfxc(C^87ta4;`JS8H*Afx+Sd1A`?41H?;n8F)O*ewx-F{#?-vfA4-1A)pqvTRE}oi9kpEZ|f%*hI%)V&?-9H0p*OX?U+kpO90t|x%urMe{EJ@Bt OE!Hba&0(;&X8-_^sBYu{ literal 256 zcmcCuU|_UA(7-T5PVItU1A{^h2h*|%j0`MO6#uW-zmUPlCZU1BB3bQ$MI!@;huMe7 zhC~J%3uXrS5IF`Di#i4dn+BkKi`qqt1q=)xhK6UYA2TpmBro@fcOAV%>$sC z=V}a=3_vwzpH)HjbpY+t0P-E1{{H}}2bz7fp`JkjYTiw#d9~Jyfo9YL@m!!@^IuOJ z=QEhpb}%mkx>d1(ks)R`(2W*~3=uUP%*#B?kG^5Bx7V<@*94jj0j?>TCHX~xrJ2du Q1v!b8dPS)@Fexw%0HVA{umAu6 diff --git a/resources/quickplay/MP1/v1.088.map b/resources/quickplay/MP1/v1.088.map new file mode 100644 index 00000000..e41d4871 --- /dev/null +++ b/resources/quickplay/MP1/v1.088.map @@ -0,0 +1,2 @@ +0x8000207c rel_loader_hook +0x8036F8BC PPCSetFpIEEEMode diff --git a/resources/quickplay/MP1/v1.088.rel b/resources/quickplay/MP1/v1.088.rel index 9477479e0b2fd25d1b44ece006fc6e4b3a4e2314..a492b4014c209bfdda4f757e1dd3f43d4a1c0b59 100644 GIT binary patch literal 2400 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 diff --git a/resources/quickplay/MP1/v1.110.bin b/resources/quickplay/MP1/v1.110.bin new file mode 100644 index 0000000000000000000000000000000000000000..0c7dc195f00224d30b9cda2637b9ce056d830a17 GIT binary patch literal 339 zcmZojW?*P&W?;}@VPJ4*`v1ePfkB~$gK60WMh2ECivK^@G&C?+v?vDDv?^D4Fl1IX zFfxc(C^87ta4;`JS8H*Afx+Sd1A`?41H?;n8F)NQXT{bZ{#?-vS literal 0 HcmV?d00001 diff --git a/resources/quickplay/MP1/v1.110.map b/resources/quickplay/MP1/v1.110.map new file mode 100644 index 00000000..f4484b84 --- /dev/null +++ b/resources/quickplay/MP1/v1.110.map @@ -0,0 +1,2 @@ +0x8000207c rel_loader_hook +0x803599e4 PPCSetFpIEEEMode \ No newline at end of file diff --git a/resources/quickplay/MP1/v1.111.bin b/resources/quickplay/MP1/v1.111.bin new file mode 100644 index 0000000000000000000000000000000000000000..a3dc9831eb140ae1ce53cb90c9ba462ce892a2ce GIT binary patch literal 339 zcmZojW?*P&W?;}@VPJ4*`v1ePfkB~$gK60WMh2ECivK^@G&C?+v?vDDv?^D4Fr->G zFfxc(C^87ta4;`JS8H*Afx+Sd1A`?41H?;n8F)O*SySr|f39c-^6}_Rut@;g zldR}s(Z~=mxshSQt+80_sC0Bp5uc>n+a literal 0 HcmV?d00001 diff --git a/resources/quickplay/MP1/v1.111.map b/resources/quickplay/MP1/v1.111.map new file mode 100644 index 00000000..5c28ef21 --- /dev/null +++ b/resources/quickplay/MP1/v1.111.map @@ -0,0 +1,2 @@ +0x8000207c rel_loader_hook +0x803704ec PPCSetFpIEEEMode \ No newline at end of file diff --git a/resources/quickplay/MP2/v1.028.bin b/resources/quickplay/MP2/v1.028.bin new file mode 100644 index 0000000000000000000000000000000000000000..43708e63f3b1f0acead0d16d686e526c402ef188 GIT binary patch literal 343 zcmZojW?*P&W?;}@VPJ4*`v1ePfkB~$gK60WMh2ECivK^@G&C?+v?zwuv?^D4=p6EG zU}O-nP-GCO;b2~duGZoJ1B1l_1_nz828PK8891yDGw^trKGCf|{JEkT$j75M!6pG{ zPqLziMI%GNw^peAbF5^77G|`JQxIZn-?;; zSkyAed;kCMYmv+#A5z0$0+O$9d0bJy^mPSLJaIxILx2Sk1KFYtH4F+MbwIW(SbYjm zee+|Gc?=%r{DDAu6R3SWHJKnkvM2)e3V4`4lm+@>2GG7K%|Q17{jvlY4hvx6P>@)X QoRM0rSCpE=U~kU=06@%dCjbBd literal 0 HcmV?d00001 diff --git a/resources/quickplay/MP2/v1.028.map b/resources/quickplay/MP2/v1.028.map new file mode 100644 index 00000000..a9b7604e --- /dev/null +++ b/resources/quickplay/MP2/v1.028.map @@ -0,0 +1,2 @@ +0x8000207c rel_loader_hook +0x8035712c PPCSetFpIEEEMode diff --git a/resources/quickplay/MP2/v1.028.rel b/resources/quickplay/MP2/v1.028.rel new file mode 100644 index 0000000000000000000000000000000000000000..9145e8485dbef58cbcb60158553dc7ccb1724809 GIT binary patch literal 3904 zcmd5-Uuaup6hAk)>#l7kd5DtDNN+WRK9tfy9d~PB>FA{5*xC`1fe%e@(l(eju}Nnq z!Q8bS8zLjDd)UKtovmXJL;9dF#M;7!4~noCVKP`ZtT5PQ6BT@zi@)>T@8)0ItqOt{ zF6W-_ob&sB-}&=hB9$s0R>Bp!g6|04G~PbEUS3%Haql9kpC{UKmq_W7K8O|g9FNCU zAo|vZ*jDGl!a-71TZUOzcG)^)x(Zs@iQ3V*-}f1*H*?iG5Yv^SFNKPUA$(uG}> zx=J3QKalzZ^47TpMOC$>jXxyqL$EW?6H-4xe%6(3whn3IFG%|W@T>7cBEHPOUD$iz z&wf3t_HUDRP1J(iMd*iQ{GnC+0@7~p zcZh1@-FE%5i0Arc5fAAu*gB-ndR@D%L(+F5{iXg0)|0jK(Vx`CQpr({rTQVw%XJ?l z?YK)J)?Hm>$n_OS-}VA&Lf*FJ!8CcHD>oyyyfs8K&{gFIorUzr+i4C`JA6afwKw~e zOVEAWZwuT_r;~Jr{qL`iKmZX#Y1mCc4oe zc(+;#`6l6GG?6!_xi22DNUw9T`ioTqulXh!8aCzKq%RVz;`@VNwe z3HvlgOlpTd$2$CaIxWR;{3`4-u+Ko(;d{==Q-_T6Tj$AdEz*qG^Cxi6)4Z`ngkh%g z-gKQY`DH%~=WB5ni=D^F!)vb>i}!)=2e!|#gUdnK{Z0($1oq3&E%^EvBWco?&F-5@ z=fs+R5$UG_%e9VAi<2uS-#Cxp!2209)Pw!F27e~R~YxFF{muOB#bVdB8CPxf}6p_t&6Cokl2oN04- zLgdVju}C>PNwjCl+H1Xk>I!q}m_q?S5_MO24RH!|j#t3ji(c~J%+Iopm(Suwez;FW z9VWqb5}Yv;LFa5vE-GfCG*ifl1`G|DKKu+8xNn{^PfN&WNaoW(PTC90)CLR#Jld^K zk=I3Pws;M)Z~@qOC4bD0auj<8tMjPzz|ON5M2_6y2_?+B$g5@N=m|OQF8P8xYSR!g zH-9Djr$yrOzHfk!!8taj#H(WtIcFko4!(?kPsV><5uX5W9e2nuWI)~}V*bFJ=RA;e z>*26P&OIXLm2Dx*+HFi_L%Ep$GsZ;^h`9!KnC%hMyW92|Jqe#y;Cr#I1Jei2PVK97 z$;*#9RN(K&9U=A=>k4(D_966jc#YbZdwbcwQzn&FfB=ymjH3->7IH;DOd=GviG z1J!*;1&C#JeYq~kz4t$g`9E>tyXaq?8UN9$|KWN$ zb#lpd^l;i&@2AjalHq9BI*dK=FFGJc^3Z3zgWyG zxbB&;6mxd@9PYluyw-7*$ejIkiF*-i`!I)%edfM7slVd*U|sDq>)w*RZg&Q!DZt_@}$=@5g^UsjrK8UK=?QO(tUcJ7Wp+?Q}AFR6i0Q%_LGIt@LU%k&MUm zY)T)EM`Lxm4uOzD~7XfoMK4!^R~aC9(Xw$k>DUfwVk?;it=cr-gU8qetOCXz{gB$d_s y #include @@ -18,7 +19,7 @@ namespace NDolphinIntegration const char* const gkDolphinPathSetting = "Quickplay/DolphinPath"; const char* const gkFeaturesSetting = "Quickplay/Features"; -const char* const gkRelFileName = "EditorQuickplay.rel"; +const char* const gkRelFileName = "patches.rel"; const char* const gkParameterFile = "dbgconfig"; const char* const gkDolPath = "sys/main.dol"; const char* const gkDolBackupPath = "sys/main.original.dol"; @@ -52,6 +53,34 @@ void CQuickplayRelay::QuickplayFinished(int ReturnCode) CQuickplayRelay gQuickplayRelay; +uint32 AssembleBranchInstruction(uint32 instructionAddress, uint32 branchTarget) +{ + int32 jumpOffset = ((int32)branchTarget - (int32)instructionAddress) / 4; + if (jumpOffset < 0) + { + jumpOffset += 1 << 24; + } + return (18 << 26) + (jumpOffset << 2); +} + +std::map LoadSymbols(const TString& mapContents) { + std::map result; + + for (auto& line : mapContents.Split("\n")) + { + auto separator = line.IndexOf(' '); + if (separator > 0) + { + auto address = line.SubString(0, separator); + auto name = line.SubString(separator + 1, line.Length() - separator - 1).Trimmed(); + + result.emplace(name, static_cast(address.ToInt32(16))); + } + } + + return result; +} + /** Attempt to launch quickplay based on the current editor state. */ EQuickplayLaunchResult LaunchQuickplay(QWidget* pParentWidget, CGameProject* pProject, @@ -59,13 +88,19 @@ EQuickplayLaunchResult LaunchQuickplay(QWidget* pParentWidget, { debugf("Launching quickplay..."); - // Check if quickplay is supported for this project + // Check if we have the files needed for this project's target game and version. + // The required files are split into two parts: + // * Quickplay Module: A .rel compiled from https://github.com/AxioDL/PWEQuickplayPatch/ + // * Dol patch for loading RELs: + // - A .bin compiled from https://github.com/aprilwade/randomprime/tree/master/compile_to_ppc/rel_loader + // - A .map, with the symbol for the .bin and the function in the dol to patch. TString QuickplayDir = gDataDir + "resources/quickplay" / ::GetGameShortName(pProject->Game()); TString BuildString = "v" + TString::FromFloat(pProject->BuildVersion()); TString RelFile = QuickplayDir / BuildString + ".rel"; TString PatchFile = QuickplayDir / BuildString + ".bin"; + TString PatchMapFile = QuickplayDir / BuildString + ".map"; - if (!FileUtil::Exists(RelFile) || !FileUtil::Exists(PatchFile)) + if (!FileUtil::Exists(RelFile) || !FileUtil::Exists(PatchFile) || !FileUtil::Exists(PatchMapFile)) { warnf("Quickplay launch failed! Quickplay is not supported for this project."); return EQuickplayLaunchResult::UnsupportedForProject; @@ -122,14 +157,16 @@ EQuickplayLaunchResult LaunchQuickplay(QWidget* pParentWidget, TString DiscSys = pProject->DiscDir(false) / "sys"; std::vector DolData; std::vector PatchData; + TString MapData; bool bLoadedDol = FileUtil::LoadFileToBuffer(DiscSys / "main.dol", DolData); bool bLoadedPatch = FileUtil::LoadFileToBuffer(PatchFile, PatchData); + bool bLoadedMap = FileUtil::LoadFileToString(PatchMapFile, MapData); - if (!bLoadedDol || !bLoadedPatch) + if (!bLoadedDol || !bLoadedPatch || !bLoadedMap) { - warnf("Quickplay launch failed! Failed to load %s into memory.", - bLoadedDol ? "patch data" : "game DOL"); + const char* failedPart = !bLoadedDol ? "game DOL" : (!bLoadedPatch ? "patch data" : "patch symbols"); + warnf("Quickplay launch failed! Failed to load %s into memory.", failedPart); return EQuickplayLaunchResult::Failure; } @@ -149,26 +186,28 @@ EQuickplayLaunchResult LaunchQuickplay(QWidget* pParentWidget, FileUtil::CopyFile(DolPath, DolBackupPath); } + auto symbols = LoadSymbols(MapData); + SDolHeader header(CMemoryInStream(DolData.data(), DolData.size(), EEndian::BigEndian)); + // Append the patch data to the end of the dol uint32 AlignedDolSize = VAL_ALIGN(DolData.size(), 32); uint32 AlignedPatchSize = VAL_ALIGN(PatchData.size(), 32); - uint32 PatchOffset = AlignedDolSize; - uint32 PatchSize = AlignedPatchSize; - DolData.resize(PatchOffset + PatchSize); - memcpy(&DolData[PatchOffset], &PatchData[0], PatchData.size()); + DolData.resize(AlignedDolSize + AlignedPatchSize); + memcpy(&DolData[AlignedDolSize], &PatchData[0], PatchData.size()); + + if (!header.AddTextSection(0x80002000, AlignedDolSize, AlignedPatchSize)) + { + warnf("Quickplay launch failed! Failed to patch DOL. Is it already patched?"); + return EQuickplayLaunchResult::Failure; + } + + uint32 callToHook = symbols["PPCSetFpIEEEMode"] + 4; + uint32 branchTarget = symbols["rel_loader_hook"]; - // These constants are hardcoded for the moment. - // We write the patch to text section 6, which must be at address 0x80002600. - // We hook over the call to LCEnable, which is at offset 0x1D64. CMemoryOutStream Mem(DolData.data(), DolData.size(), EEndian::BigEndian); - Mem.GoTo(0x18); - Mem.WriteLong(PatchOffset); - Mem.GoTo(0x60); - Mem.WriteLong(0x80002600); - Mem.GoTo(0xA8); - Mem.WriteLong(PatchSize); - Mem.GoTo(0x1D64); - Mem.WriteLong(0x4BFFD80D); // this patches in a call to the quickplay bootstrap during game boot process + header.Write(Mem); + Mem.GoTo(header.OffsetForAddress(callToHook)); + Mem.WriteLong(AssembleBranchInstruction(callToHook, branchTarget)); if (!FileUtil::SaveBufferToFile(DolPath, DolData)) { @@ -211,7 +250,8 @@ bool IsQuickplaySupported(CGameProject* pProject) TString BuildString = "v" + TString::FromFloat(pProject->BuildVersion()); TString RelFile = QuickplayDir / BuildString + ".rel"; TString PatchFile = QuickplayDir / BuildString + ".bin"; - return FileUtil::Exists(RelFile) && FileUtil::Exists(PatchFile); + TString MapFile = QuickplayDir / BuildString + ".map"; + return FileUtil::Exists(RelFile) && FileUtil::Exists(PatchFile) && FileUtil::Exists(MapFile); } /** Kill the current quickplay process, if it exists. */ diff --git a/src/Editor/SDolHeader.h b/src/Editor/SDolHeader.h new file mode 100644 index 00000000..3473cdef --- /dev/null +++ b/src/Editor/SDolHeader.h @@ -0,0 +1,106 @@ +#ifndef CDOLHEADER_H +#define CDOLHEADER_H + +#include +#include + +class SDolHeader +{ +public: + static const size_t kNumTextSections = 7; + static const size_t kNumDataSections = 11; + static const size_t kNumSections = kNumTextSections + kNumDataSections; + + struct Section + { + uint32 Offset; + uint32 BaseAddress; + uint32 Size; + + bool IsEmpty() const { + return Size == 0; + } + }; + + Section Sections[kNumSections]; + uint32 BssAddress; + uint32 BssSize; + uint32 EntryPoint; + + explicit SDolHeader(IInputStream& rInput) + { + for (size_t i = 0; i < kNumSections; ++i) + { + Sections[i].Offset = rInput.ReadLong(); + } + for (size_t i = 0; i < kNumSections; ++i) + { + Sections[i].BaseAddress = rInput.ReadLong(); + } + for (size_t i = 0; i < kNumSections; ++i) + { + Sections[i].Size = rInput.ReadLong(); + } + BssAddress = rInput.ReadLong(); + BssSize = rInput.ReadLong(); + EntryPoint = rInput.ReadLong(); + } + + void Write(IOutputStream& rOutput) const + { + for (size_t i = 0; i < kNumSections; ++i) + { + rOutput.WriteLong(Sections[i].Offset); + } + for (size_t i = 0; i < kNumSections; ++i) + { + rOutput.WriteLong(Sections[i].BaseAddress); + } + for (size_t i = 0; i < kNumSections; ++i) + { + rOutput.WriteLong(Sections[i].Size); + } + rOutput.WriteLong(BssAddress); + rOutput.WriteLong(BssSize); + rOutput.WriteLong(EntryPoint); + } + + bool AddTextSection(uint32 address, uint32 fileOffset, uint32 size) + { + if ((size & 0x1f) != 0) + { + warnf("Unable to add text section: Size not 32-bit aligned."); + return false; + } + + for (size_t i = 0; i < kNumTextSections; ++i) + { + if (Sections[i].IsEmpty()) + { + Sections[i].BaseAddress = address; + Sections[i].Offset = fileOffset; + Sections[i].Size = size; + return true; + } + } + + warnf("Unable to add text section: no empty section found."); + return false; + } + + uint32 OffsetForAddress(uint32 address) + { + for (size_t i = 0; i < kNumSections; ++i) + { + auto& sec = Sections[i]; + if (address > sec.BaseAddress && address < sec.BaseAddress + sec.Size) + { + return sec.Offset + (address - sec.BaseAddress); + } + } + warnf("Unable to add section for address: %x", address); + return 0; + } +}; + +#endif // SDOLHEADER_H