Merge pull request #5651 from JosJuice/new-disc-extraction
Use new format for disc extraction
This commit is contained in:
commit
e0b94b55b8
|
@ -3,6 +3,7 @@ set(SRCS
|
|||
CISOBlob.cpp
|
||||
WbfsBlob.cpp
|
||||
CompressedBlob.cpp
|
||||
DiscExtractor.cpp
|
||||
DiscScrubber.cpp
|
||||
DriveBlob.cpp
|
||||
Enums.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 <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
|
|
@ -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
|
|
@ -39,6 +39,7 @@
|
|||
<ClCompile Include="Blob.cpp" />
|
||||
<ClCompile Include="CISOBlob.cpp" />
|
||||
<ClCompile Include="CompressedBlob.cpp" />
|
||||
<ClCompile Include="DiscExtractor.cpp" />
|
||||
<ClCompile Include="DiscScrubber.cpp" />
|
||||
<ClCompile Include="DriveBlob.cpp" />
|
||||
<ClCompile Include="Enums.cpp" />
|
||||
|
@ -60,6 +61,7 @@
|
|||
<ClInclude Include="Blob.h" />
|
||||
<ClInclude Include="CISOBlob.h" />
|
||||
<ClInclude Include="CompressedBlob.h" />
|
||||
<ClInclude Include="DiscExtractor.h" />
|
||||
<ClInclude Include="DiscScrubber.h" />
|
||||
<ClInclude Include="DriveBlob.h" />
|
||||
<ClInclude Include="Enums.h" />
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="DiscExtractor">
|
||||
<UniqueIdentifier>{51cdf366-d3fe-464a-9f89-c9f1592a6f1c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="DiscScrubber">
|
||||
<UniqueIdentifier>{3873659a-9a30-4a58-af9e-8dad7d7eb627}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
@ -75,6 +78,9 @@
|
|||
<ClCompile Include="TGCBlob.cpp">
|
||||
<Filter>Volume\Blob</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DiscExtractor.cpp">
|
||||
<Filter>DiscExtractor</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DiscScrubber.h">
|
||||
|
@ -134,6 +140,9 @@
|
|||
<ClInclude Include="TGCBlob.h">
|
||||
<Filter>Volume\Blob</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DiscExtractor.h">
|
||||
<Filter>DiscExtractor</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "DiscIO/DiscScrubber.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
|
@ -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<u64> dol_offset = filesystem->GetBootDOLOffset();
|
||||
const std::optional<u64> dol_offset = GetBootDOLOffset(*m_disc, partition);
|
||||
if (!dol_offset)
|
||||
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)
|
||||
return false;
|
||||
header->dol_offset = *dol_offset;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<u32> fst_offset_unshifted = m_volume->ReadSwapped<u32>(0x424, m_partition);
|
||||
const std::optional<u32> fst_size_unshifted = m_volume->ReadSwapped<u32>(0x428, m_partition);
|
||||
if (!fst_offset_unshifted || !fst_size_unshifted)
|
||||
const std::optional<u64> fst_offset = GetFSTOffset(*m_volume, m_partition);
|
||||
const std::optional<u64> fst_size = GetFSTSize(*m_volume, m_partition);
|
||||
if (!fst_offset || !fst_size)
|
||||
return;
|
||||
const u64 fst_offset = static_cast<u64>(*fst_offset_unshifted) << m_offset_shift;
|
||||
const u64 fst_size = static_cast<u64>(*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<FileInfo> 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<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
|
||||
|
|
|
@ -93,14 +93,6 @@ public:
|
|||
std::unique_ptr<FileInfo> FindFileInfo(const std::string& path) 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:
|
||||
bool m_valid;
|
||||
u32 m_offset_shift;
|
||||
|
|
|
@ -120,14 +120,6 @@ public:
|
|||
// Returns nullptr if not found
|
||||
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; }
|
||||
protected:
|
||||
const Volume* const m_volume;
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
}
|
||||
virtual std::vector<Partition> GetPartitions() const { return {}; }
|
||||
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()); }
|
||||
virtual std::optional<u64> GetTitleID(const Partition& partition) const { return {}; }
|
||||
virtual const IOS::ES::TicketReader& GetTicket(const Partition& partition) const
|
||||
|
|
|
@ -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<u8*>(&banner_file), file_size))
|
||||
if (file_size != ReadFile(*this, PARTITION_NONE, file_info.get(),
|
||||
reinterpret_cast<u8*>(&banner_file), file_size))
|
||||
{
|
||||
WARN_LOG(DISCIO, "Could not read opening.bnr.");
|
||||
return;
|
||||
|
|
|
@ -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<BlobReader> reader)
|
|||
continue;
|
||||
const u64 partition_offset = static_cast<u64>(*read_buffer) << 2;
|
||||
|
||||
// Check if this is the game partition
|
||||
const bool is_game_partition =
|
||||
m_game_partition == PARTITION_NONE &&
|
||||
m_pReader->ReadSwapped<u32>(partition_table_offset + (i * 8) + 4) == u32(0);
|
||||
// Read the partition type
|
||||
const std::optional<u32> partition_type =
|
||||
m_pReader->ReadSwapped<u32>(partition_table_offset + (i * 8) + 4);
|
||||
if (!partition_type)
|
||||
continue;
|
||||
|
||||
// Read 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
|
||||
// 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<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
|
||||
{
|
||||
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::unique_ptr<FileInfo> 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, const Partition& partition) const override;
|
||||
std::vector<Partition> GetPartitions() const override;
|
||||
Partition GetGamePartition() const override;
|
||||
std::optional<u32> GetPartitionType(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::TMDReader& GetTMD(const Partition& partition) const override;
|
||||
|
@ -64,6 +65,7 @@ public:
|
|||
|
||||
private:
|
||||
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, IOS::ES::TicketReader> m_partition_tickets;
|
||||
std::map<Partition, IOS::ES::TMDReader> m_partition_tmds;
|
||||
|
|
|
@ -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<DiscIO::Language> 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);
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
@ -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<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
|
||||
{
|
||||
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<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))
|
||||
{
|
||||
// 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
|
||||
{
|
||||
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<wxString, const DiscIO::FileSystem&> 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<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());
|
||||
}
|
||||
}
|
||||
const std::pair<wxString, const DiscIO::FileSystem&> 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<DiscIO::FileInfo> 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<u32>((static_cast<float>(progress) / static_cast<float>(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<u32>((static_cast<float>(progress) / static_cast<float>(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<wxString, const DiscIO::FileSystem&> 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<wxString, const DiscIO::FileSystem&>
|
||||
FilesystemPanel::BuildDirectoryPathFromSelection() const
|
||||
{
|
||||
wxString directory_path = BuildFilePathFromSelection();
|
||||
directory_path += DIR_SEP_CHR;
|
||||
return directory_path;
|
||||
const std::pair<wxString, const DiscIO::FileSystem&> result = BuildFilePathFromSelection();
|
||||
return {result.first + DIR_SEP_CHR, result.second};
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <wx/panel.h>
|
||||
|
||||
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<wxString, const DiscIO::FileSystem&> BuildFilePathFromSelection() const;
|
||||
std::pair<wxString, const DiscIO::FileSystem&> BuildDirectoryPathFromSelection() const;
|
||||
|
||||
wxTreeCtrl* m_tree_ctrl;
|
||||
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue