WIA: Decrypt Wii data when writing
This commit is contained in:
parent
115edea34e
commit
3b8c44fd0e
|
@ -201,18 +201,9 @@ bool VolumeWii::Read(u64 offset, u64 length, u8* buffer, const Partition& partit
|
||||||
if (!m_reader->Read(block_offset_on_disc, BLOCK_TOTAL_SIZE, read_buffer.data()))
|
if (!m_reader->Read(block_offset_on_disc, BLOCK_TOTAL_SIZE, read_buffer.data()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Decrypt the block's data.
|
// Decrypt the block's data
|
||||||
// 0x3D0 - 0x3DF in read_buffer will be overwritten,
|
DecryptBlockData(read_buffer.data(), m_last_decrypted_block_data, aes_context);
|
||||||
// but that won't affect anything, because we won't
|
|
||||||
// use the content of read_buffer anymore after this
|
|
||||||
mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, BLOCK_DATA_SIZE, &read_buffer[0x3D0],
|
|
||||||
&read_buffer[BLOCK_HEADER_SIZE], m_last_decrypted_block_data);
|
|
||||||
m_last_decrypted_block = block_offset_on_disc;
|
m_last_decrypted_block = block_offset_on_disc;
|
||||||
|
|
||||||
// The only thing we currently use from the 0x000 - 0x3FF part
|
|
||||||
// of the block is the IV (at 0x3D0), but it also contains SHA-1
|
|
||||||
// hashes that IOS uses to check that discs aren't tampered with.
|
|
||||||
// http://wiibrew.org/wiki/Wii_Disc#Encrypted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the decrypted data
|
// Copy the decrypted data
|
||||||
|
@ -487,9 +478,7 @@ bool VolumeWii::CheckBlockIntegrity(u64 block_index, const std::vector<u8>& encr
|
||||||
encrypted_data.data(), reinterpret_cast<u8*>(&hashes));
|
encrypted_data.data(), reinterpret_cast<u8*>(&hashes));
|
||||||
|
|
||||||
u8 cluster_data[BLOCK_DATA_SIZE];
|
u8 cluster_data[BLOCK_DATA_SIZE];
|
||||||
std::memcpy(iv, encrypted_data.data() + 0x3D0, 16);
|
DecryptBlockData(encrypted_data.data(), cluster_data, aes_context);
|
||||||
mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, sizeof(cluster_data), iv,
|
|
||||||
encrypted_data.data() + sizeof(HashBlock), cluster_data);
|
|
||||||
|
|
||||||
for (u32 hash_index = 0; hash_index < 31; ++hash_index)
|
for (u32 hash_index = 0; hash_index < 31; ++hash_index)
|
||||||
{
|
{
|
||||||
|
@ -671,4 +660,12 @@ bool VolumeWii::EncryptGroup(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VolumeWii::DecryptBlockData(const u8* in, u8* out, mbedtls_aes_context* aes_context)
|
||||||
|
{
|
||||||
|
std::array<u8, 16> iv;
|
||||||
|
std::copy(&in[0x3d0], &in[0x3e0], iv.data());
|
||||||
|
mbedtls_aes_crypt_cbc(aes_context, MBEDTLS_AES_DECRYPT, BLOCK_DATA_SIZE, iv.data(),
|
||||||
|
&in[BLOCK_HEADER_SIZE], out);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace DiscIO
|
} // namespace DiscIO
|
||||||
|
|
|
@ -104,6 +104,8 @@ public:
|
||||||
const std::function<void(HashBlock hash_blocks[BLOCKS_PER_GROUP])>&
|
const std::function<void(HashBlock hash_blocks[BLOCKS_PER_GROUP])>&
|
||||||
hash_exception_callback = {});
|
hash_exception_callback = {});
|
||||||
|
|
||||||
|
static void DecryptBlockData(const u8* in, u8* out, mbedtls_aes_context* aes_context);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
u32 GetOffsetShift() const override { return 2; }
|
u32 GetOffsetShift() const override { return 2; }
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,12 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cinttypes>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <bzlib.h>
|
#include <bzlib.h>
|
||||||
|
@ -27,6 +29,8 @@
|
||||||
#include "Common/Swap.h"
|
#include "Common/Swap.h"
|
||||||
|
|
||||||
#include "DiscIO/Blob.h"
|
#include "DiscIO/Blob.h"
|
||||||
|
#include "DiscIO/DiscExtractor.h"
|
||||||
|
#include "DiscIO/Volume.h"
|
||||||
#include "DiscIO/VolumeWii.h"
|
#include "DiscIO/VolumeWii.h"
|
||||||
#include "DiscIO/WiiEncryptionCache.h"
|
#include "DiscIO/WiiEncryptionCache.h"
|
||||||
|
|
||||||
|
@ -902,7 +906,161 @@ bool WIAFileReader::PadTo4(File::IOFile* file, u64* bytes_written)
|
||||||
return file->WriteBytes(&ZEROES, bytes_to_write);
|
return file->WriteBytes(&ZEROES, bytes_to_write);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WIAFileReader::AddRawDataEntry(u64 offset, u64 size, int chunk_size, u32* total_groups,
|
||||||
|
std::vector<RawDataEntry>* raw_data_entries,
|
||||||
|
std::vector<DataEntry>* data_entries)
|
||||||
|
{
|
||||||
|
constexpr size_t SKIP_SIZE = sizeof(WIAHeader2::disc_header);
|
||||||
|
const u64 skip = offset < SKIP_SIZE ? std::min(SKIP_SIZE - offset, size) : 0;
|
||||||
|
|
||||||
|
offset += skip;
|
||||||
|
size -= skip;
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const u32 group_index = *total_groups;
|
||||||
|
const u32 groups = static_cast<u32>(Common::AlignUp(size, chunk_size) / chunk_size);
|
||||||
|
*total_groups += groups;
|
||||||
|
|
||||||
|
data_entries->emplace_back(raw_data_entries->size());
|
||||||
|
raw_data_entries->emplace_back(RawDataEntry{Common::swap64(offset), Common::swap64(size),
|
||||||
|
Common::swap32(group_index), Common::swap32(groups)});
|
||||||
|
}
|
||||||
|
|
||||||
|
WIAFileReader::PartitionDataEntry WIAFileReader::CreatePartitionDataEntry(
|
||||||
|
u64 offset, u64 size, u32 index, int chunk_size, u32* total_groups,
|
||||||
|
const std::vector<PartitionEntry>& partition_entries, std::vector<DataEntry>* data_entries)
|
||||||
|
{
|
||||||
|
const u32 group_index = *total_groups;
|
||||||
|
const u64 rounded_size = Common::AlignDown(size, VolumeWii::BLOCK_TOTAL_SIZE);
|
||||||
|
const u32 groups = static_cast<u32>(Common::AlignUp(rounded_size, chunk_size) / chunk_size);
|
||||||
|
*total_groups += groups;
|
||||||
|
|
||||||
|
data_entries->emplace_back(partition_entries.size(), index);
|
||||||
|
return PartitionDataEntry{Common::swap32(offset / VolumeWii::BLOCK_TOTAL_SIZE),
|
||||||
|
Common::swap32(size / VolumeWii::BLOCK_TOTAL_SIZE),
|
||||||
|
Common::swap32(group_index), Common::swap32(groups)};
|
||||||
|
}
|
||||||
|
|
||||||
|
WIAFileReader::ConversionResult WIAFileReader::SetUpDataEntriesForWriting(
|
||||||
|
const VolumeDisc* volume, int chunk_size, u64 iso_size, u32* total_groups,
|
||||||
|
std::vector<PartitionEntry>* partition_entries, std::vector<RawDataEntry>* raw_data_entries,
|
||||||
|
std::vector<DataEntry>* data_entries)
|
||||||
|
{
|
||||||
|
std::vector<Partition> partitions;
|
||||||
|
if (volume && volume->IsEncryptedAndHashed())
|
||||||
|
partitions = volume->GetPartitions();
|
||||||
|
|
||||||
|
std::sort(partitions.begin(), partitions.end(),
|
||||||
|
[](const Partition& a, const Partition& b) { return a.offset < b.offset; });
|
||||||
|
|
||||||
|
*total_groups = 0;
|
||||||
|
|
||||||
|
u64 last_partition_end_offset = 0;
|
||||||
|
|
||||||
|
const auto add_raw_data_entry = [&](u64 offset, u64 size) {
|
||||||
|
return AddRawDataEntry(offset, size, chunk_size, total_groups, raw_data_entries, data_entries);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto create_partition_data_entry = [&](u64 offset, u64 size, u32 index) {
|
||||||
|
return CreatePartitionDataEntry(offset, size, index, chunk_size, total_groups,
|
||||||
|
*partition_entries, data_entries);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const Partition& partition : partitions)
|
||||||
|
{
|
||||||
|
// If a partition is odd in some way that prevents us from encoding it as a partition,
|
||||||
|
// we encode it as raw data instead by skipping the current loop iteration.
|
||||||
|
// Partitions can always be encoded as raw data, but it is less space efficient.
|
||||||
|
|
||||||
|
if (partition.offset < last_partition_end_offset)
|
||||||
|
{
|
||||||
|
WARN_LOG(DISCIO, "Overlapping partitions at %" PRIx64, partition.offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (volume->ReadSwapped<u32>(partition.offset, PARTITION_NONE) != u32(0x10001))
|
||||||
|
{
|
||||||
|
// This looks more like garbage data than an actual partition.
|
||||||
|
// The values of data_offset and data_size will very likely also be garbage.
|
||||||
|
// Some WBFS writing programs scrub the SSBB Masterpiece partitions without
|
||||||
|
// removing them from the partition table, causing this problem.
|
||||||
|
WARN_LOG(DISCIO, "Invalid partition at %" PRIx64, partition.offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u64> data_offset =
|
||||||
|
volume->ReadSwappedAndShifted(partition.offset + 0x2b8, PARTITION_NONE);
|
||||||
|
std::optional<u64> data_size =
|
||||||
|
volume->ReadSwappedAndShifted(partition.offset + 0x2bc, PARTITION_NONE);
|
||||||
|
|
||||||
|
if (!data_offset || !data_size)
|
||||||
|
return ConversionResult::ReadFailed;
|
||||||
|
|
||||||
|
const u64 data_start = partition.offset + *data_offset;
|
||||||
|
const u64 data_end = data_start + *data_size;
|
||||||
|
|
||||||
|
if (data_start % VolumeWii::BLOCK_TOTAL_SIZE != 0)
|
||||||
|
{
|
||||||
|
WARN_LOG(DISCIO, "Misaligned partition at %" PRIx64, partition.offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*data_size < VolumeWii::BLOCK_TOTAL_SIZE)
|
||||||
|
{
|
||||||
|
WARN_LOG(DISCIO, "Very small partition at %" PRIx64, partition.offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_end > iso_size)
|
||||||
|
{
|
||||||
|
WARN_LOG(DISCIO, "Too large partition at %" PRIx64, partition.offset);
|
||||||
|
*data_size = iso_size - *data_offset - partition.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<u64> fst_offset = GetFSTOffset(*volume, partition);
|
||||||
|
const std::optional<u64> fst_size = GetFSTSize(*volume, partition);
|
||||||
|
|
||||||
|
if (!fst_offset || !fst_size)
|
||||||
|
return ConversionResult::ReadFailed;
|
||||||
|
|
||||||
|
const IOS::ES::TicketReader& ticket = volume->GetTicket(partition);
|
||||||
|
if (!ticket.IsValid())
|
||||||
|
return ConversionResult::ReadFailed;
|
||||||
|
|
||||||
|
add_raw_data_entry(last_partition_end_offset, partition.offset - last_partition_end_offset);
|
||||||
|
|
||||||
|
add_raw_data_entry(partition.offset, *data_offset);
|
||||||
|
|
||||||
|
const u64 fst_end = volume->PartitionOffsetToRawOffset(*fst_offset + *fst_size, partition);
|
||||||
|
const u64 split_point = std::min(
|
||||||
|
data_end, Common::AlignUp(fst_end - data_start, VolumeWii::GROUP_TOTAL_SIZE) + data_start);
|
||||||
|
|
||||||
|
PartitionEntry partition_entry;
|
||||||
|
partition_entry.partition_key = ticket.GetTitleKey();
|
||||||
|
partition_entry.data_entries[0] =
|
||||||
|
create_partition_data_entry(data_start, split_point - data_start, 0);
|
||||||
|
partition_entry.data_entries[1] =
|
||||||
|
create_partition_data_entry(split_point, data_end - split_point, 1);
|
||||||
|
|
||||||
|
// Note: We can't simply set last_partition_end_offset to data_end,
|
||||||
|
// because construct_partition_data_entry may have rounded it
|
||||||
|
last_partition_end_offset =
|
||||||
|
(Common::swap32(partition_entry.data_entries[1].first_sector) +
|
||||||
|
Common::swap32(partition_entry.data_entries[1].number_of_sectors)) *
|
||||||
|
VolumeWii::BLOCK_TOTAL_SIZE;
|
||||||
|
|
||||||
|
partition_entries->emplace_back(std::move(partition_entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
add_raw_data_entry(last_partition_end_offset, iso_size - last_partition_end_offset);
|
||||||
|
|
||||||
|
return ConversionResult::Success;
|
||||||
|
}
|
||||||
|
|
||||||
WIAFileReader::ConversionResult WIAFileReader::ConvertToWIA(BlobReader* infile,
|
WIAFileReader::ConversionResult WIAFileReader::ConvertToWIA(BlobReader* infile,
|
||||||
|
const VolumeDisc* infile_volume,
|
||||||
File::IOFile* outfile, int chunk_size,
|
File::IOFile* outfile, int chunk_size,
|
||||||
CompressCB callback, void* arg)
|
CompressCB callback, void* arg)
|
||||||
{
|
{
|
||||||
|
@ -913,6 +1071,7 @@ WIAFileReader::ConversionResult WIAFileReader::ConvertToWIA(BlobReader* infile,
|
||||||
|
|
||||||
u64 bytes_read = 0;
|
u64 bytes_read = 0;
|
||||||
u64 bytes_written = 0;
|
u64 bytes_written = 0;
|
||||||
|
size_t groups_written = 0;
|
||||||
|
|
||||||
// These two headers will be filled in with proper values at the very end
|
// These two headers will be filled in with proper values at the very end
|
||||||
WIAHeader1 header_1;
|
WIAHeader1 header_1;
|
||||||
|
@ -923,18 +1082,11 @@ WIAFileReader::ConversionResult WIAFileReader::ConvertToWIA(BlobReader* infile,
|
||||||
if (!PadTo4(outfile, &bytes_written))
|
if (!PadTo4(outfile, &bytes_written))
|
||||||
return ConversionResult::WriteFailed;
|
return ConversionResult::WriteFailed;
|
||||||
|
|
||||||
std::vector<GroupEntry> group_entries;
|
|
||||||
group_entries.resize(Common::AlignUp(iso_size, chunk_size) / chunk_size);
|
|
||||||
|
|
||||||
std::vector<RawDataEntry> raw_data_entries;
|
|
||||||
raw_data_entries.emplace_back(
|
|
||||||
RawDataEntry{Common::swap64(header_2.disc_header.size()),
|
|
||||||
Common::swap64(iso_size - header_2.disc_header.size()), 0,
|
|
||||||
Common::swap32(static_cast<u32>(group_entries.size()))});
|
|
||||||
|
|
||||||
std::vector<PartitionEntry> partition_entries;
|
std::vector<PartitionEntry> partition_entries;
|
||||||
|
std::vector<RawDataEntry> raw_data_entries;
|
||||||
|
std::vector<GroupEntry> group_entries;
|
||||||
|
|
||||||
const auto run_callback = [&](size_t groups_written) {
|
const auto run_callback = [&] {
|
||||||
int ratio = 0;
|
int ratio = 0;
|
||||||
if (bytes_read != 0)
|
if (bytes_read != 0)
|
||||||
ratio = static_cast<int>(100 * bytes_written / bytes_read);
|
ratio = static_cast<int>(100 * bytes_written / bytes_read);
|
||||||
|
@ -942,42 +1094,152 @@ WIAFileReader::ConversionResult WIAFileReader::ConvertToWIA(BlobReader* infile,
|
||||||
const std::string temp =
|
const std::string temp =
|
||||||
StringFromFormat(Common::GetStringT("%i of %i blocks. Compression ratio %i%%").c_str(),
|
StringFromFormat(Common::GetStringT("%i of %i blocks. Compression ratio %i%%").c_str(),
|
||||||
groups_written, group_entries.size(), ratio);
|
groups_written, group_entries.size(), ratio);
|
||||||
return callback(temp, static_cast<float>(groups_written) / group_entries.size(), arg);
|
|
||||||
|
float completion = 0.0f;
|
||||||
|
if (group_entries.size() != 0)
|
||||||
|
completion = static_cast<float>(groups_written) / group_entries.size();
|
||||||
|
|
||||||
|
return callback(temp, completion, arg);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!run_callback())
|
||||||
|
return ConversionResult::Canceled;
|
||||||
|
|
||||||
|
u32 total_groups;
|
||||||
|
std::vector<DataEntry> data_entries;
|
||||||
|
|
||||||
|
const ConversionResult set_up_data_entries_result =
|
||||||
|
SetUpDataEntriesForWriting(infile_volume, chunk_size, iso_size, &total_groups,
|
||||||
|
&partition_entries, &raw_data_entries, &data_entries);
|
||||||
|
if (set_up_data_entries_result != ConversionResult::Success)
|
||||||
|
return set_up_data_entries_result;
|
||||||
|
|
||||||
|
group_entries.resize(total_groups);
|
||||||
|
|
||||||
if (!infile->Read(0, header_2.disc_header.size(), header_2.disc_header.data()))
|
if (!infile->Read(0, header_2.disc_header.size(), header_2.disc_header.data()))
|
||||||
return ConversionResult::ReadFailed;
|
return ConversionResult::ReadFailed;
|
||||||
// We intentially do not increment bytes_read here, since these bytes will be read again
|
// We intentially do not increment bytes_read here, since these bytes will be read again
|
||||||
|
|
||||||
if (!run_callback(0))
|
|
||||||
return ConversionResult::Canceled;
|
|
||||||
|
|
||||||
std::vector<u8> buffer(chunk_size);
|
std::vector<u8> buffer(chunk_size);
|
||||||
for (size_t i = 0; i < group_entries.size(); ++i)
|
std::vector<u8> decryption_buffer(VolumeWii::BLOCK_DATA_SIZE);
|
||||||
|
for (const DataEntry& data_entry : data_entries)
|
||||||
{
|
{
|
||||||
const u64 bytes_to_read = std::min<u64>(chunk_size, iso_size - bytes_read);
|
if (data_entry.is_partition)
|
||||||
|
{
|
||||||
|
const PartitionEntry& partition_entry = partition_entries[data_entry.index];
|
||||||
|
const PartitionDataEntry& partition_data_entry =
|
||||||
|
partition_entry.data_entries[data_entry.partition_data_index];
|
||||||
|
|
||||||
if (bytes_written >> 2 > std::numeric_limits<u32>::max())
|
const u32 first_group = Common::swap32(partition_data_entry.group_index);
|
||||||
return ConversionResult::InternalError;
|
const u32 last_group = first_group + Common::swap32(partition_data_entry.number_of_groups);
|
||||||
|
|
||||||
ASSERT((bytes_written & 3) == 0);
|
const u64 data_offset =
|
||||||
group_entries[i] = GroupEntry{Common::swap32(static_cast<u32>(bytes_written >> 2)),
|
Common::swap32(partition_data_entry.first_sector) * VolumeWii::BLOCK_TOTAL_SIZE;
|
||||||
Common::swap32(static_cast<u32>(bytes_to_read))};
|
const u64 data_size =
|
||||||
|
Common::swap32(partition_data_entry.number_of_sectors) * VolumeWii::BLOCK_TOTAL_SIZE;
|
||||||
|
|
||||||
if (!infile->Read(bytes_read, bytes_to_read, buffer.data()))
|
ASSERT(groups_written == first_group);
|
||||||
return ConversionResult::ReadFailed;
|
ASSERT(bytes_read == data_offset);
|
||||||
if (!outfile->WriteArray(buffer.data(), bytes_to_read))
|
|
||||||
return ConversionResult::WriteFailed;
|
|
||||||
|
|
||||||
bytes_read += bytes_to_read;
|
mbedtls_aes_context aes_context;
|
||||||
bytes_written += bytes_to_read;
|
mbedtls_aes_setkey_dec(&aes_context, partition_entry.partition_key.data(), 128);
|
||||||
if (!PadTo4(outfile, &bytes_written))
|
|
||||||
return ConversionResult::WriteFailed;
|
|
||||||
|
|
||||||
if (!run_callback(i))
|
for (u32 i = first_group; i < last_group; ++i)
|
||||||
return ConversionResult::Canceled;
|
{
|
||||||
|
const u64 bytes_to_read = std::min<u64>(chunk_size, data_offset + data_size - bytes_read);
|
||||||
|
|
||||||
|
ASSERT(bytes_to_read % VolumeWii::BLOCK_TOTAL_SIZE == 0);
|
||||||
|
const u64 bytes_to_write =
|
||||||
|
bytes_to_read / VolumeWii::BLOCK_TOTAL_SIZE * VolumeWii::BLOCK_DATA_SIZE;
|
||||||
|
|
||||||
|
if (!infile->Read(bytes_read, bytes_to_read, buffer.data()))
|
||||||
|
return ConversionResult::ReadFailed;
|
||||||
|
|
||||||
|
const u64 exception_lists = Common::AlignUp(bytes_to_read, VolumeWii::GROUP_TOTAL_SIZE) /
|
||||||
|
VolumeWii::GROUP_TOTAL_SIZE;
|
||||||
|
|
||||||
|
const u64 exceptions_size = Common::AlignUp(exception_lists * sizeof(u16), 4);
|
||||||
|
const u64 total_size = exceptions_size + bytes_to_write;
|
||||||
|
ASSERT((bytes_written & 3) == 0);
|
||||||
|
group_entries[i].data_offset = Common::swap32(static_cast<u32>(bytes_written >> 2));
|
||||||
|
group_entries[i].data_size = Common::swap32(static_cast<u32>(total_size));
|
||||||
|
|
||||||
|
for (u64 j = 0; j < exception_lists; ++j)
|
||||||
|
{
|
||||||
|
const u16 exceptions = 0;
|
||||||
|
if (!outfile->WriteArray(&exceptions, 1))
|
||||||
|
return ConversionResult::WriteFailed;
|
||||||
|
bytes_written += sizeof(u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PadTo4(outfile, &bytes_written))
|
||||||
|
return ConversionResult::WriteFailed;
|
||||||
|
|
||||||
|
for (u64 j = 0; j < bytes_to_read; j += VolumeWii::BLOCK_TOTAL_SIZE)
|
||||||
|
{
|
||||||
|
VolumeWii::DecryptBlockData(buffer.data() + j, decryption_buffer.data(), &aes_context);
|
||||||
|
if (!outfile->WriteArray(decryption_buffer.data(), VolumeWii::BLOCK_DATA_SIZE))
|
||||||
|
return ConversionResult::WriteFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_read += bytes_to_read;
|
||||||
|
bytes_written += bytes_to_write;
|
||||||
|
++groups_written;
|
||||||
|
if (!PadTo4(outfile, &bytes_written))
|
||||||
|
return ConversionResult::WriteFailed;
|
||||||
|
|
||||||
|
if (!run_callback())
|
||||||
|
return ConversionResult::Canceled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const RawDataEntry& raw_data_entry = raw_data_entries[data_entry.index];
|
||||||
|
|
||||||
|
const u32 first_group = Common::swap32(raw_data_entry.group_index);
|
||||||
|
const u32 last_group = first_group + Common::swap32(raw_data_entry.number_of_groups);
|
||||||
|
|
||||||
|
u64 data_offset = Common::swap64(raw_data_entry.data_offset);
|
||||||
|
u64 data_size = Common::swap64(raw_data_entry.data_size);
|
||||||
|
|
||||||
|
const u64 skipped_data = data_offset % VolumeWii::BLOCK_TOTAL_SIZE;
|
||||||
|
data_offset -= skipped_data;
|
||||||
|
data_size += skipped_data;
|
||||||
|
|
||||||
|
ASSERT(groups_written == first_group);
|
||||||
|
ASSERT(bytes_read == data_offset);
|
||||||
|
|
||||||
|
for (u32 i = first_group; i < last_group; ++i)
|
||||||
|
{
|
||||||
|
const u64 bytes_to_read = std::min<u64>(chunk_size, data_offset + data_size - bytes_read);
|
||||||
|
|
||||||
|
if (bytes_written >> 2 > std::numeric_limits<u32>::max())
|
||||||
|
return ConversionResult::InternalError;
|
||||||
|
|
||||||
|
ASSERT((bytes_written & 3) == 0);
|
||||||
|
group_entries[i].data_offset = Common::swap32(static_cast<u32>(bytes_written >> 2));
|
||||||
|
group_entries[i].data_size = Common::swap32(static_cast<u32>(bytes_to_read));
|
||||||
|
|
||||||
|
if (!infile->Read(bytes_read, bytes_to_read, buffer.data()))
|
||||||
|
return ConversionResult::ReadFailed;
|
||||||
|
if (!outfile->WriteArray(buffer.data(), bytes_to_read))
|
||||||
|
return ConversionResult::WriteFailed;
|
||||||
|
|
||||||
|
bytes_read += bytes_to_read;
|
||||||
|
bytes_written += bytes_to_read;
|
||||||
|
++groups_written;
|
||||||
|
if (!PadTo4(outfile, &bytes_written))
|
||||||
|
return ConversionResult::WriteFailed;
|
||||||
|
|
||||||
|
if (!run_callback())
|
||||||
|
return ConversionResult::Canceled;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASSERT(groups_written == total_groups);
|
||||||
|
ASSERT(bytes_read == iso_size);
|
||||||
|
|
||||||
const u64 partition_entries_offset = bytes_written;
|
const u64 partition_entries_offset = bytes_written;
|
||||||
const u64 partition_entries_size = partition_entries.size() * sizeof(PartitionEntry);
|
const u64 partition_entries_size = partition_entries.size() * sizeof(PartitionEntry);
|
||||||
if (!outfile->WriteArray(partition_entries.data(), partition_entries.size()))
|
if (!outfile->WriteArray(partition_entries.data(), partition_entries.size()))
|
||||||
|
@ -1002,7 +1264,16 @@ WIAFileReader::ConversionResult WIAFileReader::ConvertToWIA(BlobReader* infile,
|
||||||
if (!PadTo4(outfile, &bytes_written))
|
if (!PadTo4(outfile, &bytes_written))
|
||||||
return ConversionResult::WriteFailed;
|
return ConversionResult::WriteFailed;
|
||||||
|
|
||||||
header_2.disc_type = 0; // TODO
|
u32 disc_type = 0;
|
||||||
|
if (infile_volume)
|
||||||
|
{
|
||||||
|
if (infile_volume->GetVolumeType() == Platform::GameCubeDisc)
|
||||||
|
disc_type = 1;
|
||||||
|
else if (infile_volume->GetVolumeType() == Platform::WiiDisc)
|
||||||
|
disc_type = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
header_2.disc_type = Common::swap32(disc_type);
|
||||||
header_2.compression_type = Common::swap32(static_cast<u32>(CompressionType::None));
|
header_2.compression_type = Common::swap32(static_cast<u32>(CompressionType::None));
|
||||||
header_2.compression_level = 0;
|
header_2.compression_level = 0;
|
||||||
header_2.chunk_size = Common::swap32(static_cast<u32>(chunk_size));
|
header_2.chunk_size = Common::swap32(static_cast<u32>(chunk_size));
|
||||||
|
@ -1059,8 +1330,10 @@ bool ConvertToWIA(BlobReader* infile, const std::string& infile_path,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<VolumeDisc> infile_volume = CreateDisc(infile_path);
|
||||||
|
|
||||||
WIAFileReader::ConversionResult result =
|
WIAFileReader::ConversionResult result =
|
||||||
WIAFileReader::ConvertToWIA(infile, &outfile, chunk_size, callback, arg);
|
WIAFileReader::ConvertToWIA(infile, infile_volume.get(), &outfile, chunk_size, callback, arg);
|
||||||
|
|
||||||
if (result == WIAFileReader::ConversionResult::ReadFailed)
|
if (result == WIAFileReader::ConversionResult::ReadFailed)
|
||||||
PanicAlertT("Failed to read from the input file \"%s\".", infile_path.c_str());
|
PanicAlertT("Failed to read from the input file \"%s\".", infile_path.c_str());
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
|
|
||||||
namespace DiscIO
|
namespace DiscIO
|
||||||
{
|
{
|
||||||
|
class VolumeDisc;
|
||||||
|
|
||||||
constexpr u32 WIA_MAGIC = 0x01414957; // "WIA\x1" (byteswapped to little endian)
|
constexpr u32 WIA_MAGIC = 0x01414957; // "WIA\x1" (byteswapped to little endian)
|
||||||
|
|
||||||
class WIAFileReader : public BlobReader
|
class WIAFileReader : public BlobReader
|
||||||
|
@ -53,8 +55,9 @@ public:
|
||||||
InternalError,
|
InternalError,
|
||||||
};
|
};
|
||||||
|
|
||||||
static ConversionResult ConvertToWIA(BlobReader* infile, File::IOFile* outfile, int chunk_size,
|
static ConversionResult ConvertToWIA(BlobReader* infile, const VolumeDisc* infile_volume,
|
||||||
CompressCB callback, void* arg);
|
File::IOFile* outfile, int chunk_size, CompressCB callback,
|
||||||
|
void* arg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using SHA1 = std::array<u8, 20>;
|
using SHA1 = std::array<u8, 20>;
|
||||||
|
@ -159,6 +162,20 @@ private:
|
||||||
static_assert(sizeof(PurgeSegment) == 0x08, "Wrong size for WIA purge segment");
|
static_assert(sizeof(PurgeSegment) == 0x08, "Wrong size for WIA purge segment");
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
struct DataEntry
|
||||||
|
{
|
||||||
|
u32 index;
|
||||||
|
bool is_partition;
|
||||||
|
u8 partition_data_index;
|
||||||
|
|
||||||
|
DataEntry(size_t index_) : index(static_cast<u32>(index_)), is_partition(false) {}
|
||||||
|
DataEntry(size_t index_, size_t partition_data_index_)
|
||||||
|
: index(static_cast<u32>(index_)), is_partition(true),
|
||||||
|
partition_data_index(static_cast<u8>(partition_data_index_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct DecompressionBuffer
|
struct DecompressionBuffer
|
||||||
{
|
{
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
|
@ -288,6 +305,18 @@ private:
|
||||||
static std::string VersionToString(u32 version);
|
static std::string VersionToString(u32 version);
|
||||||
|
|
||||||
static bool PadTo4(File::IOFile* file, u64* bytes_written);
|
static bool PadTo4(File::IOFile* file, u64* bytes_written);
|
||||||
|
static void AddRawDataEntry(u64 offset, u64 size, int chunk_size, u32* total_groups,
|
||||||
|
std::vector<RawDataEntry>* raw_data_entries,
|
||||||
|
std::vector<DataEntry>* data_entries);
|
||||||
|
static PartitionDataEntry
|
||||||
|
CreatePartitionDataEntry(u64 offset, u64 size, u32 index, int chunk_size, u32* total_groups,
|
||||||
|
const std::vector<PartitionEntry>& partition_entries,
|
||||||
|
std::vector<DataEntry>* data_entries);
|
||||||
|
static ConversionResult SetUpDataEntriesForWriting(const VolumeDisc* volume, int chunk_size,
|
||||||
|
u64 iso_size, u32* total_groups,
|
||||||
|
std::vector<PartitionEntry>* partition_entries,
|
||||||
|
std::vector<RawDataEntry>* raw_data_entries,
|
||||||
|
std::vector<DataEntry>* data_entries);
|
||||||
|
|
||||||
bool m_valid;
|
bool m_valid;
|
||||||
CompressionType m_compression_type;
|
CompressionType m_compression_type;
|
||||||
|
@ -303,20 +332,6 @@ private:
|
||||||
std::vector<RawDataEntry> m_raw_data_entries;
|
std::vector<RawDataEntry> m_raw_data_entries;
|
||||||
std::vector<GroupEntry> m_group_entries;
|
std::vector<GroupEntry> m_group_entries;
|
||||||
|
|
||||||
struct DataEntry
|
|
||||||
{
|
|
||||||
u32 index;
|
|
||||||
bool is_partition;
|
|
||||||
u8 partition_data_index;
|
|
||||||
|
|
||||||
DataEntry(size_t index_) : index(static_cast<u32>(index_)), is_partition(false) {}
|
|
||||||
DataEntry(size_t index_, size_t partition_data_index_)
|
|
||||||
: index(static_cast<u32>(index_)), is_partition(true),
|
|
||||||
partition_data_index(static_cast<u8>(partition_data_index_))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<u64, DataEntry> m_data_entries;
|
std::map<u64, DataEntry> m_data_entries;
|
||||||
|
|
||||||
static constexpr u32 WIA_VERSION = 0x01000000;
|
static constexpr u32 WIA_VERSION = 0x01000000;
|
||||||
|
|
Loading…
Reference in New Issue