#include "BMLArchive.hh" #include #include #include #include "Text.hh" #include "Types.hh" template struct BMLHeaderT { parray unknown_a1; U32T num_entries; parray unknown_a2; } __packed_ws_be__(BMLHeaderT, 0x40); using BMLHeader = BMLHeaderT; using BMLHeaderBE = BMLHeaderT; template struct BMLHeaderEntryT { pstring filename; U32T compressed_size; parray unknown_a1; U32T decompressed_size; U32T compressed_gvm_size; U32T decompressed_gvm_size; parray unknown_a2; } __packed_ws_be__(BMLHeaderEntryT, 0x40); using BMLHeaderEntry = BMLHeaderEntryT; using BMLHeaderEntryBE = BMLHeaderEntryT; template void BMLArchive::load_t() { phosg::StringReader r(*this->data); const auto& header = r.get>(); size_t offset = 0x800; while (this->entries.size() < header.num_entries) { const auto& entry = r.get>(); if (offset + entry.compressed_size > this->data->size()) { throw std::runtime_error("BML data entry extends beyond end of data"); } size_t data_offset = offset; offset = (offset + entry.compressed_size + 0x1F) & (~0x1F); if (offset + entry.compressed_gvm_size > this->data->size()) { throw std::runtime_error("BML GVM entry extends beyond end of data"); } size_t gvm_offset = offset; offset = (offset + entry.compressed_gvm_size + 0x1F) & (~0x1F); this->entries.emplace(entry.filename.decode(), Entry{data_offset, entry.compressed_size, gvm_offset, entry.compressed_gvm_size}); } } BMLArchive::BMLArchive(std::shared_ptr data, bool big_endian) : data(data) { if (big_endian) { this->load_t(); } else { this->load_t(); } } const std::unordered_map BMLArchive::all_entries() const { return this->entries; } std::pair BMLArchive::get(const std::string& name) const { try { const auto& entry = this->entries.at(name); return std::make_pair(this->data->data() + entry.offset, entry.size); } catch (const std::out_of_range&) { throw std::out_of_range("BML does not contain file: " + name); } } std::pair BMLArchive::get_gvm(const std::string& name) const { try { const auto& entry = this->entries.at(name); return std::make_pair(this->data->data() + entry.gvm_offset, entry.gvm_size); } catch (const std::out_of_range&) { throw std::out_of_range("BML does not contain file: " + name); } } std::string BMLArchive::get_copy(const std::string& name) const { try { const auto& entry = this->entries.at(name); return this->data->substr(entry.offset, entry.size); } catch (const std::out_of_range&) { throw std::out_of_range("BML does not contain file: " + name); } } phosg::StringReader BMLArchive::get_reader(const std::string& name) const { try { const auto& entry = this->entries.at(name); return phosg::StringReader(this->data->data() + entry.offset, entry.size); } catch (const std::out_of_range&) { throw std::out_of_range("BML does not contain file: " + name); } }