Merge pull request #2353 from JosJuice/wii-partition-cleanup

VolumeWiiCrypted: Replace ChangePartition with a partition parameter
This commit is contained in:
JosJuice 2017-05-16 23:06:32 +02:00 committed by GitHub
commit fa06d10f4a
35 changed files with 653 additions and 671 deletions

View File

@ -50,10 +50,10 @@ static const DiscIO::IVolume* SetDisc(std::unique_ptr<DiscIO::IVolume> volume)
} }
bool CBoot::DVDRead(const DiscIO::IVolume& volume, u64 dvd_offset, u32 output_address, u32 length, bool CBoot::DVDRead(const DiscIO::IVolume& volume, u64 dvd_offset, u32 output_address, u32 length,
bool decrypt) const DiscIO::Partition& partition)
{ {
std::vector<u8> buffer(length); std::vector<u8> buffer(length);
if (!volume.Read(dvd_offset, length, buffer.data(), decrypt)) if (!volume.Read(dvd_offset, length, buffer.data(), partition))
return false; return false;
Memory::CopyToEmu(output_address, buffer.data(), length); Memory::CopyToEmu(output_address, buffer.data(), length);
return true; return true;
@ -64,8 +64,10 @@ void CBoot::Load_FST(bool is_wii, const DiscIO::IVolume* volume)
if (!volume) if (!volume)
return; return;
const DiscIO::Partition partition = volume->GetGamePartition();
// copy first 32 bytes of disc to start of Mem 1 // 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 // copy of game id
Memory::Write_U32(Memory::Read_U32(0x0000), 0x3180); 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 fst_size = 0;
u32 max_fst_size = 0; u32 max_fst_size = 0;
volume->ReadSwapped(0x0424, &fst_offset, is_wii); volume->ReadSwapped(0x0424, &fst_offset, partition);
volume->ReadSwapped(0x0428, &fst_size, is_wii); volume->ReadSwapped(0x0428, &fst_size, partition);
volume->ReadSwapped(0x042c, &max_fst_size, is_wii); volume->ReadSwapped(0x042c, &max_fst_size, partition);
u32 arena_high = Common::AlignDown(0x817FFFFF - (max_fst_size << shift), 0x20); u32 arena_high = Common::AlignDown(0x817FFFFF - (max_fst_size << shift), 0x20);
Memory::Write_U32(arena_high, 0x00000034); Memory::Write_U32(arena_high, 0x00000034);
// load FST // 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(arena_high, 0x00000038);
Memory::Write_U32(max_fst_size << shift, 0x0000003c); Memory::Write_U32(max_fst_size << shift, 0x0000003c);

View File

@ -12,6 +12,7 @@
namespace DiscIO namespace DiscIO
{ {
class IVolume; class IVolume;
struct Partition;
} }
struct RegionSetting struct RegionSetting
@ -46,7 +47,7 @@ public:
private: private:
static bool DVDRead(const DiscIO::IVolume& volume, u64 dvd_offset, u32 output_address, u32 length, 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 RunFunction(u32 address);
static void UpdateDebugger_MapLoaded(); static void UpdateDebugger_MapLoaded();

View File

@ -79,21 +79,24 @@ void CBoot::SetupBAT(bool is_wii)
bool CBoot::RunApploader(bool is_wii, const DiscIO::IVolume& volume) 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, // 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. // but the size can differ between discs. Compare with YAGCD chap 13.
const u32 apploader_offset = 0x2440; const u32 apploader_offset = 0x2440;
u32 apploader_entry = 0; u32 apploader_entry = 0;
u32 apploader_size = 0; u32 apploader_size = 0;
u32 apploader_trailer = 0; u32 apploader_trailer = 0;
if (!volume.ReadSwapped(apploader_offset + 0x10, &apploader_entry, is_wii) || if (!volume.ReadSwapped(apploader_offset + 0x10, &apploader_entry, partition) ||
!volume.ReadSwapped(apploader_offset + 0x14, &apploader_size, is_wii) || !volume.ReadSwapped(apploader_offset + 0x14, &apploader_size, partition) ||
!volume.ReadSwapped(apploader_offset + 0x18, &apploader_trailer, is_wii) || !volume.ReadSwapped(apploader_offset + 0x18, &apploader_trailer, partition) ||
apploader_entry == (u32)-1 || apploader_size + apploader_trailer == (u32)-1) apploader_entry == (u32)-1 || apploader_size + apploader_trailer == (u32)-1)
{ {
INFO_LOG(BOOT, "Invalid apploader. Your disc image is probably corrupted."); INFO_LOG(BOOT, "Invalid apploader. Your disc image is probably corrupted.");
return false; 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!!! // 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, INFO_LOG(MASTER_LOG, "DVDRead: offset: %08x memOffset: %08x length: %i", iDVDOffset,
iRamAddress, iLength); iRamAddress, iLength);
DVDRead(volume, iDVDOffset, iRamAddress, iLength, is_wii); DVDRead(volume, iDVDOffset, iRamAddress, iLength, partition);
} while (PowerPC::ppcState.gpr[3] != 0x00); } 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 // It's possible to boot DOL and ELF files without a disc inserted
if (volume) 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 // Booted from bootrom. 0xE5207C22 = booted from jtag
PowerPC::HostWrite_U32(0x0D15EA5E, 0x80000020); 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 // When booting a WAD or the system menu, there will probably not be a disc inserted
if (volume) 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(0x0D15EA5E, 0x00000020); // Another magic word
Memory::Write_U32(0x00000001, 0x00000024); // Unknown 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) if (volume->GetVolumeType() != DiscIO::Platform::WII_DISC)
return false; 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())) if (!SetupWiiMemory(volume, tmd.GetIOSId()))
return false; 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 // 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 // as the game is running. The 6 byte ID at 0x00 is overwritten sometime
// after this check during booting. // after this check during booting.
DVDRead(*volume, 0, 0x3180, 4, true); DVDRead(*volume, 0, 0x3180, 4, partition);
SetupBAT(/*is_wii*/ true); 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 // 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! // 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; return true;
} }

View File

@ -25,7 +25,6 @@
#include "Core/FifoPlayer/FifoDataFile.h" #include "Core/FifoPlayer/FifoDataFile.h"
#include "Core/HLE/HLE.h" #include "Core/HLE/HLE.h"
#include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/DVD/DVDThread.h"
#include "Core/HW/SI/SI.h" #include "Core/HW/SI/SI.h"
#include "Core/IOS/ES/Formats.h" #include "Core/IOS/ES/Formats.h"
#include "Core/IOS/USB/Bluetooth/BTBase.h" #include "Core/IOS/USB/Bluetooth/BTBase.h"
@ -746,11 +745,12 @@ void SConfig::ResetRunningGameMetadata()
SetRunningGameMetadata("00000000", 0, 0); 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; u64 title_id = 0;
volume.GetTitleID(&title_id); volume.GetTitleID(&title_id, partition);
SetRunningGameMetadata(volume.GetGameID(), title_id, volume.GetRevision()); SetRunningGameMetadata(volume.GetGameID(partition), title_id, volume.GetRevision(partition));
} }
void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd) 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. // the disc header instead of the TMD. They can differ.
// (IOS HLE ES calls us with a TMDReader rather than a volume when launching // (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.) // 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. // If not launching a disc game, just read everything from the TMD.
SetRunningGameMetadata(tmd.GetGameID(), tmd_title_id, tmd.GetTitleVersion()); SetRunningGameMetadata(tmd.GetGameID(), tmd_title_id, tmd.GetTitleVersion());
@ -935,7 +935,7 @@ bool SConfig::AutoSetup(EBootBS2 _BootBS2)
m_strFilename.c_str()); m_strFilename.c_str());
return false; return false;
} }
SetRunningGameMetadata(*pVolume); SetRunningGameMetadata(*pVolume, pVolume->GetGamePartition());
// Check if we have a Wii disc // Check if we have a Wii disc
bWii = pVolume->GetVolumeType() == DiscIO::Platform::WII_DISC; bWii = pVolume->GetVolumeType() == DiscIO::Platform::WII_DISC;

View File

@ -19,6 +19,7 @@ namespace DiscIO
{ {
enum class Language; enum class Language;
enum class Region; enum class Region;
struct Partition;
class IVolume; class IVolume;
} }
namespace IOS namespace IOS
@ -226,7 +227,7 @@ struct SConfig : NonCopyable
u64 GetTitleID() const { return m_title_id; } u64 GetTitleID() const { return m_title_id; }
u16 GetRevision() const { return m_revision; } u16 GetRevision() const { return m_revision; }
void ResetRunningGameMetadata(); 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 SetRunningGameMetadata(const IOS::ES::TMDReader& tmd);
void LoadDefaults(); void LoadDefaults();

View File

