mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 04:26:01 -10:00
DirectoryBlob: content chunks in one sorted array
A disc's content chunks sit in one malloc'd array sorted by end offset and found by binary search, in place of a std::set walked node by node. A chunk is plain data - offset, size, kind, source offset - read through a switch rather than a std::variant visited per read. Where a chunk's bytes come from is a small C struct; the bytes it needs (a host file's path, a run of memory) live in one reference-counted allocation shared by every chunk and copy that refers to them, in place of a std::string per chunk and a shared_ptr to a std::vector. Splitting a memory chunk for a Riivolution patch moves its offset into the shared bytes instead of copying the tail into a new vector. The reference counts and the open-file slots' flags are retro_atomic.
This commit is contained in:
1 parent
09cac91415
commit
a9e048fee2
3 files changed
+436
-215
No files matched your search
@@ -56,136 +56,312 @@ constexpr u8 ENTRY_SIZE = 0x0c;
|
||||
constexpr u8 FILE_ENTRY = 0;
|
||||
constexpr u8 DIRECTORY_ENTRY = 1;
|
||||
|
||||
DiscContent::DiscContent(u64 offset, u64 size, ContentSource source)
|
||||
: m_offset(offset), m_size(size), m_content_source(std::move(source))
|
||||
ContentBlob* ContentBlobAlloc(size_t size)
|
||||
{
|
||||
ContentBlob* blob = static_cast<ContentBlob*>(std::malloc(CONTENT_BLOB_HEADER + size));
|
||||
if (!blob)
|
||||
return nullptr;
|
||||
retro_atomic_int_init(&blob->refs, 1);
|
||||
blob->size = size;
|
||||
return blob;
|
||||
}
|
||||
|
||||
ContentBlob* ContentBlobCreate(const void* data, size_t size)
|
||||
{
|
||||
ContentBlob* blob = ContentBlobAlloc(size);
|
||||
if (blob && size)
|
||||
std::memcpy(ContentBlobData(blob), data, size);
|
||||
return blob;
|
||||
}
|
||||
|
||||
void ContentBlobRetain(ContentBlob* blob)
|
||||
{
|
||||
retro_atomic_inc_int(&blob->refs);
|
||||
}
|
||||
|
||||
void ContentBlobRelease(ContentBlob* blob)
|
||||
{
|
||||
if (retro_atomic_fetch_sub_int(&blob->refs, 1) == 1)
|
||||
std::free(blob);
|
||||
}
|
||||
|
||||
ContentSource ContentFile(const char* path, u64 offset)
|
||||
{
|
||||
ContentSource source;
|
||||
source.type = CONTENT_FILE;
|
||||
source.offset = offset;
|
||||
source.blob = ContentBlobCreate(path, std::strlen(path) + 1);
|
||||
return source;
|
||||
}
|
||||
|
||||
ContentSource ContentMemory(ContentBlob* blob, u64 offset)
|
||||
{
|
||||
ContentSource source;
|
||||
source.type = CONTENT_MEMORY;
|
||||
source.offset = offset;
|
||||
source.blob = blob;
|
||||
return source;
|
||||
}
|
||||
|
||||
ContentSource ContentBytes(const void* data, size_t size)
|
||||
{
|
||||
return ContentMemory(ContentBlobCreate(data, size), 0);
|
||||
}
|
||||
|
||||
ContentSource ContentPartition(u64 offset, u64 partition_data_offset)
|
||||
{
|
||||
ContentSource source;
|
||||
source.type = CONTENT_PARTITION;
|
||||
source.offset = offset;
|
||||
source.partition_data_offset = partition_data_offset;
|
||||
return source;
|
||||
}
|
||||
|
||||
ContentSource ContentVolume(u64 offset, const Partition& partition)
|
||||
{
|
||||
ContentSource source;
|
||||
source.type = CONTENT_VOLUME;
|
||||
source.offset = offset;
|
||||
source.partition = partition;
|
||||
return source;
|
||||
}
|
||||
|
||||
ContentSource ContentFixedByte(u8 byte)
|
||||
{
|
||||
ContentSource source;
|
||||
source.type = CONTENT_FIXED_BYTE;
|
||||
source.fixed_byte = byte;
|
||||
return source;
|
||||
}
|
||||
|
||||
DiscContentContainer::DiscContentContainer() : m_items(nullptr), m_count(0), m_capacity(0)
|
||||
{
|
||||
}
|
||||
|
||||
DiscContent::DiscContent(u64 offset) : m_offset(offset)
|
||||
DiscContentContainer::~DiscContentContainer()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
u64 DiscContent::GetOffset() const
|
||||
void DiscContentContainer::Clear()
|
||||
{
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
u64 DiscContent::GetEndOffset() const
|
||||
{
|
||||
return m_offset + m_size;
|
||||
}
|
||||
|
||||
u64 DiscContent::GetSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
bool DiscContent::Read(u64* offset, u64* length, u8** buffer, DirectoryBlobReader* blob) const
|
||||
{
|
||||
if (m_size == 0)
|
||||
return true;
|
||||
|
||||
DEBUG_ASSERT(*offset >= m_offset);
|
||||
const u64 offset_in_content = *offset - m_offset;
|
||||
|
||||
if (offset_in_content < m_size)
|
||||
size_t i;
|
||||
for (i = 0; i < m_count; i++)
|
||||
{
|
||||
const u64 bytes_to_read = std::min(m_size - offset_in_content, *length);
|
||||
|
||||
if (std::holds_alternative<ContentFile>(m_content_source))
|
||||
{
|
||||
const auto& content = std::get<ContentFile>(m_content_source);
|
||||
if (!blob->ReadContentFile(content.m_filename, content.m_offset + offset_in_content, *buffer,
|
||||
bytes_to_read))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (std::holds_alternative<ContentMemory>(m_content_source))
|
||||
{
|
||||
const auto& content = std::get<ContentMemory>(m_content_source);
|
||||
std::copy_n(content->begin() + offset_in_content, bytes_to_read, *buffer);
|
||||
}
|
||||
else if (std::holds_alternative<ContentPartition>(m_content_source))
|
||||
{
|
||||
const auto& content = std::get<ContentPartition>(m_content_source);
|
||||
const u64 decrypted_size = m_size * VolumeWii::BLOCK_DATA_SIZE / VolumeWii::BLOCK_TOTAL_SIZE;
|
||||
if (!blob->EncryptPartitionData(content.m_offset + offset_in_content, bytes_to_read, *buffer,
|
||||
content.m_partition_data_offset, decrypted_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (std::holds_alternative<ContentVolume>(m_content_source))
|
||||
{
|
||||
const auto& source = std::get<ContentVolume>(m_content_source);
|
||||
if (!blob->GetWrappedVolume()->Read(source.m_offset + offset_in_content, bytes_to_read,
|
||||
*buffer, source.m_partition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (std::holds_alternative<ContentFixedByte>(m_content_source))
|
||||
{
|
||||
const ContentFixedByte& source = std::get<ContentFixedByte>(m_content_source);
|
||||
std::fill_n(*buffer, bytes_to_read, source.m_byte);
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlertFmt("DirectoryBlob: Invalid content source in DiscContent.");
|
||||
return false;
|
||||
}
|
||||
|
||||
*length -= bytes_to_read;
|
||||
*buffer += bytes_to_read;
|
||||
*offset += bytes_to_read;
|
||||
if (m_items[i].blob)
|
||||
ContentBlobRelease(m_items[i].blob);
|
||||
}
|
||||
|
||||
return true;
|
||||
std::free(m_items);
|
||||
m_items = nullptr;
|
||||
m_count = 0;
|
||||
m_capacity = 0;
|
||||
}
|
||||
|
||||
void DiscContentContainer::Add(u64 offset, u64 size, ContentSource source)
|
||||
DiscContentContainer::DiscContentContainer(const DiscContentContainer& other)
|
||||
: m_items(nullptr), m_count(0), m_capacity(0)
|
||||
{
|
||||
if (size != 0)
|
||||
m_contents.emplace(offset, size, std::move(source));
|
||||
*this = other;
|
||||
}
|
||||
|
||||
DiscContentContainer& DiscContentContainer::operator=(const DiscContentContainer& other)
|
||||
{
|
||||
size_t i;
|
||||
DiscContent* items;
|
||||
if (this == &other)
|
||||
return *this;
|
||||
items = other.m_count ?
|
||||
static_cast<DiscContent*>(std::malloc(other.m_count * sizeof(DiscContent))) :
|
||||
nullptr;
|
||||
if (other.m_count && !items)
|
||||
return *this;
|
||||
for (i = 0; i < other.m_count; i++)
|
||||
{
|
||||
items[i] = other.m_items[i];
|
||||
if (items[i].blob)
|
||||
ContentBlobRetain(items[i].blob);
|
||||
}
|
||||
Clear();
|
||||
m_items = items;
|
||||
m_count = other.m_count;
|
||||
m_capacity = other.m_count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DiscContentContainer::DiscContentContainer(DiscContentContainer&& other) noexcept
|
||||
: m_items(other.m_items), m_count(other.m_count), m_capacity(other.m_capacity)
|
||||
{
|
||||
other.m_items = nullptr;
|
||||
other.m_count = 0;
|
||||
other.m_capacity = 0;
|
||||
}
|
||||
|
||||
DiscContentContainer& DiscContentContainer::operator=(DiscContentContainer&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
Clear();
|
||||
m_items = other.m_items;
|
||||
m_count = other.m_count;
|
||||
m_capacity = other.m_capacity;
|
||||
other.m_items = nullptr;
|
||||
other.m_count = 0;
|
||||
other.m_capacity = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// the first chunk ending past offset, m_count if none does
|
||||
static size_t FirstEndingAfter(const DiscContent* items, size_t count, u64 offset)
|
||||
{
|
||||
size_t lo = 0, hi = count;
|
||||
while (lo < hi)
|
||||
{
|
||||
const size_t mid = lo + (hi - lo) / 2;
|
||||
if (items[mid].offset + items[mid].size > offset)
|
||||
hi = mid;
|
||||
else
|
||||
lo = mid + 1;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
void DiscContentContainer::Add(u64 offset, u64 size, const ContentSource& source)
|
||||
{
|
||||
size_t at;
|
||||
DiscContent* item;
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
// chunks are keyed by where they end; a second chunk ending at the same
|
||||
// place is not added
|
||||
at = FirstEndingAfter(m_items, m_count, offset + size - 1);
|
||||
if (at < m_count && m_items[at].offset + m_items[at].size == offset + size)
|
||||
return;
|
||||
|
||||
if (m_count == m_capacity)
|
||||
{
|
||||
const size_t capacity = m_capacity ? m_capacity * 2 : 16;
|
||||
DiscContent* items =
|
||||
static_cast<DiscContent*>(std::realloc(m_items, capacity * sizeof(DiscContent)));
|
||||
if (!items)
|
||||
return;
|
||||
m_items = items;
|
||||
m_capacity = capacity;
|
||||
}
|
||||
std::memmove(m_items + at + 1, m_items + at, (m_count - at) * sizeof(DiscContent));
|
||||
m_count++;
|
||||
|
||||
item = &m_items[at];
|
||||
item->offset = offset;
|
||||
item->size = size;
|
||||
item->src_offset = source.offset;
|
||||
item->partition_data_offset = source.partition_data_offset;
|
||||
item->partition = source.partition;
|
||||
item->blob = source.blob;
|
||||
item->type = source.type;
|
||||
item->fixed_byte = source.fixed_byte;
|
||||
if (item->blob)
|
||||
ContentBlobRetain(item->blob);
|
||||
}
|
||||
|
||||
void DiscContentContainer::AddBytes(u64 offset, const void* data, size_t size)
|
||||
{
|
||||
if (size)
|
||||
Add(offset, size, ContentBytes(data, size));
|
||||
}
|
||||
|
||||
u64 DiscContentContainer::CheckSizeAndAdd(u64 offset, const std::string& path)
|
||||
{
|
||||
const u64 size = File::GetSize(path);
|
||||
Add(offset, size, ContentFile{path, 0});
|
||||
Add(offset, size, ContentFile(path, 0));
|
||||
return size;
|
||||
}
|
||||
|
||||
u64 DiscContentContainer::CheckSizeAndAdd(u64 offset, u64 max_size, const std::string& path)
|
||||
{
|
||||
const u64 size = std::min(File::GetSize(path), max_size);
|
||||
Add(offset, size, ContentFile{path, 0});
|
||||
Add(offset, size, ContentFile(path, 0));
|
||||
return size;
|
||||
}
|
||||
|
||||
bool DiscContentContainer::ReadContent(const DiscContent* content, u64* offset, u64* length,
|
||||
u8** buffer, DirectoryBlobReader* blob) const
|
||||
{
|
||||
const u64 offset_in_content = *offset - content->offset;
|
||||
u64 bytes_to_read;
|
||||
|
||||
if (offset_in_content >= content->size)
|
||||
return true;
|
||||
bytes_to_read = std::min(content->size - offset_in_content, *length);
|
||||
|
||||
switch (content->type)
|
||||
{
|
||||
case CONTENT_FILE:
|
||||
if (!blob->ReadContentFile(reinterpret_cast<const char*>(ContentBlobData(content->blob)),
|
||||
size_t(content->blob->size - 1),
|
||||
content->src_offset + offset_in_content, *buffer, bytes_to_read))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case CONTENT_MEMORY:
|
||||
std::memcpy(*buffer, ContentBlobData(content->blob) + content->src_offset + offset_in_content,
|
||||
size_t(bytes_to_read));
|
||||
break;
|
||||
case CONTENT_PARTITION:
|
||||
{
|
||||
const u64 decrypted_size =
|
||||
content->size * VolumeWii::BLOCK_DATA_SIZE / VolumeWii::BLOCK_TOTAL_SIZE;
|
||||
if (!blob->EncryptPartitionData(content->src_offset + offset_in_content, bytes_to_read,
|
||||
*buffer, content->partition_data_offset, decrypted_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONTENT_VOLUME:
|
||||
if (!blob->GetWrappedVolume()->Read(content->src_offset + offset_in_content, bytes_to_read,
|
||||
*buffer, content->partition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case CONTENT_FIXED_BYTE:
|
||||
std::memset(*buffer, content->fixed_byte, size_t(bytes_to_read));
|
||||
break;
|
||||
default:
|
||||
PanicAlertFmt("DirectoryBlob: Invalid content source in DiscContent.");
|
||||
return false;
|
||||
}
|
||||
|
||||
*length -= bytes_to_read;
|
||||
*buffer += bytes_to_read;
|
||||
*offset += bytes_to_read;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiscContentContainer::Read(u64 offset, u64 length, u8* buffer, DirectoryBlobReader* blob) const
|
||||
{
|
||||
// Determine which DiscContent the offset refers to
|
||||
std::set<DiscContent>::const_iterator it = m_contents.upper_bound(DiscContent(offset));
|
||||
size_t i = FirstEndingAfter(m_items, m_count, offset);
|
||||
|
||||
while (it != m_contents.end() && length > 0)
|
||||
while (i < m_count && length > 0)
|
||||
{
|
||||
const DiscContent* content = &m_items[i];
|
||||
|
||||
// Zero fill to start of DiscContent data
|
||||
PadToAddress(it->GetOffset(), &offset, &length, &buffer);
|
||||
PadToAddress(content->offset, &offset, &length, &buffer);
|
||||
|
||||
if (length == 0)
|
||||
return true;
|
||||
|
||||
if (!it->Read(&offset, &length, &buffer, blob))
|
||||
if (!ReadContent(content, &offset, &length, &buffer, blob))
|
||||
return false;
|
||||
|
||||
++it;
|
||||
DEBUG_ASSERT(it == m_contents.end() || it->GetOffset() >= offset);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Zero fill if we went beyond the last DiscContent
|
||||
std::fill_n(buffer, static_cast<size_t>(length), 0);
|
||||
std::memset(buffer, 0, static_cast<size_t>(length));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -511,7 +687,7 @@ void DirectoryBlobReader::InitFileSlots()
|
||||
u32 i;
|
||||
for (i = 0; i < NUM_FILE_SLOTS; i++)
|
||||
{
|
||||
m_file_slots[i].busy.store(0, std::memory_order_relaxed);
|
||||
retro_atomic_int_init(&m_file_slots[i].busy, 0);
|
||||
m_file_slots[i].file = nullptr;
|
||||
m_file_slots[i].map = nullptr;
|
||||
m_file_slots[i].map_size = 0;
|
||||
@@ -529,10 +705,9 @@ DirectoryBlobReader::~DirectoryBlobReader()
|
||||
}
|
||||
}
|
||||
|
||||
bool DirectoryBlobReader::ReadContentFile(const std::string& path, u64 offset, u8* buffer, u64 size)
|
||||
bool DirectoryBlobReader::ReadContentFile(const char* name, size_t name_len, u64 offset,
|
||||
u8* buffer, u64 size)
|
||||
{
|
||||
const char* name = path.c_str();
|
||||
const size_t name_len = path.size();
|
||||
FileSlot* slot;
|
||||
bool ok;
|
||||
u32 hash = 2166136261u;
|
||||
@@ -542,7 +717,7 @@ bool DirectoryBlobReader::ReadContentFile(const std::string& path, u64 offset, u
|
||||
hash = (hash ^ u8(name[i])) * 16777619u;
|
||||
slot = &m_file_slots[hash % NUM_FILE_SLOTS];
|
||||
|
||||
if (name_len >= sizeof(slot->path) || slot->busy.exchange(1, std::memory_order_acquire))
|
||||
if (name_len >= sizeof(slot->path) || retro_atomic_exchange_int(&slot->busy, 1))
|
||||
{
|
||||
File::DirectIOFile file(name, File::AccessMode::Read);
|
||||
return file.OffsetRead(offset, buffer, size);
|
||||
@@ -572,7 +747,7 @@ bool DirectoryBlobReader::ReadContentFile(const std::string& path, u64 offset, u
|
||||
filestream_read(slot->file, buffer, s64(size)) == s64(size);
|
||||
}
|
||||
|
||||
slot->busy.store(0, std::memory_order_release);
|
||||
retro_atomic_store_release_int(&slot->busy, 0);
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -682,7 +857,7 @@ void DirectoryBlobReader::SetNonpartitionDiscHeader(std::span<const u8> partitio
|
||||
m_encrypted =
|
||||
std::all_of(header_bin.data() + 0x60, header_bin.data() + 0x64, [](u8 x) { return x == 0; });
|
||||
|
||||
m_nonpartition_contents.Add(WII_NONPARTITION_DISCHEADER_ADDRESS, std::move(header_bin));
|
||||
m_nonpartition_contents.AddBytes(WII_NONPARTITION_DISCHEADER_ADDRESS, header_bin.data(), header_bin.size());
|
||||
}
|
||||
|
||||
void DirectoryBlobReader::SetWiiRegionDataFromFile(const std::string& game_partition_root)
|
||||
@@ -709,7 +884,7 @@ void DirectoryBlobReader::SetWiiRegionData(std::span<const u8> wii_region_data,
|
||||
else if (wii_region_data.size() < 0x20)
|
||||
ERROR_LOG_FMT(DISCIO, "Couldn't read age ratings from {}", log_path);
|
||||
|
||||
m_nonpartition_contents.Add(WII_REGION_DATA_ADDRESS, std::move(region_data));
|
||||
m_nonpartition_contents.AddBytes(WII_REGION_DATA_ADDRESS, region_data.data(), region_data.size());
|
||||
}
|
||||
|
||||
void DirectoryBlobReader::SetPartitions(std::vector<PartitionWithType>&& partitions)
|
||||
@@ -772,14 +947,14 @@ void DirectoryBlobReader::SetPartitions(std::vector<PartitionWithType>&& partiti
|
||||
const u64 partition_data_offset = partition_address + PARTITION_DATA_OFFSET;
|
||||
m_partitions.emplace(partition_data_offset, std::move(partitions[i].partition));
|
||||
m_nonpartition_contents.Add(partition_data_offset, encrypted_data_size,
|
||||
ContentPartition{0, partition_data_offset});
|
||||
ContentPartition(0, partition_data_offset));
|
||||
const u64 unaligned_next_partition_address = VolumeWii::OffsetInHashedPartitionToRawOffset(
|
||||
data_size, Partition(partition_address), PARTITION_DATA_OFFSET);
|
||||
partition_address = Common::AlignUp(unaligned_next_partition_address, 0x10000ull);
|
||||
}
|
||||
m_data_size = partition_address;
|
||||
|
||||
m_nonpartition_contents.Add(PARTITION_TABLE_ADDRESS, std::move(partition_table));
|
||||
m_nonpartition_contents.AddBytes(PARTITION_TABLE_ADDRESS, partition_table.data(), partition_table.size());
|
||||
}
|
||||
|
||||
// This function sets the header that's shortly before the start of the encrypted
|
||||
@@ -800,8 +975,7 @@ void DirectoryBlobReader::SetPartitionHeader(DirectoryBlobPartition* partition,
|
||||
if (new_ticket.size() > WII_PARTITION_TICKET_SIZE)
|
||||
new_ticket.resize(WII_PARTITION_TICKET_SIZE);
|
||||
ticket_size = new_ticket.size();
|
||||
m_nonpartition_contents.Add(partition_address + WII_PARTITION_TICKET_ADDRESS,
|
||||
std::move(new_ticket));
|
||||
m_nonpartition_contents.AddBytes(partition_address + WII_PARTITION_TICKET_ADDRESS, new_ticket.data(), new_ticket.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -817,7 +991,7 @@ void DirectoryBlobReader::SetPartitionHeader(DirectoryBlobPartition* partition,
|
||||
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.Add(partition_address + TMD_OFFSET, std::move(new_tmd));
|
||||
m_nonpartition_contents.AddBytes(partition_address + TMD_OFFSET, new_tmd.data(), new_tmd.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -835,7 +1009,7 @@ void DirectoryBlobReader::SetPartitionHeader(DirectoryBlobPartition* partition,
|
||||
if (new_cert.size() > max_cert_size)
|
||||
new_cert.resize(max_cert_size);
|
||||
cert_size = new_cert.size();
|
||||
m_nonpartition_contents.Add(partition_address + cert_offset, std::move(new_cert));
|
||||
m_nonpartition_contents.AddBytes(partition_address + cert_offset, new_cert.data(), new_cert.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -855,7 +1029,7 @@ void DirectoryBlobReader::SetPartitionHeader(DirectoryBlobPartition* partition,
|
||||
if (m_wrapped_volume->Read(wrapped_partition->offset + *offset, new_h3.size(),
|
||||
new_h3.data(), PARTITION_NONE))
|
||||
{
|
||||
m_nonpartition_contents.Add(partition_address + H3_OFFSET, std::move(new_h3));
|
||||
m_nonpartition_contents.AddBytes(partition_address + H3_OFFSET, new_h3.data(), new_h3.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -877,8 +1051,7 @@ void DirectoryBlobReader::SetPartitionHeader(DirectoryBlobPartition* partition,
|
||||
Write32(PARTITION_DATA_OFFSET >> 2, 0x14, &partition_header);
|
||||
Write32(static_cast<u32>(data_size >> 2), 0x18, &partition_header);
|
||||
|
||||
m_nonpartition_contents.Add(partition_address + WII_PARTITION_TICKET_SIZE,
|
||||
std::move(partition_header));
|
||||
m_nonpartition_contents.AddBytes(partition_address + WII_PARTITION_TICKET_SIZE, partition_header.data(), partition_header.size());
|
||||
|
||||
std::vector<u8> ticket_buffer(ticket_size);
|
||||
m_nonpartition_contents.Read(partition_address + WII_PARTITION_TICKET_ADDRESS, ticket_size,
|
||||
@@ -905,7 +1078,7 @@ static void GenerateBuilderNodesFromFileSystem(const VolumeDisc& volume, const P
|
||||
{
|
||||
std::vector<BuilderContentSource> source;
|
||||
source.emplace_back(BuilderContentSource{0, file_info.GetSize(),
|
||||
ContentVolume{file_info.GetOffset(), partition}});
|
||||
ContentVolume(file_info.GetOffset(), partition)});
|
||||
nodes->emplace_back(
|
||||
FSTBuilderNode{file_info.GetName(), file_info.GetSize(), std::move(source)});
|
||||
}
|
||||
@@ -927,7 +1100,7 @@ DirectoryBlobPartition::DirectoryBlobPartition(std::string root_directory,
|
||||
SetDOLFromFile(m_root_directory + "sys/main.dol", dol_address, &disc_header);
|
||||
BuildFSTFromFolder(m_root_directory + "files/", fst_address, &disc_header);
|
||||
|
||||
m_contents.Add(DISCHEADER_ADDRESS, disc_header);
|
||||
m_contents.AddBytes(DISCHEADER_ADDRESS, disc_header.data(), disc_header.size());
|
||||
}
|
||||
|
||||
static void FillSingleFileNode(FSTBuilderNode* node, std::vector<u8> data)
|
||||
@@ -935,7 +1108,7 @@ static void FillSingleFileNode(FSTBuilderNode* node, std::vector<u8> data)
|
||||
std::vector<BuilderContentSource> contents;
|
||||
const size_t size = data.size();
|
||||
contents.emplace_back(
|
||||
BuilderContentSource{0, size, std::make_shared<std::vector<u8>>(std::move(data))});
|
||||
BuilderContentSource{0, size, ContentBytes(data.data(), data.size())});
|
||||
node->m_size = size;
|
||||
node->m_content = std::move(contents);
|
||||
}
|
||||
@@ -958,7 +1131,7 @@ static std::vector<u8> ExtractNodeToVector(std::vector<FSTBuilderNode>* nodes, v
|
||||
|
||||
DiscContentContainer tmp;
|
||||
for (auto& content : it->GetFileContent())
|
||||
tmp.Add(content.m_offset, content.m_size, std::move(content.m_source));
|
||||
tmp.Add(content.m_offset, content.m_size, content.m_source);
|
||||
data.resize(it->m_size);
|
||||
tmp.Read(0, it->m_size, data.data(), blob);
|
||||
return data;
|
||||
@@ -1015,7 +1188,7 @@ DirectoryBlobPartition::DirectoryBlobPartition(
|
||||
{
|
||||
std::vector<BuilderContentSource> dol_contents;
|
||||
dol_contents.emplace_back(
|
||||
BuilderContentSource{0, *dol_size, ContentVolume{*dol_offset, partition}});
|
||||
BuilderContentSource{0, *dol_size, ContentVolume(*dol_offset, partition)});
|
||||
dol_node.m_size = *dol_size;
|
||||
dol_node.m_content = std::move(dol_contents);
|
||||
}
|
||||
@@ -1033,7 +1206,7 @@ DirectoryBlobPartition::DirectoryBlobPartition(
|
||||
const u64 new_fst_address = SetDOL(std::move(dol_node), new_dol_address, &disc_header);
|
||||
BuildFST(std::move(nodes), new_fst_address, &disc_header);
|
||||
|
||||
m_contents.Add(DISCHEADER_ADDRESS, disc_header);
|
||||
m_contents.AddBytes(DISCHEADER_ADDRESS, disc_header.data(), disc_header.size());
|
||||
}
|
||||
|
||||
void DirectoryBlobPartition::SetDiscType(std::optional<bool> is_wii,
|
||||
@@ -1068,7 +1241,7 @@ void DirectoryBlobPartition::SetBI2FromFile(const std::string& bi2_path)
|
||||
if (!m_is_wii && bytes_read < 0x1C)
|
||||
ERROR_LOG_FMT(DISCIO, "Couldn't read region from {}", bi2_path);
|
||||
|
||||
m_contents.Add(BI2_ADDRESS, std::move(bi2));
|
||||
m_contents.AddBytes(BI2_ADDRESS, bi2.data(), bi2.size());
|
||||
}
|
||||
|
||||
void DirectoryBlobPartition::SetBI2(std::vector<u8> bi2)
|
||||
@@ -1079,7 +1252,7 @@ void DirectoryBlobPartition::SetBI2(std::vector<u8> bi2)
|
||||
if (!m_is_wii && bi2_size < 0x1C)
|
||||
Write32(INVALID_REGION, 0x18, &bi2);
|
||||
|
||||
m_contents.Add(BI2_ADDRESS, std::move(bi2));
|
||||
m_contents.AddBytes(BI2_ADDRESS, bi2.data(), bi2.size());
|
||||
}
|
||||
|
||||
u64 DirectoryBlobPartition::SetApploaderFromFile(const std::string& path)
|
||||
@@ -1116,7 +1289,7 @@ u64 DirectoryBlobPartition::SetApploader(std::vector<u8> apploader, const std::s
|
||||
}
|
||||
|
||||
size_t apploader_size = apploader.size();
|
||||
m_contents.Add(APPLOADER_ADDRESS, std::move(apploader));
|
||||
m_contents.AddBytes(APPLOADER_ADDRESS, apploader.data(), apploader.size());
|
||||
|
||||
// Return DOL address, 32 byte aligned (plus 32 byte padding)
|
||||
return Common::AlignUp(APPLOADER_ADDRESS + apploader_size + 0x20, 0x20ull);
|
||||
@@ -1137,7 +1310,7 @@ u64 DirectoryBlobPartition::SetDOL(FSTBuilderNode dol_node, u64 dol_address,
|
||||
std::vector<u8>* disc_header)
|
||||
{
|
||||
for (auto& content : dol_node.GetFileContent())
|
||||
m_contents.Add(dol_address + content.m_offset, content.m_size, std::move(content.m_source));
|
||||
m_contents.Add(dol_address + content.m_offset, content.m_size, content.m_source);
|
||||
|
||||
Write32(static_cast<u32>(dol_address >> m_address_shift), 0x0420, disc_header);
|
||||
|
||||
@@ -1159,7 +1332,7 @@ static std::vector<FSTBuilderNode> ConvertFSTEntriesToBuilderNodes(const File::F
|
||||
else
|
||||
{
|
||||
content =
|
||||
std::vector<BuilderContentSource>{{0, entry.size, ContentFile{entry.physicalName, 0}}};
|
||||
std::vector<BuilderContentSource>{{0, entry.size, ContentFile(entry.physicalName, 0)}};
|
||||
}
|
||||
|
||||
nodes.emplace_back(FSTBuilderNode{entry.virtualName, entry.size, std::move(content)});
|
||||
@@ -1245,7 +1418,7 @@ void DirectoryBlobPartition::BuildFST(std::vector<FSTBuilderNode> root_nodes, u6
|
||||
Write32((u32)(fst_data.size() >> m_address_shift), 0x0428, disc_header);
|
||||
Write32((u32)(fst_data.size() >> m_address_shift), 0x042c, disc_header);
|
||||
|
||||
m_contents.Add(fst_address, std::move(fst_data));
|
||||
m_contents.AddBytes(fst_address, fst_data.data(), fst_data.size());
|
||||
|
||||
m_data_size = current_data_address;
|
||||
}
|
||||
@@ -1324,7 +1497,7 @@ void DirectoryBlobPartition::WriteDirectory(std::vector<u8>* fst_data,
|
||||
for (BuilderContentSource& content : contents)
|
||||
{
|
||||
m_contents.Add(*data_offset + content.m_offset, content.m_size,
|
||||
std::move(content.m_source));
|
||||
content.m_source);
|
||||
}
|
||||
|
||||
// For most files, an alignment of 0x20 is enough. But some files need 0x8000,
|
||||
|
||||
@@ -4,17 +4,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <retro_atomic.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
@@ -39,53 +38,123 @@ class VolumeDisc;
|
||||
// Returns true if the path is inside a DirectoryBlob and doesn't represent the DirectoryBlob itself
|
||||
bool ShouldHideFromGameList(const std::string& volume_path);
|
||||
|
||||
// Content chunk that is loaded from a file in the host file system.
|
||||
struct ContentFile
|
||||
// Bytes shared by the content chunks that refer to them: a host file's path
|
||||
// (NUL included) or a run of memory. Reference counted; the bytes follow the
|
||||
// header in the same allocation.
|
||||
struct ContentBlob
|
||||
{
|
||||
// Path where the file can be found.
|
||||
std::string m_filename;
|
||||
|
||||
// Offset from the start of the file where the first byte of this content chunk is.
|
||||
u64 m_offset = 0;
|
||||
retro_atomic_int_t refs;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
// Content chunk that's just a direct block of memory
|
||||
typedef std::shared_ptr<std::vector<u8>> ContentMemory;
|
||||
// the bytes start here, 16-aligned whatever the atomic's width
|
||||
#define CONTENT_BLOB_HEADER ((sizeof(ContentBlob) + 15) & ~size_t(15))
|
||||
|
||||
// Content chunk that loads data from a DirectoryBlobReader.
|
||||
// Intended for representing a partition within a disc.
|
||||
struct ContentPartition
|
||||
// The size octets after the header, uninitialized; one reference.
|
||||
ContentBlob* ContentBlobAlloc(size_t size);
|
||||
ContentBlob* ContentBlobCreate(const void* data, size_t size);
|
||||
void ContentBlobRetain(ContentBlob* blob);
|
||||
void ContentBlobRelease(ContentBlob* blob);
|
||||
inline u8* ContentBlobData(ContentBlob* blob)
|
||||
{
|
||||
// Offset from the start of the partition for the first byte represented by this chunk.
|
||||
u64 m_offset = 0;
|
||||
return reinterpret_cast<u8*>(blob) + CONTENT_BLOB_HEADER;
|
||||
}
|
||||
inline const u8* ContentBlobData(const ContentBlob* blob)
|
||||
{
|
||||
return reinterpret_cast<const u8*>(blob) + CONTENT_BLOB_HEADER;
|
||||
}
|
||||
|
||||
// The value passed as partition_data_offset to EncryptPartitionData().
|
||||
u64 m_partition_data_offset = 0;
|
||||
enum ContentType : u8
|
||||
{
|
||||
// a host file: blob is its path, offset the file offset of the first byte
|
||||
CONTENT_FILE,
|
||||
// blob's bytes from offset on
|
||||
CONTENT_MEMORY,
|
||||
// the reader's own partition, encrypted on the fly: offset into it, and
|
||||
// the partition_data_offset passed to EncryptPartitionData()
|
||||
CONTENT_PARTITION,
|
||||
// the wrapped volume: offset into it, read from partition
|
||||
CONTENT_VOLUME,
|
||||
// fixed_byte repeated
|
||||
CONTENT_FIXED_BYTE,
|
||||
};
|
||||
|
||||
// Content chunk that loads data from a Volume.
|
||||
struct ContentVolume
|
||||
// Where a content chunk's bytes come from. Copies share the blob.
|
||||
struct ContentSource
|
||||
{
|
||||
// Offset from the start of the volume for the first byte represented by this chunk.
|
||||
u64 m_offset = 0;
|
||||
ContentSource()
|
||||
: type(CONTENT_FIXED_BYTE), fixed_byte(0), offset(0), partition_data_offset(0),
|
||||
blob(nullptr)
|
||||
{
|
||||
}
|
||||
ContentSource(const ContentSource& other)
|
||||
: type(other.type), fixed_byte(other.fixed_byte), offset(other.offset),
|
||||
partition_data_offset(other.partition_data_offset), partition(other.partition),
|
||||
blob(other.blob)
|
||||
{
|
||||
if (blob)
|
||||
ContentBlobRetain(blob);
|
||||
}
|
||||
ContentSource(ContentSource&& other) noexcept
|
||||
: type(other.type), fixed_byte(other.fixed_byte), offset(other.offset),
|
||||
partition_data_offset(other.partition_data_offset), partition(other.partition),
|
||||
blob(other.blob)
|
||||
{
|
||||
other.blob = nullptr;
|
||||
}
|
||||
ContentSource& operator=(const ContentSource& other)
|
||||
{
|
||||
if (other.blob)
|
||||
ContentBlobRetain(other.blob);
|
||||
if (blob)
|
||||
ContentBlobRelease(blob);
|
||||
type = other.type;
|
||||
fixed_byte = other.fixed_byte;
|
||||
offset = other.offset;
|
||||
partition_data_offset = other.partition_data_offset;
|
||||
partition = other.partition;
|
||||
blob = other.blob;
|
||||
return *this;
|
||||
}
|
||||
ContentSource& operator=(ContentSource&& other) noexcept
|
||||
{
|
||||
ContentBlob* const old = blob;
|
||||
type = other.type;
|
||||
fixed_byte = other.fixed_byte;
|
||||
offset = other.offset;
|
||||
partition_data_offset = other.partition_data_offset;
|
||||
partition = other.partition;
|
||||
blob = other.blob;
|
||||
other.blob = nullptr;
|
||||
if (old)
|
||||
ContentBlobRelease(old);
|
||||
return *this;
|
||||
}
|
||||
~ContentSource()
|
||||
{
|
||||
if (blob)
|
||||
ContentBlobRelease(blob);
|
||||
}
|
||||
|
||||
// The partition passed to the Volume's Read() method.
|
||||
Partition m_partition;
|
||||
u8 type;
|
||||
u8 fixed_byte;
|
||||
u64 offset;
|
||||
u64 partition_data_offset;
|
||||
Partition partition;
|
||||
ContentBlob* blob;
|
||||
};
|
||||
|
||||
// Content chunk representing a run of identical bytes.
|
||||
// Useful for padding between chunks within a file.
|
||||
struct ContentFixedByte
|
||||
ContentSource ContentFile(const char* path, u64 offset);
|
||||
inline ContentSource ContentFile(const std::string& path, u64 offset)
|
||||
{
|
||||
u8 m_byte = 0;
|
||||
};
|
||||
|
||||
using ContentSource = std::variant<ContentFile, // File
|
||||
ContentMemory, // Memory/Byte Sequence
|
||||
ContentPartition, // Partition
|
||||
ContentVolume, // Volume
|
||||
ContentFixedByte // Fixed value padding
|
||||
>;
|
||||
return ContentFile(path.c_str(), offset);
|
||||
}
|
||||
// Takes over the caller's reference.
|
||||
ContentSource ContentMemory(ContentBlob* blob, u64 offset);
|
||||
ContentSource ContentBytes(const void* data, size_t size);
|
||||
ContentSource ContentPartition(u64 offset, u64 partition_data_offset);
|
||||
ContentSource ContentVolume(u64 offset, const Partition& partition);
|
||||
ContentSource ContentFixedByte(u8 byte);
|
||||
|
||||
struct BuilderContentSource
|
||||
{
|
||||
@@ -129,52 +198,46 @@ struct FSTBuilderNode
|
||||
}
|
||||
};
|
||||
|
||||
class DiscContent
|
||||
// A content chunk placed at offset on the disc. Plain data: the container
|
||||
// it sits in holds the blob's reference.
|
||||
struct DiscContent
|
||||
{
|
||||
public:
|
||||
DiscContent(u64 offset, u64 size, ContentSource source);
|
||||
|
||||
// Provided because it's convenient when searching for DiscContent in an std::set
|
||||
explicit DiscContent(u64 offset);
|
||||
|
||||
u64 GetOffset() const;
|
||||
u64 GetEndOffset() const;
|
||||
u64 GetSize() const;
|
||||
bool Read(u64* offset, u64* length, u8** buffer, DirectoryBlobReader* blob) const;
|
||||
|
||||
bool operator==(const DiscContent& other) const { return GetEndOffset() == other.GetEndOffset(); }
|
||||
bool operator<(const DiscContent& other) const { return GetEndOffset() < other.GetEndOffset(); }
|
||||
bool operator>(const DiscContent& other) const { return other < *this; }
|
||||
bool operator<=(const DiscContent& other) const { return !(*this > other); }
|
||||
bool operator>=(const DiscContent& other) const { return !(*this < other); }
|
||||
|
||||
private:
|
||||
// Position of this content chunk within its parent DiscContentContainer.
|
||||
u64 m_offset;
|
||||
|
||||
// Number of bytes this content chunk takes up.
|
||||
u64 m_size = 0;
|
||||
|
||||
// Where and how to find the data for this content chunk.
|
||||
ContentSource m_content_source;
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u64 src_offset;
|
||||
u64 partition_data_offset;
|
||||
Partition partition;
|
||||
ContentBlob* blob;
|
||||
u8 type;
|
||||
u8 fixed_byte;
|
||||
};
|
||||
|
||||
// Content chunks sorted by end offset in one array, found by binary search.
|
||||
class DiscContentContainer
|
||||
{
|
||||
public:
|
||||
void Add(u64 offset, std::vector<u8> vector)
|
||||
{
|
||||
size_t vector_size = vector.size();
|
||||
return Add(offset, vector_size, std::make_shared<std::vector<u8>>(std::move(vector)));
|
||||
}
|
||||
void Add(u64 offset, u64 size, ContentSource source);
|
||||
DiscContentContainer();
|
||||
~DiscContentContainer();
|
||||
DiscContentContainer(const DiscContentContainer& other);
|
||||
DiscContentContainer& operator=(const DiscContentContainer& other);
|
||||
DiscContentContainer(DiscContentContainer&& other) noexcept;
|
||||
DiscContentContainer& operator=(DiscContentContainer&& other) noexcept;
|
||||
|
||||
void Add(u64 offset, u64 size, const ContentSource& source);
|
||||
void AddBytes(u64 offset, const void* data, size_t size);
|
||||
u64 CheckSizeAndAdd(u64 offset, const std::string& path);
|
||||
u64 CheckSizeAndAdd(u64 offset, u64 max_size, const std::string& path);
|
||||
|
||||
bool Read(u64 offset, u64 length, u8* buffer, DirectoryBlobReader* blob) const;
|
||||
|
||||
private:
|
||||
std::set<DiscContent> m_contents;
|
||||
void Clear();
|
||||
bool ReadContent(const DiscContent* content, u64* offset, u64* length, u8** buffer,
|
||||
DirectoryBlobReader* blob) const;
|
||||
|
||||
DiscContent* m_items;
|
||||
size_t m_count;
|
||||
size_t m_capacity;
|
||||
};
|
||||
|
||||
class DirectoryBlobPartition
|
||||
@@ -247,7 +310,7 @@ private:
|
||||
|
||||
class DirectoryBlobReader final : public BlobReader
|
||||
{
|
||||
friend DiscContent;
|
||||
friend DiscContentContainer;
|
||||
|
||||
public:
|
||||
static std::unique_ptr<DirectoryBlobReader> Create(const std::string& dol_path);
|
||||
@@ -314,7 +377,7 @@ private:
|
||||
const VolumeDisc* GetWrappedVolume() const { return m_wrapped_volume.get(); }
|
||||
|
||||
// Reads from a host file, through a handle kept open across reads.
|
||||
bool ReadContentFile(const std::string& path, u64 offset, u8* buffer, u64 size);
|
||||
bool ReadContentFile(const char* path, size_t path_len, u64 offset, u8* buffer, u64 size);
|
||||
|
||||
// Recently read host files, kept open (and mapped where the platform maps)
|
||||
// so a read is a copy out of the page cache rather than an open, a map, an
|
||||
@@ -323,7 +386,7 @@ private:
|
||||
static constexpr u32 NUM_FILE_SLOTS = 16;
|
||||
struct FileSlot
|
||||
{
|
||||
std::atomic<u32> busy;
|
||||
retro_atomic_int_t busy;
|
||||
RFILE* file;
|
||||
const u8* map;
|
||||
u64 map_size;
|
||||
|
||||
@@ -208,9 +208,9 @@ FileDataLoaderHostFS::MakeContentSource(std::string_view external_relative_path,
|
||||
{
|
||||
auto path = MakeAbsoluteFromRelative(external_relative_path);
|
||||
if (!path)
|
||||
return BuilderContentSource{disc_offset, external_size, ContentFixedByte{}};
|
||||
return BuilderContentSource{disc_offset, external_size, ContentFixedByte(0)};
|
||||
return BuilderContentSource{disc_offset, external_size,
|
||||
ContentFile{std::move(*path), external_offset}};
|
||||
ContentFile(*path, external_offset)};
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
@@ -233,24 +233,9 @@ static void SplitAt(BuilderContentSource* before, BuilderContentSource* after, u
|
||||
// The source after the split needs its length reduced and its start point adjusted.
|
||||
after->m_offset += before->m_size;
|
||||
after->m_size = end - split_at;
|
||||
if (std::holds_alternative<ContentFile>(after->m_source))
|
||||
{
|
||||
std::get<ContentFile>(after->m_source).m_offset += before->m_size;
|
||||
}
|
||||
else if (std::holds_alternative<ContentMemory>(after->m_source))
|
||||
{
|
||||
after->m_source = std::make_shared<std::vector<u8>>(
|
||||
std::get<ContentMemory>(after->m_source)->begin() + before->m_size,
|
||||
std::get<ContentMemory>(after->m_source)->end());
|
||||
}
|
||||
else if (std::holds_alternative<ContentPartition>(after->m_source))
|
||||
{
|
||||
std::get<ContentPartition>(after->m_source).m_offset += before->m_size;
|
||||
}
|
||||
else if (std::holds_alternative<ContentVolume>(after->m_source))
|
||||
{
|
||||
std::get<ContentVolume>(after->m_source).m_offset += before->m_size;
|
||||
}
|
||||
// every kind but fixed bytes moves its start; memory keeps sharing the blob
|
||||
if (after->m_source.type != CONTENT_FIXED_BYTE)
|
||||
after->m_source.offset += before->m_size;
|
||||
}
|
||||
|
||||
static void ApplyPatchToFile(const Patch& patch, FSTBuilderNode* file_node,
|
||||
@@ -282,7 +267,7 @@ static void ApplyPatchToFile(const Patch& patch, FSTBuilderNode* file_node,
|
||||
{
|
||||
// Insert an padding area between the old file and the patch data.
|
||||
content.emplace_back(BuilderContentSource{file_node->m_size, patch_start - file_node->m_size,
|
||||
ContentFixedByte{}});
|
||||
ContentFixedByte(0)});
|
||||
}
|
||||
|
||||
insert_where = content.size();
|
||||
@@ -338,7 +323,7 @@ static void ApplyPatchToFile(const Patch& patch, FSTBuilderNode* file_node,
|
||||
if (external_filesize < patch_size)
|
||||
{
|
||||
BuilderContentSource padding{patch_start + external_filesize, patch_size - external_filesize,
|
||||
ContentFixedByte{}};
|
||||
ContentFixedByte(0)};
|
||||
content.emplace(content.begin() + insert_where, std::move(padding));
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user