From 19b8f1c10adeefd9413687ab7a7c21b509fe777d Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 13 Jun 2015 12:51:24 +0200 Subject: [PATCH] VolumeWiiCrypted: Replace ChangePartition with a partition parameter By removing mutable state in VolumeWiiCrypted, this change makes partition-related code simpler. It also gets rid of other ugly things, like ISOProperties's "over 9000" loop that creates a list of partitions by trying possible combinations, and DiscScrubber's volume swapping that recreates the entire volume when it needs to change partition. --- Source/Core/Core/Boot/Boot.cpp | 16 +- Source/Core/Core/Boot/Boot.h | 3 +- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 24 ++- Source/Core/Core/ConfigManager.cpp | 12 +- Source/Core/Core/ConfigManager.h | 3 +- Source/Core/Core/HW/DVD/DVDInterface.cpp | 114 ++++++---- Source/Core/Core/HW/DVD/DVDInterface.h | 10 +- Source/Core/Core/HW/DVD/DVDThread.cpp | 64 +++--- Source/Core/Core/HW/DVD/DVDThread.h | 27 +-- Source/Core/Core/HW/DVD/FileMonitor.cpp | 32 ++- Source/Core/Core/HW/DVD/FileMonitor.h | 6 +- Source/Core/Core/IOS/DI/DI.cpp | 10 +- Source/Core/Core/IOS/MIOS.cpp | 3 +- Source/Core/Core/State.cpp | 2 +- Source/Core/DiscIO/DiscScrubber.cpp | 161 ++++++-------- Source/Core/DiscIO/DiscScrubber.h | 25 +-- Source/Core/DiscIO/FileSystemGCWii.cpp | 46 ++-- Source/Core/DiscIO/FileSystemGCWii.h | 3 +- Source/Core/DiscIO/Filesystem.cpp | 8 +- Source/Core/DiscIO/Filesystem.h | 11 +- Source/Core/DiscIO/Volume.h | 63 ++++-- Source/Core/DiscIO/VolumeCreator.cpp | 125 +---------- Source/Core/DiscIO/VolumeCreator.h | 7 +- Source/Core/DiscIO/VolumeDirectory.cpp | 30 ++- Source/Core/DiscIO/VolumeDirectory.h | 18 +- Source/Core/DiscIO/VolumeGC.cpp | 44 ++-- Source/Core/DiscIO/VolumeGC.h | 19 +- Source/Core/DiscIO/VolumeWad.cpp | 34 +-- Source/Core/DiscIO/VolumeWad.h | 27 ++- Source/Core/DiscIO/VolumeWiiCrypted.cpp | 201 +++++++++++++----- Source/Core/DiscIO/VolumeWiiCrypted.h | 40 ++-- .../ISOProperties/FilesystemPanel.cpp | 61 +++--- .../DolphinWX/ISOProperties/FilesystemPanel.h | 3 +- .../DolphinWX/ISOProperties/ISOProperties.cpp | 5 +- .../DolphinWX/ISOProperties/InfoPanel.cpp | 4 +- 35 files changed, 622 insertions(+), 639 deletions(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index c69d50b75c..35bad4333c 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -50,10 +50,10 @@ static const DiscIO::IVolume* SetDisc(std::unique_ptr volume) } bool CBoot::DVDRead(const DiscIO::IVolume& volume, u64 dvd_offset, u32 output_address, u32 length, - bool decrypt) + const DiscIO::Partition& partition) { std::vector buffer(length); - if (!volume.Read(dvd_offset, length, buffer.data(), decrypt)) + if (!volume.Read(dvd_offset, length, buffer.data(), partition)) return false; Memory::CopyToEmu(output_address, buffer.data(), length); return true; @@ -64,8 +64,10 @@ void CBoot::Load_FST(bool is_wii, const DiscIO::IVolume* volume) if (!volume) return; + const DiscIO::Partition partition = volume->GetGamePartition(); + // copy first 32 bytes of disc to start of Mem 1 - DVDRead(*volume, /*offset*/ 0, /*address*/ 0, /*length*/ 0x20, false); + DVDRead(*volume, /*offset*/ 0, /*address*/ 0, /*length*/ 0x20, DiscIO::PARTITION_NONE); // copy of game id Memory::Write_U32(Memory::Read_U32(0x0000), 0x3180); @@ -78,15 +80,15 @@ void CBoot::Load_FST(bool is_wii, const DiscIO::IVolume* volume) u32 fst_size = 0; u32 max_fst_size = 0; - volume->ReadSwapped(0x0424, &fst_offset, is_wii); - volume->ReadSwapped(0x0428, &fst_size, is_wii); - volume->ReadSwapped(0x042c, &max_fst_size, is_wii); + volume->ReadSwapped(0x0424, &fst_offset, partition); + volume->ReadSwapped(0x0428, &fst_size, partition); + volume->ReadSwapped(0x042c, &max_fst_size, partition); u32 arena_high = Common::AlignDown(0x817FFFFF - (max_fst_size << shift), 0x20); Memory::Write_U32(arena_high, 0x00000034); // load FST - DVDRead(*volume, fst_offset << shift, arena_high, fst_size << shift, is_wii); + DVDRead(*volume, fst_offset << shift, arena_high, fst_size << shift, partition); Memory::Write_U32(arena_high, 0x00000038); Memory::Write_U32(max_fst_size << shift, 0x0000003c); diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index 22af0f5773..1b90cf3457 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -12,6 +12,7 @@ namespace DiscIO { class IVolume; +struct Partition; } struct RegionSetting @@ -46,7 +47,7 @@ public: private: static bool DVDRead(const DiscIO::IVolume& volume, u64 dvd_offset, u32 output_address, u32 length, - bool decrypt); + const DiscIO::Partition& partition); static void RunFunction(u32 address); static void UpdateDebugger_MapLoaded(); diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index ad662cf7f3..c8288c4d44 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -79,21 +79,24 @@ void CBoot::SetupBAT(bool is_wii) bool CBoot::RunApploader(bool is_wii, const DiscIO::IVolume& volume) { + const DiscIO::Partition partition = volume.GetGamePartition(); + // Load Apploader to Memory - The apploader is hardcoded to begin at 0x2440 on the disc, // but the size can differ between discs. Compare with YAGCD chap 13. const u32 apploader_offset = 0x2440; u32 apploader_entry = 0; u32 apploader_size = 0; u32 apploader_trailer = 0; - if (!volume.ReadSwapped(apploader_offset + 0x10, &apploader_entry, is_wii) || - !volume.ReadSwapped(apploader_offset + 0x14, &apploader_size, is_wii) || - !volume.ReadSwapped(apploader_offset + 0x18, &apploader_trailer, is_wii) || + if (!volume.ReadSwapped(apploader_offset + 0x10, &apploader_entry, partition) || + !volume.ReadSwapped(apploader_offset + 0x14, &apploader_size, partition) || + !volume.ReadSwapped(apploader_offset + 0x18, &apploader_trailer, partition) || apploader_entry == (u32)-1 || apploader_size + apploader_trailer == (u32)-1) { INFO_LOG(BOOT, "Invalid apploader. Your disc image is probably corrupted."); return false; } - DVDRead(volume, apploader_offset + 0x20, 0x01200000, apploader_size + apploader_trailer, is_wii); + DVDRead(volume, apploader_offset + 0x20, 0x01200000, apploader_size + apploader_trailer, + partition); // TODO - Make Apploader(or just RunFunction()) debuggable!!! @@ -132,7 +135,7 @@ bool CBoot::RunApploader(bool is_wii, const DiscIO::IVolume& volume) INFO_LOG(MASTER_LOG, "DVDRead: offset: %08x memOffset: %08x length: %i", iDVDOffset, iRamAddress, iLength); - DVDRead(volume, iDVDOffset, iRamAddress, iLength, is_wii); + DVDRead(volume, iDVDOffset, iRamAddress, iLength, partition); } while (PowerPC::ppcState.gpr[3] != 0x00); @@ -163,7 +166,7 @@ bool CBoot::EmulatedBS2_GC(const DiscIO::IVolume* volume, bool skip_app_loader) // It's possible to boot DOL and ELF files without a disc inserted if (volume) - DVDRead(*volume, /*offset*/ 0x00000000, /*address*/ 0x00000000, 0x20, false); + DVDRead(*volume, /*offset*/ 0x00000000, /*address*/ 0x00000000, 0x20, DiscIO::PARTITION_NONE); // Booted from bootrom. 0xE5207C22 = booted from jtag PowerPC::HostWrite_U32(0x0D15EA5E, 0x80000020); @@ -280,7 +283,7 @@ bool CBoot::SetupWiiMemory(const DiscIO::IVolume* volume, u64 ios_title_id) // When booting a WAD or the system menu, there will probably not be a disc inserted if (volume) - DVDRead(*volume, 0x00000000, 0x00000000, 0x20, false); // Game Code + DVDRead(*volume, 0x00000000, 0x00000000, 0x20, DiscIO::PARTITION_NONE); // Game Code Memory::Write_U32(0x0D15EA5E, 0x00000020); // Another magic word Memory::Write_U32(0x00000001, 0x00000024); // Unknown @@ -335,7 +338,8 @@ bool CBoot::EmulatedBS2_Wii(const DiscIO::IVolume* volume) if (volume->GetVolumeType() != DiscIO::Platform::WII_DISC) return false; - const IOS::ES::TMDReader tmd = volume->GetTMD(); + const DiscIO::Partition partition = volume->GetGamePartition(); + const IOS::ES::TMDReader tmd = volume->GetTMD(partition); if (!SetupWiiMemory(volume, tmd.GetIOSId())) return false; @@ -344,7 +348,7 @@ bool CBoot::EmulatedBS2_Wii(const DiscIO::IVolume* volume) // values as the game boots. This location keeps the 4 byte ID for as long // as the game is running. The 6 byte ID at 0x00 is overwritten sometime // after this check during booting. - DVDRead(*volume, 0, 0x3180, 4, true); + DVDRead(*volume, 0, 0x3180, 4, partition); SetupBAT(/*is_wii*/ true); @@ -359,7 +363,7 @@ bool CBoot::EmulatedBS2_Wii(const DiscIO::IVolume* volume) // Warning: This call will set incorrect running game metadata if our volume parameter // doesn't point to the same disc as the one that's inserted in the emulated disc drive! - IOS::HLE::Device::ES::DIVerify(tmd, volume->GetTicket()); + IOS::HLE::Device::ES::DIVerify(tmd, volume->GetTicket(partition)); return true; } diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index b4fd424d5a..6620688339 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -25,7 +25,6 @@ #include "Core/FifoPlayer/FifoDataFile.h" #include "Core/HLE/HLE.h" #include "Core/HW/DVD/DVDInterface.h" -#include "Core/HW/DVD/DVDThread.h" #include "Core/HW/SI/SI.h" #include "Core/IOS/ES/Formats.h" #include "Core/IOS/USB/Bluetooth/BTBase.h" @@ -746,11 +745,12 @@ void SConfig::ResetRunningGameMetadata() SetRunningGameMetadata("00000000", 0, 0); } -void SConfig::SetRunningGameMetadata(const DiscIO::IVolume& volume) +void SConfig::SetRunningGameMetadata(const DiscIO::IVolume& volume, + const DiscIO::Partition& partition) { u64 title_id = 0; - volume.GetTitleID(&title_id); - SetRunningGameMetadata(volume.GetGameID(), title_id, volume.GetRevision()); + volume.GetTitleID(&title_id, partition); + SetRunningGameMetadata(volume.GetGameID(partition), title_id, volume.GetRevision(partition)); } void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd) @@ -761,7 +761,7 @@ void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd) // the disc header instead of the TMD. They can differ. // (IOS HLE ES calls us with a TMDReader rather than a volume when launching // a disc game, because ES has no reason to be accessing the disc directly.) - if (!DVDThread::UpdateRunningGameMetadata(tmd_title_id)) + if (!DVDInterface::UpdateRunningGameMetadata(tmd_title_id)) { // If not launching a disc game, just read everything from the TMD. SetRunningGameMetadata(tmd.GetGameID(), tmd_title_id, tmd.GetTitleVersion()); @@ -935,7 +935,7 @@ bool SConfig::AutoSetup(EBootBS2 _BootBS2) m_strFilename.c_str()); return false; } - SetRunningGameMetadata(*pVolume); + SetRunningGameMetadata(*pVolume, pVolume->GetGamePartition()); // Check if we have a Wii disc bWii = pVolume->GetVolumeType() == DiscIO::Platform::WII_DISC; diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index d5ad66b22b..8559361c80 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -19,6 +19,7 @@ namespace DiscIO { enum class Language; enum class Region; +struct Partition; class IVolume; } namespace IOS @@ -226,7 +227,7 @@ struct SConfig : NonCopyable u64 GetTitleID() const { return m_title_id; } u16 GetRevision() const { return m_revision; } void ResetRunningGameMetadata(); - void SetRunningGameMetadata(const DiscIO::IVolume& volume); + void SetRunningGameMetadata(const DiscIO::IVolume& volume, const DiscIO::Partition& partition); void SetRunningGameMetadata(const IOS::ES::TMDReader& tmd); void LoadDefaults(); diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 11812f6f54..3681eb5558 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -218,6 +218,7 @@ static u32 s_pending_samples; // Disc drive state static u32 s_error_code = 0; +static DiscIO::Partition s_current_partition; // Disc drive timing static u64 s_read_buffer_start_time; @@ -243,12 +244,14 @@ void UpdateInterrupts(); void GenerateDIInterrupt(DIInterruptType _DVDInterrupt); void WriteImmediate(u32 value, u32 output_address, bool reply_to_ios); -bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, ReplyType reply_type, DIInterruptType* interrupt_type); +bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length, u32 output_length, + const DiscIO::Partition& partition, ReplyType reply_type, + DIInterruptType* interrupt_type); u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type); -void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, ReplyType reply_type); +void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition, u32 output_address, + ReplyType reply_type); void DoState(PointerWrap& p) { @@ -271,6 +274,7 @@ void DoState(PointerWrap& p) p.Do(s_pending_samples); p.Do(s_error_code); + p.Do(s_current_partition); p.Do(s_read_buffer_start_time); p.Do(s_read_buffer_end_time); @@ -309,8 +313,9 @@ static u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process) { if (s_audio_position >= s_current_start + s_current_length) { - DEBUG_LOG(DVDINTERFACE, "AdvanceDTK: NextStart=%08" PRIx64 ", NextLength=%08x, " - "CurrentStart=%08" PRIx64 ", CurrentLength=%08x, AudioPos=%08" PRIx64, + DEBUG_LOG(DVDINTERFACE, + "AdvanceDTK: NextStart=%08" PRIx64 ", NextLength=%08x, " + "CurrentStart=%08" PRIx64 ", CurrentLength=%08x, AudioPos=%08" PRIx64, s_next_start, s_next_length, s_current_start, s_current_length, s_audio_position); s_audio_position = s_next_start; @@ -362,7 +367,8 @@ static void DTKStreamingCallback(const std::vector& audio_data, s64 cycles_l ticks_to_dtk -= cycles_late; if (read_length > 0) { - DVDThread::StartRead(read_offset, read_length, false, ReplyType::DTK, ticks_to_dtk); + DVDThread::StartRead(read_offset, read_length, DiscIO::PARTITION_NONE, ReplyType::DTK, + ticks_to_dtk); } else { @@ -432,6 +438,9 @@ void Shutdown() void SetDisc(std::unique_ptr disc) { + if (disc) + s_current_partition = disc->GetGamePartition(); + DVDThread::SetDisc(std::move(disc)); SetLidOpen(); } @@ -496,9 +505,33 @@ void SetLidOpen() GenerateDIInterrupt(INT_CVRINT); } -bool ChangePartition(u64 offset) +bool UpdateRunningGameMetadata(u64 title_id) { - return DVDThread::ChangePartition(offset); + if (!DVDThread::HasDisc()) + return false; + + const DiscIO::Partition& partition = DVDThread::GetDiscType() == DiscIO::Platform::WII_DISC ? + s_current_partition : + DiscIO::PARTITION_NONE; + + return DVDThread::UpdateRunningGameMetadata(partition, title_id); +} + +bool UpdateRunningGameMetadata() +{ + if (!DVDThread::HasDisc()) + return false; + + const DiscIO::Partition& partition = DVDThread::GetDiscType() == DiscIO::Platform::WII_DISC ? + s_current_partition : + DiscIO::PARTITION_NONE; + + return DVDThread::UpdateRunningGameMetadata(partition); +} + +void ChangePartition(const DiscIO::Partition& partition) +{ + s_current_partition = partition; } void RegisterMMIO(MMIO::Mapping* mmio, u32 base) @@ -618,8 +651,9 @@ void WriteImmediate(u32 value, u32 output_address, bool reply_to_ios) } // Iff false is returned, ScheduleEvent must be used to finish executing the command -bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length, - bool decrypt, ReplyType reply_type, DIInterruptType* interrupt_type) +bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length, u32 output_length, + const DiscIO::Partition& partition, ReplyType reply_type, + DIInterruptType* interrupt_type) { if (!IsDiscInside()) { @@ -634,14 +668,14 @@ bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 *interrupt_type = INT_TCINT; } - if (DVD_length > output_length) + if (dvd_length > output_length) { WARN_LOG(DVDINTERFACE, "Detected an attempt to read more data from the DVD " "than what fits inside the out buffer. Clamping."); - DVD_length = output_length; + dvd_length = output_length; } - ScheduleReads(DVD_offset, DVD_length, decrypt, output_address, reply_type); + ScheduleReads(dvd_offset, dvd_length, partition, output_address, reply_type); return true; } @@ -677,8 +711,9 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr // Only seems to be used from WII_IPC, not through direct access case DVDLowReadDiskID: INFO_LOG(DVDINTERFACE, "DVDLowReadDiskID"); - command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, - reply_type, &interrupt_type); + command_handled_by_thread = + ExecuteReadCommand(0, output_address, 0x20, output_length, DiscIO::PARTITION_NONE, + reply_type, &interrupt_type); break; // Only used from WII_IPC. This is the only read command that decrypts data @@ -686,8 +721,8 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr INFO_LOG(DVDINTERFACE, "DVDLowRead: DVDAddr: 0x%09" PRIx64 ", Size: 0x%x", (u64)command_2 << 2, command_1); command_handled_by_thread = - ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true, - reply_type, &interrupt_type); + ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, + s_current_partition, reply_type, &interrupt_type); break; // Probably only used by Wii @@ -768,8 +803,8 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr (((command_2 + command_1) > 0x7ed40000) && (command_2 + command_1) < 0x7ed40008))) { command_handled_by_thread = - ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false, - reply_type, &interrupt_type); + ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, + DiscIO::PARTITION_NONE, reply_type, &interrupt_type); } else { @@ -813,19 +848,22 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr { u64 iDVDOffset = (u64)command_1 << 2; - INFO_LOG(DVDINTERFACE, "Read: DVDOffset=%08" PRIx64 - ", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x", + INFO_LOG(DVDINTERFACE, + "Read: DVDOffset=%08" PRIx64 + ", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x", iDVDOffset, output_address, command_2, output_length); - command_handled_by_thread = ExecuteReadCommand( - iDVDOffset, output_address, command_2, output_length, false, reply_type, &interrupt_type); + command_handled_by_thread = + ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, + DiscIO::PARTITION_NONE, reply_type, &interrupt_type); } break; case 0x40: // Read DiscID INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address)); - command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, - reply_type, &interrupt_type); + command_handled_by_thread = + ExecuteReadCommand(0, output_address, 0x20, output_length, DiscIO::PARTITION_NONE, + reply_type, &interrupt_type); break; default: @@ -934,9 +972,10 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr switch (command_0 >> 16 & 0xFF) { case 0x00: // Returns streaming status - INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status " - "AudioPos:%08" PRIx64 "/%08" PRIx64 " " - "CurrentStart:%08" PRIx64 " CurrentLength:%08x", + INFO_LOG(DVDINTERFACE, + "(Audio): Stream Status: Request Audio status " + "AudioPos:%08" PRIx64 "/%08" PRIx64 " " + "CurrentStart:%08" PRIx64 " CurrentLength:%08x", s_audio_position, s_current_start + s_current_length, s_current_start, s_current_length); WriteImmediate(s_stream ? 1 : 0, output_address, reply_to_ios); @@ -1097,7 +1136,8 @@ void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type // Determines from a given read request how much of the request is buffered, // and how much is required to be read from disc. -void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, ReplyType reply_type) +void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition, u32 output_address, + ReplyType reply_type) { // The drive continues to read 1 MiB beyond the last read position when idle. // If a future read falls within this window, part of the read may be returned @@ -1124,9 +1164,7 @@ void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, Rep // The variable dvd_offset tracks the actual offset on the DVD // that the disc drive starts reading at, which differs in two ways: // It's rounded to a whole ECC block and never uses Wii partition addressing. - u64 dvd_offset = offset; - if (decrypt) - dvd_offset = DVDThread::PartitionOffsetToRawOffset(offset); + u64 dvd_offset = DiscIO::CVolumeWiiCrypted::PartitionOffsetToRawOffset(offset, partition); dvd_offset = Common::AlignDown(dvd_offset, DVD_ECC_BLOCK_SIZE); if (SConfig::GetInstance().bFastDiscSpeed) @@ -1192,8 +1230,9 @@ void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, Rep u32 buffered_blocks = 0; u32 unbuffered_blocks = 0; - const u32 bytes_per_chunk = - decrypt ? DiscIO::CVolumeWiiCrypted::BLOCK_DATA_SIZE : DVD_ECC_BLOCK_SIZE; + const u32 bytes_per_chunk = partition == DiscIO::PARTITION_NONE ? + DVD_ECC_BLOCK_SIZE : + DiscIO::CVolumeWiiCrypted::BLOCK_DATA_SIZE; while (length > 0) { @@ -1240,7 +1279,7 @@ void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, Rep // Schedule this read to complete at the appropriate time const ReplyType chunk_reply_type = chunk_length == length ? reply_type : ReplyType::NoReply; - DVDThread::StartReadToEmulatedRAM(output_address, offset, chunk_length, decrypt, + DVDThread::StartReadToEmulatedRAM(output_address, offset, chunk_length, partition, chunk_reply_type, ticks_until_completion); // Advance the read window @@ -1280,8 +1319,9 @@ void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, Rep s_read_buffer_end_offset - s_read_buffer_start_offset, wii_disc)); } - DEBUG_LOG(DVDINTERFACE, "Schedule reads: ECC blocks unbuffered=%d, buffered=%d, " - "ticks=%" PRId64 ", time=%" PRId64 " us", + DEBUG_LOG(DVDINTERFACE, + "Schedule reads: ECC blocks unbuffered=%d, buffered=%d, " + "ticks=%" PRId64 ", time=%" PRId64 " us", unbuffered_blocks, buffered_blocks, ticks_until_completion, ticks_until_completion * 1000000 / SystemTimers::GetTicksPerSecond()); } diff --git a/Source/Core/Core/HW/DVD/DVDInterface.h b/Source/Core/Core/HW/DVD/DVDInterface.h index 977e92c9fd..d3c8ba5ad8 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.h +++ b/Source/Core/Core/HW/DVD/DVDInterface.h @@ -13,6 +13,7 @@ class PointerWrap; namespace DiscIO { class IVolume; +struct Partition; } namespace MMIO { @@ -113,9 +114,16 @@ bool IsDiscInside(); void ChangeDiscAsHost(const std::string& new_path); // Can only be called by the host thread void ChangeDiscAsCPU(const std::string& new_path); // Can only be called by the CPU thread +// If a disc is inserted and its title ID is equal to the title_id argument, returns true and +// calls SConfig::SetRunningGameMetadata(IVolume&, Partition&). Otherwise, returns false. +bool UpdateRunningGameMetadata(u64 title_id); +// If a disc is inserted, returns true and calls +// SConfig::SetRunningGameMetadata(IVolume&, Partition&). Otherwise, returns false. +bool UpdateRunningGameMetadata(); + // Direct access to DI for IOS HLE (simpler to implement than how real IOS accesses DI, // and lets us skip encrypting/decrypting in some cases) -bool ChangePartition(u64 offset); +void ChangePartition(const DiscIO::Partition& partition); void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length, bool reply_to_ios); diff --git a/Source/Core/Core/HW/DVD/DVDThread.cpp b/Source/Core/Core/HW/DVD/DVDThread.cpp index 3040c56a8c..bc7bf60447 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.cpp +++ b/Source/Core/Core/HW/DVD/DVDThread.cpp @@ -41,7 +41,7 @@ struct ReadRequest u32 output_address; u64 dvd_offset; u32 length; - bool decrypt; + DiscIO::Partition partition; // This determines which code DVDInterface will run to reply // to the emulated software. We can't use callbacks, @@ -68,8 +68,8 @@ static void DVDThread(); static void WaitUntilIdle(); static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, - bool decrypt, DVDInterface::ReplyType reply_type, - s64 ticks_until_completion); + const DiscIO::Partition& partition, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion); static void FinishRead(u64 id, s64 cycles_late); static CoreTiming::EventType* s_finish_read; @@ -191,42 +191,25 @@ bool HasDisc() return s_disc != nullptr; } -u64 PartitionOffsetToRawOffset(u64 offset) -{ - // This is thread-safe as long as the partition currently isn't being changed, - // and that isn't supposed to be happening while running this function, because both - // this function and ChangePartition are only supposed to be called on the CPU thread. - _assert_(Core::IsCPUThread()); - return s_disc->PartitionOffsetToRawOffset(offset); -} - DiscIO::Platform GetDiscType() { // GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary. return s_disc->GetVolumeType(); } -IOS::ES::TMDReader GetTMD() +IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition) { WaitUntilIdle(); - return s_disc->GetTMD(); + return s_disc->GetTMD(partition); } -IOS::ES::TicketReader GetTicket() +IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition) { WaitUntilIdle(); - return s_disc->GetTicket(); + return s_disc->GetTicket(partition); } -bool ChangePartition(u64 offset) -{ - WaitUntilIdle(); - const bool success = s_disc->ChangePartition(offset); - FileMonitor::SetFileSystem(s_disc.get()); - return success; -} - -bool UpdateRunningGameMetadata(u64 title_id) +bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, u64 title_id) { if (!s_disc) return false; @@ -234,24 +217,24 @@ bool UpdateRunningGameMetadata(u64 title_id) WaitUntilIdle(); u64 volume_title_id; - if (!s_disc->GetTitleID(&volume_title_id)) + if (!s_disc->GetTitleID(&volume_title_id, partition)) return false; if (volume_title_id != title_id) return false; - SConfig::GetInstance().SetRunningGameMetadata(*s_disc); + SConfig::GetInstance().SetRunningGameMetadata(*s_disc, partition); return true; } -bool UpdateRunningGameMetadata() +bool UpdateRunningGameMetadata(const DiscIO::Partition& partition) { if (!s_disc) return false; DVDThread::WaitUntilIdle(); - SConfig::GetInstance().SetRunningGameMetadata(*s_disc); + SConfig::GetInstance().SetRunningGameMetadata(*s_disc, partition); return true; } @@ -266,22 +249,23 @@ void WaitUntilIdle() StartDVDThread(); } -void StartRead(u64 dvd_offset, u32 length, bool decrypt, DVDInterface::ReplyType reply_type, - s64 ticks_until_completion) +void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion) { - StartReadInternal(false, 0, dvd_offset, length, decrypt, reply_type, ticks_until_completion); + StartReadInternal(false, 0, dvd_offset, length, partition, reply_type, ticks_until_completion); } -void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, bool decrypt, - DVDInterface::ReplyType reply_type, s64 ticks_until_completion) +void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, + const DiscIO::Partition& partition, DVDInterface::ReplyType reply_type, + s64 ticks_until_completion) { - StartReadInternal(true, output_address, dvd_offset, length, decrypt, reply_type, + StartReadInternal(true, output_address, dvd_offset, length, partition, reply_type, ticks_until_completion); } static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, - bool decrypt, DVDInterface::ReplyType reply_type, - s64 ticks_until_completion) + const DiscIO::Partition& partition, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion) { _assert_(Core::IsCPUThread()); @@ -291,7 +275,7 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs request.output_address = output_address; request.dvd_offset = dvd_offset; request.length = length; - request.decrypt = decrypt; + request.partition = partition; request.reply_type = reply_type; u64 id = s_next_id++; @@ -381,10 +365,10 @@ static void DVDThread() ReadRequest request; while (s_request_queue.Pop(request)) { - FileMonitor::Log(request.dvd_offset, request.decrypt); + FileMonitor::Log(request.dvd_offset, request.partition); std::vector buffer(request.length); - if (!s_disc->Read(request.dvd_offset, request.length, buffer.data(), request.decrypt)) + if (!s_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition)) buffer.resize(0); request.realtime_done_us = Common::Timer::GetTimeUs(); diff --git a/Source/Core/Core/HW/DVD/DVDThread.h b/Source/Core/Core/HW/DVD/DVDThread.h index d7867d54bb..cc10480e92 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.h +++ b/Source/Core/Core/HW/DVD/DVDThread.h @@ -10,6 +10,10 @@ #include "Common/CommonTypes.h" class PointerWrap; +namespace DiscIO +{ +struct Partition; +} namespace DVDInterface { enum class ReplyType : u32; @@ -37,20 +41,19 @@ void DoState(PointerWrap& p); void SetDisc(std::unique_ptr disc); bool HasDisc(); -u64 PartitionOffsetToRawOffset(u64 offset); DiscIO::Platform GetDiscType(); -IOS::ES::TMDReader GetTMD(); -IOS::ES::TicketReader GetTicket(); -bool ChangePartition(u64 offset); +IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition); +IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition); // If a disc is inserted and its title ID is equal to the title_id argument, returns true and -// calls SConfig::SetRunningGameMetadata(IVolume&). Otherwise, returns false. -bool UpdateRunningGameMetadata(u64 title_id); +// calls SConfig::SetRunningGameMetadata(IVolume&, Partition&). Otherwise, returns false. +bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, u64 title_id); // If a disc is inserted, returns true and calls -// SConfig::SetRunningGameMetadata(IVolume&). Otherwise, returns false. -bool UpdateRunningGameMetadata(); +// SConfig::SetRunningGameMetadata(IVolume&, Partition&). Otherwise, returns false. +bool UpdateRunningGameMetadata(const DiscIO::Partition& partition); -void StartRead(u64 dvd_offset, u32 length, bool decrypt, DVDInterface::ReplyType reply_type, - s64 ticks_until_completion); -void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, bool decrypt, - DVDInterface::ReplyType reply_type, s64 ticks_until_completion); +void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition, + DVDInterface::ReplyType reply_type, s64 ticks_until_completion); +void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, + const DiscIO::Partition& partition, DVDInterface::ReplyType reply_type, + s64 ticks_until_completion); } diff --git a/Source/Core/Core/HW/DVD/FileMonitor.cpp b/Source/Core/Core/HW/DVD/FileMonitor.cpp index 93449abce3..961b351623 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.cpp +++ b/Source/Core/Core/HW/DVD/FileMonitor.cpp @@ -21,9 +21,10 @@ namespace FileMonitor { -static const DiscIO::IVolume* s_volume = nullptr; -static bool s_wii_disc; +static const DiscIO::IVolume* s_volume; +static bool s_new_volume = false; static std::unique_ptr s_filesystem; +static DiscIO::Partition s_partition; static std::string s_previous_file; // Filtered files @@ -57,34 +58,31 @@ void SetFileSystem(const DiscIO::IVolume* volume) // Instead of creating the file system object right away, we will let Log // create it later once we know that it actually will get used s_volume = volume; - - // If the volume that was passed in was nullptr, Log won't try to create a - // file system object later, so we have to set s_filesystem to nullptr right away - s_filesystem = nullptr; + s_new_volume = true; } // Logs access to files in the file system set by SetFileSystem -void Log(u64 offset, bool decrypt) +void Log(u64 offset, const DiscIO::Partition& partition) { // Do nothing if the log isn't selected if (!LogManager::GetInstance()->IsEnabled(LogTypes::FILEMON, LogTypes::LWARNING)) return; - // If a new volume has been set, use the file system of that volume - if (s_volume) + // If the volume or partition changed, load the filesystem of the new partition + if (s_new_volume || s_partition != partition) { - s_wii_disc = s_volume->GetVolumeType() == DiscIO::Platform::WII_DISC; - if (decrypt != s_wii_disc) + // Wii discs don't have PARTITION_NONE filesystems, so let's not waste time trying to read one + const bool reading_from_partition = partition != DiscIO::PARTITION_NONE; + const bool is_wii_disc = s_volume->GetVolumeType() == DiscIO::Platform::WII_DISC; + if (reading_from_partition != is_wii_disc) return; - s_filesystem = DiscIO::CreateFileSystem(s_volume); - s_volume = nullptr; + + s_new_volume = false; + s_filesystem = DiscIO::CreateFileSystem(s_volume, partition); + s_partition = partition; s_previous_file.clear(); } - // For Wii discs, FileSystemGCWii will only load file systems from encrypted partitions - if (decrypt != s_wii_disc) - return; - // Do nothing if there is no valid file system if (!s_filesystem) return; diff --git a/Source/Core/Core/HW/DVD/FileMonitor.h b/Source/Core/Core/HW/DVD/FileMonitor.h index abfc01d3c5..17d04977f3 100644 --- a/Source/Core/Core/HW/DVD/FileMonitor.h +++ b/Source/Core/Core/HW/DVD/FileMonitor.h @@ -4,13 +4,11 @@ #pragma once -#include - #include "Common/CommonTypes.h" namespace DiscIO { -class IFileSystem; +struct Partition; class IVolume; } @@ -20,5 +18,5 @@ namespace FileMonitor // with nullptr, the volume must remain valid until the next SetFileSystem call. void SetFileSystem(const DiscIO::IVolume* volume); // Logs access to files in the file system set by SetFileSystem -void Log(u64 offset, bool decrypt); +void Log(u64 offset, const DiscIO::Partition& partition); } diff --git a/Source/Core/Core/IOS/DI/DI.cpp b/Source/Core/Core/IOS/DI/DI.cpp index 7e1878a1fc..0df9861054 100644 --- a/Source/Core/Core/IOS/DI/DI.cpp +++ b/Source/Core/Core/IOS/DI/DI.cpp @@ -101,16 +101,18 @@ IPCCommandResult DI::IOCtlV(const IOCtlVRequest& request) _dbg_assert_msg_(IOS_DI, request.in_vectors[2].address == 0, "DVDLowOpenPartition with cert chain"); - u64 const partition_offset = ((u64)Memory::Read_U32(request.in_vectors[0].address + 4) << 2); - DVDInterface::ChangePartition(partition_offset); + const u64 partition_offset = + static_cast(Memory::Read_U32(request.in_vectors[0].address + 4)) << 2; + const DiscIO::Partition partition(partition_offset); + DVDInterface::ChangePartition(partition); INFO_LOG(IOS_DI, "DVDLowOpenPartition: partition_offset 0x%016" PRIx64, partition_offset); // Read TMD to the buffer - const IOS::ES::TMDReader tmd = DVDThread::GetTMD(); + const IOS::ES::TMDReader tmd = DVDThread::GetTMD(partition); const std::vector raw_tmd = tmd.GetRawTMD(); Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size()); - ES::DIVerify(tmd, DVDThread::GetTicket()); + ES::DIVerify(tmd, DVDThread::GetTicket(partition)); return_value = 1; break; diff --git a/Source/Core/Core/IOS/MIOS.cpp b/Source/Core/Core/IOS/MIOS.cpp index 7242dceb78..9263dfd97e 100644 --- a/Source/Core/Core/IOS/MIOS.cpp +++ b/Source/Core/Core/IOS/MIOS.cpp @@ -19,7 +19,6 @@ #include "Core/HLE/HLE.h" #include "Core/HW/DSP.h" #include "Core/HW/DVD/DVDInterface.h" -#include "Core/HW/DVD/DVDThread.h" #include "Core/HW/Memmap.h" #include "Core/HW/SystemTimers.h" #include "Core/IOS/ES/Formats.h" @@ -170,7 +169,7 @@ bool Load() Memory::Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE); NOTICE_LOG(IOS, "IPL ready."); SConfig::GetInstance().m_BootType = SConfig::BOOT_MIOS; - DVDThread::UpdateRunningGameMetadata(); + DVDInterface::UpdateRunningGameMetadata(); return true; } } // namespace MIOS diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index fd24cd8092..915ae56104 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -71,7 +71,7 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -static const u32 STATE_VERSION = 85; // Last changed in PR 4241 +static const u32 STATE_VERSION = 86; // Last changed in PR 2353 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list, diff --git a/Source/Core/DiscIO/DiscScrubber.cpp b/Source/Core/DiscIO/DiscScrubber.cpp index e00311e86f..ac3a39f4c6 100644 --- a/Source/Core/DiscIO/DiscScrubber.cpp +++ b/Source/Core/DiscIO/DiscScrubber.cpp @@ -123,15 +123,15 @@ void DiscScrubber::MarkAsUsedE(u64 partition_data_offset, u64 offset, u64 size) } // Helper functions for reading the BE volume -bool DiscScrubber::ReadFromVolume(u64 offset, u32& buffer, bool decrypt) +bool DiscScrubber::ReadFromVolume(u64 offset, u32& buffer, const Partition& partition) { - return m_disc->ReadSwapped(offset, &buffer, decrypt); + return m_disc->ReadSwapped(offset, &buffer, partition); } -bool DiscScrubber::ReadFromVolume(u64 offset, u64& buffer, bool decrypt) +bool DiscScrubber::ReadFromVolume(u64 offset, u64& buffer, const Partition& partition) { u32 temp_buffer; - if (!m_disc->ReadSwapped(offset, &temp_buffer, decrypt)) + if (!m_disc->ReadSwapped(offset, &temp_buffer, partition)) return false; buffer = static_cast(temp_buffer) << 2; return true; @@ -142,126 +142,85 @@ bool DiscScrubber::ParseDisc() // Mark the header as used - it's mostly 0s anyways MarkAsUsed(0, 0x50000); - for (u32 x = 0; x < 4; x++) + for (const DiscIO::Partition& partition : m_disc->GetPartitions()) { - if (!ReadFromVolume(0x40000 + (x * 8) + 0, m_partition_group[x].num_partitions, false) || - !ReadFromVolume(0x40000 + (x * 8) + 4, m_partition_group[x].partitions_offset, false)) + PartitionHeader header; + + if (!ReadFromVolume(partition.offset + 0x2a4, header.tmd_size, PARTITION_NONE) || + !ReadFromVolume(partition.offset + 0x2a8, header.tmd_offset, PARTITION_NONE) || + !ReadFromVolume(partition.offset + 0x2ac, header.cert_chain_size, PARTITION_NONE) || + !ReadFromVolume(partition.offset + 0x2b0, header.cert_chain_offset, PARTITION_NONE) || + !ReadFromVolume(partition.offset + 0x2b4, header.h3_offset, PARTITION_NONE) || + !ReadFromVolume(partition.offset + 0x2b8, header.data_offset, PARTITION_NONE) || + !ReadFromVolume(partition.offset + 0x2bc, header.data_size, PARTITION_NONE)) { return false; } - // Read all partitions - for (u32 i = 0; i < m_partition_group[x].num_partitions; i++) - { - Partition partition; + MarkAsUsed(partition.offset, 0x2c0); - partition.group_number = x; - partition.number = i; + MarkAsUsed(partition.offset + header.tmd_offset, header.tmd_size); + MarkAsUsed(partition.offset + header.cert_chain_offset, header.cert_chain_size); + MarkAsUsed(partition.offset + header.h3_offset, 0x18000); + // This would mark the whole (encrypted) data area + // we need to parse FST and other crap to find what's free within it! + // MarkAsUsed(partition.offset + header.data_offset, header.data_size); - if (!ReadFromVolume(m_partition_group[x].partitions_offset + (i * 8) + 0, partition.offset, - false) || - !ReadFromVolume(m_partition_group[x].partitions_offset + (i * 8) + 4, partition.type, - false) || - !ReadFromVolume(partition.offset + 0x2a4, partition.header.tmd_size, false) || - !ReadFromVolume(partition.offset + 0x2a8, partition.header.tmd_offset, false) || - !ReadFromVolume(partition.offset + 0x2ac, partition.header.cert_chain_size, false) || - !ReadFromVolume(partition.offset + 0x2b0, partition.header.cert_chain_offset, false) || - !ReadFromVolume(partition.offset + 0x2b4, partition.header.h3_offset, false) || - !ReadFromVolume(partition.offset + 0x2b8, partition.header.data_offset, false) || - !ReadFromVolume(partition.offset + 0x2bc, partition.header.data_size, false)) - { - return false; - } - - m_partition_group[x].partitions.push_back(partition); - } - - for (auto& partition : m_partition_group[x].partitions) - { - const PartitionHeader& header = partition.header; - - MarkAsUsed(partition.offset, 0x2c0); - - MarkAsUsed(partition.offset + header.tmd_offset, header.tmd_size); - MarkAsUsed(partition.offset + header.cert_chain_offset, header.cert_chain_size); - MarkAsUsed(partition.offset + header.h3_offset, 0x18000); - // This would mark the whole (encrypted) data area - // we need to parse FST and other crap to find what's free within it! - // MarkAsUsed(partition.offset + header.data_offset, header.data_size); - - // Parse Data! This is where the big gain is - if (!ParsePartitionData(partition)) - return false; - } + // Parse Data! This is where the big gain is + if (!ParsePartitionData(partition, &header)) + return false; } return true; } -// Operations dealing with encrypted space are done here - the volume is swapped to allow this -bool DiscScrubber::ParsePartitionData(Partition& partition) +// Operations dealing with encrypted space are done here +bool DiscScrubber::ParsePartitionData(const Partition& partition, PartitionHeader* header) { - bool parsed_ok = true; - - // Switch out the main volume temporarily - std::unique_ptr old_volume; - m_disc.swap(old_volume); - - // Ready some stuff - m_disc = CreateVolumeFromFilename(m_filename, partition.group_number, partition.number); - if (m_disc == nullptr) + std::unique_ptr filesystem(CreateFileSystem(m_disc.get(), partition)); + if (!filesystem) { - ERROR_LOG(DISCIO, "Failed to create volume from file %s", m_filename.c_str()); - m_disc.swap(old_volume); + ERROR_LOG(DISCIO, "Failed to read file system for the partition at 0x%" PRIx64, + partition.offset); return false; } - std::unique_ptr filesystem(CreateFileSystem(m_disc.get())); - if (!filesystem) + const u64 partition_data_offset = partition.offset + header->data_offset; + + // Mark things as used which are not in the filesystem + // Header, Header Information, Apploader + if (!ReadFromVolume(0x2440 + 0x14, header->apploader_size, partition) || + !ReadFromVolume(0x2440 + 0x18, header->apploader_size, partition)) { - ERROR_LOG(DISCIO, "Failed to create filesystem for group %u partition %u", - partition.group_number, partition.number); - parsed_ok = false; + return false; } - else + MarkAsUsedE(partition_data_offset, 0, + 0x2440 + header->apploader_size + header->apploader_trailer_size); + + // DOL + header->dol_offset = filesystem->GetBootDOLOffset(); + header->dol_size = filesystem->GetBootDOLSize(header->dol_offset); + if (header->dol_offset == 0 || header->dol_size == 0) + return false; + MarkAsUsedE(partition_data_offset, header->dol_offset, header->dol_size); + + // FST + if (!ReadFromVolume(0x424, header->fst_offset, partition) || + !ReadFromVolume(0x428, header->fst_size, partition)) { - // Mark things as used which are not in the filesystem - // Header, Header Information, Apploader - parsed_ok = parsed_ok && ReadFromVolume(0x2440 + 0x14, partition.header.apploader_size, true); - parsed_ok = - parsed_ok && ReadFromVolume(0x2440 + 0x18, partition.header.apploader_trailer_size, true); - MarkAsUsedE(partition.offset + partition.header.data_offset, 0, - 0x2440 + partition.header.apploader_size + partition.header.apploader_trailer_size); + return false; + } + MarkAsUsedE(partition_data_offset, header->fst_offset, header->fst_size); - // DOL - partition.header.dol_offset = filesystem->GetBootDOLOffset(); - partition.header.dol_size = filesystem->GetBootDOLSize(partition.header.dol_offset); - parsed_ok = parsed_ok && partition.header.dol_offset && partition.header.dol_size; - MarkAsUsedE(partition.offset + partition.header.data_offset, partition.header.dol_offset, - partition.header.dol_size); - - // FST - parsed_ok = parsed_ok && ReadFromVolume(0x424, partition.header.fst_offset, true); - parsed_ok = parsed_ok && ReadFromVolume(0x428, partition.header.fst_size, true); - MarkAsUsedE(partition.offset + partition.header.data_offset, partition.header.fst_offset, - partition.header.fst_size); - - // Go through the filesystem and mark entries as used - for (const SFileInfo& file : filesystem->GetFileList()) - { - DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str()); - if ((file.m_NameOffset & 0x1000000) == 0) - { - MarkAsUsedE(partition.offset + partition.header.data_offset, file.m_Offset, - file.m_FileSize); - } - } + // Go through the filesystem and mark entries as used + for (const SFileInfo& file : filesystem->GetFileList()) + { + DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str()); + if ((file.m_NameOffset & 0x1000000) == 0) + MarkAsUsedE(partition_data_offset, file.m_Offset, file.m_FileSize); } - // Swap back - m_disc.swap(old_volume); - - return parsed_ok; + return true; } } // namespace DiscIO diff --git a/Source/Core/DiscIO/DiscScrubber.h b/Source/Core/DiscIO/DiscScrubber.h index 152e16f123..959d22fd4a 100644 --- a/Source/Core/DiscIO/DiscScrubber.h +++ b/Source/Core/DiscIO/DiscScrubber.h @@ -26,6 +26,7 @@ class IOFile; namespace DiscIO { class IVolume; +struct Partition; class DiscScrubber final { @@ -57,34 +58,16 @@ private: u32 apploader_trailer_size; }; - struct Partition final - { - u32 group_number; - u32 number; - u64 offset; - u32 type; - PartitionHeader header; - }; - - struct PartitionGroup final - { - u32 num_partitions; - u64 partitions_offset; - std::vector partitions; - }; - void MarkAsUsed(u64 offset, u64 size); void MarkAsUsedE(u64 partition_data_offset, u64 offset, u64 size); - bool ReadFromVolume(u64 offset, u32& buffer, bool decrypt); - bool ReadFromVolume(u64 offset, u64& buffer, bool decrypt); + bool ReadFromVolume(u64 offset, u32& buffer, const Partition& partition); + bool ReadFromVolume(u64 offset, u64& buffer, const Partition& partition); bool ParseDisc(); - bool ParsePartitionData(Partition& partition); + bool ParsePartitionData(const Partition& partition, PartitionHeader* header); std::string m_filename; std::unique_ptr m_disc; - std::array m_partition_group{}; - std::vector m_free_table; u64 m_file_size = 0; u64 m_block_count = 0; diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index b19fd7cf95..ec4f8c2c28 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -20,8 +20,8 @@ namespace DiscIO { -CFileSystemGCWii::CFileSystemGCWii(const IVolume* _rVolume) - : IFileSystem(_rVolume), m_Initialized(false), m_Valid(false), m_Wii(false) +CFileSystemGCWii::CFileSystemGCWii(const IVolume* _rVolume, const Partition& partition) + : IFileSystem(_rVolume, partition), m_Initialized(false), m_Valid(false), m_Wii(false) { m_Valid = DetectFileSystem(); } @@ -80,7 +80,7 @@ u64 CFileSystemGCWii::ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64 read_length, _OffsetInFile, _rFullPath.c_str(), pFileInfo->m_Offset, pFileInfo->m_FileSize); - m_rVolume->Read(pFileInfo->m_Offset + _OffsetInFile, read_length, _pBuffer, m_Wii); + m_rVolume->Read(pFileInfo->m_Offset + _OffsetInFile, read_length, _pBuffer, m_partition); return read_length; } @@ -111,7 +111,7 @@ bool CFileSystemGCWii::ExportFile(const std::string& _rFullPath, std::vector buffer(readSize); - result = m_rVolume->Read(fileOffset, readSize, &buffer[0], m_Wii); + result = m_rVolume->Read(fileOffset, readSize, &buffer[0], m_partition); if (!result) break; @@ -130,14 +130,14 @@ bool CFileSystemGCWii::ExportApploader(const std::string& _rExportFolder) const u32 apploader_size; u32 trailer_size; const u32 header_size = 0x20; - if (!m_rVolume->ReadSwapped(0x2440 + 0x14, &apploader_size, m_Wii) || - !m_rVolume->ReadSwapped(0x2440 + 0x18, &trailer_size, m_Wii)) + if (!m_rVolume->ReadSwapped(0x2440 + 0x14, &apploader_size, m_partition) || + !m_rVolume->ReadSwapped(0x2440 + 0x18, &trailer_size, m_partition)) return false; apploader_size += trailer_size + header_size; DEBUG_LOG(DISCIO, "Apploader size -> %x", apploader_size); std::vector buffer(apploader_size); - if (m_rVolume->Read(0x2440, apploader_size, buffer.data(), m_Wii)) + if (m_rVolume->Read(0x2440, apploader_size, buffer.data(), m_partition)) { std::string exportName(_rExportFolder + "/apploader.img"); @@ -155,7 +155,7 @@ bool CFileSystemGCWii::ExportApploader(const std::string& _rExportFolder) const u64 CFileSystemGCWii::GetBootDOLOffset() const { u32 offset = 0; - m_rVolume->ReadSwapped(0x420, &offset, m_Wii); + m_rVolume->ReadSwapped(0x420, &offset, m_partition); return static_cast(offset) << GetOffsetShift(); } @@ -173,8 +173,8 @@ u32 CFileSystemGCWii::GetBootDOLSize(u64 dol_offset) const // Iterate through the 7 code segments for (u8 i = 0; i < 7; i++) { - if (!m_rVolume->ReadSwapped(dol_offset + 0x00 + i * 4, &offset, m_Wii) || - !m_rVolume->ReadSwapped(dol_offset + 0x90 + i * 4, &size, m_Wii)) + if (!m_rVolume->ReadSwapped(dol_offset + 0x00 + i * 4, &offset, m_partition) || + !m_rVolume->ReadSwapped(dol_offset + 0x90 + i * 4, &size, m_partition)) return 0; dol_size = std::max(offset + size, dol_size); } @@ -182,8 +182,8 @@ u32 CFileSystemGCWii::GetBootDOLSize(u64 dol_offset) const // Iterate through the 11 data segments for (u8 i = 0; i < 11; i++) { - if (!m_rVolume->ReadSwapped(dol_offset + 0x1c + i * 4, &offset, m_Wii) || - !m_rVolume->ReadSwapped(dol_offset + 0xac + i * 4, &size, m_Wii)) + if (!m_rVolume->ReadSwapped(dol_offset + 0x1c + i * 4, &offset, m_partition) || + !m_rVolume->ReadSwapped(dol_offset + 0xac + i * 4, &size, m_partition)) return 0; dol_size = std::max(offset + size, dol_size); } @@ -200,7 +200,7 @@ bool CFileSystemGCWii::ExportDOL(const std::string& _rExportFolder) const return false; std::vector buffer(DolSize); - if (m_rVolume->Read(DolOffset, DolSize, &buffer[0], m_Wii)) + if (m_rVolume->Read(DolOffset, DolSize, &buffer[0], m_partition)) { std::string exportName(_rExportFolder + "/boot.dol"); @@ -218,7 +218,7 @@ bool CFileSystemGCWii::ExportDOL(const std::string& _rExportFolder) const std::string CFileSystemGCWii::GetStringFromOffset(u64 _Offset) const { std::string data(255, 0x00); - m_rVolume->Read(_Offset, data.size(), (u8*)&data[0], m_Wii); + m_rVolume->Read(_Offset, data.size(), (u8*)&data[0], m_partition); data.erase(std::find(data.begin(), data.end(), 0x00), data.end()); // TODO: Should we really always use SHIFT-JIS? @@ -251,12 +251,12 @@ const SFileInfo* CFileSystemGCWii::FindFileInfo(const std::string& _rFullPath) bool CFileSystemGCWii::DetectFileSystem() { u32 magic_bytes; - if (m_rVolume->ReadSwapped(0x18, &magic_bytes, false) && magic_bytes == 0x5D1C9EA3) + if (m_rVolume->ReadSwapped(0x18, &magic_bytes, m_partition) && magic_bytes == 0x5D1C9EA3) { m_Wii = true; return true; } - else if (m_rVolume->ReadSwapped(0x1c, &magic_bytes, false) && magic_bytes == 0xC2339F3D) + else if (m_rVolume->ReadSwapped(0x1c, &magic_bytes, m_partition) && magic_bytes == 0xC2339F3D) { m_Wii = false; return true; @@ -272,15 +272,15 @@ void CFileSystemGCWii::InitFileSystem() // read the whole FST u32 fst_offset_unshifted; - if (!m_rVolume->ReadSwapped(0x424, &fst_offset_unshifted, m_Wii)) + if (!m_rVolume->ReadSwapped(0x424, &fst_offset_unshifted, m_partition)) return; u64 FSTOffset = static_cast(fst_offset_unshifted) << shift; // read all fileinfos u32 name_offset, offset, size; - if (!m_rVolume->ReadSwapped(FSTOffset + 0x0, &name_offset, m_Wii) || - !m_rVolume->ReadSwapped(FSTOffset + 0x4, &offset, m_Wii) || - !m_rVolume->ReadSwapped(FSTOffset + 0x8, &size, m_Wii)) + if (!m_rVolume->ReadSwapped(FSTOffset + 0x0, &name_offset, m_partition) || + !m_rVolume->ReadSwapped(FSTOffset + 0x4, &offset, m_partition) || + !m_rVolume->ReadSwapped(FSTOffset + 0x8, &size, m_partition)) return; SFileInfo root = {name_offset, static_cast(offset) << shift, size}; @@ -308,11 +308,11 @@ void CFileSystemGCWii::InitFileSystem() { const u64 read_offset = FSTOffset + (i * 0xC); name_offset = 0; - m_rVolume->ReadSwapped(read_offset + 0x0, &name_offset, m_Wii); + m_rVolume->ReadSwapped(read_offset + 0x0, &name_offset, m_partition); offset = 0; - m_rVolume->ReadSwapped(read_offset + 0x4, &offset, m_Wii); + m_rVolume->ReadSwapped(read_offset + 0x4, &offset, m_partition); size = 0; - m_rVolume->ReadSwapped(read_offset + 0x8, &size, m_Wii); + m_rVolume->ReadSwapped(read_offset + 0x8, &size, m_partition); m_FileInfoVector.emplace_back(name_offset, static_cast(offset) << shift, size); NameTableOffset += 0xC; } diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index ac75f1acf0..148aa4dd4b 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -14,11 +14,12 @@ namespace DiscIO { class IVolume; +struct Partition; class CFileSystemGCWii : public IFileSystem { public: - CFileSystemGCWii(const IVolume* _rVolume); + CFileSystemGCWii(const IVolume* _rVolume, const Partition& partition); virtual ~CFileSystemGCWii(); bool IsValid() const override { return m_Valid; } diff --git a/Source/Core/DiscIO/Filesystem.cpp b/Source/Core/DiscIO/Filesystem.cpp index 48c9a33b6f..9670ecda43 100644 --- a/Source/Core/DiscIO/Filesystem.cpp +++ b/Source/Core/DiscIO/Filesystem.cpp @@ -5,10 +5,12 @@ #include "DiscIO/Filesystem.h" #include #include "DiscIO/FileSystemGCWii.h" +#include "DiscIO/Volume.h" namespace DiscIO { -IFileSystem::IFileSystem(const IVolume* _rVolume) : m_rVolume(_rVolume) +IFileSystem::IFileSystem(const IVolume* _rVolume, const Partition& partition) + : m_rVolume(_rVolume), m_partition(partition) { } @@ -16,12 +18,12 @@ IFileSystem::~IFileSystem() { } -std::unique_ptr CreateFileSystem(const IVolume* volume) +std::unique_ptr CreateFileSystem(const IVolume* volume, const Partition& partition) { if (!volume) return nullptr; - std::unique_ptr filesystem = std::make_unique(volume); + std::unique_ptr filesystem = std::make_unique(volume, partition); if (!filesystem) return nullptr; diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index 25a414d980..71b19e6005 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -9,11 +9,10 @@ #include #include "Common/CommonTypes.h" +#include "DiscIO/Volume.h" namespace DiscIO { -class IVolume; - // file info of an FST entry struct SFileInfo { @@ -35,7 +34,7 @@ struct SFileInfo class IFileSystem { public: - IFileSystem(const IVolume* _rVolume); + IFileSystem(const IVolume* _rVolume, const Partition& partition); virtual ~IFileSystem(); virtual bool IsValid() const = 0; @@ -50,10 +49,12 @@ public: virtual u64 GetBootDOLOffset() const = 0; virtual u32 GetBootDOLSize(u64 dol_offset) const = 0; + virtual const Partition GetPartition() const { return m_partition; } protected: - const IVolume* m_rVolume; + const IVolume* const m_rVolume; + const Partition m_partition; }; -std::unique_ptr CreateFileSystem(const IVolume* volume); +std::unique_ptr CreateFileSystem(const IVolume* volume, const Partition& partition); } // namespace diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index cd48249ccb..d03e168709 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -19,49 +20,71 @@ namespace DiscIO { enum class BlobType; +struct Partition final +{ + Partition() : offset(std::numeric_limits::max()) {} + explicit Partition(u64 offset_) : offset(offset_) {} + bool operator==(const Partition& other) const { return offset == other.offset; } + bool operator!=(const Partition& other) const { return !(*this == other); } + bool operator<(const Partition& other) const { return offset < other.offset; } + bool operator>(const Partition& other) const { return other < *this; } + bool operator<=(const Partition& other) const { return !(*this < other); } + bool operator>=(const Partition& other) const { return !(*this > other); } + u64 offset; +}; + +const Partition PARTITION_NONE(std::numeric_limits::max() - 1); + class IVolume { public: IVolume() {} virtual ~IVolume() {} - // decrypt parameter must be false if not reading a Wii disc - virtual bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const = 0; + virtual bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const = 0; template - bool ReadSwapped(u64 offset, T* buffer, bool decrypt) const + bool ReadSwapped(u64 offset, T* buffer, const Partition& partition) const { T temp; - if (!Read(offset, sizeof(T), reinterpret_cast(&temp), decrypt)) + if (!Read(offset, sizeof(T), reinterpret_cast(&temp), partition)) return false; *buffer = Common::FromBigEndian(temp); return true; } - - virtual bool GetTitleID(u64*) const { return false; } - virtual IOS::ES::TicketReader GetTicket() const { return {}; } - virtual IOS::ES::TMDReader GetTMD() const { return {}; } - virtual u64 PartitionOffsetToRawOffset(u64 offset) const { return offset; } - virtual std::string GetGameID() const = 0; - virtual std::string GetMakerID() const = 0; - virtual u16 GetRevision() const = 0; - virtual std::string GetInternalName() const = 0; + virtual std::vector GetPartitions() const { return {{}}; } + virtual Partition GetGamePartition() const { return PARTITION_NONE; } + bool GetTitleID(u64* buffer) const { return GetTitleID(buffer, GetGamePartition()); } + virtual bool GetTitleID(u64* buffer, const Partition& partition) const { return false; } + virtual IOS::ES::TicketReader GetTicket(const Partition& partition) const { return {}; } + virtual IOS::ES::TMDReader GetTMD(const Partition& partition) const { return {}; } + std::string GetGameID() const { return GetGameID(GetGamePartition()); } + virtual std::string GetGameID(const Partition& partition) const = 0; + std::string GetMakerID() const { return GetMakerID(GetGamePartition()); } + virtual std::string GetMakerID(const Partition& partition) const = 0; + u16 GetRevision() const { return GetRevision(GetGamePartition()); } + virtual u16 GetRevision(const Partition& partition) const = 0; + std::string GetInternalName() const { return GetInternalName(GetGamePartition()); } + virtual std::string GetInternalName(const Partition& partition) const = 0; virtual std::map GetShortNames() const { return {{}}; } virtual std::map GetLongNames() const { return {{}}; } virtual std::map GetShortMakers() const { return {{}}; } virtual std::map GetLongMakers() const { return {{}}; } virtual std::map GetDescriptions() const { return {{}}; } virtual std::vector GetBanner(int* width, int* height) const = 0; - virtual u64 GetFSTSize() const = 0; - virtual std::string GetApploaderDate() const = 0; + u64 GetFSTSize() const { return GetFSTSize(GetGamePartition()); } + virtual u64 GetFSTSize(const Partition& partition) const = 0; + std::string GetApploaderDate() const { return GetApploaderDate(GetGamePartition()); } + virtual std::string GetApploaderDate(const Partition& partition) const = 0; // 0 is the first disc, 1 is the second disc - virtual u8 GetDiscNumber() const { return 0; } + u8 GetDiscNumber() const { return GetDiscNumber(GetGamePartition()); } + virtual u8 GetDiscNumber(const Partition& partition) const { return 0; } virtual Platform GetVolumeType() const = 0; virtual bool SupportsIntegrityCheck() const { return false; } - virtual bool CheckIntegrity() const { return false; } - virtual bool ChangePartition(u64 offset) { return false; } + virtual bool CheckIntegrity(const Partition& partition) const { return false; } virtual Region GetRegion() const = 0; - virtual Country GetCountry() const = 0; + Country GetCountry() const { return GetCountry(GetGamePartition()); } + virtual Country GetCountry(const Partition& partition) const = 0; virtual BlobType GetBlobType() const = 0; - // Size of virtual disc (not always accurate) + // Size of virtual disc (may be inaccurate depending on the blob type) virtual u64 GetSize() const = 0; // Size on disc (compressed size) virtual u64 GetRawSize() const = 0; diff --git a/Source/Core/DiscIO/VolumeCreator.cpp b/Source/Core/DiscIO/VolumeCreator.cpp index 21988d8b91..4b2c23aea9 100644 --- a/Source/Core/DiscIO/VolumeCreator.cpp +++ b/Source/Core/DiscIO/VolumeCreator.cpp @@ -9,8 +9,6 @@ #include #include -#include - #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Common/StringUtil.h" @@ -33,24 +31,9 @@ enum EDiscType DISC_TYPE_WAD }; -static const unsigned char s_master_key[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, - 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7}; - -static const unsigned char s_master_key_korean[16] = { - 0x63, 0xb8, 0x2b, 0xb4, 0xf4, 0x61, 0x4e, 0x2e, 0x13, 0xf2, 0xfe, 0xfb, 0xba, 0x4c, 0x9b, 0x7e}; - -static const unsigned char s_master_key_rvt[16] = {0xa1, 0x60, 0x4a, 0x6a, 0x71, 0x23, 0xb5, 0x29, - 0xae, 0x8b, 0xec, 0x32, 0xc8, 0x16, 0xfc, 0xaa}; - -static const char s_issuer_rvt[] = "Root-CA00000002-XS00000006"; - -static std::unique_ptr CreateVolumeFromCryptedWiiImage(std::unique_ptr reader, - u32 partition_group, - u32 volume_type, u32 volume_number); EDiscType GetDiscType(IBlobReader& _rReader); -std::unique_ptr CreateVolumeFromFilename(const std::string& filename, u32 partition_group, - u32 volume_number) +std::unique_ptr CreateVolumeFromFilename(const std::string& filename) { std::unique_ptr reader(CreateBlobReader(filename)); if (reader == nullptr) @@ -66,7 +49,7 @@ std::unique_ptr CreateVolumeFromFilename(const std::string& filename, u return std::make_unique(std::move(reader)); case DISC_TYPE_WII_CONTAINER: - return CreateVolumeFromCryptedWiiImage(std::move(reader), partition_group, 0, volume_number); + return std::make_unique(std::move(reader)); case DISC_TYPE_UNK: return nullptr; @@ -85,110 +68,6 @@ std::unique_ptr CreateVolumeFromDirectory(const std::string& directory, return nullptr; } -void VolumeKeyForPartition(IBlobReader& _rReader, u64 offset, u8* VolumeKey) -{ - u8 SubKey[16]; - _rReader.Read(offset + 0x1bf, 16, SubKey); - - u8 IV[16]; - memset(IV, 0, 16); - _rReader.Read(offset + 0x44c, 8, IV); - - mbedtls_aes_context AES_ctx; - - u8 issuer[sizeof(s_issuer_rvt)]; - _rReader.Read(offset + 0x140, sizeof(issuer), issuer); - if (!memcmp(issuer, s_issuer_rvt, sizeof(s_issuer_rvt))) - { - // RVT issuer. Use the RVT (debug) master key. - mbedtls_aes_setkey_dec(&AES_ctx, s_master_key_rvt, 128); - } - else - { - // Issue: 6813 - // Magic value is at partition's offset + 0x1f1 (1byte) - // If encrypted with the Korean key, the magic value would be 1 - // Otherwise it is zero - u8 using_korean_key = 0; - _rReader.Read(offset + 0x1f1, sizeof(u8), &using_korean_key); - u8 region = 0; - _rReader.Read(0x3, sizeof(u8), ®ion); - - mbedtls_aes_setkey_dec( - &AES_ctx, (using_korean_key == 1 && region == 'K' ? s_master_key_korean : s_master_key), - 128); - } - - mbedtls_aes_crypt_cbc(&AES_ctx, MBEDTLS_AES_DECRYPT, 16, IV, SubKey, VolumeKey); -} - -static std::unique_ptr CreateVolumeFromCryptedWiiImage(std::unique_ptr reader, - u32 partition_group, - u32 volume_type, u32 volume_number) -{ - CBlobBigEndianReader big_endian_reader(*reader); - - u32 num_partitions; - if (!big_endian_reader.ReadSwapped(0x40000 + (partition_group * 8), &num_partitions)) - return nullptr; - - // Check if we're looking for a valid partition - if ((int)volume_number != -1 && volume_number > num_partitions) - return nullptr; - - u32 partitions_offset_unshifted; - if (!big_endian_reader.ReadSwapped(0x40000 + (partition_group * 8) + 4, - &partitions_offset_unshifted)) - return nullptr; - u64 partitions_offset = (u64)partitions_offset_unshifted << 2; - - struct SPartition - { - SPartition(u64 offset_, u32 type_) : offset(offset_), type(type_) {} - u64 offset; - u32 type; - }; - - struct SPartitionGroup - { - u32 num_partitions; - u64 partitions_offset; - std::vector partitions; - }; - SPartitionGroup partition_groups[4]; - - // Read all partitions - for (SPartitionGroup& group : partition_groups) - { - for (u32 i = 0; i < num_partitions; i++) - { - u32 partition_offset, partition_type; - if (big_endian_reader.ReadSwapped(partitions_offset + (i * 8) + 0, &partition_offset) && - big_endian_reader.ReadSwapped(partitions_offset + (i * 8) + 4, &partition_type)) - { - group.partitions.emplace_back((u64)partition_offset << 2, partition_type); - } - } - } - - // Return the partition type specified or number - // types: 0 = game, 1 = firmware update, 2 = channel installer - // some partitions on SSBB use the ASCII title id of the demo VC game they hold... - for (size_t i = 0; i < partition_groups[partition_group].partitions.size(); i++) - { - const SPartition& partition = partition_groups[partition_group].partitions.at(i); - - if ((partition.type == volume_type && (int)volume_number == -1) || i == volume_number) - { - u8 volume_key[16]; - VolumeKeyForPartition(*reader, partition.offset, volume_key); - return std::make_unique(std::move(reader), partition.offset, volume_key); - } - } - - return nullptr; -} - EDiscType GetDiscType(IBlobReader& _rReader) { CBlobBigEndianReader Reader(_rReader); diff --git a/Source/Core/DiscIO/VolumeCreator.h b/Source/Core/DiscIO/VolumeCreator.h index 6b185b4b81..79c08cf1db 100644 --- a/Source/Core/DiscIO/VolumeCreator.h +++ b/Source/Core/DiscIO/VolumeCreator.h @@ -7,18 +7,13 @@ #include #include -#include "Common/CommonTypes.h" - namespace DiscIO { class IVolume; -class IBlobReader; -std::unique_ptr CreateVolumeFromFilename(const std::string& filename, - u32 partition_group = 0, u32 volume_number = -1); +std::unique_ptr CreateVolumeFromFilename(const std::string& filename); std::unique_ptr CreateVolumeFromDirectory(const std::string& directory, bool is_wii, const std::string& apploader = "", const std::string& dol = ""); -void VolumeKeyForPartition(IBlobReader& _rReader, u64 offset, u8* VolumeKey); } // namespace diff --git a/Source/Core/DiscIO/VolumeDirectory.cpp b/Source/Core/DiscIO/VolumeDirectory.cpp index 249c6f520d..397f0fc831 100644 --- a/Source/Core/DiscIO/VolumeDirectory.cpp +++ b/Source/Core/DiscIO/VolumeDirectory.cpp @@ -64,8 +64,10 @@ bool CVolumeDirectory::IsValidDirectory(const std::string& directory) return File::IsDirectory(ExtractDirectoryName(directory)); } -bool CVolumeDirectory::Read(u64 offset, u64 length, u8* buffer, bool decrypt) const +bool CVolumeDirectory::Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const { + bool decrypt = partition != PARTITION_NONE; + if (!decrypt && (offset + length >= 0x400) && m_is_wii) { // Fully supporting this would require re-encrypting every file that's read. @@ -77,7 +79,7 @@ bool CVolumeDirectory::Read(u64 offset, u64 length, u8* buffer, bool decrypt) co } if (decrypt && !m_is_wii) - PanicAlertT("Tried to decrypt data from a non-Wii volume"); + return false; // header if (offset < DISKHEADERINFO_ADDRESS) @@ -157,7 +159,17 @@ bool CVolumeDirectory::Read(u64 offset, u64 length, u8* buffer, bool decrypt) co return true; } -std::string CVolumeDirectory::GetGameID() const +std::vector CVolumeDirectory::GetPartitions() const +{ + return m_is_wii ? std::vector{GetGamePartition()} : std::vector(); +} + +Partition CVolumeDirectory::GetGamePartition() const +{ + return m_is_wii ? Partition(0x50000) : PARTITION_NONE; +} + +std::string CVolumeDirectory::GetGameID(const Partition& partition) const { return std::string(m_disk_header.begin(), m_disk_header.begin() + MAX_ID_LENGTH); } @@ -175,21 +187,21 @@ Region CVolumeDirectory::GetRegion() const return RegionSwitchGC(m_disk_header[3]); } -Country CVolumeDirectory::GetCountry() const +Country CVolumeDirectory::GetCountry(const Partition& partition) const { return CountrySwitch(m_disk_header[3]); } -std::string CVolumeDirectory::GetMakerID() const +std::string CVolumeDirectory::GetMakerID(const Partition& partition) const { // Not implemented return "00"; } -std::string CVolumeDirectory::GetInternalName() const +std::string CVolumeDirectory::GetInternalName(const Partition& partition) const { char name[0x60]; - if (Read(0x20, 0x60, (u8*)name, false)) + if (Read(0x20, 0x60, (u8*)name, partition)) return DecodeString(name); else return ""; @@ -218,13 +230,13 @@ void CVolumeDirectory::SetName(const std::string& name) m_disk_header[length + 0x20] = 0; } -u64 CVolumeDirectory::GetFSTSize() const +u64 CVolumeDirectory::GetFSTSize(const Partition& partition) const { // Not implemented return 0; } -std::string CVolumeDirectory::GetApploaderDate() const +std::string CVolumeDirectory::GetApploaderDate(const Partition& partition) const { // Not implemented return "VOID"; diff --git a/Source/Core/DiscIO/VolumeDirectory.h b/Source/Core/DiscIO/VolumeDirectory.h index 155459285c..83530c223c 100644 --- a/Source/Core/DiscIO/VolumeDirectory.h +++ b/Source/Core/DiscIO/VolumeDirectory.h @@ -39,26 +39,28 @@ public: static bool IsValidDirectory(const std::string& directory); - bool Read(u64 offset, u64 length, u8* buffer, bool decrypt) const override; + bool Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const override; + std::vector GetPartitions() const override; + Partition GetGamePartition() const override; - std::string GetGameID() const override; + std::string GetGameID(const Partition& partition = PARTITION_NONE) const override; void SetGameID(const std::string& id); - std::string GetMakerID() const override; + std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override; - u16 GetRevision() const override { return 0; } - std::string GetInternalName() const override; + u16 GetRevision(const Partition& partition = PARTITION_NONE) const override { return 0; } + std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override; std::map GetLongNames() const override; std::vector GetBanner(int* width, int* height) const override; void SetName(const std::string&); - u64 GetFSTSize() const override; + u64 GetFSTSize(const Partition& partition = PARTITION_NONE) const override; - std::string GetApploaderDate() const override; + std::string GetApploaderDate(const Partition& partition = PARTITION_NONE) const override; Platform GetVolumeType() const override; Region GetRegion() const override; - Country GetCountry() const override; + Country GetCountry(const Partition& partition = PARTITION_NONE) const override; BlobType GetBlobType() const override; u64 GetSize() const override; diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index adc20f5569..6f7568a3a1 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -32,21 +32,21 @@ CVolumeGC::~CVolumeGC() { } -bool CVolumeGC::Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const +bool CVolumeGC::Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const { - if (decrypt) - PanicAlertT("Tried to decrypt data from a non-Wii volume"); + if (partition != PARTITION_NONE) + return false; return m_pReader->Read(_Offset, _Length, _pBuffer); } -std::string CVolumeGC::GetGameID() const +std::string CVolumeGC::GetGameID(const Partition& partition) const { static const std::string NO_UID("NO_UID"); char ID[6]; - if (!Read(0, sizeof(ID), reinterpret_cast(ID))) + if (!Read(0, sizeof(ID), reinterpret_cast(ID), partition)) { PanicAlertT("Failed to read unique ID from disc image"); return NO_UID; @@ -58,43 +58,43 @@ std::string CVolumeGC::GetGameID() const Region CVolumeGC::GetRegion() const { u8 country_code; - if (!ReadSwapped(3, &country_code, false)) + if (!ReadSwapped(3, &country_code, PARTITION_NONE)) return Region::UNKNOWN_REGION; return RegionSwitchGC(country_code); } -Country CVolumeGC::GetCountry() const +Country CVolumeGC::GetCountry(const Partition& partition) const { u8 country_code; - if (!ReadSwapped(3, &country_code, false)) + if (!ReadSwapped(3, &country_code, partition)) return Country::COUNTRY_UNKNOWN; return CountrySwitch(country_code); } -std::string CVolumeGC::GetMakerID() const +std::string CVolumeGC::GetMakerID(const Partition& partition) const { char makerID[2]; - if (!Read(0x4, 0x2, (u8*)&makerID)) + if (!Read(0x4, 0x2, (u8*)&makerID, partition)) return std::string(); return DecodeString(makerID); } -u16 CVolumeGC::GetRevision() const +u16 CVolumeGC::GetRevision(const Partition& partition) const { u8 revision; - if (!ReadSwapped(7, &revision, false)) + if (!ReadSwapped(7, &revision, partition)) return 0; return revision; } -std::string CVolumeGC::GetInternalName() const +std::string CVolumeGC::GetInternalName(const Partition& partition) const { char name[0x60]; - if (Read(0x20, 0x60, (u8*)name)) + if (Read(0x20, 0x60, (u8*)name, partition)) return DecodeString(name); return ""; @@ -138,19 +138,19 @@ std::vector CVolumeGC::GetBanner(int* width, int* height) const return m_image_buffer; } -u64 CVolumeGC::GetFSTSize() const +u64 CVolumeGC::GetFSTSize(const Partition& partition) const { u32 size; - if (!Read(0x428, 0x4, (u8*)&size)) + if (!Read(0x428, 0x4, (u8*)&size, partition)) return 0; return Common::swap32(size); } -std::string CVolumeGC::GetApploaderDate() const +std::string CVolumeGC::GetApploaderDate(const Partition& partition) const { char date[16]; - if (!Read(0x2440, 0x10, (u8*)&date)) + if (!Read(0x2440, 0x10, (u8*)&date, partition)) return std::string(); return DecodeString(date); @@ -171,10 +171,10 @@ u64 CVolumeGC::GetRawSize() const return m_pReader->GetRawSize(); } -u8 CVolumeGC::GetDiscNumber() const +u8 CVolumeGC::GetDiscNumber(const Partition& partition) const { u8 disc_number = 0; - ReadSwapped(6, &disc_number, false); + ReadSwapped(6, &disc_number, partition); return disc_number; } @@ -192,8 +192,8 @@ void CVolumeGC::LoadBannerFile() const m_banner_loaded = true; GCBanner banner_file; - std::unique_ptr file_system(CreateFileSystem(this)); - size_t file_size = (size_t)file_system->GetFileSize("opening.bnr"); + std::unique_ptr file_system(CreateFileSystem(this, PARTITION_NONE)); + size_t file_size = static_cast(file_system->GetFileSize("opening.bnr")); constexpr int BNR1_MAGIC = 0x31524e42; constexpr int BNR2_MAGIC = 0x32524e42; diff --git a/Source/Core/DiscIO/VolumeGC.h b/Source/Core/DiscIO/VolumeGC.h index 10e5bf9df7..9ae149ed94 100644 --- a/Source/Core/DiscIO/VolumeGC.h +++ b/Source/Core/DiscIO/VolumeGC.h @@ -28,24 +28,25 @@ class CVolumeGC : public IVolume public: CVolumeGC(std::unique_ptr reader); ~CVolumeGC(); - bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt = false) const override; - std::string GetGameID() const override; - std::string GetMakerID() const override; - u16 GetRevision() const override; - std::string GetInternalName() const override; + bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, + const Partition& partition = PARTITION_NONE) const override; + std::string GetGameID(const Partition& partition = PARTITION_NONE) const override; + std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override; + u16 GetRevision(const Partition& partition = PARTITION_NONE) const override; + std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override; std::map GetShortNames() const override; std::map GetLongNames() const override; std::map GetShortMakers() const override; std::map GetLongMakers() const override; std::map GetDescriptions() const override; std::vector GetBanner(int* width, int* height) const override; - u64 GetFSTSize() const override; - std::string GetApploaderDate() const override; - u8 GetDiscNumber() const override; + u64 GetFSTSize(const Partition& partition = PARTITION_NONE) const override; + std::string GetApploaderDate(const Partition& partition = PARTITION_NONE) const override; + u8 GetDiscNumber(const Partition& partition = PARTITION_NONE) const override; Platform GetVolumeType() const override; Region GetRegion() const override; - Country GetCountry() const override; + Country GetCountry(const Partition& partition = PARTITION_NONE) const override; BlobType GetBlobType() const override; u64 GetSize() const override; u64 GetRawSize() const override; diff --git a/Source/Core/DiscIO/VolumeWad.cpp b/Source/Core/DiscIO/VolumeWad.cpp index 4e8185f4f6..a8316c9e04 100644 --- a/Source/Core/DiscIO/VolumeWad.cpp +++ b/Source/Core/DiscIO/VolumeWad.cpp @@ -29,11 +29,11 @@ CVolumeWAD::CVolumeWAD(std::unique_ptr reader) : m_reader(std::move _assert_(m_reader); // Source: http://wiibrew.org/wiki/WAD_files - ReadSwapped(0x00, &m_hdr_size, false); - ReadSwapped(0x08, &m_cert_size, false); - ReadSwapped(0x10, &m_tick_size, false); - ReadSwapped(0x14, &m_tmd_size, false); - ReadSwapped(0x18, &m_data_size, false); + ReadSwapped(0x00, &m_hdr_size, PARTITION_NONE); + ReadSwapped(0x08, &m_cert_size, PARTITION_NONE); + ReadSwapped(0x10, &m_tick_size, PARTITION_NONE); + ReadSwapped(0x14, &m_tmd_size, PARTITION_NONE); + ReadSwapped(0x18, &m_data_size, PARTITION_NONE); m_offset = Common::AlignUp(m_hdr_size, 0x40) + Common::AlignUp(m_cert_size, 0x40); m_tmd_offset = Common::AlignUp(m_hdr_size, 0x40) + Common::AlignUp(m_cert_size, 0x40) + @@ -48,7 +48,7 @@ CVolumeWAD::CVolumeWAD(std::unique_ptr reader) : m_reader(std::move } std::vector tmd_buffer(m_tmd_size); - Read(m_tmd_offset, m_tmd_size, tmd_buffer.data(), false); + Read(m_tmd_offset, m_tmd_size, tmd_buffer.data()); m_tmd.SetBytes(std::move(tmd_buffer)); } @@ -56,10 +56,10 @@ CVolumeWAD::~CVolumeWAD() { } -bool CVolumeWAD::Read(u64 offset, u64 length, u8* buffer, bool decrypt) const +bool CVolumeWAD::Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const { - if (decrypt) - PanicAlertT("Tried to decrypt data from a non-Wii volume"); + if (partition != PARTITION_NONE) + return false; return m_reader->Read(offset, length, buffer); } @@ -71,7 +71,7 @@ Region CVolumeWAD::GetRegion() const return m_tmd.GetRegion(); } -Country CVolumeWAD::GetCountry() const +Country CVolumeWAD::GetCountry(const Partition& partition) const { if (!m_tmd.IsValid()) return Country::COUNTRY_UNKNOWN; @@ -83,20 +83,20 @@ Country CVolumeWAD::GetCountry() const return CountrySwitch(country_code); } -IOS::ES::TMDReader CVolumeWAD::GetTMD() const +IOS::ES::TMDReader CVolumeWAD::GetTMD(const Partition& partition) const { return m_tmd; } -std::string CVolumeWAD::GetGameID() const +std::string CVolumeWAD::GetGameID(const Partition& partition) const { return m_tmd.GetGameID(); } -std::string CVolumeWAD::GetMakerID() const +std::string CVolumeWAD::GetMakerID(const Partition& partition) const { char temp[2]; - if (!Read(0x198 + m_tmd_offset, 2, (u8*)temp)) + if (!Read(0x198 + m_tmd_offset, 2, (u8*)temp, partition)) return "00"; // Some weird channels use 0x0000 in place of the MakerID, so we need a check here @@ -107,12 +107,12 @@ std::string CVolumeWAD::GetMakerID() const return DecodeString(temp); } -bool CVolumeWAD::GetTitleID(u64* buffer) const +bool CVolumeWAD::GetTitleID(u64* buffer, const Partition& partition) const { - return ReadSwapped(m_offset + 0x01DC, buffer, false); + return ReadSwapped(m_offset + 0x01DC, buffer, partition); } -u16 CVolumeWAD::GetRevision() const +u16 CVolumeWAD::GetRevision(const Partition& partition) const { if (!m_tmd.IsValid()) return 0; diff --git a/Source/Core/DiscIO/VolumeWad.h b/Source/Core/DiscIO/VolumeWad.h index f84af08796..ecaef672fe 100644 --- a/Source/Core/DiscIO/VolumeWad.h +++ b/Source/Core/DiscIO/VolumeWad.h @@ -31,20 +31,27 @@ class CVolumeWAD : public IVolume public: CVolumeWAD(std::unique_ptr reader); ~CVolumeWAD(); - bool Read(u64 offset, u64 length, u8* buffer, bool decrypt = false) const override; - bool GetTitleID(u64* buffer) const override; - IOS::ES::TMDReader GetTMD() const override; - std::string GetGameID() const override; - std::string GetMakerID() const override; - u16 GetRevision() const override; - std::string GetInternalName() const override { return ""; } + bool Read(u64 offset, u64 length, u8* buffer, + const Partition& partition = PARTITION_NONE) const override; + bool GetTitleID(u64* buffer, const Partition& partition = PARTITION_NONE) const override; + IOS::ES::TMDReader GetTMD(const Partition& partition = PARTITION_NONE) const override; + std::string GetGameID(const Partition& partition = PARTITION_NONE) const override; + std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override; + u16 GetRevision(const Partition& partition = PARTITION_NONE) const override; + std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override + { + return ""; + } std::map GetLongNames() const override; std::vector GetBanner(int* width, int* height) const override; - u64 GetFSTSize() const override { return 0; } - std::string GetApploaderDate() const override { return ""; } + u64 GetFSTSize(const Partition& partition = PARTITION_NONE) const override { return 0; } + std::string GetApploaderDate(const Partition& partition = PARTITION_NONE) const override + { + return ""; + } Platform GetVolumeType() const override; Region GetRegion() const override; - Country GetCountry() const override; + Country GetCountry(const Partition& partition = PARTITION_NONE) const override; BlobType GetBlobType() const override; u64 GetSize() const override; diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.cpp b/Source/Core/DiscIO/VolumeWiiCrypted.cpp index a78b3017d5..310d447c43 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.cpp +++ b/Source/Core/DiscIO/VolumeWiiCrypted.cpp @@ -31,41 +31,112 @@ namespace DiscIO { constexpr u64 PARTITION_DATA_OFFSET = 0x20000; -CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr reader, u64 _VolumeOffset, - const unsigned char* _pVolumeKey) - : m_pReader(std::move(reader)), m_AES_ctx(std::make_unique()), - m_VolumeOffset(_VolumeOffset), m_last_decrypted_block(-1) +CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr reader) + : m_pReader(std::move(reader)), m_game_partition(PARTITION_NONE), m_last_decrypted_block(-1) { _assert_(m_pReader); - mbedtls_aes_setkey_dec(m_AES_ctx.get(), _pVolumeKey, 128); -} + // Get decryption keys for all partitions + CBlobBigEndianReader big_endian_reader(*m_pReader.get()); + for (u32 partition_group = 0; partition_group < 4; ++partition_group) + { + u32 number_of_partitions; + if (!big_endian_reader.ReadSwapped(0x40000 + (partition_group * 8), &number_of_partitions)) + continue; -bool CVolumeWiiCrypted::ChangePartition(u64 offset) -{ - m_VolumeOffset = offset; + u32 read_buffer; + if (!big_endian_reader.ReadSwapped(0x40000 + (partition_group * 8) + 4, &read_buffer)) + continue; + const u64 partition_table_offset = (u64)read_buffer << 2; - u8 volume_key[16]; - DiscIO::VolumeKeyForPartition(*m_pReader, offset, volume_key); - mbedtls_aes_setkey_dec(m_AES_ctx.get(), volume_key, 128); - return true; + for (u32 i = 0; i < number_of_partitions; i++) + { + if (!big_endian_reader.ReadSwapped(partition_table_offset + (i * 8), &read_buffer)) + continue; + const u64 partition_offset = (u64)read_buffer << 2; + + if (m_game_partition == PARTITION_NONE) + { + u32 partition_type; + if (!big_endian_reader.ReadSwapped(partition_table_offset + (i * 8) + 4, &partition_type)) + continue; + + if (partition_type == 0) + m_game_partition = Partition(partition_offset); + } + + u8 sub_key[16]; + if (!m_pReader->Read(partition_offset + 0x1bf, 16, sub_key)) + continue; + + u8 iv[16]; + memset(iv, 0, 16); + if (!m_pReader->Read(partition_offset + 0x44c, 8, iv)) + continue; + + static const u8 common_key_standard[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, + 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7}; + static const u8 common_key_korean[16] = {0x63, 0xb8, 0x2b, 0xb4, 0xf4, 0x61, 0x4e, 0x2e, + 0x13, 0xf2, 0xfe, 0xfb, 0xba, 0x4c, 0x9b, 0x7e}; + static const u8 common_key_rvt[16] = {0xa1, 0x60, 0x4a, 0x6a, 0x71, 0x23, 0xb5, 0x29, + 0xae, 0x8b, 0xec, 0x32, 0xc8, 0x16, 0xfc, 0xaa}; + static const char issuer_rvt[] = "Root-CA00000002-XS00000006"; + + const u8* common_key; + + u8 issuer[sizeof(issuer_rvt)]; + if (!m_pReader->Read(partition_offset + 0x140, sizeof(issuer), issuer)) + continue; + + if (!memcmp(issuer, issuer_rvt, sizeof(issuer_rvt))) + { + // RVT issuer. Use the RVT (debug) master key. + common_key = common_key_rvt; + } + else + { + u8 key_number = 0; + if (!big_endian_reader.ReadSwapped(partition_offset + 0x1f1, &key_number)) + continue; + common_key = (key_number == 1) ? common_key_korean : common_key_standard; + } + + mbedtls_aes_context aes_context; + mbedtls_aes_setkey_dec(&aes_context, common_key, 128); + + u8 volume_key[16]; + mbedtls_aes_crypt_cbc(&aes_context, MBEDTLS_AES_DECRYPT, 16, iv, sub_key, volume_key); + + std::unique_ptr partition_AES_context = + std::make_unique(); + mbedtls_aes_setkey_dec(partition_AES_context.get(), volume_key, 128); + m_partitions[Partition(partition_offset)] = std::move(partition_AES_context); + } + } } CVolumeWiiCrypted::~CVolumeWiiCrypted() { } -bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, bool decrypt) const +bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, + const Partition& partition) const { - if (!decrypt) + if (partition == PARTITION_NONE) return m_pReader->Read(_ReadOffset, _Length, _pBuffer); + // Get the decryption key for the partition + auto it = m_partitions.find(partition); + if (it == m_partitions.end()) + return false; + mbedtls_aes_context* aes_context = it->second.get(); + std::vector read_buffer(BLOCK_TOTAL_SIZE); while (_Length > 0) { // Calculate offsets u64 block_offset_on_disc = - _ReadOffset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE + m_VolumeOffset + PARTITION_DATA_OFFSET; + partition.offset + PARTITION_DATA_OFFSET + _ReadOffset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE; u64 data_offset_in_block = _ReadOffset % BLOCK_DATA_SIZE; if (m_last_decrypted_block != block_offset_on_disc) @@ -78,9 +149,8 @@ bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, bool de // 0x3D0 - 0x3DF in read_buffer will be overwritten, // but that won't affect anything, because we won't // use the content of read_buffer anymore after this - mbedtls_aes_crypt_cbc(m_AES_ctx.get(), MBEDTLS_AES_DECRYPT, BLOCK_DATA_SIZE, - &read_buffer[0x3D0], &read_buffer[BLOCK_HEADER_SIZE], - m_last_decrypted_block_data); + mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, BLOCK_DATA_SIZE, &read_buffer[0x3D0], + &read_buffer[BLOCK_HEADER_SIZE], m_last_decrypted_block_data); m_last_decrypted_block = block_offset_on_disc; // The only thing we currently use from the 0x000 - 0x3FF part @@ -103,26 +173,39 @@ bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, bool de return true; } -bool CVolumeWiiCrypted::GetTitleID(u64* buffer) const +std::vector CVolumeWiiCrypted::GetPartitions() const { - return ReadSwapped(m_VolumeOffset + 0x1DC, buffer, false); + std::vector partitions; + for (const auto& pair : m_partitions) + partitions.push_back(pair.first); + return partitions; } -IOS::ES::TicketReader CVolumeWiiCrypted::GetTicket() const +Partition CVolumeWiiCrypted::GetGamePartition() const +{ + return m_game_partition; +} + +bool CVolumeWiiCrypted::GetTitleID(u64* buffer, const Partition& partition) const +{ + return ReadSwapped(partition.offset + 0x1DC, buffer, PARTITION_NONE); +} + +IOS::ES::TicketReader CVolumeWiiCrypted::GetTicket(const Partition& partition) const { std::vector buffer(0x2a4); - Read(m_VolumeOffset, buffer.size(), buffer.data(), false); + Read(partition.offset, buffer.size(), buffer.data(), PARTITION_NONE); return IOS::ES::TicketReader{std::move(buffer)}; } -IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD() const +IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD(const Partition& partition) const { u32 tmd_size = 0; u32 tmd_address = 0; - if (!ReadSwapped(m_VolumeOffset + 0x2a4, &tmd_size, false)) + if (!ReadSwapped(partition.offset + 0x2a4, &tmd_size, PARTITION_NONE)) return {}; - if (!ReadSwapped(m_VolumeOffset + 0x2a8, &tmd_address, false)) + if (!ReadSwapped(partition.offset + 0x2a8, &tmd_address, PARTITION_NONE)) return {}; tmd_address <<= 2; @@ -137,23 +220,26 @@ IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD() const } std::vector buffer(tmd_size); - if (!Read(m_VolumeOffset + tmd_address, tmd_size, buffer.data(), false)) + if (!Read(partition.offset + tmd_address, tmd_size, buffer.data(), PARTITION_NONE)) return {}; return IOS::ES::TMDReader{std::move(buffer)}; } -u64 CVolumeWiiCrypted::PartitionOffsetToRawOffset(u64 offset) const +u64 CVolumeWiiCrypted::PartitionOffsetToRawOffset(u64 offset, const Partition& partition) { - return m_VolumeOffset + PARTITION_DATA_OFFSET + (offset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE) + + if (partition == PARTITION_NONE) + return offset; + + return partition.offset + PARTITION_DATA_OFFSET + (offset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE) + (offset % BLOCK_DATA_SIZE); } -std::string CVolumeWiiCrypted::GetGameID() const +std::string CVolumeWiiCrypted::GetGameID(const Partition& partition) const { char ID[6]; - if (!Read(0, 6, (u8*)ID, true)) + if (!Read(0, 6, (u8*)ID, partition)) return std::string(); return DecodeString(ID); @@ -162,16 +248,16 @@ std::string CVolumeWiiCrypted::GetGameID() const Region CVolumeWiiCrypted::GetRegion() const { u32 region_code; - if (!ReadSwapped(0x4E000, ®ion_code, false)) + if (!ReadSwapped(0x4E000, ®ion_code, PARTITION_NONE)) return Region::UNKNOWN_REGION; return static_cast(region_code); } -Country CVolumeWiiCrypted::GetCountry() const +Country CVolumeWiiCrypted::GetCountry(const Partition& partition) const { u8 country_byte; - if (!ReadSwapped(3, &country_byte, true)) + if (!ReadSwapped(3, &country_byte, partition)) return Country::COUNTRY_UNKNOWN; const Region region = GetRegion(); @@ -182,29 +268,29 @@ Country CVolumeWiiCrypted::GetCountry() const return CountrySwitch(country_byte); } -std::string CVolumeWiiCrypted::GetMakerID() const +std::string CVolumeWiiCrypted::GetMakerID(const Partition& partition) const { char makerID[2]; - if (!Read(0x4, 0x2, (u8*)&makerID, true)) + if (!Read(0x4, 0x2, (u8*)&makerID, partition)) return std::string(); return DecodeString(makerID); } -u16 CVolumeWiiCrypted::GetRevision() const +u16 CVolumeWiiCrypted::GetRevision(const Partition& partition) const { u8 revision; - if (!ReadSwapped(7, &revision, true)) + if (!ReadSwapped(7, &revision, partition)) return 0; return revision; } -std::string CVolumeWiiCrypted::GetInternalName() const +std::string CVolumeWiiCrypted::GetInternalName(const Partition& partition) const { char name_buffer[0x60]; - if (Read(0x20, 0x60, (u8*)&name_buffer, true)) + if (Read(0x20, 0x60, (u8*)&name_buffer, partition)) return DecodeString(name_buffer); return ""; @@ -212,7 +298,7 @@ std::string CVolumeWiiCrypted::GetInternalName() const std::map CVolumeWiiCrypted::GetLongNames() const { - std::unique_ptr file_system(CreateFileSystem(this)); + std::unique_ptr file_system(CreateFileSystem(this, GetGamePartition())); std::vector opening_bnr(NAMES_TOTAL_BYTES); size_t size = file_system->ReadFile("opening.bnr", opening_bnr.data(), opening_bnr.size(), 0x5C); opening_bnr.resize(size); @@ -225,27 +311,27 @@ std::vector CVolumeWiiCrypted::GetBanner(int* width, int* height) const *height = 0; u64 title_id; - if (!GetTitleID(&title_id)) + if (!GetTitleID(&title_id, GetGamePartition())) return std::vector(); return GetWiiBanner(width, height, title_id); } -u64 CVolumeWiiCrypted::GetFSTSize() const +u64 CVolumeWiiCrypted::GetFSTSize(const Partition& partition) const { u32 size; - if (!Read(0x428, 0x4, (u8*)&size, true)) + if (!Read(0x428, 0x4, (u8*)&size, partition)) return 0; return (u64)Common::swap32(size) << 2; } -std::string CVolumeWiiCrypted::GetApploaderDate() const +std::string CVolumeWiiCrypted::GetApploaderDate(const Partition& partition) const { char date[16]; - if (!Read(0x2440, 0x10, (u8*)&date, true)) + if (!Read(0x2440, 0x10, (u8*)&date, partition)) return std::string(); return DecodeString(date); @@ -256,10 +342,10 @@ Platform CVolumeWiiCrypted::GetVolumeType() const return Platform::WII_DISC; } -u8 CVolumeWiiCrypted::GetDiscNumber() const +u8 CVolumeWiiCrypted::GetDiscNumber(const Partition& partition) const { u8 disc_number = 0; - ReadSwapped(6, &disc_number, true); + ReadSwapped(6, &disc_number, partition); return disc_number; } @@ -278,29 +364,34 @@ u64 CVolumeWiiCrypted::GetRawSize() const return m_pReader->GetRawSize(); } -bool CVolumeWiiCrypted::CheckIntegrity() const +bool CVolumeWiiCrypted::CheckIntegrity(const Partition& partition) const { + // Get the decryption key for the partition + auto it = m_partitions.find(partition); + if (it == m_partitions.end()) + return false; + mbedtls_aes_context* aes_context = it->second.get(); + // Get partition data size u32 partSizeDiv4; - Read(m_VolumeOffset + 0x2BC, 4, (u8*)&partSizeDiv4, false); + Read(partition.offset + 0x2BC, 4, (u8*)&partSizeDiv4, PARTITION_NONE); u64 partDataSize = (u64)Common::swap32(partSizeDiv4) * 4; u32 nClusters = (u32)(partDataSize / 0x8000); for (u32 clusterID = 0; clusterID < nClusters; ++clusterID) { - u64 clusterOff = m_VolumeOffset + PARTITION_DATA_OFFSET + (u64)clusterID * 0x8000; + u64 clusterOff = partition.offset + PARTITION_DATA_OFFSET + (u64)clusterID * 0x8000; // Read and decrypt the cluster metadata u8 clusterMDCrypted[0x400]; u8 clusterMD[0x400]; u8 IV[16] = {0}; - if (!Read(clusterOff, 0x400, clusterMDCrypted, false)) + if (!Read(clusterOff, 0x400, clusterMDCrypted, PARTITION_NONE)) { WARN_LOG(DISCIO, "Integrity Check: fail at cluster %d: could not read metadata", clusterID); return false; } - mbedtls_aes_crypt_cbc(m_AES_ctx.get(), MBEDTLS_AES_DECRYPT, 0x400, IV, clusterMDCrypted, - clusterMD); + mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, 0x400, IV, clusterMDCrypted, clusterMD); // Some clusters have invalid data and metadata because they aren't // meant to be read by the game (for example, holes between files). To @@ -319,7 +410,7 @@ bool CVolumeWiiCrypted::CheckIntegrity() const continue; u8 clusterData[0x7C00]; - if (!Read((u64)clusterID * 0x7C00, 0x7C00, clusterData, true)) + if (!Read((u64)clusterID * 0x7C00, 0x7C00, clusterData, partition)) { WARN_LOG(DISCIO, "Integrity Check: fail at cluster %d: could not read data", clusterID); return false; diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.h b/Source/Core/DiscIO/VolumeWiiCrypted.h index 88ffdc61de..88befabcb6 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.h +++ b/Source/Core/DiscIO/VolumeWiiCrypted.h @@ -28,44 +28,44 @@ enum class Platform; class CVolumeWiiCrypted : public IVolume { public: - CVolumeWiiCrypted(std::unique_ptr reader, u64 _VolumeOffset, - const unsigned char* _pVolumeKey); + CVolumeWiiCrypted(std::unique_ptr reader); ~CVolumeWiiCrypted(); - bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const override; - bool GetTitleID(u64* buffer) const override; - IOS::ES::TicketReader GetTicket() const override; - IOS::ES::TMDReader GetTMD() const override; - u64 PartitionOffsetToRawOffset(u64 offset) const override; - std::string GetGameID() const override; - std::string GetMakerID() const override; - u16 GetRevision() const override; - std::string GetInternalName() const override; + bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const override; + std::vector GetPartitions() const override; + Partition GetGamePartition() const override; + bool GetTitleID(u64* buffer, const Partition& partition) const override; + IOS::ES::TicketReader GetTicket(const Partition& partition) const override; + IOS::ES::TMDReader GetTMD(const Partition& partition) const override; + std::string GetGameID(const Partition& partition) const override; + std::string GetMakerID(const Partition& partition) const override; + u16 GetRevision(const Partition& partition) const override; + std::string GetInternalName(const Partition& partition) const override; std::map GetLongNames() const override; std::vector GetBanner(int* width, int* height) const override; - u64 GetFSTSize() const override; - std::string GetApploaderDate() const override; - u8 GetDiscNumber() const override; + u64 GetFSTSize(const Partition& partition) const override; + std::string GetApploaderDate(const Partition& partition) const override; + u8 GetDiscNumber(const Partition& partition) const override; Platform GetVolumeType() const override; bool SupportsIntegrityCheck() const override { return true; } - bool CheckIntegrity() const override; - bool ChangePartition(u64 offset) override; + bool CheckIntegrity(const Partition& partition) const override; Region GetRegion() const override; - Country GetCountry() const override; + Country GetCountry(const Partition& partition) const override; BlobType GetBlobType() const override; u64 GetSize() const override; u64 GetRawSize() const override; + static u64 PartitionOffsetToRawOffset(u64 offset, const Partition& partition); + static constexpr unsigned int BLOCK_HEADER_SIZE = 0x0400; static constexpr unsigned int BLOCK_DATA_SIZE = 0x7C00; static constexpr unsigned int BLOCK_TOTAL_SIZE = BLOCK_HEADER_SIZE + BLOCK_DATA_SIZE; private: std::unique_ptr m_pReader; - std::unique_ptr m_AES_ctx; - - u64 m_VolumeOffset; + std::map> m_partitions; + Partition m_game_partition; mutable u64 m_last_decrypted_block; mutable u8 m_last_decrypted_block_data[BLOCK_DATA_SIZE]; diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp index bd1fc44f3b..e6d94f8a6d 100644 --- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp @@ -33,32 +33,31 @@ namespace class WiiPartition final : public wxTreeItemData { public: - WiiPartition(std::unique_ptr volume_, - std::unique_ptr filesystem_) - : volume{std::move(volume_)}, filesystem{std::move(filesystem_)} + WiiPartition(std::unique_ptr filesystem_) + : filesystem{std::move(filesystem_)} { } - std::unique_ptr volume; std::unique_ptr filesystem; }; class IntegrityCheckThread final : public wxThread { public: - explicit IntegrityCheckThread(const WiiPartition& partition) - : wxThread{wxTHREAD_JOINABLE}, m_partition{partition} + explicit IntegrityCheckThread(const DiscIO::IVolume* volume, DiscIO::Partition partition) + : wxThread{wxTHREAD_JOINABLE}, m_volume{volume}, m_partition{partition} { Create(); } ExitCode Entry() override { - return reinterpret_cast(m_partition.volume->CheckIntegrity()); + return reinterpret_cast(m_volume->CheckIntegrity(m_partition)); } private: - const WiiPartition& m_partition; + const DiscIO::IVolume* const m_volume; + const DiscIO::Partition m_partition; }; enum : int @@ -156,9 +155,9 @@ WiiPartition* FindWiiPartition(wxTreeCtrl* tree_ctrl, const wxString& label) } } // Anonymous namespace -FilesystemPanel::FilesystemPanel(wxWindow* parent, wxWindowID id, const GameListItem& item, +FilesystemPanel::FilesystemPanel(wxWindow* parent, wxWindowID id, const std::unique_ptr& opened_iso) - : wxPanel{parent, id}, m_game_list_item{item}, m_opened_iso{opened_iso} + : wxPanel{parent, id}, m_opened_iso{opened_iso} { CreateGUI(); BindEvents(); @@ -217,7 +216,7 @@ void FilesystemPanel::PopulateFileSystemTree() void FilesystemPanel::PopulateFileSystemTreeGC() { - m_filesystem = DiscIO::CreateFileSystem(m_opened_iso.get()); + m_filesystem = DiscIO::CreateFileSystem(m_opened_iso.get(), DiscIO::PARTITION_NONE); if (!m_filesystem) return; @@ -226,34 +225,23 @@ void FilesystemPanel::PopulateFileSystemTreeGC() void FilesystemPanel::PopulateFileSystemTreeWii() const { - u32 partition_count = 0; - - for (u32 group = 0; group < 4; group++) + std::vector partitions = m_opened_iso->GetPartitions(); + for (size_t i = 0; i < partitions.size(); ++i) { - // yes, technically there can be OVER NINE THOUSAND partitions... - for (u32 i = 0; i < 0xFFFFFFFF; i++) + std::unique_ptr file_system( + DiscIO::CreateFileSystem(m_opened_iso.get(), partitions[i])); + if (file_system) { - auto volume = DiscIO::CreateVolumeFromFilename(m_game_list_item.GetFileName(), group, i); - if (volume == nullptr) - break; + wxTreeItemId partition_root = m_tree_ctrl->AppendItem( + m_tree_ctrl->GetRootItem(), wxString::Format(_("Partition %i"), i), ICON_DISC); - auto file_system = DiscIO::CreateFileSystem(volume.get()); - if (file_system != nullptr) - { - auto* const partition = new WiiPartition(std::move(volume), std::move(file_system)); + WiiPartition* const partition = new WiiPartition(std::move(file_system)); - const wxTreeItemId partition_root = m_tree_ctrl->AppendItem( - m_tree_ctrl->GetRootItem(), wxString::Format(_("Partition %u"), partition_count), - ICON_DISC); + m_tree_ctrl->SetItemData(partition_root, partition); + CreateDirectoryTree(m_tree_ctrl, partition_root, partition->filesystem->GetFileList()); - m_tree_ctrl->SetItemData(partition_root, partition); - CreateDirectoryTree(m_tree_ctrl, partition_root, partition->filesystem->GetFileList()); - - if (partition_count == 1) - m_tree_ctrl->Expand(partition_root); - - partition_count++; - } + if (i == 1) + m_tree_ctrl->Expand(partition_root); } } } @@ -382,8 +370,9 @@ void FilesystemPanel::OnCheckPartitionIntegrity(wxCommandEvent& WXUNUSED(event)) wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_SMOOTH); const auto selection = m_tree_ctrl->GetSelection(); - - IntegrityCheckThread thread(*static_cast(m_tree_ctrl->GetItemData(selection))); + WiiPartition* partition = + static_cast(m_tree_ctrl->GetItemData(m_tree_ctrl->GetSelection())); + IntegrityCheckThread thread(m_opened_iso.get(), partition->filesystem->GetPartition()); thread.Run(); while (thread.IsAlive()) diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h index 7b40fb36c9..75ab22fad1 100644 --- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h +++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h @@ -21,7 +21,7 @@ class IVolume; class FilesystemPanel final : public wxPanel { public: - explicit FilesystemPanel(wxWindow* parent, wxWindowID id, const GameListItem& item, + explicit FilesystemPanel(wxWindow* parent, wxWindowID id, const std::unique_ptr& opened_iso); ~FilesystemPanel(); @@ -69,7 +69,6 @@ private: wxTreeCtrl* m_tree_ctrl; - const GameListItem& m_game_list_item; const std::unique_ptr& m_opened_iso; std::unique_ptr m_filesystem; diff --git a/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp index 8b53ec7b4c..ee71292aaf 100644 --- a/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp +++ b/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp @@ -433,9 +433,8 @@ void CISOProperties::CreateGUIControls() if (m_open_iso->GetVolumeType() != DiscIO::Platform::WII_WAD) { - m_Notebook->AddPage( - new FilesystemPanel(m_Notebook, ID_FILESYSTEM, OpenGameListItem, m_open_iso), - _("Filesystem")); + m_Notebook->AddPage(new FilesystemPanel(m_Notebook, ID_FILESYSTEM, m_open_iso), + _("Filesystem")); } wxStdDialogButtonSizer* sButtons = CreateStdDialogButtonSizer(wxOK | wxNO_DEFAULT); diff --git a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp index 3fc9efc14d..1d52bdde6e 100644 --- a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp @@ -182,7 +182,7 @@ void InfoPanel::LoadISODetails() m_fst->SetValue(StrToWxStr(std::to_string(m_opened_iso->GetFSTSize()))); if (m_ios_version) { - const IOS::ES::TMDReader tmd = m_opened_iso->GetTMD(); + const IOS::ES::TMDReader tmd = m_opened_iso->GetTMD(m_opened_iso->GetGamePartition()); if (tmd.IsValid()) m_ios_version->SetValue(StringFromFormat("IOS%u", static_cast(tmd.GetIOSId()))); } @@ -223,7 +223,7 @@ wxStaticBoxSizer* InfoPanel::CreateISODetailsSizer() {_("Apploader Date:"), m_date}, {_("FST Size:"), m_fst}, }}; - if (m_opened_iso->GetTMD().IsValid()) + if (m_opened_iso->GetTMD(m_opened_iso->GetGamePartition()).IsValid()) controls.emplace_back(_("IOS Version:"), m_ios_version); const int space_10 = FromDIP(10);