@ -218,6 +218,7 @@ static u32 s_pending_samples;
// Disc drive state // Disc drive state
static u32 s_error_code = 0; static u32 s_error_code = 0;
static DiscIO::Partition s_current_partition;
// Disc drive timing // Disc drive timing
static u64 s_read_buffer_start_time; static u64 s_read_buffer_start_time;
@ -243,12 +244,14 @@ void UpdateInterrupts();
void GenerateDIInterrupt(DIInterruptType _DVDInterrupt); void GenerateDIInterrupt(DIInterruptType _DVDInterrupt);
void WriteImmediate(u32 value, u32 output_address, bool reply_to_ios); 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 ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length, u32 output_length,
bool decrypt, ReplyType reply_type, DIInterruptType* interrupt_type); const DiscIO::Partition& partition, ReplyType reply_type,
DIInterruptType* interrupt_type);
u64 PackFinishExecutingCommandUserdata(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) void DoState(PointerWrap& p)
{ {
@ -271,6 +274,7 @@ void DoState(PointerWrap& p)
p.Do(s_pending_samples); p.Do(s_pending_samples);
p.Do(s_error_code); p.Do(s_error_code);
p.Do(s_current_partition);
p.Do(s_read_buffer_start_time); p.Do(s_read_buffer_start_time);
p.Do(s_read_buffer_end_time); p.Do(s_read_buffer_end_time);
@ -309,7 +313,8 @@ static u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process)
{ {
if (s_audio_position >= s_current_start + s_current_length) if (s_audio_position >= s_current_start + s_current_length)
{ {
DEBUG_LOG(DVDINTERFACE, "AdvanceDTK: NextStart=%08" PRIx64 ", NextLength=%08x, " DEBUG_LOG(DVDINTERFACE,
"AdvanceDTK: NextStart=%08" PRIx64 ", NextLength=%08x, "
"CurrentStart=%08" PRIx64 ", CurrentLength=%08x, AudioPos=%08" PRIx64, "CurrentStart=%08" PRIx64 ", CurrentLength=%08x, AudioPos=%08" PRIx64,
s_next_start, s_next_length, s_current_start, s_current_length, s_audio_position); s_next_start, s_next_length, s_current_start, s_current_length, s_audio_position);
@ -362,7 +367,8 @@ static void DTKStreamingCallback(const std::vector<u8>& audio_data, s64 cycles_l
ticks_to_dtk -= cycles_late; ticks_to_dtk -= cycles_late;
if (read_length > 0) 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 else
{ {
@ -432,6 +438,9 @@ void Shutdown()
void SetDisc(std::unique_ptr<DiscIO::IVolume> disc) void SetDisc(std::unique_ptr<DiscIO::IVolume> disc)
{ {
if (disc)
s_current_partition = disc->GetGamePartition();
DVDThread::SetDisc(std::move(disc)); DVDThread::SetDisc(std::move(disc));
SetLidOpen(); SetLidOpen();
} }
@ -498,9 +507,33 @@ void SetLidOpen()
GenerateDIInterrupt(INT_CVRINT); 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) void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
@ -620,8 +653,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 // 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 ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length, u32 output_length,
bool decrypt, ReplyType reply_type, DIInterruptType* interrupt_type) const DiscIO::Partition& partition, ReplyType reply_type,
DIInterruptType* interrupt_type)
{ {
if (!IsDiscInside()) if (!IsDiscInside())
{ {
@ -636,14 +670,14 @@ bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32
*interrupt_type = INT_TCINT; *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 " WARN_LOG(DVDINTERFACE, "Detected an attempt to read more data from the DVD "
"than what fits inside the out buffer. Clamping."); "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; return true;
} }
@ -679,7 +713,8 @@ 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 // Only seems to be used from WII_IPC, not through direct access
case DVDLowReadDiskID: case DVDLowReadDiskID:
INFO_LOG(DVDINTERFACE, "DVDLowReadDiskID"); INFO_LOG(DVDINTERFACE, "DVDLowReadDiskID");
command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, command_handled_by_thread =
ExecuteReadCommand(0, output_address, 0x20, output_length, DiscIO::PARTITION_NONE,
reply_type, &interrupt_type); reply_type, &interrupt_type);
break; break;
@ -688,8 +723,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, INFO_LOG(DVDINTERFACE, "DVDLowRead: DVDAddr: 0x%09" PRIx64 ", Size: 0x%x", (u64)command_2 << 2,
command_1); command_1);
command_handled_by_thread = command_handled_by_thread =
ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true, ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length,
reply_type, &interrupt_type); s_current_partition, reply_type, &interrupt_type);
break; break;
// Probably only used by Wii // Probably only used by Wii
@ -770,8 +805,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_2 + command_1) > 0x7ed40000) && (command_2 + command_1) < 0x7ed40008)))
{ {
command_handled_by_thread = command_handled_by_thread =
ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false, ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length,
reply_type, &interrupt_type); DiscIO::PARTITION_NONE, reply_type, &interrupt_type);
} }
else else
{ {
@ -815,18 +850,21 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
{ {
u64 iDVDOffset = (u64)command_1 << 2; u64 iDVDOffset = (u64)command_1 << 2;
INFO_LOG(DVDINTERFACE, "Read: DVDOffset=%08" PRIx64 INFO_LOG(DVDINTERFACE,
"Read: DVDOffset=%08" PRIx64
", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x", ", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x",
iDVDOffset, output_address, command_2, output_length); iDVDOffset, output_address, command_2, output_length);
command_handled_by_thread = ExecuteReadCommand( command_handled_by_thread =
iDVDOffset, output_address, command_2, output_length, false, reply_type, &interrupt_type); ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length,
DiscIO::PARTITION_NONE, reply_type, &interrupt_type);
} }
break; break;
case 0x40: // Read DiscID case 0x40: // Read DiscID
INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address)); INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address));
command_handled_by_thread = ExecuteReadCommand(0, output_address, 0x20, output_length, false, command_handled_by_thread =
ExecuteReadCommand(0, output_address, 0x20, output_length, DiscIO::PARTITION_NONE,
reply_type, &interrupt_type); reply_type, &interrupt_type);
break; break;
@ -936,7 +974,8 @@ void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_addr
switch (command_0 >> 16 & 0xFF) switch (command_0 >> 16 & 0xFF)
{ {
case 0x00: // Returns streaming status case 0x00: // Returns streaming status
INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status " INFO_LOG(DVDINTERFACE,
"(Audio): Stream Status: Request Audio status "
"AudioPos:%08" PRIx64 "/%08" PRIx64 " " "AudioPos:%08" PRIx64 "/%08" PRIx64 " "
"CurrentStart:%08" PRIx64 " CurrentLength:%08x", "CurrentStart:%08" PRIx64 " CurrentLength:%08x",
s_audio_position, s_current_start + s_current_length, s_current_start, s_audio_position, s_current_start + s_current_length, s_current_start,
@ -1099,7 +1138,8 @@ void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type
// Determines from a given read request how much of the request is buffered, // Determines from a given read request how much of the request is buffered,
// and how much is required to be read from disc. // 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. // 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 // If a future read falls within this window, part of the read may be returned
@ -1126,9 +1166,7 @@ void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, Rep
// The variable dvd_offset tracks the actual offset on the DVD // The variable dvd_offset tracks the actual offset on the DVD
// that the disc drive starts reading at, which differs in two ways: // 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. // It's rounded to a whole ECC block and never uses Wii partition addressing.
u64 dvd_offset = offset; u64 dvd_offset = DiscIO::CVolumeWiiCrypted::PartitionOffsetToRawOffset(offset, partition);
if (decrypt)
dvd_offset = DVDThread::PartitionOffsetToRawOffset(offset);
dvd_offset = Common::AlignDown(dvd_offset, DVD_ECC_BLOCK_SIZE); dvd_offset = Common::AlignDown(dvd_offset, DVD_ECC_BLOCK_SIZE);
if (SConfig::GetInstance().bFastDiscSpeed) if (SConfig::GetInstance().bFastDiscSpeed)
@ -1194,8 +1232,9 @@ void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, Rep
u32 buffered_blocks = 0; u32 buffered_blocks = 0;
u32 unbuffered_blocks = 0; u32 unbuffered_blocks = 0;
const u32 bytes_per_chunk = const u32 bytes_per_chunk = partition == DiscIO::PARTITION_NONE ?
decrypt ? DiscIO::CVolumeWiiCrypted::BLOCK_DATA_SIZE : DVD_ECC_BLOCK_SIZE; DVD_ECC_BLOCK_SIZE :
DiscIO::CVolumeWiiCrypted::BLOCK_DATA_SIZE;
while (length > 0) while (length > 0)
{ {
@ -1242,7 +1281,7 @@ void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, Rep
// Schedule this read to complete at the appropriate time // Schedule this read to complete at the appropriate time
const ReplyType chunk_reply_type = chunk_length == length ? reply_type : ReplyType::NoReply; 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); chunk_reply_type, ticks_until_completion);
// Advance the read window // Advance the read window
@ -1282,7 +1321,8 @@ void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, Rep
s_read_buffer_end_offset - s_read_buffer_start_offset, wii_disc)); s_read_buffer_end_offset - s_read_buffer_start_offset, wii_disc));
} }
DEBUG_LOG(DVDINTERFACE, "Schedule reads: ECC blocks unbuffered=%d, buffered=%d, " DEBUG_LOG(DVDINTERFACE,
"Schedule reads: ECC blocks unbuffered=%d, buffered=%d, "
"ticks=%" PRId64 ", time=%" PRId64 " us", "ticks=%" PRId64 ", time=%" PRId64 " us",
unbuffered_blocks, buffered_blocks, ticks_until_completion, unbuffered_blocks, buffered_blocks, ticks_until_completion,
ticks_until_completion * 1000000 / SystemTimers::GetTicksPerSecond()); ticks_until_completion * 1000000 / SystemTimers::GetTicksPerSecond());

View File

@ -13,6 +13,7 @@ class PointerWrap;
namespace DiscIO namespace DiscIO
{ {
class IVolume; class IVolume;
struct Partition;
} }
namespace MMIO namespace MMIO
{ {
@ -113,9 +114,16 @@ bool IsDiscInside();
void ChangeDiscAsHost(const std::string& new_path); // Can only be called by the host thread 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 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, // 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) // 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, void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address,
u32 output_length, bool reply_to_ios); u32 output_length, bool reply_to_ios);

View File

@ -41,7 +41,7 @@ struct ReadRequest
u32 output_address; u32 output_address;
u64 dvd_offset; u64 dvd_offset;
u32 length; u32 length;
bool decrypt; DiscIO::Partition partition;
// This determines which code DVDInterface will run to reply // This determines which code DVDInterface will run to reply
// to the emulated software. We can't use callbacks, // to the emulated software. We can't use callbacks,
@ -68,8 +68,8 @@ static void DVDThread();
static void WaitUntilIdle(); static void WaitUntilIdle();
static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length,
bool decrypt, DVDInterface::ReplyType reply_type, const DiscIO::Partition& partition,
s64 ticks_until_completion); DVDInterface::ReplyType reply_type, s64 ticks_until_completion);
static void FinishRead(u64 id, s64 cycles_late); static void FinishRead(u64 id, s64 cycles_late);
static CoreTiming::EventType* s_finish_read; static CoreTiming::EventType* s_finish_read;
@ -191,42 +191,25 @@ bool HasDisc()
return s_disc != nullptr; 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() DiscIO::Platform GetDiscType()
{ {
// GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary. // GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary.
return s_disc->GetVolumeType(); return s_disc->GetVolumeType();
} }
IOS::ES::TMDReader GetTMD() IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition)
{ {
WaitUntilIdle(); WaitUntilIdle();
return s_disc->GetTMD(); return s_disc->GetTMD(partition);
} }
IOS::ES::TicketReader GetTicket() IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition)
{ {
WaitUntilIdle(); WaitUntilIdle();
return s_disc->GetTicket(); return s_disc->GetTicket(partition);
} }
bool ChangePartition(u64 offset) bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, u64 title_id)
{
WaitUntilIdle();
const bool success = s_disc->ChangePartition(offset);
FileMonitor::SetFileSystem(s_disc.get());
return success;
}
bool UpdateRunningGameMetadata(u64 title_id)
{ {
if (!s_disc) if (!s_disc)
return false; return false;
@ -234,24 +217,24 @@ bool UpdateRunningGameMetadata(u64 title_id)
WaitUntilIdle(); WaitUntilIdle();
u64 volume_title_id; u64 volume_title_id;
if (!s_disc->GetTitleID(&volume_title_id)) if (!s_disc->GetTitleID(&volume_title_id, partition))
return false; return false;
if (volume_title_id != title_id) if (volume_title_id != title_id)
return false; return false;
SConfig::GetInstance().SetRunningGameMetadata(*s_disc); SConfig::GetInstance().SetRunningGameMetadata(*s_disc, partition);
return true; return true;
} }
bool UpdateRunningGameMetadata() bool UpdateRunningGameMetadata(const DiscIO::Partition& partition)
{ {
if (!s_disc) if (!s_disc)
return false; return false;
DVDThread::WaitUntilIdle(); DVDThread::WaitUntilIdle();
SConfig::GetInstance().SetRunningGameMetadata(*s_disc); SConfig::GetInstance().SetRunningGameMetadata(*s_disc, partition);
return true; return true;
} }
@ -266,22 +249,23 @@ void WaitUntilIdle()
StartDVDThread(); StartDVDThread();
} }
void StartRead(u64 dvd_offset, u32 length, bool decrypt, DVDInterface::ReplyType reply_type, void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition,
s64 ticks_until_completion)
{
StartReadInternal(false, 0, dvd_offset, length, decrypt, 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) DVDInterface::ReplyType reply_type, s64 ticks_until_completion)
{ {
StartReadInternal(true, output_address, dvd_offset, length, decrypt, reply_type, StartReadInternal(false, 0, dvd_offset, length, partition, reply_type, 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, partition, reply_type,
ticks_until_completion); ticks_until_completion);
} }
static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length, static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length,
bool decrypt, DVDInterface::ReplyType reply_type, const DiscIO::Partition& partition,
s64 ticks_until_completion) DVDInterface::ReplyType reply_type, s64 ticks_until_completion)
{ {
_assert_(Core::IsCPUThread()); _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.output_address = output_address;
request.dvd_offset = dvd_offset; request.dvd_offset = dvd_offset;
request.length = length; request.length = length;
request.decrypt = decrypt; request.partition = partition;
request.reply_type = reply_type; request.reply_type = reply_type;
u64 id = s_next_id++; u64 id = s_next_id++;
@ -381,10 +365,10 @@ static void DVDThread()
ReadRequest request; ReadRequest request;
while (s_request_queue.Pop(request)) while (s_request_queue.Pop(request))
{ {
FileMonitor::Log(request.dvd_offset, request.decrypt); FileMonitor::Log(request.dvd_offset, request.partition);
std::vector<u8> buffer(request.length); std::vector<u8> 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); buffer.resize(0);
request.realtime_done_us = Common::Timer::GetTimeUs(); request.realtime_done_us = Common::Timer::GetTimeUs();

View File

@ -10,6 +10,10 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
class PointerWrap; class PointerWrap;
namespace DiscIO
{
struct Partition;
}
namespace DVDInterface namespace DVDInterface
{ {
enum class ReplyType : u32; enum class ReplyType : u32;
@ -37,20 +41,19 @@ void DoState(PointerWrap& p);
void SetDisc(std::unique_ptr<DiscIO::IVolume> disc); void SetDisc(std::unique_ptr<DiscIO::IVolume> disc);
bool HasDisc(); bool HasDisc();
u64 PartitionOffsetToRawOffset(u64 offset);
DiscIO::Platform GetDiscType(); DiscIO::Platform GetDiscType();
IOS::ES::TMDReader GetTMD(); IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition);
IOS::ES::TicketReader GetTicket(); IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition);
bool ChangePartition(u64 offset);
// If a disc is inserted and its title ID is equal to the title_id argument, returns true and // 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. // calls SConfig::SetRunningGameMetadata(IVolume&, Partition&). Otherwise, returns false.
bool UpdateRunningGameMetadata(u64 title_id); bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, u64 title_id);
// If a disc is inserted, returns true and calls // If a disc is inserted, returns true and calls
// SConfig::SetRunningGameMetadata(IVolume&). Otherwise, returns false. // SConfig::SetRunningGameMetadata(IVolume&, Partition&). Otherwise, returns false.
bool UpdateRunningGameMetadata(); bool UpdateRunningGameMetadata(const DiscIO::Partition& partition);
void StartRead(u64 dvd_offset, u32 length, bool decrypt, DVDInterface::ReplyType reply_type, void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition,
s64 ticks_until_completion);
void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length, bool decrypt,
DVDInterface::ReplyType reply_type, s64 ticks_until_completion); 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);
} }

