Merge pull request #5651 from JosJuice/new-disc-extraction

Use new format for disc extraction
This commit is contained in:
Leo Lam 2017-07-04 13:46:18 +02:00 committed by GitHub
commit e0b94b55b8
20 changed files with 626 additions and 368 deletions

View File

@ -3,6 +3,7 @@ set(SRCS
CISOBlob.cpp CISOBlob.cpp
WbfsBlob.cpp WbfsBlob.cpp
CompressedBlob.cpp CompressedBlob.cpp
DiscExtractor.cpp
DiscScrubber.cpp DiscScrubber.cpp
DriveBlob.cpp DriveBlob.cpp
Enums.cpp Enums.cpp

View File

@ -0,0 +1,354 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DiscIO/DiscExtractor.h"
#include <algorithm>
#include <cinttypes>
#include <locale>
#include <optional>
#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<char>((partition_type >> 24) & 0xFF),
static_cast<char>((partition_type >> 16) & 0xFF),
static_cast<char>((partition_type >> 8) & 0xFF),
static_cast<char>(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<size_t>(std::min<u64>(size, 0x08000000));
std::vector<u8> 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<bool(const std::string& path)>& 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<u32> size = volume.ReadSwapped<u32>(partition.offset + 0x2a4, PARTITION_NONE);
std::optional<u32> offset = volume.ReadSwapped<u32>(partition.offset + 0x2a8, PARTITION_NONE);
if (!size || !offset)
return false;
const u64 actual_offset = partition.offset + (static_cast<u64>(*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<u32> size = volume.ReadSwapped<u32>(partition.offset + 0x2ac, PARTITION_NONE);
std::optional<u32> offset = volume.ReadSwapped<u32>(partition.offset + 0x2b0, PARTITION_NONE);
if (!size || !offset)
return false;
const u64 actual_offset = partition.offset + (static_cast<u64>(*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<u32> offset = volume.ReadSwapped<u32>(partition.offset + 0x2b4, PARTITION_NONE);
if (!offset)
return false;
const u64 actual_offset = partition.offset + (static_cast<u64>(*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<u32> apploader_size = volume.ReadSwapped<u32>(0x2440 + 0x14, partition);
const std::optional<u32> trailer_size = volume.ReadSwapped<u32>(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<u64> GetBootDOLOffset(const Volume& volume, const Partition& partition)
{
const Platform volume_type = volume.GetVolumeType();
if (!IsDisc(volume_type))
return {};
const std::optional<u32> offset = volume.ReadSwapped<u32>(0x420, partition);
const u8 offset_shift = volume_type == Platform::WII_DISC ? 2 : 0;
return offset ? static_cast<u64>(*offset) << offset_shift : std::optional<u64>();
}
std::optional<u32> 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<u32> offset = volume.ReadSwapped<u32>(dol_offset + 0x00 + i * 4, partition);
const std::optional<u32> size = volume.ReadSwapped<u32>(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<u32> offset = volume.ReadSwapped<u32>(dol_offset + 0x1c + i * 4, partition);
const std::optional<u32> size = volume.ReadSwapped<u32>(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<u64> dol_offset = GetBootDOLOffset(volume, partition);
if (!dol_offset)
return false;
const std::optional<u32> dol_size = GetBootDOLSize(volume, partition, *dol_offset);
if (!dol_size)
return false;
return ExportData(volume, partition, *dol_offset, *dol_size, export_filename);
}
std::optional<u64> GetFSTOffset(const Volume& volume, const Partition& partition)
{
const Platform volume_type = volume.GetVolumeType();
if (!IsDisc(volume_type))
return {};
const std::optional<u32> offset = volume.ReadSwapped<u32>(0x424, partition);
const u8 offset_shift = volume_type == Platform::WII_DISC ? 2 : 0;
return offset ? static_cast<u64>(*offset) << offset_shift : std::optional<u64>();
}
std::optional<u64> GetFSTSize(const Volume& volume, const Partition& partition)
{
const Platform volume_type = volume.GetVolumeType();
if (!IsDisc(volume_type))
return {};
const std::optional<u32> size = volume.ReadSwapped<u64>(0x428, partition);
const u8 offset_shift = volume_type == Platform::WII_DISC ? 2 : 0;
return size ? static_cast<u64>(*size) << offset_shift : std::optional<u64>();
}
bool ExportFST(const Volume& volume, const Partition& partition, const std::string& export_filename)
{
if (!IsDisc(volume.GetVolumeType()))
return false;
const std::optional<u64> fst_offset = GetFSTOffset(volume, partition);
const std::optional<u64> 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

View File

@ -0,0 +1,67 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <optional>
#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<bool(const std::string& path)>& 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<u64> GetBootDOLOffset(const Volume& volume, const Partition& partition);
std::optional<u32> 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<u64> GetFSTOffset(const Volume& volume, const Partition& partition);
std::optional<u64> 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

View File

@ -39,6 +39,7 @@
<ClCompile Include="Blob.cpp" /> <ClCompile Include="Blob.cpp" />
<ClCompile Include="CISOBlob.cpp" /> <ClCompile Include="CISOBlob.cpp" />
<ClCompile Include="CompressedBlob.cpp" /> <ClCompile Include="CompressedBlob.cpp" />
<ClCompile Include="DiscExtractor.cpp" />
<ClCompile Include="DiscScrubber.cpp" /> <ClCompile Include="DiscScrubber.cpp" />
<ClCompile Include="DriveBlob.cpp" /> <ClCompile Include="DriveBlob.cpp" />
<ClCompile Include="Enums.cpp" /> <ClCompile Include="Enums.cpp" />
@ -60,6 +61,7 @@
<ClInclude Include="Blob.h" /> <ClInclude Include="Blob.h" />
<ClInclude Include="CISOBlob.h" /> <ClInclude Include="CISOBlob.h" />
<ClInclude Include="CompressedBlob.h" /> <ClInclude Include="CompressedBlob.h" />
<ClInclude Include="DiscExtractor.h" />
<ClInclude Include="DiscScrubber.h" /> <ClInclude Include="DiscScrubber.h" />
<ClInclude Include="DriveBlob.h" /> <ClInclude Include="DriveBlob.h" />
<ClInclude Include="Enums.h" /> <ClInclude Include="Enums.h" />

View File

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<Filter Include="DiscExtractor">
<UniqueIdentifier>{51cdf366-d3fe-464a-9f89-c9f1592a6f1c}</UniqueIdentifier>
</Filter>
<Filter Include="DiscScrubber"> <Filter Include="DiscScrubber">
<UniqueIdentifier>{3873659a-9a30-4a58-af9e-8dad7d7eb627}</UniqueIdentifier> <UniqueIdentifier>{3873659a-9a30-4a58-af9e-8dad7d7eb627}</UniqueIdentifier>
</Filter> </Filter>
@ -75,6 +78,9 @@
<ClCompile Include="TGCBlob.cpp"> <ClCompile Include="TGCBlob.cpp">
<Filter>Volume\Blob</Filter> <Filter>Volume\Blob</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="DiscExtractor.cpp">
<Filter>DiscExtractor</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="DiscScrubber.h"> <ClInclude Include="DiscScrubber.h">
@ -134,6 +140,9 @@
<ClInclude Include="TGCBlob.h"> <ClInclude Include="TGCBlob.h">
<Filter>Volume\Blob</Filter> <Filter>Volume\Blob</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="DiscExtractor.h">
<Filter>DiscExtractor</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Text Include="CMakeLists.txt" /> <Text Include="CMakeLists.txt" />

View File

@ -2,6 +2,8 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "DiscIO/DiscScrubber.h"
#include <algorithm> #include <algorithm>
#include <cinttypes> #include <cinttypes>
#include <cstddef> #include <cstddef>
@ -14,7 +16,8 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/File.h" #include "Common/File.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "DiscIO/DiscScrubber.h"
#include "DiscIO/DiscExtractor.h"
#include "DiscIO/Filesystem.h" #include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h" #include "DiscIO/Volume.h"
@ -200,10 +203,10 @@ bool DiscScrubber::ParsePartitionData(const Partition& partition, PartitionHeade
0x2440 + header->apploader_size + header->apploader_trailer_size); 0x2440 + header->apploader_size + header->apploader_trailer_size);
// DOL // DOL
const std::optional<u64> dol_offset = filesystem->GetBootDOLOffset(); const std::optional<u64> dol_offset = GetBootDOLOffset(*m_disc, partition);
if (!dol_offset) if (!dol_offset)
return false; return false;
const std::optional<u64> dol_size = filesystem->GetBootDOLSize(*dol_offset); const std::optional<u64> dol_size = GetBootDOLSize(*m_disc, partition, *dol_offset);
if (!dol_size) if (!dol_size)
return false; return false;
header->dol_offset = *dol_offset; header->dol_offset = *dol_offset;

View File

@ -11,6 +11,16 @@
namespace DiscIO 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) bool IsNTSC(Region region)
{ {
return region == Region::NTSC_J || region == Region::NTSC_U || region == Region::NTSC_K; return region == Region::NTSC_J || region == Region::NTSC_U || region == Region::NTSC_K;

View File

@ -68,6 +68,8 @@ enum class Language
LANGUAGE_UNKNOWN LANGUAGE_UNKNOWN
}; };
bool IsDisc(Platform volume_type);
bool IsWii(Platform volume_type);
bool IsNTSC(Region region); bool IsNTSC(Region region);
Country TypicalCountryForRegion(Region region); Country TypicalCountryForRegion(Region region);
Region RegionSwitchGC(u8 country_code); Region RegionSwitchGC(u8 country_code);

View File

@ -18,6 +18,7 @@
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/MsgHandler.h" #include "Common/MsgHandler.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "DiscIO/DiscExtractor.h"
#include "DiscIO/FileSystemGCWii.h" #include "DiscIO/FileSystemGCWii.h"
#include "DiscIO/Filesystem.h" #include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h" #include "DiscIO/Volume.h"
@ -194,13 +195,11 @@ FileSystemGCWii::FileSystemGCWii(const Volume* volume, const Partition& partitio
else else
return; return;
const std::optional<u32> fst_offset_unshifted = m_volume->ReadSwapped<u32>(0x424, m_partition); const std::optional<u64> fst_offset = GetFSTOffset(*m_volume, m_partition);
const std::optional<u32> fst_size_unshifted = m_volume->ReadSwapped<u32>(0x428, m_partition); const std::optional<u64> fst_size = GetFSTSize(*m_volume, m_partition);
if (!fst_offset_unshifted || !fst_size_unshifted) if (!fst_offset || !fst_size)
return; return;
const u64 fst_offset = static_cast<u64>(*fst_offset_unshifted) << m_offset_shift; if (*fst_size < FST_ENTRY_SIZE)
const u64 fst_size = static_cast<u64>(*fst_size_unshifted) << m_offset_shift;
if (fst_size < FST_ENTRY_SIZE)
{ {
ERROR_LOG(DISCIO, "File system is too small"); ERROR_LOG(DISCIO, "File system is too small");
return; 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. // 128 MiB is more than the total amount of RAM in a Wii.
// No file system should use anywhere near that much. // No file system should use anywhere near that much.
static const u32 ARBITRARY_FILE_SYSTEM_SIZE_LIMIT = 128 * 1024 * 1024; 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 // Without this check, Dolphin can crash by trying to allocate too much
// memory when loading a disc image with an incorrect FST size. // 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 // Read the whole FST
m_file_system_table.resize(fst_size); m_file_system_table.resize(*fst_size);
if (!m_volume->Read(fst_offset, fst_size, m_file_system_table.data(), m_partition)) if (!m_volume->Read(*fst_offset, *fst_size, m_file_system_table.data(), m_partition))
{ {
ERROR_LOG(DISCIO, "Couldn't read file system table"); ERROR_LOG(DISCIO, "Couldn't read file system table");
return; return;
@ -234,20 +233,20 @@ FileSystemGCWii::FileSystemGCWii(const Volume* volume, const Partition& partitio
return; 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"); ERROR_LOG(DISCIO, "File system has too many entries for its size");
return; return;
} }
// If the FST's final byte isn't 0, CFileInfoGCWii::GetName() can read past the end // 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) if (m_file_system_table[*fst_size - 1] != 0)
{ {
ERROR_LOG(DISCIO, "File system does not end with a null byte"); ERROR_LOG(DISCIO, "File system does not end with a null byte");
return; return;
} }
m_valid = m_root.IsValid(fst_size, m_root); m_valid = m_root.IsValid(*fst_size, m_root);
} }
FileSystemGCWii::~FileSystemGCWii() = default; FileSystemGCWii::~FileSystemGCWii() = default;
@ -330,148 +329,4 @@ std::unique_ptr<FileInfo> FileSystemGCWii::FindFileInfo(u64 disc_offset) const
return nullptr; 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<u8> 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<u32> apploader_size = m_volume->ReadSwapped<u32>(0x2440 + 0x14, m_partition);
const std::optional<u32> trailer_size = m_volume->ReadSwapped<u32>(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<u8> 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<u64> FileSystemGCWii::GetBootDOLOffset() const
{
std::optional<u32> offset = m_volume->ReadSwapped<u32>(0x420, m_partition);
return offset ? static_cast<u64>(*offset) << m_offset_shift : std::optional<u64>();
}
std::optional<u32> 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<u32> offset =
m_volume->ReadSwapped<u32>(dol_offset + 0x00 + i * 4, m_partition);
const std::optional<u32> size =
m_volume->ReadSwapped<u32>(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<u32> offset =
m_volume->ReadSwapped<u32>(dol_offset + 0x1c + i * 4, m_partition);
const std::optional<u32> size =
m_volume->ReadSwapped<u32>(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<u64> dol_offset = GetBootDOLOffset();
if (!dol_offset)
return false;
std::optional<u32> dol_size = GetBootDOLSize(*dol_offset);
if (!dol_size)
return false;
std::vector<u8> 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 } // namespace

View File

@ -93,14 +93,6 @@ public:
std::unique_ptr<FileInfo> FindFileInfo(const std::string& path) const override; std::unique_ptr<FileInfo> FindFileInfo(const std::string& path) const override;
std::unique_ptr<FileInfo> FindFileInfo(u64 disc_offset) const override; std::unique_ptr<FileInfo> 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<u64> GetBootDOLOffset() const override;
std::optional<u32> GetBootDOLSize(u64 dol_offset) const override;
private: private:
bool m_valid; bool m_valid;
u32 m_offset_shift; u32 m_offset_shift;

View File

@ -120,14 +120,6 @@ public:
// Returns nullptr if not found // Returns nullptr if not found
virtual std::unique_ptr<FileInfo> FindFileInfo(u64 disc_offset) const = 0; virtual std::unique_ptr<FileInfo> 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<u64> GetBootDOLOffset() const = 0;
virtual std::optional<u32> GetBootDOLSize(u64 dol_offset) const = 0;
virtual const Partition GetPartition() const { return m_partition; } virtual const Partition GetPartition() const { return m_partition; }
protected: protected:
const Volume* const m_volume; const Volume* const m_volume;

View File

@ -53,6 +53,7 @@ public:
} }
virtual std::vector<Partition> GetPartitions() const { return {}; } virtual std::vector<Partition> GetPartitions() const { return {}; }
virtual Partition GetGamePartition() const { return PARTITION_NONE; } virtual Partition GetGamePartition() const { return PARTITION_NONE; }
virtual std::optional<u32> GetPartitionType(const Partition& partition) const { return {}; }
std::optional<u64> GetTitleID() const { return GetTitleID(GetGamePartition()); } std::optional<u64> GetTitleID() const { return GetTitleID(GetGamePartition()); }
virtual std::optional<u64> GetTitleID(const Partition& partition) const { return {}; } virtual std::optional<u64> GetTitleID(const Partition& partition) const { return {}; }
virtual const IOS::ES::TicketReader& GetTicket(const Partition& partition) const virtual const IOS::ES::TicketReader& GetTicket(const Partition& partition) const

View File

@ -16,7 +16,9 @@
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/MsgHandler.h" #include "Common/MsgHandler.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "DiscIO/Blob.h" #include "DiscIO/Blob.h"
#include "DiscIO/DiscExtractor.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
#include "DiscIO/Filesystem.h" #include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h" #include "DiscIO/Volume.h"
@ -190,8 +192,8 @@ void VolumeGC::LoadBannerFile() const
return; return;
} }
if (file_size != if (file_size != ReadFile(*this, PARTITION_NONE, file_info.get(),
file_system->ReadFile(file_info.get(), reinterpret_cast<u8*>(&banner_file), file_size)) reinterpret_cast<u8*>(&banner_file), file_size))
{ {
WARN_LOG(DISCIO, "Could not read opening.bnr."); WARN_LOG(DISCIO, "Could not read opening.bnr.");
return; return;

View File

@ -24,6 +24,7 @@
#include "Common/Swap.h" #include "Common/Swap.h"
#include "DiscIO/Blob.h" #include "DiscIO/Blob.h"
#include "DiscIO/DiscExtractor.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
#include "DiscIO/Filesystem.h" #include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h" #include "DiscIO/Volume.h"
@ -66,10 +67,11 @@ VolumeWii::VolumeWii(std::unique_ptr<BlobReader> reader)
continue; continue;
const u64 partition_offset = static_cast<u64>(*read_buffer) << 2; const u64 partition_offset = static_cast<u64>(*read_buffer) << 2;
// Check if this is the game partition // Read the partition type
const bool is_game_partition = const std::optional<u32> partition_type =
m_game_partition == PARTITION_NONE && m_pReader->ReadSwapped<u32>(partition_table_offset + (i * 8) + 4);
m_pReader->ReadSwapped<u32>(partition_table_offset + (i * 8) + 4) == u32(0); if (!partition_type)
continue;
// Read ticket // Read ticket
std::vector<u8> ticket_buffer(sizeof(IOS::ES::Ticket)); std::vector<u8> ticket_buffer(sizeof(IOS::ES::Ticket));
@ -105,10 +107,11 @@ VolumeWii::VolumeWii(std::unique_ptr<BlobReader> reader)
// We've read everything. Time to store it! (The reason we don't store anything // 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.) // earlier is because we want to be able to skip adding the partition if an error occurs.)
const Partition partition(partition_offset); const Partition partition(partition_offset);
m_partition_types[partition] = *partition_type;
m_partition_keys[partition] = std::move(aes_context); m_partition_keys[partition] = std::move(aes_context);
m_partition_tickets[partition] = std::move(ticket); m_partition_tickets[partition] = std::move(ticket);
m_partition_tmds[partition] = std::move(tmd); m_partition_tmds[partition] = std::move(tmd);
if (is_game_partition) if (m_game_partition == PARTITION_NONE && *partition_type == 0)
m_game_partition = partition; m_game_partition = partition;
} }
} }
@ -184,6 +187,12 @@ Partition VolumeWii::GetGamePartition() const
return m_game_partition; return m_game_partition;
} }
std::optional<u32> VolumeWii::GetPartitionType(const Partition& partition) const
{
auto it = m_partition_types.find(partition);
return it != m_partition_types.end() ? it->second : std::optional<u32>();
}
std::optional<u64> VolumeWii::GetTitleID(const Partition& partition) const std::optional<u64> VolumeWii::GetTitleID(const Partition& partition) const
{ {
const IOS::ES::TicketReader& ticket = GetTicket(partition); const IOS::ES::TicketReader& ticket = GetTicket(partition);
@ -274,8 +283,8 @@ std::map<Language, std::string> VolumeWii::GetLongNames() const
std::vector<u8> opening_bnr(NAMES_TOTAL_BYTES); std::vector<u8> opening_bnr(NAMES_TOTAL_BYTES);
std::unique_ptr<FileInfo> file_info = file_system->FindFileInfo("opening.bnr"); std::unique_ptr<FileInfo> file_info = file_system->FindFileInfo("opening.bnr");
opening_bnr.resize( opening_bnr.resize(ReadFile(*this, GetGamePartition(), file_info.get(), opening_bnr.data(),
file_system->ReadFile(file_info.get(), opening_bnr.data(), opening_bnr.size(), 0x5C)); opening_bnr.size(), 0x5C));
return ReadWiiNames(opening_bnr); return ReadWiiNames(opening_bnr);
} }

View File

@ -34,6 +34,7 @@ public:
bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const override; bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const override;
std::vector<Partition> GetPartitions() const override; std::vector<Partition> GetPartitions() const override;
Partition GetGamePartition() const override; Partition GetGamePartition() const override;
std::optional<u32> GetPartitionType(const Partition& partition) const override;
std::optional<u64> GetTitleID(const Partition& partition) const override; std::optional<u64> GetTitleID(const Partition& partition) const override;
const IOS::ES::TicketReader& GetTicket(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; const IOS::ES::TMDReader& GetTMD(const Partition& partition) const override;
@ -64,6 +65,7 @@ public:
private: private:
std::unique_ptr<BlobReader> m_pReader; std::unique_ptr<BlobReader> m_pReader;
std::map<Partition, u32> m_partition_types;
std::map<Partition, std::unique_ptr<mbedtls_aes_context>> m_partition_keys; std::map<Partition, std::unique_ptr<mbedtls_aes_context>> m_partition_keys;
std::map<Partition, IOS::ES::TicketReader> m_partition_tickets; std::map<Partition, IOS::ES::TicketReader> m_partition_tickets;
std::map<Partition, IOS::ES::TMDReader> m_partition_tmds; std::map<Partition, IOS::ES::TMDReader> m_partition_tmds;

View File

@ -259,7 +259,7 @@ bool GameListItem::BannerChanged()
if (!m_volume_banner.empty()) if (!m_volume_banner.empty())
return false; return false;
if (m_platform != DiscIO::Platform::WII_DISC && m_platform != DiscIO::Platform::WII_WAD) if (!DiscIO::IsWii(m_platform))
return false; return false;
auto& banner = m_pending.volume_banner; auto& banner = m_pending.volume_banner;
@ -286,7 +286,7 @@ std::string GameListItem::GetDescription(DiscIO::Language language) const
std::string GameListItem::GetDescription() 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)); return GetDescription(SConfig::GetInstance().GetCurrentLanguage(wii));
} }
@ -300,7 +300,7 @@ std::string GameListItem::GetName() const
if (!m_custom_name.empty()) if (!m_custom_name.empty())
return m_custom_name; 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)); std::string name = GetName(SConfig::GetInstance().GetCurrentLanguage(wii));
if (!name.empty()) if (!name.empty())
return name; return name;
@ -356,7 +356,7 @@ std::vector<DiscIO::Language> GameListItem::GetLanguages() const
const std::string GameListItem::GetWiiFSPath() 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 ""; return "";
const std::string path = Common::GetTitleDataPath(m_title_id, Common::FROM_CONFIGURED_ROOT); const std::string path = Common::GetTitleDataPath(m_title_id, Common::FROM_CONFIGURED_ROOT);

