From 49ccc77ebb1862a61293a14ee1d1d8b18b7f1fe1 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 9 Mar 2021 20:06:34 +0100 Subject: [PATCH 1/5] DiscIO: Move some code from DiscExtractor to new file DiscUtils --- Source/Core/DiscIO/CMakeLists.txt | 2 + Source/Core/DiscIO/DiscExtractor.cpp | 111 +--------------- Source/Core/DiscIO/DiscExtractor.h | 12 -- Source/Core/DiscIO/DiscScrubber.cpp | 2 +- Source/Core/DiscIO/DiscUtils.cpp | 123 ++++++++++++++++++ Source/Core/DiscIO/DiscUtils.h | 29 +++++ Source/Core/DiscIO/FileSystemGCWii.cpp | 2 +- Source/Core/DiscIO/VolumeDisc.cpp | 2 +- Source/Core/DiscIO/VolumeGC.cpp | 1 + Source/Core/DiscIO/VolumeVerifier.cpp | 2 +- Source/Core/DiscIO/WIABlob.cpp | 2 +- Source/Core/DolphinLib.props | 2 + .../DolphinQt/Config/FilesystemWidget.cpp | 1 + 13 files changed, 167 insertions(+), 124 deletions(-) create mode 100644 Source/Core/DiscIO/DiscUtils.cpp create mode 100644 Source/Core/DiscIO/DiscUtils.h diff --git a/Source/Core/DiscIO/CMakeLists.txt b/Source/Core/DiscIO/CMakeLists.txt index 54e437c521..1729f0d959 100644 --- a/Source/Core/DiscIO/CMakeLists.txt +++ b/Source/Core/DiscIO/CMakeLists.txt @@ -11,6 +11,8 @@ add_library(discio DiscExtractor.h DiscScrubber.cpp DiscScrubber.h + DiscUtils.cpp + DiscUtils.h DriveBlob.cpp DriveBlob.h Enums.cpp diff --git a/Source/Core/DiscIO/DiscExtractor.cpp b/Source/Core/DiscIO/DiscExtractor.cpp index 8aff525cec..5cc665f7d9 100644 --- a/Source/Core/DiscIO/DiscExtractor.cpp +++ b/Source/Core/DiscIO/DiscExtractor.cpp @@ -5,50 +5,21 @@ #include "DiscIO/DiscExtractor.h" #include -#include +#include #include - -#include +#include +#include #include "Common/CommonTypes.h" #include "Common/FileUtil.h" #include "Common/IOFile.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" namespace DiscIO { -std::string NameForPartitionType(u32 partition_type, bool include_prefix) -{ - switch (partition_type) - { - case PARTITION_DATA: - return "DATA"; - case PARTITION_UPDATE: - return "UPDATE"; - case PARTITION_CHANNEL: - return "CHANNEL"; - case PARTITION_INSTALL: - // wit doesn't recognize the name "INSTALL", so we can't use it when naming partition folders - if (!include_prefix) - return "INSTALL"; - [[fallthrough]]; - default: - const std::string type_as_game_id{static_cast((partition_type >> 24) & 0xFF), - static_cast((partition_type >> 16) & 0xFF), - static_cast((partition_type >> 8) & 0xFF), - static_cast(partition_type & 0xFF)}; - if (std::all_of(type_as_game_id.cbegin(), type_as_game_id.cend(), - [](char c) { return std::isalnum(c, std::locale::classic()); })) - { - return include_prefix ? "P-" + type_as_game_id : type_as_game_id; - } - - return fmt::format(include_prefix ? "P{}" : "{}", partition_type); - } -} - u64 ReadFile(const Volume& volume, const Partition& partition, const FileInfo* file_info, u8* buffer, u64 max_buffer_size, u64 offset_in_file) { @@ -248,17 +219,6 @@ bool ExportBI2Data(const Volume& volume, const Partition& partition, return ExportData(volume, partition, 0x440, 0x2000, export_filename); } -std::optional GetApploaderSize(const Volume& volume, const Partition& partition) -{ - constexpr u64 header_size = 0x20; - const std::optional apploader_size = volume.ReadSwapped(0x2440 + 0x14, partition); - const std::optional trailer_size = volume.ReadSwapped(0x2440 + 0x18, partition); - if (!apploader_size || !trailer_size) - return std::nullopt; - - return header_size + *apploader_size + *trailer_size; -} - bool ExportApploader(const Volume& volume, const Partition& partition, const std::string& export_filename) { @@ -272,51 +232,6 @@ bool ExportApploader(const Volume& volume, const Partition& partition, return ExportData(volume, partition, 0x2440, *apploader_size, export_filename); } -std::optional GetBootDOLOffset(const Volume& volume, const Partition& partition) -{ - const Platform volume_type = volume.GetVolumeType(); - if (!IsDisc(volume_type)) - return std::nullopt; - - std::optional dol_offset = volume.ReadSwappedAndShifted(0x420, partition); - - // Datel AR disc has 0x00000000 as the offset (invalid) and doesn't use it in the AppLoader. - if (dol_offset && *dol_offset == 0) - dol_offset.reset(); - - return dol_offset; -} - -std::optional GetBootDOLSize(const Volume& volume, const Partition& partition, u64 dol_offset) -{ - if (!IsDisc(volume.GetVolumeType())) - return std::nullopt; - - u32 dol_size = 0; - - // Iterate through the 7 code segments - for (u8 i = 0; i < 7; i++) - { - const std::optional offset = volume.ReadSwapped(dol_offset + 0x00 + i * 4, partition); - const std::optional size = volume.ReadSwapped(dol_offset + 0x90 + i * 4, partition); - if (!offset || !size) - return {}; - dol_size = std::max(*offset + *size, dol_size); - } - - // Iterate through the 11 data segments - for (u8 i = 0; i < 11; i++) - { - const std::optional offset = volume.ReadSwapped(dol_offset + 0x1c + i * 4, partition); - const std::optional size = volume.ReadSwapped(dol_offset + 0xac + i * 4, partition); - if (!offset || !size) - return {}; - dol_size = std::max(*offset + *size, dol_size); - } - - return dol_size; -} - bool ExportDOL(const Volume& volume, const Partition& partition, const std::string& export_filename) { if (!IsDisc(volume.GetVolumeType())) @@ -332,24 +247,6 @@ bool ExportDOL(const Volume& volume, const Partition& partition, const std::stri return ExportData(volume, partition, *dol_offset, *dol_size, export_filename); } -std::optional GetFSTOffset(const Volume& volume, const Partition& partition) -{ - const Platform volume_type = volume.GetVolumeType(); - if (!IsDisc(volume_type)) - return std::nullopt; - - return volume.ReadSwappedAndShifted(0x424, partition); -} - -std::optional GetFSTSize(const Volume& volume, const Partition& partition) -{ - const Platform volume_type = volume.GetVolumeType(); - if (!IsDisc(volume_type)) - return std::nullopt; - - return volume.ReadSwappedAndShifted(0x428, partition); -} - bool ExportFST(const Volume& volume, const Partition& partition, const std::string& export_filename) { if (!IsDisc(volume.GetVolumeType())) diff --git a/Source/Core/DiscIO/DiscExtractor.h b/Source/Core/DiscIO/DiscExtractor.h index c7e18f83d6..07ca88adf6 100644 --- a/Source/Core/DiscIO/DiscExtractor.h +++ b/Source/Core/DiscIO/DiscExtractor.h @@ -17,13 +17,6 @@ class FileInfo; struct Partition; class Volume; -constexpr u32 PARTITION_DATA = 0; -constexpr u32 PARTITION_UPDATE = 1; -constexpr u32 PARTITION_CHANNEL = 2; // Mario Kart Wii, Wii Fit, Wii Fit Plus, Rabbids Go Home -constexpr u32 PARTITION_INSTALL = 3; // Dragon Quest X only - -std::string NameForPartitionType(u32 partition_type, bool include_prefix); - u64 ReadFile(const Volume& volume, const Partition& partition, const FileInfo* file_info, u8* buffer, u64 max_buffer_size, u64 offset_in_file = 0); u64 ReadFile(const Volume& volume, const Partition& partition, std::string_view path, u8* buffer, @@ -61,15 +54,10 @@ bool ExportHeader(const Volume& volume, const Partition& partition, const std::string& export_filename); bool ExportBI2Data(const Volume& volume, const Partition& partition, const std::string& export_filename); -std::optional GetApploaderSize(const Volume& volume, const Partition& partition); bool ExportApploader(const Volume& volume, const Partition& partition, const std::string& export_filename); -std::optional GetBootDOLOffset(const Volume& volume, const Partition& partition); -std::optional GetBootDOLSize(const Volume& volume, const Partition& partition, u64 dol_offset); bool ExportDOL(const Volume& volume, const Partition& partition, const std::string& export_filename); -std::optional GetFSTOffset(const Volume& volume, const Partition& partition); -std::optional GetFSTSize(const Volume& volume, const Partition& partition); bool ExportFST(const Volume& volume, const Partition& partition, const std::string& export_filename); diff --git a/Source/Core/DiscIO/DiscScrubber.cpp b/Source/Core/DiscIO/DiscScrubber.cpp index bd39e8bb7a..7ed42f870a 100644 --- a/Source/Core/DiscIO/DiscScrubber.cpp +++ b/Source/Core/DiscIO/DiscScrubber.cpp @@ -16,7 +16,7 @@ #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" -#include "DiscIO/DiscExtractor.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" diff --git a/Source/Core/DiscIO/DiscUtils.cpp b/Source/Core/DiscIO/DiscUtils.cpp new file mode 100644 index 0000000000..1919a1d6e5 --- /dev/null +++ b/Source/Core/DiscIO/DiscUtils.cpp @@ -0,0 +1,123 @@ +// Copyright 2021 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DiscIO/DiscUtils.h" + +#include +#include +#include +#include + +#include + +#include "Common/CommonTypes.h" +#include "DiscIO/Volume.h" + +namespace DiscIO +{ +std::string NameForPartitionType(u32 partition_type, bool include_prefix) +{ + switch (partition_type) + { + case PARTITION_DATA: + return "DATA"; + case PARTITION_UPDATE: + return "UPDATE"; + case PARTITION_CHANNEL: + return "CHANNEL"; + case PARTITION_INSTALL: + // wit doesn't recognize the name "INSTALL", so we can't use it when naming partition folders + if (!include_prefix) + return "INSTALL"; + [[fallthrough]]; + default: + const std::string type_as_game_id{static_cast((partition_type >> 24) & 0xFF), + static_cast((partition_type >> 16) & 0xFF), + static_cast((partition_type >> 8) & 0xFF), + static_cast(partition_type & 0xFF)}; + if (std::all_of(type_as_game_id.cbegin(), type_as_game_id.cend(), + [](char c) { return std::isalnum(c, std::locale::classic()); })) + { + return include_prefix ? "P-" + type_as_game_id : type_as_game_id; + } + + return fmt::format(include_prefix ? "P{}" : "{}", partition_type); + } +} + +std::optional GetApploaderSize(const Volume& volume, const Partition& partition) +{ + constexpr u64 header_size = 0x20; + const std::optional apploader_size = volume.ReadSwapped(0x2440 + 0x14, partition); + const std::optional trailer_size = volume.ReadSwapped(0x2440 + 0x18, partition); + if (!apploader_size || !trailer_size) + return std::nullopt; + + return header_size + *apploader_size + *trailer_size; +} + +std::optional GetBootDOLOffset(const Volume& volume, const Partition& partition) +{ + const Platform volume_type = volume.GetVolumeType(); + if (!IsDisc(volume_type)) + return std::nullopt; + + std::optional dol_offset = volume.ReadSwappedAndShifted(0x420, partition); + + // Datel AR disc has 0x00000000 as the offset (invalid) and doesn't use it in the AppLoader. + if (dol_offset && *dol_offset == 0) + dol_offset.reset(); + + return dol_offset; +} + +std::optional GetBootDOLSize(const Volume& volume, const Partition& partition, u64 dol_offset) +{ + if (!IsDisc(volume.GetVolumeType())) + return std::nullopt; + + u32 dol_size = 0; + + // Iterate through the 7 code segments + for (size_t i = 0; i < 7; i++) + { + const std::optional offset = volume.ReadSwapped(dol_offset + 0x00 + i * 4, partition); + const std::optional size = volume.ReadSwapped(dol_offset + 0x90 + i * 4, partition); + if (!offset || !size) + return {}; + dol_size = std::max(*offset + *size, dol_size); + } + + // Iterate through the 11 data segments + for (size_t i = 0; i < 11; i++) + { + const std::optional offset = volume.ReadSwapped(dol_offset + 0x1c + i * 4, partition); + const std::optional size = volume.ReadSwapped(dol_offset + 0xac + i * 4, partition); + if (!offset || !size) + return {}; + dol_size = std::max(*offset + *size, dol_size); + } + + return dol_size; +} + +std::optional GetFSTOffset(const Volume& volume, const Partition& partition) +{ + const Platform volume_type = volume.GetVolumeType(); + if (!IsDisc(volume_type)) + return std::nullopt; + + return volume.ReadSwappedAndShifted(0x424, partition); +} + +std::optional GetFSTSize(const Volume& volume, const Partition& partition) +{ + const Platform volume_type = volume.GetVolumeType(); + if (!IsDisc(volume_type)) + return std::nullopt; + + return volume.ReadSwappedAndShifted(0x428, partition); +} + +} // namespace DiscIO diff --git a/Source/Core/DiscIO/DiscUtils.h b/Source/Core/DiscIO/DiscUtils.h new file mode 100644 index 0000000000..3a86ff8b0c --- /dev/null +++ b/Source/Core/DiscIO/DiscUtils.h @@ -0,0 +1,29 @@ +// Copyright 2021 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" + +namespace DiscIO +{ +struct Partition; +class Volume; + +constexpr u32 PARTITION_DATA = 0; +constexpr u32 PARTITION_UPDATE = 1; +constexpr u32 PARTITION_CHANNEL = 2; // Mario Kart Wii, Wii Fit, Wii Fit Plus, Rabbids Go Home +constexpr u32 PARTITION_INSTALL = 3; // Dragon Quest X only + +std::string NameForPartitionType(u32 partition_type, bool include_prefix); + +std::optional GetApploaderSize(const Volume& volume, const Partition& partition); +std::optional GetBootDOLOffset(const Volume& volume, const Partition& partition); +std::optional GetBootDOLSize(const Volume& volume, const Partition& partition, u64 dol_offset); +std::optional GetFSTOffset(const Volume& volume, const Partition& partition); +std::optional GetFSTSize(const Volume& volume, const Partition& partition); +} diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index c706e809a8..cc48fa8f85 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -19,7 +19,7 @@ #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" -#include "DiscIO/DiscExtractor.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/FileSystemGCWii.h" #include "DiscIO/Filesystem.h" #include "DiscIO/VolumeDisc.h" diff --git a/Source/Core/DiscIO/VolumeDisc.cpp b/Source/Core/DiscIO/VolumeDisc.cpp index 3ccea4b62f..ec23922274 100644 --- a/Source/Core/DiscIO/VolumeDisc.cpp +++ b/Source/Core/DiscIO/VolumeDisc.cpp @@ -12,7 +12,7 @@ #include #include "Common/CommonTypes.h" -#include "DiscIO/DiscExtractor.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index e8bab67771..d47f9c5735 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -23,6 +23,7 @@ #include "DiscIO/Blob.h" #include "DiscIO/DiscExtractor.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/Enums.h" #include "DiscIO/FileSystemGCWii.h" #include "DiscIO/Filesystem.h" diff --git a/Source/Core/DiscIO/VolumeVerifier.cpp b/Source/Core/DiscIO/VolumeVerifier.cpp index 21ac8f5d39..c5fdb0cf91 100644 --- a/Source/Core/DiscIO/VolumeVerifier.cpp +++ b/Source/Core/DiscIO/VolumeVerifier.cpp @@ -41,8 +41,8 @@ #include "Core/IOS/IOS.h" #include "Core/IOS/IOSC.h" #include "DiscIO/Blob.h" -#include "DiscIO/DiscExtractor.h" #include "DiscIO/DiscScrubber.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" diff --git a/Source/Core/DiscIO/WIABlob.cpp b/Source/Core/DiscIO/WIABlob.cpp index 8a76b9bd77..07894fa25a 100644 --- a/Source/Core/DiscIO/WIABlob.cpp +++ b/Source/Core/DiscIO/WIABlob.cpp @@ -30,7 +30,7 @@ #include "Common/Swap.h" #include "DiscIO/Blob.h" -#include "DiscIO/DiscExtractor.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/Filesystem.h" #include "DiscIO/LaggedFibonacciGenerator.h" #include "DiscIO/MultithreadedCompressor.h" diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 638e6734e2..9d89aee8b9 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -417,6 +417,7 @@ + @@ -991,6 +992,7 @@ + diff --git a/Source/Core/DolphinQt/Config/FilesystemWidget.cpp b/Source/Core/DolphinQt/Config/FilesystemWidget.cpp index ddffe76963..09ab45a6c4 100644 --- a/Source/Core/DolphinQt/Config/FilesystemWidget.cpp +++ b/Source/Core/DolphinQt/Config/FilesystemWidget.cpp @@ -18,6 +18,7 @@ #include #include "DiscIO/DiscExtractor.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" From b14bf8273245374e14b1b62a1ba5e26e4cccf42d Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 9 Mar 2021 20:54:59 +0100 Subject: [PATCH 2/5] DiscIO: Move some code from VolumeVerifier to DiscUtils --- Source/Core/DiscIO/DiscUtils.cpp | 80 +++++++++++++++++++++++++++ Source/Core/DiscIO/DiscUtils.h | 13 ++++- Source/Core/DiscIO/VolumeVerifier.cpp | 65 +--------------------- Source/Core/DiscIO/VolumeVerifier.h | 4 -- 4 files changed, 93 insertions(+), 69 deletions(-) diff --git a/Source/Core/DiscIO/DiscUtils.cpp b/Source/Core/DiscIO/DiscUtils.cpp index 1919a1d6e5..f352c5d5c8 100644 --- a/Source/Core/DiscIO/DiscUtils.cpp +++ b/Source/Core/DiscIO/DiscUtils.cpp @@ -8,10 +8,12 @@ #include #include #include +#include #include #include "Common/CommonTypes.h" +#include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" namespace DiscIO @@ -120,4 +122,82 @@ std::optional GetFSTSize(const Volume& volume, const Partition& partition) return volume.ReadSwappedAndShifted(0x428, partition); } +u64 GetBiggestReferencedOffset(const Volume& volume) +{ + std::vector partitions = volume.GetPartitions(); + + // If a partition doesn't seem to contain any valid data, skip it. + // This can happen when certain programs that create WBFS files scrub the entirety of + // the Masterpiece partitions in Super Smash Bros. Brawl without removing them from + // the partition table. https://bugs.dolphin-emu.org/issues/8733 + constexpr u32 WII_MAGIC = 0x5D1C9EA3; + const auto it = + std::remove_if(partitions.begin(), partitions.end(), [&](const Partition& partition) { + return volume.ReadSwapped(0x18, partition) != WII_MAGIC; + }); + partitions.erase(it, partitions.end()); + + if (partitions.empty()) + partitions.push_back(PARTITION_NONE); + + return GetBiggestReferencedOffset(volume, partitions); +} + +static u64 GetBiggestReferencedOffset(const Volume& volume, const FileInfo& file_info) +{ + if (file_info.IsDirectory()) + { + u64 biggest_offset = 0; + for (const FileInfo& f : file_info) + biggest_offset = std::max(biggest_offset, GetBiggestReferencedOffset(volume, f)); + return biggest_offset; + } + else + { + return file_info.GetOffset() + file_info.GetSize(); + } +} + +u64 GetBiggestReferencedOffset(const Volume& volume, const std::vector& partitions) +{ + const u64 disc_header_size = volume.GetVolumeType() == Platform::GameCubeDisc ? 0x460 : 0x50000; + u64 biggest_offset = disc_header_size; + for (const Partition& partition : partitions) + { + if (partition != PARTITION_NONE) + { + const u64 offset = volume.PartitionOffsetToRawOffset(0x440, partition); + biggest_offset = std::max(biggest_offset, offset); + } + + const std::optional dol_offset = GetBootDOLOffset(volume, partition); + if (dol_offset) + { + const std::optional dol_size = GetBootDOLSize(volume, partition, *dol_offset); + if (dol_size) + { + const u64 offset = volume.PartitionOffsetToRawOffset(*dol_offset + *dol_size, partition); + biggest_offset = std::max(biggest_offset, offset); + } + } + + const std::optional fst_offset = GetFSTOffset(volume, partition); + const std::optional fst_size = GetFSTSize(volume, partition); + if (fst_offset && fst_size) + { + const u64 offset = volume.PartitionOffsetToRawOffset(*fst_offset + *fst_size, partition); + biggest_offset = std::max(biggest_offset, offset); + } + + const FileSystem* fs = volume.GetFileSystem(partition); + if (fs) + { + const u64 offset_in_partition = GetBiggestReferencedOffset(volume, fs->GetRoot()); + const u64 offset = volume.PartitionOffsetToRawOffset(offset_in_partition, partition); + biggest_offset = std::max(biggest_offset, offset); + } + } + return biggest_offset; +} + } // namespace DiscIO diff --git a/Source/Core/DiscIO/DiscUtils.h b/Source/Core/DiscIO/DiscUtils.h index 3a86ff8b0c..b2844e7759 100644 --- a/Source/Core/DiscIO/DiscUtils.h +++ b/Source/Core/DiscIO/DiscUtils.h @@ -6,14 +6,22 @@ #include #include +#include #include "Common/CommonTypes.h" namespace DiscIO { +class FileInfo; struct Partition; class Volume; +constexpr u64 MINI_DVD_SIZE = 1459978240; // GameCube +constexpr u64 SL_DVD_SIZE = 4699979776; // Wii retail +constexpr u64 SL_DVD_R_SIZE = 4707319808; // Wii RVT-R +constexpr u64 DL_DVD_SIZE = 8511160320; // Wii retail +constexpr u64 DL_DVD_R_SIZE = 8543666176; // Wii RVT-R + constexpr u32 PARTITION_DATA = 0; constexpr u32 PARTITION_UPDATE = 1; constexpr u32 PARTITION_CHANNEL = 2; // Mario Kart Wii, Wii Fit, Wii Fit Plus, Rabbids Go Home @@ -26,4 +34,7 @@ std::optional GetBootDOLOffset(const Volume& volume, const Partition& parti std::optional GetBootDOLSize(const Volume& volume, const Partition& partition, u64 dol_offset); std::optional GetFSTOffset(const Volume& volume, const Partition& partition); std::optional GetFSTSize(const Volume& volume, const Partition& partition); -} + +u64 GetBiggestReferencedOffset(const Volume& volume); +u64 GetBiggestReferencedOffset(const Volume& volume, const std::vector& partitions); +} // namespace DiscIO diff --git a/Source/Core/DiscIO/VolumeVerifier.cpp b/Source/Core/DiscIO/VolumeVerifier.cpp index c5fdb0cf91..d9ef9b991b 100644 --- a/Source/Core/DiscIO/VolumeVerifier.cpp +++ b/Source/Core/DiscIO/VolumeVerifier.cpp @@ -358,12 +358,6 @@ RedumpVerifier::Result RedumpVerifier::Finish(const Hashes>& has return {Status::Unknown, Common::GetStringT("Unknown disc")}; } -constexpr u64 MINI_DVD_SIZE = 1459978240; // GameCube -constexpr u64 SL_DVD_SIZE = 4699979776; // Wii retail -constexpr u64 SL_DVD_R_SIZE = 4707319808; // Wii RVT-R -constexpr u64 DL_DVD_SIZE = 8511160320; // Wii retail -constexpr u64 DL_DVD_R_SIZE = 8543666176; // Wii RVT-R - constexpr u64 BLOCK_SIZE = 0x20000; VolumeVerifier::VolumeVerifier(const Volume& volume, bool redump_verification, @@ -397,7 +391,7 @@ void VolumeVerifier::Start() const std::vector partitions = CheckPartitions(); if (IsDisc(m_volume.GetVolumeType())) - m_biggest_referenced_offset = GetBiggestReferencedOffset(partitions); + m_biggest_referenced_offset = GetBiggestReferencedOffset(m_volume, partitions); CheckMisc(); @@ -822,63 +816,6 @@ void VolumeVerifier::CheckVolumeSize() } } -u64 VolumeVerifier::GetBiggestReferencedOffset(const std::vector& partitions) const -{ - const u64 disc_header_size = m_volume.GetVolumeType() == Platform::GameCubeDisc ? 0x460 : 0x50000; - u64 biggest_offset = disc_header_size; - for (const Partition& partition : partitions) - { - if (partition != PARTITION_NONE) - { - const u64 offset = m_volume.PartitionOffsetToRawOffset(0x440, partition); - biggest_offset = std::max(biggest_offset, offset); - } - - const std::optional dol_offset = GetBootDOLOffset(m_volume, partition); - if (dol_offset) - { - const std::optional dol_size = GetBootDOLSize(m_volume, partition, *dol_offset); - if (dol_size) - { - const u64 offset = m_volume.PartitionOffsetToRawOffset(*dol_offset + *dol_size, partition); - biggest_offset = std::max(biggest_offset, offset); - } - } - - const std::optional fst_offset = GetFSTOffset(m_volume, partition); - const std::optional fst_size = GetFSTSize(m_volume, partition); - if (fst_offset && fst_size) - { - const u64 offset = m_volume.PartitionOffsetToRawOffset(*fst_offset + *fst_size, partition); - biggest_offset = std::max(biggest_offset, offset); - } - - const FileSystem* fs = m_volume.GetFileSystem(partition); - if (fs) - { - const u64 offset = - m_volume.PartitionOffsetToRawOffset(GetBiggestReferencedOffset(fs->GetRoot()), partition); - biggest_offset = std::max(biggest_offset, offset); - } - } - return biggest_offset; -} - -u64 VolumeVerifier::GetBiggestReferencedOffset(const FileInfo& file_info) const -{ - if (file_info.IsDirectory()) - { - u64 biggest_offset = 0; - for (const FileInfo& f : file_info) - biggest_offset = std::max(biggest_offset, GetBiggestReferencedOffset(f)); - return biggest_offset; - } - else - { - return file_info.GetOffset() + file_info.GetSize(); - } -} - void VolumeVerifier::CheckMisc() { const std::string game_id_unencrypted = m_volume.GetGameID(PARTITION_NONE); diff --git a/Source/Core/DiscIO/VolumeVerifier.h b/Source/Core/DiscIO/VolumeVerifier.h index 7992335d7b..78c4305107 100644 --- a/Source/Core/DiscIO/VolumeVerifier.h +++ b/Source/Core/DiscIO/VolumeVerifier.h @@ -34,8 +34,6 @@ namespace DiscIO { -class FileInfo; - template struct Hashes { @@ -154,8 +152,6 @@ private: bool ShouldHaveMasterpiecePartitions() const; bool ShouldBeDualLayer() const; void CheckVolumeSize(); - u64 GetBiggestReferencedOffset(const std::vector& partitions) const; - u64 GetBiggestReferencedOffset(const FileInfo& file_info) const; void CheckMisc(); void CheckSuperPaperMario(); void SetUpHashing(); From 7d570f1edb063dd2b486eb63cc78f9382b83a354 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 9 Mar 2021 21:06:57 +0100 Subject: [PATCH 3/5] DiscIO: Move magic constants for discs to DiscUtils --- Source/Core/DiscIO/DirectoryBlob.cpp | 5 +++-- Source/Core/DiscIO/DiscUtils.cpp | 3 +-- Source/Core/DiscIO/DiscUtils.h | 3 +++ Source/Core/DiscIO/FileSystemGCWii.cpp | 4 ++-- Source/Core/DiscIO/Volume.cpp | 9 +++------ Source/Core/DiscIO/VolumeVerifier.cpp | 3 +-- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Source/Core/DiscIO/DirectoryBlob.cpp b/Source/Core/DiscIO/DirectoryBlob.cpp index 8840283777..65451a33d1 100644 --- a/Source/Core/DiscIO/DirectoryBlob.cpp +++ b/Source/Core/DiscIO/DirectoryBlob.cpp @@ -28,6 +28,7 @@ #include "Core/Boot/DolReader.h" #include "Core/IOS/ES/Formats.h" #include "DiscIO/Blob.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/VolumeWii.h" #include "DiscIO/WiiEncryptionCache.h" @@ -653,8 +654,8 @@ void DirectoryBlobPartition::SetDiscHeaderAndDiscType(std::optional is_wii } else { - m_is_wii = Common::swap32(&m_disc_header[0x18]) == 0x5d1c9ea3; - const bool is_gc = Common::swap32(&m_disc_header[0x1c]) == 0xc2339f3d; + m_is_wii = Common::swap32(&m_disc_header[0x18]) == WII_DISC_MAGIC; + const bool is_gc = Common::swap32(&m_disc_header[0x1c]) == GAMECUBE_DISC_MAGIC; if (m_is_wii == is_gc) ERROR_LOG_FMT(DISCIO, "Couldn't detect disc type based on {}", boot_bin_path); } diff --git a/Source/Core/DiscIO/DiscUtils.cpp b/Source/Core/DiscIO/DiscUtils.cpp index f352c5d5c8..d0bcd66857 100644 --- a/Source/Core/DiscIO/DiscUtils.cpp +++ b/Source/Core/DiscIO/DiscUtils.cpp @@ -130,10 +130,9 @@ u64 GetBiggestReferencedOffset(const Volume& volume) // This can happen when certain programs that create WBFS files scrub the entirety of // the Masterpiece partitions in Super Smash Bros. Brawl without removing them from // the partition table. https://bugs.dolphin-emu.org/issues/8733 - constexpr u32 WII_MAGIC = 0x5D1C9EA3; const auto it = std::remove_if(partitions.begin(), partitions.end(), [&](const Partition& partition) { - return volume.ReadSwapped(0x18, partition) != WII_MAGIC; + return volume.ReadSwapped(0x18, partition) != WII_DISC_MAGIC; }); partitions.erase(it, partitions.end()); diff --git a/Source/Core/DiscIO/DiscUtils.h b/Source/Core/DiscIO/DiscUtils.h index b2844e7759..91bf3eaece 100644 --- a/Source/Core/DiscIO/DiscUtils.h +++ b/Source/Core/DiscIO/DiscUtils.h @@ -22,6 +22,9 @@ constexpr u64 SL_DVD_R_SIZE = 4707319808; // Wii RVT-R constexpr u64 DL_DVD_SIZE = 8511160320; // Wii retail constexpr u64 DL_DVD_R_SIZE = 8543666176; // Wii RVT-R +constexpr u32 GAMECUBE_DISC_MAGIC = 0xC2339F3D; +constexpr u32 WII_DISC_MAGIC = 0x5D1C9EA3; + constexpr u32 PARTITION_DATA = 0; constexpr u32 PARTITION_UPDATE = 1; constexpr u32 PARTITION_CHANNEL = 2; // Mario Kart Wii, Wii Fit, Wii Fit Plus, Rabbids Go Home diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index cc48fa8f85..5b64931be7 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -232,9 +232,9 @@ FileSystemGCWii::FileSystemGCWii(const VolumeDisc* volume, const Partition& part { u8 offset_shift; // Check if this is a GameCube or Wii disc - if (volume->ReadSwapped(0x18, partition) == u32(0x5D1C9EA3)) + if (volume->ReadSwapped(0x18, partition) == WII_DISC_MAGIC) offset_shift = 2; // Wii file system - else if (volume->ReadSwapped(0x1c, partition) == u32(0xC2339F3D)) + else if (volume->ReadSwapped(0x1c, partition) == GAMECUBE_DISC_MAGIC) offset_shift = 0; // GameCube file system else return; // Invalid partition (maybe someone removed its data but not its partition table entry) diff --git a/Source/Core/DiscIO/Volume.cpp b/Source/Core/DiscIO/Volume.cpp index ab4b0e562b..911a486a4e 100644 --- a/Source/Core/DiscIO/Volume.cpp +++ b/Source/Core/DiscIO/Volume.cpp @@ -20,6 +20,7 @@ #include "Core/IOS/ES/Formats.h" #include "DiscIO/Blob.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/Enums.h" #include "DiscIO/VolumeDisc.h" #include "DiscIO/VolumeGC.h" @@ -87,14 +88,10 @@ std::map Volume::ReadWiiNames(const std::vector static std::unique_ptr CreateDisc(std::unique_ptr& reader) { - // Check for Wii - const std::optional wii_magic = reader->ReadSwapped(0x18); - if (wii_magic == u32(0x5D1C9EA3)) + if (reader->ReadSwapped(0x18) == WII_DISC_MAGIC) return std::make_unique(std::move(reader)); - // Check for GC - const std::optional gc_magic = reader->ReadSwapped(0x1C); - if (gc_magic == u32(0xC2339F3D)) + if (reader->ReadSwapped(0x1C) == GAMECUBE_DISC_MAGIC) return std::make_unique(std::move(reader)); // No known magic words found diff --git a/Source/Core/DiscIO/VolumeVerifier.cpp b/Source/Core/DiscIO/VolumeVerifier.cpp index d9ef9b991b..79c589f5c1 100644 --- a/Source/Core/DiscIO/VolumeVerifier.cpp +++ b/Source/Core/DiscIO/VolumeVerifier.cpp @@ -523,12 +523,11 @@ bool VolumeVerifier::CheckPartition(const Partition& partition) bool invalid_header = false; bool blank_contents = false; std::vector disc_header(0x80); - constexpr u32 WII_MAGIC = 0x5D1C9EA3; if (!m_volume.Read(0, disc_header.size(), disc_header.data(), partition)) { invalid_header = true; } - else if (Common::swap32(disc_header.data() + 0x18) != WII_MAGIC) + else if (Common::swap32(disc_header.data() + 0x18) != WII_DISC_MAGIC) { for (size_t i = 0; i < disc_header.size(); i += 4) { From e2b3aadad7632ce7d6722f6d2db29cfb9ed80f6b Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 9 Mar 2021 22:34:28 +0100 Subject: [PATCH 4/5] DVDInterface: Make the WBFS/CISO hack only affect WBFS/CISO 21c152f added a small hack to DVDInterface to keep WBFS and CISO files working with Nintendo's "Error #001" anti-piracy check. Unfortunately I don't think it's possible to support WBFS and CISO without any kind of hack or heuristic, but what we can do is replace the 21c152f hack (which applies regardless of file format) with a hack that only is active when using WBFS or CISO. This change is similar to 2a5a399, but the disc size is calculated in a different way. --- Source/Core/Core/HW/DVD/DVDInterface.cpp | 52 +++++++++++++++++------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 66ea9a0eb4..dd9a2c0ccf 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -36,6 +36,7 @@ #include "Core/Movie.h" #include "DiscIO/Blob.h" +#include "DiscIO/DiscUtils.h" #include "DiscIO/Enums.h" #include "DiscIO/VolumeDisc.h" #include "DiscIO/VolumeWii.h" @@ -164,6 +165,7 @@ static u8 s_dtk_buffer_length = 0; // TODO: figure out how this affects the reg // Disc drive state static DriveState s_drive_state; static DriveError s_error_code; +static u64 s_disc_end_offset; // Disc drive timing static u64 s_read_buffer_start_time; @@ -425,6 +427,33 @@ void Shutdown() DVDThread::Stop(); } +static u64 GetDiscEndOffset(const DiscIO::VolumeDisc& disc) +{ + u64 size = disc.GetSize(); + + if (disc.IsSizeAccurate()) + { + if (size == DiscIO::MINI_DVD_SIZE) + return DiscIO::MINI_DVD_SIZE; + } + else + { + size = DiscIO::GetBiggestReferencedOffset(disc); + } + + const bool should_be_mini_dvd = + disc.GetVolumeType() == DiscIO::Platform::GameCubeDisc || disc.IsDatelDisc(); + + // We always return standard DVD sizes here, not DVD-R sizes. + // RVT-R (devkit) consoles can't read the extra megabytes there are on RVT-R (DVD-R) discs. + if (should_be_mini_dvd && size <= DiscIO::MINI_DVD_SIZE) + return DiscIO::MINI_DVD_SIZE; + else if (size <= DiscIO::SL_DVD_R_SIZE) + return DiscIO::SL_DVD_SIZE; + else + return DiscIO::DL_DVD_SIZE; +} + void SetDisc(std::unique_ptr disc, std::optional> auto_disc_change_paths = {}) { @@ -433,6 +462,10 @@ void SetDisc(std::unique_ptr disc, if (has_disc) { + s_disc_end_offset = GetDiscEndOffset(*disc); + if (!disc->IsSizeAccurate()) + WARN_LOG_FMT(DVDINTERFACE, "Unknown disc size, guessing {0} bytes", s_disc_end_offset); + const DiscIO::BlobReader& blob = disc->GetBlobReader(); if (!blob.HasFastRandomAccessInBlock() && blob.GetBlockSize() > 0x200000) { @@ -763,20 +796,11 @@ static bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_lengt dvd_length = output_length; } - // Many Wii games intentionally try to read from an offset which is just past the end of a regular - // DVD but just before the end of a DVD-R, displaying "Error #001" and failing to boot if the read - // succeeds (see https://wiibrew.org/wiki//dev/di#0x8D_DVDLowUnencryptedRead for more details). - // It would be nice if we simply could rely on DiscIO for letting us know whether a read is out - // of bounds, but this unfortunately doesn't work when using a disc image format that doesn't - // store the original size of the disc, most notably WBFS. Instead, we have a little hack here: - // reject all non-partition reads that come from IOS that go past the offset 0x50000. IOS only - // allows non-partition reads if they are before 0x50000 or if they are in one of the two small - // areas 0x118240000-0x118240020 and 0x1FB4E0000-0x1FB4E0020 (both of which only are used for - // Error #001 checks), so the only thing we disallow with this hack that actually should be - // allowed is non-partition reads in the 0x118240000-0x118240020 area on dual-layer discs. - // In practice, dual-layer games don't attempt to do non-partition reads in that area. - if (reply_type == ReplyType::IOS && partition == DiscIO::PARTITION_NONE && - dvd_offset + dvd_length > 0x50000) + // Many Wii games intentionally try to read from an offset which is just past the end of a + // regular DVD but just before the end of a DVD-R, displaying "Error #001" and failing to boot + // if the read succeeds, so it's critical that we set the correct error code for such reads. + // See https://wiibrew.org/wiki//dev/di#0x8D_DVDLowUnencryptedRead for details on Error #001. + if (dvd_offset + dvd_length > s_disc_end_offset) { SetDriveError(DriveError::BlockOOB); *interrupt_type = DIInterruptType::DEINT; From e47c9764de5c2e8f273c9dcf52b3730914f05521 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 9 Mar 2021 22:40:59 +0100 Subject: [PATCH 5/5] DVDThread: Set ReadError instead of BlockOOB for failed reads With DVDInterface catching out-of-bounds reads, we can let DVDThread use a more accurate error code for situations like a disk failure. --- Source/Core/Core/HW/DVD/DVDThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/DVD/DVDThread.cpp b/Source/Core/Core/HW/DVD/DVDThread.cpp index 1cd143269b..bd79cc05fe 100644 --- a/Source/Core/Core/HW/DVD/DVDThread.cpp +++ b/Source/Core/Core/HW/DVD/DVDThread.cpp @@ -347,7 +347,7 @@ static void FinishRead(u64 id, s64 cycles_late) PanicAlertFmtT("The disc could not be read (at {0:#x} - {1:#x}).", request.dvd_offset, request.dvd_offset + request.length); - DVDInterface::SetDriveError(DVDInterface::DriveError::BlockOOB); + DVDInterface::SetDriveError(DVDInterface::DriveError::ReadError); interrupt = DVDInterface::DIInterruptType::DEINT; } else