View File

@ -21,9 +21,10 @@
namespace FileMonitor namespace FileMonitor
{ {
static const DiscIO::IVolume* s_volume = nullptr; static const DiscIO::IVolume* s_volume;
static bool s_wii_disc; static bool s_new_volume = false;
static std::unique_ptr<DiscIO::IFileSystem> s_filesystem; static std::unique_ptr<DiscIO::IFileSystem> s_filesystem;
static DiscIO::Partition s_partition;
static std::string s_previous_file; static std::string s_previous_file;
// Filtered files // 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 // 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 // create it later once we know that it actually will get used
s_volume = volume; s_volume = volume;
s_new_volume = true;
// 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;
} }
// Logs access to files in the file system set by SetFileSystem // 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 // Do nothing if the log isn't selected
if (!LogManager::GetInstance()->IsEnabled(LogTypes::FILEMON, LogTypes::LWARNING)) if (!LogManager::GetInstance()->IsEnabled(LogTypes::FILEMON, LogTypes::LWARNING))
return; return;
// If a new volume has been set, use the file system of that volume // If the volume or partition changed, load the filesystem of the new partition
if (s_volume) if (s_new_volume || s_partition != partition)
{ {
s_wii_disc = s_volume->GetVolumeType() == DiscIO::Platform::WII_DISC; // Wii discs don't have PARTITION_NONE filesystems, so let's not waste time trying to read one
if (decrypt != s_wii_disc) 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; 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(); 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 // Do nothing if there is no valid file system
if (!s_filesystem) if (!s_filesystem)
return; return;

View File

@ -4,13 +4,11 @@
#pragma once #pragma once
#include <memory>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
namespace DiscIO namespace DiscIO
{ {
class IFileSystem; struct Partition;
class IVolume; class IVolume;
} }
@ -20,5 +18,5 @@ namespace FileMonitor
// with nullptr, the volume must remain valid until the next SetFileSystem call. // with nullptr, the volume must remain valid until the next SetFileSystem call.
void SetFileSystem(const DiscIO::IVolume* volume); void SetFileSystem(const DiscIO::IVolume* volume);
// Logs access to files in the file system set by SetFileSystem // 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);
} }

View File

@ -101,16 +101,18 @@ IPCCommandResult DI::IOCtlV(const IOCtlVRequest& request)
_dbg_assert_msg_(IOS_DI, request.in_vectors[2].address == 0, _dbg_assert_msg_(IOS_DI, request.in_vectors[2].address == 0,
"DVDLowOpenPartition with cert chain"); "DVDLowOpenPartition with cert chain");
u64 const partition_offset = ((u64)Memory::Read_U32(request.in_vectors[0].address + 4) << 2); const u64 partition_offset =
DVDInterface::ChangePartition(partition_offset); static_cast<u64>(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); INFO_LOG(IOS_DI, "DVDLowOpenPartition: partition_offset 0x%016" PRIx64, partition_offset);
// Read TMD to the buffer // Read TMD to the buffer
const IOS::ES::TMDReader tmd = DVDThread::GetTMD(); const IOS::ES::TMDReader tmd = DVDThread::GetTMD(partition);
const std::vector<u8> raw_tmd = tmd.GetRawTMD(); const std::vector<u8> raw_tmd = tmd.GetRawTMD();
Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size()); 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; return_value = 1;
break; break;

View File

@ -19,7 +19,6 @@
#include "Core/HLE/HLE.h" #include "Core/HLE/HLE.h"
#include "Core/HW/DSP.h" #include "Core/HW/DSP.h"
#include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/DVD/DVDThread.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "Core/HW/SystemTimers.h" #include "Core/HW/SystemTimers.h"
#include "Core/IOS/ES/Formats.h" #include "Core/IOS/ES/Formats.h"
@ -170,7 +169,7 @@ bool Load()
Memory::Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE); Memory::Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE);
NOTICE_LOG(IOS, "IPL ready."); NOTICE_LOG(IOS, "IPL ready.");
SConfig::GetInstance().m_BootType = SConfig::BOOT_MIOS; SConfig::GetInstance().m_BootType = SConfig::BOOT_MIOS;
DVDThread::UpdateRunningGameMetadata(); DVDInterface::UpdateRunningGameMetadata();
return true; return true;
} }
} // namespace MIOS } // namespace MIOS

View File

@ -71,7 +71,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread; static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system // 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. // Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list, // Versions after 42 don't need to be added to this list,

View File

@ -123,15 +123,15 @@ void DiscScrubber::MarkAsUsedE(u64 partition_data_offset, u64 offset, u64 size)
} }
// Helper functions for reading the BE volume // 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; u32 temp_buffer;
if (!m_disc->ReadSwapped(offset, &temp_buffer, decrypt)) if (!m_disc->ReadSwapped(offset, &temp_buffer, partition))
return false; return false;
buffer = static_cast<u64>(temp_buffer) << 2; buffer = static_cast<u64>(temp_buffer) << 2;
return true; return true;
@ -142,44 +142,21 @@ bool DiscScrubber::ParseDisc()
// Mark the header as used - it's mostly 0s anyways // Mark the header as used - it's mostly 0s anyways
MarkAsUsed(0, 0x50000); 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) || PartitionHeader header;
!ReadFromVolume(0x40000 + (x * 8) + 4, m_partition_group[x].partitions_offset, false))
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; return false;
} }
// Read all partitions
for (u32 i = 0; i < m_partition_group[x].num_partitions; i++)
{
Partition partition;
partition.group_number = x;
partition.number = i;
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, 0x2c0);
MarkAsUsed(partition.offset + header.tmd_offset, header.tmd_size); MarkAsUsed(partition.offset + header.tmd_offset, header.tmd_size);
@ -190,78 +167,60 @@ bool DiscScrubber::ParseDisc()
// MarkAsUsed(partition.offset + header.data_offset, header.data_size); // MarkAsUsed(partition.offset + header.data_offset, header.data_size);
// Parse Data! This is where the big gain is // Parse Data! This is where the big gain is
if (!ParsePartitionData(partition)) if (!ParsePartitionData(partition, &header))
return false; return false;
} }
}
return true; return true;
} }
// Operations dealing with encrypted space are done here - the volume is swapped to allow this // Operations dealing with encrypted space are done here
bool DiscScrubber::ParsePartitionData(Partition& partition) bool DiscScrubber::ParsePartitionData(const Partition& partition, PartitionHeader* header)
{ {
bool parsed_ok = true; std::unique_ptr<IFileSystem> filesystem(CreateFileSystem(m_disc.get(), partition));
if (!filesystem)
// Switch out the main volume temporarily
std::unique_ptr<IVolume> old_volume;
m_disc.swap(old_volume);
// Ready some stuff
m_disc = CreateVolumeFromFilename(m_filename, partition.group_number, partition.number);
if (m_disc == nullptr)
{ {
ERROR_LOG(DISCIO, "Failed to create volume from file %s", m_filename.c_str()); ERROR_LOG(DISCIO, "Failed to read file system for the partition at 0x%" PRIx64,
m_disc.swap(old_volume); partition.offset);
return false; return false;
} }
std::unique_ptr<IFileSystem> filesystem(CreateFileSystem(m_disc.get())); const u64 partition_data_offset = partition.offset + header->data_offset;
if (!filesystem)
{
ERROR_LOG(DISCIO, "Failed to create filesystem for group %u partition %u",
partition.group_number, partition.number);
parsed_ok = false;
}
else
{
// Mark things as used which are not in the filesystem // Mark things as used which are not in the filesystem
// Header, Header Information, Apploader // Header, Header Information, Apploader
parsed_ok = parsed_ok && ReadFromVolume(0x2440 + 0x14, partition.header.apploader_size, true); if (!ReadFromVolume(0x2440 + 0x14, header->apploader_size, partition) ||
parsed_ok = !ReadFromVolume(0x2440 + 0x18, header->apploader_size, partition))
parsed_ok && ReadFromVolume(0x2440 + 0x18, partition.header.apploader_trailer_size, true); {
MarkAsUsedE(partition.offset + partition.header.data_offset, 0, return false;
0x2440 + partition.header.apploader_size + partition.header.apploader_trailer_size); }
MarkAsUsedE(partition_data_offset, 0,
0x2440 + header->apploader_size + header->apploader_trailer_size);
// DOL // DOL
partition.header.dol_offset = filesystem->GetBootDOLOffset(); header->dol_offset = filesystem->GetBootDOLOffset();
partition.header.dol_size = filesystem->GetBootDOLSize(partition.header.dol_offset); header->dol_size = filesystem->GetBootDOLSize(header->dol_offset);
parsed_ok = parsed_ok && partition.header.dol_offset && partition.header.dol_size; if (header->dol_offset == 0 || header->dol_size == 0)
MarkAsUsedE(partition.offset + partition.header.data_offset, partition.header.dol_offset, return false;
partition.header.dol_size); MarkAsUsedE(partition_data_offset, header->dol_offset, header->dol_size);
// FST // FST
parsed_ok = parsed_ok && ReadFromVolume(0x424, partition.header.fst_offset, true); if (!ReadFromVolume(0x424, header->fst_offset, partition) ||
parsed_ok = parsed_ok && ReadFromVolume(0x428, partition.header.fst_size, true); !ReadFromVolume(0x428, header->fst_size, partition))
MarkAsUsedE(partition.offset + partition.header.data_offset, partition.header.fst_offset, {
partition.header.fst_size); return false;
}
MarkAsUsedE(partition_data_offset, header->fst_offset, header->fst_size);
// Go through the filesystem and mark entries as used // Go through the filesystem and mark entries as used
for (const SFileInfo& file : filesystem->GetFileList()) for (const SFileInfo& file : filesystem->GetFileList())
{ {
DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str()); DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str());
if ((file.m_NameOffset & 0x1000000) == 0) if ((file.m_NameOffset & 0x1000000) == 0)
{ MarkAsUsedE(partition_data_offset, file.m_Offset, file.m_FileSize);
MarkAsUsedE(partition.offset + partition.header.data_offset, file.m_Offset,
file.m_FileSize);
}
}
} }
// Swap back return true;
m_disc.swap(old_volume);
return parsed_ok;
} }
} // namespace DiscIO } // namespace DiscIO

View File