View File

@ -6,7 +6,6 @@
#include <array> #include <array>
#include <chrono> #include <chrono>
#include <functional>
#include <future> #include <future>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -25,6 +24,7 @@
#include "Common/CommonPaths.h" #include "Common/CommonPaths.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "DiscIO/DiscExtractor.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
#include "DiscIO/Filesystem.h" #include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h" #include "DiscIO/Volume.h"
@ -124,10 +124,9 @@ void FilesystemPanel::BindEvents()
m_tree_ctrl->Bind(wxEVT_TREE_ITEM_RIGHT_CLICK, &FilesystemPanel::OnRightClickTree, this); 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::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::OnExtractDirectories, this, ID_EXTRACT_DIR);
Bind(wxEVT_MENU, &FilesystemPanel::OnExtractHeaderData, this, ID_EXTRACT_APPLOADER); Bind(wxEVT_MENU, &FilesystemPanel::OnExtractSystemData, this, ID_EXTRACT_SYSTEM_DATA);
Bind(wxEVT_MENU, &FilesystemPanel::OnExtractHeaderData, this, ID_EXTRACT_DOL);
Bind(wxEVT_MENU, &FilesystemPanel::OnCheckPartitionIntegrity, this, ID_CHECK_INTEGRITY); Bind(wxEVT_MENU, &FilesystemPanel::OnCheckPartitionIntegrity, this, ID_CHECK_INTEGRITY);
} }
@ -190,36 +189,31 @@ void FilesystemPanel::OnRightClickTree(wxTreeEvent& event)
wxMenu menu; wxMenu menu;
const auto selection = m_tree_ctrl->GetSelection(); const wxTreeItemId selection = m_tree_ctrl->GetSelection();
const auto first_visible_item = m_tree_ctrl->GetFirstVisibleItem(); const wxTreeItemId first_visible_item = m_tree_ctrl->GetFirstVisibleItem();
const int image_type = m_tree_ctrl->GetItemImage(selection); 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) if (image_type == ICON_FILE)
{
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)
{
menu.Append(ID_EXTRACT_FILE, _("Extract 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 (image_type == ICON_DISC)
if (!m_has_partitions || (image_type == ICON_DISC && first_visible_item != selection))
{ {
menu.AppendSeparator(); if (!is_parent_of_partitions)
menu.Append(ID_EXTRACT_APPLOADER, _("Extract Apploader...")); menu.Append(ID_EXTRACT_SYSTEM_DATA, _("Extract System Data..."));
menu.Append(ID_EXTRACT_DOL, _("Extract DOL..."));
}
if (image_type == ICON_DISC && first_visible_item != selection) if (first_visible_item == selection)
{ menu.Append(ID_EXTRACT_ALL, _("Extract Entire Disc..."));
menu.AppendSeparator(); else
menu.Append(ID_CHECK_INTEGRITY, _("Check Partition Integrity")); 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); 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 selected_directory_label = m_tree_ctrl->GetItemText(m_tree_ctrl->GetSelection());
const wxString extract_path = wxDirSelector(_("Choose the folder to extract to")); const wxString extract_path = wxDirSelector(_("Choose the folder to extract to"));
if (extract_path.empty() || selected_directory_label.empty()) if (!extract_path.empty() && !selected_directory_label.empty())
return;
switch (event.GetId())
{
case ID_EXTRACT_ALL:
ExtractAllFiles(extract_path);
break;
case ID_EXTRACT_DIR:
ExtractSingleDirectory(extract_path); 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")); const wxString path = wxDirSelector(_("Choose the folder to extract to"));
if (path.empty()) if (path.empty())
return; return;
DiscIO::Partition partition;
if (m_has_partitions) if (m_has_partitions)
{ {
const auto* const selection_data = m_tree_ctrl->GetItemData(m_tree_ctrl->GetSelection()); const auto* const selection_data = m_tree_ctrl->GetItemData(m_tree_ctrl->GetSelection());
const auto* const partition = static_cast<const WiiPartition*>(selection_data); const auto* const wii_partition = static_cast<const WiiPartition*>(selection_data);
file_system = partition->filesystem.get(); partition = wii_partition->filesystem->GetPartition();
} }
else else
{ {
file_system = m_filesystem.get(); partition = DiscIO::PARTITION_NONE;
} }
bool ret = false; if (!DiscIO::ExportSystemData(*m_opened_iso, partition, WxStrToStr(path)))
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)
{ {
WxUtils::ShowErrorDialog( WxUtils::ShowErrorDialog(
wxString::Format(_("Failed to extract to %s!"), WxStrToStr(path).c_str())); 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<WiiPartition*>(m_tree_ctrl->GetItemData(item));
const std::optional<u32> 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<WiiPartition*>(m_tree_ctrl->GetItemData(selection));
ExtractPartition(std_extract_path, *partition->filesystem);
}
else
{
ExtractPartition(std_extract_path, *m_filesystem);
}
}
void FilesystemPanel::OnCheckPartitionIntegrity(wxCommandEvent& WXUNUSED(event)) void FilesystemPanel::OnCheckPartitionIntegrity(wxCommandEvent& WXUNUSED(event))
{ {
// Normally we can't enter this function if we're analyzing a volume that // 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<WiiPartition*>(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 void FilesystemPanel::ExtractSingleFile(const wxString& output_file_path) const
{ {
wxString selection_file_path = BuildFilePathFromSelection(); const std::pair<wxString, const DiscIO::FileSystem&> path = BuildFilePathFromSelection();
DiscIO::ExportFile(*m_opened_iso, path.second.GetPartition(),
if (m_has_partitions) path.second.FindFileInfo(WxStrToStr(path.first)).get(),
{ WxStrToStr(output_file_path));
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));
}
} }
void FilesystemPanel::ExtractSingleDirectory(const wxString& output_folder) void FilesystemPanel::ExtractSingleDirectory(const wxString& output_folder)
{ {
wxString directory_path = BuildDirectoryPathFromSelection(); const std::pair<wxString, const DiscIO::FileSystem&> path = BuildDirectoryPathFromSelection();
ExtractDirectories(WxStrToStr(path.first), WxStrToStr(output_folder), path.second);
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<bool(const std::string& path)>& 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());
}
}
} }
void FilesystemPanel::ExtractDirectories(const std::string& full_path, void FilesystemPanel::ExtractDirectories(const std::string& full_path,
const std::string& output_folder, const std::string& output_folder,
const DiscIO::FileSystem& filesystem) const DiscIO::FileSystem& filesystem)
{ {
if (full_path.empty()) // Root
{
filesystem.ExportApploader(output_folder);
filesystem.ExportDOL(output_folder);
}
std::unique_ptr<DiscIO::FileInfo> file_info = filesystem.FindFileInfo(full_path); std::unique_ptr<DiscIO::FileInfo> file_info = filesystem.FindFileInfo(full_path);
u32 size = file_info->GetTotalChildren(); u32 size = file_info->GetTotalChildren();
u32 progress = 0; 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_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME |
wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME | wxPD_SMOOTH); wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME | wxPD_SMOOTH);
File::CreateFullPath(output_folder + "/" + full_path); DiscIO::ExportDirectory(
ExtractDir(full_path, output_folder, filesystem, *file_info, [&](const std::string& path) { *m_opened_iso, filesystem.GetPartition(), *file_info, true, full_path, output_folder,
dialog.SetTitle(wxString::Format( [&](const std::string& path) {
"%s : %d%%", dialog_title.c_str(), dialog.SetTitle(wxString::Format(
static_cast<u32>((static_cast<float>(progress) / static_cast<float>(size)) * 100))); "%s : %d%%", dialog_title.c_str(),
dialog.Update(progress, wxString::Format(_("Extracting %s"), StrToWxStr(path))); static_cast<u32>((static_cast<float>(progress) / static_cast<float>(size)) * 100)));
++progress; dialog.Update(progress, wxString::Format(_("Extracting %s"), StrToWxStr(path)));
return dialog.WasCancelled(); ++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(); std::pair<wxString, const DiscIO::FileSystem&> FilesystemPanel::BuildFilePathFromSelection() const
auto node = m_tree_ctrl->GetItemParent(m_tree_ctrl->GetSelection()); {
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); 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<wxString, const DiscIO::FileSystem&>
FilesystemPanel::BuildDirectoryPathFromSelection() const
{ {
wxString directory_path = BuildFilePathFromSelection(); const std::pair<wxString, const DiscIO::FileSystem&> result = BuildFilePathFromSelection();
directory_path += DIR_SEP_CHR; return {result.first + DIR_SEP_CHR, result.second};
return directory_path;
} }

View File

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility>
#include <wx/panel.h> #include <wx/panel.h>
class GameListItem; class GameListItem;
@ -31,8 +32,7 @@ private:
ID_EXTRACT_DIR = 20000, ID_EXTRACT_DIR = 20000,
ID_EXTRACT_ALL, ID_EXTRACT_ALL,
ID_EXTRACT_FILE, ID_EXTRACT_FILE,
ID_EXTRACT_APPLOADER, ID_EXTRACT_SYSTEM_DATA,
ID_EXTRACT_DOL,
ID_CHECK_INTEGRITY, ID_CHECK_INTEGRITY,
}; };
@ -44,17 +44,18 @@ private:
void OnRightClickTree(wxTreeEvent&); void OnRightClickTree(wxTreeEvent&);
void OnExtractFile(wxCommandEvent&); void OnExtractFile(wxCommandEvent&);
void OnExtractDirectories(wxCommandEvent&); void OnExtractDirectories(wxCommandEvent&);
void OnExtractHeaderData(wxCommandEvent&); void OnExtractSystemData(wxCommandEvent&);
void OnExtractAll(wxCommandEvent&);
void OnCheckPartitionIntegrity(wxCommandEvent&); void OnCheckPartitionIntegrity(wxCommandEvent&);
void ExtractAllFiles(const wxString& output_folder);
void ExtractSingleFile(const wxString& output_file_path) const; void ExtractSingleFile(const wxString& output_file_path) const;
void ExtractSingleDirectory(const wxString& output_folder); void ExtractSingleDirectory(const wxString& output_folder);
void ExtractDirectories(const std::string& full_path, const std::string& output_folder, void ExtractDirectories(const std::string& full_path, const std::string& output_folder,
const DiscIO::FileSystem& filesystem); const DiscIO::FileSystem& filesystem);
void ExtractPartition(const std::string& output_folder, const DiscIO::FileSystem& filesystem);
wxString BuildFilePathFromSelection() const; std::pair<wxString, const DiscIO::FileSystem&> BuildFilePathFromSelection() const;
wxString BuildDirectoryPathFromSelection() const; std::pair<wxString, const DiscIO::FileSystem&> BuildDirectoryPathFromSelection() const;
wxTreeCtrl* m_tree_ctrl; wxTreeCtrl* m_tree_ctrl;

View File

@ -430,7 +430,7 @@ void CISOProperties::CreateGUIControls()
gecko_layout->Add(m_geckocode_panel, 1, wxEXPAND); gecko_layout->Add(m_geckocode_panel, 1, wxEXPAND);
gecko_cheat_page->SetSizer(gecko_layout); 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), m_Notebook->AddPage(new FilesystemPanel(m_Notebook, ID_FILESYSTEM, m_open_iso),
_("Filesystem")); _("Filesystem"));

View File

@ -198,7 +198,7 @@ void InfoPanel::LoadBannerDetails()
{ {
LoadBannerImage(); 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)); ChangeBannerDetails(SConfig::GetInstance().GetCurrentLanguage(is_wii));
} }
@ -311,7 +311,7 @@ wxStaticBoxSizer* InfoPanel::CreateBannerDetailsSizer()
wxChoice* InfoPanel::CreateCommentLanguageChoice() wxChoice* InfoPanel::CreateCommentLanguageChoice()
{ {
const auto languages = m_game_list_item.GetLanguages(); 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 auto preferred_language = SConfig::GetInstance().GetCurrentLanguage(is_wii);
const int preferred_language_index = FindPreferredLanguageIndex(preferred_language, languages); const int preferred_language_index = FindPreferredLanguageIndex(preferred_language, languages);
const auto choices = GetLanguageChoiceStrings(languages); const auto choices = GetLanguageChoiceStrings(languages);