add MapFile::serialize

This commit is contained in:
Martin Michelsen
2025-12-20 18:52:45 -08:00
parent 0a4c9a0a61
commit a9fa138213
21 changed files with 309 additions and 70 deletions
+130
View File
@@ -4127,6 +4127,136 @@ string MapFile::disassemble(bool reassembly, Version version) const {
return phosg::join(ret, "\n");
}
std::string MapFile::serialize() const {
using T = SectionHeader::Type;
phosg::StringWriter w;
auto write_section_header = [&](T type, uint32_t floor, uint32_t data_size) -> void {
uint32_t section_size = data_size + sizeof(SectionHeader);
w.put<SectionHeader>({static_cast<uint32_t>(type), section_size, floor, data_size});
};
// Try to serialize the sections in the same order as the input file; if the offsets are missing, serialize in
// (floor, type) order
struct SectionOrderEntry {
T type;
uint8_t floor;
size_t file_offset;
bool operator<(const SectionOrderEntry& other) const {
if (this->file_offset < other.file_offset) {
return true;
} else if (this->file_offset > other.file_offset) {
return false;
}
if (this->floor < other.floor) {
return true;
} else if (this->floor > other.floor) {
return false;
}
return static_cast<uint8_t>(this->type) < static_cast<uint8_t>(other.type);
}
};
std::set<SectionOrderEntry> sections_to_serialize;
for (uint8_t floor = 0; floor < this->sections_for_floor.size(); floor++) {
const auto& sf = this->sections_for_floor[floor];
if (sf.object_sets) {
sections_to_serialize.emplace(SectionOrderEntry{T::OBJECT_SETS, floor, sf.object_sets_file_offset});
}
if (sf.enemy_sets) {
sections_to_serialize.emplace(SectionOrderEntry{T::ENEMY_SETS, floor, sf.enemy_sets_file_offset});
}
if (sf.events1 || sf.events2) {
sections_to_serialize.emplace(SectionOrderEntry{T::EVENTS, floor, sf.events_file_offset});
}
if (sf.random_enemy_room_count && sf.random_enemy_locations) {
sections_to_serialize.emplace(SectionOrderEntry{
T::RANDOM_ENEMY_LOCATIONS, floor, sf.random_enemy_locations_file_offset});
}
if (sf.random_enemy_definitions && sf.random_enemy_weights) {
sections_to_serialize.emplace(SectionOrderEntry{
T::RANDOM_ENEMY_DEFINITIONS, floor, sf.random_enemy_definitions_file_offset});
}
}
for (const auto& section : sections_to_serialize) {
const auto& sf = this->sections_for_floor[section.floor];
switch (section.type) {
case T::OBJECT_SETS: {
size_t data_size = sizeof(ObjectSetEntry) * sf.object_set_count;
write_section_header(T::OBJECT_SETS, section.floor, data_size);
w.write(sf.object_sets, data_size);
break;
}
case T::ENEMY_SETS: {
size_t data_size = sizeof(EnemySetEntry) * sf.enemy_set_count;
write_section_header(T::ENEMY_SETS, section.floor, data_size);
w.write(sf.enemy_sets, data_size);
break;
}
case T::EVENTS: {
auto serialize_events_section = [&]<typename EventT>(const EventT* events) -> void {
EventsSectionHeader ev_header = {
.action_stream_offset = sizeof(EventsSectionHeader) + sizeof(EventT) * sf.event_count,
.entries_offset = sizeof(EventsSectionHeader),
.entry_count = sf.event_count,
.format = std::is_same_v<EventT, Event2Entry> ? 0x65767432 : 0x00000000,
};
size_t data_size = ev_header.action_stream_offset + sf.event_action_stream_bytes;
data_size = (data_size + 3) & (~3);
write_section_header(T::EVENTS, section.floor, data_size);
w.put(ev_header);
w.write(events, sf.event_count * sizeof(EventT));
w.write(sf.event_action_stream, sf.event_action_stream_bytes);
while (w.size() & 3) {
w.put_u8(0x00);
}
};
if (sf.events1) {
serialize_events_section(sf.events1);
} else if (sf.events2) {
serialize_events_section(sf.events2);
}
break;
}
case T::RANDOM_ENEMY_LOCATIONS: {
size_t data_size = sizeof(RandomEnemyLocationsHeader) +
sf.random_enemy_room_count * sizeof(RandomEnemyRoom) +
sf.random_enemy_location_count * sizeof(RandomEnemyLocation);
write_section_header(T::RANDOM_ENEMY_LOCATIONS, section.floor, data_size);
w.put(RandomEnemyLocationsHeader{
.room_table_offset = sizeof(RandomEnemyLocationsHeader),
.entries_offset = sizeof(RandomEnemyLocationsHeader) + sf.random_enemy_room_count * sizeof(RandomEnemyRoom),
.num_rooms = sf.random_enemy_room_count,
});
w.write(sf.random_enemy_rooms, sf.random_enemy_room_count * sizeof(RandomEnemyRoom));
w.write(sf.random_enemy_locations, sf.random_enemy_location_count * sizeof(RandomEnemyLocation));
break;
}
case T::RANDOM_ENEMY_DEFINITIONS: {
size_t data_size = sizeof(RandomEnemyDefinitionsHeader) +
sf.random_enemy_definition_count * sizeof(RandomEnemyDefinition) +
sf.random_enemy_weight_count * sizeof(RandomEnemyWeight);
write_section_header(T::RANDOM_ENEMY_DEFINITIONS, section.floor, data_size);
w.put(RandomEnemyDefinitionsHeader{
.entries_offset = sizeof(RandomEnemyDefinitionsHeader),
.weight_entries_offset = sizeof(RandomEnemyDefinitionsHeader) + sf.random_enemy_definition_count * sizeof(RandomEnemyDefinition),
.entry_count = sf.random_enemy_definition_count,
.weight_entry_count = sf.random_enemy_weight_count,
});
w.write(sf.random_enemy_definitions, sf.random_enemy_definition_count * sizeof(RandomEnemyDefinition));
w.write(sf.random_enemy_weights, sf.random_enemy_weight_count * sizeof(RandomEnemyWeight));
break;
}
default:
throw std::logic_error("invalid section type descriptor");
}
}
w.put<SectionHeader>({static_cast<uint32_t>(T::END), 0, 0, 0});
return std::move(w.str());
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Super map