@ -26,6 +26,7 @@ class IOFile;
namespace DiscIO namespace DiscIO
{ {
class IVolume; class IVolume;
struct Partition;
class DiscScrubber final class DiscScrubber final
{ {
@ -57,34 +58,16 @@ private:
u32 apploader_trailer_size; 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<Partition> partitions;
};
void MarkAsUsed(u64 offset, u64 size); void MarkAsUsed(u64 offset, u64 size);
void MarkAsUsedE(u64 partition_data_offset, 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, u32& buffer, const Partition& partition);
bool ReadFromVolume(u64 offset, u64& buffer, bool decrypt); bool ReadFromVolume(u64 offset, u64& buffer, const Partition& partition);
bool ParseDisc(); bool ParseDisc();
bool ParsePartitionData(Partition& partition); bool ParsePartitionData(const Partition& partition, PartitionHeader* header);
std::string m_filename; std::string m_filename;
std::unique_ptr<IVolume> m_disc; std::unique_ptr<IVolume> m_disc;
std::array<PartitionGroup, 4> m_partition_group{};
std::vector<u8> m_free_table; std::vector<u8> m_free_table;
u64 m_file_size = 0; u64 m_file_size = 0;
u64 m_block_count = 0; u64 m_block_count = 0;

View File

@ -20,8 +20,8 @@
namespace DiscIO namespace DiscIO
{ {
CFileSystemGCWii::CFileSystemGCWii(const IVolume* _rVolume) CFileSystemGCWii::CFileSystemGCWii(const IVolume* _rVolume, const Partition& partition)
: IFileSystem(_rVolume), m_Initialized(false), m_Valid(false), m_Wii(false) : IFileSystem(_rVolume, partition), m_Initialized(false), m_Valid(false), m_offset_shift(0)
{ {
m_Valid = DetectFileSystem(); 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, read_length, _OffsetInFile, _rFullPath.c_str(), pFileInfo->m_Offset,
pFileInfo->m_FileSize); 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; return read_length;
} }
@ -111,7 +111,7 @@ bool CFileSystemGCWii::ExportFile(const std::string& _rFullPath,
std::vector<u8> buffer(readSize); std::vector<u8> buffer(readSize);
result = m_rVolume->Read(fileOffset, readSize, &buffer[0], m_Wii); result = m_rVolume->Read(fileOffset, readSize, &buffer[0], m_partition);
if (!result) if (!result)
break; break;
@ -130,14 +130,14 @@ bool CFileSystemGCWii::ExportApploader(const std::string& _rExportFolder) const
u32 apploader_size; u32 apploader_size;
u32 trailer_size; u32 trailer_size;
const u32 header_size = 0x20; const u32 header_size = 0x20;
if (!m_rVolume->ReadSwapped(0x2440 + 0x14, &apploader_size, m_Wii) || if (!m_rVolume->ReadSwapped(0x2440 + 0x14, &apploader_size, m_partition) ||
!m_rVolume->ReadSwapped(0x2440 + 0x18, &trailer_size, m_Wii)) !m_rVolume->ReadSwapped(0x2440 + 0x18, &trailer_size, m_partition))
return false; return false;
apploader_size += trailer_size + header_size; apploader_size += trailer_size + header_size;
DEBUG_LOG(DISCIO, "Apploader size -> %x", apploader_size); DEBUG_LOG(DISCIO, "Apploader size -> %x", apploader_size);
std::vector<u8> buffer(apploader_size); std::vector<u8> 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"); std::string exportName(_rExportFolder + "/apploader.img");
@ -155,8 +155,8 @@ bool CFileSystemGCWii::ExportApploader(const std::string& _rExportFolder) const
u64 CFileSystemGCWii::GetBootDOLOffset() const u64 CFileSystemGCWii::GetBootDOLOffset() const
{ {
u32 offset = 0; u32 offset = 0;
m_rVolume->ReadSwapped(0x420, &offset, m_Wii); m_rVolume->ReadSwapped(0x420, &offset, m_partition);
return static_cast<u64>(offset) << GetOffsetShift(); return static_cast<u64>(offset) << m_offset_shift;
} }
u32 CFileSystemGCWii::GetBootDOLSize(u64 dol_offset) const u32 CFileSystemGCWii::GetBootDOLSize(u64 dol_offset) const
@ -173,8 +173,8 @@ u32 CFileSystemGCWii::GetBootDOLSize(u64 dol_offset) const
// Iterate through the 7 code segments // Iterate through the 7 code segments
for (u8 i = 0; i < 7; i++) for (u8 i = 0; i < 7; i++)
{ {
if (!m_rVolume->ReadSwapped(dol_offset + 0x00 + i * 4, &offset, m_Wii) || if (!m_rVolume->ReadSwapped(dol_offset + 0x00 + i * 4, &offset, m_partition) ||
!m_rVolume->ReadSwapped(dol_offset + 0x90 + i * 4, &size, m_Wii)) !m_rVolume->ReadSwapped(dol_offset + 0x90 + i * 4, &size, m_partition))
return 0; return 0;
dol_size = std::max(offset + size, dol_size); 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 // Iterate through the 11 data segments
for (u8 i = 0; i < 11; i++) for (u8 i = 0; i < 11; i++)
{ {
if (!m_rVolume->ReadSwapped(dol_offset + 0x1c + i * 4, &offset, m_Wii) || if (!m_rVolume->ReadSwapped(dol_offset + 0x1c + i * 4, &offset, m_partition) ||
!m_rVolume->ReadSwapped(dol_offset + 0xac + i * 4, &size, m_Wii)) !m_rVolume->ReadSwapped(dol_offset + 0xac + i * 4, &size, m_partition))
return 0; return 0;
dol_size = std::max(offset + size, dol_size); dol_size = std::max(offset + size, dol_size);
} }
@ -200,7 +200,7 @@ bool CFileSystemGCWii::ExportDOL(const std::string& _rExportFolder) const
return false; return false;
std::vector<u8> buffer(DolSize); std::vector<u8> 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"); 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 CFileSystemGCWii::GetStringFromOffset(u64 _Offset) const
{ {
std::string data(255, 0x00); 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()); data.erase(std::find(data.begin(), data.end(), 0x00), data.end());
// TODO: Should we really always use SHIFT-JIS? // TODO: Should we really always use SHIFT-JIS?
@ -251,14 +251,14 @@ const SFileInfo* CFileSystemGCWii::FindFileInfo(const std::string& _rFullPath)
bool CFileSystemGCWii::DetectFileSystem() bool CFileSystemGCWii::DetectFileSystem()
{ {
u32 magic_bytes; 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; m_offset_shift = 2; // Wii file system
return 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; m_offset_shift = 0; // GameCube file system
return true; return true;
} }
@ -268,21 +268,20 @@ bool CFileSystemGCWii::DetectFileSystem()
void CFileSystemGCWii::InitFileSystem() void CFileSystemGCWii::InitFileSystem()
{ {
m_Initialized = true; m_Initialized = true;
u32 const shift = GetOffsetShift();
// read the whole FST // read the whole FST
u32 fst_offset_unshifted; 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; return;
u64 FSTOffset = static_cast<u64>(fst_offset_unshifted) << shift; u64 FSTOffset = static_cast<u64>(fst_offset_unshifted) << m_offset_shift;
// read all fileinfos // read all fileinfos
u32 name_offset, offset, size; u32 name_offset, offset, size;
if (!m_rVolume->ReadSwapped(FSTOffset + 0x0, &name_offset, m_Wii) || if (!m_rVolume->ReadSwapped(FSTOffset + 0x0, &name_offset, m_partition) ||
!m_rVolume->ReadSwapped(FSTOffset + 0x4, &offset, m_Wii) || !m_rVolume->ReadSwapped(FSTOffset + 0x4, &offset, m_partition) ||
!m_rVolume->ReadSwapped(FSTOffset + 0x8, &size, m_Wii)) !m_rVolume->ReadSwapped(FSTOffset + 0x8, &size, m_partition))
return; return;
SFileInfo root = {name_offset, static_cast<u64>(offset) << shift, size}; SFileInfo root = {name_offset, static_cast<u64>(offset) << m_offset_shift, size};
if (!root.IsDirectory()) if (!root.IsDirectory())
return; return;
@ -308,12 +307,12 @@ void CFileSystemGCWii::InitFileSystem()
{ {
const u64 read_offset = FSTOffset + (i * 0xC); const u64 read_offset = FSTOffset + (i * 0xC);
name_offset = 0; 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; offset = 0;
m_rVolume->ReadSwapped(read_offset + 0x4, &offset, m_Wii); m_rVolume->ReadSwapped(read_offset + 0x4, &offset, m_partition);
size = 0; 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<u64>(offset) << shift, size); m_FileInfoVector.emplace_back(name_offset, static_cast<u64>(offset) << m_offset_shift, size);
NameTableOffset += 0xC; NameTableOffset += 0xC;
} }
@ -351,9 +350,4 @@ size_t CFileSystemGCWii::BuildFilenames(const size_t _FirstIndex, const size_t _
return CurrentIndex; return CurrentIndex;
} }
u32 CFileSystemGCWii::GetOffsetShift() const
{
return m_Wii ? 2 : 0;
}
} // namespace } // namespace

View File

@ -14,11 +14,12 @@
namespace DiscIO namespace DiscIO
{ {
class IVolume; class IVolume;
struct Partition;
class CFileSystemGCWii : public IFileSystem class CFileSystemGCWii : public IFileSystem
{ {
public: public:
CFileSystemGCWii(const IVolume* _rVolume); CFileSystemGCWii(const IVolume* _rVolume, const Partition& partition);
virtual ~CFileSystemGCWii(); virtual ~CFileSystemGCWii();
bool IsValid() const override { return m_Valid; } bool IsValid() const override { return m_Valid; }
@ -36,7 +37,7 @@ public:
private: private:
bool m_Initialized; bool m_Initialized;
bool m_Valid; bool m_Valid;
bool m_Wii; u32 m_offset_shift;
std::vector<SFileInfo> m_FileInfoVector; std::vector<SFileInfo> m_FileInfoVector;
std::string GetStringFromOffset(u64 _Offset) const; std::string GetStringFromOffset(u64 _Offset) const;
@ -45,7 +46,6 @@ private:
void InitFileSystem(); void InitFileSystem();
size_t BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex, size_t BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex,
const std::string& _szDirectory, u64 _NameTableOffset); const std::string& _szDirectory, u64 _NameTableOffset);
u32 GetOffsetShift() const;
}; };
} // namespace } // namespace

View File

@ -5,10 +5,12 @@
#include "DiscIO/Filesystem.h" #include "DiscIO/Filesystem.h"
#include <memory> #include <memory>
#include "DiscIO/FileSystemGCWii.h" #include "DiscIO/FileSystemGCWii.h"
#include "DiscIO/Volume.h"
namespace DiscIO 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<IFileSystem> CreateFileSystem(const IVolume* volume) std::unique_ptr<IFileSystem> CreateFileSystem(const IVolume* volume, const Partition& partition)
{ {
if (!volume) if (!volume)
return nullptr; return nullptr;
std::unique_ptr<IFileSystem> filesystem = std::make_unique<CFileSystemGCWii>(volume); std::unique_ptr<IFileSystem> filesystem = std::make_unique<CFileSystemGCWii>(volume, partition);
if (!filesystem) if (!filesystem)
return nullptr; return nullptr;

View File

@ -9,11 +9,10 @@
#include <vector> #include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "DiscIO/Volume.h"
namespace DiscIO namespace DiscIO
{ {
class IVolume;
// file info of an FST entry // file info of an FST entry
struct SFileInfo struct SFileInfo
{ {
@ -35,7 +34,7 @@ struct SFileInfo
class IFileSystem class IFileSystem
{ {
public: public:
IFileSystem(const IVolume* _rVolume); IFileSystem(const IVolume* _rVolume, const Partition& partition);
virtual ~IFileSystem(); virtual ~IFileSystem();
virtual bool IsValid() const = 0; virtual bool IsValid() const = 0;
@ -50,10 +49,12 @@ public:
virtual u64 GetBootDOLOffset() const = 0; virtual u64 GetBootDOLOffset() const = 0;
virtual u32 GetBootDOLSize(u64 dol_offset) const = 0; virtual u32 GetBootDOLSize(u64 dol_offset) const = 0;
virtual const Partition GetPartition() const { return m_partition; }
protected: protected:
const IVolume* m_rVolume; const IVolume* const m_rVolume;
const Partition m_partition;
}; };
std::unique_ptr<IFileSystem> CreateFileSystem(const IVolume* volume); std::unique_ptr<IFileSystem> CreateFileSystem(const IVolume* volume, const Partition& partition);
} // namespace } // namespace

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <cstring> #include <cstring>
#include <limits>
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
@ -19,49 +20,71 @@ namespace DiscIO
{ {
enum class BlobType; enum class BlobType;
struct Partition final
{
Partition() : offset(std::numeric_limits<u64>::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<u64>::max() - 1);
class IVolume class IVolume
{ {
public: public:
IVolume() {} IVolume() {}
virtual ~IVolume() {} virtual ~IVolume() {}
// decrypt parameter must be false if not reading a Wii disc virtual bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const = 0;
virtual bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const = 0;
template <typename T> template <typename T>
bool ReadSwapped(u64 offset, T* buffer, bool decrypt) const bool ReadSwapped(u64 offset, T* buffer, const Partition& partition) const
{ {
T temp; T temp;
if (!Read(offset, sizeof(T), reinterpret_cast<u8*>(&temp), decrypt)) if (!Read(offset, sizeof(T), reinterpret_cast<u8*>(&temp), partition))
return false; return false;
*buffer = Common::FromBigEndian(temp); *buffer = Common::FromBigEndian(temp);
return true; return true;
} }
virtual std::vector<Partition> GetPartitions() const { return {{}}; }
virtual bool GetTitleID(u64*) const { return false; } virtual Partition GetGamePartition() const { return PARTITION_NONE; }
virtual IOS::ES::TicketReader GetTicket() const { return {}; } bool GetTitleID(u64* buffer) const { return GetTitleID(buffer, GetGamePartition()); }
virtual IOS::ES::TMDReader GetTMD() const { return {}; } virtual bool GetTitleID(u64* buffer, const Partition& partition) const { return false; }
virtual u64 PartitionOffsetToRawOffset(u64 offset) const { return offset; } virtual IOS::ES::TicketReader GetTicket(const Partition& partition) const { return {}; }
virtual std::string GetGameID() const = 0; virtual IOS::ES::TMDReader GetTMD(const Partition& partition) const { return {}; }
virtual std::string GetMakerID() const = 0; std::string GetGameID() const { return GetGameID(GetGamePartition()); }
virtual u16 GetRevision() const = 0; virtual std::string GetGameID(const Partition& partition) const = 0;
virtual std::string GetInternalName() 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<Language, std::string> GetShortNames() const { return {{}}; } virtual std::map<Language, std::string> GetShortNames() const { return {{}}; }
virtual std::map<Language, std::string> GetLongNames() const { return {{}}; } virtual std::map<Language, std::string> GetLongNames() const { return {{}}; }
virtual std::map<Language, std::string> GetShortMakers() const { return {{}}; } virtual std::map<Language, std::string> GetShortMakers() const { return {{}}; }
virtual std::map<Language, std::string> GetLongMakers() const { return {{}}; } virtual std::map<Language, std::string> GetLongMakers() const { return {{}}; }
virtual std::map<Language, std::string> GetDescriptions() const { return {{}}; } virtual std::map<Language, std::string> GetDescriptions() const { return {{}}; }
virtual std::vector<u32> GetBanner(int* width, int* height) const = 0; virtual std::vector<u32> GetBanner(int* width, int* height) const = 0;
virtual u64 GetFSTSize() const = 0; u64 GetFSTSize() const { return GetFSTSize(GetGamePartition()); }
virtual std::string GetApploaderDate() const = 0; 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 // 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 Platform GetVolumeType() const = 0;
virtual bool SupportsIntegrityCheck() const { return false; } virtual bool SupportsIntegrityCheck() const { return false; }
virtual bool CheckIntegrity() const { return false; } virtual bool CheckIntegrity(const Partition& partition) const { return false; }
virtual bool ChangePartition(u64 offset) { return false; }
virtual Region GetRegion() const = 0; 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; 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; virtual u64 GetSize() const = 0;
// Size on disc (compressed size) // Size on disc (compressed size)
virtual u64 GetRawSize() const = 0; virtual u64 GetRawSize() const = 0;

View File

@ -9,8 +9,6 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <mbedtls/aes.h>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
@ -33,24 +31,9 @@ enum EDiscType
DISC_TYPE_WAD 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<IVolume> CreateVolumeFromCryptedWiiImage(std::unique_ptr<IBlobReader> reader,
u32 partition_group,
u32 volume_type, u32 volume_number);
EDiscType GetDiscType(IBlobReader& _rReader); EDiscType GetDiscType(IBlobReader& _rReader);
std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename, u32 partition_group, std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename)
u32 volume_number)
{ {
std::unique_ptr<IBlobReader> reader(CreateBlobReader(filename)); std::unique_ptr<IBlobReader> reader(CreateBlobReader(filename));
if (reader == nullptr) if (reader == nullptr)
@ -66,7 +49,7 @@ std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename, u
return std::make_unique<CVolumeWAD>(std::move(reader)); return std::make_unique<CVolumeWAD>(std::move(reader));
case DISC_TYPE_WII_CONTAINER: case DISC_TYPE_WII_CONTAINER:
return CreateVolumeFromCryptedWiiImage(std::move(reader), partition_group, 0, volume_number); return std::make_unique<CVolumeWiiCrypted>(std::move(reader));
case DISC_TYPE_UNK: case DISC_TYPE_UNK:
return nullptr; return nullptr;
@ -85,110 +68,6 @@ std::unique_ptr<IVolume> CreateVolumeFromDirectory(const std::string& directory,
return nullptr; 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), &region);
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<IVolume> CreateVolumeFromCryptedWiiImage(std::unique_ptr<IBlobReader> 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<SPartition> 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<CVolumeWiiCrypted>(std::move(reader), partition.offset, volume_key);
}
}
return nullptr;
}
EDiscType GetDiscType(IBlobReader& _rReader) EDiscType GetDiscType(IBlobReader& _rReader)
{ {
CBlobBigEndianReader Reader(_rReader); CBlobBigEndianReader Reader(_rReader);

View File

@ -7,18 +7,13 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "Common/CommonTypes.h"
namespace DiscIO namespace DiscIO
{ {
class IVolume; class IVolume;
class IBlobReader;
std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename, std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename);
u32 partition_group = 0, u32 volume_number = -1);
std::unique_ptr<IVolume> CreateVolumeFromDirectory(const std::string& directory, bool is_wii, std::unique_ptr<IVolume> CreateVolumeFromDirectory(const std::string& directory, bool is_wii,
const std::string& apploader = "", const std::string& apploader = "",
const std::string& dol = ""); const std::string& dol = "");
void VolumeKeyForPartition(IBlobReader& _rReader, u64 offset, u8* VolumeKey);
} // namespace } // namespace

View File

@ -64,8 +64,10 @@ bool CVolumeDirectory::IsValidDirectory(const std::string& directory)
return File::IsDirectory(ExtractDirectoryName(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) if (!decrypt && (offset + length >= 0x400) && m_is_wii)
{ {
// Fully supporting this would require re-encrypting every file that's read. // 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) if (decrypt && !m_is_wii)
PanicAlertT("Tried to decrypt data from a non-Wii volume"); return false;
// header // header
if (offset < DISKHEADERINFO_ADDRESS) if (offset < DISKHEADERINFO_ADDRESS)
@ -157,7 +159,17 @@ bool CVolumeDirectory::Read(u64 offset, u64 length, u8* buffer, bool decrypt) co
return true; return true;
} }
std::string CVolumeDirectory::GetGameID() const std::vector<Partition> CVolumeDirectory::GetPartitions() const
{
return m_is_wii ? std::vector<Partition>{GetGamePartition()} : std::vector<Partition>();
}
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); 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]); return RegionSwitchGC(m_disk_header[3]);
} }
Country CVolumeDirectory::GetCountry() const Country CVolumeDirectory::GetCountry(const Partition& partition) const
{ {
return CountrySwitch(m_disk_header[3]); return CountrySwitch(m_disk_header[3]);
} }
std::string CVolumeDirectory::GetMakerID() const std::string CVolumeDirectory::GetMakerID(const Partition& partition) const
{ {
// Not implemented // Not implemented
return "00"; return "00";
} }
std::string CVolumeDirectory::GetInternalName() const std::string CVolumeDirectory::GetInternalName(const Partition& partition) const
{ {
char name[0x60]; char name[0x60];
if (Read(0x20, 0x60, (u8*)name, false)) if (Read(0x20, 0x60, (u8*)name, partition))
return DecodeString(name); return DecodeString(name);
else else
return ""; return "";
@ -218,13 +230,13 @@ void CVolumeDirectory::SetName(const std::string& name)
m_disk_header[length + 0x20] = 0; m_disk_header[length + 0x20] = 0;
} }
u64 CVolumeDirectory::GetFSTSize() const u64 CVolumeDirectory::GetFSTSize(const Partition& partition) const
{ {
// Not implemented // Not implemented
return 0; return 0;
} }
std::string CVolumeDirectory::GetApploaderDate() const std::string CVolumeDirectory::GetApploaderDate(const Partition& partition) const
{ {
// Not implemented // Not implemented
return "VOID"; return "VOID";

View File

@ -39,26 +39,28 @@ public:
static bool IsValidDirectory(const std::string& directory); 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<Partition> 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); 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; } u16 GetRevision(const Partition& partition = PARTITION_NONE) const override { return 0; }
std::string GetInternalName() const override; std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override;
std::map<Language, std::string> GetLongNames() const override; std::map<Language, std::string> GetLongNames() const override;
std::vector<u32> GetBanner(int* width, int* height) const override; std::vector<u32> GetBanner(int* width, int* height) const override;
void SetName(const std::string&); 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; Platform GetVolumeType() const override;
Region GetRegion() const override; Region GetRegion() const override;
Country GetCountry() const override; Country GetCountry(const Partition& partition = PARTITION_NONE) const override;
BlobType GetBlobType() const override; BlobType GetBlobType() const override;
u64 GetSize() const override; u64 GetSize() const override;

View File

@ -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) if (partition != PARTITION_NONE)
PanicAlertT("Tried to decrypt data from a non-Wii volume"); return false;
return m_pReader->Read(_Offset, _Length, _pBuffer); 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"); static const std::string NO_UID("NO_UID");
char ID[6]; char ID[6];
if (!Read(0, sizeof(ID), reinterpret_cast<u8*>(ID))) if (!Read(0, sizeof(ID), reinterpret_cast<u8*>(ID), partition))
{ {
PanicAlertT("Failed to read unique ID from disc image"); PanicAlertT("Failed to read unique ID from disc image");
return NO_UID; return NO_UID;
@ -58,43 +58,43 @@ std::string CVolumeGC::GetGameID() const
Region CVolumeGC::GetRegion() const Region CVolumeGC::GetRegion() const
{ {
u8 country_code; u8 country_code;
if (!ReadSwapped(3, &country_code, false)) if (!ReadSwapped(3, &country_code, PARTITION_NONE))
return Region::UNKNOWN_REGION; return Region::UNKNOWN_REGION;
return RegionSwitchGC(country_code); return RegionSwitchGC(country_code);
} }
Country CVolumeGC::GetCountry() const Country CVolumeGC::GetCountry(const Partition& partition) const
{ {
u8 country_code; u8 country_code;
if (!ReadSwapped(3, &country_code, false)) if (!ReadSwapped(3, &country_code, partition))
return Country::COUNTRY_UNKNOWN; return Country::COUNTRY_UNKNOWN;
return CountrySwitch(country_code); return CountrySwitch(country_code);
} }
std::string CVolumeGC::GetMakerID() const std::string CVolumeGC::GetMakerID(const Partition& partition) const
{ {
char makerID[2]; char makerID[2];
if (!Read(0x4, 0x2, (u8*)&makerID)) if (!Read(0x4, 0x2, (u8*)&makerID, partition))
return std::string(); return std::string();
return DecodeString(makerID); return DecodeString(makerID);
} }
u16 CVolumeGC::GetRevision() const u16 CVolumeGC::GetRevision(const Partition& partition) const
{ {
u8 revision; u8 revision;
if (!ReadSwapped(7, &revision, false)) if (!ReadSwapped(7, &revision, partition))
return 0; return 0;
return revision; return revision;
} }
std::string CVolumeGC::GetInternalName() const std::string CVolumeGC::GetInternalName(const Partition& partition) const
{ {
char name[0x60]; char name[0x60];
if (Read(0x20, 0x60, (u8*)name)) if (Read(0x20, 0x60, (u8*)name, partition))
return DecodeString(name); return DecodeString(name);
return ""; return "";
@ -138,19 +138,19 @@ std::vector<u32> CVolumeGC::GetBanner(int* width, int* height) const
return m_image_buffer; return m_image_buffer;
} }
u64 CVolumeGC::GetFSTSize() const u64 CVolumeGC::GetFSTSize(const Partition& partition) const
{ {
u32 size; u32 size;
if (!Read(0x428, 0x4, (u8*)&size)) if (!Read(0x428, 0x4, (u8*)&size, partition))
return 0; return 0;
return Common::swap32(size); return Common::swap32(size);
} }
std::string CVolumeGC::GetApploaderDate() const std::string CVolumeGC::GetApploaderDate(const Partition& partition) const
{ {
char date[16]; char date[16];
if (!Read(0x2440, 0x10, (u8*)&date)) if (!Read(0x2440, 0x10, (u8*)&date, partition))
return std::string(); return std::string();
return DecodeString(date); return DecodeString(date);
@ -171,10 +171,10 @@ u64 CVolumeGC::GetRawSize() const
return m_pReader->GetRawSize(); return m_pReader->GetRawSize();
} }
u8 CVolumeGC::GetDiscNumber() const u8 CVolumeGC::GetDiscNumber(const Partition& partition) const
{ {
u8 disc_number = 0; u8 disc_number = 0;
ReadSwapped(6, &disc_number, false); ReadSwapped(6, &disc_number, partition);
return disc_number; return disc_number;
} }
@ -192,9 +192,11 @@ void CVolumeGC::LoadBannerFile() const
m_banner_loaded = true; m_banner_loaded = true;
GCBanner banner_file; GCBanner banner_file;
std::unique_ptr<IFileSystem> file_system(CreateFileSystem(this)); std::unique_ptr<IFileSystem> file_system(CreateFileSystem(this, PARTITION_NONE));
size_t file_size = (size_t)file_system->GetFileSize("opening.bnr"); if (!file_system)
return;
size_t file_size = static_cast<size_t>(file_system->GetFileSize("opening.bnr"));
constexpr int BNR1_MAGIC = 0x31524e42; constexpr int BNR1_MAGIC = 0x31524e42;
constexpr int BNR2_MAGIC = 0x32524e42; constexpr int BNR2_MAGIC = 0x32524e42;
if (file_size != BNR1_SIZE && file_size != BNR2_SIZE) if (file_size != BNR1_SIZE && file_size != BNR2_SIZE)

View File

@ -28,24 +28,25 @@ class CVolumeGC : public IVolume
public: public:
CVolumeGC(std::unique_ptr<IBlobReader> reader); CVolumeGC(std::unique_ptr<IBlobReader> reader);
~CVolumeGC(); ~CVolumeGC();
bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt = false) const override; bool Read(u64 _Offset, u64 _Length, u8* _pBuffer,
std::string GetGameID() const override; const Partition& partition = PARTITION_NONE) const override;
std::string GetMakerID() const override; std::string GetGameID(const Partition& partition = PARTITION_NONE) const override;
u16 GetRevision() const override; std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override;
std::string GetInternalName() const override; u16 GetRevision(const Partition& partition = PARTITION_NONE) const override;
std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override;
std::map<Language, std::string> GetShortNames() const override; std::map<Language, std::string> GetShortNames() const override;
std::map<Language, std::string> GetLongNames() const override; std::map<Language, std::string> GetLongNames() const override;
std::map<Language, std::string> GetShortMakers() const override; std::map<Language, std::string> GetShortMakers() const override;
std::map<Language, std::string> GetLongMakers() const override; std::map<Language, std::string> GetLongMakers() const override;
std::map<Language, std::string> GetDescriptions() const override; std::map<Language, std::string> GetDescriptions() const override;
std::vector<u32> GetBanner(int* width, int* height) const override; std::vector<u32> GetBanner(int* width, int* height) const override;
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;
u8 GetDiscNumber() const override; u8 GetDiscNumber(const Partition& partition = PARTITION_NONE) const override;
Platform GetVolumeType() const override; Platform GetVolumeType() const override;
Region GetRegion() const override; Region GetRegion() const override;
Country GetCountry() const override; Country GetCountry(const Partition& partition = PARTITION_NONE) const override;
BlobType GetBlobType() const override; BlobType GetBlobType() const override;
u64 GetSize() const override; u64 GetSize() const override;
u64 GetRawSize() const override; u64 GetRawSize() const override;

View File

@ -29,11 +29,11 @@ CVolumeWAD::CVolumeWAD(std::unique_ptr<IBlobReader> reader) : m_reader(std::move
_assert_(m_reader); _assert_(m_reader);
// Source: http://wiibrew.org/wiki/WAD_files // Source: http://wiibrew.org/wiki/WAD_files
ReadSwapped(0x00, &m_hdr_size, false); ReadSwapped(0x00, &m_hdr_size, PARTITION_NONE);
ReadSwapped(0x08, &m_cert_size, false); ReadSwapped(0x08, &m_cert_size, PARTITION_NONE);
ReadSwapped(0x10, &m_tick_size, false); ReadSwapped(0x10, &m_tick_size, PARTITION_NONE);
ReadSwapped(0x14, &m_tmd_size, false); ReadSwapped(0x14, &m_tmd_size, PARTITION_NONE);
ReadSwapped(0x18, &m_data_size, false); ReadSwapped(0x18, &m_data_size, PARTITION_NONE);
m_offset = Common::AlignUp(m_hdr_size, 0x40) + Common::AlignUp(m_cert_size, 0x40); 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) + m_tmd_offset = Common::AlignUp(m_hdr_size, 0x40) + Common::AlignUp(m_cert_size, 0x40) +
@ -48,7 +48,7 @@ CVolumeWAD::CVolumeWAD(std::unique_ptr<IBlobReader> reader) : m_reader(std::move
} }
std::vector<u8> tmd_buffer(m_tmd_size); std::vector<u8> 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)); 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) if (partition != PARTITION_NONE)
PanicAlertT("Tried to decrypt data from a non-Wii volume"); return false;
return m_reader->Read(offset, length, buffer); return m_reader->Read(offset, length, buffer);
} }
@ -71,7 +71,7 @@ Region CVolumeWAD::GetRegion() const
return m_tmd.GetRegion(); return m_tmd.GetRegion();
} }
Country CVolumeWAD::GetCountry() const Country CVolumeWAD::GetCountry(const Partition& partition) const
{ {
if (!m_tmd.IsValid()) if (!m_tmd.IsValid())
return Country::COUNTRY_UNKNOWN; return Country::COUNTRY_UNKNOWN;
@ -83,20 +83,20 @@ Country CVolumeWAD::GetCountry() const
return CountrySwitch(country_code); return CountrySwitch(country_code);
} }
IOS::ES::TMDReader CVolumeWAD::GetTMD() const IOS::ES::TMDReader CVolumeWAD::GetTMD(const Partition& partition) const
{ {
return m_tmd; return m_tmd;
} }
std::string CVolumeWAD::GetGameID() const std::string CVolumeWAD::GetGameID(const Partition& partition) const
{ {
return m_tmd.GetGameID(); return m_tmd.GetGameID();
} }
std::string CVolumeWAD::GetMakerID() const std::string CVolumeWAD::GetMakerID(const Partition& partition) const
{ {
char temp[2]; char temp[2];
if (!Read(0x198 + m_tmd_offset, 2, (u8*)temp)) if (!Read(0x198 + m_tmd_offset, 2, (u8*)temp, partition))
return "00"; return "00";
// Some weird channels use 0x0000 in place of the MakerID, so we need a check here // 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); 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()) if (!m_tmd.IsValid())
return 0; return 0;

View File

@ -31,20 +31,27 @@ class CVolumeWAD : public IVolume
public: public:
CVolumeWAD(std::unique_ptr<IBlobReader> reader); CVolumeWAD(std::unique_ptr<IBlobReader> reader);
~CVolumeWAD(); ~CVolumeWAD();
bool Read(u64 offset, u64 length, u8* buffer, bool decrypt = false) const override; bool Read(u64 offset, u64 length, u8* buffer,
bool GetTitleID(u64* buffer) const override; const Partition& partition = PARTITION_NONE) const override;
IOS::ES::TMDReader GetTMD() const override; bool GetTitleID(u64* buffer, const Partition& partition = PARTITION_NONE) const override;
std::string GetGameID() const override; IOS::ES::TMDReader GetTMD(const Partition& partition = PARTITION_NONE) const override;
std::string GetMakerID() const override; std::string GetGameID(const Partition& partition = PARTITION_NONE) const override;
u16 GetRevision() const override; std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override;
std::string GetInternalName() const override { return ""; } u16 GetRevision(const Partition& partition = PARTITION_NONE) const override;
std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override
{
return "";
}
std::map<Language, std::string> GetLongNames() const override; std::map<Language, std::string> GetLongNames() const override;
std::vector<u32> GetBanner(int* width, int* height) const override; std::vector<u32> GetBanner(int* width, int* height) const override;
u64 GetFSTSize() const override { return 0; } u64 GetFSTSize(const Partition& partition = PARTITION_NONE) const override { return 0; }
std::string GetApploaderDate() const override { return ""; } std::string GetApploaderDate(const Partition& partition = PARTITION_NONE) const override
{
return "";
}
Platform GetVolumeType() const override; Platform GetVolumeType() const override;
Region GetRegion() const override; Region GetRegion() const override;
Country GetCountry() const override; Country GetCountry(const Partition& partition = PARTITION_NONE) const override;
BlobType GetBlobType() const override; BlobType GetBlobType() const override;
u64 GetSize() const override; u64 GetSize() const override;

View File

@ -4,6 +4,7 @@
#include "DiscIO/VolumeWiiCrypted.h" #include "DiscIO/VolumeWiiCrypted.h"
#include <algorithm>
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include <map> #include <map>
@ -28,58 +29,129 @@
namespace DiscIO namespace DiscIO
{ {
CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader, u64 _VolumeOffset, constexpr u64 PARTITION_DATA_OFFSET = 0x20000;
const unsigned char* _pVolumeKey)
: m_pReader(std::move(reader)), m_AES_ctx(std::make_unique<mbedtls_aes_context>()), CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader)
m_VolumeOffset(_VolumeOffset), m_dataOffset(0x20000), m_LastDecryptedBlockOffset(-1) : m_pReader(std::move(reader)), m_game_partition(PARTITION_NONE), m_last_decrypted_block(-1)
{ {
_assert_(m_pReader); _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) u32 read_buffer;
{ if (!big_endian_reader.ReadSwapped(0x40000 + (partition_group * 8) + 4, &read_buffer))
m_VolumeOffset = offset; continue;
m_LastDecryptedBlockOffset = -1; const u64 partition_table_offset = (u64)read_buffer << 2;
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]; u8 volume_key[16];
DiscIO::VolumeKeyForPartition(*m_pReader, offset, volume_key); mbedtls_aes_crypt_cbc(&aes_context, MBEDTLS_AES_DECRYPT, 16, iv, sub_key, volume_key);
mbedtls_aes_setkey_dec(m_AES_ctx.get(), volume_key, 128);
return true; std::unique_ptr<mbedtls_aes_context> partition_AES_context =
std::make_unique<mbedtls_aes_context>();
mbedtls_aes_setkey_dec(partition_AES_context.get(), volume_key, 128);
m_partitions[Partition(partition_offset)] = std::move(partition_AES_context);
}
}
} }
CVolumeWiiCrypted::~CVolumeWiiCrypted() 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); 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<u8> read_buffer(BLOCK_TOTAL_SIZE); std::vector<u8> read_buffer(BLOCK_TOTAL_SIZE);
while (_Length > 0) while (_Length > 0)
{ {
// Calculate block offset // Calculate offsets
u64 Block = _ReadOffset / BLOCK_DATA_SIZE; u64 block_offset_on_disc =
u64 Offset = _ReadOffset % BLOCK_DATA_SIZE; partition.offset + PARTITION_DATA_OFFSET + _ReadOffset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE;
u64 data_offset_in_block = _ReadOffset % BLOCK_DATA_SIZE;
if (m_LastDecryptedBlockOffset != Block) if (m_last_decrypted_block != block_offset_on_disc)
{ {
// Read the current block // Read the current block
if (!m_pReader->Read(m_VolumeOffset + m_dataOffset + Block * BLOCK_TOTAL_SIZE, if (!m_pReader->Read(block_offset_on_disc, BLOCK_TOTAL_SIZE, read_buffer.data()))
BLOCK_TOTAL_SIZE, read_buffer.data()))
return false; return false;
// Decrypt the block's data. // Decrypt the block's data.
// 0x3D0 - 0x3DF in m_pBuffer will be overwritten, // 0x3D0 - 0x3DF in read_buffer will be overwritten,
// but that won't affect anything, because we won't // but that won't affect anything, because we won't
// use the content of m_pBuffer anymore after this // use the content of read_buffer anymore after this
mbedtls_aes_crypt_cbc(m_AES_ctx.get(), MBEDTLS_AES_DECRYPT, BLOCK_DATA_SIZE, mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, BLOCK_DATA_SIZE, &read_buffer[0x3D0],
&read_buffer[0x3D0], &read_buffer[BLOCK_HEADER_SIZE], &read_buffer[BLOCK_HEADER_SIZE], m_last_decrypted_block_data);
m_LastDecryptedBlock); m_last_decrypted_block = block_offset_on_disc;
m_LastDecryptedBlockOffset = Block;
// The only thing we currently use from the 0x000 - 0x3FF part // The only thing we currently use from the 0x000 - 0x3FF part
// of the block is the IV (at 0x3D0), but it also contains SHA-1 // of the block is the IV (at 0x3D0), but it also contains SHA-1
@ -88,39 +160,52 @@ bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, bool de
} }
// Copy the decrypted data // Copy the decrypted data
u64 MaxSizeToCopy = BLOCK_DATA_SIZE - Offset; u64 copy_size = std::min(_Length, BLOCK_DATA_SIZE - data_offset_in_block);
u64 CopySize = (_Length > MaxSizeToCopy) ? MaxSizeToCopy : _Length; memcpy(_pBuffer, &m_last_decrypted_block_data[data_offset_in_block],
memcpy(_pBuffer, &m_LastDecryptedBlock[Offset], (size_t)CopySize); static_cast<size_t>(copy_size));
// Update offsets // Update offsets
_Length -= CopySize; _Length -= copy_size;
_pBuffer += CopySize; _pBuffer += copy_size;
_ReadOffset += CopySize; _ReadOffset += copy_size;
} }
return true; return true;
} }
bool CVolumeWiiCrypted::GetTitleID(u64* buffer) const std::vector<Partition> CVolumeWiiCrypted::GetPartitions() const
{ {
return ReadSwapped(m_VolumeOffset + 0x1DC, buffer, false); std::vector<Partition> 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<u8> buffer(0x2a4); std::vector<u8> 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)}; 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_size = 0;
u32 tmd_address = 0; u32 tmd_address = 0;
if (!ReadSwapped(m_VolumeOffset + 0x2a4, &tmd_size, false)) if (!ReadSwapped(partition.offset + 0x2a4, &tmd_size, PARTITION_NONE))
return {}; return {};
if (!ReadSwapped(m_VolumeOffset + 0x2a8, &tmd_address, false)) if (!ReadSwapped(partition.offset + 0x2a8, &tmd_address, PARTITION_NONE))
return {}; return {};
tmd_address <<= 2; tmd_address <<= 2;
@ -135,23 +220,26 @@ IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD() const
} }
std::vector<u8> buffer(tmd_size); std::vector<u8> 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 {};
return IOS::ES::TMDReader{std::move(buffer)}; return IOS::ES::TMDReader{std::move(buffer)};
} }
u64 CVolumeWiiCrypted::PartitionOffsetToRawOffset(u64 offset) const u64 CVolumeWiiCrypted::PartitionOffsetToRawOffset(u64 offset, const Partition& partition)
{ {
return m_VolumeOffset + m_dataOffset + (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); (offset % BLOCK_DATA_SIZE);
} }
std::string CVolumeWiiCrypted::GetGameID() const std::string CVolumeWiiCrypted::GetGameID(const Partition& partition) const
{ {
char ID[6]; char ID[6];
if (!Read(0, 6, (u8*)ID, true)) if (!Read(0, 6, (u8*)ID, partition))
return std::string(); return std::string();
return DecodeString(ID); return DecodeString(ID);
@ -160,16 +248,16 @@ std::string CVolumeWiiCrypted::GetGameID() const
Region CVolumeWiiCrypted::GetRegion() const Region CVolumeWiiCrypted::GetRegion() const
{ {
u32 region_code; u32 region_code;
if (!ReadSwapped(0x4E000, &region_code, false)) if (!ReadSwapped(0x4E000, &region_code, PARTITION_NONE))
return Region::UNKNOWN_REGION; return Region::UNKNOWN_REGION;
return static_cast<Region>(region_code); return static_cast<Region>(region_code);
} }
Country CVolumeWiiCrypted::GetCountry() const Country CVolumeWiiCrypted::GetCountry(const Partition& partition) const
{ {
u8 country_byte; u8 country_byte;
if (!ReadSwapped(3, &country_byte, true)) if (!ReadSwapped(3, &country_byte, partition))
return Country::COUNTRY_UNKNOWN; return Country::COUNTRY_UNKNOWN;
const Region region = GetRegion(); const Region region = GetRegion();
@ -180,29 +268,29 @@ Country CVolumeWiiCrypted::GetCountry() const
return CountrySwitch(country_byte); return CountrySwitch(country_byte);
} }
std::string CVolumeWiiCrypted::GetMakerID() const std::string CVolumeWiiCrypted::GetMakerID(const Partition& partition) const
{ {
char makerID[2]; char makerID[2];
if (!Read(0x4, 0x2, (u8*)&makerID, true)) if (!Read(0x4, 0x2, (u8*)&makerID, partition))
return std::string(); return std::string();
return DecodeString(makerID); return DecodeString(makerID);
} }
u16 CVolumeWiiCrypted::GetRevision() const u16 CVolumeWiiCrypted::GetRevision(const Partition& partition) const
{ {
u8 revision; u8 revision;
if (!ReadSwapped(7, &revision, true)) if (!ReadSwapped(7, &revision, partition))
return 0; return 0;
return revision; return revision;
} }
std::string CVolumeWiiCrypted::GetInternalName() const std::string CVolumeWiiCrypted::GetInternalName(const Partition& partition) const
{ {
char name_buffer[0x60]; 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 DecodeString(name_buffer);
return ""; return "";
@ -210,7 +298,10 @@ std::string CVolumeWiiCrypted::GetInternalName() const
std::map<Language, std::string> CVolumeWiiCrypted::GetLongNames() const std::map<Language, std::string> CVolumeWiiCrypted::GetLongNames() const
{ {
std::unique_ptr<IFileSystem> file_system(CreateFileSystem(this)); std::unique_ptr<IFileSystem> file_system(CreateFileSystem(this, GetGamePartition()));
if (!file_system)
return {{}};
std::vector<u8> opening_bnr(NAMES_TOTAL_BYTES); std::vector<u8> opening_bnr(NAMES_TOTAL_BYTES);
size_t size = file_system->ReadFile("opening.bnr", opening_bnr.data(), opening_bnr.size(), 0x5C); size_t size = file_system->ReadFile("opening.bnr", opening_bnr.data(), opening_bnr.size(), 0x5C);
opening_bnr.resize(size); opening_bnr.resize(size);
@ -223,27 +314,27 @@ std::vector<u32> CVolumeWiiCrypted::GetBanner(int* width, int* height) const
*height = 0; *height = 0;
u64 title_id; u64 title_id;
if (!GetTitleID(&title_id)) if (!GetTitleID(&title_id, GetGamePartition()))
return std::vector<u32>(); return std::vector<u32>();
return GetWiiBanner(width, height, title_id); return GetWiiBanner(width, height, title_id);
} }
u64 CVolumeWiiCrypted::GetFSTSize() const u64 CVolumeWiiCrypted::GetFSTSize(const Partition& partition) const
{ {
u32 size; u32 size;
if (!Read(0x428, 0x4, (u8*)&size, true)) if (!Read(0x428, 0x4, (u8*)&size, partition))
return 0; return 0;
return (u64)Common::swap32(size) << 2; return (u64)Common::swap32(size) << 2;
} }
std::string CVolumeWiiCrypted::GetApploaderDate() const std::string CVolumeWiiCrypted::GetApploaderDate(const Partition& partition) const
{ {
char date[16]; char date[16];
if (!Read(0x2440, 0x10, (u8*)&date, true)) if (!Read(0x2440, 0x10, (u8*)&date, partition))
return std::string(); return std::string();
return DecodeString(date); return DecodeString(date);
@ -254,10 +345,10 @@ Platform CVolumeWiiCrypted::GetVolumeType() const
return Platform::WII_DISC; return Platform::WII_DISC;
} }
u8 CVolumeWiiCrypted::GetDiscNumber() const u8 CVolumeWiiCrypted::GetDiscNumber(const Partition& partition) const
{ {
u8 disc_number = 0; u8 disc_number = 0;
ReadSwapped(6, &disc_number, true); ReadSwapped(6, &disc_number, partition);
return disc_number; return disc_number;
} }
@ -276,29 +367,34 @@ u64 CVolumeWiiCrypted::GetRawSize() const
return m_pReader->GetRawSize(); 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 // Get partition data size
u32 partSizeDiv4; 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; u64 partDataSize = (u64)Common::swap32(partSizeDiv4) * 4;
u32 nClusters = (u32)(partDataSize / 0x8000); u32 nClusters = (u32)(partDataSize / 0x8000);
for (u32 clusterID = 0; clusterID < nClusters; ++clusterID) for (u32 clusterID = 0; clusterID < nClusters; ++clusterID)
{ {
u64 clusterOff = m_VolumeOffset + m_dataOffset + (u64)clusterID * 0x8000; u64 clusterOff = partition.offset + PARTITION_DATA_OFFSET + (u64)clusterID * 0x8000;
// Read and decrypt the cluster metadata // Read and decrypt the cluster metadata
u8 clusterMDCrypted[0x400]; u8 clusterMDCrypted[0x400];
u8 clusterMD[0x400]; u8 clusterMD[0x400];
u8 IV[16] = {0}; 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); WARN_LOG(DISCIO, "Integrity Check: fail at cluster %d: could not read metadata", clusterID);
return false; return false;
} }
mbedtls_aes_crypt_cbc(m_AES_ctx.get(), MBEDTLS_AES_DECRYPT, 0x400, IV, clusterMDCrypted, mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, 0x400, IV, clusterMDCrypted, clusterMD);
clusterMD);
// Some clusters have invalid data and metadata because they aren't // Some clusters have invalid data and metadata because they aren't
// meant to be read by the game (for example, holes between files). To // meant to be read by the game (for example, holes between files). To
@ -317,7 +413,7 @@ bool CVolumeWiiCrypted::CheckIntegrity() const
continue; continue;
u8 clusterData[0x7C00]; 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); WARN_LOG(DISCIO, "Integrity Check: fail at cluster %d: could not read data", clusterID);
return false; return false;

View File

@ -28,48 +28,47 @@ enum class Platform;
class CVolumeWiiCrypted : public IVolume class CVolumeWiiCrypted : public IVolume
{ {
public: public:
CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader, u64 _VolumeOffset, CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader);
const unsigned char* _pVolumeKey);
~CVolumeWiiCrypted(); ~CVolumeWiiCrypted();
bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const override; bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const override;
bool GetTitleID(u64* buffer) const override; std::vector<Partition> GetPartitions() const override;
IOS::ES::TicketReader GetTicket() const override; Partition GetGamePartition() const override;
IOS::ES::TMDReader GetTMD() const override; bool GetTitleID(u64* buffer, const Partition& partition) const override;
u64 PartitionOffsetToRawOffset(u64 offset) const override; IOS::ES::TicketReader GetTicket(const Partition& partition) const override;
std::string GetGameID() const override; IOS::ES::TMDReader GetTMD(const Partition& partition) const override;
std::string GetMakerID() const override; std::string GetGameID(const Partition& partition) const override;
u16 GetRevision() const override; std::string GetMakerID(const Partition& partition) const override;
std::string GetInternalName() const override; u16 GetRevision(const Partition& partition) const override;
std::string GetInternalName(const Partition& partition) const override;
std::map<Language, std::string> GetLongNames() const override; std::map<Language, std::string> GetLongNames() const override;
std::vector<u32> GetBanner(int* width, int* height) const override; std::vector<u32> GetBanner(int* width, int* height) const override;
u64 GetFSTSize() const override; u64 GetFSTSize(const Partition& partition) const override;
std::string GetApploaderDate() const override; std::string GetApploaderDate(const Partition& partition) const override;
u8 GetDiscNumber() const override; u8 GetDiscNumber(const Partition& partition) const override;
Platform GetVolumeType() const override; Platform GetVolumeType() const override;
bool SupportsIntegrityCheck() const override { return true; } bool SupportsIntegrityCheck() const override { return true; }
bool CheckIntegrity() const override; bool CheckIntegrity(const Partition& partition) const override;
bool ChangePartition(u64 offset) override;
Region GetRegion() const override; Region GetRegion() const override;
Country GetCountry() const override; Country GetCountry(const Partition& partition) const override;
BlobType GetBlobType() const override; BlobType GetBlobType() const override;
u64 GetSize() const override; u64 GetSize() const override;
u64 GetRawSize() 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_HEADER_SIZE = 0x0400;
static constexpr unsigned int BLOCK_DATA_SIZE = 0x7C00; static constexpr unsigned int BLOCK_DATA_SIZE = 0x7C00;
static constexpr unsigned int BLOCK_TOTAL_SIZE = BLOCK_HEADER_SIZE + BLOCK_DATA_SIZE; static constexpr unsigned int BLOCK_TOTAL_SIZE = BLOCK_HEADER_SIZE + BLOCK_DATA_SIZE;
private: private:
std::unique_ptr<IBlobReader> m_pReader; std::unique_ptr<IBlobReader> m_pReader;
std::unique_ptr<mbedtls_aes_context> m_AES_ctx; std::map<Partition, std::unique_ptr<mbedtls_aes_context>> m_partitions;
Partition m_game_partition;
u64 m_VolumeOffset; mutable u64 m_last_decrypted_block;
u64 m_dataOffset; mutable u8 m_last_decrypted_block_data[BLOCK_DATA_SIZE];
mutable u64 m_LastDecryptedBlockOffset;
mutable unsigned char m_LastDecryptedBlock[BLOCK_DATA_SIZE];
}; };
} // namespace } // namespace

