Merge pull request #6947 from JosJuice/unencrypted-wii-disc
Make the support for unencrypted Wii disc images less broken
This commit is contained in:
commit
92ec97f899
|
@ -1142,7 +1142,7 @@ void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition, u
|
|||
// The variable dvd_offset tracks the actual offset on the DVD
|
||||
// that the disc drive starts reading at, which differs in two ways:
|
||||
// It's rounded to a whole ECC block and never uses Wii partition addressing.
|
||||
u64 dvd_offset = DiscIO::VolumeWii::PartitionOffsetToRawOffset(offset, partition);
|
||||
u64 dvd_offset = DVDThread::PartitionOffsetToRawOffset(offset, partition);
|
||||
dvd_offset = Common::AlignDown(dvd_offset, DVD_ECC_BLOCK_SIZE);
|
||||
|
||||
if (SConfig::GetInstance().bFastDiscSpeed)
|
||||
|
@ -1209,7 +1209,9 @@ void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition, u
|
|||
u32 unbuffered_blocks = 0;
|
||||
|
||||
const u32 bytes_per_chunk =
|
||||
partition == DiscIO::PARTITION_NONE ? DVD_ECC_BLOCK_SIZE : DiscIO::VolumeWii::BLOCK_DATA_SIZE;
|
||||
partition != DiscIO::PARTITION_NONE && DVDThread::IsEncryptedAndHashed() ?
|
||||
DiscIO::VolumeWii::BLOCK_DATA_SIZE :
|
||||
DVD_ECC_BLOCK_SIZE;
|
||||
|
||||
do
|
||||
{
|
||||
|
|
|
@ -186,12 +186,24 @@ bool HasDisc()
|
|||
return s_disc != nullptr;
|
||||
}
|
||||
|
||||
bool IsEncryptedAndHashed()
|
||||
{
|
||||
// IsEncryptedAndHashed is thread-safe, so calling WaitUntilIdle isn't necessary.
|
||||
return s_disc->IsEncryptedAndHashed();
|
||||
}
|
||||
|
||||
DiscIO::Platform GetDiscType()
|
||||
{
|
||||
// GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary.
|
||||
return s_disc->GetVolumeType();
|
||||
}
|
||||
|
||||
u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition)
|
||||
{
|
||||
// PartitionOffsetToRawOffset is thread-safe, so calling WaitUntilIdle isn't necessary.
|
||||
return s_disc->PartitionOffsetToRawOffset(offset, partition);
|
||||
}
|
||||
|
||||
IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition)
|
||||
{
|
||||
WaitUntilIdle();
|
||||
|
|
|
@ -42,7 +42,9 @@ void DoState(PointerWrap& p);
|
|||
void SetDisc(std::unique_ptr<DiscIO::Volume> disc);
|
||||
bool HasDisc();
|
||||
|
||||
bool IsEncryptedAndHashed();
|
||||
DiscIO::Platform GetDiscType();
|
||||
u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition);
|
||||
IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition);
|
||||
IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition);
|
||||
// This function returns true and calls SConfig::SetRunningGameMetadata(Volume&, Partition&)
|
||||
|
|
|
@ -54,6 +54,8 @@ enum class PartitionType : u32
|
|||
// 0xFF is an arbitrarily picked value. Note that we can't use 0x00, because that means NTSC-J
|
||||
constexpr u32 INVALID_REGION = 0xFF;
|
||||
|
||||
constexpr u32 PARTITION_DATA_OFFSET = 0x20000;
|
||||
|
||||
constexpr u8 ENTRY_SIZE = 0x0c;
|
||||
constexpr u8 FILE_ENTRY = 0;
|
||||
constexpr u8 DIRECTORY_ENTRY = 1;
|
||||
|
@ -508,8 +510,8 @@ void DirectoryBlobReader::SetPartitions(std::vector<PartitionWithType>&& partiti
|
|||
|
||||
const u64 partition_data_size = partitions[i].partition.GetDataSize();
|
||||
m_partitions.emplace(partition_address, std::move(partitions[i].partition));
|
||||
const u64 unaligned_next_partition_address =
|
||||
VolumeWii::PartitionOffsetToRawOffset(partition_data_size, Partition(partition_address));
|
||||
const u64 unaligned_next_partition_address = VolumeWii::EncryptedPartitionOffsetToRawOffset(
|
||||
partition_data_size, Partition(partition_address), PARTITION_DATA_OFFSET);
|
||||
partition_address = Common::AlignUp(unaligned_next_partition_address, 0x10000ull);
|
||||
}
|
||||
m_data_size = partition_address;
|
||||
|
@ -546,7 +548,6 @@ void DirectoryBlobReader::SetPartitionHeader(const DirectoryBlobPartition& parti
|
|||
partition_root + "h3.bin");
|
||||
|
||||
constexpr u32 PARTITION_HEADER_SIZE = 0x1c;
|
||||
constexpr u32 DATA_OFFSET = 0x20000;
|
||||
const u64 data_size = Common::AlignUp(partition.GetDataSize(), 0x7c00) / 0x7c00 * 0x8000;
|
||||
m_partition_headers.emplace_back(PARTITION_HEADER_SIZE);
|
||||
std::vector<u8>& partition_header = m_partition_headers.back();
|
||||
|
@ -555,7 +556,7 @@ void DirectoryBlobReader::SetPartitionHeader(const DirectoryBlobPartition& parti
|
|||
Write32(static_cast<u32>(cert_size), 0x8, &partition_header);
|
||||
Write32(static_cast<u32>(cert_offset >> 2), 0x0C, &partition_header);
|
||||
Write32(H3_OFFSET >> 2, 0x10, &partition_header);
|
||||
Write32(DATA_OFFSET >> 2, 0x14, &partition_header);
|
||||
Write32(PARTITION_DATA_OFFSET >> 2, 0x14, &partition_header);
|
||||
Write32(static_cast<u32>(data_size >> 2), 0x18, &partition_header);
|
||||
|
||||
m_nonpartition_contents.Add(partition_address + TICKET_SIZE, partition_header);
|
||||
|
|
|
@ -361,7 +361,8 @@ bool ExportSystemData(const Volume& volume, const Partition& partition,
|
|||
success &= ExportTicket(volume, partition, export_folder + "/ticket.bin");
|
||||
success &= ExportTMD(volume, partition, export_folder + "/tmd.bin");
|
||||
success &= ExportCertificateChain(volume, partition, export_folder + "/cert.bin");
|
||||
success &= ExportH3Hashes(volume, partition, export_folder + "/h3.bin");
|
||||
if (volume.IsEncryptedAndHashed())
|
||||
success &= ExportH3Hashes(volume, partition, export_folder + "/h3.bin");
|
||||
}
|
||||
|
||||
return success;
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
return temp ? static_cast<u64>(*temp) << GetOffsetShift() : std::optional<u64>();
|
||||
}
|
||||
|
||||
virtual bool IsEncryptedAndHashed() const { return false; }
|
||||
virtual std::vector<Partition> GetPartitions() const { return {}; }
|
||||
virtual Partition GetGamePartition() const { return PARTITION_NONE; }
|
||||
virtual std::optional<u32> GetPartitionType(const Partition& partition) const { return {}; }
|
||||
|
@ -70,6 +71,10 @@ public:
|
|||
virtual const IOS::ES::TMDReader& GetTMD(const Partition& partition) const { return INVALID_TMD; }
|
||||
// Returns a non-owning pointer. Returns nullptr if the file system couldn't be read.
|
||||
virtual const FileSystem* GetFileSystem(const Partition& partition) const = 0;
|
||||
virtual u64 PartitionOffsetToRawOffset(u64 offset, const Partition& partition) const
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
std::string GetGameID() const { return GetGameID(GetGamePartition()); }
|
||||
virtual std::string GetGameID(const Partition& partition) const = 0;
|
||||
std::string GetMakerID() const { return GetMakerID(GetGamePartition()); }
|
||||
|
|
|
@ -33,19 +33,13 @@
|
|||
|
||||
namespace DiscIO
|
||||
{
|
||||
constexpr u64 PARTITION_DATA_OFFSET = 0x20000;
|
||||
|
||||
VolumeWii::VolumeWii(std::unique_ptr<BlobReader> reader)
|
||||
: m_pReader(std::move(reader)), m_game_partition(PARTITION_NONE),
|
||||
m_last_decrypted_block(UINT64_MAX)
|
||||
{
|
||||
ASSERT(m_pReader);
|
||||
|
||||
if (m_pReader->ReadSwapped<u32>(0x60) != u32(0))
|
||||
{
|
||||
// No partitions - just read unencrypted data like with a GC disc
|
||||
return;
|
||||
}
|
||||
m_encrypted = m_pReader->ReadSwapped<u32>(0x60) == u32(0);
|
||||
|
||||
for (u32 partition_group = 0; partition_group < 4; ++partition_group)
|
||||
{
|
||||
|
@ -118,12 +112,16 @@ VolumeWii::VolumeWii(std::unique_ptr<BlobReader> reader)
|
|||
return file_system->IsValid() ? std::move(file_system) : nullptr;
|
||||
};
|
||||
|
||||
auto get_data_offset = [this, partition]() -> u64 {
|
||||
return ReadSwappedAndShifted(partition.offset + 0x2b8, PARTITION_NONE).value_or(0);
|
||||
};
|
||||
|
||||
m_partitions.emplace(
|
||||
partition, PartitionDetails{Common::Lazy<std::unique_ptr<mbedtls_aes_context>>(get_key),
|
||||
Common::Lazy<IOS::ES::TicketReader>(get_ticket),
|
||||
Common::Lazy<IOS::ES::TMDReader>(get_tmd),
|
||||
Common::Lazy<std::unique_ptr<FileSystem>>(get_file_system),
|
||||
*partition_type});
|
||||
Common::Lazy<u64>(get_data_offset), *partition_type});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,14 +135,21 @@ bool VolumeWii::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, const Partition
|
|||
if (partition == PARTITION_NONE)
|
||||
return m_pReader->Read(_ReadOffset, _Length, _pBuffer);
|
||||
|
||||
if (m_pReader->SupportsReadWiiDecrypted())
|
||||
return m_pReader->ReadWiiDecrypted(_ReadOffset, _Length, _pBuffer, partition.offset);
|
||||
|
||||
// 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.key->get();
|
||||
const PartitionDetails& partition_details = it->second;
|
||||
|
||||
if (!m_encrypted)
|
||||
{
|
||||
return m_pReader->Read(partition.offset + *partition_details.data_offset + _ReadOffset, _Length,
|
||||
_pBuffer);
|
||||
}
|
||||
|
||||
if (m_pReader->SupportsReadWiiDecrypted())
|
||||
return m_pReader->ReadWiiDecrypted(_ReadOffset, _Length, _pBuffer, partition.offset);
|
||||
|
||||
mbedtls_aes_context* aes_context = partition_details.key->get();
|
||||
if (!aes_context)
|
||||
return false;
|
||||
|
||||
|
@ -152,8 +157,8 @@ bool VolumeWii::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, const Partition
|
|||
while (_Length > 0)
|
||||
{
|
||||
// Calculate offsets
|
||||
u64 block_offset_on_disc =
|
||||
partition.offset + PARTITION_DATA_OFFSET + _ReadOffset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE;
|
||||
u64 block_offset_on_disc = partition.offset + *partition_details.data_offset +
|
||||
_ReadOffset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE;
|
||||
u64 data_offset_in_block = _ReadOffset % BLOCK_DATA_SIZE;
|
||||
|
||||
if (m_last_decrypted_block != block_offset_on_disc)
|
||||
|
@ -190,6 +195,11 @@ bool VolumeWii::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, const Partition
|
|||
return true;
|
||||
}
|
||||
|
||||
bool VolumeWii::IsEncryptedAndHashed() const
|
||||
{
|
||||
return m_encrypted;
|
||||
}
|
||||
|
||||
std::vector<Partition> VolumeWii::GetPartitions() const
|
||||
{
|
||||
std::vector<Partition> partitions;
|
||||
|
@ -235,15 +245,29 @@ const FileSystem* VolumeWii::GetFileSystem(const Partition& partition) const
|
|||
return it != m_partitions.end() ? it->second.file_system->get() : nullptr;
|
||||
}
|
||||
|
||||
u64 VolumeWii::PartitionOffsetToRawOffset(u64 offset, const Partition& partition)
|
||||
u64 VolumeWii::EncryptedPartitionOffsetToRawOffset(u64 offset, const Partition& partition,
|
||||
u64 partition_data_offset)
|
||||
{
|
||||
if (partition == PARTITION_NONE)
|
||||
return offset;
|
||||
|
||||
return partition.offset + PARTITION_DATA_OFFSET + (offset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE) +
|
||||
return partition.offset + partition_data_offset + (offset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE) +
|
||||
(offset % BLOCK_DATA_SIZE);
|
||||
}
|
||||
|
||||
u64 VolumeWii::PartitionOffsetToRawOffset(u64 offset, const Partition& partition) const
|
||||
{
|
||||
auto it = m_partitions.find(partition);
|
||||
if (it == m_partitions.end())
|
||||
return offset;
|
||||
const u64 data_offset = *it->second.data_offset;
|
||||
|
||||
if (!m_encrypted)
|
||||
return partition.offset + data_offset + offset;
|
||||
|
||||
return EncryptedPartitionOffsetToRawOffset(offset, partition, data_offset);
|
||||
}
|
||||
|
||||
std::string VolumeWii::GetGameID(const Partition& partition) const
|
||||
{
|
||||
char ID[6];
|
||||
|
@ -357,11 +381,15 @@ u64 VolumeWii::GetRawSize() const
|
|||
|
||||
bool VolumeWii::CheckIntegrity(const Partition& partition) const
|
||||
{
|
||||
if (!m_encrypted)
|
||||
return false;
|
||||
|
||||
// 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.key->get();
|
||||
const PartitionDetails& partition_details = it->second;
|
||||
mbedtls_aes_context* aes_context = partition_details.key->get();
|
||||
if (!aes_context)
|
||||
return false;
|
||||
|
||||
|
@ -373,7 +401,7 @@ bool VolumeWii::CheckIntegrity(const Partition& partition) const
|
|||
u32 nClusters = (u32)(partDataSize / 0x8000);
|
||||
for (u32 clusterID = 0; clusterID < nClusters; ++clusterID)
|
||||
{
|
||||
u64 clusterOff = partition.offset + PARTITION_DATA_OFFSET + (u64)clusterID * 0x8000;
|
||||
u64 clusterOff = partition.offset + *partition_details.data_offset + (u64)clusterID * 0x8000;
|
||||
|
||||
// Read and decrypt the cluster metadata
|
||||
u8 clusterMDCrypted[0x400];
|
||||
|
|
|
@ -33,6 +33,7 @@ public:
|
|||
VolumeWii(std::unique_ptr<BlobReader> reader);
|
||||
~VolumeWii();
|
||||
bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const override;
|
||||
bool IsEncryptedAndHashed() const override;
|
||||
std::vector<Partition> GetPartitions() const override;
|
||||
Partition GetGamePartition() const override;
|
||||
std::optional<u32> GetPartitionType(const Partition& partition) const override;
|
||||
|
@ -40,6 +41,9 @@ public:
|
|||
const IOS::ES::TicketReader& GetTicket(const Partition& partition) const override;
|
||||
const IOS::ES::TMDReader& GetTMD(const Partition& partition) const override;
|
||||
const FileSystem* GetFileSystem(const Partition& partition) const override;
|
||||
static u64 EncryptedPartitionOffsetToRawOffset(u64 offset, const Partition& partition,
|
||||
u64 partition_data_offset);
|
||||
u64 PartitionOffsetToRawOffset(u64 offset, const Partition& partition) const override;
|
||||
std::string GetGameID(const Partition& partition) const override;
|
||||
std::string GetMakerID(const Partition& partition) const override;
|
||||
std::optional<u16> GetRevision(const Partition& partition) const override;
|
||||
|
@ -59,8 +63,6 @@ public:
|
|||
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;
|
||||
|
@ -75,12 +77,14 @@ private:
|
|||
Common::Lazy<IOS::ES::TicketReader> ticket;
|
||||
Common::Lazy<IOS::ES::TMDReader> tmd;
|
||||
Common::Lazy<std::unique_ptr<FileSystem>> file_system;
|
||||
Common::Lazy<u64> data_offset;
|
||||
u32 type;
|
||||
};
|
||||
|
||||
std::unique_ptr<BlobReader> m_pReader;
|
||||
std::map<Partition, PartitionDetails> m_partitions;
|
||||
Partition m_game_partition;
|
||||
bool m_encrypted;
|
||||
|
||||
mutable u64 m_last_decrypted_block;
|
||||
mutable u8 m_last_decrypted_block_data[BLOCK_DATA_SIZE];
|
||||
|
|
|
@ -219,9 +219,12 @@ void FilesystemWidget::ShowContextMenu(const QPoint&)
|
|||
if (!folder.isEmpty())
|
||||
ExtractPartition(partition, folder);
|
||||
});
|
||||
menu->addSeparator();
|
||||
AddAction(menu, tr("Check Partition Integrity"), this,
|
||||
[this, partition] { CheckIntegrity(partition); });
|
||||
if (m_volume->IsEncryptedAndHashed())
|
||||
{
|
||||
menu->addSeparator();
|
||||
AddAction(menu, tr("Check Partition Integrity"), this,
|
||||
[this, partition] { CheckIntegrity(partition); });
|
||||
}
|
||||
break;
|
||||
case EntryType::File:
|
||||
AddAction(menu, tr("Extract File..."), this, [this, partition, path] {
|
||||
|
|
|
@ -204,7 +204,7 @@ void FilesystemPanel::OnRightClickTree(wxTreeEvent& event)
|
|||
else
|
||||
menu.Append(ID_EXTRACT_ALL, _("Extract Entire Partition..."));
|
||||
|
||||
if (first_visible_item != selection)
|
||||
if (first_visible_item != selection && m_opened_iso->IsEncryptedAndHashed())
|
||||
{
|
||||
menu.AppendSeparator();
|
||||
menu.Append(ID_CHECK_INTEGRITY, _("Check Partition Integrity"));
|
||||
|
|
Loading…
Reference in New Issue