diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 54469fee68..0acbbc24e2 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -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 { diff --git a/Source/Core/Core/HW/DVD/DVDThread.cpp b/Source/Core/Core/HW/DVD/DVDThread.cpp index 3198d120c5..3693bb9fe3 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.cpp +++ b/Source/Core/Core/HW/DVD/DVDThread.cpp @@ -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(); diff --git a/Source/Core/Core/HW/DVD/DVDThread.h b/Source/Core/Core/HW/DVD/DVDThread.h index d9d5d3e604..3a158cad1c 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.h +++ b/Source/Core/Core/HW/DVD/DVDThread.h @@ -42,7 +42,9 @@ void DoState(PointerWrap& p); void SetDisc(std::unique_ptr 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&) diff --git a/Source/Core/DiscIO/DirectoryBlob.cpp b/Source/Core/DiscIO/DirectoryBlob.cpp index 61aac73694..8628c50fc0 100644 --- a/Source/Core/DiscIO/DirectoryBlob.cpp +++ b/Source/Core/DiscIO/DirectoryBlob.cpp @@ -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&& 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& partition_header = m_partition_headers.back(); @@ -555,7 +556,7 @@ void DirectoryBlobReader::SetPartitionHeader(const DirectoryBlobPartition& parti Write32(static_cast(cert_size), 0x8, &partition_header); Write32(static_cast(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(data_size >> 2), 0x18, &partition_header); m_nonpartition_contents.Add(partition_address + TICKET_SIZE, partition_header); diff --git a/Source/Core/DiscIO/DiscExtractor.cpp b/Source/Core/DiscIO/DiscExtractor.cpp index 7494bd5ab7..eaba17b68a 100644 --- a/Source/Core/DiscIO/DiscExtractor.cpp +++ b/Source/Core/DiscIO/DiscExtractor.cpp @@ -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; diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index f1625a9681..2d800da31a 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -58,6 +58,7 @@ public: return temp ? static_cast(*temp) << GetOffsetShift() : std::optional(); } + virtual bool IsEncryptedAndHashed() const { return false; } virtual std::vector GetPartitions() const { return {}; } virtual Partition GetGamePartition() const { return PARTITION_NONE; } virtual std::optional 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()); } diff --git a/Source/Core/DiscIO/VolumeWii.cpp b/Source/Core/DiscIO/VolumeWii.cpp index d4558c0c99..9816b0cb66 100644 --- a/Source/Core/DiscIO/VolumeWii.cpp +++ b/Source/Core/DiscIO/VolumeWii.cpp @@ -33,19 +33,13 @@ namespace DiscIO { -constexpr u64 PARTITION_DATA_OFFSET = 0x20000; - VolumeWii::VolumeWii(std::unique_ptr reader) : m_pReader(std::move(reader)), m_game_partition(PARTITION_NONE), m_last_decrypted_block(UINT64_MAX) { ASSERT(m_pReader); - if (m_pReader->ReadSwapped(0x60) != u32(0)) - { - // No partitions - just read unencrypted data like with a GC disc - return; - } + m_encrypted = m_pReader->ReadSwapped(0x60) == u32(0); for (u32 partition_group = 0; partition_group < 4; ++partition_group) { @@ -118,12 +112,16 @@ VolumeWii::VolumeWii(std::unique_ptr 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>(get_key), Common::Lazy(get_ticket), Common::Lazy(get_tmd), Common::Lazy>(get_file_system), - *partition_type}); + Common::Lazy(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 VolumeWii::GetPartitions() const { std::vector 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]; diff --git a/Source/Core/DiscIO/VolumeWii.h b/Source/Core/DiscIO/VolumeWii.h index a173d17aa7..ede26959f9 100644 --- a/Source/Core/DiscIO/VolumeWii.h +++ b/Source/Core/DiscIO/VolumeWii.h @@ -33,6 +33,7 @@ public: VolumeWii(std::unique_ptr reader); ~VolumeWii(); bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const override; + bool IsEncryptedAndHashed() const override; std::vector GetPartitions() const override; Partition GetGamePartition() const override; std::optional 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 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 ticket; Common::Lazy tmd; Common::Lazy> file_system; + Common::Lazy data_offset; u32 type; }; std::unique_ptr m_pReader; std::map 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]; diff --git a/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp b/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp index f0a84094bb..bfae5b78df 100644 --- a/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp +++ b/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp @@ -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] { diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp index 7107bd3c3c..303b0332ff 100644 --- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp @@ -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"));