View File

@ -33,32 +33,31 @@ namespace
class WiiPartition final : public wxTreeItemData class WiiPartition final : public wxTreeItemData
{ {
public: public:
WiiPartition(std::unique_ptr<DiscIO::IVolume> volume_, WiiPartition(std::unique_ptr<DiscIO::IFileSystem> filesystem_)
std::unique_ptr<DiscIO::IFileSystem> filesystem_) : filesystem{std::move(filesystem_)}
: volume{std::move(volume_)}, filesystem{std::move(filesystem_)}
{ {
} }
std::unique_ptr<DiscIO::IVolume> volume;
std::unique_ptr<DiscIO::IFileSystem> filesystem; std::unique_ptr<DiscIO::IFileSystem> filesystem;
}; };
class IntegrityCheckThread final : public wxThread class IntegrityCheckThread final : public wxThread
{ {
public: public:
explicit IntegrityCheckThread(const WiiPartition& partition) explicit IntegrityCheckThread(const DiscIO::IVolume* volume, DiscIO::Partition partition)
: wxThread{wxTHREAD_JOINABLE}, m_partition{partition} : wxThread{wxTHREAD_JOINABLE}, m_volume{volume}, m_partition{partition}
{ {
Create(); Create();
} }
ExitCode Entry() override ExitCode Entry() override
{ {
return reinterpret_cast<ExitCode>(m_partition.volume->CheckIntegrity()); return reinterpret_cast<ExitCode>(m_volume->CheckIntegrity(m_partition));
} }
private: private:
const WiiPartition& m_partition; const DiscIO::IVolume* const m_volume;
const DiscIO::Partition m_partition;
}; };
enum : int enum : int
@ -156,9 +155,9 @@ WiiPartition* FindWiiPartition(wxTreeCtrl* tree_ctrl, const wxString& label)
} }
} // Anonymous namespace } // Anonymous namespace
FilesystemPanel::FilesystemPanel(wxWindow* parent, wxWindowID id, const GameListItem& item, FilesystemPanel::FilesystemPanel(wxWindow* parent, wxWindowID id,
const std::unique_ptr<DiscIO::IVolume>& opened_iso) const std::unique_ptr<DiscIO::IVolume>& opened_iso)
: wxPanel{parent, id}, m_game_list_item{item}, m_opened_iso{opened_iso} : wxPanel{parent, id}, m_opened_iso{opened_iso}
{ {
CreateGUI(); CreateGUI();
BindEvents(); BindEvents();
@ -217,7 +216,7 @@ void FilesystemPanel::PopulateFileSystemTree()
void FilesystemPanel::PopulateFileSystemTreeGC() 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) if (!m_filesystem)
return; return;
@ -226,34 +225,23 @@ void FilesystemPanel::PopulateFileSystemTreeGC()
void FilesystemPanel::PopulateFileSystemTreeWii() const void FilesystemPanel::PopulateFileSystemTreeWii() const
{ {
u32 partition_count = 0; std::vector<DiscIO::Partition> partitions = m_opened_iso->GetPartitions();
for (size_t i = 0; i < partitions.size(); ++i)
for (u32 group = 0; group < 4; group++)
{ {
// yes, technically there can be OVER NINE THOUSAND partitions... std::unique_ptr<DiscIO::IFileSystem> file_system(
for (u32 i = 0; i < 0xFFFFFFFF; i++) DiscIO::CreateFileSystem(m_opened_iso.get(), partitions[i]));
if (file_system)
{ {
auto volume = DiscIO::CreateVolumeFromFilename(m_game_list_item.GetFileName(), group, i); wxTreeItemId partition_root = m_tree_ctrl->AppendItem(
if (volume == nullptr) m_tree_ctrl->GetRootItem(), wxString::Format(_("Partition %i"), i), ICON_DISC);
break;
auto file_system = DiscIO::CreateFileSystem(volume.get()); WiiPartition* const partition = new WiiPartition(std::move(file_system));
if (file_system != nullptr)
{
auto* const partition = new WiiPartition(std::move(volume), 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); m_tree_ctrl->SetItemData(partition_root, partition);
CreateDirectoryTree(m_tree_ctrl, partition_root, partition->filesystem->GetFileList()); CreateDirectoryTree(m_tree_ctrl, partition_root, partition->filesystem->GetFileList());
if (partition_count == 1) if (i == 1)
m_tree_ctrl->Expand(partition_root); m_tree_ctrl->Expand(partition_root);
partition_count++;
}
} }
} }
} }
@ -382,8 +370,9 @@ void FilesystemPanel::OnCheckPartitionIntegrity(wxCommandEvent& WXUNUSED(event))
wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_SMOOTH); wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_SMOOTH);
const auto selection = m_tree_ctrl->GetSelection(); const auto selection = m_tree_ctrl->GetSelection();
WiiPartition* partition =
IntegrityCheckThread thread(*static_cast<WiiPartition*>(m_tree_ctrl->GetItemData(selection))); static_cast<WiiPartition*>(m_tree_ctrl->GetItemData(m_tree_ctrl->GetSelection()));
IntegrityCheckThread thread(m_opened_iso.get(), partition->filesystem->GetPartition());
thread.Run(); thread.Run();
while (thread.IsAlive()) while (thread.IsAlive())

