From 8489d9da90867d28ae7a33869d66df6b9d689832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 6 Aug 2017 19:09:45 +0800 Subject: [PATCH 1/7] Boot: Move StateFlags from Boot_WiiWAD to Boot It's not specific to WADs. The BS2 emulation boot code will also need to update the state file. Move the struct to Boot and add a helper function that will handle reading + computing the checksum + writing the state file. --- Source/Core/Core/Boot/Boot.cpp | 39 ++++++++++++++++++++ Source/Core/Core/Boot/Boot.h | 16 ++++++++ Source/Core/Core/Boot/Boot_WiiWAD.cpp | 53 ++------------------------- 3 files changed, 58 insertions(+), 50 deletions(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index be4ef2636c..3b004eca78 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -5,7 +5,10 @@ #include "Core/Boot/Boot.h" #include +#include +#include #include +#include #include #include #include @@ -25,6 +28,7 @@ #include "Core/Boot/DolReader.h" #include "Core/Boot/ElfReader.h" +#include "Core/CommonTitles.h" #include "Core/ConfigManager.h" #include "Core/FifoPlayer/FifoPlayer.h" #include "Core/HLE/HLE.h" @@ -418,3 +422,38 @@ BootExecutableReader::BootExecutableReader(const std::vector& bytes) : m_byt } BootExecutableReader::~BootExecutableReader() = default; + +void StateFlags::UpdateChecksum() +{ + constexpr size_t length_in_bytes = sizeof(StateFlags) - 4; + constexpr size_t num_elements = length_in_bytes / sizeof(u32); + std::array flag_data; + std::memcpy(flag_data.data(), &flags, length_in_bytes); + checksum = std::accumulate(flag_data.cbegin(), flag_data.cend(), 0U); +} + +void UpdateStateFlags(std::function update_function) +{ + const std::string file_path = + Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + WII_STATE; + + File::IOFile file; + StateFlags state; + if (File::Exists(file_path)) + { + file.Open(file_path, "r+b"); + file.ReadBytes(&state, sizeof(state)); + } + else + { + File::CreateFullPath(file_path); + file.Open(file_path, "a+b"); + memset(&state, 0, sizeof(state)); + } + + update_function(&state); + state.UpdateChecksum(); + + file.Seek(0, SEEK_SET); + file.WriteBytes(&state, sizeof(state)); +} diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index f5428616af..96dd2002e4 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -128,3 +129,18 @@ public: protected: std::vector m_bytes; }; + +struct StateFlags +{ + void UpdateChecksum(); + u32 checksum; + u8 flags; + u8 type; + u8 discstate; + u8 returnto; + u32 unknown[6]; +}; + +// Reads the state file from the NAND, then calls the passed update function to update the struct, +// and finally writes the updated state file to the NAND. +void UpdateStateFlags(std::function update_function); diff --git a/Source/Core/Core/Boot/Boot_WiiWAD.cpp b/Source/Core/Core/Boot/Boot_WiiWAD.cpp index 145ae48a1f..44d2b64262 100644 --- a/Source/Core/Core/Boot/Boot_WiiWAD.cpp +++ b/Source/Core/Core/Boot/Boot_WiiWAD.cpp @@ -2,15 +2,12 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include #include #include -#include #include #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" -#include "Common/File.h" #include "Common/FileUtil.h" #include "Common/MsgHandler.h" #include "Common/NandPaths.h" @@ -24,55 +21,11 @@ #include "DiscIO/NANDContentLoader.h" -struct StateFlags -{ - u32 checksum; - u8 flags; - u8 type; - u8 discstate; - u8 returnto; - u32 unknown[6]; -}; - -static u32 StateChecksum(const StateFlags& flags) -{ - constexpr size_t length_in_bytes = sizeof(StateFlags) - 4; - constexpr size_t num_elements = length_in_bytes / sizeof(u32); - std::array flag_data; - - std::memcpy(flag_data.data(), &flags.flags, length_in_bytes); - - return std::accumulate(flag_data.cbegin(), flag_data.cend(), 0U); -} - bool CBoot::Boot_WiiWAD(const std::string& _pFilename) { - std::string state_filename( - Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + WII_STATE); - - if (File::Exists(state_filename)) - { - File::IOFile state_file(state_filename, "r+b"); - StateFlags state; - state_file.ReadBytes(&state, sizeof(StateFlags)); - - state.type = 0x03; // TYPE_RETURN - state.checksum = StateChecksum(state); - - state_file.Seek(0, SEEK_SET); - state_file.WriteBytes(&state, sizeof(StateFlags)); - } - else - { - File::CreateFullPath(state_filename); - File::IOFile state_file(state_filename, "a+b"); - StateFlags state; - memset(&state, 0, sizeof(StateFlags)); - state.type = 0x03; // TYPE_RETURN - state.discstate = 0x01; // DISCSTATE_WII - state.checksum = StateChecksum(state); - state_file.WriteBytes(&state, sizeof(StateFlags)); - } + UpdateStateFlags([](StateFlags* state) { + state->type = 0x03; // TYPE_RETURN + }); const DiscIO::NANDContentLoader& ContentLoader = DiscIO::NANDContentManager::Access().GetNANDLoader(_pFilename); From 363bf27cce6a8d57048f735daba0ba6e86322bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 6 Aug 2017 20:06:19 +0800 Subject: [PATCH 2/7] Boot/BS2: Write empty play record --- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 910fe5db81..a817fcca8f 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -319,6 +319,15 @@ bool CBoot::SetupWiiMemory(u64 ios_title_id) return true; } +static void WriteEmptyPlayRecord() +{ + const std::string file_path = + Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + "play_rec.dat"; + File::IOFile playrec_file(file_path, "r+b"); + std::vector empty_record(0x80); + playrec_file.WriteBytes(empty_record.data(), empty_record.size()); +} + // __________________________________________________________________________________________________ // Wii Bootstrap 2 HLE: // copy the apploader to 0x81200000 @@ -335,6 +344,8 @@ bool CBoot::EmulatedBS2_Wii(const DiscIO::Volume& volume) if (!tmd.IsValid()) return false; + WriteEmptyPlayRecord(); + if (!SetupWiiMemory(tmd.GetIOSId())) return false; From 29b1276548c4ddccf3900cca5de30a9962818431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 6 Aug 2017 20:08:05 +0800 Subject: [PATCH 3/7] Boot/BS2: Update state file --- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index a817fcca8f..a93a551e71 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -345,6 +345,11 @@ bool CBoot::EmulatedBS2_Wii(const DiscIO::Volume& volume) return false; WriteEmptyPlayRecord(); + UpdateStateFlags([](StateFlags* state) { + state->flags = 0xc1; + state->type = 0xff; + state->discstate = 0x01; + }); if (!SetupWiiMemory(tmd.GetIOSId())) return false; From 81b298f7473086197c1e8dfd6e00d622ba646cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 6 Aug 2017 20:17:06 +0800 Subject: [PATCH 4/7] Boot/BS2: Set the proper value for 0x000030d8 No clue where people got the 0 value from, or why it's labelled as "time". As far as I can tell, it is always set to 0xffffffff by official NAND titles, including the system menu. --- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index a93a551e71..6cb4c3e851 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -296,7 +296,7 @@ bool CBoot::SetupWiiMemory(u64 ios_title_id) Memory::Write_U32(0x00000000, 0x000030c0); // EXI Memory::Write_U32(0x00000000, 0x000030c4); // EXI Memory::Write_U32(0x00000000, 0x000030dc); // Time - Memory::Write_U32(0x00000000, 0x000030d8); // Time + Memory::Write_U32(0xffffffff, 0x000030d8); // Unknown, set by any official NAND title Memory::Write_U16(0x8201, 0x000030e6); // Dev console / debug capable Memory::Write_U32(0x00000000, 0x000030f0); // Apploader From 0bdcabdfa966660a9c1a19cda9468e2683748d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 6 Aug 2017 20:38:02 +0800 Subject: [PATCH 5/7] Boot/BS2: Write the "devkit boot program version" --- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 6cb4c3e851..9723622715 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -300,6 +300,15 @@ bool CBoot::SetupWiiMemory(u64 ios_title_id) Memory::Write_U16(0x8201, 0x000030e6); // Dev console / debug capable Memory::Write_U32(0x00000000, 0x000030f0); // Apploader + // During the boot process, 0x315c is first set to 0xdeadbeef by IOS + // in the boot_ppc syscall. The value is then partly overwritten by SDK titles. + // Two bytes at 0x315e are used to indicate the "devkit boot program version". + // It is only written to by the system menu, so we must do so here as well. + // + // 0x0113 appears to mean v1.13, which is the latest version. + // It is fine to always use the latest value as apploaders work with all versions. + Memory::Write_U16(0x0113, 0x0000315e); + if (!IOS::HLE::GetIOS()->BootIOS(ios_title_id)) return false; From d612416ce83d41b87e1d22835caf9c8cfc7e64a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 6 Aug 2017 23:29:43 +0800 Subject: [PATCH 6/7] Boot/BS2: Write to 0x3194 and 0x3198 --- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 9723622715..a2a5ff0173 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -360,6 +360,14 @@ bool CBoot::EmulatedBS2_Wii(const DiscIO::Volume& volume) state->discstate = 0x01; }); + // While reading a disc, the system menu reads the first partition table + // (0x20 bytes from 0x00040020) and stores a pointer to the data partition entry. + // When launching the disc game, it copies the partition type and offset to 0x3194 + // and 0x3198 respectively. + const DiscIO::Partition data_partition = volume.GetGamePartition(); + Memory::Write_U32(0, 0x3194); + Memory::Write_U32(static_cast(data_partition.offset >> 2), 0x3198); + if (!SetupWiiMemory(tmd.GetIOSId())) return false; From 6d73c3e8e33764147e3c552f9952caa376913d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 7 Aug 2017 00:18:04 +0800 Subject: [PATCH 7/7] Boot/BS2: Don't write part of the debugger hook Writing to 0x60 does actually not "init exception[s]" or anything like that. Not at all. Rather, it *breaks* a check in Nintendo's SDK, which makes it fail to realise that the hook hasn't been set up. This prevents the SDK initialisation routines from writing the rest of the hook instructions (total: 0x20 bytes), which in turn causes an anti-piracy check to fail in some Ubisoft games (including Tintin). Dolphin can be really amazing sometimes. --- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index a2a5ff0173..794e979aa1 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -286,7 +286,6 @@ bool CBoot::SetupWiiMemory(u64 ios_title_id) Memory::Write_U32(0x00000000, 0x00000030); // Init Memory::Write_U32(0x817FEC60, 0x00000034); // Init // 38, 3C should get start, size of FST through apploader - Memory::Write_U32(0x38a00040, 0x00000060); // Exception init Memory::Write_U32(0x8008f7b8, 0x000000e4); // Thread Init Memory::Write_U32(Memory::REALRAM_SIZE, 0x000000f0); // "Simulated memory size" (debug mode?) Memory::Write_U32(0x8179b500, 0x000000f4); // __start