DiscIO/DirectoryBlob: Allow constructing a DirectoryBlobReader from a VolumeDisc.

This commit is contained in:
Admiral H. Curtiss 2021-09-22 04:35:27 +02:00
parent 3a72a39efd
commit 00ef9f2b4f
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
2 changed files with 187 additions and 33 deletions

View File

@ -356,6 +356,15 @@ std::unique_ptr<DirectoryBlobReader> DirectoryBlobReader::Create(const std::stri
return std::unique_ptr<DirectoryBlobReader>(new DirectoryBlobReader(partition_root, true_root));
}
std::unique_ptr<DirectoryBlobReader>
DirectoryBlobReader::Create(std::unique_ptr<DiscIO::VolumeDisc> volume)
{
if (!volume)
return nullptr;
return std::unique_ptr<DirectoryBlobReader>(new DirectoryBlobReader(std::move(volume)));
}
DirectoryBlobReader::DirectoryBlobReader(const std::string& game_partition_root,
const std::string& true_root)
: m_encryption_cache(this)
@ -371,8 +380,8 @@ DirectoryBlobReader::DirectoryBlobReader(const std::string& game_partition_root,
}
else
{
SetNonpartitionDiscHeader(game_partition.GetHeader(), game_partition_root);
SetWiiRegionData(game_partition_root);
SetNonpartitionDiscHeaderFromFile(game_partition.GetHeader(), game_partition_root);
SetWiiRegionDataFromFile(game_partition_root);
std::vector<PartitionWithType> partitions;
partitions.emplace_back(std::move(game_partition), PartitionType::Game);
@ -400,6 +409,58 @@ DirectoryBlobReader::DirectoryBlobReader(const std::string& game_partition_root,
}
}
DirectoryBlobReader::DirectoryBlobReader(std::unique_ptr<DiscIO::VolumeDisc> volume)
: m_encryption_cache(this), m_wrapped_volume(std::move(volume))
{
DirectoryBlobPartition game_partition(m_wrapped_volume.get(),
m_wrapped_volume->GetGamePartition(), std::nullopt);
m_is_wii = game_partition.IsWii();
if (!m_is_wii)
{
m_gamecube_pseudopartition = std::move(game_partition);
m_data_size = m_gamecube_pseudopartition.GetDataSize();
m_encrypted = false;
}
else
{
std::vector<u8> header_bin(WII_NONPARTITION_DISCHEADER_SIZE);
if (!m_wrapped_volume->Read(WII_NONPARTITION_DISCHEADER_ADDRESS,
WII_NONPARTITION_DISCHEADER_SIZE, header_bin.data(),
PARTITION_NONE))
{
header_bin.clear();
}
SetNonpartitionDiscHeader(game_partition.GetHeader(), std::move(header_bin));
std::vector<u8> wii_region_data(WII_REGION_DATA_SIZE);
if (!m_wrapped_volume->Read(WII_REGION_DATA_ADDRESS, WII_REGION_DATA_SIZE,
wii_region_data.data(), PARTITION_NONE))
{
wii_region_data.clear();
}
SetWiiRegionData(wii_region_data, "volume");
std::vector<PartitionWithType> partitions;
partitions.emplace_back(std::move(game_partition), PartitionType::Game);
for (Partition partition : m_wrapped_volume->GetPartitions())
{
if (partition == m_wrapped_volume->GetGamePartition())
continue;
auto type = m_wrapped_volume->GetPartitionType(partition);
if (type)
{
partitions.emplace_back(DirectoryBlobPartition(m_wrapped_volume.get(), partition, m_is_wii),
static_cast<PartitionType>(*type));
}
}
SetPartitions(std::move(partitions));
}
}
bool DirectoryBlobReader::Read(u64 offset, u64 length, u8* buffer)
{
if (offset + length > m_data_size)
@ -469,22 +530,35 @@ u64 DirectoryBlobReader::GetDataSize() const
return m_data_size;
}
void DirectoryBlobReader::SetNonpartitionDiscHeader(const std::vector<u8>& partition_header,
const std::string& game_partition_root)
void DirectoryBlobReader::SetNonpartitionDiscHeaderFromFile(const std::vector<u8>& partition_header,
const std::string& game_partition_root)
{
m_disc_header_nonpartition.resize(WII_NONPARTITION_DISCHEADER_SIZE);
std::vector<u8> header_bin(WII_NONPARTITION_DISCHEADER_SIZE);
const size_t header_bin_bytes_read =
ReadFileToVector(game_partition_root + "disc/header.bin", &m_disc_header_nonpartition);
ReadFileToVector(game_partition_root + "disc/header.bin", &header_bin);
header_bin.resize(header_bin_bytes_read);
SetNonpartitionDiscHeader(partition_header, std::move(header_bin));
}
void DirectoryBlobReader::SetNonpartitionDiscHeader(const std::vector<u8>& partition_header,
std::vector<u8> header_bin)
{
const size_t header_bin_size = header_bin.size();
m_disc_header_nonpartition = std::move(header_bin);
m_disc_header_nonpartition.resize(WII_NONPARTITION_DISCHEADER_SIZE);
// If header.bin is missing or smaller than expected, use the content of sys/boot.bin instead
std::copy(partition_header.data() + header_bin_bytes_read,
partition_header.data() + m_disc_header_nonpartition.size(),
m_disc_header_nonpartition.data() + header_bin_bytes_read);
if (header_bin_size < m_disc_header_nonpartition.size())
{
std::copy(partition_header.data() + header_bin_size,
partition_header.data() + m_disc_header_nonpartition.size(),
m_disc_header_nonpartition.data() + header_bin_size);
}
// 0x60 and 0x61 are the only differences between the partition and non-partition headers
if (header_bin_bytes_read < 0x60)
if (header_bin_size < 0x60)
m_disc_header_nonpartition[0x60] = 0;
if (header_bin_bytes_read < 0x61)
if (header_bin_size < 0x61)
m_disc_header_nonpartition[0x61] = 0;
m_encrypted = std::all_of(m_disc_header_nonpartition.data() + 0x60,
@ -494,18 +568,30 @@ void DirectoryBlobReader::SetNonpartitionDiscHeader(const std::vector<u8>& parti
m_disc_header_nonpartition);
}
void DirectoryBlobReader::SetWiiRegionData(const std::string& game_partition_root)
void DirectoryBlobReader::SetWiiRegionDataFromFile(const std::string& game_partition_root)
{
std::vector<u8> wii_region_data(WII_REGION_DATA_SIZE);
const std::string region_bin_path = game_partition_root + "disc/region.bin";
const size_t bytes_read = ReadFileToVector(region_bin_path, &wii_region_data);
wii_region_data.resize(bytes_read);
SetWiiRegionData(wii_region_data, region_bin_path);
}
void DirectoryBlobReader::SetWiiRegionData(const std::vector<u8>& wii_region_data,
const std::string& log_path)
{
m_wii_region_data.resize(0x10, 0x00);
m_wii_region_data.resize(0x20, 0x80);
m_wii_region_data.resize(WII_REGION_DATA_SIZE, 0x80);
Write32(INVALID_REGION, 0, &m_wii_region_data);
const std::string region_bin_path = game_partition_root + "disc/region.bin";
const size_t bytes_read = ReadFileToVector(region_bin_path, &m_wii_region_data);
if (bytes_read < 0x4)
ERROR_LOG_FMT(DISCIO, "Couldn't read region from {}", region_bin_path);
else if (bytes_read < 0x20)
ERROR_LOG_FMT(DISCIO, "Couldn't read age ratings from {}", region_bin_path);
std::copy_n(wii_region_data.begin(),
std::min<size_t>(wii_region_data.size(), WII_REGION_DATA_SIZE),
m_wii_region_data.begin());
if (wii_region_data.size() < 0x4)
ERROR_LOG_FMT(DISCIO, "Couldn't read region from {}", log_path);
else if (wii_region_data.size() < 0x20)
ERROR_LOG_FMT(DISCIO, "Couldn't read age ratings from {}", log_path);
m_nonpartition_contents.AddReference(WII_REGION_DATA_ADDRESS, m_wii_region_data);
}
@ -585,27 +671,88 @@ void DirectoryBlobReader::SetPartitionHeader(DirectoryBlobPartition* partition,
constexpr u32 TMD_OFFSET = 0x2c0;
constexpr u32 H3_OFFSET = 0x4000;
const std::optional<DiscIO::Partition>& wrapped_partition = partition->GetWrappedPartition();
const std::string& partition_root = partition->GetRootDirectory();
const u64 ticket_size = m_nonpartition_contents.CheckSizeAndAdd(
partition_address + WII_PARTITION_TICKET_ADDRESS, WII_PARTITION_TICKET_SIZE,
partition_root + "ticket.bin");
u64 ticket_size;
if (wrapped_partition)
{
const auto& ticket = m_wrapped_volume->GetTicket(*wrapped_partition).GetBytes();
auto& new_ticket = m_extra_data.emplace_back(ticket);
if (new_ticket.size() > WII_PARTITION_TICKET_SIZE)
new_ticket.resize(WII_PARTITION_TICKET_SIZE);
ticket_size = new_ticket.size();
m_nonpartition_contents.AddReference(partition_address + WII_PARTITION_TICKET_ADDRESS,
new_ticket);
}
else
{
ticket_size = m_nonpartition_contents.CheckSizeAndAdd(
partition_address + WII_PARTITION_TICKET_ADDRESS, WII_PARTITION_TICKET_SIZE,
partition_root + "ticket.bin");
}
const u64 tmd_size = m_nonpartition_contents.CheckSizeAndAdd(
partition_address + TMD_OFFSET, IOS::ES::MAX_TMD_SIZE, partition_root + "tmd.bin");
u64 tmd_size;
if (wrapped_partition)
{
const auto& tmd = m_wrapped_volume->GetTMD(*wrapped_partition).GetBytes();
auto& new_tmd = m_extra_data.emplace_back(tmd);
if (new_tmd.size() > IOS::ES::MAX_TMD_SIZE)
new_tmd.resize(IOS::ES::MAX_TMD_SIZE);
tmd_size = new_tmd.size();
m_nonpartition_contents.AddReference(partition_address + TMD_OFFSET, new_tmd);
}
else
{
tmd_size = m_nonpartition_contents.CheckSizeAndAdd(
partition_address + TMD_OFFSET, IOS::ES::MAX_TMD_SIZE, partition_root + "tmd.bin");
}
const u64 cert_offset = Common::AlignUp(TMD_OFFSET + tmd_size, 0x20ull);
const u64 max_cert_size = H3_OFFSET - cert_offset;
const u64 cert_size = m_nonpartition_contents.CheckSizeAndAdd(
partition_address + cert_offset, max_cert_size, partition_root + "cert.bin");
m_nonpartition_contents.CheckSizeAndAdd(partition_address + H3_OFFSET, WII_PARTITION_H3_SIZE,
partition_root + "h3.bin");
u64 cert_size;
if (wrapped_partition)
{
const auto& cert = m_wrapped_volume->GetCertificateChain(*wrapped_partition);
auto& new_cert = m_extra_data.emplace_back(cert);
if (new_cert.size() > max_cert_size)
new_cert.resize(max_cert_size);
cert_size = new_cert.size();
m_nonpartition_contents.AddReference(partition_address + cert_offset, new_cert);
}
else
{
cert_size = m_nonpartition_contents.CheckSizeAndAdd(partition_address + cert_offset,
max_cert_size, partition_root + "cert.bin");
}
if (wrapped_partition)
{
if (m_wrapped_volume->IsEncryptedAndHashed())
{
const std::optional<u64> offset = m_wrapped_volume->ReadSwappedAndShifted(
wrapped_partition->offset + WII_PARTITION_H3_OFFSET_ADDRESS, PARTITION_NONE);
if (offset)
{
auto& new_h3 = m_extra_data.emplace_back(WII_PARTITION_H3_SIZE);
if (m_wrapped_volume->Read(wrapped_partition->offset + *offset, new_h3.size(),
new_h3.data(), PARTITION_NONE))
{
m_nonpartition_contents.AddReference(partition_address + H3_OFFSET, new_h3);
}
}
}
}
else
{
m_nonpartition_contents.CheckSizeAndAdd(partition_address + H3_OFFSET, WII_PARTITION_H3_SIZE,
partition_root + "h3.bin");
}
constexpr u32 PARTITION_HEADER_SIZE = 0x1c;
const u64 data_size = Common::AlignUp(partition->GetDataSize(), 0x7c00) / 0x7c00 * 0x8000;
m_partition_headers.emplace_back(PARTITION_HEADER_SIZE);
std::vector<u8>& partition_header = m_partition_headers.back();
std::vector<u8>& partition_header = m_extra_data.emplace_back(PARTITION_HEADER_SIZE);
Write32(static_cast<u32>(tmd_size), 0x0, &partition_header);
Write32(TMD_OFFSET >> 2, 0x4, &partition_header);
Write32(static_cast<u32>(cert_size), 0x8, &partition_header);

View File

@ -252,6 +252,7 @@ class DirectoryBlobReader : public BlobReader
public:
static std::unique_ptr<DirectoryBlobReader> Create(const std::string& dol_path);
static std::unique_ptr<DirectoryBlobReader> Create(std::unique_ptr<DiscIO::VolumeDisc> volume);
// We do not allow copying, because it might mess up the pointers inside DiscContents
DirectoryBlobReader(const DirectoryBlobReader&) = delete;
@ -287,15 +288,19 @@ private:
explicit DirectoryBlobReader(const std::string& game_partition_root,
const std::string& true_root);
explicit DirectoryBlobReader(std::unique_ptr<DiscIO::VolumeDisc> volume);
const DirectoryBlobPartition* GetPartition(u64 offset, u64 size, u64 partition_data_offset) const;
bool EncryptPartitionData(u64 offset, u64 size, u8* buffer, u64 partition_data_offset,
u64 partition_data_decrypted_size);
void SetNonpartitionDiscHeaderFromFile(const std::vector<u8>& partition_header,
const std::string& game_partition_root);
void SetNonpartitionDiscHeader(const std::vector<u8>& partition_header,
const std::string& game_partition_root);
void SetWiiRegionData(const std::string& game_partition_root);
std::vector<u8> header_bin);
void SetWiiRegionDataFromFile(const std::string& game_partition_root);
void SetWiiRegionData(const std::vector<u8>& wii_region_data, const std::string& log_path);
void SetPartitions(std::vector<PartitionWithType>&& partitions);
void SetPartitionHeader(DirectoryBlobPartition* partition, u64 partition_address);
@ -313,9 +318,11 @@ private:
std::vector<u8> m_disc_header_nonpartition;
std::vector<u8> m_partition_table;
std::vector<u8> m_wii_region_data;
std::vector<std::vector<u8>> m_partition_headers;
std::vector<std::vector<u8>> m_extra_data;
u64 m_data_size;
std::unique_ptr<DiscIO::VolumeDisc> m_wrapped_volume;
};
} // namespace DiscIO