DirectoryBlob: Prepare for supporting multiple partitions
This commit is contained in:
parent
953ca9cee1
commit
ba0ee3f54b
|
@ -156,14 +156,13 @@ std::unique_ptr<DirectoryBlobReader> DirectoryBlobReader::Create(const std::stri
|
||||||
}
|
}
|
||||||
|
|
||||||
DirectoryBlobReader::DirectoryBlobReader(const std::string& root_directory)
|
DirectoryBlobReader::DirectoryBlobReader(const std::string& root_directory)
|
||||||
: m_root_directory(root_directory)
|
: m_root_directory(root_directory), m_game_partition(root_directory)
|
||||||
{
|
{
|
||||||
SetDiscHeaderAndDiscType();
|
m_is_wii = m_game_partition.IsWii();
|
||||||
SetBI2();
|
|
||||||
BuildFST(SetDOL(SetApploader()));
|
|
||||||
|
|
||||||
if (m_is_wii)
|
if (m_is_wii)
|
||||||
{
|
{
|
||||||
|
SetNonpartitionDiscHeader(m_game_partition.GetHeader());
|
||||||
SetPartitionTable();
|
SetPartitionTable();
|
||||||
SetWiiRegionData();
|
SetWiiRegionData();
|
||||||
SetTMDAndTicket();
|
SetTMDAndTicket();
|
||||||
|
@ -206,7 +205,8 @@ bool DirectoryBlobReader::Read(u64 offset, u64 length, u8* buffer)
|
||||||
{
|
{
|
||||||
// TODO: We don't handle raw access to the encrypted area of Wii discs correctly.
|
// TODO: We don't handle raw access to the encrypted area of Wii discs correctly.
|
||||||
|
|
||||||
return ReadInternal(offset, length, buffer, m_is_wii ? m_nonpartition_contents : m_virtual_disc);
|
return ReadInternal(offset, length, buffer,
|
||||||
|
m_is_wii ? m_nonpartition_contents : m_game_partition.GetContents());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirectoryBlobReader::SupportsReadWiiDecrypted() const
|
bool DirectoryBlobReader::SupportsReadWiiDecrypted() const
|
||||||
|
@ -219,7 +219,7 @@ bool DirectoryBlobReader::ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64
|
||||||
if (!m_is_wii || partition_offset != GAME_PARTITION_ADDRESS)
|
if (!m_is_wii || partition_offset != GAME_PARTITION_ADDRESS)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return ReadInternal(offset, size, buffer, m_virtual_disc);
|
return ReadInternal(offset, size, buffer, m_game_partition.GetContents());
|
||||||
}
|
}
|
||||||
|
|
||||||
BlobType DirectoryBlobReader::GetBlobType() const
|
BlobType DirectoryBlobReader::GetBlobType() const
|
||||||
|
@ -239,18 +239,25 @@ u64 DirectoryBlobReader::GetDataSize() const
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectoryBlobReader::SetDiscHeaderAndDiscType()
|
DirectoryBlobPartition::DirectoryBlobPartition(const std::string& root_directory)
|
||||||
|
: m_root_directory(root_directory)
|
||||||
|
{
|
||||||
|
SetDiscHeaderAndDiscType();
|
||||||
|
SetBI2();
|
||||||
|
BuildFST(SetDOL(SetApploader()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectoryBlobPartition::SetDiscHeaderAndDiscType()
|
||||||
{
|
{
|
||||||
constexpr u64 DISKHEADER_ADDRESS = 0;
|
constexpr u64 DISKHEADER_ADDRESS = 0;
|
||||||
constexpr u64 DISKHEADER_SIZE = 0x440;
|
constexpr u64 DISKHEADER_SIZE = 0x440;
|
||||||
constexpr u64 NONPARTITION_DISKHEADER_SIZE = 0x100;
|
|
||||||
|
|
||||||
m_disk_header.resize(DISKHEADER_SIZE);
|
m_disk_header.resize(DISKHEADER_SIZE);
|
||||||
const std::string boot_bin_path = m_root_directory + "sys/boot.bin";
|
const std::string boot_bin_path = m_root_directory + "sys/boot.bin";
|
||||||
if (ReadFileToVector(boot_bin_path, &m_disk_header) < 0x20)
|
if (ReadFileToVector(boot_bin_path, &m_disk_header) < 0x20)
|
||||||
ERROR_LOG(DISCIO, "%s doesn't exist or is too small", boot_bin_path.c_str());
|
ERROR_LOG(DISCIO, "%s doesn't exist or is too small", boot_bin_path.c_str());
|
||||||
|
|
||||||
m_virtual_disc.emplace(DISKHEADER_ADDRESS, DISKHEADER_SIZE, m_disk_header.data());
|
m_contents.emplace(DISKHEADER_ADDRESS, DISKHEADER_SIZE, m_disk_header.data());
|
||||||
|
|
||||||
m_is_wii = Common::swap32(&m_disk_header[0x18]) == 0x5d1c9ea3;
|
m_is_wii = Common::swap32(&m_disk_header[0x18]) == 0x5d1c9ea3;
|
||||||
const bool is_gc = Common::swap32(&m_disk_header[0x1c]) == 0xc2339f3d;
|
const bool is_gc = Common::swap32(&m_disk_header[0x1c]) == 0xc2339f3d;
|
||||||
|
@ -258,34 +265,37 @@ void DirectoryBlobReader::SetDiscHeaderAndDiscType()
|
||||||
ERROR_LOG(DISCIO, "Couldn't detect disc type based on %s", boot_bin_path.c_str());
|
ERROR_LOG(DISCIO, "Couldn't detect disc type based on %s", boot_bin_path.c_str());
|
||||||
|
|
||||||
m_address_shift = m_is_wii ? 2 : 0;
|
m_address_shift = m_is_wii ? 2 : 0;
|
||||||
|
|
||||||
if (m_is_wii)
|
|
||||||
{
|
|
||||||
m_disk_header_nonpartition.resize(NONPARTITION_DISKHEADER_SIZE);
|
|
||||||
const size_t header_bin_bytes_read =
|
|
||||||
ReadFileToVector(m_root_directory + "disc/header.bin", &m_disk_header_nonpartition);
|
|
||||||
|
|
||||||
// If header.bin is missing or smaller than expected, use the content of sys/boot.bin instead
|
|
||||||
std::copy(m_disk_header.data() + header_bin_bytes_read,
|
|
||||||
m_disk_header.data() + m_disk_header_nonpartition.size(),
|
|
||||||
m_disk_header_nonpartition.data() + header_bin_bytes_read);
|
|
||||||
|
|
||||||
// 0x60 and 0x61 are the only differences between the partition and non-partition headers
|
|
||||||
if (header_bin_bytes_read < 0x60)
|
|
||||||
m_disk_header_nonpartition[0x60] = 0;
|
|
||||||
if (header_bin_bytes_read < 0x61)
|
|
||||||
m_disk_header_nonpartition[0x61] = 0;
|
|
||||||
|
|
||||||
m_nonpartition_contents.emplace(DISKHEADER_ADDRESS, NONPARTITION_DISKHEADER_SIZE,
|
|
||||||
m_disk_header_nonpartition.data());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectoryBlobReader::SetBI2()
|
void DirectoryBlobReader::SetNonpartitionDiscHeader(const std::vector<u8>& partition_header)
|
||||||
|
{
|
||||||
|
constexpr u64 NONPARTITION_DISKHEADER_ADDRESS = 0;
|
||||||
|
constexpr u64 NONPARTITION_DISKHEADER_SIZE = 0x100;
|
||||||
|
|
||||||
|
m_disk_header_nonpartition.resize(NONPARTITION_DISKHEADER_SIZE);
|
||||||
|
const size_t header_bin_bytes_read =
|
||||||
|
ReadFileToVector(m_root_directory + "disc/header.bin", &m_disk_header_nonpartition);
|
||||||
|
|
||||||
|
// 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_disk_header_nonpartition.size(),
|
||||||
|
m_disk_header_nonpartition.data() + header_bin_bytes_read);
|
||||||
|
|
||||||
|
// 0x60 and 0x61 are the only differences between the partition and non-partition headers
|
||||||
|
if (header_bin_bytes_read < 0x60)
|
||||||
|
m_disk_header_nonpartition[0x60] = 0;
|
||||||
|
if (header_bin_bytes_read < 0x61)
|
||||||
|
m_disk_header_nonpartition[0x61] = 0;
|
||||||
|
|
||||||
|
m_nonpartition_contents.emplace(NONPARTITION_DISKHEADER_ADDRESS, NONPARTITION_DISKHEADER_SIZE,
|
||||||
|
m_disk_header_nonpartition.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectoryBlobPartition::SetBI2()
|
||||||
{
|
{
|
||||||
constexpr u64 BI2_ADDRESS = 0x440;
|
constexpr u64 BI2_ADDRESS = 0x440;
|
||||||
constexpr u64 BI2_SIZE = 0x2000;
|
constexpr u64 BI2_SIZE = 0x2000;
|
||||||
AddFileToContents(&m_virtual_disc, m_root_directory + "sys/bi2.bin", BI2_ADDRESS, BI2_SIZE);
|
AddFileToContents(&m_contents, m_root_directory + "sys/bi2.bin", BI2_ADDRESS, BI2_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectoryBlobReader::SetPartitionTable()
|
void DirectoryBlobReader::SetPartitionTable()
|
||||||
|
@ -331,13 +341,12 @@ void DirectoryBlobReader::SetTMDAndTicket()
|
||||||
GAME_PARTITION_ADDRESS + TICKET_OFFSET, TICKET_SIZE);
|
GAME_PARTITION_ADDRESS + TICKET_OFFSET, TICKET_SIZE);
|
||||||
const DiscContent& tmd = AddFileToContents(&m_nonpartition_contents, m_root_directory + "tmd.bin",
|
const DiscContent& tmd = AddFileToContents(&m_nonpartition_contents, m_root_directory + "tmd.bin",
|
||||||
GAME_PARTITION_ADDRESS + TMD_OFFSET, MAX_TMD_SIZE);
|
GAME_PARTITION_ADDRESS + TMD_OFFSET, MAX_TMD_SIZE);
|
||||||
m_tmd_header = {Common::swap32(static_cast<u32>(tmd.GetSize())),
|
m_tmd_header = {Common::swap32(static_cast<u32>(tmd.GetSize())), Common::swap32(TMD_OFFSET >> 2)};
|
||||||
Common::swap32(TMD_OFFSET >> m_address_shift)};
|
|
||||||
m_nonpartition_contents.emplace(GAME_PARTITION_ADDRESS + TICKET_SIZE, sizeof(m_tmd_header),
|
m_nonpartition_contents.emplace(GAME_PARTITION_ADDRESS + TICKET_SIZE, sizeof(m_tmd_header),
|
||||||
reinterpret_cast<const u8*>(&m_tmd_header));
|
reinterpret_cast<const u8*>(&m_tmd_header));
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 DirectoryBlobReader::SetApploader()
|
u64 DirectoryBlobPartition::SetApploader()
|
||||||
{
|
{
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
|
@ -367,16 +376,16 @@ u64 DirectoryBlobReader::SetApploader()
|
||||||
|
|
||||||
constexpr u64 APPLOADER_ADDRESS = 0x2440;
|
constexpr u64 APPLOADER_ADDRESS = 0x2440;
|
||||||
|
|
||||||
m_virtual_disc.emplace(APPLOADER_ADDRESS, m_apploader.size(), m_apploader.data());
|
m_contents.emplace(APPLOADER_ADDRESS, m_apploader.size(), m_apploader.data());
|
||||||
|
|
||||||
// Return DOL address, 32 byte aligned (plus 32 byte padding)
|
// Return DOL address, 32 byte aligned (plus 32 byte padding)
|
||||||
return Common::AlignUp(APPLOADER_ADDRESS + m_apploader.size() + 0x20, 0x20ull);
|
return Common::AlignUp(APPLOADER_ADDRESS + m_apploader.size() + 0x20, 0x20ull);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 DirectoryBlobReader::SetDOL(u64 dol_address)
|
u64 DirectoryBlobPartition::SetDOL(u64 dol_address)
|
||||||
{
|
{
|
||||||
const DiscContent& dol =
|
const DiscContent& dol =
|
||||||
AddFileToContents(&m_virtual_disc, m_root_directory + "sys/main.dol", dol_address);
|
AddFileToContents(&m_contents, m_root_directory + "sys/main.dol", dol_address);
|
||||||
|
|
||||||
Write32(static_cast<u32>(dol_address >> m_address_shift), 0x0420, &m_disk_header);
|
Write32(static_cast<u32>(dol_address >> m_address_shift), 0x0420, &m_disk_header);
|
||||||
|
|
||||||
|
@ -384,7 +393,7 @@ u64 DirectoryBlobReader::SetDOL(u64 dol_address)
|
||||||
return Common::AlignUp(dol_address + dol.GetSize() + 0x20, 0x20ull);
|
return Common::AlignUp(dol_address + dol.GetSize() + 0x20, 0x20ull);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectoryBlobReader::BuildFST(u64 fst_address)
|
void DirectoryBlobPartition::BuildFST(u64 fst_address)
|
||||||
{
|
{
|
||||||
m_fst_data.clear();
|
m_fst_data.clear();
|
||||||
|
|
||||||
|
@ -419,11 +428,11 @@ void DirectoryBlobReader::BuildFST(u64 fst_address)
|
||||||
Write32((u32)(m_fst_data.size() >> m_address_shift), 0x0428, &m_disk_header);
|
Write32((u32)(m_fst_data.size() >> m_address_shift), 0x0428, &m_disk_header);
|
||||||
Write32((u32)(m_fst_data.size() >> m_address_shift), 0x042c, &m_disk_header);
|
Write32((u32)(m_fst_data.size() >> m_address_shift), 0x042c, &m_disk_header);
|
||||||
|
|
||||||
m_virtual_disc.emplace(fst_address, m_fst_data.size(), m_fst_data.data());
|
m_contents.emplace(fst_address, m_fst_data.size(), m_fst_data.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectoryBlobReader::WriteEntryData(u32* entry_offset, u8 type, u32 name_offset,
|
void DirectoryBlobPartition::WriteEntryData(u32* entry_offset, u8 type, u32 name_offset,
|
||||||
u64 data_offset, u64 length, u32 address_shift)
|
u64 data_offset, u64 length, u32 address_shift)
|
||||||
{
|
{
|
||||||
m_fst_data[(*entry_offset)++] = type;
|
m_fst_data[(*entry_offset)++] = type;
|
||||||
|
|
||||||
|
@ -438,17 +447,17 @@ void DirectoryBlobReader::WriteEntryData(u32* entry_offset, u8 type, u32 name_of
|
||||||
*entry_offset += 4;
|
*entry_offset += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectoryBlobReader::WriteEntryName(u32* name_offset, const std::string& name,
|
void DirectoryBlobPartition::WriteEntryName(u32* name_offset, const std::string& name,
|
||||||
u64 name_table_offset)
|
u64 name_table_offset)
|
||||||
{
|
{
|
||||||
strncpy((char*)&m_fst_data[*name_offset + name_table_offset], name.c_str(), name.length() + 1);
|
strncpy((char*)&m_fst_data[*name_offset + name_table_offset], name.c_str(), name.length() + 1);
|
||||||
|
|
||||||
*name_offset += (u32)(name.length() + 1);
|
*name_offset += (u32)(name.length() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirectoryBlobReader::WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset,
|
void DirectoryBlobPartition::WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset,
|
||||||
u32* name_offset, u64* data_offset, u32 parent_entry_index,
|
u32* name_offset, u64* data_offset,
|
||||||
u64 name_table_offset)
|
u32 parent_entry_index, u64 name_table_offset)
|
||||||
{
|
{
|
||||||
std::vector<File::FSTEntry> sorted_entries = parent_entry.children;
|
std::vector<File::FSTEntry> sorted_entries = parent_entry.children;
|
||||||
|
|
||||||
|
@ -479,7 +488,7 @@ void DirectoryBlobReader::WriteDirectory(const File::FSTEntry& parent_entry, u32
|
||||||
WriteEntryName(name_offset, entry.virtualName, name_table_offset);
|
WriteEntryName(name_offset, entry.virtualName, name_table_offset);
|
||||||
|
|
||||||
// write entry to virtual disc
|
// write entry to virtual disc
|
||||||
auto result = m_virtual_disc.emplace(*data_offset, entry.size, entry.physicalName);
|
auto result = m_contents.emplace(*data_offset, entry.size, entry.physicalName);
|
||||||
_dbg_assert_(DISCIO, result.second); // Check that this offset wasn't already occupied
|
_dbg_assert_(DISCIO, result.second); // Check that this offset wasn't already occupied
|
||||||
|
|
||||||
// 32 KiB aligned - many games are fine with less alignment, but not all
|
// 32 KiB aligned - many games are fine with less alignment, but not all
|
||||||
|
|
|
@ -52,6 +52,49 @@ private:
|
||||||
ContentSource m_content_source;
|
ContentSource m_content_source;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DirectoryBlobPartition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit DirectoryBlobPartition(const std::string& root_directory);
|
||||||
|
|
||||||
|
// We do not allow copying, because it might mess up the pointers inside DiscContents
|
||||||
|
DirectoryBlobPartition(const DirectoryBlobPartition&) = delete;
|
||||||
|
DirectoryBlobPartition& operator=(const DirectoryBlobPartition&) = delete;
|
||||||
|
DirectoryBlobPartition(DirectoryBlobPartition&&) = default;
|
||||||
|
DirectoryBlobPartition& operator=(DirectoryBlobPartition&&) = default;
|
||||||
|
|
||||||
|
bool IsWii() const { return m_is_wii; }
|
||||||
|
const std::vector<u8>& GetHeader() const { return m_disk_header; }
|
||||||
|
const std::set<DiscContent>& GetContents() const { return m_contents; }
|
||||||
|
private:
|
||||||
|
void SetDiscHeaderAndDiscType();
|
||||||
|
void SetBI2();
|
||||||
|
|
||||||
|
// Returns DOL address
|
||||||
|
u64 SetApploader();
|
||||||
|
// Returns FST address
|
||||||
|
u64 SetDOL(u64 dol_address);
|
||||||
|
|
||||||
|
void BuildFST(u64 fst_address);
|
||||||
|
|
||||||
|
// FST creation
|
||||||
|
void WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, u64 data_offset, u64 length,
|
||||||
|
u32 address_shift);
|
||||||
|
void WriteEntryName(u32* name_offset, const std::string& name, u64 name_table_offset);
|
||||||
|
void WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset, u32* name_offset,
|
||||||
|
u64* data_offset, u32 parent_entry_index, u64 name_table_offset);
|
||||||
|
|
||||||
|
std::set<DiscContent> m_contents;
|
||||||
|
std::vector<u8> m_disk_header;
|
||||||
|
std::vector<u8> m_apploader;
|
||||||
|
std::vector<u8> m_fst_data;
|
||||||
|
|
||||||
|
std::string m_root_directory;
|
||||||
|
bool m_is_wii;
|
||||||
|
// GameCube has no shift, Wii has 2 bit shift
|
||||||
|
u32 m_address_shift;
|
||||||
|
};
|
||||||
|
|
||||||
class DirectoryBlobReader : public BlobReader
|
class DirectoryBlobReader : public BlobReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -76,39 +119,18 @@ private:
|
||||||
|
|
||||||
bool ReadInternal(u64 offset, u64 length, u8* buffer, const std::set<DiscContent>& contents);
|
bool ReadInternal(u64 offset, u64 length, u8* buffer, const std::set<DiscContent>& contents);
|
||||||
|
|
||||||
void SetDiscHeaderAndDiscType();
|
void SetNonpartitionDiscHeader(const std::vector<u8>& partition_header);
|
||||||
void SetBI2();
|
|
||||||
void SetPartitionTable();
|
void SetPartitionTable();
|
||||||
void SetWiiRegionData();
|
void SetWiiRegionData();
|
||||||
void SetTMDAndTicket();
|
void SetTMDAndTicket();
|
||||||
|
|
||||||
// Returns DOL address
|
|
||||||
u64 SetApploader();
|
|
||||||
// Returns FST address
|
|
||||||
u64 SetDOL(u64 dol_address);
|
|
||||||
|
|
||||||
void BuildFST(u64 fst_address);
|
|
||||||
|
|
||||||
// FST creation
|
|
||||||
void WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, u64 data_offset, u64 length,
|
|
||||||
u32 address_shift);
|
|
||||||
void WriteEntryName(u32* name_offset, const std::string& name, u64 name_table_offset);
|
|
||||||
void WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset, u32* name_offset,
|
|
||||||
u64* data_offset, u32 parent_entry_index, u64 name_table_offset);
|
|
||||||
|
|
||||||
std::string m_root_directory;
|
std::string m_root_directory;
|
||||||
|
|
||||||
std::set<DiscContent> m_virtual_disc;
|
DirectoryBlobPartition m_game_partition;
|
||||||
std::set<DiscContent> m_nonpartition_contents;
|
std::set<DiscContent> m_nonpartition_contents;
|
||||||
|
|
||||||
bool m_is_wii;
|
bool m_is_wii;
|
||||||
|
|
||||||
// GameCube has no shift, Wii has 2 bit shift
|
|
||||||
u32 m_address_shift;
|
|
||||||
|
|
||||||
std::vector<u8> m_fst_data;
|
|
||||||
|
|
||||||
std::vector<u8> m_disk_header;
|
|
||||||
std::vector<u8> m_disk_header_nonpartition;
|
std::vector<u8> m_disk_header_nonpartition;
|
||||||
std::vector<u8> m_wii_region_data;
|
std::vector<u8> m_wii_region_data;
|
||||||
|
|
||||||
|
@ -120,8 +142,6 @@ private:
|
||||||
} m_tmd_header;
|
} m_tmd_header;
|
||||||
static_assert(sizeof(TMDHeader) == 8, "Wrong size for TMDHeader");
|
static_assert(sizeof(TMDHeader) == 8, "Wrong size for TMDHeader");
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
std::vector<u8> m_apploader;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
Loading…
Reference in New Issue