View File

@ -21,7 +21,7 @@ class IVolume;
class FilesystemPanel final : public wxPanel class FilesystemPanel final : public wxPanel
{ {
public: public:
explicit FilesystemPanel(wxWindow* parent, wxWindowID id, const GameListItem& item, explicit FilesystemPanel(wxWindow* parent, wxWindowID id,
const std::unique_ptr<DiscIO::IVolume>& opened_iso); const std::unique_ptr<DiscIO::IVolume>& opened_iso);
~FilesystemPanel(); ~FilesystemPanel();
@ -69,7 +69,6 @@ private:
wxTreeCtrl* m_tree_ctrl; wxTreeCtrl* m_tree_ctrl;
const GameListItem& m_game_list_item;
const std::unique_ptr<DiscIO::IVolume>& m_opened_iso; const std::unique_ptr<DiscIO::IVolume>& m_opened_iso;
std::unique_ptr<DiscIO::IFileSystem> m_filesystem; std::unique_ptr<DiscIO::IFileSystem> m_filesystem;

View File

@ -433,8 +433,7 @@ void CISOProperties::CreateGUIControls()
if (m_open_iso->GetVolumeType() != DiscIO::Platform::WII_WAD) if (m_open_iso->GetVolumeType() != DiscIO::Platform::WII_WAD)
{ {
m_Notebook->AddPage( m_Notebook->AddPage(new FilesystemPanel(m_Notebook, ID_FILESYSTEM, m_open_iso),
new FilesystemPanel(m_Notebook, ID_FILESYSTEM, OpenGameListItem, m_open_iso),
_("Filesystem")); _("Filesystem"));
} }

View File

@ -182,7 +182,7 @@ void InfoPanel::LoadISODetails()
m_fst->SetValue(StrToWxStr(std::to_string(m_opened_iso->GetFSTSize()))); m_fst->SetValue(StrToWxStr(std::to_string(m_opened_iso->GetFSTSize())));
if (m_ios_version) 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()) if (tmd.IsValid())
m_ios_version->SetValue(StringFromFormat("IOS%u", static_cast<u32>(tmd.GetIOSId()))); m_ios_version->SetValue(StringFromFormat("IOS%u", static_cast<u32>(tmd.GetIOSId())));
} }
@ -223,7 +223,7 @@ wxStaticBoxSizer* InfoPanel::CreateISODetailsSizer()
{_("Apploader Date:"), m_date}, {_("Apploader Date:"), m_date},
{_("FST Size:"), m_fst}, {_("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); controls.emplace_back(_("IOS Version:"), m_ios_version);
const int space_10 = FromDIP(10); const int space_10 = FromDIP(10);