mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 22:29:39 -06:00
Merge pull request #2353 from JosJuice/wii-partition-cleanup
VolumeWiiCrypted: Replace ChangePartition with a partition parameter
This commit is contained in:
@ -123,15 +123,15 @@ void DiscScrubber::MarkAsUsedE(u64 partition_data_offset, u64 offset, u64 size)
|
||||
}
|
||||
|
||||
// Helper functions for reading the BE volume
|
||||
bool DiscScrubber::ReadFromVolume(u64 offset, u32& buffer, bool decrypt)
|
||||
bool DiscScrubber::ReadFromVolume(u64 offset, u32& buffer, const Partition& partition)
|
||||
{
|
||||
return m_disc->ReadSwapped(offset, &buffer, decrypt);
|
||||
return m_disc->ReadSwapped(offset, &buffer, partition);
|
||||
}
|
||||
|
||||
bool DiscScrubber::ReadFromVolume(u64 offset, u64& buffer, bool decrypt)
|
||||
bool DiscScrubber::ReadFromVolume(u64 offset, u64& buffer, const Partition& partition)
|
||||
{
|
||||
u32 temp_buffer;
|
||||
if (!m_disc->ReadSwapped(offset, &temp_buffer, decrypt))
|
||||
if (!m_disc->ReadSwapped(offset, &temp_buffer, partition))
|
||||
return false;
|
||||
buffer = static_cast<u64>(temp_buffer) << 2;
|
||||
return true;
|
||||
@ -142,126 +142,85 @@ bool DiscScrubber::ParseDisc()
|
||||
// Mark the header as used - it's mostly 0s anyways
|
||||
MarkAsUsed(0, 0x50000);
|
||||
|
||||
for (u32 x = 0; x < 4; x++)
|
||||
for (const DiscIO::Partition& partition : m_disc->GetPartitions())
|
||||
{
|
||||
if (!ReadFromVolume(0x40000 + (x * 8) + 0, m_partition_group[x].num_partitions, false) ||
|
||||
!ReadFromVolume(0x40000 + (x * 8) + 4, m_partition_group[x].partitions_offset, false))
|
||||
PartitionHeader header;
|
||||
|
||||
if (!ReadFromVolume(partition.offset + 0x2a4, header.tmd_size, PARTITION_NONE) ||
|
||||
!ReadFromVolume(partition.offset + 0x2a8, header.tmd_offset, PARTITION_NONE) ||
|
||||
!ReadFromVolume(partition.offset + 0x2ac, header.cert_chain_size, PARTITION_NONE) ||
|
||||
!ReadFromVolume(partition.offset + 0x2b0, header.cert_chain_offset, PARTITION_NONE) ||
|
||||
!ReadFromVolume(partition.offset + 0x2b4, header.h3_offset, PARTITION_NONE) ||
|
||||
!ReadFromVolume(partition.offset + 0x2b8, header.data_offset, PARTITION_NONE) ||
|
||||
!ReadFromVolume(partition.offset + 0x2bc, header.data_size, PARTITION_NONE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read all partitions
|
||||
for (u32 i = 0; i < m_partition_group[x].num_partitions; i++)
|
||||
{
|
||||
Partition partition;
|
||||
MarkAsUsed(partition.offset, 0x2c0);
|
||||
|
||||
partition.group_number = x;
|
||||
partition.number = i;
|
||||
MarkAsUsed(partition.offset + header.tmd_offset, header.tmd_size);
|
||||
MarkAsUsed(partition.offset + header.cert_chain_offset, header.cert_chain_size);
|
||||
MarkAsUsed(partition.offset + header.h3_offset, 0x18000);
|
||||
// This would mark the whole (encrypted) data area
|
||||
// we need to parse FST and other crap to find what's free within it!
|
||||
// MarkAsUsed(partition.offset + header.data_offset, header.data_size);
|
||||
|
||||
if (!ReadFromVolume(m_partition_group[x].partitions_offset + (i * 8) + 0, partition.offset,
|
||||
false) ||
|
||||
!ReadFromVolume(m_partition_group[x].partitions_offset + (i * 8) + 4, partition.type,
|
||||
false) ||
|
||||
!ReadFromVolume(partition.offset + 0x2a4, partition.header.tmd_size, false) ||
|
||||
!ReadFromVolume(partition.offset + 0x2a8, partition.header.tmd_offset, false) ||
|
||||
!ReadFromVolume(partition.offset + 0x2ac, partition.header.cert_chain_size, false) ||
|
||||
!ReadFromVolume(partition.offset + 0x2b0, partition.header.cert_chain_offset, false) ||
|
||||
!ReadFromVolume(partition.offset + 0x2b4, partition.header.h3_offset, false) ||
|
||||
!ReadFromVolume(partition.offset + 0x2b8, partition.header.data_offset, false) ||
|
||||
!ReadFromVolume(partition.offset + 0x2bc, partition.header.data_size, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_partition_group[x].partitions.push_back(partition);
|
||||
}
|
||||
|
||||
for (auto& partition : m_partition_group[x].partitions)
|
||||
{
|
||||
const PartitionHeader& header = partition.header;
|
||||
|
||||
MarkAsUsed(partition.offset, 0x2c0);
|
||||
|
||||
MarkAsUsed(partition.offset + header.tmd_offset, header.tmd_size);
|
||||
MarkAsUsed(partition.offset + header.cert_chain_offset, header.cert_chain_size);
|
||||
MarkAsUsed(partition.offset + header.h3_offset, 0x18000);
|
||||
// This would mark the whole (encrypted) data area
|
||||
// we need to parse FST and other crap to find what's free within it!
|
||||
// MarkAsUsed(partition.offset + header.data_offset, header.data_size);
|
||||
|
||||
// Parse Data! This is where the big gain is
|
||||
if (!ParsePartitionData(partition))
|
||||
return false;
|
||||
}
|
||||
// Parse Data! This is where the big gain is
|
||||
if (!ParsePartitionData(partition, &header))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Operations dealing with encrypted space are done here - the volume is swapped to allow this
|
||||
bool DiscScrubber::ParsePartitionData(Partition& partition)
|
||||
// Operations dealing with encrypted space are done here
|
||||
bool DiscScrubber::ParsePartitionData(const Partition& partition, PartitionHeader* header)
|
||||
{
|
||||
bool parsed_ok = true;
|
||||
|
||||
// Switch out the main volume temporarily
|
||||
std::unique_ptr<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)
|
||||
std::unique_ptr<IFileSystem> filesystem(CreateFileSystem(m_disc.get(), partition));
|
||||
if (!filesystem)
|
||||
{
|
||||
ERROR_LOG(DISCIO, "Failed to create volume from file %s", m_filename.c_str());
|
||||
m_disc.swap(old_volume);
|
||||
ERROR_LOG(DISCIO, "Failed to read file system for the partition at 0x%" PRIx64,
|
||||
partition.offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<IFileSystem> filesystem(CreateFileSystem(m_disc.get()));
|
||||
if (!filesystem)
|
||||
const u64 partition_data_offset = partition.offset + header->data_offset;
|
||||
|
||||
// Mark things as used which are not in the filesystem
|
||||
// Header, Header Information, Apploader
|
||||
if (!ReadFromVolume(0x2440 + 0x14, header->apploader_size, partition) ||
|
||||
!ReadFromVolume(0x2440 + 0x18, header->apploader_size, partition))
|
||||
{
|
||||
ERROR_LOG(DISCIO, "Failed to create filesystem for group %u partition %u",
|
||||
partition.group_number, partition.number);
|
||||
parsed_ok = false;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
MarkAsUsedE(partition_data_offset, 0,
|
||||
0x2440 + header->apploader_size + header->apploader_trailer_size);
|
||||
|
||||
// DOL
|
||||
header->dol_offset = filesystem->GetBootDOLOffset();
|
||||
header->dol_size = filesystem->GetBootDOLSize(header->dol_offset);
|
||||
if (header->dol_offset == 0 || header->dol_size == 0)
|
||||
return false;
|
||||
MarkAsUsedE(partition_data_offset, header->dol_offset, header->dol_size);
|
||||
|
||||
// FST
|
||||
if (!ReadFromVolume(0x424, header->fst_offset, partition) ||
|
||||
!ReadFromVolume(0x428, header->fst_size, partition))
|
||||
{
|
||||
// Mark things as used which are not in the filesystem
|
||||
// Header, Header Information, Apploader
|
||||
parsed_ok = parsed_ok && ReadFromVolume(0x2440 + 0x14, partition.header.apploader_size, true);
|
||||
parsed_ok =
|
||||
parsed_ok && ReadFromVolume(0x2440 + 0x18, partition.header.apploader_trailer_size, true);
|
||||
MarkAsUsedE(partition.offset + partition.header.data_offset, 0,
|
||||
0x2440 + partition.header.apploader_size + partition.header.apploader_trailer_size);
|
||||
return false;
|
||||
}
|
||||
MarkAsUsedE(partition_data_offset, header->fst_offset, header->fst_size);
|
||||
|
||||
// DOL
|
||||
partition.header.dol_offset = filesystem->GetBootDOLOffset();
|
||||
partition.header.dol_size = filesystem->GetBootDOLSize(partition.header.dol_offset);
|
||||
parsed_ok = parsed_ok && partition.header.dol_offset && partition.header.dol_size;
|
||||
MarkAsUsedE(partition.offset + partition.header.data_offset, partition.header.dol_offset,
|
||||
partition.header.dol_size);
|
||||
|
||||
// FST
|
||||
parsed_ok = parsed_ok && ReadFromVolume(0x424, partition.header.fst_offset, true);
|
||||
parsed_ok = parsed_ok && ReadFromVolume(0x428, partition.header.fst_size, true);
|
||||
MarkAsUsedE(partition.offset + partition.header.data_offset, partition.header.fst_offset,
|
||||
partition.header.fst_size);
|
||||
|
||||
// Go through the filesystem and mark entries as used
|
||||
for (const SFileInfo& file : filesystem->GetFileList())
|
||||
{
|
||||
DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str());
|
||||
if ((file.m_NameOffset & 0x1000000) == 0)
|
||||
{
|
||||
MarkAsUsedE(partition.offset + partition.header.data_offset, file.m_Offset,
|
||||
file.m_FileSize);
|
||||
}
|
||||
}
|
||||
// Go through the filesystem and mark entries as used
|
||||
for (const SFileInfo& file : filesystem->GetFileList())
|
||||
{
|
||||
DEBUG_LOG(DISCIO, "%s", file.m_FullPath.empty() ? "/" : file.m_FullPath.c_str());
|
||||
if ((file.m_NameOffset & 0x1000000) == 0)
|
||||
MarkAsUsedE(partition_data_offset, file.m_Offset, file.m_FileSize);
|
||||
}
|
||||
|
||||
// Swap back
|
||||
m_disc.swap(old_volume);
|
||||
|
||||
return parsed_ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace DiscIO
|
||||
|
@ -26,6 +26,7 @@ class IOFile;
|
||||
namespace DiscIO
|
||||
{
|
||||
class IVolume;
|
||||
struct Partition;
|
||||
|
||||
class DiscScrubber final
|
||||
{
|
||||
@ -57,34 +58,16 @@ private:
|
||||
u32 apploader_trailer_size;
|
||||
};
|
||||
|
||||
struct Partition final
|
||||
{
|
||||
u32 group_number;
|
||||
u32 number;
|
||||
u64 offset;
|
||||
u32 type;
|
||||
PartitionHeader header;
|
||||
};
|
||||
|
||||
struct PartitionGroup final
|
||||
{
|
||||
u32 num_partitions;
|
||||
u64 partitions_offset;
|
||||
std::vector<Partition> partitions;
|
||||
};
|
||||
|
||||
void MarkAsUsed(u64 offset, u64 size);
|
||||
void MarkAsUsedE(u64 partition_data_offset, u64 offset, u64 size);
|
||||
bool ReadFromVolume(u64 offset, u32& buffer, bool decrypt);
|
||||
bool ReadFromVolume(u64 offset, u64& buffer, bool decrypt);
|
||||
bool ReadFromVolume(u64 offset, u32& buffer, const Partition& partition);
|
||||
bool ReadFromVolume(u64 offset, u64& buffer, const Partition& partition);
|
||||
bool ParseDisc();
|
||||
bool ParsePartitionData(Partition& partition);
|
||||
bool ParsePartitionData(const Partition& partition, PartitionHeader* header);
|
||||
|
||||
std::string m_filename;
|
||||
std::unique_ptr<IVolume> m_disc;
|
||||
|
||||
std::array<PartitionGroup, 4> m_partition_group{};
|
||||
|
||||
std::vector<u8> m_free_table;
|
||||
u64 m_file_size = 0;
|
||||
u64 m_block_count = 0;
|
||||
|
@ -20,8 +20,8 @@
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
CFileSystemGCWii::CFileSystemGCWii(const IVolume* _rVolume)
|
||||
: IFileSystem(_rVolume), m_Initialized(false), m_Valid(false), m_Wii(false)
|
||||
CFileSystemGCWii::CFileSystemGCWii(const IVolume* _rVolume, const Partition& partition)
|
||||
: IFileSystem(_rVolume, partition), m_Initialized(false), m_Valid(false), m_offset_shift(0)
|
||||
{
|
||||
m_Valid = DetectFileSystem();
|
||||
}
|
||||
@ -80,7 +80,7 @@ u64 CFileSystemGCWii::ReadFile(const std::string& _rFullPath, u8* _pBuffer, u64
|
||||
read_length, _OffsetInFile, _rFullPath.c_str(), pFileInfo->m_Offset,
|
||||
pFileInfo->m_FileSize);
|
||||
|
||||
m_rVolume->Read(pFileInfo->m_Offset + _OffsetInFile, read_length, _pBuffer, m_Wii);
|
||||
m_rVolume->Read(pFileInfo->m_Offset + _OffsetInFile, read_length, _pBuffer, m_partition);
|
||||
return read_length;
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ bool CFileSystemGCWii::ExportFile(const std::string& _rFullPath,
|
||||
|
||||
std::vector<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)
|
||||
break;
|
||||
@ -130,14 +130,14 @@ bool CFileSystemGCWii::ExportApploader(const std::string& _rExportFolder) const
|
||||
u32 apploader_size;
|
||||
u32 trailer_size;
|
||||
const u32 header_size = 0x20;
|
||||
if (!m_rVolume->ReadSwapped(0x2440 + 0x14, &apploader_size, m_Wii) ||
|
||||
!m_rVolume->ReadSwapped(0x2440 + 0x18, &trailer_size, m_Wii))
|
||||
if (!m_rVolume->ReadSwapped(0x2440 + 0x14, &apploader_size, m_partition) ||
|
||||
!m_rVolume->ReadSwapped(0x2440 + 0x18, &trailer_size, m_partition))
|
||||
return false;
|
||||
apploader_size += trailer_size + header_size;
|
||||
DEBUG_LOG(DISCIO, "Apploader size -> %x", apploader_size);
|
||||
|
||||
std::vector<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");
|
||||
|
||||
@ -155,8 +155,8 @@ bool CFileSystemGCWii::ExportApploader(const std::string& _rExportFolder) const
|
||||
u64 CFileSystemGCWii::GetBootDOLOffset() const
|
||||
{
|
||||
u32 offset = 0;
|
||||
m_rVolume->ReadSwapped(0x420, &offset, m_Wii);
|
||||
return static_cast<u64>(offset) << GetOffsetShift();
|
||||
m_rVolume->ReadSwapped(0x420, &offset, m_partition);
|
||||
return static_cast<u64>(offset) << m_offset_shift;
|
||||
}
|
||||
|
||||
u32 CFileSystemGCWii::GetBootDOLSize(u64 dol_offset) const
|
||||
@ -173,8 +173,8 @@ u32 CFileSystemGCWii::GetBootDOLSize(u64 dol_offset) const
|
||||
// Iterate through the 7 code segments
|
||||
for (u8 i = 0; i < 7; i++)
|
||||
{
|
||||
if (!m_rVolume->ReadSwapped(dol_offset + 0x00 + i * 4, &offset, m_Wii) ||
|
||||
!m_rVolume->ReadSwapped(dol_offset + 0x90 + i * 4, &size, m_Wii))
|
||||
if (!m_rVolume->ReadSwapped(dol_offset + 0x00 + i * 4, &offset, m_partition) ||
|
||||
!m_rVolume->ReadSwapped(dol_offset + 0x90 + i * 4, &size, m_partition))
|
||||
return 0;
|
||||
dol_size = std::max(offset + size, dol_size);
|
||||
}
|
||||
@ -182,8 +182,8 @@ u32 CFileSystemGCWii::GetBootDOLSize(u64 dol_offset) const
|
||||
// Iterate through the 11 data segments
|
||||
for (u8 i = 0; i < 11; i++)
|
||||
{
|
||||
if (!m_rVolume->ReadSwapped(dol_offset + 0x1c + i * 4, &offset, m_Wii) ||
|
||||
!m_rVolume->ReadSwapped(dol_offset + 0xac + i * 4, &size, m_Wii))
|
||||
if (!m_rVolume->ReadSwapped(dol_offset + 0x1c + i * 4, &offset, m_partition) ||
|
||||
!m_rVolume->ReadSwapped(dol_offset + 0xac + i * 4, &size, m_partition))
|
||||
return 0;
|
||||
dol_size = std::max(offset + size, dol_size);
|
||||
}
|
||||
@ -200,7 +200,7 @@ bool CFileSystemGCWii::ExportDOL(const std::string& _rExportFolder) const
|
||||
return false;
|
||||
|
||||
std::vector<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");
|
||||
|
||||
@ -218,7 +218,7 @@ bool CFileSystemGCWii::ExportDOL(const std::string& _rExportFolder) const
|
||||
std::string CFileSystemGCWii::GetStringFromOffset(u64 _Offset) const
|
||||
{
|
||||
std::string data(255, 0x00);
|
||||
m_rVolume->Read(_Offset, data.size(), (u8*)&data[0], m_Wii);
|
||||
m_rVolume->Read(_Offset, data.size(), (u8*)&data[0], m_partition);
|
||||
data.erase(std::find(data.begin(), data.end(), 0x00), data.end());
|
||||
|
||||
// TODO: Should we really always use SHIFT-JIS?
|
||||
@ -251,14 +251,14 @@ const SFileInfo* CFileSystemGCWii::FindFileInfo(const std::string& _rFullPath)
|
||||
bool CFileSystemGCWii::DetectFileSystem()
|
||||
{
|
||||
u32 magic_bytes;
|
||||
if (m_rVolume->ReadSwapped(0x18, &magic_bytes, false) && magic_bytes == 0x5D1C9EA3)
|
||||
if (m_rVolume->ReadSwapped(0x18, &magic_bytes, m_partition) && magic_bytes == 0x5D1C9EA3)
|
||||
{
|
||||
m_Wii = true;
|
||||
m_offset_shift = 2; // Wii file system
|
||||
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;
|
||||
}
|
||||
|
||||
@ -268,21 +268,20 @@ bool CFileSystemGCWii::DetectFileSystem()
|
||||
void CFileSystemGCWii::InitFileSystem()
|
||||
{
|
||||
m_Initialized = true;
|
||||
u32 const shift = GetOffsetShift();
|
||||
|
||||
// read the whole FST
|
||||
u32 fst_offset_unshifted;
|
||||
if (!m_rVolume->ReadSwapped(0x424, &fst_offset_unshifted, m_Wii))
|
||||
if (!m_rVolume->ReadSwapped(0x424, &fst_offset_unshifted, m_partition))
|
||||
return;
|
||||
u64 FSTOffset = static_cast<u64>(fst_offset_unshifted) << shift;
|
||||
u64 FSTOffset = static_cast<u64>(fst_offset_unshifted) << m_offset_shift;
|
||||
|
||||
// read all fileinfos
|
||||
u32 name_offset, offset, size;
|
||||
if (!m_rVolume->ReadSwapped(FSTOffset + 0x0, &name_offset, m_Wii) ||
|
||||
!m_rVolume->ReadSwapped(FSTOffset + 0x4, &offset, m_Wii) ||
|
||||
!m_rVolume->ReadSwapped(FSTOffset + 0x8, &size, m_Wii))
|
||||
if (!m_rVolume->ReadSwapped(FSTOffset + 0x0, &name_offset, m_partition) ||
|
||||
!m_rVolume->ReadSwapped(FSTOffset + 0x4, &offset, m_partition) ||
|
||||
!m_rVolume->ReadSwapped(FSTOffset + 0x8, &size, m_partition))
|
||||
return;
|
||||
SFileInfo root = {name_offset, static_cast<u64>(offset) << shift, size};
|
||||
SFileInfo root = {name_offset, static_cast<u64>(offset) << m_offset_shift, size};
|
||||
|
||||
if (!root.IsDirectory())
|
||||
return;
|
||||
@ -308,12 +307,12 @@ void CFileSystemGCWii::InitFileSystem()
|
||||
{
|
||||
const u64 read_offset = FSTOffset + (i * 0xC);
|
||||
name_offset = 0;
|
||||
m_rVolume->ReadSwapped(read_offset + 0x0, &name_offset, m_Wii);
|
||||
m_rVolume->ReadSwapped(read_offset + 0x0, &name_offset, m_partition);
|
||||
offset = 0;
|
||||
m_rVolume->ReadSwapped(read_offset + 0x4, &offset, m_Wii);
|
||||
m_rVolume->ReadSwapped(read_offset + 0x4, &offset, m_partition);
|
||||
size = 0;
|
||||
m_rVolume->ReadSwapped(read_offset + 0x8, &size, m_Wii);
|
||||
m_FileInfoVector.emplace_back(name_offset, static_cast<u64>(offset) << shift, size);
|
||||
m_rVolume->ReadSwapped(read_offset + 0x8, &size, m_partition);
|
||||
m_FileInfoVector.emplace_back(name_offset, static_cast<u64>(offset) << m_offset_shift, size);
|
||||
NameTableOffset += 0xC;
|
||||
}
|
||||
|
||||
@ -351,9 +350,4 @@ size_t CFileSystemGCWii::BuildFilenames(const size_t _FirstIndex, const size_t _
|
||||
return CurrentIndex;
|
||||
}
|
||||
|
||||
u32 CFileSystemGCWii::GetOffsetShift() const
|
||||
{
|
||||
return m_Wii ? 2 : 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -14,11 +14,12 @@
|
||||
namespace DiscIO
|
||||
{
|
||||
class IVolume;
|
||||
struct Partition;
|
||||
|
||||
class CFileSystemGCWii : public IFileSystem
|
||||
{
|
||||
public:
|
||||
CFileSystemGCWii(const IVolume* _rVolume);
|
||||
CFileSystemGCWii(const IVolume* _rVolume, const Partition& partition);
|
||||
virtual ~CFileSystemGCWii();
|
||||
|
||||
bool IsValid() const override { return m_Valid; }
|
||||
@ -36,7 +37,7 @@ public:
|
||||
private:
|
||||
bool m_Initialized;
|
||||
bool m_Valid;
|
||||
bool m_Wii;
|
||||
u32 m_offset_shift;
|
||||
std::vector<SFileInfo> m_FileInfoVector;
|
||||
|
||||
std::string GetStringFromOffset(u64 _Offset) const;
|
||||
@ -45,7 +46,6 @@ private:
|
||||
void InitFileSystem();
|
||||
size_t BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex,
|
||||
const std::string& _szDirectory, u64 _NameTableOffset);
|
||||
u32 GetOffsetShift() const;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -5,10 +5,12 @@
|
||||
#include "DiscIO/Filesystem.h"
|
||||
#include <memory>
|
||||
#include "DiscIO/FileSystemGCWii.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
IFileSystem::IFileSystem(const IVolume* _rVolume) : m_rVolume(_rVolume)
|
||||
IFileSystem::IFileSystem(const IVolume* _rVolume, const Partition& partition)
|
||||
: m_rVolume(_rVolume), m_partition(partition)
|
||||
{
|
||||
}
|
||||
|
||||
@ -16,12 +18,12 @@ IFileSystem::~IFileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<IFileSystem> CreateFileSystem(const IVolume* volume)
|
||||
std::unique_ptr<IFileSystem> CreateFileSystem(const IVolume* volume, const Partition& partition)
|
||||
{
|
||||
if (!volume)
|
||||
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)
|
||||
return nullptr;
|
||||
|
@ -9,11 +9,10 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
class IVolume;
|
||||
|
||||
// file info of an FST entry
|
||||
struct SFileInfo
|
||||
{
|
||||
@ -35,7 +34,7 @@ struct SFileInfo
|
||||
class IFileSystem
|
||||
{
|
||||
public:
|
||||
IFileSystem(const IVolume* _rVolume);
|
||||
IFileSystem(const IVolume* _rVolume, const Partition& partition);
|
||||
|
||||
virtual ~IFileSystem();
|
||||
virtual bool IsValid() const = 0;
|
||||
@ -50,10 +49,12 @@ public:
|
||||
virtual u64 GetBootDOLOffset() const = 0;
|
||||
virtual u32 GetBootDOLSize(u64 dol_offset) const = 0;
|
||||
|
||||
virtual const Partition GetPartition() const { return m_partition; }
|
||||
protected:
|
||||
const IVolume* m_rVolume;
|
||||
const IVolume* const m_rVolume;
|
||||
const Partition m_partition;
|
||||
};
|
||||
|
||||
std::unique_ptr<IFileSystem> CreateFileSystem(const IVolume* volume);
|
||||
std::unique_ptr<IFileSystem> CreateFileSystem(const IVolume* volume, const Partition& partition);
|
||||
|
||||
} // namespace
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -19,49 +20,71 @@ namespace DiscIO
|
||||
{
|
||||
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
|
||||
{
|
||||
public:
|
||||
IVolume() {}
|
||||
virtual ~IVolume() {}
|
||||
// decrypt parameter must be false if not reading a Wii disc
|
||||
virtual bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const = 0;
|
||||
virtual bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const = 0;
|
||||
template <typename T>
|
||||
bool ReadSwapped(u64 offset, T* buffer, bool decrypt) const
|
||||
bool ReadSwapped(u64 offset, T* buffer, const Partition& partition) const
|
||||
{
|
||||
T temp;
|
||||
if (!Read(offset, sizeof(T), reinterpret_cast<u8*>(&temp), decrypt))
|
||||
if (!Read(offset, sizeof(T), reinterpret_cast<u8*>(&temp), partition))
|
||||
return false;
|
||||
*buffer = Common::FromBigEndian(temp);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool GetTitleID(u64*) const { return false; }
|
||||
virtual IOS::ES::TicketReader GetTicket() const { return {}; }
|
||||
virtual IOS::ES::TMDReader GetTMD() const { return {}; }
|
||||
virtual u64 PartitionOffsetToRawOffset(u64 offset) const { return offset; }
|
||||
virtual std::string GetGameID() const = 0;
|
||||
virtual std::string GetMakerID() const = 0;
|
||||
virtual u16 GetRevision() const = 0;
|
||||
virtual std::string GetInternalName() const = 0;
|
||||
virtual std::vector<Partition> GetPartitions() const { return {{}}; }
|
||||
virtual Partition GetGamePartition() const { return PARTITION_NONE; }
|
||||
bool GetTitleID(u64* buffer) const { return GetTitleID(buffer, GetGamePartition()); }
|
||||
virtual bool GetTitleID(u64* buffer, const Partition& partition) const { return false; }
|
||||
virtual IOS::ES::TicketReader GetTicket(const Partition& partition) const { return {}; }
|
||||
virtual IOS::ES::TMDReader GetTMD(const Partition& partition) const { return {}; }
|
||||
std::string GetGameID() const { return GetGameID(GetGamePartition()); }
|
||||
virtual std::string GetGameID(const Partition& partition) const = 0;
|
||||
std::string GetMakerID() const { return GetMakerID(GetGamePartition()); }
|
||||
virtual std::string GetMakerID(const Partition& partition) const = 0;
|
||||
u16 GetRevision() const { return GetRevision(GetGamePartition()); }
|
||||
virtual u16 GetRevision(const Partition& partition) const = 0;
|
||||
std::string GetInternalName() const { return GetInternalName(GetGamePartition()); }
|
||||
virtual std::string GetInternalName(const Partition& partition) const = 0;
|
||||
virtual std::map<Language, std::string> GetShortNames() 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> GetLongMakers() const { return {{}}; }
|
||||
virtual std::map<Language, std::string> GetDescriptions() const { return {{}}; }
|
||||
virtual std::vector<u32> GetBanner(int* width, int* height) const = 0;
|
||||
virtual u64 GetFSTSize() const = 0;
|
||||
virtual std::string GetApploaderDate() const = 0;
|
||||
u64 GetFSTSize() const { return GetFSTSize(GetGamePartition()); }
|
||||
virtual u64 GetFSTSize(const Partition& partition) const = 0;
|
||||
std::string GetApploaderDate() const { return GetApploaderDate(GetGamePartition()); }
|
||||
virtual std::string GetApploaderDate(const Partition& partition) const = 0;
|
||||
// 0 is the first disc, 1 is the second disc
|
||||
virtual u8 GetDiscNumber() const { return 0; }
|
||||
u8 GetDiscNumber() const { return GetDiscNumber(GetGamePartition()); }
|
||||
virtual u8 GetDiscNumber(const Partition& partition) const { return 0; }
|
||||
virtual Platform GetVolumeType() const = 0;
|
||||
virtual bool SupportsIntegrityCheck() const { return false; }
|
||||
virtual bool CheckIntegrity() const { return false; }
|
||||
virtual bool ChangePartition(u64 offset) { return false; }
|
||||
virtual bool CheckIntegrity(const Partition& partition) const { return false; }
|
||||
virtual Region GetRegion() const = 0;
|
||||
virtual Country GetCountry() const = 0;
|
||||
Country GetCountry() const { return GetCountry(GetGamePartition()); }
|
||||
virtual Country GetCountry(const Partition& partition) const = 0;
|
||||
virtual BlobType GetBlobType() const = 0;
|
||||
// Size of virtual disc (not always accurate)
|
||||
// Size of virtual disc (may be inaccurate depending on the blob type)
|
||||
virtual u64 GetSize() const = 0;
|
||||
// Size on disc (compressed size)
|
||||
virtual u64 GetRawSize() const = 0;
|
||||
|
@ -9,8 +9,6 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <mbedtls/aes.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
@ -33,24 +31,9 @@ enum EDiscType
|
||||
DISC_TYPE_WAD
|
||||
};
|
||||
|
||||
static const unsigned char s_master_key[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4,
|
||||
0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7};
|
||||
|
||||
static const unsigned char s_master_key_korean[16] = {
|
||||
0x63, 0xb8, 0x2b, 0xb4, 0xf4, 0x61, 0x4e, 0x2e, 0x13, 0xf2, 0xfe, 0xfb, 0xba, 0x4c, 0x9b, 0x7e};
|
||||
|
||||
static const unsigned char s_master_key_rvt[16] = {0xa1, 0x60, 0x4a, 0x6a, 0x71, 0x23, 0xb5, 0x29,
|
||||
0xae, 0x8b, 0xec, 0x32, 0xc8, 0x16, 0xfc, 0xaa};
|
||||
|
||||
static const char s_issuer_rvt[] = "Root-CA00000002-XS00000006";
|
||||
|
||||
static std::unique_ptr<IVolume> CreateVolumeFromCryptedWiiImage(std::unique_ptr<IBlobReader> reader,
|
||||
u32 partition_group,
|
||||
u32 volume_type, u32 volume_number);
|
||||
EDiscType GetDiscType(IBlobReader& _rReader);
|
||||
|
||||
std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename, u32 partition_group,
|
||||
u32 volume_number)
|
||||
std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename)
|
||||
{
|
||||
std::unique_ptr<IBlobReader> reader(CreateBlobReader(filename));
|
||||
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));
|
||||
|
||||
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:
|
||||
return nullptr;
|
||||
@ -85,110 +68,6 @@ std::unique_ptr<IVolume> CreateVolumeFromDirectory(const std::string& directory,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VolumeKeyForPartition(IBlobReader& _rReader, u64 offset, u8* VolumeKey)
|
||||
{
|
||||
u8 SubKey[16];
|
||||
_rReader.Read(offset + 0x1bf, 16, SubKey);
|
||||
|
||||
u8 IV[16];
|
||||
memset(IV, 0, 16);
|
||||
_rReader.Read(offset + 0x44c, 8, IV);
|
||||
|
||||
mbedtls_aes_context AES_ctx;
|
||||
|
||||
u8 issuer[sizeof(s_issuer_rvt)];
|
||||
_rReader.Read(offset + 0x140, sizeof(issuer), issuer);
|
||||
if (!memcmp(issuer, s_issuer_rvt, sizeof(s_issuer_rvt)))
|
||||
{
|
||||
// RVT issuer. Use the RVT (debug) master key.
|
||||
mbedtls_aes_setkey_dec(&AES_ctx, s_master_key_rvt, 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Issue: 6813
|
||||
// Magic value is at partition's offset + 0x1f1 (1byte)
|
||||
// If encrypted with the Korean key, the magic value would be 1
|
||||
// Otherwise it is zero
|
||||
u8 using_korean_key = 0;
|
||||
_rReader.Read(offset + 0x1f1, sizeof(u8), &using_korean_key);
|
||||
u8 region = 0;
|
||||
_rReader.Read(0x3, sizeof(u8), ®ion);
|
||||
|
||||
mbedtls_aes_setkey_dec(
|
||||
&AES_ctx, (using_korean_key == 1 && region == 'K' ? s_master_key_korean : s_master_key),
|
||||
128);
|
||||
}
|
||||
|
||||
mbedtls_aes_crypt_cbc(&AES_ctx, MBEDTLS_AES_DECRYPT, 16, IV, SubKey, VolumeKey);
|
||||
}
|
||||
|
||||
static std::unique_ptr<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)
|
||||
{
|
||||
CBlobBigEndianReader Reader(_rReader);
|
||||
|
@ -7,18 +7,13 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
class IVolume;
|
||||
class IBlobReader;
|
||||
|
||||
std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename,
|
||||
u32 partition_group = 0, u32 volume_number = -1);
|
||||
std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename);
|
||||
std::unique_ptr<IVolume> CreateVolumeFromDirectory(const std::string& directory, bool is_wii,
|
||||
const std::string& apploader = "",
|
||||
const std::string& dol = "");
|
||||
void VolumeKeyForPartition(IBlobReader& _rReader, u64 offset, u8* VolumeKey);
|
||||
|
||||
} // namespace
|
||||
|
@ -64,8 +64,10 @@ bool CVolumeDirectory::IsValidDirectory(const std::string& directory)
|
||||
return File::IsDirectory(ExtractDirectoryName(directory));
|
||||
}
|
||||
|
||||
bool CVolumeDirectory::Read(u64 offset, u64 length, u8* buffer, bool decrypt) const
|
||||
bool CVolumeDirectory::Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const
|
||||
{
|
||||
bool decrypt = partition != PARTITION_NONE;
|
||||
|
||||
if (!decrypt && (offset + length >= 0x400) && m_is_wii)
|
||||
{
|
||||
// Fully supporting this would require re-encrypting every file that's read.
|
||||
@ -77,7 +79,7 @@ bool CVolumeDirectory::Read(u64 offset, u64 length, u8* buffer, bool decrypt) co
|
||||
}
|
||||
|
||||
if (decrypt && !m_is_wii)
|
||||
PanicAlertT("Tried to decrypt data from a non-Wii volume");
|
||||
return false;
|
||||
|
||||
// header
|
||||
if (offset < DISKHEADERINFO_ADDRESS)
|
||||
@ -157,7 +159,17 @@ bool CVolumeDirectory::Read(u64 offset, u64 length, u8* buffer, bool decrypt) co
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CVolumeDirectory::GetGameID() const
|
||||
std::vector<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);
|
||||
}
|
||||
@ -175,21 +187,21 @@ Region CVolumeDirectory::GetRegion() const
|
||||
return RegionSwitchGC(m_disk_header[3]);
|
||||
}
|
||||
|
||||
Country CVolumeDirectory::GetCountry() const
|
||||
Country CVolumeDirectory::GetCountry(const Partition& partition) const
|
||||
{
|
||||
return CountrySwitch(m_disk_header[3]);
|
||||
}
|
||||
|
||||
std::string CVolumeDirectory::GetMakerID() const
|
||||
std::string CVolumeDirectory::GetMakerID(const Partition& partition) const
|
||||
{
|
||||
// Not implemented
|
||||
return "00";
|
||||
}
|
||||
|
||||
std::string CVolumeDirectory::GetInternalName() const
|
||||
std::string CVolumeDirectory::GetInternalName(const Partition& partition) const
|
||||
{
|
||||
char name[0x60];
|
||||
if (Read(0x20, 0x60, (u8*)name, false))
|
||||
if (Read(0x20, 0x60, (u8*)name, partition))
|
||||
return DecodeString(name);
|
||||
else
|
||||
return "";
|
||||
@ -218,13 +230,13 @@ void CVolumeDirectory::SetName(const std::string& name)
|
||||
m_disk_header[length + 0x20] = 0;
|
||||
}
|
||||
|
||||
u64 CVolumeDirectory::GetFSTSize() const
|
||||
u64 CVolumeDirectory::GetFSTSize(const Partition& partition) const
|
||||
{
|
||||
// Not implemented
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string CVolumeDirectory::GetApploaderDate() const
|
||||
std::string CVolumeDirectory::GetApploaderDate(const Partition& partition) const
|
||||
{
|
||||
// Not implemented
|
||||
return "VOID";
|
||||
|
@ -39,26 +39,28 @@ public:
|
||||
|
||||
static bool IsValidDirectory(const std::string& directory);
|
||||
|
||||
bool Read(u64 offset, u64 length, u8* buffer, bool decrypt) const override;
|
||||
bool Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const override;
|
||||
std::vector<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);
|
||||
|
||||
std::string GetMakerID() const override;
|
||||
std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override;
|
||||
|
||||
u16 GetRevision() const override { return 0; }
|
||||
std::string GetInternalName() const override;
|
||||
u16 GetRevision(const Partition& partition = PARTITION_NONE) const override { return 0; }
|
||||
std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override;
|
||||
std::map<Language, std::string> GetLongNames() const override;
|
||||
std::vector<u32> GetBanner(int* width, int* height) const override;
|
||||
void SetName(const std::string&);
|
||||
|
||||
u64 GetFSTSize() const override;
|
||||
u64 GetFSTSize(const Partition& partition = PARTITION_NONE) const override;
|
||||
|
||||
std::string GetApploaderDate() const override;
|
||||
std::string GetApploaderDate(const Partition& partition = PARTITION_NONE) const override;
|
||||
Platform GetVolumeType() const override;
|
||||
|
||||
Region GetRegion() const override;
|
||||
Country GetCountry() const override;
|
||||
Country GetCountry(const Partition& partition = PARTITION_NONE) const override;
|
||||
|
||||
BlobType GetBlobType() const override;
|
||||
u64 GetSize() const override;
|
||||
|
@ -32,21 +32,21 @@ CVolumeGC::~CVolumeGC()
|
||||
{
|
||||
}
|
||||
|
||||
bool CVolumeGC::Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const
|
||||
bool CVolumeGC::Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const
|
||||
{
|
||||
if (decrypt)
|
||||
PanicAlertT("Tried to decrypt data from a non-Wii volume");
|
||||
if (partition != PARTITION_NONE)
|
||||
return false;
|
||||
|
||||
return m_pReader->Read(_Offset, _Length, _pBuffer);
|
||||
}
|
||||
|
||||
std::string CVolumeGC::GetGameID() const
|
||||
std::string CVolumeGC::GetGameID(const Partition& partition) const
|
||||
{
|
||||
static const std::string NO_UID("NO_UID");
|
||||
|
||||
char ID[6];
|
||||
|
||||
if (!Read(0, sizeof(ID), reinterpret_cast<u8*>(ID)))
|
||||
if (!Read(0, sizeof(ID), reinterpret_cast<u8*>(ID), partition))
|
||||
{
|
||||
PanicAlertT("Failed to read unique ID from disc image");
|
||||
return NO_UID;
|
||||
@ -58,43 +58,43 @@ std::string CVolumeGC::GetGameID() const
|
||||
Region CVolumeGC::GetRegion() const
|
||||
{
|
||||
u8 country_code;
|
||||
if (!ReadSwapped(3, &country_code, false))
|
||||
if (!ReadSwapped(3, &country_code, PARTITION_NONE))
|
||||
return Region::UNKNOWN_REGION;
|
||||
|
||||
return RegionSwitchGC(country_code);
|
||||
}
|
||||
|
||||
Country CVolumeGC::GetCountry() const
|
||||
Country CVolumeGC::GetCountry(const Partition& partition) const
|
||||
{
|
||||
u8 country_code;
|
||||
if (!ReadSwapped(3, &country_code, false))
|
||||
if (!ReadSwapped(3, &country_code, partition))
|
||||
return Country::COUNTRY_UNKNOWN;
|
||||
|
||||
return CountrySwitch(country_code);
|
||||
}
|
||||
|
||||
std::string CVolumeGC::GetMakerID() const
|
||||
std::string CVolumeGC::GetMakerID(const Partition& partition) const
|
||||
{
|
||||
char makerID[2];
|
||||
if (!Read(0x4, 0x2, (u8*)&makerID))
|
||||
if (!Read(0x4, 0x2, (u8*)&makerID, partition))
|
||||
return std::string();
|
||||
|
||||
return DecodeString(makerID);
|
||||
}
|
||||
|
||||
u16 CVolumeGC::GetRevision() const
|
||||
u16 CVolumeGC::GetRevision(const Partition& partition) const
|
||||
{
|
||||
u8 revision;
|
||||
if (!ReadSwapped(7, &revision, false))
|
||||
if (!ReadSwapped(7, &revision, partition))
|
||||
return 0;
|
||||
|
||||
return revision;
|
||||
}
|
||||
|
||||
std::string CVolumeGC::GetInternalName() const
|
||||
std::string CVolumeGC::GetInternalName(const Partition& partition) const
|
||||
{
|
||||
char name[0x60];
|
||||
if (Read(0x20, 0x60, (u8*)name))
|
||||
if (Read(0x20, 0x60, (u8*)name, partition))
|
||||
return DecodeString(name);
|
||||
|
||||
return "";
|
||||
@ -138,19 +138,19 @@ std::vector<u32> CVolumeGC::GetBanner(int* width, int* height) const
|
||||
return m_image_buffer;
|
||||
}
|
||||
|
||||
u64 CVolumeGC::GetFSTSize() const
|
||||
u64 CVolumeGC::GetFSTSize(const Partition& partition) const
|
||||
{
|
||||
u32 size;
|
||||
if (!Read(0x428, 0x4, (u8*)&size))
|
||||
if (!Read(0x428, 0x4, (u8*)&size, partition))
|
||||
return 0;
|
||||
|
||||
return Common::swap32(size);
|
||||
}
|
||||
|
||||
std::string CVolumeGC::GetApploaderDate() const
|
||||
std::string CVolumeGC::GetApploaderDate(const Partition& partition) const
|
||||
{
|
||||
char date[16];
|
||||
if (!Read(0x2440, 0x10, (u8*)&date))
|
||||
if (!Read(0x2440, 0x10, (u8*)&date, partition))
|
||||
return std::string();
|
||||
|
||||
return DecodeString(date);
|
||||
@ -171,10 +171,10 @@ u64 CVolumeGC::GetRawSize() const
|
||||
return m_pReader->GetRawSize();
|
||||
}
|
||||
|
||||
u8 CVolumeGC::GetDiscNumber() const
|
||||
u8 CVolumeGC::GetDiscNumber(const Partition& partition) const
|
||||
{
|
||||
u8 disc_number = 0;
|
||||
ReadSwapped(6, &disc_number, false);
|
||||
ReadSwapped(6, &disc_number, partition);
|
||||
return disc_number;
|
||||
}
|
||||
|
||||
@ -192,9 +192,11 @@ void CVolumeGC::LoadBannerFile() const
|
||||
m_banner_loaded = true;
|
||||
|
||||
GCBanner banner_file;
|
||||
std::unique_ptr<IFileSystem> file_system(CreateFileSystem(this));
|
||||
size_t file_size = (size_t)file_system->GetFileSize("opening.bnr");
|
||||
std::unique_ptr<IFileSystem> file_system(CreateFileSystem(this, PARTITION_NONE));
|
||||
if (!file_system)
|
||||
return;
|
||||
|
||||
size_t file_size = static_cast<size_t>(file_system->GetFileSize("opening.bnr"));
|
||||
constexpr int BNR1_MAGIC = 0x31524e42;
|
||||
constexpr int BNR2_MAGIC = 0x32524e42;
|
||||
if (file_size != BNR1_SIZE && file_size != BNR2_SIZE)
|
||||
|
@ -28,24 +28,25 @@ class CVolumeGC : public IVolume
|
||||
public:
|
||||
CVolumeGC(std::unique_ptr<IBlobReader> reader);
|
||||
~CVolumeGC();
|
||||
bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt = false) const override;
|
||||
std::string GetGameID() const override;
|
||||
std::string GetMakerID() const override;
|
||||
u16 GetRevision() const override;
|
||||
std::string GetInternalName() const override;
|
||||
bool Read(u64 _Offset, u64 _Length, u8* _pBuffer,
|
||||
const Partition& partition = PARTITION_NONE) const override;
|
||||
std::string GetGameID(const Partition& partition = PARTITION_NONE) const override;
|
||||
std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override;
|
||||
u16 GetRevision(const Partition& partition = PARTITION_NONE) const override;
|
||||
std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override;
|
||||
std::map<Language, std::string> GetShortNames() const override;
|
||||
std::map<Language, std::string> GetLongNames() const override;
|
||||
std::map<Language, std::string> GetShortMakers() const override;
|
||||
std::map<Language, std::string> GetLongMakers() const override;
|
||||
std::map<Language, std::string> GetDescriptions() const override;
|
||||
std::vector<u32> GetBanner(int* width, int* height) const override;
|
||||
u64 GetFSTSize() const override;
|
||||
std::string GetApploaderDate() const override;
|
||||
u8 GetDiscNumber() const override;
|
||||
u64 GetFSTSize(const Partition& partition = PARTITION_NONE) const override;
|
||||
std::string GetApploaderDate(const Partition& partition = PARTITION_NONE) const override;
|
||||
u8 GetDiscNumber(const Partition& partition = PARTITION_NONE) const override;
|
||||
|
||||
Platform GetVolumeType() const override;
|
||||
Region GetRegion() const override;
|
||||
Country GetCountry() const override;
|
||||
Country GetCountry(const Partition& partition = PARTITION_NONE) const override;
|
||||
BlobType GetBlobType() const override;
|
||||
u64 GetSize() const override;
|
||||
u64 GetRawSize() const override;
|
||||
|
@ -29,11 +29,11 @@ CVolumeWAD::CVolumeWAD(std::unique_ptr<IBlobReader> reader) : m_reader(std::move
|
||||
_assert_(m_reader);
|
||||
|
||||
// Source: http://wiibrew.org/wiki/WAD_files
|
||||
ReadSwapped(0x00, &m_hdr_size, false);
|
||||
ReadSwapped(0x08, &m_cert_size, false);
|
||||
ReadSwapped(0x10, &m_tick_size, false);
|
||||
ReadSwapped(0x14, &m_tmd_size, false);
|
||||
ReadSwapped(0x18, &m_data_size, false);
|
||||
ReadSwapped(0x00, &m_hdr_size, PARTITION_NONE);
|
||||
ReadSwapped(0x08, &m_cert_size, PARTITION_NONE);
|
||||
ReadSwapped(0x10, &m_tick_size, PARTITION_NONE);
|
||||
ReadSwapped(0x14, &m_tmd_size, PARTITION_NONE);
|
||||
ReadSwapped(0x18, &m_data_size, PARTITION_NONE);
|
||||
|
||||
m_offset = Common::AlignUp(m_hdr_size, 0x40) + Common::AlignUp(m_cert_size, 0x40);
|
||||
m_tmd_offset = Common::AlignUp(m_hdr_size, 0x40) + Common::AlignUp(m_cert_size, 0x40) +
|
||||
@ -48,7 +48,7 @@ CVolumeWAD::CVolumeWAD(std::unique_ptr<IBlobReader> reader) : m_reader(std::move
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@ -56,10 +56,10 @@ CVolumeWAD::~CVolumeWAD()
|
||||
{
|
||||
}
|
||||
|
||||
bool CVolumeWAD::Read(u64 offset, u64 length, u8* buffer, bool decrypt) const
|
||||
bool CVolumeWAD::Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const
|
||||
{
|
||||
if (decrypt)
|
||||
PanicAlertT("Tried to decrypt data from a non-Wii volume");
|
||||
if (partition != PARTITION_NONE)
|
||||
return false;
|
||||
|
||||
return m_reader->Read(offset, length, buffer);
|
||||
}
|
||||
@ -71,7 +71,7 @@ Region CVolumeWAD::GetRegion() const
|
||||
return m_tmd.GetRegion();
|
||||
}
|
||||
|
||||
Country CVolumeWAD::GetCountry() const
|
||||
Country CVolumeWAD::GetCountry(const Partition& partition) const
|
||||
{
|
||||
if (!m_tmd.IsValid())
|
||||
return Country::COUNTRY_UNKNOWN;
|
||||
@ -83,20 +83,20 @@ Country CVolumeWAD::GetCountry() const
|
||||
return CountrySwitch(country_code);
|
||||
}
|
||||
|
||||
IOS::ES::TMDReader CVolumeWAD::GetTMD() const
|
||||
IOS::ES::TMDReader CVolumeWAD::GetTMD(const Partition& partition) const
|
||||
{
|
||||
return m_tmd;
|
||||
}
|
||||
|
||||
std::string CVolumeWAD::GetGameID() const
|
||||
std::string CVolumeWAD::GetGameID(const Partition& partition) const
|
||||
{
|
||||
return m_tmd.GetGameID();
|
||||
}
|
||||
|
||||
std::string CVolumeWAD::GetMakerID() const
|
||||
std::string CVolumeWAD::GetMakerID(const Partition& partition) const
|
||||
{
|
||||
char temp[2];
|
||||
if (!Read(0x198 + m_tmd_offset, 2, (u8*)temp))
|
||||
if (!Read(0x198 + m_tmd_offset, 2, (u8*)temp, partition))
|
||||
return "00";
|
||||
|
||||
// Some weird channels use 0x0000 in place of the MakerID, so we need a check here
|
||||
@ -107,12 +107,12 @@ std::string CVolumeWAD::GetMakerID() const
|
||||
return DecodeString(temp);
|
||||
}
|
||||
|
||||
bool CVolumeWAD::GetTitleID(u64* buffer) const
|
||||
bool CVolumeWAD::GetTitleID(u64* buffer, const Partition& partition) const
|
||||
{
|
||||
return ReadSwapped(m_offset + 0x01DC, buffer, false);
|
||||
return ReadSwapped(m_offset + 0x01DC, buffer, partition);
|
||||
}
|
||||
|
||||
u16 CVolumeWAD::GetRevision() const
|
||||
u16 CVolumeWAD::GetRevision(const Partition& partition) const
|
||||
{
|
||||
if (!m_tmd.IsValid())
|
||||
return 0;
|
||||
|
@ -31,20 +31,27 @@ class CVolumeWAD : public IVolume
|
||||
public:
|
||||
CVolumeWAD(std::unique_ptr<IBlobReader> reader);
|
||||
~CVolumeWAD();
|
||||
bool Read(u64 offset, u64 length, u8* buffer, bool decrypt = false) const override;
|
||||
bool GetTitleID(u64* buffer) const override;
|
||||
IOS::ES::TMDReader GetTMD() const override;
|
||||
std::string GetGameID() const override;
|
||||
std::string GetMakerID() const override;
|
||||
u16 GetRevision() const override;
|
||||
std::string GetInternalName() const override { return ""; }
|
||||
bool Read(u64 offset, u64 length, u8* buffer,
|
||||
const Partition& partition = PARTITION_NONE) const override;
|
||||
bool GetTitleID(u64* buffer, const Partition& partition = PARTITION_NONE) const override;
|
||||
IOS::ES::TMDReader GetTMD(const Partition& partition = PARTITION_NONE) const override;
|
||||
std::string GetGameID(const Partition& partition = PARTITION_NONE) const override;
|
||||
std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override;
|
||||
u16 GetRevision(const Partition& partition = PARTITION_NONE) const override;
|
||||
std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override
|
||||
{
|
||||
return "";
|
||||
}
|
||||
std::map<Language, std::string> GetLongNames() const override;
|
||||
std::vector<u32> GetBanner(int* width, int* height) const override;
|
||||
u64 GetFSTSize() const override { return 0; }
|
||||
std::string GetApploaderDate() const override { return ""; }
|
||||
u64 GetFSTSize(const Partition& partition = PARTITION_NONE) const override { return 0; }
|
||||
std::string GetApploaderDate(const Partition& partition = PARTITION_NONE) const override
|
||||
{
|
||||
return "";
|
||||
}
|
||||
Platform GetVolumeType() const override;
|
||||
Region GetRegion() const override;
|
||||
Country GetCountry() const override;
|
||||
Country GetCountry(const Partition& partition = PARTITION_NONE) const override;
|
||||
|
||||
BlobType GetBlobType() const override;
|
||||
u64 GetSize() const override;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "DiscIO/VolumeWiiCrypted.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
@ -28,58 +29,129 @@
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader, u64 _VolumeOffset,
|
||||
const unsigned char* _pVolumeKey)
|
||||
: m_pReader(std::move(reader)), m_AES_ctx(std::make_unique<mbedtls_aes_context>()),
|
||||
m_VolumeOffset(_VolumeOffset), m_dataOffset(0x20000), m_LastDecryptedBlockOffset(-1)
|
||||
constexpr u64 PARTITION_DATA_OFFSET = 0x20000;
|
||||
|
||||
CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader)
|
||||
: m_pReader(std::move(reader)), m_game_partition(PARTITION_NONE), m_last_decrypted_block(-1)
|
||||
{
|
||||
_assert_(m_pReader);
|
||||
|
||||
mbedtls_aes_setkey_dec(m_AES_ctx.get(), _pVolumeKey, 128);
|
||||
}
|
||||
// Get decryption keys for all partitions
|
||||
CBlobBigEndianReader big_endian_reader(*m_pReader.get());
|
||||
for (u32 partition_group = 0; partition_group < 4; ++partition_group)
|
||||
{
|
||||
u32 number_of_partitions;
|
||||
if (!big_endian_reader.ReadSwapped(0x40000 + (partition_group * 8), &number_of_partitions))
|
||||
continue;
|
||||
|
||||
bool CVolumeWiiCrypted::ChangePartition(u64 offset)
|
||||
{
|
||||
m_VolumeOffset = offset;
|
||||
m_LastDecryptedBlockOffset = -1;
|
||||
u32 read_buffer;
|
||||
if (!big_endian_reader.ReadSwapped(0x40000 + (partition_group * 8) + 4, &read_buffer))
|
||||
continue;
|
||||
const u64 partition_table_offset = (u64)read_buffer << 2;
|
||||
|
||||
u8 volume_key[16];
|
||||
DiscIO::VolumeKeyForPartition(*m_pReader, offset, volume_key);
|
||||
mbedtls_aes_setkey_dec(m_AES_ctx.get(), volume_key, 128);
|
||||
return true;
|
||||
for (u32 i = 0; i < number_of_partitions; i++)
|
||||
{
|
||||
if (!big_endian_reader.ReadSwapped(partition_table_offset + (i * 8), &read_buffer))
|
||||
continue;
|
||||
const u64 partition_offset = (u64)read_buffer << 2;
|
||||
|
||||
if (m_game_partition == PARTITION_NONE)
|
||||
{
|
||||
u32 partition_type;
|
||||
if (!big_endian_reader.ReadSwapped(partition_table_offset + (i * 8) + 4, &partition_type))
|
||||
continue;
|
||||
|
||||
if (partition_type == 0)
|
||||
m_game_partition = Partition(partition_offset);
|
||||
}
|
||||
|
||||
u8 sub_key[16];
|
||||
if (!m_pReader->Read(partition_offset + 0x1bf, 16, sub_key))
|
||||
continue;
|
||||
|
||||
u8 iv[16];
|
||||
memset(iv, 0, 16);
|
||||
if (!m_pReader->Read(partition_offset + 0x44c, 8, iv))
|
||||
continue;
|
||||
|
||||
static const u8 common_key_standard[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4,
|
||||
0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7};
|
||||
static const u8 common_key_korean[16] = {0x63, 0xb8, 0x2b, 0xb4, 0xf4, 0x61, 0x4e, 0x2e,
|
||||
0x13, 0xf2, 0xfe, 0xfb, 0xba, 0x4c, 0x9b, 0x7e};
|
||||
static const u8 common_key_rvt[16] = {0xa1, 0x60, 0x4a, 0x6a, 0x71, 0x23, 0xb5, 0x29,
|
||||
0xae, 0x8b, 0xec, 0x32, 0xc8, 0x16, 0xfc, 0xaa};
|
||||
static const char issuer_rvt[] = "Root-CA00000002-XS00000006";
|
||||
|
||||
const u8* common_key;
|
||||
|
||||
u8 issuer[sizeof(issuer_rvt)];
|
||||
if (!m_pReader->Read(partition_offset + 0x140, sizeof(issuer), issuer))
|
||||
continue;
|
||||
|
||||
if (!memcmp(issuer, issuer_rvt, sizeof(issuer_rvt)))
|
||||
{
|
||||
// RVT issuer. Use the RVT (debug) master key.
|
||||
common_key = common_key_rvt;
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 key_number = 0;
|
||||
if (!big_endian_reader.ReadSwapped(partition_offset + 0x1f1, &key_number))
|
||||
continue;
|
||||
common_key = (key_number == 1) ? common_key_korean : common_key_standard;
|
||||
}
|
||||
|
||||
mbedtls_aes_context aes_context;
|
||||
mbedtls_aes_setkey_dec(&aes_context, common_key, 128);
|
||||
|
||||
u8 volume_key[16];
|
||||
mbedtls_aes_crypt_cbc(&aes_context, MBEDTLS_AES_DECRYPT, 16, iv, sub_key, volume_key);
|
||||
|
||||
std::unique_ptr<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()
|
||||
{
|
||||
}
|
||||
|
||||
bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, bool decrypt) const
|
||||
bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer,
|
||||
const Partition& partition) const
|
||||
{
|
||||
if (!decrypt)
|
||||
if (partition == PARTITION_NONE)
|
||||
return m_pReader->Read(_ReadOffset, _Length, _pBuffer);
|
||||
|
||||
// Get the decryption key for the partition
|
||||
auto it = m_partitions.find(partition);
|
||||
if (it == m_partitions.end())
|
||||
return false;
|
||||
mbedtls_aes_context* aes_context = it->second.get();
|
||||
|
||||
std::vector<u8> read_buffer(BLOCK_TOTAL_SIZE);
|
||||
while (_Length > 0)
|
||||
{
|
||||
// Calculate block offset
|
||||
u64 Block = _ReadOffset / BLOCK_DATA_SIZE;
|
||||
u64 Offset = _ReadOffset % BLOCK_DATA_SIZE;
|
||||
// Calculate offsets
|
||||
u64 block_offset_on_disc =
|
||||
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
|
||||
if (!m_pReader->Read(m_VolumeOffset + m_dataOffset + Block * BLOCK_TOTAL_SIZE,
|
||||
BLOCK_TOTAL_SIZE, read_buffer.data()))
|
||||
if (!m_pReader->Read(block_offset_on_disc, BLOCK_TOTAL_SIZE, read_buffer.data()))
|
||||
return false;
|
||||
|
||||
// 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
|
||||
// use the content of m_pBuffer anymore after this
|
||||
mbedtls_aes_crypt_cbc(m_AES_ctx.get(), MBEDTLS_AES_DECRYPT, BLOCK_DATA_SIZE,
|
||||
&read_buffer[0x3D0], &read_buffer[BLOCK_HEADER_SIZE],
|
||||
m_LastDecryptedBlock);
|
||||
m_LastDecryptedBlockOffset = Block;
|
||||
// use the content of read_buffer anymore after this
|
||||
mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, BLOCK_DATA_SIZE, &read_buffer[0x3D0],
|
||||
&read_buffer[BLOCK_HEADER_SIZE], m_last_decrypted_block_data);
|
||||
m_last_decrypted_block = block_offset_on_disc;
|
||||
|
||||
// The only thing we currently use from the 0x000 - 0x3FF part
|
||||
// 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
|
||||
u64 MaxSizeToCopy = BLOCK_DATA_SIZE - Offset;
|
||||
u64 CopySize = (_Length > MaxSizeToCopy) ? MaxSizeToCopy : _Length;
|
||||
memcpy(_pBuffer, &m_LastDecryptedBlock[Offset], (size_t)CopySize);
|
||||
u64 copy_size = std::min(_Length, BLOCK_DATA_SIZE - data_offset_in_block);
|
||||
memcpy(_pBuffer, &m_last_decrypted_block_data[data_offset_in_block],
|
||||
static_cast<size_t>(copy_size));
|
||||
|
||||
// Update offsets
|
||||
_Length -= CopySize;
|
||||
_pBuffer += CopySize;
|
||||
_ReadOffset += CopySize;
|
||||
_Length -= copy_size;
|
||||
_pBuffer += copy_size;
|
||||
_ReadOffset += copy_size;
|
||||
}
|
||||
|
||||
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);
|
||||
Read(m_VolumeOffset, buffer.size(), buffer.data(), false);
|
||||
Read(partition.offset, buffer.size(), buffer.data(), PARTITION_NONE);
|
||||
return IOS::ES::TicketReader{std::move(buffer)};
|
||||
}
|
||||
|
||||
IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD() const
|
||||
IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD(const Partition& partition) const
|
||||
{
|
||||
u32 tmd_size = 0;
|
||||
u32 tmd_address = 0;
|
||||
|
||||
if (!ReadSwapped(m_VolumeOffset + 0x2a4, &tmd_size, false))
|
||||
if (!ReadSwapped(partition.offset + 0x2a4, &tmd_size, PARTITION_NONE))
|
||||
return {};
|
||||
if (!ReadSwapped(m_VolumeOffset + 0x2a8, &tmd_address, false))
|
||||
if (!ReadSwapped(partition.offset + 0x2a8, &tmd_address, PARTITION_NONE))
|
||||
return {};
|
||||
tmd_address <<= 2;
|
||||
|
||||
@ -135,23 +220,26 @@ IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD() const
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
std::string CVolumeWiiCrypted::GetGameID() const
|
||||
std::string CVolumeWiiCrypted::GetGameID(const Partition& partition) const
|
||||
{
|
||||
char ID[6];
|
||||
|
||||
if (!Read(0, 6, (u8*)ID, true))
|
||||
if (!Read(0, 6, (u8*)ID, partition))
|
||||
return std::string();
|
||||
|
||||
return DecodeString(ID);
|
||||
@ -160,16 +248,16 @@ std::string CVolumeWiiCrypted::GetGameID() const
|
||||
Region CVolumeWiiCrypted::GetRegion() const
|
||||
{
|
||||
u32 region_code;
|
||||
if (!ReadSwapped(0x4E000, ®ion_code, false))
|
||||
if (!ReadSwapped(0x4E000, ®ion_code, PARTITION_NONE))
|
||||
return Region::UNKNOWN_REGION;
|
||||
|
||||
return static_cast<Region>(region_code);
|
||||
}
|
||||
|
||||
Country CVolumeWiiCrypted::GetCountry() const
|
||||
Country CVolumeWiiCrypted::GetCountry(const Partition& partition) const
|
||||
{
|
||||
u8 country_byte;
|
||||
if (!ReadSwapped(3, &country_byte, true))
|
||||
if (!ReadSwapped(3, &country_byte, partition))
|
||||
return Country::COUNTRY_UNKNOWN;
|
||||
|
||||
const Region region = GetRegion();
|
||||
@ -180,29 +268,29 @@ Country CVolumeWiiCrypted::GetCountry() const
|
||||
return CountrySwitch(country_byte);
|
||||
}
|
||||
|
||||
std::string CVolumeWiiCrypted::GetMakerID() const
|
||||
std::string CVolumeWiiCrypted::GetMakerID(const Partition& partition) const
|
||||
{
|
||||
char makerID[2];
|
||||
|
||||
if (!Read(0x4, 0x2, (u8*)&makerID, true))
|
||||
if (!Read(0x4, 0x2, (u8*)&makerID, partition))
|
||||
return std::string();
|
||||
|
||||
return DecodeString(makerID);
|
||||
}
|
||||
|
||||
u16 CVolumeWiiCrypted::GetRevision() const
|
||||
u16 CVolumeWiiCrypted::GetRevision(const Partition& partition) const
|
||||
{
|
||||
u8 revision;
|
||||
if (!ReadSwapped(7, &revision, true))
|
||||
if (!ReadSwapped(7, &revision, partition))
|
||||
return 0;
|
||||
|
||||
return revision;
|
||||
}
|
||||
|
||||
std::string CVolumeWiiCrypted::GetInternalName() const
|
||||
std::string CVolumeWiiCrypted::GetInternalName(const Partition& partition) const
|
||||
{
|
||||
char name_buffer[0x60];
|
||||
if (Read(0x20, 0x60, (u8*)&name_buffer, true))
|
||||
if (Read(0x20, 0x60, (u8*)&name_buffer, partition))
|
||||
return DecodeString(name_buffer);
|
||||
|
||||
return "";
|
||||
@ -210,7 +298,10 @@ std::string CVolumeWiiCrypted::GetInternalName() 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);
|
||||
size_t size = file_system->ReadFile("opening.bnr", opening_bnr.data(), opening_bnr.size(), 0x5C);
|
||||
opening_bnr.resize(size);
|
||||
@ -223,27 +314,27 @@ std::vector<u32> CVolumeWiiCrypted::GetBanner(int* width, int* height) const
|
||||
*height = 0;
|
||||
|
||||
u64 title_id;
|
||||
if (!GetTitleID(&title_id))
|
||||
if (!GetTitleID(&title_id, GetGamePartition()))
|
||||
return std::vector<u32>();
|
||||
|
||||
return GetWiiBanner(width, height, title_id);
|
||||
}
|
||||
|
||||
u64 CVolumeWiiCrypted::GetFSTSize() const
|
||||
u64 CVolumeWiiCrypted::GetFSTSize(const Partition& partition) const
|
||||
{
|
||||
u32 size;
|
||||
|
||||
if (!Read(0x428, 0x4, (u8*)&size, true))
|
||||
if (!Read(0x428, 0x4, (u8*)&size, partition))
|
||||
return 0;
|
||||
|
||||
return (u64)Common::swap32(size) << 2;
|
||||
}
|
||||
|
||||
std::string CVolumeWiiCrypted::GetApploaderDate() const
|
||||
std::string CVolumeWiiCrypted::GetApploaderDate(const Partition& partition) const
|
||||
{
|
||||
char date[16];
|
||||
|
||||
if (!Read(0x2440, 0x10, (u8*)&date, true))
|
||||
if (!Read(0x2440, 0x10, (u8*)&date, partition))
|
||||
return std::string();
|
||||
|
||||
return DecodeString(date);
|
||||
@ -254,10 +345,10 @@ Platform CVolumeWiiCrypted::GetVolumeType() const
|
||||
return Platform::WII_DISC;
|
||||
}
|
||||
|
||||
u8 CVolumeWiiCrypted::GetDiscNumber() const
|
||||
u8 CVolumeWiiCrypted::GetDiscNumber(const Partition& partition) const
|
||||
{
|
||||
u8 disc_number = 0;
|
||||
ReadSwapped(6, &disc_number, true);
|
||||
ReadSwapped(6, &disc_number, partition);
|
||||
return disc_number;
|
||||
}
|
||||
|
||||
@ -276,29 +367,34 @@ u64 CVolumeWiiCrypted::GetRawSize() const
|
||||
return m_pReader->GetRawSize();
|
||||
}
|
||||
|
||||
bool CVolumeWiiCrypted::CheckIntegrity() const
|
||||
bool CVolumeWiiCrypted::CheckIntegrity(const Partition& partition) const
|
||||
{
|
||||
// Get the decryption key for the partition
|
||||
auto it = m_partitions.find(partition);
|
||||
if (it == m_partitions.end())
|
||||
return false;
|
||||
mbedtls_aes_context* aes_context = it->second.get();
|
||||
|
||||
// Get partition data size
|
||||
u32 partSizeDiv4;
|
||||
Read(m_VolumeOffset + 0x2BC, 4, (u8*)&partSizeDiv4, false);
|
||||
Read(partition.offset + 0x2BC, 4, (u8*)&partSizeDiv4, PARTITION_NONE);
|
||||
u64 partDataSize = (u64)Common::swap32(partSizeDiv4) * 4;
|
||||
|
||||
u32 nClusters = (u32)(partDataSize / 0x8000);
|
||||
for (u32 clusterID = 0; clusterID < nClusters; ++clusterID)
|
||||
{
|
||||
u64 clusterOff = m_VolumeOffset + m_dataOffset + (u64)clusterID * 0x8000;
|
||||
u64 clusterOff = partition.offset + PARTITION_DATA_OFFSET + (u64)clusterID * 0x8000;
|
||||
|
||||
// Read and decrypt the cluster metadata
|
||||
u8 clusterMDCrypted[0x400];
|
||||
u8 clusterMD[0x400];
|
||||
u8 IV[16] = {0};
|
||||
if (!Read(clusterOff, 0x400, clusterMDCrypted, false))
|
||||
if (!Read(clusterOff, 0x400, clusterMDCrypted, PARTITION_NONE))
|
||||
{
|
||||
WARN_LOG(DISCIO, "Integrity Check: fail at cluster %d: could not read metadata", clusterID);
|
||||
return false;
|
||||
}
|
||||
mbedtls_aes_crypt_cbc(m_AES_ctx.get(), MBEDTLS_AES_DECRYPT, 0x400, IV, clusterMDCrypted,
|
||||
clusterMD);
|
||||
mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, 0x400, IV, clusterMDCrypted, clusterMD);
|
||||
|
||||
// Some clusters have invalid data and metadata because they aren't
|
||||
// meant to be read by the game (for example, holes between files). To
|
||||
@ -317,7 +413,7 @@ bool CVolumeWiiCrypted::CheckIntegrity() const
|
||||
continue;
|
||||
|
||||
u8 clusterData[0x7C00];
|
||||
if (!Read((u64)clusterID * 0x7C00, 0x7C00, clusterData, true))
|
||||
if (!Read((u64)clusterID * 0x7C00, 0x7C00, clusterData, partition))
|
||||
{
|
||||
WARN_LOG(DISCIO, "Integrity Check: fail at cluster %d: could not read data", clusterID);
|
||||
return false;
|
||||
|
@ -28,48 +28,47 @@ enum class Platform;
|
||||
class CVolumeWiiCrypted : public IVolume
|
||||
{
|
||||
public:
|
||||
CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader, u64 _VolumeOffset,
|
||||
const unsigned char* _pVolumeKey);
|
||||
CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader);
|
||||
~CVolumeWiiCrypted();
|
||||
bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const override;
|
||||
bool GetTitleID(u64* buffer) const override;
|
||||
IOS::ES::TicketReader GetTicket() const override;
|
||||
IOS::ES::TMDReader GetTMD() const override;
|
||||
u64 PartitionOffsetToRawOffset(u64 offset) const override;
|
||||
std::string GetGameID() const override;
|
||||
std::string GetMakerID() const override;
|
||||
u16 GetRevision() const override;
|
||||
std::string GetInternalName() const override;
|
||||
bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const override;
|
||||
std::vector<Partition> GetPartitions() const override;
|
||||
Partition GetGamePartition() const override;
|
||||
bool GetTitleID(u64* buffer, const Partition& partition) const override;
|
||||
IOS::ES::TicketReader GetTicket(const Partition& partition) const override;
|
||||
IOS::ES::TMDReader GetTMD(const Partition& partition) const override;
|
||||
std::string GetGameID(const Partition& partition) const override;
|
||||
std::string GetMakerID(const Partition& partition) const override;
|
||||
u16 GetRevision(const Partition& partition) const override;
|
||||
std::string GetInternalName(const Partition& partition) const override;
|
||||
std::map<Language, std::string> GetLongNames() const override;
|
||||
std::vector<u32> GetBanner(int* width, int* height) const override;
|
||||
u64 GetFSTSize() const override;
|
||||
std::string GetApploaderDate() const override;
|
||||
u8 GetDiscNumber() const override;
|
||||
u64 GetFSTSize(const Partition& partition) const override;
|
||||
std::string GetApploaderDate(const Partition& partition) const override;
|
||||
u8 GetDiscNumber(const Partition& partition) const override;
|
||||
|
||||
Platform GetVolumeType() const override;
|
||||
bool SupportsIntegrityCheck() const override { return true; }
|
||||
bool CheckIntegrity() const override;
|
||||
bool ChangePartition(u64 offset) override;
|
||||
bool CheckIntegrity(const Partition& partition) const override;
|
||||
|
||||
Region GetRegion() const override;
|
||||
Country GetCountry() const override;
|
||||
Country GetCountry(const Partition& partition) const override;
|
||||
BlobType GetBlobType() const override;
|
||||
u64 GetSize() const override;
|
||||
u64 GetRawSize() const override;
|
||||
|
||||
static u64 PartitionOffsetToRawOffset(u64 offset, const Partition& partition);
|
||||
|
||||
static constexpr unsigned int BLOCK_HEADER_SIZE = 0x0400;
|
||||
static constexpr unsigned int BLOCK_DATA_SIZE = 0x7C00;
|
||||
static constexpr unsigned int BLOCK_TOTAL_SIZE = BLOCK_HEADER_SIZE + BLOCK_DATA_SIZE;
|
||||
|
||||
private:
|
||||
std::unique_ptr<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;
|
||||
u64 m_dataOffset;
|
||||
|
||||
mutable u64 m_LastDecryptedBlockOffset;
|
||||
mutable unsigned char m_LastDecryptedBlock[BLOCK_DATA_SIZE];
|
||||
mutable u64 m_last_decrypted_block;
|
||||
mutable u8 m_last_decrypted_block_data[BLOCK_DATA_SIZE];
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
Reference in New Issue
Block a user