From d965ff50317bab91abbee15cbcfc31628902312b Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sun, 6 Jul 2025 13:57:31 -0700 Subject: [PATCH] add stat boosts to ItemPMT formatting --- src/ItemNameIndex.cc | 18 +++-- src/ItemParameterTable.cc | 143 ++++++++++++++++++++++---------------- src/ItemParameterTable.hh | 40 ++++++++--- 3 files changed, 129 insertions(+), 72 deletions(-) diff --git a/src/ItemNameIndex.cc b/src/ItemNameIndex.cc index 1834eeab..365c60f5 100644 --- a/src/ItemNameIndex.cc +++ b/src/ItemNameIndex.cc @@ -697,7 +697,8 @@ void ItemNameIndex::print_table(FILE* stream) const { item.data1[2] = data1_2; string name = this->describe_item(item); - phosg::fwrite_fmt(stream, "00{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:04X} {:5} {:5} {:5} {:5} {:5} {:5} {:3} {:02X} {:02X} {:3} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:2}* {} {} {}\n", + auto& stat_boost = pmt->get_stat_boost(w.stat_boost_entry_index); + 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, @@ -715,7 +716,11 @@ void ItemNameIndex::print_table(FILE* stream) const { w.photon, w.special, w.ata, - w.stat_boost, + w.stat_boost_entry_index, + stat_boost.stats[0], + stat_boost.amounts[0], + stat_boost.stats[1], + stat_boost.amounts[1], w.projectile, w.trail1_x, w.trail1_y, @@ -754,7 +759,8 @@ void ItemNameIndex::print_table(FILE* stream) const { item.data1[2] = data1_2; string name = this->describe_item(item); - 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} {:02X} {:02X} {:2}* {} {}\n", + auto& stat_boost = pmt->get_stat_boost(a.stat_boost_entry_index); + 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, @@ -774,7 +780,11 @@ void ItemNameIndex::print_table(FILE* stream) const { a.elt, a.dfp_range, a.evp_range, - a.stat_boost, + a.stat_boost_entry_index, + stat_boost.stats[0], + stat_boost.amounts[0], + stat_boost.stats[1], + stat_boost.amounts[1], a.tech_boost, a.flags_type, a.unknown_a4, diff --git a/src/ItemParameterTable.cc b/src/ItemParameterTable.cc index a3a11aef..c76f54de 100644 --- a/src/ItemParameterTable.cc +++ b/src/ItemParameterTable.cc @@ -175,7 +175,7 @@ ItemParameterTable::WeaponV4 ItemParameterTable::WeaponV1V2::to_v4() const { ret.photon = this->photon; ret.special = this->special; ret.ata = this->ata; - ret.stat_boost = this->stat_boost; + ret.stat_boost_entry_index = this->stat_boost_entry_index; return ret; } @@ -195,7 +195,7 @@ ItemParameterTable::WeaponV4 ItemParameterTable::WeaponGCNTE::to_v4() const { ret.photon = this->photon; ret.special = this->special; ret.ata = this->ata; - ret.stat_boost = this->stat_boost; + 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; @@ -225,7 +225,7 @@ ItemParameterTable::WeaponV4 ItemParameterTable::WeaponV3T::to_v4() const { ret.photon = this->photon; ret.special = this->special; ret.ata = this->ata; - ret.stat_boost = this->stat_boost; + 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; @@ -277,7 +277,7 @@ ItemParameterTable::ArmorOrShieldV4 ItemParameterTable::ArmorOrShieldV1V2::to_v4 ret.elt = this->elt; ret.dfp_range = this->dfp_range; ret.evp_range = this->evp_range; - ret.stat_boost = this->stat_boost; + 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; @@ -303,7 +303,7 @@ ItemParameterTable::ArmorOrShieldV4 ItemParameterTable::ArmorOrShieldV3T::to ret.elt = this->elt; ret.dfp_range = this->dfp_range; ret.evp_range = this->evp_range; - ret.stat_boost = this->stat_boost; + 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; @@ -522,25 +522,22 @@ const ItemParameterTable::ArmorOrShieldV4& ItemParameterTable::get_armor_or_shie } return ret; } catch (const std::out_of_range&) { - ArmorOrShieldV4 def_v4; - - if (this->offsets_dc_protos) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_dc_protos->armor_table, data1_1 - 1, data1_2).to_v4(); - } else if (this->offsets_v1_v2) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v1_v2->armor_table, data1_1 - 1, data1_2).to_v4(); - } else if (this->offsets_gc_nte) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_gc_nte->armor_table, data1_1 - 1, data1_2).to_v4(); - } else if (this->offsets_v3_le) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_le->armor_table, data1_1 - 1, data1_2).to_v4(); - } else if (this->offsets_v3_be) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_be->armor_table, data1_1 - 1, data1_2).to_v4(); - } else { - throw logic_error("table is not v2, v3, or v4"); + 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"); + } } - if (data1_2 >= parsed_vec.size()) { - parsed_vec.resize(data1_2 + 1); - } - parsed_vec[data1_2] = def_v4; return parsed_vec[data1_2]; } } @@ -575,24 +572,22 @@ const ItemParameterTable::UnitV4& ItemParameterTable::get_unit(uint8_t data1_2) } return ret; } catch (const std::out_of_range&) { - UnitV4 def_v4; - if (this->offsets_dc_protos) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_dc_protos->unit_table, 0, data1_2).to_v4(); - } else if (this->offsets_v1_v2) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v1_v2->unit_table, 0, data1_2).to_v4(); - } else if (this->offsets_gc_nte) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_gc_nte->unit_table, 0, data1_2).to_v4(); - } else if (this->offsets_v3_le) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_le->unit_table, 0, data1_2).to_v4(); - } else if (this->offsets_v3_be) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_be->unit_table, 0, data1_2).to_v4(); - } else { - throw logic_error("table is not v2, v3, or v4"); + 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"); + } } - if (data1_2 >= this->parsed_units.size()) { - this->parsed_units.resize(data1_2 + 1); - } - this->parsed_units[data1_2] = def_v4; return this->parsed_units[data1_2]; } } @@ -627,28 +622,26 @@ const ItemParameterTable::MagV4& ItemParameterTable::get_mag(uint8_t data1_1) co } return ret; } catch (const std::out_of_range&) { - MagV4 def_v4; - if (this->offsets_dc_protos) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_dc_protos->mag_table, 0, data1_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, data1_1).to_v4(); + 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 { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v1_v2->mag_table, 0, data1_1).to_v4(); + throw logic_error("table is not v2, v3, or v4"); } - } else if (this->offsets_gc_nte) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_gc_nte->mag_table, 0, data1_1).to_v4(); - } else if (this->offsets_v3_le) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_le->mag_table, 0, data1_1).to_v4(); - } else if (this->offsets_v3_be) { - def_v4 = indirect_lookup_2d(this->r, this->offsets_v3_be->mag_table, 0, data1_1).to_v4(); - } else { - throw logic_error("table is not v2, v3, or v4"); } - if (data1_1 >= this->parsed_mags.size()) { - this->parsed_mags.resize(data1_1 + 1); - } - this->parsed_mags[data1_1] = def_v4; return this->parsed_mags[data1_1]; } } @@ -928,6 +921,38 @@ const ItemParameterTable::Special& ItemParameterTable::get_special(uint8_t speci } } +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"); diff --git a/src/ItemParameterTable.hh b/src/ItemParameterTable.hh index 0e3325a1..5be2f11d 100644 --- a/src/ItemParameterTable.hh +++ b/src/ItemParameterTable.hh @@ -71,7 +71,7 @@ public: /* 11 */ uint8_t photon = 0; /* 12 */ uint8_t special = 0; /* 13 */ uint8_t ata = 0; - /* 14 */ uint8_t stat_boost = 0; // TODO: This could be larger (16 or 32 bits) + /* 14 */ uint8_t stat_boost_entry_index = 0; // TODO: This could be larger (16 or 32 bits) /* 15 */ parray unknown_a9; /* 18 */ @@ -91,7 +91,7 @@ public: /* 17 */ uint8_t photon = 0; /* 18 */ uint8_t special = 0; /* 19 */ uint8_t ata = 0; - /* 1A */ uint8_t stat_boost = 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; @@ -120,7 +120,7 @@ public: /* 17 */ uint8_t photon = 0; /* 18 */ uint8_t special = 0; /* 19 */ uint8_t ata = 0; - /* 1A */ uint8_t stat_boost = 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; @@ -162,7 +162,7 @@ public: /* 1B */ uint8_t photon = 0; /* 1C */ uint8_t special = 0; /* 1D */ uint8_t ata = 0; - /* 1E */ uint8_t stat_boost = 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; @@ -201,7 +201,7 @@ public: template struct ArmorOrShieldFinalT : ArmorOrShieldT { - /* 14 */ uint8_t stat_boost = 0; + /* 14 */ uint8_t stat_boost_entry_index = 0; /* 15 */ uint8_t tech_boost = 0; // TODO: Figure out what this does. Only a few values appear to do anything: // Shields: @@ -391,10 +391,30 @@ public: template struct StatBoostT { - uint8_t stat1 = 0; - uint8_t stat2 = 0; - U16T amount1 = 0; - U16T amount2 = 0; + // 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. + // Values for stats: + // 01 = ATP bonus + // 02 = ATA bonus + // 03 = EVP bonus + // 04 = DFP bonus + // 05 = MST bonus + // 06 = HP bonus + // 07 = LCK bonus + // 08 = all of the above bonuses except HP + // 09 = ATP penalty + // 0A = ATA penalty + // 0B = EVP penalty + // 0C = DFP penalty + // 0D = MST penalty + // 0E = HP penalty + // 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; } __attribute__((packed)); using StatBoost = StatBoostT; using StatBoostBE = StatBoostT; @@ -476,6 +496,7 @@ public: 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; @@ -624,6 +645,7 @@ protected: mutable std::vector parsed_mags; mutable std::unordered_map parsed_tools; mutable std::vector parsed_specials; + mutable std::vector parsed_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.