#include "GSLArchive.hh" #include #include #include #include "Text.hh" #include "Types.hh" using namespace std; template struct GSLHeaderEntryT { pstring filename; U32T offset; // In pages, so actual offset is this * 0x800 U32T size; uint64_t unused; } __attribute__((packed)); using GSLHeaderEntry = GSLHeaderEntryT; using GSLHeaderEntryBE = GSLHeaderEntryT; check_struct_size(GSLHeaderEntry, 0x30); check_struct_size(GSLHeaderEntryBE, 0x30); template void GSLArchive::load_t() { phosg::StringReader r(*this->data); uint64_t min_data_offset = 0xFFFFFFFFFFFFFFFF; while (r.where() < min_data_offset) { const auto& entry = r.get>(); if (entry.filename.empty()) { break; } uint64_t offset = static_cast(entry.offset) * 0x800; if (offset + entry.size > this->data->size()) { throw runtime_error("GSL entry extends beyond end of data"); } this->entries.emplace(entry.filename.decode(), Entry{offset, entry.size}); } } GSLArchive::GSLArchive(shared_ptr data, bool big_endian) : data(data) { if (big_endian) { this->load_t(); } else { this->load_t(); } } const unordered_map GSLArchive::all_entries() const { return this->entries; } pair GSLArchive::get(const std::string& name) const { try { const auto& entry = this->entries.at(name); return make_pair(this->data->data() + entry.offset, entry.size); } catch (const out_of_range&) { throw out_of_range("GSL does not contain file: " + name); } } string GSLArchive::get_copy(const string& name) const { try { const auto& entry = this->entries.at(name); return this->data->substr(entry.offset, entry.size); } catch (const out_of_range&) { throw out_of_range("GSL does not contain file: " + name); } } phosg::StringReader GSLArchive::get_reader(const string& name) const { try { const auto& entry = this->entries.at(name); return phosg::StringReader(this->data->data() + entry.offset, entry.size); } catch (const out_of_range&) { throw out_of_range("GSL does not contain file: " + name); } } string GSLArchive::generate(const unordered_map& files, bool big_endian) { return big_endian ? GSLArchive::generate_t(files) : GSLArchive::generate_t(files); } template string GSLArchive::generate_t(const unordered_map& files) { phosg::StringWriter w; // Make sure there's enough space for a blank header entry before any file's // data pages begin uint32_t data_start_offset = ((sizeof(GSLHeaderEntryT) * (files.size() + 1)) + 0x7FF) & (~0x7FF); uint32_t data_offset = data_start_offset; for (const auto& file : files) { GSLHeaderEntryT entry; entry.filename.encode(file.first); entry.offset = data_offset >> 11; entry.size = file.second.size(); entry.unused = 0; w.put(entry); data_offset = (data_offset + file.second.size() + 0x7FF) & (~0x7FF); } w.extend_to(data_start_offset); for (const auto& file : files) { w.write(file.second); w.extend_to((w.size() + 0x7FF) & (~0x7FF)); } return std::move(w.str()); }