diff --git a/Source/Core/DiscIO/CMakeLists.txt b/Source/Core/DiscIO/CMakeLists.txt index f54a048f89..b59435efa0 100644 --- a/Source/Core/DiscIO/CMakeLists.txt +++ b/Source/Core/DiscIO/CMakeLists.txt @@ -3,6 +3,7 @@ set(SRCS CISOBlob.cpp WbfsBlob.cpp CompressedBlob.cpp + DiscExtractor.cpp DiscScrubber.cpp DriveBlob.cpp Enums.cpp diff --git a/Source/Core/DiscIO/DiscExtractor.cpp b/Source/Core/DiscIO/DiscExtractor.cpp new file mode 100644 index 0000000000..b56f120a67 --- /dev/null +++ b/Source/Core/DiscIO/DiscExtractor.cpp @@ -0,0 +1,354 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DiscIO/DiscExtractor.h" + +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/StringUtil.h" +#include "DiscIO/Enums.h" +#include "DiscIO/Filesystem.h" +#include "DiscIO/Volume.h" + +namespace DiscIO +{ +std::string DirectoryNameForPartitionType(u32 partition_type) +{ + switch (partition_type) + { + case 0: + return "DATA"; + case 1: + return "UPDATE"; + case 2: + return "CHANNEL"; + 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::isprint(c, std::locale::classic()); })) + { + return "P-" + type_as_game_id; + } + + return StringFromFormat("P%u", partition_type); + } +} + +u64 ReadFile(const Volume& volume, const Partition& partition, const FileInfo* file_info, + u8* buffer, u64 max_buffer_size, u64 offset_in_file) +{ + if (!file_info || file_info->IsDirectory() || offset_in_file >= file_info->GetSize()) + return 0; + + const u64 read_length = std::min(max_buffer_size, file_info->GetSize() - offset_in_file); + + DEBUG_LOG(DISCIO, "Reading %" PRIx64 " bytes at %" PRIx64 " from file %s. Offset: %" PRIx64 + " Size: %" PRIx32, + read_length, offset_in_file, file_info->GetPath().c_str(), file_info->GetOffset(), + file_info->GetSize()); + + if (!volume.Read(file_info->GetOffset() + offset_in_file, read_length, buffer, partition)) + return 0; + + return read_length; +} + +bool ExportData(const Volume& volume, const Partition& partition, u64 offset, u64 size, + const std::string& export_filename) +{ + File::IOFile f(export_filename, "wb"); + if (!f) + return false; + + while (size) + { + // Limit read size to 128 MB + const size_t read_size = static_cast(std::min(size, 0x08000000)); + + std::vector buffer(read_size); + + if (!volume.Read(offset, read_size, buffer.data(), partition)) + return false; + + if (!f.WriteBytes(buffer.data(), read_size)) + return false; + + size -= read_size; + offset += read_size; + } + + return true; +} + +bool ExportFile(const Volume& volume, const Partition& partition, const FileInfo* file_info, + const std::string& export_filename) +{ + if (!file_info || file_info->IsDirectory()) + return false; + + return ExportData(volume, partition, file_info->GetOffset(), file_info->GetSize(), + export_filename); +} + +void ExportDirectory(const Volume& volume, const Partition partition, const FileInfo& directory, + bool recursive, const std::string& filesystem_path, + const std::string& export_folder, + const std::function& update_progress) +{ + File::CreateFullPath(export_folder + '/'); + + for (const FileInfo& file_info : directory) + { + const std::string name = file_info.GetName() + (file_info.IsDirectory() ? "/" : ""); + const std::string path = filesystem_path + name; + const std::string export_path = export_folder + '/' + name; + + if (update_progress(path)) + return; + + DEBUG_LOG(DISCIO, "%s", export_path.c_str()); + + if (!file_info.IsDirectory()) + { + if (File::Exists(export_path)) + NOTICE_LOG(DISCIO, "%s already exists", export_path.c_str()); + else if (!ExportFile(volume, partition, &file_info, export_path)) + ERROR_LOG(DISCIO, "Could not export %s", export_path.c_str()); + } + else if (recursive) + { + ExportDirectory(volume, partition, file_info, recursive, path, export_path, update_progress); + } + } +} + +bool ExportWiiUnencryptedHeader(const Volume& volume, const std::string& export_filename) +{ + if (volume.GetVolumeType() != Platform::WII_DISC) + return false; + + return ExportData(volume, PARTITION_NONE, 0, 0x100, export_filename); +} + +bool ExportWiiRegionData(const Volume& volume, const std::string& export_filename) +{ + if (volume.GetVolumeType() != Platform::WII_DISC) + return false; + + return ExportData(volume, PARTITION_NONE, 0x4E000, 0x20, export_filename); +} + +bool ExportTicket(const Volume& volume, const Partition& partition, + const std::string& export_filename) +{ + if (volume.GetVolumeType() != Platform::WII_DISC) + return false; + + return ExportData(volume, PARTITION_NONE, partition.offset, 0x2a4, export_filename); +} + +bool ExportTMD(const Volume& volume, const Partition& partition, const std::string& export_filename) +{ + if (volume.GetVolumeType() != Platform::WII_DISC) + return false; + + std::optional size = volume.ReadSwapped(partition.offset + 0x2a4, PARTITION_NONE); + std::optional offset = volume.ReadSwapped(partition.offset + 0x2a8, PARTITION_NONE); + if (!size || !offset) + return false; + + const u64 actual_offset = partition.offset + (static_cast(*offset) << 2); + return ExportData(volume, PARTITION_NONE, actual_offset, *size, export_filename); +} + +bool ExportCertificateChain(const Volume& volume, const Partition& partition, + const std::string& export_filename) +{ + if (volume.GetVolumeType() != Platform::WII_DISC) + return false; + + std::optional size = volume.ReadSwapped(partition.offset + 0x2ac, PARTITION_NONE); + std::optional offset = volume.ReadSwapped(partition.offset + 0x2b0, PARTITION_NONE); + if (!size || !offset) + return false; + + const u64 actual_offset = partition.offset + (static_cast(*offset) << 2); + return ExportData(volume, PARTITION_NONE, actual_offset, *size, export_filename); +} + +bool ExportH3Hashes(const Volume& volume, const Partition& partition, + const std::string& export_filename) +{ + if (volume.GetVolumeType() != Platform::WII_DISC) + return false; + + std::optional offset = volume.ReadSwapped(partition.offset + 0x2b4, PARTITION_NONE); + if (!offset) + return false; + + const u64 actual_offset = partition.offset + (static_cast(*offset) << 2); + return ExportData(volume, PARTITION_NONE, actual_offset, 0x18000, export_filename); +} + +bool ExportHeader(const Volume& volume, const Partition& partition, + const std::string& export_filename) +{ + if (!IsDisc(volume.GetVolumeType())) + return false; + + return ExportData(volume, partition, 0, 0x440, export_filename); +} + +bool ExportBI2Data(const Volume& volume, const Partition& partition, + const std::string& export_filename) +{ + if (!IsDisc(volume.GetVolumeType())) + return false; + + return ExportData(volume, partition, 0x440, 0x2000, export_filename); +} + +bool ExportApploader(const Volume& volume, const Partition& partition, + const std::string& export_filename) +{ + if (!IsDisc(volume.GetVolumeType())) + return false; + + std::optional apploader_size = volume.ReadSwapped(0x2440 + 0x14, partition); + const std::optional trailer_size = volume.ReadSwapped(0x2440 + 0x18, partition); + constexpr u32 header_size = 0x20; + if (!apploader_size || !trailer_size) + return false; + *apploader_size += *trailer_size + header_size; + DEBUG_LOG(DISCIO, "Apploader size -> %x", *apploader_size); + + 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 {}; + + const std::optional offset = volume.ReadSwapped(0x420, partition); + const u8 offset_shift = volume_type == Platform::WII_DISC ? 2 : 0; + return offset ? static_cast(*offset) << offset_shift : std::optional(); +} + +std::optional GetBootDOLSize(const Volume& volume, const Partition& partition, u64 dol_offset) +{ + if (!IsDisc(volume.GetVolumeType())) + return {}; + + 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())) + return false; + + const std::optional dol_offset = GetBootDOLOffset(volume, partition); + if (!dol_offset) + return false; + const std::optional dol_size = GetBootDOLSize(volume, partition, *dol_offset); + if (!dol_size) + return false; + + 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 {}; + + const std::optional offset = volume.ReadSwapped(0x424, partition); + const u8 offset_shift = volume_type == Platform::WII_DISC ? 2 : 0; + return offset ? static_cast(*offset) << offset_shift : std::optional(); +} + +std::optional GetFSTSize(const Volume& volume, const Partition& partition) +{ + const Platform volume_type = volume.GetVolumeType(); + if (!IsDisc(volume_type)) + return {}; + + const std::optional size = volume.ReadSwapped(0x428, partition); + const u8 offset_shift = volume_type == Platform::WII_DISC ? 2 : 0; + return size ? static_cast(*size) << offset_shift : std::optional(); +} + +bool ExportFST(const Volume& volume, const Partition& partition, const std::string& export_filename) +{ + if (!IsDisc(volume.GetVolumeType())) + return false; + + const std::optional fst_offset = GetFSTOffset(volume, partition); + const std::optional fst_size = GetFSTSize(volume, partition); + if (!fst_offset || !fst_size) + return false; + + return ExportData(volume, partition, *fst_offset, *fst_size, export_filename); +} + +bool ExportSystemData(const Volume& volume, const Partition& partition, + const std::string& export_folder) +{ + bool success = true; + + File::CreateFullPath(export_folder + "/sys/"); + success &= ExportHeader(volume, partition, export_folder + "/sys/boot.bin"); + success &= ExportBI2Data(volume, partition, export_folder + "/sys/bi2.bin"); + success &= ExportApploader(volume, partition, export_folder + "/sys/apploader.img"); + success &= ExportDOL(volume, partition, export_folder + "/sys/main.dol"); + success &= ExportFST(volume, partition, export_folder + "/sys/fst.bin"); + + if (volume.GetVolumeType() == Platform::WII_DISC) + { + File::CreateFullPath(export_folder + "/disc/"); + success &= ExportWiiUnencryptedHeader(volume, export_folder + "/disc/header.bin"); + success &= ExportWiiRegionData(volume, export_folder + "/disc/region.bin"); + + 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"); + } + + return success; +} + +} // namespace DiscIO diff --git a/Source/Core/DiscIO/DiscExtractor.h b/Source/Core/DiscIO/DiscExtractor.h new file mode 100644 index 0000000000..b3a1332c4b --- /dev/null +++ b/Source/Core/DiscIO/DiscExtractor.h @@ -0,0 +1,67 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" + +namespace DiscIO +{ +class FileInfo; +struct Partition; +class Volume; + +std::string DirectoryNameForPartitionType(u32 partition_type); + +u64 ReadFile(const Volume& volume, const Partition& partition, const FileInfo* file_info, + u8* buffer, u64 max_buffer_size, u64 offset_in_file = 0); +bool ExportData(const Volume& volume, const Partition& partition, u64 offset, u64 size, + const std::string& export_filename); +bool ExportFile(const Volume& volume, const Partition& partition, const FileInfo* file_info, + const std::string& export_filename); + +// update_progress is called once for each child (file or directory). +// If update_progress returns true, the extraction gets cancelled. +// filesystem_path is supposed to be the path corresponding to the directory argument. +void ExportDirectory(const Volume& volume, const Partition partition, const FileInfo& directory, + bool recursive, const std::string& filesystem_path, + const std::string& export_folder, + const std::function& update_progress); + +// To export everything listed below, you can use ExportSystemData + +bool ExportWiiUnencryptedHeader(const Volume& volume, const std::string& export_filename); +bool ExportWiiRegionData(const Volume& volume, const std::string& export_filename); + +bool ExportTicket(const Volume& volume, const Partition& partition, + const std::string& export_filename); +bool ExportTMD(const Volume& volume, const Partition& partition, + const std::string& export_filename); +bool ExportCertificateChain(const Volume& volume, const Partition& partition, + const std::string& export_filename); +bool ExportH3Hashes(const Volume& volume, const Partition& partition, + const std::string& export_filename); + +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); +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); + +bool ExportSystemData(const Volume& volume, const Partition& partition, + const std::string& export_folder); + +} // namespace DiscIO diff --git a/Source/Core/DiscIO/DiscIO.vcxproj b/Source/Core/DiscIO/DiscIO.vcxproj index 1bc3994345..3a978ad262 100644 --- a/Source/Core/DiscIO/DiscIO.vcxproj +++ b/Source/Core/DiscIO/DiscIO.vcxproj @@ -39,6 +39,7 @@ + @@ -60,6 +61,7 @@ + diff --git a/Source/Core/DiscIO/DiscIO.vcxproj.filters b/Source/Core/DiscIO/DiscIO.vcxproj.filters index f5c3f00959..3c1f85ca10 100644 --- a/Source/Core/DiscIO/DiscIO.vcxproj.filters +++ b/Source/Core/DiscIO/DiscIO.vcxproj.filters @@ -1,6 +1,9 @@  + + {51cdf366-d3fe-464a-9f89-c9f1592a6f1c} + {3873659a-9a30-4a58-af9e-8dad7d7eb627} @@ -75,6 +78,9 @@ Volume\Blob + + DiscExtractor + @@ -134,6 +140,9 @@ Volume\Blob + + DiscExtractor + diff --git a/Source/Core/DiscIO/DiscScrubber.cpp b/Source/Core/DiscIO/DiscScrubber.cpp index 838df208cf..52d907be69 100644 --- a/Source/Core/DiscIO/DiscScrubber.cpp +++ b/Source/Core/DiscIO/DiscScrubber.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "DiscIO/DiscScrubber.h" + #include #include #include @@ -14,7 +16,8 @@ #include "Common/CommonTypes.h" #include "Common/File.h" #include "Common/Logging/Log.h" -#include "DiscIO/DiscScrubber.h" + +#include "DiscIO/DiscExtractor.h" #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" @@ -200,10 +203,10 @@ bool DiscScrubber::ParsePartitionData(const Partition& partition, PartitionHeade 0x2440 + header->apploader_size + header->apploader_trailer_size); // DOL - const std::optional dol_offset = filesystem->GetBootDOLOffset(); + const std::optional dol_offset = GetBootDOLOffset(*m_disc, partition); if (!dol_offset) return false; - const std::optional dol_size = filesystem->GetBootDOLSize(*dol_offset); + const std::optional dol_size = GetBootDOLSize(*m_disc, partition, *dol_offset); if (!dol_size) return false; header->dol_offset = *dol_offset; diff --git a/Source/Core/DiscIO/Enums.cpp b/Source/Core/DiscIO/Enums.cpp index eae90b34ab..94af6c75a9 100644 --- a/Source/Core/DiscIO/Enums.cpp +++ b/Source/Core/DiscIO/Enums.cpp @@ -11,6 +11,16 @@ namespace DiscIO { +bool IsDisc(Platform volume_type) +{ + return volume_type == Platform::GAMECUBE_DISC || volume_type == Platform::WII_DISC; +} + +bool IsWii(Platform volume_type) +{ + return volume_type == Platform::WII_DISC || volume_type == Platform::WII_WAD; +} + bool IsNTSC(Region region) { return region == Region::NTSC_J || region == Region::NTSC_U || region == Region::NTSC_K; diff --git a/Source/Core/DiscIO/Enums.h b/Source/Core/DiscIO/Enums.h index fb604fd3be..cce235c38e 100644 --- a/Source/Core/DiscIO/Enums.h +++ b/Source/Core/DiscIO/Enums.h @@ -68,6 +68,8 @@ enum class Language LANGUAGE_UNKNOWN }; +bool IsDisc(Platform volume_type); +bool IsWii(Platform volume_type); bool IsNTSC(Region region); Country TypicalCountryForRegion(Region region); Region RegionSwitchGC(u8 country_code); diff --git a/Source/Core/DiscIO/FileSystemGCWii.cpp b/Source/Core/DiscIO/FileSystemGCWii.cpp index 615a33888b..bbc89cfc15 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/FileSystemGCWii.cpp @@ -18,6 +18,7 @@ #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" +#include "DiscIO/DiscExtractor.h" #include "DiscIO/FileSystemGCWii.h" #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" @@ -194,13 +195,11 @@ FileSystemGCWii::FileSystemGCWii(const Volume* volume, const Partition& partitio else return; - const std::optional fst_offset_unshifted = m_volume->ReadSwapped(0x424, m_partition); - const std::optional fst_size_unshifted = m_volume->ReadSwapped(0x428, m_partition); - if (!fst_offset_unshifted || !fst_size_unshifted) + const std::optional fst_offset = GetFSTOffset(*m_volume, m_partition); + const std::optional fst_size = GetFSTSize(*m_volume, m_partition); + if (!fst_offset || !fst_size) return; - const u64 fst_offset = static_cast(*fst_offset_unshifted) << m_offset_shift; - const u64 fst_size = static_cast(*fst_size_unshifted) << m_offset_shift; - if (fst_size < FST_ENTRY_SIZE) + if (*fst_size < FST_ENTRY_SIZE) { ERROR_LOG(DISCIO, "File system is too small"); return; @@ -209,7 +208,7 @@ FileSystemGCWii::FileSystemGCWii(const Volume* volume, const Partition& partitio // 128 MiB is more than the total amount of RAM in a Wii. // No file system should use anywhere near that much. static const u32 ARBITRARY_FILE_SYSTEM_SIZE_LIMIT = 128 * 1024 * 1024; - if (fst_size > ARBITRARY_FILE_SYSTEM_SIZE_LIMIT) + if (*fst_size > ARBITRARY_FILE_SYSTEM_SIZE_LIMIT) { // Without this check, Dolphin can crash by trying to allocate too much // memory when loading a disc image with an incorrect FST size. @@ -219,8 +218,8 @@ FileSystemGCWii::FileSystemGCWii(const Volume* volume, const Partition& partitio } // Read the whole FST - m_file_system_table.resize(fst_size); - if (!m_volume->Read(fst_offset, fst_size, m_file_system_table.data(), m_partition)) + m_file_system_table.resize(*fst_size); + if (!m_volume->Read(*fst_offset, *fst_size, m_file_system_table.data(), m_partition)) { ERROR_LOG(DISCIO, "Couldn't read file system table"); return; @@ -234,20 +233,20 @@ FileSystemGCWii::FileSystemGCWii(const Volume* volume, const Partition& partitio return; } - if (FST_ENTRY_SIZE * m_root.GetSize() > fst_size) + if (FST_ENTRY_SIZE * m_root.GetSize() > *fst_size) { ERROR_LOG(DISCIO, "File system has too many entries for its size"); return; } - // If the FST's final byte isn't 0, CFileInfoGCWii::GetName() can read past the end - if (m_file_system_table[fst_size - 1] != 0) + // If the FST's final byte isn't 0, FileInfoGCWii::GetName() can read past the end + if (m_file_system_table[*fst_size - 1] != 0) { ERROR_LOG(DISCIO, "File system does not end with a null byte"); return; } - m_valid = m_root.IsValid(fst_size, m_root); + m_valid = m_root.IsValid(*fst_size, m_root); } FileSystemGCWii::~FileSystemGCWii() = default; @@ -330,148 +329,4 @@ std::unique_ptr FileSystemGCWii::FindFileInfo(u64 disc_offset) const return nullptr; } -u64 FileSystemGCWii::ReadFile(const FileInfo* file_info, u8* buffer, u64 max_buffer_size, - u64 offset_in_file) const -{ - if (!file_info || file_info->IsDirectory()) - return 0; - - if (offset_in_file >= file_info->GetSize()) - return 0; - - u64 read_length = std::min(max_buffer_size, file_info->GetSize() - offset_in_file); - - DEBUG_LOG(DISCIO, "Reading %" PRIx64 " bytes at %" PRIx64 " from file %s. Offset: %" PRIx64 - " Size: %" PRIx32, - read_length, offset_in_file, file_info->GetPath().c_str(), file_info->GetOffset(), - file_info->GetSize()); - - m_volume->Read(file_info->GetOffset() + offset_in_file, read_length, buffer, m_partition); - return read_length; -} - -bool FileSystemGCWii::ExportFile(const FileInfo* file_info, - const std::string& export_filename) const -{ - if (!file_info || file_info->IsDirectory()) - return false; - - u64 remaining_size = file_info->GetSize(); - u64 file_offset = file_info->GetOffset(); - - File::IOFile f(export_filename, "wb"); - if (!f) - return false; - - bool result = true; - - while (remaining_size) - { - // Limit read size to 128 MB - size_t read_size = (size_t)std::min(remaining_size, (u64)0x08000000); - - std::vector buffer(read_size); - - result = m_volume->Read(file_offset, read_size, &buffer[0], m_partition); - - if (!result) - break; - - f.WriteBytes(&buffer[0], read_size); - - remaining_size -= read_size; - file_offset += read_size; - } - - return result; -} - -bool FileSystemGCWii::ExportApploader(const std::string& export_folder) const -{ - std::optional apploader_size = m_volume->ReadSwapped(0x2440 + 0x14, m_partition); - const std::optional trailer_size = m_volume->ReadSwapped(0x2440 + 0x18, m_partition); - constexpr u32 header_size = 0x20; - if (!apploader_size || !trailer_size) - return false; - *apploader_size += *trailer_size + header_size; - DEBUG_LOG(DISCIO, "Apploader size -> %x", *apploader_size); - - std::vector buffer(*apploader_size); - if (m_volume->Read(0x2440, *apploader_size, buffer.data(), m_partition)) - { - std::string export_name(export_folder + "/apploader.img"); - - File::IOFile apploader_file(export_name, "wb"); - if (apploader_file) - { - apploader_file.WriteBytes(buffer.data(), *apploader_size); - return true; - } - } - - return false; -} - -std::optional FileSystemGCWii::GetBootDOLOffset() const -{ - std::optional offset = m_volume->ReadSwapped(0x420, m_partition); - return offset ? static_cast(*offset) << m_offset_shift : std::optional(); -} - -std::optional FileSystemGCWii::GetBootDOLSize(u64 dol_offset) const -{ - u32 dol_size = 0; - - // Iterate through the 7 code segments - for (u8 i = 0; i < 7; i++) - { - const std::optional offset = - m_volume->ReadSwapped(dol_offset + 0x00 + i * 4, m_partition); - const std::optional size = - m_volume->ReadSwapped(dol_offset + 0x90 + i * 4, m_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 = - m_volume->ReadSwapped(dol_offset + 0x1c + i * 4, m_partition); - const std::optional size = - m_volume->ReadSwapped(dol_offset + 0xac + i * 4, m_partition); - if (!offset || !size) - return {}; - dol_size = std::max(*offset + *size, dol_size); - } - - return dol_size; -} - -bool FileSystemGCWii::ExportDOL(const std::string& export_folder) const -{ - std::optional dol_offset = GetBootDOLOffset(); - if (!dol_offset) - return false; - std::optional dol_size = GetBootDOLSize(*dol_offset); - if (!dol_size) - return false; - - std::vector buffer(*dol_size); - if (m_volume->Read(*dol_offset, *dol_size, buffer.data(), m_partition)) - { - std::string export_name(export_folder + "/boot.dol"); - - File::IOFile dol_file(export_name, "wb"); - if (dol_file) - { - dol_file.WriteBytes(&buffer[0], *dol_size); - return true; - } - } - - return false; -} - } // namespace diff --git a/Source/Core/DiscIO/FileSystemGCWii.h b/Source/Core/DiscIO/FileSystemGCWii.h index ee69bfed94..8e393642bf 100644 --- a/Source/Core/DiscIO/FileSystemGCWii.h +++ b/Source/Core/DiscIO/FileSystemGCWii.h @@ -93,14 +93,6 @@ public: std::unique_ptr FindFileInfo(const std::string& path) const override; std::unique_ptr FindFileInfo(u64 disc_offset) const override; - u64 ReadFile(const FileInfo* file_info, u8* buffer, u64 max_buffer_size, - u64 offset_in_file) const override; - bool ExportFile(const FileInfo* file_info, const std::string& export_filename) const override; - bool ExportApploader(const std::string& export_folder) const override; - bool ExportDOL(const std::string& export_folder) const override; - std::optional GetBootDOLOffset() const override; - std::optional GetBootDOLSize(u64 dol_offset) const override; - private: bool m_valid; u32 m_offset_shift; diff --git a/Source/Core/DiscIO/Filesystem.h b/Source/Core/DiscIO/Filesystem.h index 879c6fbbdd..a5695958b7 100644 --- a/Source/Core/DiscIO/Filesystem.h +++ b/Source/Core/DiscIO/Filesystem.h @@ -120,14 +120,6 @@ public: // Returns nullptr if not found virtual std::unique_ptr FindFileInfo(u64 disc_offset) const = 0; - virtual u64 ReadFile(const FileInfo* file_info, u8* buffer, u64 max_buffer_size, - u64 offset_in_file = 0) const = 0; - virtual bool ExportFile(const FileInfo* file_info, const std::string& export_filename) const = 0; - virtual bool ExportApploader(const std::string& export_folder) const = 0; - virtual bool ExportDOL(const std::string& export_folder) const = 0; - virtual std::optional GetBootDOLOffset() const = 0; - virtual std::optional GetBootDOLSize(u64 dol_offset) const = 0; - virtual const Partition GetPartition() const { return m_partition; } protected: const Volume* const m_volume; diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index bbdffaa618..e0978048ab 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -53,6 +53,7 @@ public: } virtual std::vector GetPartitions() const { return {}; } virtual Partition GetGamePartition() const { return PARTITION_NONE; } + virtual std::optional GetPartitionType(const Partition& partition) const { return {}; } std::optional GetTitleID() const { return GetTitleID(GetGamePartition()); } virtual std::optional GetTitleID(const Partition& partition) const { return {}; } virtual const IOS::ES::TicketReader& GetTicket(const Partition& partition) const diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index af4178682f..1a89917812 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -16,7 +16,9 @@ #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" + #include "DiscIO/Blob.h" +#include "DiscIO/DiscExtractor.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" @@ -190,8 +192,8 @@ void VolumeGC::LoadBannerFile() const return; } - if (file_size != - file_system->ReadFile(file_info.get(), reinterpret_cast(&banner_file), file_size)) + if (file_size != ReadFile(*this, PARTITION_NONE, file_info.get(), + reinterpret_cast(&banner_file), file_size)) { WARN_LOG(DISCIO, "Could not read opening.bnr."); return; diff --git a/Source/Core/DiscIO/VolumeWii.cpp b/Source/Core/DiscIO/VolumeWii.cpp index 8ccca04409..93c7133036 100644 --- a/Source/Core/DiscIO/VolumeWii.cpp +++ b/Source/Core/DiscIO/VolumeWii.cpp @@ -24,6 +24,7 @@ #include "Common/Swap.h" #include "DiscIO/Blob.h" +#include "DiscIO/DiscExtractor.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" @@ -66,10 +67,11 @@ VolumeWii::VolumeWii(std::unique_ptr reader) continue; const u64 partition_offset = static_cast(*read_buffer) << 2; - // Check if this is the game partition - const bool is_game_partition = - m_game_partition == PARTITION_NONE && - m_pReader->ReadSwapped(partition_table_offset + (i * 8) + 4) == u32(0); + // Read the partition type + const std::optional partition_type = + m_pReader->ReadSwapped(partition_table_offset + (i * 8) + 4); + if (!partition_type) + continue; // Read ticket std::vector ticket_buffer(sizeof(IOS::ES::Ticket)); @@ -105,10 +107,11 @@ VolumeWii::VolumeWii(std::unique_ptr reader) // We've read everything. Time to store it! (The reason we don't store anything // earlier is because we want to be able to skip adding the partition if an error occurs.) const Partition partition(partition_offset); + m_partition_types[partition] = *partition_type; m_partition_keys[partition] = std::move(aes_context); m_partition_tickets[partition] = std::move(ticket); m_partition_tmds[partition] = std::move(tmd); - if (is_game_partition) + if (m_game_partition == PARTITION_NONE && *partition_type == 0) m_game_partition = partition; } } @@ -184,6 +187,12 @@ Partition VolumeWii::GetGamePartition() const return m_game_partition; } +std::optional VolumeWii::GetPartitionType(const Partition& partition) const +{ + auto it = m_partition_types.find(partition); + return it != m_partition_types.end() ? it->second : std::optional(); +} + std::optional VolumeWii::GetTitleID(const Partition& partition) const { const IOS::ES::TicketReader& ticket = GetTicket(partition); @@ -274,8 +283,8 @@ std::map VolumeWii::GetLongNames() const std::vector opening_bnr(NAMES_TOTAL_BYTES); std::unique_ptr file_info = file_system->FindFileInfo("opening.bnr"); - opening_bnr.resize( - file_system->ReadFile(file_info.get(), opening_bnr.data(), opening_bnr.size(), 0x5C)); + opening_bnr.resize(ReadFile(*this, GetGamePartition(), file_info.get(), opening_bnr.data(), + opening_bnr.size(), 0x5C)); return ReadWiiNames(opening_bnr); } diff --git a/Source/Core/DiscIO/VolumeWii.h b/Source/Core/DiscIO/VolumeWii.h index 38dc5c4bc2..780bdb6dd4 100644 --- a/Source/Core/DiscIO/VolumeWii.h +++ b/Source/Core/DiscIO/VolumeWii.h @@ -34,6 +34,7 @@ public: bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const override; std::vector GetPartitions() const override; Partition GetGamePartition() const override; + std::optional GetPartitionType(const Partition& partition) const override; std::optional GetTitleID(const Partition& partition) const override; const IOS::ES::TicketReader& GetTicket(const Partition& partition) const override; const IOS::ES::TMDReader& GetTMD(const Partition& partition) const override; @@ -64,6 +65,7 @@ public: private: std::unique_ptr m_pReader; + std::map m_partition_types; std::map> m_partition_keys; std::map m_partition_tickets; std::map m_partition_tmds; diff --git a/Source/Core/DolphinWX/ISOFile.cpp b/Source/Core/DolphinWX/ISOFile.cpp index 7a35cdb10a..910295b7b2 100644 --- a/Source/Core/DolphinWX/ISOFile.cpp +++ b/Source/Core/DolphinWX/ISOFile.cpp @@ -259,7 +259,7 @@ bool GameListItem::BannerChanged() if (!m_volume_banner.empty()) return false; - if (m_platform != DiscIO::Platform::WII_DISC && m_platform != DiscIO::Platform::WII_WAD) + if (!DiscIO::IsWii(m_platform)) return false; auto& banner = m_pending.volume_banner; @@ -286,7 +286,7 @@ std::string GameListItem::GetDescription(DiscIO::Language language) const std::string GameListItem::GetDescription() const { - bool wii = m_platform != DiscIO::Platform::GAMECUBE_DISC; + const bool wii = DiscIO::IsWii(m_platform); return GetDescription(SConfig::GetInstance().GetCurrentLanguage(wii)); } @@ -300,7 +300,7 @@ std::string GameListItem::GetName() const if (!m_custom_name.empty()) return m_custom_name; - bool wii = m_platform != DiscIO::Platform::GAMECUBE_DISC; + const bool wii = DiscIO::IsWii(m_platform); std::string name = GetName(SConfig::GetInstance().GetCurrentLanguage(wii)); if (!name.empty()) return name; @@ -356,7 +356,7 @@ std::vector GameListItem::GetLanguages() const const std::string GameListItem::GetWiiFSPath() const { - if (m_platform != DiscIO::Platform::WII_DISC && m_platform != DiscIO::Platform::WII_WAD) + if (!DiscIO::IsWii(m_platform)) return ""; const std::string path = Common::GetTitleDataPath(m_title_id, Common::FROM_CONFIGURED_ROOT); diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp index 93ee3bdb9d..9888036d2a 100644 --- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -25,6 +24,7 @@ #include "Common/CommonPaths.h" #include "Common/FileUtil.h" #include "Common/Logging/Log.h" +#include "DiscIO/DiscExtractor.h" #include "DiscIO/Enums.h" #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" @@ -124,10 +124,9 @@ void FilesystemPanel::BindEvents() m_tree_ctrl->Bind(wxEVT_TREE_ITEM_RIGHT_CLICK, &FilesystemPanel::OnRightClickTree, this); Bind(wxEVT_MENU, &FilesystemPanel::OnExtractFile, this, ID_EXTRACT_FILE); - Bind(wxEVT_MENU, &FilesystemPanel::OnExtractDirectories, this, ID_EXTRACT_ALL); + Bind(wxEVT_MENU, &FilesystemPanel::OnExtractAll, this, ID_EXTRACT_ALL); Bind(wxEVT_MENU, &FilesystemPanel::OnExtractDirectories, this, ID_EXTRACT_DIR); - Bind(wxEVT_MENU, &FilesystemPanel::OnExtractHeaderData, this, ID_EXTRACT_APPLOADER); - Bind(wxEVT_MENU, &FilesystemPanel::OnExtractHeaderData, this, ID_EXTRACT_DOL); + Bind(wxEVT_MENU, &FilesystemPanel::OnExtractSystemData, this, ID_EXTRACT_SYSTEM_DATA); Bind(wxEVT_MENU, &FilesystemPanel::OnCheckPartitionIntegrity, this, ID_CHECK_INTEGRITY); } @@ -190,36 +189,31 @@ void FilesystemPanel::OnRightClickTree(wxTreeEvent& event) wxMenu menu; - const auto selection = m_tree_ctrl->GetSelection(); - const auto first_visible_item = m_tree_ctrl->GetFirstVisibleItem(); + const wxTreeItemId selection = m_tree_ctrl->GetSelection(); + const wxTreeItemId first_visible_item = m_tree_ctrl->GetFirstVisibleItem(); const int image_type = m_tree_ctrl->GetItemImage(selection); + const bool is_parent_of_partitions = m_has_partitions && first_visible_item == selection; - if (image_type == ICON_DISC && first_visible_item != selection) - { - menu.Append(ID_EXTRACT_DIR, _("Extract Partition...")); - } - else if (image_type == ICON_FOLDER) - { - menu.Append(ID_EXTRACT_DIR, _("Extract Directory...")); - } - else if (image_type == ICON_FILE) - { + if (image_type == ICON_FILE) menu.Append(ID_EXTRACT_FILE, _("Extract File...")); - } + else if (!is_parent_of_partitions) + menu.Append(ID_EXTRACT_DIR, _("Extract Files...")); - menu.Append(ID_EXTRACT_ALL, _("Extract All Files...")); - - if (!m_has_partitions || (image_type == ICON_DISC && first_visible_item != selection)) + if (image_type == ICON_DISC) { - menu.AppendSeparator(); - menu.Append(ID_EXTRACT_APPLOADER, _("Extract Apploader...")); - menu.Append(ID_EXTRACT_DOL, _("Extract DOL...")); - } + if (!is_parent_of_partitions) + menu.Append(ID_EXTRACT_SYSTEM_DATA, _("Extract System Data...")); - if (image_type == ICON_DISC && first_visible_item != selection) - { - menu.AppendSeparator(); - menu.Append(ID_CHECK_INTEGRITY, _("Check Partition Integrity")); + if (first_visible_item == selection) + menu.Append(ID_EXTRACT_ALL, _("Extract Entire Disc...")); + else + menu.Append(ID_EXTRACT_ALL, _("Extract Entire Partition...")); + + if (first_visible_item != selection) + { + menu.AppendSeparator(); + menu.Append(ID_CHECK_INTEGRITY, _("Check Partition Integrity")); + } } PopupMenu(&menu); @@ -245,57 +239,80 @@ void FilesystemPanel::OnExtractDirectories(wxCommandEvent& event) const wxString selected_directory_label = m_tree_ctrl->GetItemText(m_tree_ctrl->GetSelection()); const wxString extract_path = wxDirSelector(_("Choose the folder to extract to")); - if (extract_path.empty() || selected_directory_label.empty()) - return; - - switch (event.GetId()) - { - case ID_EXTRACT_ALL: - ExtractAllFiles(extract_path); - break; - case ID_EXTRACT_DIR: + if (!extract_path.empty() && !selected_directory_label.empty()) ExtractSingleDirectory(extract_path); - break; - } } -void FilesystemPanel::OnExtractHeaderData(wxCommandEvent& event) +void FilesystemPanel::OnExtractSystemData(wxCommandEvent& event) { - DiscIO::FileSystem* file_system = nullptr; const wxString path = wxDirSelector(_("Choose the folder to extract to")); if (path.empty()) return; + DiscIO::Partition partition; if (m_has_partitions) { const auto* const selection_data = m_tree_ctrl->GetItemData(m_tree_ctrl->GetSelection()); - const auto* const partition = static_cast(selection_data); + const auto* const wii_partition = static_cast(selection_data); - file_system = partition->filesystem.get(); + partition = wii_partition->filesystem->GetPartition(); } else { - file_system = m_filesystem.get(); + partition = DiscIO::PARTITION_NONE; } - bool ret = false; - if (event.GetId() == ID_EXTRACT_APPLOADER) - { - ret = file_system->ExportApploader(WxStrToStr(path)); - } - else if (event.GetId() == ID_EXTRACT_DOL) - { - ret = file_system->ExportDOL(WxStrToStr(path)); - } - - if (!ret) + if (!DiscIO::ExportSystemData(*m_opened_iso, partition, WxStrToStr(path))) { WxUtils::ShowErrorDialog( wxString::Format(_("Failed to extract to %s!"), WxStrToStr(path).c_str())); } } +void FilesystemPanel::OnExtractAll(wxCommandEvent& event) +{ + const wxString extract_path = wxDirSelector(_("Choose the folder to extract to")); + + if (extract_path.empty()) + return; + + const std::string std_extract_path = WxStrToStr(extract_path); + + const wxTreeItemId selection = m_tree_ctrl->GetSelection(); + const bool first_item_selected = m_tree_ctrl->GetFirstVisibleItem() == selection; + + if (m_has_partitions && first_item_selected) + { + const wxTreeItemId root = m_tree_ctrl->GetRootItem(); + + wxTreeItemIdValue cookie; + wxTreeItemId item = m_tree_ctrl->GetFirstChild(root, cookie); + + while (item.IsOk()) + { + const auto* const partition = static_cast(m_tree_ctrl->GetItemData(item)); + const std::optional partition_type = + *m_opened_iso->GetPartitionType(partition->filesystem->GetPartition()); + if (partition_type) + { + const std::string partition_name = DiscIO::DirectoryNameForPartitionType(*partition_type); + ExtractPartition(std_extract_path + '/' + partition_name, *partition->filesystem); + } + item = m_tree_ctrl->GetNextChild(root, cookie); + } + } + else if (m_has_partitions && !first_item_selected) + { + const auto* const partition = static_cast(m_tree_ctrl->GetItemData(selection)); + ExtractPartition(std_extract_path, *partition->filesystem); + } + else + { + ExtractPartition(std_extract_path, *m_filesystem); + } +} + void FilesystemPanel::OnCheckPartitionIntegrity(wxCommandEvent& WXUNUSED(event)) { // Normally we can't enter this function if we're analyzing a volume that @@ -331,113 +348,24 @@ void FilesystemPanel::OnCheckPartitionIntegrity(wxCommandEvent& WXUNUSED(event)) } } -void FilesystemPanel::ExtractAllFiles(const wxString& output_folder) -{ - if (m_has_partitions) - { - const wxTreeItemId root = m_tree_ctrl->GetRootItem(); - - wxTreeItemIdValue cookie; - wxTreeItemId item = m_tree_ctrl->GetFirstChild(root, cookie); - - while (item.IsOk()) - { - const auto* const partition = static_cast(m_tree_ctrl->GetItemData(item)); - ExtractDirectories("", WxStrToStr(output_folder), *partition->filesystem); - item = m_tree_ctrl->GetNextChild(root, cookie); - } - } - else - { - ExtractDirectories("", WxStrToStr(output_folder), *m_filesystem); - } -} - void FilesystemPanel::ExtractSingleFile(const wxString& output_file_path) const { - wxString selection_file_path = BuildFilePathFromSelection(); - - if (m_has_partitions) - { - const size_t slash_index = selection_file_path.find('/'); - const wxString partition_label = selection_file_path.substr(0, slash_index); - const auto* const partition = FindWiiPartition(m_tree_ctrl, partition_label); - - // Remove "Partition x/" - selection_file_path.erase(0, slash_index + 1); - - partition->filesystem->ExportFile( - partition->filesystem->FindFileInfo(WxStrToStr(selection_file_path)).get(), - WxStrToStr(output_file_path)); - } - else - { - m_filesystem->ExportFile(m_filesystem->FindFileInfo(WxStrToStr(selection_file_path)).get(), - WxStrToStr(output_file_path)); - } + const std::pair path = BuildFilePathFromSelection(); + DiscIO::ExportFile(*m_opened_iso, path.second.GetPartition(), + path.second.FindFileInfo(WxStrToStr(path.first)).get(), + WxStrToStr(output_file_path)); } void FilesystemPanel::ExtractSingleDirectory(const wxString& output_folder) { - wxString directory_path = BuildDirectoryPathFromSelection(); - - if (m_has_partitions) - { - const size_t slash_index = directory_path.find('/'); - const wxString partition_label = directory_path.substr(0, slash_index); - const auto* const partition = FindWiiPartition(m_tree_ctrl, partition_label); - - // Remove "Partition x/" - directory_path.erase(0, slash_index + 1); - - ExtractDirectories(WxStrToStr(directory_path), WxStrToStr(output_folder), - *partition->filesystem); - } - else - { - ExtractDirectories(WxStrToStr(directory_path), WxStrToStr(output_folder), *m_filesystem); - } -} - -static void ExtractDir(const std::string& full_path, const std::string& output_folder, - const DiscIO::FileSystem& file_system, const DiscIO::FileInfo& directory, - const std::function& update_progress) -{ - for (const DiscIO::FileInfo& file_info : directory) - { - const std::string path = full_path + file_info.GetName() + (file_info.IsDirectory() ? "/" : ""); - const std::string output_path = output_folder + DIR_SEP_CHR + path; - - if (update_progress(path)) - return; - - DEBUG_LOG(DISCIO, "%s", output_path.c_str()); - - if (file_info.IsDirectory()) - { - File::CreateFullPath(output_path); - ExtractDir(path, output_folder, file_system, file_info, update_progress); - } - else - { - if (File::Exists(output_path)) - NOTICE_LOG(DISCIO, "%s already exists", output_path.c_str()); - else if (!file_system.ExportFile(&file_info, output_path)) - ERROR_LOG(DISCIO, "Could not export %s", output_path.c_str()); - } - } + const std::pair path = BuildDirectoryPathFromSelection(); + ExtractDirectories(WxStrToStr(path.first), WxStrToStr(output_folder), path.second); } void FilesystemPanel::ExtractDirectories(const std::string& full_path, const std::string& output_folder, const DiscIO::FileSystem& filesystem) { - if (full_path.empty()) // Root - { - filesystem.ExportApploader(output_folder); - filesystem.ExportDOL(output_folder); - } - std::unique_ptr file_info = filesystem.FindFileInfo(full_path); u32 size = file_info->GetTotalChildren(); u32 progress = 0; @@ -447,36 +375,64 @@ void FilesystemPanel::ExtractDirectories(const std::string& full_path, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME | wxPD_SMOOTH); - File::CreateFullPath(output_folder + "/" + full_path); - ExtractDir(full_path, output_folder, filesystem, *file_info, [&](const std::string& path) { - dialog.SetTitle(wxString::Format( - "%s : %d%%", dialog_title.c_str(), - static_cast((static_cast(progress) / static_cast(size)) * 100))); - dialog.Update(progress, wxString::Format(_("Extracting %s"), StrToWxStr(path))); - ++progress; - return dialog.WasCancelled(); - }); + DiscIO::ExportDirectory( + *m_opened_iso, filesystem.GetPartition(), *file_info, true, full_path, output_folder, + [&](const std::string& path) { + dialog.SetTitle(wxString::Format( + "%s : %d%%", dialog_title.c_str(), + static_cast((static_cast(progress) / static_cast(size)) * 100))); + dialog.Update(progress, wxString::Format(_("Extracting %s"), StrToWxStr(path))); + ++progress; + return dialog.WasCancelled(); + }); } -wxString FilesystemPanel::BuildFilePathFromSelection() const +void FilesystemPanel::ExtractPartition(const std::string& output_folder, + const DiscIO::FileSystem& filesystem) { - wxString file_path = m_tree_ctrl->GetItemText(m_tree_ctrl->GetSelection()); + ExtractDirectories("", output_folder + "/files", filesystem); + DiscIO::ExportSystemData(*m_opened_iso, filesystem.GetPartition(), output_folder); +} - const auto root_node = m_tree_ctrl->GetRootItem(); - auto node = m_tree_ctrl->GetItemParent(m_tree_ctrl->GetSelection()); +std::pair FilesystemPanel::BuildFilePathFromSelection() const +{ + const wxTreeItemId root_node = m_tree_ctrl->GetRootItem(); + wxTreeItemId node = m_tree_ctrl->GetSelection(); - while (node != root_node) + wxString file_path; + + if (node != root_node) { - file_path = m_tree_ctrl->GetItemText(node) + DIR_SEP_CHR + file_path; + file_path = m_tree_ctrl->GetItemText(node); node = m_tree_ctrl->GetItemParent(node); + + while (node != root_node) + { + file_path = m_tree_ctrl->GetItemText(node) + DIR_SEP_CHR + file_path; + node = m_tree_ctrl->GetItemParent(node); + } } - return file_path; + if (m_has_partitions) + { + const size_t slash_index = file_path.find('/'); + const wxString partition_label = file_path.substr(0, slash_index); + const auto* const partition = FindWiiPartition(m_tree_ctrl, partition_label); + + // Remove "Partition x/" + file_path.erase(0, slash_index + 1); + + return {file_path, *partition->filesystem}; + } + else + { + return {file_path, *m_filesystem}; + } } -wxString FilesystemPanel::BuildDirectoryPathFromSelection() const +std::pair +FilesystemPanel::BuildDirectoryPathFromSelection() const { - wxString directory_path = BuildFilePathFromSelection(); - directory_path += DIR_SEP_CHR; - return directory_path; + const std::pair result = BuildFilePathFromSelection(); + return {result.first + DIR_SEP_CHR, result.second}; } diff --git a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h index ae898b8fd5..59da51ce95 100644 --- a/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h +++ b/Source/Core/DolphinWX/ISOProperties/FilesystemPanel.h @@ -6,6 +6,7 @@ #include #include +#include #include class GameListItem; @@ -31,8 +32,7 @@ private: ID_EXTRACT_DIR = 20000, ID_EXTRACT_ALL, ID_EXTRACT_FILE, - ID_EXTRACT_APPLOADER, - ID_EXTRACT_DOL, + ID_EXTRACT_SYSTEM_DATA, ID_CHECK_INTEGRITY, }; @@ -44,17 +44,18 @@ private: void OnRightClickTree(wxTreeEvent&); void OnExtractFile(wxCommandEvent&); void OnExtractDirectories(wxCommandEvent&); - void OnExtractHeaderData(wxCommandEvent&); + void OnExtractSystemData(wxCommandEvent&); + void OnExtractAll(wxCommandEvent&); void OnCheckPartitionIntegrity(wxCommandEvent&); - void ExtractAllFiles(const wxString& output_folder); void ExtractSingleFile(const wxString& output_file_path) const; void ExtractSingleDirectory(const wxString& output_folder); void ExtractDirectories(const std::string& full_path, const std::string& output_folder, const DiscIO::FileSystem& filesystem); + void ExtractPartition(const std::string& output_folder, const DiscIO::FileSystem& filesystem); - wxString BuildFilePathFromSelection() const; - wxString BuildDirectoryPathFromSelection() const; + std::pair BuildFilePathFromSelection() const; + std::pair BuildDirectoryPathFromSelection() const; wxTreeCtrl* m_tree_ctrl; diff --git a/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp index b05baa7250..5c81f904a5 100644 --- a/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp +++ b/Source/Core/DolphinWX/ISOProperties/ISOProperties.cpp @@ -430,7 +430,7 @@ void CISOProperties::CreateGUIControls() gecko_layout->Add(m_geckocode_panel, 1, wxEXPAND); gecko_cheat_page->SetSizer(gecko_layout); - if (m_open_iso->GetVolumeType() != DiscIO::Platform::WII_WAD) + if (DiscIO::IsDisc(m_open_iso->GetVolumeType())) { m_Notebook->AddPage(new FilesystemPanel(m_Notebook, ID_FILESYSTEM, m_open_iso), _("Filesystem")); diff --git a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp index 7ac5b27287..36fba6c543 100644 --- a/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp +++ b/Source/Core/DolphinWX/ISOProperties/InfoPanel.cpp @@ -198,7 +198,7 @@ void InfoPanel::LoadBannerDetails() { LoadBannerImage(); - const bool is_wii = m_opened_iso->GetVolumeType() != DiscIO::Platform::GAMECUBE_DISC; + const bool is_wii = DiscIO::IsWii(m_opened_iso->GetVolumeType()); ChangeBannerDetails(SConfig::GetInstance().GetCurrentLanguage(is_wii)); } @@ -311,7 +311,7 @@ wxStaticBoxSizer* InfoPanel::CreateBannerDetailsSizer() wxChoice* InfoPanel::CreateCommentLanguageChoice() { const auto languages = m_game_list_item.GetLanguages(); - const bool is_wii = m_opened_iso->GetVolumeType() != DiscIO::Platform::GAMECUBE_DISC; + const bool is_wii = DiscIO::IsWii(m_opened_iso->GetVolumeType()); const auto preferred_language = SConfig::GetInstance().GetCurrentLanguage(is_wii); const int preferred_language_index = FindPreferredLanguageIndex(preferred_language, languages); const auto choices = GetLanguageChoiceStrings(languages);