extend persistence to enemy, set, and switch flags

This commit is contained in:
Martin Michelsen
2024-03-12 19:37:34 -07:00
parent 84bb946e05
commit 69f40f9157
14 changed files with 572 additions and 109 deletions
+187 -36
View File
@@ -14,6 +14,10 @@ using namespace std;
static constexpr float UINT32_MAX_AS_FLOAT = 4294967296.0f;
static uint64_t section_index_key(uint8_t floor, uint16_t section, uint16_t wave_number) {
return (static_cast<uint64_t>(floor) << 32) | (static_cast<uint64_t>(section) << 16) | static_cast<uint64_t>(wave_number);
}
const char* Map::name_for_object_type(uint16_t type) {
switch (type) {
case 0x0000:
@@ -655,23 +659,34 @@ string Map::EnemyEntry::str() const {
this->unused.load());
}
Map::Enemy::Enemy(uint16_t enemy_id, size_t source_index, size_t set_index, uint8_t floor, EnemyType type)
Map::Enemy::Enemy(
uint16_t enemy_id,
size_t source_index,
size_t set_index,
uint8_t floor,
uint16_t section,
uint16_t wave_number,
EnemyType type)
: source_index(source_index),
set_index(set_index),
enemy_id(enemy_id),
total_damage(0),
game_flags(0),
section(section),
wave_number(wave_number),
type(type),
floor(floor),
state_flags(0) {}
string Map::Enemy::str() const {
return string_printf("[Map::Enemy E-%hX source %zX %s%s floor=%02hhX flags=%02hhX]",
return string_printf("[Map::Enemy E-%hX source %zX %s%s floor=%02hhX section=%04hX wave_number=%04hX flags=%02hhX]",
this->enemy_id,
this->source_index,
name_for_enum(this->type),
enemy_type_is_rare(this->type) ? " RARE" : "",
this->floor,
this->section,
this->wave_number,
this->state_flags);
}
@@ -725,6 +740,7 @@ void Map::add_objects_from_map_data(uint8_t floor, const void* data, size_t size
.floor = floor,
.base_type = objects[z].base_type,
.section = objects[z].section,
.group = objects[z].group,
.param1 = objects[z].param1,
.param3 = objects[z].param3,
.param4 = objects[z].param4,
@@ -734,6 +750,8 @@ void Map::add_objects_from_map_data(uint8_t floor, const void* data, size_t size
.set_flags = 0,
.item_drop_checked = false,
});
uint64_t k = section_index_key(floor, objects[z].section, objects[z].group);
this->floor_section_and_group_to_object_index.emplace(k, object_id);
}
}
@@ -782,7 +800,9 @@ void Map::add_enemy(
auto add = [&](EnemyType type) -> void {
uint16_t enemy_id = this->enemies.size();
this->enemies.emplace_back(enemy_id, source_index, set_index, floor, type);
this->enemies.emplace_back(enemy_id, source_index, set_index, floor, e.section, e.wave_number, type);
uint64_t k = section_index_key(floor, e.section, e.wave_number);
this->floor_section_and_wave_number_to_enemy_index.emplace(k, enemy_id);
};
EnemyType child_type = EnemyType::UNKNOWN;
@@ -1434,7 +1454,7 @@ void Map::add_random_enemies_from_map_data(
}
if (remaining_waves) {
/* ev.delay = */ random_state->rand_int_biased(entry.min_delay, entry.max_delay);
this->add_event(wave_next_event_id, entry.flags, floor, this->event_action_stream.size());
this->add_event(wave_next_event_id, entry.flags, floor, entry.section, wave_number, this->event_action_stream.size());
this->event_action_stream.push_back(0x0C);
wave_next_event_id = entry.event_id + wave_number + 10000;
this->event_action_stream.append(reinterpret_cast<const char*>(&wave_next_event_id), sizeof(wave_next_event_id));
@@ -1444,15 +1464,17 @@ void Map::add_random_enemies_from_map_data(
}
/* ev.delay = */ random_state->rand_int_biased(entry.min_delay, entry.max_delay);
this->add_event(wave_next_event_id, entry.flags, floor, action_stream_base_offset + entry.action_stream_offset);
this->add_event(wave_next_event_id, entry.flags, floor, entry.section, wave_number, action_stream_base_offset + entry.action_stream_offset);
wave_number++;
}
}
void Map::add_event(uint32_t event_id, uint16_t flags, uint8_t floor, uint32_t action_stream_offset) {
void Map::add_event(uint32_t event_id, uint16_t flags, uint8_t floor, uint16_t section, uint16_t wave_number, uint32_t action_stream_offset) {
size_t index = this->events.size();
auto& ev = this->events.emplace_back();
ev.event_id = event_id;
ev.section = section;
ev.wave_number = wave_number;
ev.flags = flags;
ev.floor = floor;
ev.action_stream_offset = action_stream_offset;
@@ -1460,6 +1482,9 @@ void Map::add_event(uint32_t event_id, uint16_t flags, uint8_t floor, uint32_t a
if (!this->floor_and_event_id_to_index.emplace(k, index).second) {
this->log.warning("Duplicate event ID: W-%02hhX-%" PRIX32, floor, event_id);
}
k = section_index_key(floor, section, wave_number);
this->floor_section_and_wave_number_to_event_index.emplace(k, index);
}
Map::Event& Map::get_event(uint8_t floor, uint32_t event_id) {
@@ -1486,7 +1511,7 @@ void Map::add_events_from_map_data(uint8_t floor, const void* data, size_t size)
auto events_r = r.sub(header.entries_offset, sizeof(Event1Entry) * header.entry_count);
while (!events_r.eof()) {
const auto& entry = events_r.get<Event1Entry>();
this->add_event(entry.event_id, entry.flags, floor, entry.action_stream_offset + action_stream_base_offset);
this->add_event(entry.event_id, entry.flags, floor, entry.section, entry.wave_number, entry.action_stream_offset + action_stream_base_offset);
}
}
@@ -1638,6 +1663,154 @@ Map::Enemy& Map::find_enemy(uint8_t floor, EnemyType type) {
throw out_of_range("enemy not found");
}
std::vector<Map::Object*> Map::get_objects(uint8_t floor, uint16_t section, uint16_t group) {
uint64_t k = section_index_key(floor, section, group);
vector<Object*> ret;
for (auto its = this->floor_section_and_group_to_object_index.equal_range(k); its.first != its.second; its.first++) {
ret.emplace_back(&this->objects.at(its.first->second));
}
return ret;
}
std::vector<Map::Enemy*> Map::get_enemies(uint8_t floor, uint16_t section, uint16_t wave_number) {
uint64_t k = section_index_key(floor, section, wave_number);
vector<Enemy*> ret;
for (auto its = this->floor_section_and_wave_number_to_enemy_index.equal_range(k); its.first != its.second; its.first++) {
ret.emplace_back(&this->enemies.at(its.first->second));
}
return ret;
}
std::vector<Map::Event*> Map::get_events(uint8_t floor, uint16_t section, uint16_t wave_number) {
uint64_t k = section_index_key(floor, section, wave_number);
vector<Event*> ret;
for (auto its = this->floor_section_and_wave_number_to_event_index.equal_range(k); its.first != its.second; its.first++) {
ret.emplace_back(&this->events.at(its.first->second));
}
return ret;
}
std::vector<Map::Event*> Map::get_events(uint8_t floor) {
uint64_t k_start = (static_cast<uint64_t>(floor) << 32);
uint64_t k_end = (static_cast<uint64_t>(floor + 1) << 32);
vector<Event*> ret;
for (auto it = this->floor_and_event_id_to_index.lower_bound(k_start);
(it != this->floor_and_event_id_to_index.end()) && (it->first < k_end);
it++) {
ret.emplace_back(&this->events.at(it->second));
}
return ret;
}
template <typename EntryT>
static string disassemble_vector_file_t(const void* data, size_t size, size_t* entry_number, char type_ch) {
deque<string> ret;
StringReader r(data, size);
size_t local_entry_number = 0;
if (!entry_number) {
entry_number = &local_entry_number;
}
while (r.remaining() >= sizeof(EntryT)) {
string o_str = r.get<EntryT>().str();
ret.emplace_back(string_printf("/* %c-%zX */ %s", type_ch, (*entry_number)++, o_str.c_str()));
}
if (r.remaining()) {
ret.emplace_back("// Warning: section size is not a multiple of entry size");
size_t size = r.remaining();
ret.emplace_back(format_data(r.getv(size), size));
}
return join(ret, "\n");
}
string Map::disassemble_objects_data(const void* data, size_t size, size_t* object_number) {
return disassemble_vector_file_t<ObjectEntry>(data, size, object_number, 'K');
}
string Map::disassemble_enemies_data(const void* data, size_t size, size_t* enemy_number) {
return disassemble_vector_file_t<EnemyEntry>(data, size, enemy_number, 'S');
}
string Map::disassemble_wave_events_data(const void* data, size_t size, uint8_t floor) {
deque<string> ret;
StringReader r(data, size);
const auto& evt_header = r.get<EventsSectionHeader>();
if (evt_header.format == 0x65767432) { // 'evt2'
ret.emplace_back(".evt2_format"); // TODO
size_t size = r.remaining();
ret.emplace_back(format_data(r.getv(size), size));
} else {
auto action_stream_r = r.sub(evt_header.action_stream_offset);
for (size_t z = 0; z < evt_header.entry_count; z++) {
const auto& entry = r.get<Event1Entry>();
ret.emplace_back(string_printf("/* W-%02hhX-%" PRIX32 " */ [Event1Entry flags=%04hX type=%04hX section=%04hX wave_number=%04hX delay=%" PRIu32 "]",
floor,
entry.event_id.load(),
entry.flags.load(),
entry.event_type.load(),
entry.section.load(),
entry.wave_number.load(),
entry.delay.load()));
auto ev_actions_r = action_stream_r.sub(entry.action_stream_offset);
bool should_continue = true;
while (!ev_actions_r.eof() && should_continue) {
uint8_t opcode = ev_actions_r.get_u8();
switch (opcode) {
case 0x00:
ret.emplace_back(string_printf(" 00 nop"));
break;
case 0x01:
ret.emplace_back(string_printf(" 01 stop"));
should_continue = false;
break;
case 0x08: {
uint16_t section = ev_actions_r.get_u16l();
uint16_t group = ev_actions_r.get_u16l();
ret.emplace_back(string_printf(" 08 %04hX %04hX construct_objects section=%04hX group=%04hX",
section, group, section, group));
break;
}
case 0x09: {
uint16_t section = ev_actions_r.get_u16l();
uint16_t wave_number = ev_actions_r.get_u16l();
ret.emplace_back(string_printf(" 09 %04hX %04hX construct_enemies section=%04hX wave_number=%04hX",
section, wave_number, section, wave_number));
break;
}
case 0x0A: {
uint16_t id = ev_actions_r.get_u16l();
ret.emplace_back(string_printf(" 0A %04hX enable_switch_flag id=%04hX", id, id));
break;
}
case 0x0B: {
uint16_t id = ev_actions_r.get_u16l();
ret.emplace_back(string_printf(" 0B %04hX disable_switch_flag id=%04hX", id, id));
break;
}
case 0x0C: {
uint32_t event_id = ev_actions_r.get_u32l();
ret.emplace_back(string_printf(" 0C %04hX trigger_event event_id=%08" PRIX32, event_id, event_id));
break;
}
case 0x0D: {
uint16_t section = ev_actions_r.get_u16l();
uint16_t wave_number = ev_actions_r.get_u16l();
ret.emplace_back(string_printf(" 0D %04hX %04hX construct_enemies_stop section=%04hX wave_number=%04hX",
section, wave_number, section, wave_number));
break;
}
default:
ret.emplace_back(string_printf(" %02hhX .invalid", opcode));
}
}
}
}
return join(ret, "\n");
}
string Map::disassemble_quest_data(const void* data, size_t size) {
auto all_floor_sections = Map::collect_quest_map_data_sections(data, size);
@@ -1651,56 +1824,34 @@ string Map::disassemble_quest_data(const void* data, size_t size) {
if (floor_sections.objects != 0xFFFFFFFF) {
ret.emplace_back(string_printf(".objects %zu", floor));
const auto& header = r.pget<SectionHeader>(floor_sections.objects);
auto sub_r = r.sub(floor_sections.objects + sizeof(SectionHeader), header.data_size);
while (sub_r.remaining() >= sizeof(ObjectEntry)) {
string o_str = sub_r.get<ObjectEntry>().str();
ret.emplace_back(string_printf("/* K-%zX */ %s", object_number++, o_str.c_str()));
}
if (sub_r.remaining()) {
ret.emplace_back("// Warning: object section size is not a multiple of object entry size");
size_t offset = floor_sections.objects + sizeof(SectionHeader) + r.where();
size_t bytes = r.remaining();
ret.emplace_back(format_data(r.getv(r.remaining()), bytes, offset));
}
size_t offset = floor_sections.objects + sizeof(SectionHeader);
ret.emplace_back(Map::disassemble_objects_data(r.pgetv(offset, header.data_size), header.data_size, &object_number));
}
if (floor_sections.enemies != 0xFFFFFFFF) {
ret.emplace_back(string_printf(".enemies %zu", floor));
const auto& header = r.pget<SectionHeader>(floor_sections.enemies);
auto sub_r = r.sub(floor_sections.enemies + sizeof(SectionHeader), header.data_size);
while (sub_r.remaining() >= sizeof(EnemyEntry)) {
string e_str = sub_r.get<EnemyEntry>().str();
ret.emplace_back(string_printf("/* entry %zX */ %s", enemy_number++, e_str.c_str()));
}
if (sub_r.remaining()) {
ret.emplace_back("// Warning: enemy section size is not a multiple of enemy entry size");
size_t offset = floor_sections.objects + sizeof(SectionHeader) + r.where();
size_t bytes = r.remaining();
ret.emplace_back(format_data(r.getv(r.remaining()), bytes, offset));
}
size_t offset = floor_sections.enemies + sizeof(SectionHeader);
ret.emplace_back(Map::disassemble_enemies_data(r.pgetv(offset, header.data_size), header.data_size, &enemy_number));
}
// TODO: Add disassembly for these section types
if (floor_sections.wave_events != 0xFFFFFFFF) {
ret.emplace_back(string_printf(".wave_events %zu", floor));
const auto& header = r.pget<SectionHeader>(floor_sections.wave_events);
size_t offset = floor_sections.wave_events + sizeof(SectionHeader);
auto sub_r = r.sub(offset, header.data_size);
ret.emplace_back(format_data(r.getv(r.remaining()), header.data_size, offset));
ret.emplace_back(Map::disassemble_wave_events_data(r.pgetv(offset, header.data_size), header.data_size, floor));
}
if (floor_sections.random_enemy_locations != 0xFFFFFFFF) {
ret.emplace_back(string_printf(".random_enemy_locations %zu", floor));
const auto& header = r.pget<SectionHeader>(floor_sections.random_enemy_locations);
size_t offset = floor_sections.random_enemy_locations + sizeof(SectionHeader);
auto sub_r = r.sub(offset, header.data_size);
ret.emplace_back(format_data(r.getv(r.remaining()), header.data_size, offset));
ret.emplace_back(format_data(sub_r.getv(sub_r.remaining()), header.data_size, offset));
}
if (floor_sections.random_enemy_definitions != 0xFFFFFFFF) {
ret.emplace_back(string_printf(".random_enemy_definitions %zu", floor));
const auto& header = r.pget<SectionHeader>(floor_sections.random_enemy_definitions);
size_t offset = floor_sections.random_enemy_definitions + sizeof(SectionHeader);
auto sub_r = r.sub(offset, header.data_size);
ret.emplace_back(format_data(r.getv(r.remaining()), header.data_size, offset));
ret.emplace_back(format_data(sub_r.getv(sub_r.remaining()), header.data_size, offset));
}
}