diff --git a/src/ItemCreator.cc b/src/ItemCreator.cc index db97abe9..14433edc 100644 --- a/src/ItemCreator.cc +++ b/src/ItemCreator.cc @@ -827,7 +827,7 @@ uint8_t ItemCreator::choose_weapon_special(uint8_t det) { uint8_t det2 = this->rand_int(maxes[det]); this->log.info_f("Choosing special with det {:02X} and det2 {:02X}", det, det2); size_t index = 0; - for (size_t z = 1; z < this->item_parameter_table->num_specials; z++) { + for (size_t z = 1; z < this->item_parameter_table->num_specials(); z++) { if (det + 1 == this->item_parameter_table->get_special_stars(z)) { if (index == det2) { this->log.info_f("Chose special {:02X}", z); diff --git a/src/ItemData.cc b/src/ItemData.cc index 9a1850a2..c4ac5b99 100644 --- a/src/ItemData.cc +++ b/src/ItemData.cc @@ -486,7 +486,7 @@ void ItemData::encode_for_version(Version to_version, shared_ptrdata1[1] > 0x26)) { if (this->data1[1] < 0x89) { this->data1[5] = this->data1[1]; - this->data1[1] = item_parameter_table->get_weapon_v1_replacement(this->data1[1]); + this->data1[1] = item_parameter_table->get_weapon_class(this->data1[1]); if (this->data1[1] == 0x00) { this->data1[1] = 0x0F; } diff --git a/src/ItemNameIndex.cc b/src/ItemNameIndex.cc index c22ab83b..cf555c03 100644 --- a/src/ItemNameIndex.cc +++ b/src/ItemNameIndex.cc @@ -787,9 +787,9 @@ void ItemNameIndex::print_table(FILE* stream) const { auto pmt = this->item_parameter_table; phosg::fwrite_fmt(stream, "WEAPONS\n"); - phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS FLAG ATPLO ATPHI ATPRQ MSTRQ ATARQ -MST- GND PH SP ATA SB(S1:AMT1,S2:AMT2) PJ 1X 1Y 2X 2Y CL --A1-- A4 A5 TB BF V1 ST* USL ---DIVISOR--- NAME\n"); - for (size_t data1_1 = 0; data1_1 < pmt->num_weapon_classes; data1_1++) { - uint8_t v1_replacement = pmt->get_weapon_v1_replacement(data1_1); + phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS FLAG ATPLO ATPHI ATPRQ MSTRQ ATARQ -MST- GND PH SP ATA SB(S1:AMT1,S2:AMT2) PJ 1X 1Y 2X 2Y CR --A1-- A4 A5 TB BF CL ST* USL ---DIVISOR--- NAME\n"); + for (size_t data1_1 = 0; data1_1 < pmt->num_weapon_classes(); data1_1++) { + uint8_t weapon_class = pmt->get_weapon_class(data1_1); float sale_divisor = pmt->get_sale_divisor(0x00, data1_1); string divisor_str = std::format("{:g}", sale_divisor); divisor_str.resize(13, ' '); @@ -797,7 +797,7 @@ void ItemNameIndex::print_table(FILE* stream) const { size_t data1_2_limit = pmt->num_weapons_in_class(data1_1); for (size_t data1_2 = 0; data1_2 < data1_2_limit; data1_2++) { const auto& w = pmt->get_weapon(data1_1, data1_2); - uint8_t stars = pmt->get_item_stars(w.base.id); + uint8_t stars = pmt->get_item_stars(w.id); bool is_unsealable = pmt->is_unsealable_item(0x00, data1_1, data1_2); ItemData item; @@ -810,10 +810,10 @@ void ItemNameIndex::print_table(FILE* stream) const { phosg::fwrite_fmt(stream, " 00{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:04X} {:5} {:5} {:5} {:5} {:5} {:5} {:3} {:02X} {:02X} {:3} {:02X}({:02X}:{:04X},{:02X}:{:04X}) {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}{:02X}{:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:2}* {} {} {}\n", data1_1, data1_2, - w.base.id, - w.base.type, - w.base.skin, - w.base.team_points, + w.id, + w.type, + w.skin, + w.team_points, w.class_flags, w.atp_min, w.atp_max, @@ -826,10 +826,10 @@ void ItemNameIndex::print_table(FILE* stream) const { w.special, w.ata, w.stat_boost_entry_index, - stat_boost.stats[0], - stat_boost.amounts[0], - stat_boost.stats[1], - stat_boost.amounts[1], + stat_boost.stat1, + stat_boost.amount1, + stat_boost.stat2, + stat_boost.amount2, w.projectile, w.trail1_x, w.trail1_y, @@ -843,7 +843,7 @@ void ItemNameIndex::print_table(FILE* stream) const { w.unknown_a5, w.tech_boost, w.behavior_flags, - v1_replacement, + weapon_class, stars, is_unsealable ? "YES" : " no", divisor_str, @@ -861,7 +861,7 @@ void ItemNameIndex::print_table(FILE* stream) const { size_t data1_2_limit = pmt->num_armors_or_shields_in_class(data1_1); for (size_t data1_2 = 0; data1_2 < data1_2_limit; data1_2++) { const auto& a = pmt->get_armor_or_shield(data1_1, data1_2); - uint8_t stars = pmt->get_item_stars(a.base.id); + uint8_t stars = pmt->get_item_stars(a.id); ItemData item; item.data1[0] = 0x01; @@ -873,10 +873,10 @@ void ItemNameIndex::print_table(FILE* stream) const { phosg::fwrite_fmt(stream, " 01{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:5} {:5} {:02X} {:02X} {:04X} {:3} {:3} {:3} {:3} {:3} {:3} {:3} {:3} {:02X}({:02X}:{:04X},{:02X}:{:04X}) {:02X} {:02X} {:02X} {:2}* {} {}\n", data1_1, data1_2, - a.base.id, - a.base.type, - a.base.skin, - a.base.team_points, + a.id, + a.type, + a.skin, + a.team_points, a.dfp, a.evp, a.block_particle, @@ -891,10 +891,10 @@ void ItemNameIndex::print_table(FILE* stream) const { a.dfp_range, a.evp_range, a.stat_boost_entry_index, - stat_boost.stats[0], - stat_boost.amounts[0], - stat_boost.stats[1], - stat_boost.amounts[1], + stat_boost.stat1, + stat_boost.amount1, + stat_boost.stat2, + stat_boost.amount2, a.tech_boost, a.flags_type, a.unknown_a4, @@ -914,7 +914,7 @@ void ItemNameIndex::print_table(FILE* stream) const { size_t data1_2_limit = pmt->num_units(); for (size_t data1_2 = 0; data1_2 < data1_2_limit; data1_2++) { const auto& u = pmt->get_unit(data1_2); - uint8_t stars = pmt->get_item_stars(u.base.id); + uint8_t stars = pmt->get_item_stars(u.id); ItemData item; item.data1[0] = 0x01; @@ -924,10 +924,10 @@ void ItemNameIndex::print_table(FILE* stream) const { phosg::fwrite_fmt(stream, " 0103{:02X} => {:08X} {:04X} {:04X} {:6} {:04X} {:5} {:6} {:2}* {} {}\n", data1_2, - u.base.id, - u.base.type, - u.base.skin, - u.base.team_points, + u.id, + u.type, + u.skin, + u.team_points, u.stat, u.stat_amount, u.modifier_amount, @@ -956,10 +956,10 @@ void ItemNameIndex::print_table(FILE* stream) const { phosg::fwrite_fmt(stream, " 02{:02X}00 => {:08X} {:04X} {:04X} {:6} {:04X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:04X} {} {}\n", data1_1, - m.base.id, - m.base.type, - m.base.skin, - m.base.team_points, + m.id, + m.type, + m.skin, + m.team_points, m.feed_table, m.photon_blast, m.activation, @@ -979,7 +979,7 @@ void ItemNameIndex::print_table(FILE* stream) const { phosg::fwrite_fmt(stream, "TOOLS\n"); phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS COUNT TECH -COST- ITEMFLAG ---DIVISOR--- NAME\n"); - for (size_t data1_1 = 0; data1_1 < pmt->num_tool_classes; data1_1++) { + for (size_t data1_1 = 0; data1_1 < pmt->num_tool_classes(); data1_1++) { float sale_divisor = pmt->get_sale_divisor(0x03, data1_1); string divisor_str = std::format("{:g}", sale_divisor); divisor_str.resize(13, ' '); @@ -998,10 +998,10 @@ void ItemNameIndex::print_table(FILE* stream) const { phosg::fwrite_fmt(stream, " 03{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:5} {:04X} {:6} {:08X} {} {}\n", data1_1, data1_2, - t.base.id, - t.base.type, - t.base.skin, - t.base.team_points, + t.id, + t.type, + t.skin, + t.team_points, t.amount, t.tech, t.cost, @@ -1041,7 +1041,7 @@ void ItemNameIndex::print_table(FILE* stream) const { phosg::fwrite_fmt(stream, "SPECIAL DEFINITIONS\n"); phosg::fwrite_fmt(stream, " SPECIAL => TYPE COUNT ST* NAME\n"); - for (size_t index = 0; index < pmt->num_specials; index++) { + for (size_t index = 0; index < pmt->num_specials(); index++) { const auto& sp = pmt->get_special(index); uint8_t stars = pmt->get_special_stars(index); const char* name = ""; diff --git a/src/ItemParameterTable.cc b/src/ItemParameterTable.cc index c4d91305..f3ddf82e 100644 --- a/src/ItemParameterTable.cc +++ b/src/ItemParameterTable.cc @@ -39,114 +39,664 @@ const char* phosg::name_for_enum(ServerDropMode value) { } } -ItemParameterTable::ItemParameterTable(shared_ptr data, Version version) - : version(version), - data(data), - r(*data), - offsets_dc_protos(nullptr), - offsets_v1_v2(nullptr), - offsets_gc_nte(nullptr), - offsets_v3_le(nullptr), - offsets_v3_be(nullptr), - offsets_v4(nullptr) { - size_t offset_table_offset = is_big_endian(version) - ? this->r.pget_u32b(this->data->size() - 0x10) - : this->r.pget_u32l(this->data->size() - 0x10); - - switch (this->version) { - case Version::DC_NTE: { - this->offsets_dc_protos = &this->r.pget(offset_table_offset); - this->num_weapon_classes = 0x27; - this->num_tool_classes = 0x0D; - this->item_stars_first_id = 0x22; - this->item_stars_last_id = 0x168; - this->special_stars_begin_index = 0xAA; - this->num_specials = 0x28; - // TODO: Check if first_rare_mag_index is the same on this version - break; - } - case Version::DC_11_2000: { - this->offsets_dc_protos = &this->r.pget(offset_table_offset); - this->num_weapon_classes = 0x27; - this->num_tool_classes = 0x0E; - this->item_stars_first_id = 0x26; - this->item_stars_last_id = 0x16C; - this->special_stars_begin_index = 0xAE; - this->num_specials = 0x28; - // TODO: Check if first_rare_mag_index is the same on this version - break; - } - case Version::DC_V1: { - this->offsets_v1_v2 = &this->r.pget(offset_table_offset); - this->num_weapon_classes = 0x27; - this->num_tool_classes = 0x0E; - this->item_stars_first_id = 0x26; - this->item_stars_last_id = 0x16C; - this->special_stars_begin_index = 0xAE; - this->num_specials = 0x29; - // TODO: Check if first_rare_mag_index is the same on this version - break; - } - case Version::DC_V2: - case Version::PC_NTE: - case Version::PC_V2: { - this->offsets_v1_v2 = &this->r.pget(offset_table_offset); - this->num_weapon_classes = 0x89; - this->num_tool_classes = 0x10; - this->item_stars_first_id = 0x4E; - this->item_stars_last_id = 0x215; - this->special_stars_begin_index = 0x138; - this->num_specials = 0x29; - break; - } - - case Version::GC_NTE: { - this->offsets_gc_nte = &this->r.pget(offset_table_offset); - this->num_weapon_classes = 0x8D; - this->num_tool_classes = 0x13; - this->item_stars_first_id = 0x76; - this->item_stars_last_id = 0x298; - this->special_stars_begin_index = 0x1A3; - this->num_specials = 0x29; - break; - } - - case Version::GC_EP3_NTE: - case Version::GC_EP3: - case Version::GC_V3: - case Version::XB_V3: { - if (is_big_endian(this->version)) { - this->offsets_v3_be = &this->r.pget(offset_table_offset); - } else { - this->offsets_v3_le = &this->r.pget(offset_table_offset); - } - this->num_weapon_classes = 0xAA; - this->num_tool_classes = 0x18; - this->item_stars_first_id = 0x94; - this->item_stars_last_id = 0x2F7; - this->special_stars_begin_index = 0x1CB; - this->num_specials = 0x29; - break; - } - - case Version::BB_V4: { - this->offsets_v4 = &this->r.pget(offset_table_offset); - this->num_weapon_classes = 0xED; - this->num_tool_classes = 0x1B; - this->item_stars_first_id = 0xB1; - this->item_stars_last_id = 0x437; - this->special_stars_begin_index = 0x256; - this->num_specials = 0x29; - break; - } - default: - throw logic_error("invalid item parameter table version"); +template +struct ItemBaseV2T { + /* 00 */ U32T id = 0xFFFFFFFF; + /* 04 */ + void parse_into(ItemParameterTable::ItemBase& ret) const { + ret.id = this->id; } +} __packed_ws_be__(ItemBaseV2T, 4); - this->first_rare_mag_index = 0x28; -} +template +struct ItemBaseV3T : ItemBaseV2T { + /* 04 */ U16T type = 0; + /* 06 */ U16T skin = 0; + /* 08 */ + void parse_into(ItemParameterTable::ItemBase& ret) const { + this->ItemBaseV2T::parse_into(ret); + ret.type = this->type; + ret.skin = this->skin; + } +} __packed_ws_be__(ItemBaseV3T, 8); -set ItemParameterTable::compute_all_valid_primary_identifiers() const { +struct ItemBaseV4 : ItemBaseV3T { + /* 08 */ le_uint32_t team_points = 0; + /* 0C */ + void parse_into(ItemParameterTable::ItemBase& ret) const { + this->ItemBaseV3T::parse_into(ret); + ret.team_points = this->team_points; + } +} __packed_ws__(ItemBaseV4, 0x0C); + +struct WeaponDCProtos { + /* 00 */ ItemBaseV2T base; + /* 04 */ le_uint16_t class_flags = 0; + /* 06 */ le_uint16_t atp_min = 0; + /* 08 */ le_uint16_t atp_max = 0; + /* 0A */ le_uint16_t atp_required = 0; + /* 0C */ le_uint16_t mst_required = 0; + /* 0E */ le_uint16_t ata_required = 0; + /* 10 */ uint8_t max_grind = 0; + /* 11 */ uint8_t photon = 0; + /* 12 */ uint8_t special = 0; + /* 13 */ uint8_t ata = 0; + /* 14 */ + operator ItemParameterTable::Weapon() const { + ItemParameterTable::Weapon ret; + this->base.parse_into(ret); + ret.class_flags = this->class_flags; + ret.atp_min = this->atp_min; + ret.atp_max = this->atp_max; + ret.atp_required = this->atp_required; + ret.mst_required = this->mst_required; + ret.ata_required = this->ata_required; + ret.max_grind = this->max_grind; + ret.photon = this->photon; + ret.special = this->special; + ret.ata = this->ata; + return ret; + } +} __packed_ws__(WeaponDCProtos, 0x14); + +struct WeaponV1V2 : WeaponDCProtos { + /* 14 */ uint8_t stat_boost_entry_index = 0; // TODO: This could be larger (16 or 32 bits) + /* 15 */ parray unknown_a9; + /* 18 */ + operator ItemParameterTable::Weapon() const { + ItemParameterTable::Weapon ret = this->WeaponDCProtos::operator ItemParameterTable::Weapon(); + ret.stat_boost_entry_index = this->stat_boost_entry_index; + return ret; + } +} __packed_ws__(WeaponV1V2, 0x18); + +template +struct WeaponGCNTET { + /* 00 */ ItemBaseV3T base; + /* 08 */ U16T class_flags = 0; + /* 0A */ U16T atp_min = 0; + /* 0C */ U16T atp_max = 0; + /* 0E */ U16T atp_required = 0; + /* 10 */ U16T mst_required = 0; + /* 12 */ U16T ata_required = 0; + /* 14 */ U16T mst = 0; + /* 16 */ uint8_t max_grind = 0; + /* 17 */ uint8_t photon = 0; + /* 18 */ uint8_t special = 0; + /* 19 */ uint8_t ata = 0; + /* 1A */ uint8_t stat_boost_entry_index = 0; + /* 1B */ uint8_t projectile = 0; + /* 1C */ int8_t trail1_x = 0; + /* 1D */ int8_t trail1_y = 0; + /* 1E */ int8_t trail2_x = 0; + /* 1F */ int8_t trail2_y = 0; + /* 20 */ int8_t color = 0; + /* 21 */ parray unknown_a1 = 0; + /* 24 */ + operator ItemParameterTable::Weapon() const { + ItemParameterTable::Weapon ret; + this->base.parse_into(ret); + ret.class_flags = this->class_flags; + ret.atp_min = this->atp_min; + ret.atp_max = this->atp_max; + ret.atp_required = this->atp_required; + ret.mst_required = this->mst_required; + ret.ata_required = this->ata_required; + ret.mst = this->mst; + ret.max_grind = this->max_grind; + ret.photon = this->photon; + ret.special = this->special; + ret.ata = this->ata; + ret.stat_boost_entry_index = this->stat_boost_entry_index; + ret.projectile = this->projectile; + ret.trail1_x = this->trail1_x; + ret.trail1_y = this->trail1_y; + ret.trail2_x = this->trail2_x; + ret.trail2_y = this->trail2_y; + ret.color = this->color; + ret.unknown_a1 = this->unknown_a1; + return ret; + } +} __packed_ws_be__(WeaponGCNTET, 0x24); +using WeaponGCNTE = WeaponGCNTET; + +template +struct WeaponV3T : WeaponGCNTET { + /* 24 */ uint8_t unknown_a4 = 0; + /* 25 */ uint8_t unknown_a5 = 0; + /* 26 */ uint8_t tech_boost = 0; + /* 27 */ uint8_t behavior_flags = 0; + /* 28 */ + operator ItemParameterTable::Weapon() const { + ItemParameterTable::Weapon ret = this->WeaponGCNTET::operator ItemParameterTable::Weapon(); + ret.unknown_a4 = this->unknown_a4; + ret.unknown_a5 = this->unknown_a5; + ret.tech_boost = this->tech_boost; + ret.behavior_flags = this->behavior_flags; + return ret; + } +} __packed_ws_be__(WeaponV3T, 0x28); +using WeaponGC = WeaponV3T; +using WeaponXB = WeaponV3T; + +struct WeaponV4 { + /* 00 */ ItemBaseV4 base; + /* 0C */ le_uint16_t class_flags = 0x00FF; + /* 0E */ le_uint16_t atp_min = 0; + /* 10 */ le_uint16_t atp_max = 0; + /* 12 */ le_uint16_t atp_required = 0; + /* 14 */ le_uint16_t mst_required = 0; + /* 16 */ le_uint16_t ata_required = 0; + /* 18 */ le_uint16_t mst = 0; + /* 1A */ uint8_t max_grind = 0; + /* 1B */ uint8_t photon = 0; + /* 1C */ uint8_t special = 0; + /* 1D */ uint8_t ata = 0; + /* 1E */ uint8_t stat_boost_entry_index = 0; + /* 1F */ uint8_t projectile = 0; + /* 20 */ int8_t trail1_x = 0; + /* 21 */ int8_t trail1_y = 0; + /* 22 */ int8_t trail2_x = 0; + /* 23 */ int8_t trail2_y = 0; + /* 24 */ int8_t color = 0; + /* 25 */ parray unknown_a1 = 0; + /* 28 */ uint8_t unknown_a4 = 0; + /* 29 */ uint8_t unknown_a5 = 0; + /* 2A */ uint8_t tech_boost = 0; + /* 2B */ uint8_t behavior_flags = 0; + /* 2C */ + operator ItemParameterTable::Weapon() const { + ItemParameterTable::Weapon ret; + this->base.parse_into(ret); + ret.class_flags = this->class_flags; + ret.atp_min = this->atp_min; + ret.atp_max = this->atp_max; + ret.atp_required = this->atp_required; + ret.mst_required = this->mst_required; + ret.ata_required = this->ata_required; + ret.mst = this->mst; + ret.max_grind = this->max_grind; + ret.photon = this->photon; + ret.special = this->special; + ret.ata = this->ata; + ret.stat_boost_entry_index = this->stat_boost_entry_index; + ret.projectile = this->projectile; + ret.trail1_x = this->trail1_x; + ret.trail1_y = this->trail1_y; + ret.trail2_x = this->trail2_x; + ret.trail2_y = this->trail2_y; + ret.color = this->color; + ret.unknown_a1 = this->unknown_a1; + ret.unknown_a4 = this->unknown_a4; + ret.unknown_a5 = this->unknown_a5; + ret.tech_boost = this->tech_boost; + ret.behavior_flags = this->behavior_flags; + return ret; + } +} __packed_ws__(WeaponV4, 0x2C); + +template +struct ArmorOrShieldT { + /* V1/V2 offsets */ + /* 00 */ BaseT base; + /* 04 */ U16T dfp = 0; + /* 06 */ U16T evp = 0; + /* 08 */ uint8_t block_particle = 0; + /* 09 */ uint8_t block_effect = 0; + /* 0A */ U16T class_flags = 0x00FF; + /* 0C */ uint8_t required_level = 0; + /* 0D */ uint8_t efr = 0; + /* 0E */ uint8_t eth = 0; + /* 0F */ uint8_t eic = 0; + /* 10 */ uint8_t edk = 0; + /* 11 */ uint8_t elt = 0; + /* 12 */ uint8_t dfp_range = 0; + /* 13 */ uint8_t evp_range = 0; + /* 14 */ + operator ItemParameterTable::ArmorOrShield() const { + ItemParameterTable::ArmorOrShield ret; + this->base.parse_into(ret); + ret.dfp = this->dfp; + ret.evp = this->evp; + ret.block_particle = this->block_particle; + ret.block_effect = this->block_effect; + ret.class_flags = this->class_flags; + ret.required_level = this->required_level; + ret.efr = this->efr; + ret.eth = this->eth; + ret.eic = this->eic; + ret.edk = this->edk; + ret.elt = this->elt; + ret.dfp_range = this->dfp_range; + ret.evp_range = this->evp_range; + return ret; + } +} __attribute__((packed)); +static_assert(sizeof(ArmorOrShieldT, false>) == 0x14, "Structure size is incorrect"); +static_assert(sizeof(ArmorOrShieldT, true>) == 0x14, "Structure size is incorrect"); +static_assert(sizeof(ArmorOrShieldT, false>) == 0x18, "Structure size is incorrect"); +static_assert(sizeof(ArmorOrShieldT, true>) == 0x18, "Structure size is incorrect"); +static_assert(sizeof(ArmorOrShieldT) == 0x1C, "Structure size is incorrect"); +using ArmorOrShieldDCProtos = ArmorOrShieldT, false>; + +template +struct ArmorOrShieldFinalT : ArmorOrShieldT { + /* 14 */ uint8_t stat_boost_entry_index = 0; + /* 15 */ uint8_t tech_boost = 0; + /* 16 */ uint8_t flags_type = 0; + /* 17 */ uint8_t unknown_a4 = 0; + /* 18 */ + operator ItemParameterTable::ArmorOrShield() const { + ItemParameterTable::ArmorOrShield ret = this->ArmorOrShieldT::operator ItemParameterTable::ArmorOrShield(); + ret.stat_boost_entry_index = this->stat_boost_entry_index; + ret.tech_boost = this->tech_boost; + ret.flags_type = this->flags_type; + ret.unknown_a4 = this->unknown_a4; + return ret; + } +} __attribute__((packed)); +static_assert(sizeof(ArmorOrShieldFinalT, false>) == 0x18, "Structure size is incorrect"); +static_assert(sizeof(ArmorOrShieldFinalT, true>) == 0x18, "Structure size is incorrect"); +static_assert(sizeof(ArmorOrShieldFinalT, false>) == 0x1C, "Structure size is incorrect"); +static_assert(sizeof(ArmorOrShieldFinalT, true>) == 0x1C, "Structure size is incorrect"); +static_assert(sizeof(ArmorOrShieldFinalT) == 0x20, "Structure size is incorrect"); +using ArmorOrShieldV1V2 = ArmorOrShieldFinalT, false>; +using ArmorOrShieldGC = ArmorOrShieldFinalT, true>; +using ArmorOrShieldXB = ArmorOrShieldFinalT, false>; +using ArmorOrShieldV4 = ArmorOrShieldFinalT; + +template +struct UnitT { + /* V1/V2 offsets */ + /* 00 */ BaseT base; + /* 04 */ U16T stat = 0; + /* 06 */ U16T stat_amount = 0; + /* 08 */ + operator ItemParameterTable::Unit() const { + ItemParameterTable::Unit ret; + this->base.parse_into(ret); + ret.stat = this->stat; + ret.stat_amount = this->stat_amount; + return ret; + } +} __attribute__((packed)); +static_assert(sizeof(UnitT, false>) == 8, "Structure size is incorrect"); +static_assert(sizeof(UnitT, true>) == 8, "Structure size is incorrect"); +static_assert(sizeof(UnitT, false>) == 0x0C, "Structure size is incorrect"); +static_assert(sizeof(UnitT, true>) == 0x0C, "Structure size is incorrect"); +static_assert(sizeof(UnitT) == 0x10, "Structure size is incorrect"); +using UnitDCProtos = UnitT, false>; + +template +struct UnitFinalT : UnitT { + /* 08 */ S16T modifier_amount = 0; + /* 0A */ parray unused; + /* 0C */ + operator ItemParameterTable::Unit() const { + ItemParameterTable::Unit ret = this->UnitT::operator ItemParameterTable::Unit(); + ret.modifier_amount = this->modifier_amount; + return ret; + } +} __attribute__((packed)); +static_assert(sizeof(UnitFinalT, false>) == 0x0C, "Structure size is incorrect"); +static_assert(sizeof(UnitFinalT, true>) == 0x0C, "Structure size is incorrect"); +static_assert(sizeof(UnitFinalT, false>) == 0x10, "Structure size is incorrect"); +static_assert(sizeof(UnitFinalT, true>) == 0x10, "Structure size is incorrect"); +static_assert(sizeof(UnitFinalT) == 0x14, "Structure size is incorrect"); +using UnitV1V2 = UnitFinalT, false>; +using UnitGC = UnitFinalT, true>; +using UnitXB = UnitFinalT, false>; +using UnitV4 = UnitFinalT; + +template +struct MagT { + /* V1/V2 offsets */ + /* 00 */ BaseT base; + /* 04 */ U16T feed_table = 0; + /* 06 */ uint8_t photon_blast = 0; + /* 07 */ uint8_t activation = 0; + /* 08 */ uint8_t on_pb_full = 0; + /* 09 */ uint8_t on_low_hp = 0; + /* 0A */ uint8_t on_death = 0; + /* 0B */ uint8_t on_boss = 0; + /* 0C */ uint8_t on_pb_full_flag = 0; + /* 0D */ uint8_t on_low_hp_flag = 0; + /* 0E */ uint8_t on_death_flag = 0; + /* 0F */ uint8_t on_boss_flag = 0; + /* 10 */ + operator ItemParameterTable::Mag() const { + ItemParameterTable::Mag ret; + this->base.parse_into(ret); + ret.feed_table = this->feed_table; + ret.photon_blast = this->photon_blast; + ret.activation = this->activation; + ret.on_pb_full = this->on_pb_full; + ret.on_low_hp = this->on_low_hp; + ret.on_death = this->on_death; + ret.on_boss = this->on_boss; + ret.on_pb_full_flag = this->on_pb_full_flag; + ret.on_low_hp_flag = this->on_low_hp_flag; + ret.on_death_flag = this->on_death_flag; + ret.on_boss_flag = this->on_boss_flag; + return ret; + } +} __attribute__((packed)); +static_assert(sizeof(MagT, false>) == 0x10, "Structure size is incorrect"); +static_assert(sizeof(MagT, true>) == 0x10, "Structure size is incorrect"); +static_assert(sizeof(MagT, false>) == 0x14, "Structure size is incorrect"); +static_assert(sizeof(MagT, true>) == 0x14, "Structure size is incorrect"); +static_assert(sizeof(MagT) == 0x18, "Structure size is incorrect"); +using MagV1 = MagT, false>; + +template +struct MagV2V3V4T : MagT { + U16T class_flags = 0x00FF; + parray unused; + operator ItemParameterTable::Mag() const { + ItemParameterTable::Mag ret = this->MagT::operator ItemParameterTable::Mag(); + ret.class_flags = this->class_flags; + return ret; + } +} __attribute__((packed)); +static_assert(sizeof(MagV2V3V4T, false>) == 0x14, "Structure size is incorrect"); +static_assert(sizeof(MagV2V3V4T, true>) == 0x14, "Structure size is incorrect"); +static_assert(sizeof(MagV2V3V4T, false>) == 0x18, "Structure size is incorrect"); +static_assert(sizeof(MagV2V3V4T, true>) == 0x18, "Structure size is incorrect"); +static_assert(sizeof(MagV2V3V4T) == 0x1C, "Structure size is incorrect"); +using MagV2 = MagV2V3V4T, false>; +using MagGC = MagV2V3V4T, true>; +using MagXB = MagV2V3V4T, false>; +using MagV4 = MagV2V3V4T; + +template +struct ToolT { + /* V1/V2 offsets */ + /* 00 */ BaseT base; + /* 04 */ U16T amount = 0; + /* 06 */ U16T tech = 0; + /* 08 */ S32T cost = 0; + /* 0C */ U32T item_flags = 0; + /* 10 */ + operator ItemParameterTable::Tool() const { + ItemParameterTable::Tool ret; + this->base.parse_into(ret); + ret.amount = this->amount; + ret.tech = this->tech; + ret.cost = this->cost; + ret.item_flags = this->item_flags; + return ret; + } +} __attribute__((packed)); +static_assert(sizeof(ToolT, false>) == 0x10, "Structure size is incorrect"); +static_assert(sizeof(ToolT, true>) == 0x10, "Structure size is incorrect"); +static_assert(sizeof(ToolT, false>) == 0x14, "Structure size is incorrect"); +static_assert(sizeof(ToolT, true>) == 0x14, "Structure size is incorrect"); +static_assert(sizeof(ToolT) == 0x18, "Structure size is incorrect"); +using ToolV1V2 = ToolT, false>; +using ToolGC = ToolT, true>; +using ToolXB = ToolT, false>; +using ToolV4 = ToolT; + +using MagFeedResultsList = parray; + +template +using MagFeedResultsListOffsetsT = parray, 8>; + +template +struct SpecialT { + U16T type = 0xFFFF; + U16T amount = 0; + operator ItemParameterTable::Special() const { + return {this->type, this->amount}; + } +} __packed_ws_be__(SpecialT, 4); + +template +struct StatBoostT { + parray stats = 0; + parray, 2> amounts; + operator ItemParameterTable::StatBoost() const { + return {this->stats[0], this->amounts[0], this->stats[1], this->amounts[1]}; + } +} __packed_ws_be__(StatBoostT, 6); + +template +struct TechniqueBoostEntryT { + uint8_t tech_num = 0; + uint8_t flags = 0; + parray unused; + F32T amount = 0.0f; + operator ItemParameterTable::TechniqueBoost() const { + return {this->tech_num, this->flags, this->amount}; + } +} __packed_ws_be__(TechniqueBoostEntryT, 8); + +struct NonWeaponSaleDivisorsDCProtos { + uint8_t armor_divisor = 0; + uint8_t shield_divisor = 0; + uint8_t unit_divisor = 0; + operator ItemParameterTable::NonWeaponSaleDivisors() const { + return { + static_cast(this->armor_divisor), + static_cast(this->shield_divisor), + static_cast(this->unit_divisor), + 0.0f}; + } +} __packed_ws__(NonWeaponSaleDivisorsDCProtos, 3); + +template +struct NonWeaponSaleDivisorsT { + F32T armor_divisor = 0.0f; + F32T shield_divisor = 0.0f; + F32T unit_divisor = 0.0f; + F32T mag_divisor = 0.0f; + operator ItemParameterTable::NonWeaponSaleDivisors() const { + return {this->armor_divisor, this->shield_divisor, this->unit_divisor, this->mag_divisor}; + } +} __packed_ws_be__(NonWeaponSaleDivisorsT, 0x10); + +template +struct ShieldEffectT { + U32T sound_id; + U32T unknown_a1; + operator ItemParameterTable::ShieldEffect() const { + return {this->sound_id, this->unknown_a1}; + } +} __packed_ws_be__(ShieldEffectT, 8); + +template +struct PhotonColorEntryT { + /* 00 */ U32T unknown_a1; + /* 04 */ parray, 4> unknown_a2; + /* 14 */ parray, 4> unknown_a3; + /* 24 */ + operator ItemParameterTable::PhotonColorEntry() const { + ItemParameterTable::PhotonColorEntry ret; + ret.unknown_a1 = this->unknown_a1; + ret.unknown_a2.x = this->unknown_a2[0]; + ret.unknown_a2.y = this->unknown_a2[1]; + ret.unknown_a2.z = this->unknown_a2[2]; + ret.unknown_a2.t = this->unknown_a2[3]; + ret.unknown_a3.x = this->unknown_a3[0]; + ret.unknown_a3.y = this->unknown_a3[1]; + ret.unknown_a3.z = this->unknown_a3[2]; + ret.unknown_a3.t = this->unknown_a3[3]; + return ret; + } +} __packed_ws_be__(PhotonColorEntryT, 0x24); + +template +struct UnknownA1T { + U16T unknown_a1; + U16T unknown_a2; + operator ItemParameterTable::UnknownA1() const { + return {this->unknown_a1, this->unknown_a2}; + } +} __packed_ws_be__(UnknownA1T, 4); + +template +struct UnknownA5T { + U32T target_param; + U32T unknown_a2; + U32T unknown_a3; + operator ItemParameterTable::UnknownA5() const { + return {this->target_param, this->unknown_a2, this->unknown_a3}; + } +} __packed_ws_be__(UnknownA5T, 0x0C); + +template +struct WeaponRangeT { + F32T unknown_a1; + F32T unknown_a2; + U32T unknown_a3; + U32T unknown_a4; + U32T unknown_a5; + operator ItemParameterTable::WeaponRange() const { + return {this->unknown_a1, this->unknown_a2, this->unknown_a3, this->unknown_a4, this->unknown_a5}; + } +} __packed_ws_be__(WeaponRangeT, 0x14); + +template +struct WeaponEffect { + U32T sound_id1; + U32T eff_value1; + U32T sound_id2; + U32T eff_value2; + parray unknown_a5; + operator ItemParameterTable::WeaponEffect() const { + return {this->sound_id1, this->eff_value1, this->sound_id2, this->eff_value2, this->unknown_a5}; + } +} __packed_ws_be__(WeaponEffect, 0x20); + +/* The fields in the root structure are: + * DCTE / 112K / V1 / V2 / GCTE / V3 / V4 + * 0013 / 0013 / 0013 / 0013 / / / entry_count // Count of pointers in root struct; unused + * 0668 / 0668 / / / / / armor_stat_boost_index_table // -> [uint8_t] + * 2D94 / 2E28 / 3258 / 5A5C / 6E4C / EF90 / 1478C armor_table // -> [{count, offset -> [ArmorOrShieldV*]}](2; armors and shields) + * / / / / 737C / F5D0 / 14FF4 combination_table // -> {count, offset -> [ItemCombination]} + * 2F54 / 2FF0 / 3420 / 5F4C / 7384 / F608 / 1502C mag_feed_table // -> MagFeedResultsTable + * 2DAC / 2E40 / 3270 / 5A74 / 6E64 / EFA8 / 147A4 mag_table // -> {count, offset -> [MagV*]} + * / / / / 69D8 / DF88 / 12894 max_tech_level_table // -> MaxTechniqueLevels + * 1FE6 / 207A / 248C / 40A8 / 4A80 / BBCC / 0F83C non_weapon_sale_divisor_table // -> NonWeaponSaleDivisors + * 1994 / 1A28 / 1DB0 / 2E4C / 37A4 / A7FC / 0DE7C photon_color_table // -> PhotonColorEntry[...] + * / / / / / F600 / 15024 ranged_special_table // -> {count, offset -> [RangedSpecial]} + * / / 3198 / 5704 / 61B8 / D6E4 / 11C80 shield_effect_table // -> ShieldEffect[...] (indexed by data1[2]) + * 030C / 030C / / / / / shield_stat_boost_index_table // -> [uint8_t] + * 275E / 27F4 / 2C12 / 4540 / 4F72 / C100 / 0FE3C special_table // -> [Special] + * 22A9 / 233D / 275C / 4378 / 4D50 / BE9C / 0FB0C star_value_table // -> [uint8_t] (indexed by .id from weapon, armor, etc.) + * 2CE4 / 2D78 / 2CB8 / 58DC / 68B8 / DE50 / 1275C stat_boost_table // -> [StatBoost] + * / / / / 6B1C / EB8C / 14278 tech_boost_table // -> [TechniqueBoostEntry[3]] + * 2DB4 / 2E48 / 3278 / 5A7C / 6E6C / EFB0 / 147AC tool_table // -> [{count, offset -> [ToolV*]}] (last if out of range) + * 2DA4 / 2E38 / 3268 / 5A6C / 6E5C / EFA0 / 1479C unit_table // -> {count, offset -> [UnitV*]} (last if out of range) + * 23EE / 2484 / 28A2 / 45E4 / / / unknown_a1 // TODO + * / / / / 68B0 / DE48 / 12754 unknown_a5 // -> {count, offset -> [UnknownA5]} + * / / / / / F5F8 / 1501C unsealable_table // -> {count, offset -> [UnsealableItem]} + * / / / / / F5F0 / 15014 unwrap_table // -> {count, offset -> [{count, offset -> [EventItem]}]} + * 1F98 / 202C / 23C8 / 3DF8 / 47BC / B88C / 0F4B8 weapon_class_table // -> [uint8_t](0x89) + * 2804 / 2898 / / / 5018 / C1A4 / 0FEE0 weapon_effect_table // -> [WeaponEffect] + * 1C64 / 1CF8 / 2080 / 32CC / 3A74 / AACC / 0E194 weapon_range_table // -> WeaponRange[...] + * 1FBF / 2053 / 23F0 / 3E84 / 484C / B938 / 0F5A8 weapon_sale_divisor_table // -> [uint8_t] on DC protos; [float] on all other versions + * 1908 / 199C / / / / / weapon_stat_boost_index_table // -> [StatBoost] + * 2E1C / 2EB8 / 32E8 / 5AFC / 6F0C / F078 / 14884 weapon_table // -> [{count, offset -> [WeaponV*]}] + */ + +struct RootDCProtos { + /* 00 */ le_uint32_t entry_count; + /* 04 */ le_uint32_t weapon_table; + /* 08 */ le_uint32_t armor_table; + /* 0C */ le_uint32_t unit_table; + /* 10 */ le_uint32_t tool_table; + /* 14 */ le_uint32_t mag_table; + /* 18 */ le_uint32_t weapon_class_table; + /* 1C */ le_uint32_t photon_color_table; + /* 20 */ le_uint32_t weapon_range_table; + /* 24 */ le_uint32_t weapon_integral_sale_divisor_table; + /* 28 */ le_uint32_t non_weapon_integral_sale_divisor_table; + /* 2C */ le_uint32_t mag_feed_table; + /* 30 */ le_uint32_t star_value_table; + /* 34 */ le_uint32_t unknown_a1; + /* 38 */ le_uint32_t special_table; + /* 3C */ le_uint32_t weapon_effect_table; + /* 40 */ le_uint32_t weapon_stat_boost_index_table; + /* 44 */ le_uint32_t armor_stat_boost_index_table; + /* 48 */ le_uint32_t shield_stat_boost_index_table; + /* 4C */ le_uint32_t stat_boost_table; +} __packed_ws__(RootDCProtos, 0x50); + +struct RootV1V2 { + /* 00 */ le_uint32_t entry_count; + /* 04 */ le_uint32_t weapon_table; + /* 08 */ le_uint32_t armor_table; + /* 0C */ le_uint32_t unit_table; + /* 10 */ le_uint32_t tool_table; + /* 14 */ le_uint32_t mag_table; + /* 18 */ le_uint32_t weapon_class_table; + /* 1C */ le_uint32_t photon_color_table; + /* 20 */ le_uint32_t weapon_range_table; + /* 24 */ le_uint32_t weapon_sale_divisor_table; + /* 28 */ le_uint32_t non_weapon_sale_divisor_table; + /* 2C */ le_uint32_t mag_feed_table; + /* 30 */ le_uint32_t star_value_table; + /* 34 */ le_uint32_t unknown_a1; + /* 38 */ le_uint32_t special_table; + /* 3C */ le_uint32_t stat_boost_table; + /* 40 */ le_uint32_t shield_effect_table; +} __packed_ws__(RootV1V2, 0x44); + +struct RootGCNTE { + /* 00 */ be_uint32_t weapon_table; + /* 04 */ be_uint32_t armor_table; + /* 08 */ be_uint32_t unit_table; + /* 0C */ be_uint32_t tool_table; + /* 10 */ be_uint32_t mag_table; + /* 14 */ be_uint32_t weapon_class_table; + /* 18 */ be_uint32_t photon_color_table; + /* 1C */ be_uint32_t weapon_range_table; + /* 20 */ be_uint32_t weapon_sale_divisor_table; + /* 24 */ be_uint32_t non_weapon_sale_divisor_table; + /* 28 */ be_uint32_t mag_feed_table; + /* 2C */ be_uint32_t star_value_table; + /* 30 */ be_uint32_t special_table; + /* 34 */ be_uint32_t weapon_effect_table; + /* 38 */ be_uint32_t stat_boost_table; + /* 3C */ be_uint32_t shield_effect_table; + /* 40 */ be_uint32_t max_tech_level_table; + /* 44 */ be_uint32_t combination_table; + /* 48 */ be_uint32_t unknown_a5; + /* 4C */ be_uint32_t tech_boost_table; +} __packed_ws__(RootGCNTE, 0x50); + +template +struct RootV3V4T { + /* 00 */ U32T weapon_table; + /* 04 */ U32T armor_table; + /* 08 */ U32T unit_table; + /* 0C */ U32T tool_table; + /* 10 */ U32T mag_table; + /* 14 */ U32T weapon_class_table; + /* 18 */ U32T photon_color_table; + /* 1C */ U32T weapon_range_table; + /* 20 */ U32T weapon_sale_divisor_table; + /* 24 */ U32T non_weapon_sale_divisor_table; + /* 28 */ U32T mag_feed_table; + /* 2C */ U32T star_value_table; + /* 30 */ U32T special_table; + /* 34 */ U32T weapon_effect_table; + /* 38 */ U32T stat_boost_table; + /* 3C */ U32T shield_effect_table; + /* 40 */ U32T max_tech_level_table; + /* 44 */ U32T combination_table; + /* 48 */ U32T unknown_a5; + /* 4C */ U32T tech_boost_table; + /* 50 */ U32T unwrap_table; + /* 54 */ U32T unsealable_table; + /* 58 */ U32T ranged_special_table; +} __packed_ws_be__(RootV3V4T, 0x5C); + +ItemParameterTable::ItemParameterTable(std::shared_ptr data) + : data(data), r(*this->data) {} + +std::set ItemParameterTable::compute_all_valid_primary_identifiers() const { set ret; auto find_items_1d = [&](uint64_t data1, size_t position) -> size_t { @@ -181,596 +731,12 @@ set ItemParameterTable::compute_all_valid_primary_identifiers() const return ret; } -ItemParameterTable::WeaponV4 ItemParameterTable::WeaponDCProtos::to_v4() const { - WeaponV4 ret; - ret.base.id = this->base.id; - ret.class_flags = this->class_flags; - ret.atp_min = this->atp_min; - ret.atp_max = this->atp_max; - ret.atp_required = this->atp_required; - ret.mst_required = this->mst_required; - ret.ata_required = this->ata_required; - ret.max_grind = this->max_grind; - ret.photon = this->photon; - ret.special = this->special; - ret.ata = this->ata; - return ret; -} - -ItemParameterTable::WeaponV4 ItemParameterTable::WeaponV1V2::to_v4() const { - WeaponV4 ret; - ret.base.id = this->base.id; - ret.class_flags = this->class_flags; - ret.atp_min = this->atp_min; - ret.atp_max = this->atp_max; - ret.atp_required = this->atp_required; - ret.mst_required = this->mst_required; - ret.ata_required = this->ata_required; - ret.max_grind = this->max_grind; - ret.photon = this->photon; - ret.special = this->special; - ret.ata = this->ata; - ret.stat_boost_entry_index = this->stat_boost_entry_index; - return ret; -} - -ItemParameterTable::WeaponV4 ItemParameterTable::WeaponGCNTE::to_v4() const { - WeaponV4 ret; - ret.base.id = this->base.id; - ret.base.type = this->base.type; - ret.base.skin = this->base.skin; - ret.class_flags = this->class_flags; - ret.atp_min = this->atp_min; - ret.atp_max = this->atp_max; - ret.atp_required = this->atp_required; - ret.mst_required = this->mst_required; - ret.ata_required = this->ata_required; - ret.mst = this->mst; - ret.max_grind = this->max_grind; - ret.photon = this->photon; - ret.special = this->special; - ret.ata = this->ata; - ret.stat_boost_entry_index = this->stat_boost_entry_index; - ret.projectile = this->projectile; - ret.trail1_x = this->trail1_x; - ret.trail1_y = this->trail1_y; - ret.trail2_x = this->trail2_x; - ret.trail2_y = this->trail2_y; - ret.color = this->color; - ret.unknown_a1 = this->unknown_a1; - return ret; -} - -template -ItemParameterTable::WeaponV4 ItemParameterTable::WeaponV3T::to_v4() const { - WeaponV4 ret; - ret.base.id = this->base.id; - ret.base.type = this->base.type; - ret.base.skin = this->base.skin; - ret.class_flags = this->class_flags; - ret.atp_min = this->atp_min; - ret.atp_max = this->atp_max; - ret.atp_required = this->atp_required; - ret.mst_required = this->mst_required; - ret.ata_required = this->ata_required; - ret.mst = this->mst; - ret.max_grind = this->max_grind; - ret.photon = this->photon; - ret.special = this->special; - ret.ata = this->ata; - ret.stat_boost_entry_index = this->stat_boost_entry_index; - ret.projectile = this->projectile; - ret.trail1_x = this->trail1_x; - ret.trail1_y = this->trail1_y; - ret.trail2_x = this->trail2_x; - ret.trail2_y = this->trail2_y; - ret.color = this->color; - ret.unknown_a1 = this->unknown_a1; - ret.unknown_a4 = this->unknown_a4; - ret.unknown_a5 = this->unknown_a5; - ret.tech_boost = this->tech_boost; - ret.behavior_flags = this->behavior_flags; - return ret; -} - -ItemParameterTable::ArmorOrShieldV4 ItemParameterTable::ArmorOrShieldDCProtos::to_v4() const { - ArmorOrShieldV4 ret; - ret.base.id = this->base.id; - ret.dfp = this->dfp; - ret.evp = this->evp; - ret.block_particle = this->block_particle; - ret.block_effect = this->block_effect; - ret.class_flags = this->class_flags; - ret.required_level = this->required_level; - ret.efr = this->efr; - ret.eth = this->eth; - ret.eic = this->eic; - ret.edk = this->edk; - ret.elt = this->elt; - ret.dfp_range = this->dfp_range; - ret.evp_range = this->evp_range; - return ret; -} - -ItemParameterTable::ArmorOrShieldV4 ItemParameterTable::ArmorOrShieldV1V2::to_v4() const { - ArmorOrShieldV4 ret; - ret.base.id = this->base.id; - ret.dfp = this->dfp; - ret.evp = this->evp; - ret.block_particle = this->block_particle; - ret.block_effect = this->block_effect; - ret.class_flags = this->class_flags; - ret.required_level = this->required_level; - ret.efr = this->efr; - ret.eth = this->eth; - ret.eic = this->eic; - ret.edk = this->edk; - ret.elt = this->elt; - ret.dfp_range = this->dfp_range; - ret.evp_range = this->evp_range; - ret.stat_boost_entry_index = this->stat_boost_entry_index; - ret.tech_boost = this->tech_boost; - ret.flags_type = this->flags_type; - ret.unknown_a4 = this->unknown_a4; - return ret; -} - -template -ItemParameterTable::ArmorOrShieldV4 ItemParameterTable::ArmorOrShieldV3T::to_v4() const { - ArmorOrShieldV4 ret; - ret.base.id = this->base.id; - ret.base.type = this->base.type; - ret.base.skin = this->base.skin; - ret.dfp = this->dfp; - ret.evp = this->evp; - ret.block_particle = this->block_particle; - ret.block_effect = this->block_effect; - ret.class_flags = this->class_flags; - ret.required_level = this->required_level; - ret.efr = this->efr; - ret.eth = this->eth; - ret.eic = this->eic; - ret.edk = this->edk; - ret.elt = this->elt; - ret.dfp_range = this->dfp_range; - ret.evp_range = this->evp_range; - ret.stat_boost_entry_index = this->stat_boost_entry_index; - ret.tech_boost = this->tech_boost; - ret.flags_type = this->flags_type; - ret.unknown_a4 = this->unknown_a4; - return ret; -} - -ItemParameterTable::UnitV4 ItemParameterTable::UnitDCProtos::to_v4() const { - UnitV4 ret; - ret.base.id = this->base.id; - ret.stat = this->stat; - ret.stat_amount = this->stat_amount; - return ret; -} - -ItemParameterTable::UnitV4 ItemParameterTable::UnitV1V2::to_v4() const { - UnitV4 ret; - ret.base.id = this->base.id; - ret.stat = this->stat; - ret.stat_amount = this->stat_amount; - ret.modifier_amount = this->modifier_amount; - return ret; -} - -template -ItemParameterTable::UnitV4 ItemParameterTable::UnitV3T::to_v4() const { - UnitV4 ret; - ret.base.id = this->base.id; - ret.base.type = this->base.type; - ret.base.skin = this->base.skin; - ret.stat = this->stat; - ret.stat_amount = this->stat_amount; - ret.modifier_amount = this->modifier_amount; - return ret; -} - -ItemParameterTable::MagV4 ItemParameterTable::MagV1::to_v4() const { - MagV4 ret; - ret.base.id = this->base.id; - ret.feed_table = this->feed_table; - ret.photon_blast = this->photon_blast; - ret.activation = this->activation; - ret.on_pb_full = this->on_pb_full; - ret.on_low_hp = this->on_low_hp; - ret.on_death = this->on_death; - ret.on_boss = this->on_boss; - ret.on_pb_full_flag = this->on_pb_full_flag; - ret.on_low_hp_flag = this->on_low_hp_flag; - ret.on_death_flag = this->on_death_flag; - ret.on_boss_flag = this->on_boss_flag; - return ret; -} - -ItemParameterTable::MagV4 ItemParameterTable::MagV2::to_v4() const { - MagV4 ret; - ret.base.id = this->base.id; - ret.feed_table = this->feed_table; - ret.photon_blast = this->photon_blast; - ret.activation = this->activation; - ret.on_pb_full = this->on_pb_full; - ret.on_low_hp = this->on_low_hp; - ret.on_death = this->on_death; - ret.on_boss = this->on_boss; - ret.on_pb_full_flag = this->on_pb_full_flag; - ret.on_low_hp_flag = this->on_low_hp_flag; - ret.on_death_flag = this->on_death_flag; - ret.on_boss_flag = this->on_boss_flag; - ret.class_flags = this->class_flags; - return ret; -} - -template -ItemParameterTable::MagV4 ItemParameterTable::MagV3T::to_v4() const { - MagV4 ret; - ret.base.id = this->base.id; - ret.base.type = this->base.type; - ret.base.skin = this->base.skin; - ret.feed_table = this->feed_table; - ret.photon_blast = this->photon_blast; - ret.activation = this->activation; - ret.on_pb_full = this->on_pb_full; - ret.on_low_hp = this->on_low_hp; - ret.on_death = this->on_death; - ret.on_boss = this->on_boss; - ret.on_pb_full_flag = this->on_pb_full_flag; - ret.on_low_hp_flag = this->on_low_hp_flag; - ret.on_death_flag = this->on_death_flag; - ret.on_boss_flag = this->on_boss_flag; - ret.class_flags = this->class_flags; - return ret; -} - -ItemParameterTable::ToolV4 ItemParameterTable::ToolV1V2::to_v4() const { - ToolV4 ret; - ret.base.id = this->base.id; - ret.amount = this->amount; - ret.tech = this->tech; - ret.cost = this->cost; - ret.item_flags = this->item_flags; - return ret; -} - -template -ItemParameterTable::ToolV4 ItemParameterTable::ToolV3T::to_v4() const { - ToolV4 ret; - ret.base.id = this->base.id; - ret.base.type = this->base.type; - ret.base.skin = this->base.skin; - ret.amount = this->amount; - ret.tech = this->tech; - ret.cost = this->cost; - ret.item_flags = this->item_flags; - return ret; -} - -template -size_t indirect_lookup_2d_count(const phosg::StringReader& r, size_t root_offset, size_t co_index) { - return r.pget>(root_offset + sizeof(ArrayRefT) * co_index).count; -} - -template -const T& indirect_lookup_2d(const phosg::StringReader& r, size_t root_offset, size_t co_index, size_t item_index) { - const auto& co = r.pget>(root_offset + sizeof(ArrayRefT) * co_index); - if (item_index >= co.count) { - throw out_of_range("item ID out of range"); - } - return r.pget(co.offset + sizeof(T) * item_index); -} - -size_t ItemParameterTable::num_weapons_in_class(uint8_t data1_1) const { - if (data1_1 >= this->num_weapon_classes) { - throw out_of_range("weapon ID out of range"); - } - if (this->offsets_dc_protos) { - return indirect_lookup_2d_count(this->r, this->offsets_dc_protos->weapon_table, data1_1); - } else if (this->offsets_v1_v2) { - return indirect_lookup_2d_count(this->r, this->offsets_v1_v2->weapon_table, data1_1); - } else if (this->offsets_gc_nte) { - return indirect_lookup_2d_count(this->r, this->offsets_gc_nte->weapon_table, data1_1); - } else if (this->offsets_v3_le) { - return indirect_lookup_2d_count(this->r, this->offsets_v3_le->weapon_table, data1_1); - } else if (this->offsets_v3_be) { - return indirect_lookup_2d_count(this->r, this->offsets_v3_be->weapon_table, data1_1); - } else if (this->offsets_v4) { - return indirect_lookup_2d_count(this->r, this->offsets_v4->weapon_table, data1_1); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -const ItemParameterTable::WeaponV4& ItemParameterTable::get_weapon(uint8_t data1_1, uint8_t data1_2) const { - if (data1_1 >= this->num_weapon_classes) { - throw out_of_range("weapon ID out of range"); - } - - if (this->offsets_v4) { - return indirect_lookup_2d(this->r, this->offsets_v4->weapon_table, data1_1, data1_2); - } - - uint16_t key = (data1_1 << 8) | data1_2; - try { - return this->parsed_weapons.at(key); - } catch (const std::out_of_range&) { - WeaponV4 def_v4; - if (this->offsets_dc_protos) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_dc_protos->weapon_table, data1_1, data1_2).to_v4(); - } else if (this->offsets_v1_v2) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v1_v2->weapon_table, data1_1, data1_2).to_v4(); - } else if (this->offsets_gc_nte) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_gc_nte->weapon_table, data1_1, data1_2).to_v4(); - } else if (this->offsets_v3_le) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_le->weapon_table, data1_1, data1_2).to_v4(); - } else if (this->offsets_v3_be) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_be->weapon_table, data1_1, data1_2).to_v4(); - } else { - throw logic_error("table is not v2, v3, or v4"); - } - return this->parsed_weapons.emplace(key, def_v4).first->second; - } -} - -size_t ItemParameterTable::num_armors_or_shields_in_class(uint8_t data1_1) const { - if ((data1_1 < 1) || (data1_1 > 2)) { - throw out_of_range("armor/shield class ID out of range"); - } - if (this->offsets_dc_protos) { - return indirect_lookup_2d_count(this->r, this->offsets_dc_protos->armor_table, data1_1 - 1); - } else if (this->offsets_v1_v2) { - return indirect_lookup_2d_count(this->r, this->offsets_v1_v2->armor_table, data1_1 - 1); - } else if (this->offsets_gc_nte) { - return indirect_lookup_2d_count(this->r, this->offsets_gc_nte->armor_table, data1_1 - 1); - } else if (this->offsets_v3_le) { - return indirect_lookup_2d_count(this->r, this->offsets_v3_le->armor_table, data1_1 - 1); - } else if (this->offsets_v3_be) { - return indirect_lookup_2d_count(this->r, this->offsets_v3_be->armor_table, data1_1 - 1); - } else if (this->offsets_v4) { - return indirect_lookup_2d_count(this->r, this->offsets_v4->armor_table, data1_1 - 1); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -const ItemParameterTable::ArmorOrShieldV4& ItemParameterTable::get_armor_or_shield(uint8_t data1_1, uint8_t data1_2) const { - if ((data1_1 < 1) || (data1_1 > 2)) { - throw out_of_range("armor/shield class ID out of range"); - } - - if (this->offsets_v4) { - return indirect_lookup_2d(this->r, this->offsets_v4->armor_table, data1_1 - 1, data1_2); - } - - auto& parsed_vec = (data1_1 == 2) ? this->parsed_shields : this->parsed_armors; - try { - const auto& ret = parsed_vec.at(data1_2); - if (ret.base.id == 0xFFFFFFFF) { - throw out_of_range("cache entry not populated"); - } - return ret; - } catch (const std::out_of_range&) { - while (data1_2 >= parsed_vec.size()) { - auto& def_v4 = parsed_vec.emplace_back(); - if (this->offsets_dc_protos) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_dc_protos->armor_table, data1_1 - 1, parsed_vec.size() - 1).to_v4(); - } else if (this->offsets_v1_v2) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v1_v2->armor_table, data1_1 - 1, parsed_vec.size() - 1).to_v4(); - } else if (this->offsets_gc_nte) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_gc_nte->armor_table, data1_1 - 1, parsed_vec.size() - 1).to_v4(); - } else if (this->offsets_v3_le) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_le->armor_table, data1_1 - 1, parsed_vec.size() - 1).to_v4(); - } else if (this->offsets_v3_be) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_be->armor_table, data1_1 - 1, parsed_vec.size() - 1).to_v4(); - } else { - throw logic_error("table is not v2, v3, or v4"); - } - } - return parsed_vec[data1_2]; - } -} - -size_t ItemParameterTable::num_units() const { - if (this->offsets_dc_protos) { - return indirect_lookup_2d_count(this->r, this->offsets_dc_protos->unit_table, 0); - } else if (this->offsets_v1_v2) { - return indirect_lookup_2d_count(this->r, this->offsets_v1_v2->unit_table, 0); - } else if (this->offsets_gc_nte) { - return indirect_lookup_2d_count(this->r, this->offsets_gc_nte->unit_table, 0); - } else if (this->offsets_v3_le) { - return indirect_lookup_2d_count(this->r, this->offsets_v3_le->unit_table, 0); - } else if (this->offsets_v3_be) { - return indirect_lookup_2d_count(this->r, this->offsets_v3_be->unit_table, 0); - } else if (this->offsets_v4) { - return indirect_lookup_2d_count(this->r, this->offsets_v4->unit_table, 0); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -const ItemParameterTable::UnitV4& ItemParameterTable::get_unit(uint8_t data1_2) const { - if (this->offsets_v4) { - return indirect_lookup_2d(this->r, this->offsets_v4->unit_table, 0, data1_2); - } - - try { - const auto& ret = this->parsed_units.at(data1_2); - if (ret.base.id == 0xFFFFFFFF) { - throw out_of_range("cache entry not populated"); - } - return ret; - } catch (const std::out_of_range&) { - while (data1_2 >= this->parsed_units.size()) { - auto& def_v4 = this->parsed_units.emplace_back(); - if (this->offsets_dc_protos) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_dc_protos->unit_table, 0, this->parsed_units.size() - 1).to_v4(); - } else if (this->offsets_v1_v2) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v1_v2->unit_table, 0, this->parsed_units.size() - 1).to_v4(); - } else if (this->offsets_gc_nte) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_gc_nte->unit_table, 0, this->parsed_units.size() - 1).to_v4(); - } else if (this->offsets_v3_le) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_le->unit_table, 0, this->parsed_units.size() - 1).to_v4(); - } else if (this->offsets_v3_be) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_be->unit_table, 0, this->parsed_units.size() - 1).to_v4(); - } else { - throw logic_error("table is not v2, v3, or v4"); - } - } - return this->parsed_units[data1_2]; - } -} - -size_t ItemParameterTable::num_mags() const { - if (this->offsets_dc_protos) { - return indirect_lookup_2d_count(this->r, this->offsets_dc_protos->mag_table, 0); - } else if (this->offsets_v1_v2) { - return indirect_lookup_2d_count(this->r, this->offsets_v1_v2->mag_table, 0); - } else if (this->offsets_gc_nte) { - return indirect_lookup_2d_count(this->r, this->offsets_gc_nte->mag_table, 0); - } else if (this->offsets_v3_le) { - return indirect_lookup_2d_count(this->r, this->offsets_v3_le->mag_table, 0); - } else if (this->offsets_v3_be) { - return indirect_lookup_2d_count(this->r, this->offsets_v3_be->mag_table, 0); - } else if (this->offsets_v4) { - return indirect_lookup_2d_count(this->r, this->offsets_v4->mag_table, 0); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -const ItemParameterTable::MagV4& ItemParameterTable::get_mag(uint8_t data1_1) const { - if (this->offsets_v4) { - return indirect_lookup_2d(this->r, this->offsets_v4->mag_table, 0, data1_1); - } - - try { - const auto& ret = this->parsed_mags.at(data1_1); - if (ret.base.id == 0xFFFFFFFF) { - throw out_of_range("cache entry not populated"); - } - return ret; - } catch (const std::out_of_range&) { - while (data1_1 >= this->parsed_mags.size()) { - auto& def_v4 = this->parsed_mags.emplace_back(); - if (this->offsets_dc_protos) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_dc_protos->mag_table, 0, this->parsed_mags.size() - 1).to_v4(); - } else if (this->offsets_v1_v2) { - if (is_v1(this->version)) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v1_v2->mag_table, 0, this->parsed_mags.size() - 1).to_v4(); - } else { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v1_v2->mag_table, 0, this->parsed_mags.size() - 1).to_v4(); - } - } else if (this->offsets_gc_nte) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_gc_nte->mag_table, 0, this->parsed_mags.size() - 1).to_v4(); - } else if (this->offsets_v3_le) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_le->mag_table, 0, this->parsed_mags.size() - 1).to_v4(); - } else if (this->offsets_v3_be) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_be->mag_table, 0, this->parsed_mags.size() - 1).to_v4(); - } else { - throw logic_error("table is not v2, v3, or v4"); - } - } - return this->parsed_mags[data1_1]; - } -} - -size_t ItemParameterTable::num_tools_in_class(uint8_t data1_1) const { - if (data1_1 >= this->num_tool_classes) { - throw out_of_range("tool class ID out of range"); - } - if (this->offsets_dc_protos) { - return indirect_lookup_2d_count(this->r, this->offsets_dc_protos->tool_table, data1_1); - } else if (this->offsets_v1_v2) { - return indirect_lookup_2d_count(this->r, this->offsets_v1_v2->tool_table, data1_1); - } else if (this->offsets_gc_nte) { - return indirect_lookup_2d_count(this->r, this->offsets_gc_nte->tool_table, data1_1); - } else if (this->offsets_v3_le) { - return indirect_lookup_2d_count(this->r, this->offsets_v3_le->tool_table, data1_1); - } else if (this->offsets_v3_be) { - return indirect_lookup_2d_count(this->r, this->offsets_v3_be->tool_table, data1_1); - } else if (this->offsets_v4) { - return indirect_lookup_2d_count(this->r, this->offsets_v4->tool_table, data1_1); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -const ItemParameterTable::ToolV4& ItemParameterTable::get_tool(uint8_t data1_1, uint8_t data1_2) const { - if (data1_1 >= this->num_tool_classes) { - throw out_of_range("tool class ID out of range"); - } - - if (this->offsets_v4) { - return indirect_lookup_2d(this->r, this->offsets_v4->tool_table, data1_1, data1_2); - } - - uint16_t key = (data1_1 << 8) | data1_2; - try { - return this->parsed_tools.at(key); - } catch (const std::out_of_range&) { - ToolV4 def_v4; - - if (this->offsets_dc_protos) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_dc_protos->tool_table, data1_1, data1_2).to_v4(); - } else if (this->offsets_v1_v2) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v1_v2->tool_table, data1_1, data1_2).to_v4(); - } else if (this->offsets_gc_nte) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_gc_nte->tool_table, data1_1, data1_2).to_v4(); - } else if (this->offsets_v3_le) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_le->tool_table, data1_1, data1_2).to_v4(); - } else if (this->offsets_v3_be) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_be->tool_table, data1_1, data1_2).to_v4(); - } else { - throw logic_error("table is not v2, v3, or v4"); - } - - return this->parsed_tools.emplace(key, def_v4).first->second; - } -} - -template -pair ItemParameterTable::find_tool_by_id_t(uint32_t tool_table_offset, uint32_t item_id) const { - const auto* cos = &this->r.pget>( - tool_table_offset, this->num_tool_classes * sizeof(ArrayRefT)); - for (size_t z = 0; z < this->num_tool_classes; z++) { - const auto& co = cos[z]; - const auto* defs = &this->r.pget(co.offset, sizeof(ToolDefT) * co.count); - for (size_t y = 0; y < co.count; y++) { - if (defs[y].base.id == item_id) { - return make_pair(z, y); - } - } - } - throw out_of_range(std::format("invalid tool class {:08X}", item_id)); -} - -pair ItemParameterTable::find_tool_by_id(uint32_t item_id) const { - if (this->offsets_dc_protos) { - return this->find_tool_by_id_t(this->offsets_dc_protos->tool_table, item_id); - } else if (this->offsets_v1_v2) { - return this->find_tool_by_id_t(this->offsets_v1_v2->tool_table, item_id); - } else if (this->offsets_gc_nte) { - return this->find_tool_by_id_t(this->offsets_gc_nte->tool_table, item_id); - } else if (this->offsets_v3_le) { - return this->find_tool_by_id_t(this->offsets_v3_le->tool_table, item_id); - } else if (this->offsets_v3_be) { - return this->find_tool_by_id_t(this->offsets_v3_be->tool_table, item_id); - } else if (this->offsets_v4) { - return this->find_tool_by_id_t(this->offsets_v4->tool_table, item_id); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -variant< - const ItemParameterTable::WeaponV4*, - const ItemParameterTable::ArmorOrShieldV4*, - const ItemParameterTable::UnitV4*, - const ItemParameterTable::MagV4*, - const ItemParameterTable::ToolV4*> +std::variant< + const ItemParameterTable::Weapon*, + const ItemParameterTable::ArmorOrShield*, + const ItemParameterTable::Unit*, + const ItemParameterTable::Mag*, + const ItemParameterTable::Tool*> ItemParameterTable::definition_for_primary_identifier(uint32_t primary_identifier) const { uint8_t data1_0 = (primary_identifier >> 24) & 0xFF; uint8_t data1_1 = (primary_identifier >> 16) & 0xFF; @@ -799,255 +765,24 @@ ItemParameterTable::definition_for_primary_identifier(uint32_t primary_identifie } } -template -float ItemParameterTable::get_sale_divisor_t(const OffsetsT* offsets, uint8_t data1_0, uint8_t data1_1) const { - switch (data1_0) { - case 0: - if (data1_1 >= this->num_weapon_classes) { - return 0.0f; - } - return this->r.pget>(offsets->weapon_sale_divisor_table + data1_1 * sizeof(F32T)); - - case 1: { - const auto& divisors = this->r.pget>(offsets->sale_divisor_table); - switch (data1_1) { - case 1: - return divisors.armor_divisor; - case 2: - return divisors.shield_divisor; - case 3: - return divisors.unit_divisor; - } - return 0.0f; - } - - case 2: { - const auto& divisors = this->r.pget>(offsets->sale_divisor_table); - return divisors.mag_divisor; - } - - default: - return 0.0f; - } -} - -float ItemParameterTable::get_sale_divisor(uint8_t data1_0, uint8_t data1_1) const { - if (this->offsets_dc_protos) { - return this->get_sale_divisor_t(this->offsets_dc_protos, data1_0, data1_1); - } else if (this->offsets_v1_v2) { - return this->get_sale_divisor_t(this->offsets_v1_v2, data1_0, data1_1); - } else if (this->offsets_gc_nte) { - return this->get_sale_divisor_t(this->offsets_gc_nte, data1_0, data1_1); - } else if (this->offsets_v3_le) { - return this->get_sale_divisor_t(this->offsets_v3_le, data1_0, data1_1); - } else if (this->offsets_v3_be) { - return this->get_sale_divisor_t(this->offsets_v3_be, data1_0, data1_1); - } else if (this->offsets_v4) { - return this->get_sale_divisor_t(this->offsets_v4, data1_0, data1_1); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -const ItemParameterTable::MagFeedResult& ItemParameterTable::get_mag_feed_result( - uint8_t table_index, uint8_t item_index) const { - if (table_index >= 8) { - throw out_of_range("invalid mag feed table index"); - } - if (item_index >= 11) { - throw out_of_range("invalid mag feed item index"); - } - - uint32_t offset; - if (this->offsets_dc_protos) { - const auto& table_offsets = this->r.pget(this->offsets_dc_protos->mag_feed_table); - offset = table_offsets.offsets[table_index]; - } else if (this->offsets_v1_v2) { - const auto& table_offsets = this->r.pget(this->offsets_v1_v2->mag_feed_table); - offset = table_offsets.offsets[table_index]; - } else if (this->offsets_gc_nte) { - const auto& table_offsets = this->r.pget(this->offsets_gc_nte->mag_feed_table); - offset = table_offsets.offsets[table_index]; - } else if (this->offsets_v3_le) { - const auto& table_offsets = this->r.pget(this->offsets_v3_le->mag_feed_table); - offset = table_offsets.offsets[table_index]; - } else if (this->offsets_v3_be) { - const auto& table_offsets = this->r.pget(this->offsets_v3_be->mag_feed_table); - offset = table_offsets.offsets[table_index]; - } else if (this->offsets_v4) { - const auto& table_offsets = this->r.pget(this->offsets_v4->mag_feed_table); - offset = table_offsets.offsets[table_index]; - } else { - throw logic_error("table is not v2, v3, or v4"); - } - - return this->r.pget(offset)[item_index]; -} - -uint8_t ItemParameterTable::get_item_stars(uint32_t item_id) const { - uint32_t base_offset; - if (this->offsets_dc_protos) { - base_offset = this->offsets_dc_protos->star_value_table; - } else if (this->offsets_v1_v2) { - base_offset = this->offsets_v1_v2->star_value_table; - } else if (this->offsets_gc_nte) { - base_offset = this->offsets_gc_nte->star_value_table; - } else if (this->offsets_v3_le) { - base_offset = this->offsets_v3_le->star_value_table; - } else if (this->offsets_v3_be) { - base_offset = this->offsets_v3_be->star_value_table; - } else if (this->offsets_v4) { - base_offset = this->offsets_v4->star_value_table; - } else { - throw logic_error("table is not v2, v3, or v4"); - } - - return ((item_id >= this->item_stars_first_id) && (item_id < this->item_stars_last_id)) - ? this->r.pget_u8(base_offset + item_id - this->item_stars_first_id) - : 0; -} - -uint8_t ItemParameterTable::get_special_stars(uint8_t special) const { - return ((special & 0x3F) && !(special & 0x80)) ? this->get_item_stars(special + this->special_stars_begin_index) : 0; -} - -const ItemParameterTable::Special& ItemParameterTable::get_special(uint8_t special) const { - special &= 0x3F; - if (special >= this->num_specials) { - throw out_of_range("invalid special index"); - } - - if (this->offsets_dc_protos) { - return this->r.pget(this->offsets_dc_protos->special_data_table + sizeof(Special) * special); - } else if (this->offsets_v1_v2) { - return this->r.pget(this->offsets_v1_v2->special_data_table + sizeof(Special) * special); - } else if (this->offsets_v3_le) { - return this->r.pget(this->offsets_v3_le->special_data_table + sizeof(Special) * special); - } else if (this->offsets_gc_nte) { - if ((special >= this->parsed_specials.size()) || (this->parsed_specials[special].type != 0xFFFF)) { - if (special >= this->parsed_specials.size()) { - this->parsed_specials.resize(special + 1); - } - const auto& sp_be = this->r.pget(this->offsets_gc_nte->special_data_table + sizeof(SpecialBE) * special); - this->parsed_specials[special].type = sp_be.type; - this->parsed_specials[special].amount = sp_be.amount; - } - return this->parsed_specials[special]; - } else if (this->offsets_v3_be) { - if ((special >= this->parsed_specials.size()) || (this->parsed_specials[special].type != 0xFFFF)) { - if (special >= this->parsed_specials.size()) { - this->parsed_specials.resize(special + 1); - } - const auto& sp_be = this->r.pget(this->offsets_v3_be->special_data_table + sizeof(SpecialBE) * special); - this->parsed_specials[special].type = sp_be.type; - this->parsed_specials[special].amount = sp_be.amount; - } - return this->parsed_specials[special]; - } else if (this->offsets_v4) { - return this->r.pget(this->offsets_v4->special_data_table + sizeof(Special) * special); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -const ItemParameterTable::StatBoost& ItemParameterTable::get_stat_boost(uint8_t entry_index) const { - if (this->offsets_dc_protos) { - return this->r.pget(this->offsets_dc_protos->stat_boost_table + sizeof(StatBoost) * entry_index); - } else if (this->offsets_v1_v2) { - return this->r.pget(this->offsets_v1_v2->stat_boost_table + sizeof(StatBoost) * entry_index); - } else if (this->offsets_v3_le) { - return this->r.pget(this->offsets_v3_le->stat_boost_table + sizeof(StatBoost) * entry_index); - } else if (this->offsets_gc_nte) { - while (entry_index >= this->parsed_stat_boosts.size()) { - const auto& sb_be = this->r.pget(this->offsets_gc_nte->stat_boost_table + sizeof(StatBoostBE) * this->parsed_stat_boosts.size()); - auto& sb = this->parsed_stat_boosts.emplace_back(); - sb.stats = sb_be.stats; - sb.amounts[0] = sb_be.amounts[0]; - sb.amounts[1] = sb_be.amounts[1]; - } - return this->parsed_stat_boosts[entry_index]; - } else if (this->offsets_v3_be) { - while (entry_index >= this->parsed_stat_boosts.size()) { - const auto& sb_be = this->r.pget(this->offsets_v3_be->stat_boost_table + sizeof(StatBoostBE) * this->parsed_stat_boosts.size()); - auto& sb = this->parsed_stat_boosts.emplace_back(); - sb.stats = sb_be.stats; - sb.amounts[0] = sb_be.amounts[0]; - sb.amounts[1] = sb_be.amounts[1]; - } - return this->parsed_stat_boosts[entry_index]; - } else if (this->offsets_v4) { - return this->r.pget(this->offsets_v4->stat_boost_table + sizeof(StatBoost) * entry_index); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -uint8_t ItemParameterTable::get_max_tech_level(uint8_t char_class, uint8_t tech_num) const { - if (char_class >= 12) { - throw out_of_range("invalid character class"); - } - if (tech_num >= 19) { - throw out_of_range("invalid technique number"); - } - - if (this->offsets_dc_protos || this->offsets_v1_v2) { - if ((tech_num == 14) || (tech_num == 17)) { // Ryuker or Reverser - return 0; - } else { - return ((char_class == 6) || (char_class == 7) || (char_class == 8) || (char_class == 10)) ? 29 : 14; - } - } else if (this->offsets_gc_nte) { - return r.pget_u8(this->offsets_gc_nte->max_tech_level_table + tech_num * 12 + char_class); - } else if (this->offsets_v3_le) { - return r.pget_u8(this->offsets_v3_le->max_tech_level_table + tech_num * 12 + char_class); - } else if (this->offsets_v3_be) { - return r.pget_u8(this->offsets_v3_be->max_tech_level_table + tech_num * 12 + char_class); - } else if (this->offsets_v4) { - return r.pget_u8(this->offsets_v4->max_tech_level_table + tech_num * 12 + char_class); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -uint8_t ItemParameterTable::get_weapon_v1_replacement(uint8_t data1_1) const { - uint32_t offset; - if (this->offsets_dc_protos) { - offset = this->offsets_dc_protos->v1_replacement_table; - } else if (this->offsets_v1_v2) { - offset = this->offsets_v1_v2->v1_replacement_table; - } else if (this->offsets_gc_nte) { - offset = this->offsets_gc_nte->v1_replacement_table; - } else if (this->offsets_v3_le) { - offset = this->offsets_v3_le->v1_replacement_table; - } else if (this->offsets_v3_be) { - offset = this->offsets_v3_be->v1_replacement_table; - } else if (this->offsets_v4) { - offset = this->offsets_v4->v1_replacement_table; - } else { - throw logic_error("table is not v2, v3, or v4"); - } - - return (data1_1 < this->num_weapon_classes) ? this->r.pget_u8(offset + data1_1) : 0x00; -} - uint32_t ItemParameterTable::get_item_id(const ItemData& item) const { switch (item.data1[0]) { case 0: - return this->get_weapon(item.data1[1], item.data1[2]).base.id; + return this->get_weapon(item.data1[1], item.data1[2]).id; case 1: if (item.data1[1] == 3) { - return this->get_unit(item.data1[2]).base.id; + return this->get_unit(item.data1[2]).id; } else if ((item.data1[1] == 1) || (item.data1[1] == 2)) { - return this->get_armor_or_shield(item.data1[1], item.data1[2]).base.id; + return this->get_armor_or_shield(item.data1[1], item.data1[2]).id; } throw runtime_error("invalid item"); case 2: - return this->get_mag(item.data1[1]).base.id; + return this->get_mag(item.data1[1]).id; case 3: if (item.data1[1] == 2) { - return this->get_tool(2, item.data1[4]).base.id; + return this->get_tool(2, item.data1[4]).id; } else { - return this->get_tool(item.data1[1], item.data1[2]).base.id; + return this->get_tool(item.data1[1], item.data1[2]).id; } throw logic_error("this should be impossible"); case 4: @@ -1060,21 +795,21 @@ uint32_t ItemParameterTable::get_item_id(const ItemData& item) const { uint32_t ItemParameterTable::get_item_team_points(const ItemData& item) const { switch (item.data1[0]) { case 0: - return this->get_weapon(item.data1[1], item.data1[2]).base.team_points; + return this->get_weapon(item.data1[1], item.data1[2]).team_points; case 1: if (item.data1[1] == 3) { - return this->get_unit(item.data1[2]).base.team_points; + return this->get_unit(item.data1[2]).team_points; } else if ((item.data1[1] == 1) || (item.data1[1] == 2)) { - return this->get_armor_or_shield(item.data1[1], item.data1[2]).base.team_points; + return this->get_armor_or_shield(item.data1[1], item.data1[2]).team_points; } throw runtime_error("invalid item"); case 2: - return this->get_mag(item.data1[1]).base.team_points; + return this->get_mag(item.data1[1]).team_points; case 3: if (item.data1[1] == 2) { - return this->get_tool(2, item.data1[4]).base.team_points; + return this->get_tool(2, item.data1[4]).team_points; } else { - return this->get_tool(item.data1[1], item.data1[2]).base.team_points; + return this->get_tool(item.data1[1], item.data1[2]).team_points; } throw logic_error("this should be impossible"); case 4: @@ -1085,17 +820,20 @@ uint32_t ItemParameterTable::get_item_team_points(const ItemData& item) const { } uint8_t ItemParameterTable::get_item_base_stars(const ItemData& item) const { - if (item.data1[0] == 2) { - return (item.data1[1] >= this->first_rare_mag_index) ? 12 : 0; - } else if (item.data1[0] < 2) { - return this->get_item_stars(this->get_item_id(item)); - } else if (item.data1[0] == 3) { - const auto& def = (item.data1[1] == 2) - ? this->get_tool(2, item.data1[4]) - : this->get_tool(item.data1[1], item.data1[2]); - return (def.item_flags & 0x80) ? 12 : 0; - } else { - return 0; + switch (item.data1[0]) { + case 0: + case 1: + return this->get_item_stars(this->get_item_id(item)); + case 2: + return (item.data1[1] >= 0x28) ? 12 : 0; + case 3: { + const auto& def = (item.data1[1] == 2) + ? this->get_tool(2, item.data1[4]) + : this->get_tool(item.data1[1], item.data1[2]); + return (def.item_flags & 0x80) ? 12 : 0; + } + default: + return 0; } } @@ -1131,35 +869,6 @@ bool ItemParameterTable::is_item_rare(const ItemData& item) const { } } -bool ItemParameterTable::is_unsealable_item(uint8_t data1_0, uint8_t data1_1, uint8_t data1_2) const { - uint32_t offset, count; - if (this->offsets_dc_protos || this->offsets_v1_v2 || this->offsets_gc_nte) { - return false; - } else if (this->offsets_v3_le) { - const auto& co = this->r.pget(this->offsets_v3_le->unsealable_table); - offset = co.offset; - count = co.count; - } else if (this->offsets_v3_be) { - const auto& co = this->r.pget(this->offsets_v3_be->unsealable_table); - offset = co.offset; - count = co.count; - } else if (this->offsets_v4) { - const auto& co = this->r.pget(this->offsets_v4->unsealable_table); - offset = co.offset; - count = co.count; - } else { - throw logic_error("table is not v2, v3, or v4"); - } - - const auto* defs = &this->r.pget(offset, count * sizeof(UnsealableItem)); - for (size_t z = 0; z < count; z++) { - if ((defs[z].item[0] == data1_0) && (defs[z].item[1] == data1_1) && (defs[z].item[2] == data1_2)) { - return true; - } - } - return false; -} - bool ItemParameterTable::is_unsealable_item(const ItemData& item) const { return this->is_unsealable_item(item.data1[0], item.data1[1], item.data1[2]); } @@ -1187,83 +896,6 @@ const std::vector& ItemParameterTable::get_ } } -const std::map>& ItemParameterTable::get_all_item_combinations() const { - if (this->item_combination_index.empty()) { - uint32_t offset, count; - if (this->offsets_dc_protos || this->offsets_v1_v2 || this->offsets_gc_nte) { - static const std::map> empty_map; - return empty_map; - } else if (this->offsets_v3_le) { - const auto& co = this->r.pget(this->offsets_v3_le->combination_table); - offset = co.offset; - count = co.count; - } else if (this->offsets_v3_be) { - const auto& co = this->r.pget(this->offsets_v3_be->combination_table); - offset = co.offset; - count = co.count; - } else if (this->offsets_v4) { - const auto& co = this->r.pget(this->offsets_v4->combination_table); - offset = co.offset; - count = co.count; - } else { - throw logic_error("table is not v2, v3, or v4"); - } - - const auto* defs = &this->r.pget(offset, count * sizeof(ItemCombination)); - for (size_t z = 0; z < count; z++) { - const auto& def = defs[z]; - uint32_t key = (def.used_item[0] << 16) | (def.used_item[1] << 8) | def.used_item[2]; - this->item_combination_index[key].emplace_back(def); - } - } - return this->item_combination_index; -} - -template -size_t ItemParameterTable::num_events_t(uint32_t base_offset) const { - return this->r.pget>(base_offset).count; -} - -size_t ItemParameterTable::num_events() const { - if (this->offsets_dc_protos || this->offsets_v1_v2 || this->offsets_gc_nte) { - return 0; - } else if (this->offsets_v3_le) { - return this->num_events_t(this->offsets_v3_le->unwrap_table); - } else if (this->offsets_v3_be) { - return this->num_events_t(this->offsets_v3_be->unwrap_table); - } else if (this->offsets_v4) { - return this->num_events_t(this->offsets_v4->unwrap_table); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - -template -std::pair ItemParameterTable::get_event_items_t( - uint32_t base_offset, uint8_t event_number) const { - const auto& co = this->r.pget>(base_offset); - if (event_number >= co.count) { - throw out_of_range("invalid event number"); - } - const auto& event_co = this->r.pget>(co.offset + sizeof(ArrayRefT) * event_number); - const auto* defs = &this->r.pget(event_co.offset, event_co.count * sizeof(EventItem)); - return make_pair(defs, event_co.count); -} - -std::pair ItemParameterTable::get_event_items(uint8_t event_number) const { - if (this->offsets_dc_protos || this->offsets_v1_v2 || this->offsets_gc_nte) { - return make_pair(nullptr, 0); - } else if (this->offsets_v3_le) { - return this->get_event_items_t(this->offsets_v3_le->unwrap_table, event_number); - } else if (this->offsets_v3_be) { - return this->get_event_items_t(this->offsets_v3_be->unwrap_table, event_number); - } else if (this->offsets_v4) { - return this->get_event_items_t(this->offsets_v4->unwrap_table, event_number); - } else { - throw logic_error("table is not v2, v3, or v4"); - } -} - size_t ItemParameterTable::price_for_item(const ItemData& item) const { switch (item.data1[0]) { case 0: { @@ -1340,6 +972,451 @@ size_t ItemParameterTable::price_for_item(const ItemData& item) const { throw logic_error("this should be impossible"); } +template < + typename RootT, + typename WeaponT, + size_t NumWeaponClasses, + typename ArmorOrShieldT, + typename UnitT, + typename ToolT, + size_t NumToolClasses, + typename MagT, + size_t ItemStarsFirstID, + size_t ItemStarsLastID, + size_t SpecialStarsBeginIndex, + size_t NumSpecials, + bool BE> +class ItemParameterTableT : public ItemParameterTable { +public: + explicit ItemParameterTableT(std::shared_ptr data) + : ItemParameterTable(data), + root(&this->r.pget(BE ? r.pget_u32b(r.size() - 0x10) : r.pget_u32l(r.size() - 0x10))) {} + ~ItemParameterTableT() = default; + + inline size_t indirect_lookup_2d_count(size_t base_offset, size_t co_index) const { + return this->r.pget>(base_offset + sizeof(ArrayRefT) * co_index).count; + } + + template + const T& indirect_lookup_2d(size_t base_offset, size_t co_index, size_t item_index) const { + const auto& co = this->r.pget>(base_offset + sizeof(ArrayRefT) * co_index); + if (item_index >= co.count) { + throw out_of_range("2-D array index out of range"); + } + return this->r.pget(co.offset + sizeof(T) * item_index); + } + + virtual size_t num_weapon_classes() const { + return NumWeaponClasses; + } + + virtual size_t num_weapons_in_class(uint8_t data1_1) const { + if (data1_1 >= NumWeaponClasses) { + throw out_of_range("weapon ID out of range"); + } + return this->indirect_lookup_2d_count(this->root->weapon_table, data1_1); + } + + virtual const Weapon& get_weapon(uint8_t data1_1, uint8_t data1_2) const { + if (data1_1 >= NumWeaponClasses) { + throw out_of_range("weapon ID out of range"); + } + uint16_t key = (data1_1 << 8) | data1_2; + auto it = this->weapons.find(key); + if (it == this->weapons.end()) { + const auto& weapon = this->indirect_lookup_2d(this->root->weapon_table, data1_1, data1_2); + it = this->weapons.emplace(key, weapon).first; + } + return it->second; + } + + virtual size_t num_armors_or_shields_in_class(uint8_t data1_1) const { + if ((data1_1 < 1) || (data1_1 > 2)) { + throw out_of_range("armor/shield class ID out of range"); + } + return this->indirect_lookup_2d_count(this->root->armor_table, data1_1 - 1); + } + + virtual const ArmorOrShield& get_armor_or_shield(uint8_t data1_1, uint8_t data1_2) const { + if ((data1_1 < 1) || (data1_1 > 2)) { + throw out_of_range("armor/shield class ID out of range"); + } + auto& vec = (data1_1 == 1) ? this->armors : this->shields; + while (vec.size() <= data1_2) { + vec.emplace_back(this->indirect_lookup_2d(this->root->armor_table, data1_1 - 1, vec.size())); + } + return vec[data1_2]; + } + + virtual size_t num_units() const { + return this->indirect_lookup_2d_count(this->root->unit_table, 0); + } + + virtual const Unit& get_unit(uint8_t data1_2) const { + while (this->units.size() <= data1_2) { + this->units.emplace_back(this->indirect_lookup_2d(this->root->unit_table, 0, this->units.size())); + } + return this->units[data1_2]; + } + + virtual size_t num_mags() const { + return this->indirect_lookup_2d_count(this->root->mag_table, 0); + } + + virtual const Mag& get_mag(uint8_t data1_1) const { + while (this->mags.size() <= data1_1) { + this->mags.emplace_back(this->indirect_lookup_2d(this->root->mag_table, 0, data1_1)); + } + return this->mags[data1_1]; + } + + virtual size_t num_tool_classes() const { + return NumToolClasses; + } + + virtual size_t num_tools_in_class(uint8_t data1_1) const { + if (data1_1 >= NumToolClasses) { + throw out_of_range("tool class ID out of range"); + } + return this->indirect_lookup_2d_count(this->root->tool_table, data1_1); + } + + virtual const Tool& get_tool(uint8_t data1_1, uint8_t data1_2) const { + if (data1_1 >= NumToolClasses) { + throw out_of_range("tool class ID out of range"); + } + uint16_t key = (data1_1 << 8) | data1_2; + auto it = this->tools.find(key); + if (it == this->tools.end()) { + const auto& tool = this->indirect_lookup_2d(this->root->tool_table, data1_1, data1_2); + it = this->tools.emplace(key, tool).first; + } + return it->second; + } + + virtual std::pair find_tool_by_id(uint32_t id) const { + const auto* cos = &this->r.pget>( + this->root->tool_table, NumToolClasses * sizeof(ArrayRefT)); + for (size_t z = 0; z < NumToolClasses; z++) { + const auto& co = cos[z]; + const auto* defs = &this->r.pget(co.offset, sizeof(ToolT) * co.count); + for (size_t y = 0; y < co.count; y++) { + if (defs[y].base.id == id) { + return make_pair(z, y); + } + } + } + throw out_of_range(std::format("invalid tool class {:08X}", id)); + } + + virtual float get_sale_divisor(uint8_t data1_0, uint8_t data1_1) const { + if (data1_0 == 0) { + if (data1_1 >= NumWeaponClasses) { + return 0.0f; + } + if constexpr (requires { this->root->weapon_sale_divisor_table; }) { + return this->r.pget>(this->root->weapon_sale_divisor_table + data1_1 * sizeof(F32T)); + } else { + return this->r.pget(this->root->weapon_integral_sale_divisor_table + data1_1 * sizeof(uint8_t)); + } + } + + if constexpr (requires { this->root->non_weapon_sale_divisor_table; }) { + const auto& divisors = this->r.pget>(this->root->non_weapon_sale_divisor_table); + if (data1_0 == 1) { + switch (data1_1) { + case 1: + return divisors.armor_divisor; + case 2: + return divisors.shield_divisor; + case 3: + return divisors.unit_divisor; + } + } else if (data1_0 == 2) { + return divisors.mag_divisor; + } + } else { + if (data1_0 == 1) { + const auto& divisors = this->r.pget( + this->root->non_weapon_integral_sale_divisor_table); + switch (data1_1) { + case 1: + return divisors.armor_divisor; + case 2: + return divisors.shield_divisor; + case 3: + return divisors.unit_divisor; + } + } + } + + return 0.0f; + } + + virtual const MagFeedResult& get_mag_feed_result(uint8_t table_index, uint8_t item_index) const { + if (table_index >= 8) { + throw out_of_range("invalid mag feed table index"); + } + if (item_index >= 11) { + throw out_of_range("invalid mag feed item index"); + } + const auto& table_offsets = this->r.pget>(this->root->mag_feed_table); + return this->r.pget(table_offsets[table_index])[item_index]; + } + + virtual uint8_t get_item_stars(uint32_t id) const { + return ((id >= ItemStarsFirstID) && (id < ItemStarsLastID)) + ? this->r.pget_u8(this->root->star_value_table + id - ItemStarsFirstID) + : 0; + } + + virtual uint8_t get_special_stars(uint8_t special) const { + return ((special & 0x3F) && !(special & 0x80)) ? this->get_item_stars(special + SpecialStarsBeginIndex) : 0; + } + + virtual size_t num_specials() const { + return NumSpecials; + } + + virtual const Special& get_special(uint8_t special) const { + special &= 0x3F; + if (special >= NumSpecials) { + throw out_of_range("invalid special index"); + } + while (this->specials.size() <= special) { + this->specials.emplace_back(this->r.pget>( + this->root->special_table + sizeof(SpecialT) * this->specials.size())); + } + return this->specials[special]; + } + + virtual const StatBoost& get_stat_boost(uint8_t entry_index) const { + while (this->stat_boosts.size() <= entry_index) { + this->stat_boosts.emplace_back(this->r.pget>( + this->root->stat_boost_table + sizeof(StatBoostT) * this->stat_boosts.size())); + } + return this->stat_boosts[entry_index]; + } + + virtual uint8_t get_max_tech_level(uint8_t char_class, uint8_t tech_num) const { + if (char_class >= 12) { + throw out_of_range("invalid character class"); + } + if (tech_num >= 19) { + throw out_of_range("invalid technique number"); + } + if constexpr (requires { this->root->max_tech_level_table; }) { + return r.pget_u8(this->root->max_tech_level_table + tech_num * 12 + char_class); + } else { + if ((tech_num == 14) || (tech_num == 17)) { // Ryuker or Reverser + return 0; + } else { + return ((char_class == 6) || (char_class == 7) || (char_class == 8) || (char_class == 10)) ? 29 : 14; + } + } + } + + virtual uint8_t get_weapon_class(uint8_t data1_1) const { + return (data1_1 < NumWeaponClasses) ? this->r.pget_u8(this->root->weapon_class_table + data1_1) : 0x00; + } + + virtual bool is_unsealable_item(uint8_t data1_0, uint8_t data1_1, uint8_t data1_2) const { + if constexpr (requires { this->root->unsealable_table; }) { + const auto& co = this->r.pget>(this->root->unsealable_table); + const auto* defs = &this->r.pget(co.offset, co.count * sizeof(UnsealableItem)); + for (size_t z = 0; z < co.count; z++) { + if ((defs[z].item[0] == data1_0) && (defs[z].item[1] == data1_1) && (defs[z].item[2] == data1_2)) { + return true; + } + } + } + return false; + } + + virtual const std::map>& get_all_item_combinations() const { + if constexpr (requires { this->root->combination_table; }) { + if (this->item_combination_index.empty()) { + const auto& co = this->r.pget>(this->root->combination_table); + const auto* defs = &this->r.pget(co.offset, co.count * sizeof(ItemCombination)); + for (size_t z = 0; z < co.count; z++) { + const auto& def = defs[z]; + uint32_t key = (def.used_item[0] << 16) | (def.used_item[1] << 8) | def.used_item[2]; + this->item_combination_index[key].emplace_back(def); + } + } + return this->item_combination_index; + } + static const std::map> empty_map; + return empty_map; + } + + virtual size_t num_events() const { + if constexpr (requires { this->root->unwrap_table; }) { + return this->r.pget>(this->root->unwrap_table).count; + } else { + return 0; + } + } + + virtual std::pair get_event_items(uint8_t event_number) const { + if constexpr (requires { this->root->unwrap_table; }) { + const auto& co = this->r.pget>(this->root->unwrap_table); + if (event_number >= co.count) { + throw out_of_range("invalid event number"); + } + const auto& event_co = this->r.pget>(co.offset + sizeof(ArrayRefT) * event_number); + const auto* defs = &this->r.pget(event_co.offset, event_co.count * sizeof(EventItem)); + return make_pair(defs, event_co.count); + } else { + return make_pair(nullptr, 0); + } + } + +protected: + const RootT* root; +}; + +using ItemParameterTableDCNTE = ItemParameterTableT< + RootDCProtos, // typename RootT + WeaponDCProtos, // typename WeaponT + 0x27, // size_t NumWeaponClasses + ArmorOrShieldDCProtos, // typename ArmorOrShieldT + UnitDCProtos, // typename UnitT + ToolV1V2, // typename ToolT + 0x0D, // size_t NumToolClasses + MagV1, // typename MagT + 0x22, // size_t ItemStarsFirstID + 0x168, // size_t ItemStarsLastID + 0xAA, // size_t SpecialStarsBeginIndex + 0x28, // size_t NumSpecials + false>; // bool BE +using ItemParameterTableDC112000 = ItemParameterTableT< + RootDCProtos, // typename RootT + WeaponDCProtos, // typename WeaponT + 0x27, // size_t NumWeaponClasses + ArmorOrShieldDCProtos, // typename ArmorOrShieldT + UnitDCProtos, // typename UnitT + ToolV1V2, // typename ToolT + 0x0E, // size_t NumToolClasses + MagV1, // typename MagT + 0x26, // size_t ItemStarsFirstID + 0x16C, // size_t ItemStarsLastID + 0xAE, // size_t SpecialStarsBeginIndex + 0x28, // size_t NumSpecials + false>; // bool BE +using ItemParameterTableV1 = ItemParameterTableT< + RootV1V2, // typename RootT + WeaponV1V2, // typename WeaponT + 0x27, // size_t NumWeaponClasses + ArmorOrShieldV1V2, // typename ArmorOrShieldT + UnitV1V2, // typename UnitT + ToolV1V2, // typename ToolT + 0x0E, // size_t NumToolClasses + MagV1, // typename MagT + 0x26, // size_t ItemStarsFirstID + 0x16C, // size_t ItemStarsLastID + 0xAE, // size_t SpecialStarsBeginIndex + 0x29, // size_t NumSpecials + false>; // bool BE +using ItemParameterTableV2 = ItemParameterTableT< + RootV1V2, // typename RootT + WeaponV1V2, // typename WeaponT + 0x89, // size_t NumWeaponClasses + ArmorOrShieldV1V2, // typename ArmorOrShieldT + UnitV1V2, // typename UnitT + ToolV1V2, // typename ToolT + 0x10, // size_t NumToolClasses + MagV2, // typename MagT + 0x4E, // size_t ItemStarsFirstID + 0x215, // size_t ItemStarsLastID + 0x138, // size_t SpecialStarsBeginIndex + 0x29, // size_t NumSpecials + false>; // bool BE +using ItemParameterTableGCNTE = ItemParameterTableT< + RootGCNTE, // typename RootT + WeaponGCNTE, // typename WeaponT + 0x8D, // size_t NumWeaponClasses + ArmorOrShieldGC, // typename ArmorOrShieldT + UnitGC, // typename UnitT + ToolGC, // typename ToolT + 0x13, // size_t NumToolClasses + MagGC, // typename MagT + 0x76, // size_t ItemStarsFirstID + 0x298, // size_t ItemStarsLastID + 0x1A3, // size_t SpecialStarsBeginIndex + 0x29, // size_t NumSpecials + true>; // bool BE +using ItemParameterTableGC = ItemParameterTableT< + RootV3V4T, // typename RootT + WeaponGC, // typename WeaponT + 0xAA, // size_t NumWeaponClasses + ArmorOrShieldGC, // typename ArmorOrShieldT + UnitGC, // typename UnitT + ToolGC, // typename ToolT + 0x18, // size_t NumToolClasses + MagGC, // typename MagT + 0x94, // size_t ItemStarsFirstID + 0x2F7, // size_t ItemStarsLastID + 0x1CB, // size_t SpecialStarsBeginIndex + 0x29, // size_t NumSpecials + true>; // bool BE +using ItemParameterTableXB = ItemParameterTableT< + RootV3V4T, // typename RootT + WeaponXB, // typename WeaponT + 0xAA, // size_t NumWeaponClasses + ArmorOrShieldXB, // typename ArmorOrShieldT + UnitXB, // typename UnitT + ToolXB, // typename ToolT + 0x18, // size_t NumToolClasses + MagXB, // typename MagT + 0x94, // size_t ItemStarsFirstID + 0x2F7, // size_t ItemStarsLastID + 0x1CB, // size_t SpecialStarsBeginIndex + 0x29, // size_t NumSpecials + false>; // bool BE +using ItemParameterTableV4 = ItemParameterTableT< + RootV3V4T, // typename RootT + WeaponV4, // typename WeaponT + 0xED, // size_t NumWeaponClasses + ArmorOrShieldV4, // typename ArmorOrShieldT + UnitV4, // typename UnitT + ToolV4, // typename ToolT + 0x1B, // size_t NumToolClasses + MagV4, // typename MagT + 0xB1, // size_t ItemStarsFirstID + 0x437, // size_t ItemStarsLastID + 0x256, // size_t SpecialStarsBeginIndex + 0x29, // size_t NumSpecials + false>; // bool BE + +std::shared_ptr ItemParameterTable::create( + std::shared_ptr data, Version version) { + switch (version) { + case Version::DC_NTE: + return std::make_shared(data); + case Version::DC_11_2000: + return std::make_shared(data); + case Version::DC_V1: + return std::make_shared(data); + case Version::DC_V2: + case Version::PC_NTE: + case Version::PC_V2: + return std::make_shared(data); + case Version::GC_NTE: + return std::make_shared(data); + case Version::GC_V3: + case Version::GC_EP3: + case Version::GC_EP3_NTE: + return std::make_shared(data); + case Version::XB_V3: + return std::make_shared(data); + case Version::BB_V4: + return std::make_shared(data); + default: + throw std::logic_error("Cannot create item parameter table for this version"); + } +} + MagEvolutionTable::MagEvolutionTable(shared_ptr data, size_t num_mags) : data(data), num_mags(num_mags), diff --git a/src/ItemParameterTable.hh b/src/ItemParameterTable.hh index b0d814c6..68035b93 100644 --- a/src/ItemParameterTable.hh +++ b/src/ItemParameterTable.hh @@ -13,6 +13,7 @@ #include #include +#include "CommonFileFormats.hh" #include "ItemData.hh" #include "Text.hh" #include "Types.hh" @@ -40,310 +41,113 @@ const char* phosg::name_for_enum(ServerDropMode value); class ItemParameterTable { public: - // TODO: This implementation is ugly. We should use real classes and virtual functions instead of manually branching - // on various offset table pointers being null or not in each public function. Rewrite this and make it better. + // These structures are all parsed representations of the file's data. The file's actual structures are defined in + // ItemParamterTable.cc, with analogous names to these structures. - template - struct ItemBaseV2T { + struct ItemBase { // id specifies several things; notably, it doubles as the index of the item's name in the text archive (e.g. // TextEnglish) collection 0. - /* 00 */ U32T id = 0xFFFFFFFF; - /* 04 */ - } __attribute__((packed)); - template - struct ItemBaseV3T : ItemBaseV2T { - /* 04 */ U16T type = 0; // "Model" in Soly's ItemPMT editor - /* 06 */ U16T skin = 0; // "Texture" in Soly's ItemPMT editor - /* 08 */ - } __attribute__((packed)); - template - struct ItemBaseV4T : ItemBaseV3T { - /* 08 */ U32T team_points = 0; - /* 0C */ - } __attribute__((packed)); - - struct WeaponV4; - struct WeaponDCProtos { - /* 00 */ ItemBaseV2T base; - /* 04 */ le_uint16_t class_flags = 0; - /* 06 */ le_uint16_t atp_min = 0; - /* 08 */ le_uint16_t atp_max = 0; - /* 0A */ le_uint16_t atp_required = 0; - /* 0C */ le_uint16_t mst_required = 0; - /* 0E */ le_uint16_t ata_required = 0; - /* 10 */ uint8_t max_grind = 0; - /* 11 */ uint8_t photon = 0; - /* 12 */ uint8_t special = 0; - /* 13 */ uint8_t ata = 0; - /* 14 */ - - WeaponV4 to_v4() const; - } __packed_ws__(WeaponDCProtos, 0x14); - - struct WeaponV1V2 { - /* 00 */ ItemBaseV2T base; - /* 04 */ le_uint16_t class_flags = 0; - /* 06 */ le_uint16_t atp_min = 0; - /* 08 */ le_uint16_t atp_max = 0; - /* 0A */ le_uint16_t atp_required = 0; - /* 0C */ le_uint16_t mst_required = 0; - /* 0E */ le_uint16_t ata_required = 0; - /* 10 */ uint8_t max_grind = 0; - /* 11 */ uint8_t photon = 0; - /* 12 */ uint8_t special = 0; - /* 13 */ uint8_t ata = 0; - /* 14 */ uint8_t stat_boost_entry_index = 0; // TODO: This could be larger (16 or 32 bits) - /* 15 */ parray unknown_a9; - /* 18 */ - - WeaponV4 to_v4() const; - } __packed_ws__(WeaponV1V2, 0x18); - - struct WeaponGCNTE { - /* 00 */ ItemBaseV3T base; - /* 08 */ be_uint16_t class_flags = 0; - /* 0A */ be_uint16_t atp_min = 0; - /* 0C */ be_uint16_t atp_max = 0; - /* 0E */ be_uint16_t atp_required = 0; - /* 10 */ be_uint16_t mst_required = 0; - /* 12 */ be_uint16_t ata_required = 0; - /* 14 */ be_uint16_t mst = 0; - /* 16 */ uint8_t max_grind = 0; - /* 17 */ uint8_t photon = 0; - /* 18 */ uint8_t special = 0; - /* 19 */ uint8_t ata = 0; - /* 1A */ uint8_t stat_boost_entry_index = 0; - /* 1B */ uint8_t projectile = 0; - /* 1C */ int8_t trail1_x = 0; - /* 1D */ int8_t trail1_y = 0; - /* 1E */ int8_t trail2_x = 0; - /* 1F */ int8_t trail2_y = 0; - /* 20 */ int8_t color = 0; - /* 21 */ parray unknown_a1 = 0; - /* 24 */ - - WeaponV4 to_v4() const; - } __packed_ws__(WeaponGCNTE, 0x24); - - template - struct WeaponV3T { - /* 00 */ ItemBaseV3T base; - /* 08 */ U16T class_flags = 0; - /* 0A */ U16T atp_min = 0; - /* 0C */ U16T atp_max = 0; - /* 0E */ U16T atp_required = 0; - /* 10 */ U16T mst_required = 0; - /* 12 */ U16T ata_required = 0; - /* 14 */ U16T mst = 0; - /* 16 */ uint8_t max_grind = 0; - /* 17 */ uint8_t photon = 0; - /* 18 */ uint8_t special = 0; - /* 19 */ uint8_t ata = 0; - /* 1A */ uint8_t stat_boost_entry_index = 0; - /* 1B */ uint8_t projectile = 0; - /* 1C */ int8_t trail1_x = 0; - /* 1D */ int8_t trail1_y = 0; - /* 1E */ int8_t trail2_x = 0; - /* 1F */ int8_t trail2_y = 0; - /* 20 */ int8_t color = 0; - /* 21 */ parray unknown_a1 = 0; - /* 24 */ uint8_t unknown_a4 = 0; - /* 25 */ uint8_t unknown_a5 = 0; - /* 26 */ uint8_t tech_boost = 0; - // Flags in this field: + uint32_t id = 0xFFFFFFFF; + uint16_t type = 0; // "Model" in Soly's ItemPMT editor + uint16_t skin = 0; // "Texture" in Soly's ItemPMT editor + uint32_t team_points = 0; + }; + struct Weapon : ItemBase { + uint16_t class_flags = 0; + uint16_t atp_min = 0; + uint16_t atp_max = 0; + uint16_t atp_required = 0; + uint16_t mst_required = 0; + uint16_t ata_required = 0; + uint16_t mst = 0; + uint8_t max_grind = 0; + uint8_t photon = 0; + uint8_t special = 0; + uint8_t ata = 0; + uint8_t stat_boost_entry_index = 0; // TODO: This could be larger (16 or 32 bits) + uint8_t projectile = 0; + int8_t trail1_x = 0; + int8_t trail1_y = 0; + int8_t trail2_x = 0; + int8_t trail2_y = 0; + int8_t color = 0; + parray unknown_a1 = 0; + uint8_t unknown_a4 = 0; + uint8_t unknown_a5 = 0; + uint8_t tech_boost = 0; + // Bits in behavior_flags: // 01 = disable combos (weapon can only be used once in a row) // 02 = TODO (sets TItemWeapon flag 40000; used in TItemWeapon_v1E) // 04 = TODO (sets TItemWeapon flag 80000; used in TItemWeapon_v1E) // 08 = weapon cannot have attributes (they are ignored if present) - /* 27 */ uint8_t behavior_flags = 0; - /* 28 */ + uint8_t behavior_flags = 0; + }; - WeaponV4 to_v4() const; - } __attribute__((packed)); - - using WeaponV3 = WeaponV3T; - using WeaponV3BE = WeaponV3T; - check_struct_size(WeaponV3, 0x28); - check_struct_size(WeaponV3BE, 0x28); - - struct WeaponV4 { - /* 00 */ ItemBaseV4T base; - /* 0C */ le_uint16_t class_flags = 0x00FF; - /* 0E */ le_uint16_t atp_min = 0; - /* 10 */ le_uint16_t atp_max = 0; - /* 12 */ le_uint16_t atp_required = 0; - /* 14 */ le_uint16_t mst_required = 0; - /* 16 */ le_uint16_t ata_required = 0; - /* 18 */ le_uint16_t mst = 0; - /* 1A */ uint8_t max_grind = 0; - /* 1B */ uint8_t photon = 0; - /* 1C */ uint8_t special = 0; - /* 1D */ uint8_t ata = 0; - /* 1E */ uint8_t stat_boost_entry_index = 0; - /* 1F */ uint8_t projectile = 0; - /* 20 */ int8_t trail1_x = 0; - /* 21 */ int8_t trail1_y = 0; - /* 22 */ int8_t trail2_x = 0; - /* 23 */ int8_t trail2_y = 0; - /* 24 */ int8_t color = 0; - /* 25 */ parray unknown_a1 = 0; - /* 28 */ uint8_t unknown_a4 = 0; - /* 29 */ uint8_t unknown_a5 = 0; - /* 2A */ uint8_t tech_boost = 0; - /* 2B */ uint8_t behavior_flags = 0; - /* 2C */ - } __packed_ws__(WeaponV4, 0x2C); - - template - struct ArmorOrShieldT { - /* V1/V2 offsets */ - /* 00 */ BaseT base; - /* 04 */ U16T dfp = 0; - /* 06 */ U16T evp = 0; - /* 08 */ uint8_t block_particle = 0; - /* 09 */ uint8_t block_effect = 0; - /* 0A */ U16T class_flags = 0x00FF; - /* 0C */ uint8_t required_level = 0; - /* 0D */ uint8_t efr = 0; - /* 0E */ uint8_t eth = 0; - /* 0F */ uint8_t eic = 0; - /* 10 */ uint8_t edk = 0; - /* 11 */ uint8_t elt = 0; - /* 12 */ uint8_t dfp_range = 0; - /* 13 */ uint8_t evp_range = 0; - /* 14 */ - } __attribute__((packed)); - - template - struct ArmorOrShieldFinalT : ArmorOrShieldT { - /* 14 */ uint8_t stat_boost_entry_index = 0; - /* 15 */ uint8_t tech_boost = 0; + struct ArmorOrShield : ItemBase { + uint16_t dfp = 0; + uint16_t evp = 0; + uint8_t block_particle = 0; + uint8_t block_effect = 0; + uint16_t class_flags = 0x00FF; + uint8_t required_level = 0; + uint8_t efr = 0; + uint8_t eth = 0; + uint8_t eic = 0; + uint8_t edk = 0; + uint8_t elt = 0; + uint8_t dfp_range = 0; + uint8_t evp_range = 0; + uint8_t stat_boost_entry_index = 0; + uint8_t tech_boost = 0; // TODO: Figure out what this does. Only a few values appear to do anything: - // Shields: - // 01 sets item->flags |= 4 (used in TItemProShield_v10) - // 03 sets item->flags |= 8 (used in TItemProShield_v1A) // Armors: // 01 sets item->flags |= 1 (used in TItemProArmor_v10) // 02 constructs TItemProArmorParticle instead of TItemProArmor - /* 16 */ uint8_t flags_type = 0; - /* 17 */ uint8_t unknown_a4 = 0; - /* 18 */ - } __attribute__((packed)); - using ArmorOrShieldV4 = ArmorOrShieldFinalT, false>; - check_struct_size(ArmorOrShieldV4, 0x20); - struct ArmorOrShieldDCProtos : ArmorOrShieldT, false> { - ArmorOrShieldV4 to_v4() const; - } __packed_ws__(ArmorOrShieldDCProtos, 0x14); + // Shields: + // 01 sets item->flags |= 4 (used in TItemProShield_v10) + // 03 sets item->flags |= 8 (used in TItemProShield_v1A) + uint8_t flags_type = 0; + uint8_t unknown_a4 = 0; + }; - struct ArmorOrShieldV1V2 : ArmorOrShieldFinalT, false> { - ArmorOrShieldV4 to_v4() const; - } __packed_ws__(ArmorOrShieldV1V2, 0x18); - template - struct ArmorOrShieldV3T : ArmorOrShieldFinalT, BE> { - ArmorOrShieldV4 to_v4() const; - } __attribute__((packed)); - using ArmorOrShieldV3 = ArmorOrShieldV3T; - using ArmorOrShieldV3BE = ArmorOrShieldV3T; - check_struct_size(ArmorOrShieldV3, 0x1C); - check_struct_size(ArmorOrShieldV3BE, 0x1C); + struct Unit : ItemBase { + uint16_t stat = 0; + uint16_t stat_amount = 0; + int16_t modifier_amount = 0; + }; - template - struct UnitT { - /* V1/V2 offsets */ - /* 00 */ BaseT base; - /* 04 */ U16T stat = 0; - /* 06 */ U16T stat_amount = 0; - /* 08 */ - } __attribute__((packed)); - - template - struct UnitFinalT : UnitT { - /* 08 */ S16T modifier_amount = 0; - /* 0A */ parray unused; - /* 0C */ - } __attribute__((packed)); - using UnitV4 = UnitFinalT, false>; - check_struct_size(UnitV4, 0x14); - struct UnitDCProtos : UnitT, false> { - UnitV4 to_v4() const; - } __packed_ws__(UnitDCProtos, 0x08); - struct UnitV1V2 : UnitFinalT, false> { - UnitV4 to_v4() const; - } __packed_ws__(UnitV1V2, 0x0C); - template - struct UnitV3T : UnitFinalT, BE> { - UnitV4 to_v4() const; - } __attribute__((packed)); - using UnitV3 = UnitV3T; - using UnitV3BE = UnitV3T; - check_struct_size(UnitV3, 0x10); - check_struct_size(UnitV3BE, 0x10); - - template - struct MagT { - /* V1/V2 offsets */ - /* 00 */ BaseT base; - /* 04 */ U16T feed_table = 0; - /* 06 */ uint8_t photon_blast = 0; - /* 07 */ uint8_t activation = 0; - /* 08 */ uint8_t on_pb_full = 0; - /* 09 */ uint8_t on_low_hp = 0; - /* 0A */ uint8_t on_death = 0; - /* 0B */ uint8_t on_boss = 0; + struct Mag : ItemBase { + uint16_t feed_table = 0; + uint8_t photon_blast = 0; + uint8_t activation = 0; + uint8_t on_pb_full = 0; + uint8_t on_low_hp = 0; + uint8_t on_death = 0; + uint8_t on_boss = 0; // These flags control how likely each effect is to activate. First, the game computes step_synchro as follows: - // if synchro in [0, 30], step_synchro = 0 - // if synchro in [31, 60], step_synchro = 15 - // if synchro in [61, 80], step_synchro = 25 - // if synchro in [81, 100], step_synchro = 30 - // if synchro in [101, 120], step_synchro = 35 + // If synchro is in [0, 30], set step_synchro = 0 + // If synchro is in [31, 60], set step_synchro = 15 + // If synchro is in [61, 80], set step_synchro = 25 + // If synchro is in [81, 100], set step_synchro = 30 + // If synchro is in [101, 120], set step_synchro = 35 // Then, the percent chance of the effect occurring upon its trigger (e.g. entering a boss arena) is: - // flag == 0 => activation - // flag == 1 => activation + step_synchro - // flag == 2 => step_synchro - // flag == 3 => activation - 10 - // flag == 4 => step_synchro - 10 - // anything else => 0 (effect never occurs) - /* 0C */ uint8_t on_pb_full_flag = 0; - /* 0D */ uint8_t on_low_hp_flag = 0; - /* 0E */ uint8_t on_death_flag = 0; - /* 0F */ uint8_t on_boss_flag = 0; - /* 10 */ - } __attribute__((packed)); + // flag == 0 => chance is (activation) + // flag == 1 => chance is (activation + step_synchro) + // flag == 2 => chance is (step_synchro) + // flag == 3 => chance is (activation - 10) + // flag == 4 => chance is (step_synchro - 10) + // anything else => chance is 0 (effect never occurs) + uint8_t on_pb_full_flag = 0; + uint8_t on_low_hp_flag = 0; + uint8_t on_death_flag = 0; + uint8_t on_boss_flag = 0; + uint16_t class_flags = 0x00FF; + }; - struct MagV4 : MagT, false> { - le_uint16_t class_flags = 0x00FF; - parray unused; - } __packed_ws__(MagV4, 0x1C); - struct MagV1 : MagT, false> { - MagV4 to_v4() const; - } __packed_ws__(MagV1, 0x10); - struct MagV2 : MagT, false> { - /* 10 */ le_uint16_t class_flags = 0x00FF; - /* 12 */ parray unused; - /* 14 */ - - MagV4 to_v4() const; - } __packed_ws__(MagV2, 0x14); - template - struct MagV3T : MagT, BE> { - /* 10 */ U16T class_flags = 0x00FF; - /* 12 */ parray unused; - /* 14 */ - - MagV4 to_v4() const; - } __attribute__((packed)); - using MagV3 = MagV3T; - using MagV3BE = MagV3T; - check_struct_size(MagV3, 0x18); - check_struct_size(MagV3BE, 0x18); - - template - struct ToolT { - /* V1/V2 offsets */ - /* 00 */ BaseT base; - /* 04 */ U16T amount = 0; - /* 06 */ U16T tech = 0; - /* 08 */ S32T cost = 0; + struct Tool : ItemBase { + uint16_t amount = 0; + uint16_t tech = 0; + int32_t cost = 0; // Bits in item_flags: // 00000001 - ever usable by player ("Use" appears in inventory menu) // 00000002 - unknown @@ -352,24 +156,9 @@ public: // 00000010 - usable in Pioneer 2 / Lab // 00000020 - usable in boss arenas // 00000040 - usable in Challenge mode - // 00000080 - is rare (renders as red box; V3+) - /* 0C */ U32T item_flags = 0; - /* 10 */ - } __attribute__((packed)); - - using ToolV4 = ToolT, false>; - check_struct_size(ToolV4, 0x18); - struct ToolV1V2 : ToolT, false> { - ToolV4 to_v4() const; - } __packed_ws__(ToolV1V2, 0x10); - template - struct ToolV3T : ToolT, BE> { - ToolV4 to_v4() const; - } __attribute__((packed)); - using ToolV3 = ToolV3T; - using ToolV3BE = ToolV3T; - check_struct_size(ToolV3, 0x14); - check_struct_size(ToolV3BE, 0x14); + // 00000080 - is rare (renders as red box; V3+ only) + /* 0C */ uint32_t item_flags = 0; + }; struct MagFeedResult { int8_t def = 0; @@ -381,29 +170,12 @@ public: parray unused; } __packed_ws__(MagFeedResult, 8); - using MagFeedResultsList = parray; + struct Special { + uint16_t type = 0xFFFF; + uint16_t amount = 0; + }; - template - struct MagFeedResultsListOffsetsT { - parray, 8> offsets; // Offsets of MagFeedResultsList objects - } __attribute__((packed)); - using MagFeedResultsListOffsets = MagFeedResultsListOffsetsT; - using MagFeedResultsListOffsetsBE = MagFeedResultsListOffsetsT; - check_struct_size(MagFeedResultsListOffsets, 0x20); - check_struct_size(MagFeedResultsListOffsetsBE, 0x20); - - template - struct SpecialT { - U16T type = 0xFFFF; - U16T amount = 0; - } __attribute__((packed)); - using Special = SpecialT; - using SpecialBE = SpecialT; - check_struct_size(Special, 4); - check_struct_size(SpecialBE, 4); - - template - struct StatBoostT { + struct StatBoost { // Only the first of these stat/amount pairs is used in most versions of the game. In DC 11/2000 Sega apparently // changed the loop from `for (z = 0; z != 2; z++)` to `for (z = 0; z != 1; z++)`, so only the first stat/amount // pair is used on all versions after DC NTE. @@ -425,13 +197,11 @@ public: // 0F = LCK penalty // 10 = all of the above penalties except HP // Anything else (including 00) = no bonus or penalty - parray stats = 0; - parray, 2> amounts; + uint8_t stat1 = 0; + uint16_t amount1 = 0; + uint8_t stat2 = 0; + uint16_t amount2 = 0; } __attribute__((packed)); - using StatBoost = StatBoostT; - using StatBoostBE = StatBoostT; - check_struct_size(StatBoost, 6); - check_struct_size(StatBoostBE, 6); // Indexed as [tech_num][char_class] using MaxTechniqueLevels = parray, 19>; @@ -447,19 +217,12 @@ public: parray unused; } __packed_ws__(ItemCombination, 0x10); - template - struct TechniqueBoostEntryT { + struct TechniqueBoost { uint8_t tech_num = 0; - // It appears that only one bit in the flags field is used: - // 01 = enable piercing (for Megid) + // It appears that only one bit in the flags field is used: 01 = enable piercing (for Megid) uint8_t flags = 0; - parray unused; - F32T amount = 0.0f; - } __attribute__((packed)); - using TechniqueBoostEntry = TechniqueBoostEntryT; - using TechniqueBoostEntryBE = TechniqueBoostEntryT; - check_struct_size(TechniqueBoostEntry, 0x08); - check_struct_size(TechniqueBoostEntryBE, 0x08); + float amount = 0.0f; + }; struct EventItem { parray item; @@ -471,204 +234,126 @@ public: uint8_t unused = 0; } __packed_ws__(UnsealableItem, 4); - template - struct NonWeaponSaleDivisorsT { - F32T armor_divisor = 0.0f; - F32T shield_divisor = 0.0f; - F32T unit_divisor = 0.0f; - F32T mag_divisor = 0.0f; - } __attribute__((packed)); - using NonWeaponSaleDivisors = NonWeaponSaleDivisorsT; - using NonWeaponSaleDivisorsBE = NonWeaponSaleDivisorsT; - check_struct_size(NonWeaponSaleDivisors, 0x10); - check_struct_size(NonWeaponSaleDivisorsBE, 0x10); + struct NonWeaponSaleDivisors { + float armor_divisor = 0.0f; + float shield_divisor = 0.0f; + float unit_divisor = 0.0f; + float mag_divisor = 0.0f; + }; - ItemParameterTable(std::shared_ptr data, Version version); - ~ItemParameterTable() = default; + struct ShieldEffect { + uint32_t sound_id; + uint32_t unknown_a1; + }; + + struct PhotonColorEntry { + uint32_t unknown_a1; + VectorXYZTF unknown_a2; + VectorXYZTF unknown_a3; + }; + + struct UnknownA1 { + uint16_t unknown_a1; + uint16_t unknown_a2; + }; + + struct UnknownA5 { + uint32_t target_param; // For players, char_class; for enemies, rt_index; for objects, 0x30 + uint32_t unknown_a2; + uint32_t unknown_a3; + }; + + struct WeaponEffect { + uint32_t sound_id1; + uint32_t eff_value1; + uint32_t sound_id2; + uint32_t eff_value2; + parray unknown_a5; + }; + + struct WeaponRange { + float unknown_a1; + float unknown_a2; + uint32_t unknown_a3; // Angle + uint32_t unknown_a4; // Angle + uint32_t unknown_a5; + }; + + struct RangedSpecial { + uint8_t data1_1; + uint8_t data1_2; + uint8_t weapon_range_index; + uint8_t unknown_a1; + } __packed_ws__(RangedSpecial, 4); + + ItemParameterTable() = delete; + virtual ~ItemParameterTable() = default; + + static std::shared_ptr create(std::shared_ptr data, Version version); std::set compute_all_valid_primary_identifiers() const; - size_t num_weapons_in_class(uint8_t data1_1) const; - const WeaponV4& get_weapon(uint8_t data1_1, uint8_t data1_2) const; - size_t num_armors_or_shields_in_class(uint8_t data1_1) const; - const ArmorOrShieldV4& get_armor_or_shield(uint8_t data1_1, uint8_t data1_2) const; - size_t num_units() const; - const UnitV4& get_unit(uint8_t data1_2) const; - size_t num_mags() const; - const MagV4& get_mag(uint8_t data1_1) const; - size_t num_tools_in_class(uint8_t data1_1) const; - const ToolV4& get_tool(uint8_t data1_1, uint8_t data1_2) const; - std::pair find_tool_by_id(uint32_t id) const; + virtual size_t num_weapon_classes() const = 0; + virtual size_t num_weapons_in_class(uint8_t data1_1) const = 0; + virtual const Weapon& get_weapon(uint8_t data1_1, uint8_t data1_2) const = 0; + virtual size_t num_armors_or_shields_in_class(uint8_t data1_1) const = 0; + virtual const ArmorOrShield& get_armor_or_shield(uint8_t data1_1, uint8_t data1_2) const = 0; + virtual size_t num_units() const = 0; + virtual const Unit& get_unit(uint8_t data1_2) const = 0; + virtual size_t num_mags() const = 0; + virtual const Mag& get_mag(uint8_t data1_1) const = 0; + virtual size_t num_tool_classes() const = 0; + virtual size_t num_tools_in_class(uint8_t data1_1) const = 0; + virtual const Tool& get_tool(uint8_t data1_1, uint8_t data1_2) const = 0; + virtual std::pair find_tool_by_id(uint32_t id) const = 0; - std::variant + std::variant definition_for_primary_identifier(uint32_t primary_identifier) const; - float get_sale_divisor(uint8_t data1_0, uint8_t data1_1) const; - const MagFeedResult& get_mag_feed_result(uint8_t table_index, uint8_t which) const; - uint8_t get_item_stars(uint32_t id) const; - uint8_t get_special_stars(uint8_t special) const; - const Special& get_special(uint8_t special) const; - const StatBoost& get_stat_boost(uint8_t entry_index) const; - uint8_t get_max_tech_level(uint8_t char_class, uint8_t tech_num) const; - uint8_t get_weapon_v1_replacement(uint8_t data1_1) const; + virtual float get_sale_divisor(uint8_t data1_0, uint8_t data1_1) const = 0; + virtual const MagFeedResult& get_mag_feed_result(uint8_t table_index, uint8_t which) const = 0; + virtual uint8_t get_item_stars(uint32_t id) const = 0; + virtual uint8_t get_special_stars(uint8_t special) const = 0; + virtual size_t num_specials() const = 0; + virtual const Special& get_special(uint8_t special) const = 0; + virtual const StatBoost& get_stat_boost(uint8_t entry_index) const = 0; + virtual uint8_t get_max_tech_level(uint8_t char_class, uint8_t tech_num) const = 0; + virtual uint8_t get_weapon_class(uint8_t data1_1) const = 0; uint32_t get_item_id(const ItemData& item) const; uint32_t get_item_team_points(const ItemData& item) const; uint8_t get_item_base_stars(const ItemData& item) const; uint8_t get_item_adjusted_stars(const ItemData& item, bool ignore_unidentified = false) const; bool is_item_rare(const ItemData& item) const; - bool is_unsealable_item(uint8_t data1_0, uint8_t data1_1, uint8_t data1_2) const; - bool is_unsealable_item(const ItemData& param_1) const; + virtual bool is_unsealable_item(uint8_t data1_0, uint8_t data1_1, uint8_t data1_2) const = 0; + bool is_unsealable_item(const ItemData& item) const; const ItemCombination& get_item_combination(const ItemData& used_item, const ItemData& equipped_item) const; const std::vector& get_all_combinations_for_used_item(const ItemData& used_item) const; - const std::map>& get_all_item_combinations() const; - size_t num_events() const; - std::pair get_event_items(uint8_t event_number) const; + virtual const std::map>& get_all_item_combinations() const = 0; + virtual size_t num_events() const = 0; + virtual std::pair get_event_items(uint8_t event_number) const = 0; size_t price_for_item(const ItemData& item) const; - size_t num_weapon_classes; - size_t num_tool_classes; - size_t item_stars_first_id; - size_t item_stars_last_id; - size_t special_stars_begin_index; - size_t num_specials; - size_t first_rare_mag_index; - protected: - struct TableOffsetsDCProtos { - /* ## / NTE / 11/2000 */ - /* 00 / 0013 / 0013 */ le_uint32_t unknown_a0; - /* 04 / 2E1C / 2EB8 */ le_uint32_t weapon_table; - /* 08 / 2D94 / 2E28 */ le_uint32_t armor_table; - /* 0C / 2DA4 / 2E38 */ le_uint32_t unit_table; - /* 10 / 2DB4 / 2E48 */ le_uint32_t tool_table; - /* 14 / 2DAC / 2E40 */ le_uint32_t mag_table; - /* 18 / 1F98 / 202C */ le_uint32_t v1_replacement_table; - /* 1C / 1994 / 1A28 */ le_uint32_t photon_color_table; - /* 20 / 1C64 / 1CF8 */ le_uint32_t weapon_range_table; - /* 24 / 1FBF / 2053 */ le_uint32_t weapon_sale_divisor_table; - /* 28 / 1FE6 / 207A */ le_uint32_t sale_divisor_table; - /* 2C / 2F54 / 2FF0 */ le_uint32_t mag_feed_table; - /* 30 / 22A9 / 233D */ le_uint32_t star_value_table; - /* 34 / 23EE / 2484 */ le_uint32_t unknown_a1; - /* 38 / 275E / 27F4 */ le_uint32_t special_data_table; - /* 3C / 2804 / 2898 */ le_uint32_t stat_boost_table; - /* 40 / 1908 / 199C */ le_uint32_t shield_effect_table; - /* 44 / 0668 / 0668 */ le_uint32_t unknown_a2; - /* 48 / 030C / 030C */ le_uint32_t unknown_a3; - /* 4C / 2CE4 / 2D78 */ le_uint32_t unknown_a4; - } __packed_ws__(TableOffsetsDCProtos, 0x50); - - struct TableOffsetsV1V2 { - // TODO: Is weapon count 0x89 or 0x8A? It could be that the last entry in weapon_table is used for ???? items. - /* ## / V1 / V2*/ - /* 00 / 0013 / 0013 */ le_uint32_t unknown_a0; - /* 04 / 32E8 / 5AFC */ le_uint32_t weapon_table; // -> [{count, offset -> [WeaponV2]}](0x89) - /* 08 / 3258 / 5A5C */ le_uint32_t armor_table; // -> [{count, offset -> [ArmorOrShieldV2]}](2; armors and shields) - /* 0C / 3268 / 5A6C */ le_uint32_t unit_table; // -> {count, offset -> [UnitV2]} (last if out of range) - /* 10 / 3278 / 5A7C */ le_uint32_t tool_table; // -> [{count, offset -> [ToolV2]}](0x10) (last if out of range) - /* 14 / 3270 / 5A74 */ le_uint32_t mag_table; // -> {count, offset -> [MagV2]} - /* 18 / 23C8 / 3DF8 */ le_uint32_t v1_replacement_table; // -> [uint8_t](0x89) - /* 1C / 1DB0 / 2E4C */ le_uint32_t photon_color_table; // -> [0x24-byte structs](0x20) - /* 20 / 2080 / 32CC */ le_uint32_t weapon_range_table; // -> ??? - /* 24 / 23F0 / 3E84 */ le_uint32_t weapon_sale_divisor_table; // -> [float](0x89) - /* 28 / 248C / 40A8 */ le_uint32_t sale_divisor_table; // -> NonWeaponSaleDivisors - /* 2C / 3420 / 5F4C */ le_uint32_t mag_feed_table; // -> MagFeedResultsTable - /* 30 / 275C / 4378 */ le_uint32_t star_value_table; // -> [uint8_t](0x1C7) - /* 34 / 28A2 / 45E4 */ le_uint32_t unknown_a1; - /* 38 / 2C12 / 4540 */ le_uint32_t special_data_table; // -> [Special](0x29) - /* 3C / 2CB8 / 58DC */ le_uint32_t stat_boost_table; // -> [StatBoost] - /* 40 / 3198 / 5704 */ le_uint32_t shield_effect_table; // -> [8-byte structs] - } __packed_ws__(TableOffsetsV1V2, 0x44); - - struct TableOffsetsGCNTE { - /* 00 / 6F0C */ be_uint32_t weapon_table; // -> [{count, offset -> [WeaponV3/WeaponV4]}](0xED) - /* 04 / 6E4C */ be_uint32_t armor_table; // -> [{count, offset -> [ArmorOrShieldV3/ArmorOrShieldV4]}](2; armors and shields) - /* 08 / 6E5C */ be_uint32_t unit_table; // -> {count, offset -> [UnitV3/UnitV4]} (last if out of range) - /* 0C / 6E6C */ be_uint32_t tool_table; // -> [{count, offset -> [ToolV3/ToolV4]}](0x1A) (last if out of range) - /* 10 / 6E64 */ be_uint32_t mag_table; // -> {count, offset -> [MagV3/MagV4]} - /* 14 / 47BC */ be_uint32_t v1_replacement_table; // -> [uint8_t](0xED) - /* 18 / 37A4 */ be_uint32_t photon_color_table; // -> [0x24-byte structs](0x20) - /* 1C / 3A74 */ be_uint32_t weapon_range_table; // -> ??? - /* 20 / 484C */ be_uint32_t weapon_sale_divisor_table; // -> [float](0xED) - /* 24 / 4A80 */ be_uint32_t sale_divisor_table; // -> NonWeaponSaleDivisors - /* 28 / 7384 */ be_uint32_t mag_feed_table; // -> MagFeedResultsTable - /* 2C / 4D50 */ be_uint32_t star_value_table; // -> [uint8_t](0x330) (indexed by .id from weapon, armor, etc.) - /* 30 / 4F72 */ be_uint32_t special_data_table; // -> [Special] - /* 34 / 5018 */ be_uint32_t weapon_effect_table; // -> [16-byte structs] - /* 38 / 68B8 */ be_uint32_t stat_boost_table; // -> [StatBoost] - /* 3C / 61B8 */ be_uint32_t shield_effect_table; // -> [8-byte structs] - /* 40 / 69D8 */ be_uint32_t max_tech_level_table; // -> MaxTechniqueLevels - /* 44 / 737C */ be_uint32_t combination_table; // -> {count, offset -> [ItemCombination]} - /* 48 / 68B0 */ be_uint32_t unknown_a1; - /* 4C / 6B1C */ be_uint32_t tech_boost_table; // -> [TechniqueBoostEntry[3]] - } __packed_ws__(TableOffsetsGCNTE, 0x50); - - template - struct TableOffsetsV3V4T { - /* ## / GC / BB */ - /* 00 / F078 / 14884 */ U32T weapon_table; // -> [{count, offset -> [WeaponV3/WeaponV4]}](0xED) - /* 04 / EF90 / 1478C */ U32T armor_table; // -> [{count, offset -> [ArmorOrShieldV3/ArmorOrShieldV4]}](2; armors and shields) - /* 08 / EFA0 / 1479C */ U32T unit_table; // -> {count, offset -> [UnitV3/UnitV4]} (last if out of range) - /* 0C / EFB0 / 147AC */ U32T tool_table; // -> [{count, offset -> [ToolV3/ToolV4]}](0x1A) (last if out of range) - /* 10 / EFA8 / 147A4 */ U32T mag_table; // -> {count, offset -> [MagV3/MagV4]} - /* 14 / B88C / 0F4B8 */ U32T v1_replacement_table; // -> [uint8_t](0xED) - /* 18 / A7FC / 0DE7C */ U32T photon_color_table; // -> [0x24-byte structs](0x20) - /* 1C / AACC / 0E194 */ U32T weapon_range_table; // -> ??? - /* 20 / B938 / 0F5A8 */ U32T weapon_sale_divisor_table; // -> [float](0xED) - /* 24 / BBCC / 0F83C */ U32T sale_divisor_table; // -> NonWeaponSaleDivisors - /* 28 / F608 / 1502C */ U32T mag_feed_table; // -> MagFeedResultsTable - /* 2C / BE9C / 0FB0C */ U32T star_value_table; // -> [uint8_t](0x330) (indexed by .id from weapon, armor, etc.) - /* 30 / C100 / 0FE3C */ U32T special_data_table; // -> [Special] - /* 34 / C1A4 / 0FEE0 */ U32T weapon_effect_table; // -> [16-byte structs] - /* 38 / DE50 / 1275C */ U32T stat_boost_table; // -> [StatBoost] - /* 3C / D6E4 / 11C80 */ U32T shield_effect_table; // -> [8-byte structs] - /* 40 / DF88 / 12894 */ U32T max_tech_level_table; // -> MaxTechniqueLevels - /* 44 / F5D0 / 14FF4 */ U32T combination_table; // -> {count, offset -> [ItemCombination]} - /* 48 / DE48 / 12754 */ U32T unknown_a1; - /* 4C / EB8C / 14278 */ U32T tech_boost_table; // -> [TechniqueBoost[3]] - /* 50 / F5F0 / 15014 */ U32T unwrap_table; // -> {count, offset -> [{count, offset -> [EventItem]}]} - /* 54 / F5F8 / 1501C */ U32T unsealable_table; // -> {count, offset -> [UnsealableItem]} - /* 58 / F600 / 15024 */ U32T ranged_special_table; // -> {count, offset -> [4-byte structs]} - } __attribute__((packed)); - using TableOffsetsV3V4 = TableOffsetsV3V4T; - using TableOffsetsV3V4BE = TableOffsetsV3V4T; - check_struct_size(TableOffsetsV3V4, 0x5C); - check_struct_size(TableOffsetsV3V4BE, 0x5C); - - Version version; std::shared_ptr data; phosg::StringReader r; - const TableOffsetsDCProtos* offsets_dc_protos; - const TableOffsetsV1V2* offsets_v1_v2; - const TableOffsetsGCNTE* offsets_gc_nte; - const TableOffsetsV3V4* offsets_v3_le; - const TableOffsetsV3V4BE* offsets_v3_be; - const TableOffsetsV3V4* offsets_v4; - // These are unused if offsets_v4 is not null (in that case, we just return references to within the data string) - mutable std::unordered_map parsed_weapons; - mutable std::vector parsed_armors; - mutable std::vector parsed_shields; - mutable std::vector parsed_units; - mutable std::vector parsed_mags; - mutable std::unordered_map parsed_tools; - mutable std::vector parsed_specials; - mutable std::vector parsed_stat_boosts; + mutable std::unordered_map weapons; + mutable std::vector armors; + mutable std::vector shields; + mutable std::vector units; + mutable std::vector mags; + mutable std::unordered_map tools; + + mutable std::vector specials; + mutable std::vector stat_boosts; // Key is used_item. We can't index on (used_item, equipped_item) because equipped_item may contain wildcards, and // the matching order matters. mutable std::map> item_combination_index; - template - std::pair find_tool_by_id_t(uint32_t tool_table_offset, uint32_t id) const; - template - float get_sale_divisor_t(const OffsetsT* offsets, uint8_t data1_0, uint8_t data1_1) const; - template - size_t num_events_t(uint32_t base_offset) const; - template - std::pair get_event_items_t(uint32_t base_offset, uint8_t event_number) const; + explicit ItemParameterTable(std::shared_ptr data); }; class MagEvolutionTable { diff --git a/src/ServerState.cc b/src/ServerState.cc index 600f0f1f..2ac4f4c6 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -2149,7 +2149,7 @@ void ServerState::load_item_definitions() { string path = std::format("system/item-tables/ItemPMT-{}.prs", file_path_token_for_version(v)); config_log.debug_f("Loading item definition table {}", path); auto data = make_shared(prs_decompress(phosg::load_file(path))); - new_item_parameter_tables[v_s] = make_shared(data, v); + new_item_parameter_tables[v_s] = ItemParameterTable::create(data, v); } auto json = phosg::JSON::parse(phosg::load_file("system/item-tables/translation-table.json"));