diff --git a/TODO.md b/TODO.md index 9273a3e2..493f618f 100644 --- a/TODO.md +++ b/TODO.md @@ -4,6 +4,7 @@ - Implement decrypt/encrypt actions for VMS files - Make UI strings localizable (e.g. entries in menus, welcome message, etc.) - Add an idle connection timeout for proxy sessions +- Clean up ItemParameterTable implementation (see comment ad the top of the class definition) ## Episode 3 diff --git a/src/ItemNameIndex.cc b/src/ItemNameIndex.cc index 904a2d9f..b2b4fa9a 100644 --- a/src/ItemNameIndex.cc +++ b/src/ItemNameIndex.cc @@ -11,48 +11,22 @@ ItemNameIndex::ItemNameIndex( : version(version), item_parameter_table(item_parameter_table) { - auto find_items_1d = [&](uint64_t data1, size_t position) -> size_t { - ItemData item(data1, 0); - for (size_t x = 0; x < 0x100; x++) { - item.data1[position] = x; - uint32_t id; - try { - id = this->item_parameter_table->get_item_id(item); - } catch (const out_of_range&) { - return x; - } - const string* name = nullptr; - try { - name = &name_coll.at(id); - } catch (const out_of_range&) { - } - - if (name) { - auto meta = make_shared(); - meta->primary_identifier = item.primary_identifier(); - meta->name = *name; - this->primary_identifier_index.emplace(meta->primary_identifier, meta); - this->name_index.emplace(tolower(meta->name), meta); - } + for (uint32_t primary_identifier : item_parameter_table->compute_all_valid_primary_identifiers()) { + const string* name = nullptr; + try { + ItemData item = ItemData::from_primary_identifier(this->version, primary_identifier); + name = &name_coll.at(item_parameter_table->get_item_id(item)); + } catch (const out_of_range&) { } - return 0x100; - }; - auto find_items_2d = [&](uint64_t data1) { - for (size_t x = 0; x < 0x100; x++) { - size_t effective_data1 = data1 | (static_cast(x) << 48); - size_t data2_position = (effective_data1 == 0x0302000000000000) ? 4 : 2; - if (find_items_1d(effective_data1, data2_position) == 0) { - break; - } - } - }; - find_items_2d(0x0000000000000000); - find_items_1d(0x0101000000000000, 2); - find_items_1d(0x0102000000000000, 2); - find_items_1d(0x0103000000000000, 2); - find_items_1d(0x0200000000000000, 1); - find_items_2d(0x0300000000000000); + if (name) { + auto meta = make_shared(); + meta->primary_identifier = primary_identifier; + meta->name = *name; + this->primary_identifier_index.emplace(meta->primary_identifier, meta); + this->name_index.emplace(tolower(meta->name), meta); + } + } } static const char* s_rank_name_characters = "\0ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; @@ -645,3 +619,309 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript return ret; } + +void ItemNameIndex::print_table(FILE* stream) const { + auto pmt = this->item_parameter_table; + + fprintf(stream, "WEAPON => ---ID--- TYPE SKIN POINTS FLAG ATPLO ATPHI ATPRQ MSTRQ ATARQ -MST- GND PH SP ATA SB PJ 1X 1Y 2X 2Y CL A1 A2 A3 A4 A5 TB CT 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); + float sale_divisor = pmt->get_sale_divisor(0x00, data1_1); + string divisor_str = string_printf("%g", sale_divisor); + divisor_str.resize(13, ' '); + + 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); + bool is_unsealable = pmt->is_unsealable_item(0x00, data1_1, data1_2); + + ItemData item; + item.data1[0] = 0x00; + item.data1[1] = data1_1; + item.data1[2] = data1_2; + string name = this->describe_item(item); + + fprintf(stream, "00%02zX%02zX => %08" PRIX32 " %04hX %04hX %6" PRIu32 " %04hX %5hu %5hu %5hu %5hu %5hu %5hu %3hhu %02hhX %02hhX %3hhu %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %2hhu* %s %s %s\n", + data1_1, + data1_2, + w.base.id.load(), + w.base.type.load(), + w.base.skin.load(), + w.base.team_points.load(), + w.class_flags.load(), + w.atp_min.load(), + w.atp_max.load(), + w.atp_required.load(), + w.mst_required.load(), + w.ata_required.load(), + w.mst.load(), + w.max_grind, + w.photon, + w.special, + w.ata, + w.stat_boost, + w.projectile, + w.trail1_x, + w.trail1_y, + w.trail2_x, + w.trail2_y, + w.color, + w.unknown_a1, + w.unknown_a2, + w.unknown_a3, + w.unknown_a4, + w.unknown_a5, + w.tech_boost, + w.combo_type, + v1_replacement, + stars, + is_unsealable ? "YES" : " no", + divisor_str.c_str(), + name.c_str()); + } + } + + fprintf(stream, "ARMOR => ---ID--- TYPE SKIN POINTS -DFP- -EVP- BP BE FLAG LVL EFR ETH EIC EDK ELT DFR EVR SB TB -A2- ST* ---DIVISOR--- NAME\n"); + for (size_t data1_1 = 1; data1_1 < 3; data1_1++) { + float sale_divisor = pmt->get_sale_divisor(0x01, data1_1); + string divisor_str = string_printf("%g", sale_divisor); + divisor_str.resize(13, ' '); + + 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); + + ItemData item; + item.data1[0] = 0x01; + item.data1[1] = data1_1; + item.data1[2] = data1_2; + string name = this->describe_item(item); + + fprintf(stream, "01%02zX%02zX => %08" PRIX32 " %04hX %04hX %6" PRIu32 " %5hu %5hu %02hhX %02hhX %04hX %3hhu %3hhu %3hhu %3hhu %3hhu %3hhu %3hhu %3hhu %02hhX %02hhX %04hX %2hhu* %s %s\n", + data1_1, + data1_2, + a.base.id.load(), + a.base.type.load(), + a.base.skin.load(), + a.base.team_points.load(), + a.dfp.load(), + a.evp.load(), + a.block_particle, + a.block_effect, + a.class_flags.load(), + static_cast(a.required_level + 1), + a.efr, + a.eth, + a.eic, + a.edk, + a.elt, + a.dfp_range, + a.evp_range, + a.stat_boost, + a.tech_boost, + a.unknown_a2.load(), + stars, + divisor_str.c_str(), + name.c_str()); + } + } + + fprintf(stream, "UNIT => ---ID--- TYPE SKIN POINTS STAT COUNT ST-MOD ST* ---DIVISOR--- NAME\n"); + { + float sale_divisor = pmt->get_sale_divisor(0x01, 0x03); + string divisor_str = string_printf("%g", sale_divisor); + divisor_str.resize(13, ' '); + + 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); + + ItemData item; + item.data1[0] = 0x01; + item.data1[1] = 0x03; + item.data1[2] = data1_2; + string name = this->describe_item(item); + + fprintf(stream, "0103%02zX => %08" PRIX32 " %04hX %04hX %6" PRIu32 " %04hX %5hu %6hd %2hhu* %s %s\n", + data1_2, + u.base.id.load(), + u.base.type.load(), + u.base.skin.load(), + u.base.team_points.load(), + u.stat.load(), + u.stat_amount.load(), + u.modifier_amount.load(), + stars, + divisor_str.c_str(), + name.c_str()); + } + } + + fprintf(stream, "MAG => ---ID--- TYPE SKIN POINTS FTBL PB AC E1 E2 E3 E4 C1 C2 C3 C4 FLAG ST* ---DIVISOR--- NAME\n"); + { + size_t data1_1_limit = pmt->num_mags(); + for (size_t data1_1 = 0; data1_1 < data1_1_limit; data1_1++) { + const auto& m = pmt->get_mag(data1_1); + uint8_t stars = pmt->get_item_stars(m.base.id); + + float sale_divisor = pmt->get_sale_divisor(0x02, data1_1); + string divisor_str = string_printf("%g", sale_divisor); + divisor_str.resize(13, ' '); + + ItemData item; + item.data1[0] = 0x02; + item.data1[1] = data1_1; + item.data1[2] = 0x00; + string name = this->describe_item(item); + + fprintf(stream, "02%02zX00 => %08" PRIX32 " %04hX %04hX %6" PRIu32 " %04hX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %04hX %2hhu* %s %s\n", + data1_1, + m.base.id.load(), + m.base.type.load(), + m.base.skin.load(), + m.base.team_points.load(), + m.feed_table.load(), + m.photon_blast, + m.activation, + m.on_pb_full, + m.on_low_hp, + m.on_death, + m.on_boss, + m.on_pb_full_flag, + m.on_low_hp_flag, + m.on_death_flag, + m.on_boss_flag, + m.class_flags.load(), + stars, + divisor_str.c_str(), + name.c_str()); + } + } + + fprintf(stream, "TOOL => ---ID--- TYPE SKIN POINTS COUNT TECH -COST- ITEMFLAG ST* ---DIVISOR--- NAME\n"); + 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 = string_printf("%g", sale_divisor); + divisor_str.resize(13, ' '); + + size_t data1_2_limit = pmt->num_tools_in_class(data1_1); + for (size_t data1_2 = 0; data1_2 < data1_2_limit; data1_2++) { + const auto& t = pmt->get_tool(data1_1, data1_2); + uint8_t stars = pmt->get_item_stars(t.base.id); + + ItemData item; + item.data1[0] = 0x03; + item.data1[1] = data1_1; + item.data1[(data1_1 == 0x02) ? 4 : 2] = data1_2; + item.set_tool_item_amount(this->version, 1); + string name = this->describe_item(item); + + fprintf(stream, "03%02zX%02zX => %08" PRIX32 " %04hX %04hX %6" PRIu32 " %5hu %04hX %6" PRId32 " %08" PRIX32 " %2hhu* %s %s\n", + data1_1, + data1_2, + t.base.id.load(), + t.base.type.load(), + t.base.skin.load(), + t.base.team_points.load(), + t.amount.load(), + t.tech.load(), + t.cost.load(), + t.item_flag.load(), + stars, + divisor_str.c_str(), + name.c_str()); + } + } + + fprintf(stream, "CLASS => F GF RF B GB RB Z GZ RZ GR DB JL ZL SH RY RS AT RV MG\n"); + for (size_t char_class = 0; char_class < 12; char_class++) { + fprintf(stream, "%9s =>", name_for_char_class(char_class)); + for (size_t tech_num = 0; tech_num < 0x13; tech_num++) { + uint8_t max_level = pmt->get_max_tech_level(char_class, tech_num) + 1; + if (max_level == 0x00) { + fprintf(stream, " "); + } else { + fprintf(stream, " %2hhu", max_level); + } + } + fprintf(stream, "\n"); + } + + fprintf(stream, "CLASS => F GF RF B GB RB Z GZ RZ GR DB JL ZL SH RY RS AT RV MG\n"); + for (size_t char_class = 0; char_class < 12; char_class++) { + fprintf(stream, "%9s =>", name_for_char_class(char_class)); + for (size_t tech_num = 0; tech_num < 0x13; tech_num++) { + uint8_t max_level = pmt->get_max_tech_level(char_class, tech_num) + 1; + if (max_level == 0x00) { + fprintf(stream, " "); + } else { + fprintf(stream, " %2hhu", max_level); + } + } + fprintf(stream, "\n"); + } + + for (size_t table_index = 0; table_index < 8; table_index++) { + static const char* names[11] = { + "Monomate", "Dimate", "Trimate", "Monofluid", + "Difluid", "Trifluid", "Antidote", "Antiparalysis", + "Sol Atomizer", "Moon Atomizer", "Star Atomizer"}; + fprintf(stream, "TABLE %02zX => -DEF -POW -DEX MIND -IQ- SYNC\n", table_index); + for (size_t which = 0; which < 11; which++) { + const auto& res = pmt->get_mag_feed_result(table_index, which); + fprintf(stream, "%14s => %4hhd %4hhd %4hhd %4hhd %4hhd %4hhd\n", + names[which], res.def, res.pow, res.dex, res.mind, res.iq, res.synchro); + } + } + + fprintf(stream, "SPECIAL => TYPE COUNT ST*\n"); + 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); + fprintf(stream, " %02zX => %04hX %5hu %2hu*\n", index, sp.type.load(), sp.amount.load(), stars); + } + + fprintf(stream, "---USE + -EQUIP => RESULT MLV GND LVL CLS\n"); + for (const auto& combo_list_it : pmt->get_all_item_combinations()) { + for (const auto& combo : combo_list_it.second) { + fprintf(stream, "%02hhX%02hhX%02hhX + %02hhX%02hhX%02hhX => %02hhX%02hhX%02hhX", + combo.used_item[0], combo.used_item[1], combo.used_item[2], + combo.equipped_item[0], combo.equipped_item[1], combo.equipped_item[2], + combo.result_item[0], combo.result_item[1], combo.result_item[2]); + if (combo.mag_level != 0xFF) { + fprintf(stream, " %3hu", combo.mag_level); + } else { + fprintf(stream, " "); + } + if (combo.grind != 0xFF) { + fprintf(stream, " %3hu", combo.grind); + } else { + fprintf(stream, " "); + } + if (combo.level != 0xFF) { + fprintf(stream, " %3hu", combo.level); + } else { + fprintf(stream, " "); + } + if (combo.char_class != 0xFF) { + fprintf(stream, " %3hu\n", combo.char_class); + } else { + fprintf(stream, " \n"); + } + } + } + + size_t num_events = pmt->num_events(); + for (size_t event_number = 0; event_number < num_events; event_number++) { + fprintf(stream, "EV %3zu => PRB\n", event_number); + auto events_list = pmt->get_event_items(event_number); + for (size_t z = 0; z < events_list.second; z++) { + const auto& event_item = events_list.first[z]; + fprintf(stream, "%02hhX%02hhX%02hhX => %3hhu\n", + event_item.item[0], event_item.item[1], event_item.item[2], event_item.probability); + } + } +} diff --git a/src/ItemNameIndex.hh b/src/ItemNameIndex.hh index 451249d4..27138a7a 100644 --- a/src/ItemNameIndex.hh +++ b/src/ItemNameIndex.hh @@ -38,6 +38,8 @@ public: std::string describe_item(const ItemData& item, bool include_color_escapes = false) const; ItemData parse_item_description(const std::string& description) const; + void print_table(FILE* stream) const; + private: ItemData parse_item_description_phase(const std::string& description, bool skip_special) const; diff --git a/src/ItemParameterTable.cc b/src/ItemParameterTable.cc index af76cdb4..941f0080 100644 --- a/src/ItemParameterTable.cc +++ b/src/ItemParameterTable.cc @@ -3,64 +3,462 @@ using namespace std; ItemParameterTable::ItemParameterTable(shared_ptr data, Version version) - : data(data), + : version(version), + data(data), r(*data), - offsets_v2(nullptr), - offsets_v3(nullptr), + offsets_dc_protos(nullptr), + offsets_v1_v2(nullptr), + offsets_gc_nte(nullptr), + offsets_v3_le(nullptr), + offsets_v3_be(nullptr), offsets_v4(nullptr) { - switch (version) { - case Version::V2: { - size_t offset_table_offset = this->r.pget_u32l(this->data->size() - 0x10); - this->offsets_v2 = &this->r.pget(offset_table_offset); + 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_V1_11_2000_PROTOTYPE: { + 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->star_value_table_size = 0x1C7; + this->num_specials = 0x29; break; } - case Version::V3: { - size_t offset_table_offset = this->r.pget_u32b(this->data->size() - 0x10); - this->offsets_v3 = &this->r.pget>(offset_table_offset); + + 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->star_value_table_size = 0x263; + this->num_specials = 0x29; break; } - case Version::V4: { - size_t offset_table_offset = this->r.pget_u32l(this->data->size() - 0x10); + + 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->star_value_table_size = 0x330; + this->num_specials = 0x29; break; } default: throw logic_error("invalid item parameter table version"); } - this->num_specials = 0x29; this->first_rare_mag_index = 0x28; } +set ItemParameterTable::compute_all_valid_primary_identifiers() const { + set ret; + + auto find_items_1d = [&](uint64_t data1, size_t position) -> size_t { + ItemData item(data1, 0); + for (size_t x = 0; x < 0x100; x++) { + item.data1[position] = x; + try { + this->get_item_id(item); + } catch (const out_of_range&) { + return x; + } + ret.emplace(item.primary_identifier()); + } + return 0x100; + }; + auto find_items_2d = [&](uint64_t data1) { + for (size_t x = 0; x < 0x100; x++) { + size_t effective_data1 = data1 | (static_cast(x) << 48); + size_t data2_position = (effective_data1 == 0x0302000000000000) ? 4 : 2; + if (find_items_1d(effective_data1, data2_position) == 0) { + break; + } + } + }; + + find_items_2d(0x0000000000000000); + find_items_1d(0x0101000000000000, 2); + find_items_1d(0x0102000000000000, 2); + find_items_1d(0x0103000000000000, 2); + find_items_1d(0x0200000000000000, 1); + find_items_2d(0x0300000000000000); + 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 = this->stat_boost; + return ret; +} + +ItemParameterTable::WeaponV4 ItemParameterTable::WeaponGCNTE::to_v4() const { + WeaponV4 ret; + ret.base.id = this->base.id.load(); + ret.base.type = this->base.type.load(); + ret.base.skin = this->base.skin.load(); + ret.class_flags = this->class_flags.load(); + ret.atp_min = this->atp_min.load(); + ret.atp_max = this->atp_max.load(); + ret.atp_required = this->atp_required.load(); + ret.mst_required = this->mst_required.load(); + ret.ata_required = this->ata_required.load(); + ret.mst = this->mst.load(); + ret.max_grind = this->max_grind; + ret.photon = this->photon; + ret.special = this->special; + ret.ata = this->ata; + ret.stat_boost = this->stat_boost; + 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_a2 = this->unknown_a2; + ret.unknown_a3 = this->unknown_a3; + return ret; +} + +template +ItemParameterTable::WeaponV4 ItemParameterTable::WeaponV3::to_v4() const { + WeaponV4 ret; + ret.base.id = this->base.id.load(); + ret.base.type = this->base.type.load(); + ret.base.skin = this->base.skin.load(); + ret.class_flags = this->class_flags.load(); + ret.atp_min = this->atp_min.load(); + ret.atp_max = this->atp_max.load(); + ret.atp_required = this->atp_required.load(); + ret.mst_required = this->mst_required.load(); + ret.ata_required = this->ata_required.load(); + ret.mst = this->mst.load(); + ret.max_grind = this->max_grind; + ret.photon = this->photon; + ret.special = this->special; + ret.ata = this->ata; + ret.stat_boost = this->stat_boost; + 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_a2 = this->unknown_a2; + ret.unknown_a3 = this->unknown_a3; + ret.unknown_a4 = this->unknown_a4; + ret.unknown_a5 = this->unknown_a5; + ret.tech_boost = this->tech_boost; + ret.combo_type = this->combo_type; + 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 = this->stat_boost; + ret.tech_boost = this->tech_boost; + ret.unknown_a2 = this->unknown_a2; + return ret; +} + +template +ItemParameterTable::ArmorOrShieldV4 ItemParameterTable::ArmorOrShieldV3::to_v4() const { + ArmorOrShieldV4 ret; + ret.base.id = this->base.id.load(); + ret.base.type = this->base.type.load(); + ret.base.skin = this->base.skin.load(); + ret.dfp = this->dfp.load(); + ret.evp = this->evp.load(); + ret.block_particle = this->block_particle; + ret.block_effect = this->block_effect; + ret.class_flags = this->class_flags.load(); + 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 = this->stat_boost; + ret.tech_boost = this->tech_boost; + ret.unknown_a2 = this->unknown_a2.load(); + 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::UnitV3::to_v4() const { + UnitV4 ret; + ret.base.id = this->base.id.load(); + ret.base.type = this->base.type.load(); + ret.base.skin = this->base.skin.load(); + ret.stat = this->stat.load(); + ret.stat_amount = this->stat_amount.load(); + ret.modifier_amount = this->modifier_amount.load(); + 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::MagV3::to_v4() const { + MagV4 ret; + ret.base.id = this->base.id.load(); + ret.base.type = this->base.type.load(); + ret.base.skin = this->base.skin.load(); + ret.feed_table = this->feed_table.load(); + 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.load(); + 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_flag = this->item_flag; + return ret; +} + +template +ItemParameterTable::ToolV4 ItemParameterTable::ToolV3::to_v4() const { + ToolV4 ret; + ret.base.id = this->base.id.load(); + ret.base.type = this->base.type.load(); + ret.base.skin = this->base.skin.load(); + ret.amount = this->amount.load(); + ret.tech = this->tech.load(); + ret.cost = this->cost.load(); + ret.item_flag = this->item_flag.load(); + return ret; +} + +template +size_t indirect_lookup_2d_count(const StringReader& r, size_t root_offset, size_t co_index) { + using ArrayRefT = typename std::conditional_t; + return r.pget(root_offset + sizeof(ArrayRefT) * co_index).count; +} + +template +const T& indirect_lookup_2d(const StringReader& r, size_t root_offset, size_t co_index, size_t item_index) { + using ArrayRefT = typename std::conditional_t; + + 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) { - const auto& co = this->r.pget(this->offsets_v4->weapon_table + sizeof(ArrayRefLE) * data1_1); - if (data1_2 >= co.count) { - throw out_of_range("weapon ID out of range"); - } - return this->r.pget(co.offset + sizeof(WeaponV4) * data1_2); + return indirect_lookup_2d(this->r, this->offsets_v4->weapon_table, data1_1, data1_2); } uint16_t key = (data1_1 << 8) | data1_2; @@ -68,79 +466,51 @@ const ItemParameterTable::WeaponV4& ItemParameterTable::get_weapon(uint8_t data1 return this->parsed_weapons.at(key); } catch (const std::out_of_range&) { WeaponV4 def_v4; - - if (this->offsets_v2) { - const auto& co = this->r.pget(this->offsets_v2->weapon_table + sizeof(ArrayRefLE) * data1_1); - if (data1_2 >= co.count) { - throw out_of_range("weapon ID out of range"); - } - const auto& def_v2 = this->r.pget(co.offset + sizeof(WeaponV2) * data1_2); - def_v4.base.id = def_v2.base.id; - def_v4.class_flags = def_v2.class_flags; - def_v4.atp_min = def_v2.atp_min; - def_v4.atp_max = def_v2.atp_max; - def_v4.atp_required = def_v2.atp_required; - def_v4.mst_required = def_v2.mst_required; - def_v4.ata_required = def_v2.ata_required; - def_v4.max_grind = def_v2.max_grind; - def_v4.photon = def_v2.photon; - def_v4.special = def_v2.special; - def_v4.ata = def_v2.ata; - - } else if (this->offsets_v3) { - const auto& co = this->r.pget(this->offsets_v3->weapon_table + sizeof(ArrayRefBE) * data1_1); - if (data1_2 >= co.count) { - throw out_of_range("weapon ID out of range"); - } - const auto& def_v3 = this->r.pget>(co.offset + sizeof(WeaponV3) * data1_2); - def_v4.base.id = def_v3.base.id.load(); - def_v4.base.type = def_v3.base.type.load(); - def_v4.base.skin = def_v3.base.skin.load(); - def_v4.class_flags = def_v3.class_flags.load(); - def_v4.atp_min = def_v3.atp_min.load(); - def_v4.atp_max = def_v3.atp_max.load(); - def_v4.atp_required = def_v3.atp_required.load(); - def_v4.mst_required = def_v3.mst_required.load(); - def_v4.ata_required = def_v3.ata_required.load(); - def_v4.mst = def_v3.mst.load(); - def_v4.max_grind = def_v3.max_grind; - def_v4.photon = def_v3.photon; - def_v4.special = def_v3.special; - def_v4.ata = def_v3.ata; - def_v4.stat_boost = def_v3.stat_boost; - def_v4.projectile = def_v3.projectile; - def_v4.trail1_x = def_v3.trail1_x; - def_v4.trail1_y = def_v3.trail1_y; - def_v4.trail2_x = def_v3.trail2_x; - def_v4.trail2_y = def_v3.trail2_y; - def_v4.color = def_v3.color; - def_v4.unknown_a1 = def_v3.unknown_a1; - def_v4.unknown_a2 = def_v3.unknown_a2; - def_v4.unknown_a3 = def_v3.unknown_a3; - def_v4.unknown_a4 = def_v3.unknown_a4; - def_v4.unknown_a5 = def_v3.unknown_a5; - def_v4.tech_boost = def_v3.tech_boost; - def_v4.combo_type = def_v3.combo_type; - + 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, false>(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, true>(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) { - const auto& co = this->r.pget(this->offsets_v4->armor_table + sizeof(ArrayRefLE) * (data1_1 - 1)); - if (data1_2 >= co.count) { - throw out_of_range("armor/shield ID out of range"); - } - return this->r.pget(co.offset + sizeof(ArmorOrShieldV4) * data1_2); + 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; @@ -153,60 +523,19 @@ const ItemParameterTable::ArmorOrShieldV4& ItemParameterTable::get_armor_or_shie } catch (const std::out_of_range&) { ArmorOrShieldV4 def_v4; - if (this->offsets_v2) { - const auto& co = this->r.pget(this->offsets_v2->armor_table + sizeof(ArrayRefLE) * (data1_1 - 1)); - if (data1_2 >= co.count) { - throw out_of_range("armor/shield ID out of range"); - } - const auto& def_v2 = this->r.pget(co.offset + sizeof(ArmorOrShieldV2) * data1_2); - def_v4.base.id = def_v2.base.id; - def_v4.dfp = def_v2.dfp; - def_v4.evp = def_v2.evp; - def_v4.block_particle = def_v2.block_particle; - def_v4.block_effect = def_v2.block_effect; - def_v4.class_flags = def_v2.class_flags; - def_v4.required_level = def_v2.required_level; - def_v4.efr = def_v2.efr; - def_v4.eth = def_v2.eth; - def_v4.eic = def_v2.eic; - def_v4.edk = def_v2.edk; - def_v4.elt = def_v2.elt; - def_v4.dfp_range = def_v2.dfp_range; - def_v4.evp_range = def_v2.evp_range; - def_v4.stat_boost = def_v2.stat_boost; - def_v4.tech_boost = def_v2.tech_boost; - def_v4.unknown_a2 = def_v2.unknown_a2; - - } else if (this->offsets_v3) { - const auto& co = this->r.pget(this->offsets_v3->armor_table + sizeof(ArrayRefBE) * (data1_1 - 1)); - if (data1_2 >= co.count) { - throw out_of_range("armor/shield ID out of range"); - } - const auto& def_v3 = this->r.pget(co.offset + sizeof(ArmorOrShieldV3) * data1_2); - def_v4.base.id = def_v3.base.id.load(); - def_v4.base.type = def_v3.base.type.load(); - def_v4.base.skin = def_v3.base.skin.load(); - def_v4.dfp = def_v3.dfp.load(); - def_v4.evp = def_v3.evp.load(); - def_v4.block_particle = def_v3.block_particle; - def_v4.block_effect = def_v3.block_effect; - def_v4.class_flags = def_v3.class_flags.load(); - def_v4.required_level = def_v3.required_level; - def_v4.efr = def_v3.efr; - def_v4.eth = def_v3.eth; - def_v4.eic = def_v3.eic; - def_v4.edk = def_v3.edk; - def_v4.elt = def_v3.elt; - def_v4.dfp_range = def_v3.dfp_range; - def_v4.evp_range = def_v3.evp_range; - def_v4.stat_boost = def_v3.stat_boost; - def_v4.tech_boost = def_v3.tech_boost; - def_v4.unknown_a2 = def_v3.unknown_a2.load(); - + 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, true>(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, false>(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, true>(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"); } - if (data1_2 >= parsed_vec.size()) { parsed_vec.resize(data1_2 + 1); } @@ -215,13 +544,27 @@ const ItemParameterTable::ArmorOrShieldV4& ItemParameterTable::get_armor_or_shie } } +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) { - const auto& co = this->r.pget(this->offsets_v4->unit_table); - if (data1_2 >= co.count) { - throw out_of_range("unit ID out of range"); - } - return this->r.pget(co.offset + sizeof(UnitV4) * data1_2); + return indirect_lookup_2d(this->r, this->offsets_v4->unit_table, 0, data1_2); } try { @@ -232,35 +575,19 @@ const ItemParameterTable::UnitV4& ItemParameterTable::get_unit(uint8_t data1_2) return ret; } catch (const std::out_of_range&) { UnitV4 def_v4; - - if (this->offsets_v2) { - const auto& co = this->r.pget(this->offsets_v2->unit_table); - if (data1_2 >= co.count) { - throw out_of_range("unit ID out of range"); - } - const auto& def_v2 = this->r.pget(co.offset + sizeof(UnitV2) * data1_2); - def_v4.base.id = def_v2.base.id; - def_v4.stat = def_v2.stat; - def_v4.stat_amount = def_v2.stat_amount; - def_v4.modifier_amount = def_v2.modifier_amount; - - } else if (this->offsets_v3) { - const auto& co = this->r.pget(this->offsets_v3->unit_table); - if (data1_2 >= co.count) { - throw out_of_range("unit ID out of range"); - } - const auto& def_v3 = this->r.pget(co.offset + sizeof(UnitV3) * data1_2); - def_v4.base.id = def_v3.base.id.load(); - def_v4.base.type = def_v3.base.type.load(); - def_v4.base.skin = def_v3.base.skin.load(); - def_v4.stat = def_v3.stat.load(); - def_v4.stat_amount = def_v3.stat_amount.load(); - def_v4.modifier_amount = def_v3.modifier_amount.load(); - + 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, true>(this->r, this->offsets_gc_nte->unit_table, 0, data1_2).to_v4(); + } else if (this->offsets_v3_le) { + def_v4 = indirect_lookup_2d, false>(this->r, this->offsets_v3_le->unit_table, 0, data1_2).to_v4(); + } else if (this->offsets_v3_be) { + def_v4 = indirect_lookup_2d, true>(this->r, this->offsets_v3_be->unit_table, 0, data1_2).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); } @@ -269,13 +596,27 @@ const ItemParameterTable::UnitV4& ItemParameterTable::get_unit(uint8_t 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) { - const auto& co = this->r.pget(this->offsets_v4->mag_table); - if (data1_1 >= co.count) { - throw out_of_range("mag ID out of range"); - } - return this->r.pget(co.offset + sizeof(MagV4) * data1_1); + return indirect_lookup_2d(this->r, this->offsets_v4->mag_table, 0, data1_1); } try { @@ -286,53 +627,23 @@ 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_v2) { - const auto& co = this->r.pget(this->offsets_v2->mag_table); - if (data1_1 >= co.count) { - throw out_of_range("mag ID out of range"); + 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(); + } else { + def_v4 = indirect_lookup_2d(this->r, this->offsets_v1_v2->mag_table, 0, data1_1).to_v4(); } - const auto& def_v2 = this->r.pget(co.offset + sizeof(MagV2) * data1_1); - def_v4.base.id = def_v2.base.id; - def_v4.feed_table = def_v2.feed_table; - def_v4.photon_blast = def_v2.photon_blast; - def_v4.activation = def_v2.activation; - def_v4.on_pb_full = def_v2.on_pb_full; - def_v4.on_low_hp = def_v2.on_low_hp; - def_v4.on_death = def_v2.on_death; - def_v4.on_boss = def_v2.on_boss; - def_v4.on_pb_full_flag = def_v2.on_pb_full_flag; - def_v4.on_low_hp_flag = def_v2.on_low_hp_flag; - def_v4.on_death_flag = def_v2.on_death_flag; - def_v4.on_boss_flag = def_v2.on_boss_flag; - def_v4.class_flags = def_v2.class_flags; - - } else if (this->offsets_v3) { - const auto& co = this->r.pget(this->offsets_v3->mag_table); - if (data1_1 >= co.count) { - throw out_of_range("mag ID out of range"); - } - const auto& def_v3 = this->r.pget(co.offset + sizeof(MagV3) * data1_1); - def_v4.base.id = def_v3.base.id.load(); - def_v4.base.type = def_v3.base.type.load(); - def_v4.base.skin = def_v3.base.skin.load(); - def_v4.feed_table = def_v3.feed_table.load(); - def_v4.photon_blast = def_v3.photon_blast; - def_v4.activation = def_v3.activation; - def_v4.on_pb_full = def_v3.on_pb_full; - def_v4.on_low_hp = def_v3.on_low_hp; - def_v4.on_death = def_v3.on_death; - def_v4.on_boss = def_v3.on_boss; - def_v4.on_pb_full_flag = def_v3.on_pb_full_flag; - def_v4.on_low_hp_flag = def_v3.on_low_hp_flag; - def_v4.on_death_flag = def_v3.on_death_flag; - def_v4.on_boss_flag = def_v3.on_boss_flag; - def_v4.class_flags = def_v3.class_flags.load(); - + } else if (this->offsets_gc_nte) { + def_v4 = indirect_lookup_2d, true>(this->r, this->offsets_gc_nte->mag_table, 0, data1_1).to_v4(); + } else if (this->offsets_v3_le) { + def_v4 = indirect_lookup_2d, false>(this->r, this->offsets_v3_le->mag_table, 0, data1_1).to_v4(); + } else if (this->offsets_v3_be) { + def_v4 = indirect_lookup_2d, true>(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); } @@ -341,17 +652,34 @@ const ItemParameterTable::MagV4& ItemParameterTable::get_mag(uint8_t data1_1) co } } +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) { - const auto& co = this->r.pget(this->offsets_v4->tool_table + sizeof(ArrayRefLE) * data1_1); - if (data1_2 >= co.count) { - throw out_of_range("tool ID out of range"); - } - return this->r.pget(co.offset + sizeof(ToolV4) * data1_2); + return indirect_lookup_2d(this->r, this->offsets_v4->tool_table, data1_1, data1_2); } uint16_t key = (data1_1 << 8) | data1_2; @@ -360,32 +688,16 @@ const ItemParameterTable::ToolV4& ItemParameterTable::get_tool(uint8_t data1_1, } catch (const std::out_of_range&) { ToolV4 def_v4; - if (this->offsets_v2) { - const auto& co = this->r.pget(this->offsets_v2->tool_table + sizeof(ArrayRefLE) * data1_1); - if (data1_2 >= co.count) { - throw out_of_range("tool ID out of range"); - } - const auto& def_v2 = this->r.pget(co.offset + sizeof(ToolV2) * data1_2); - def_v4.base.id = def_v2.base.id; - def_v4.amount = def_v2.amount; - def_v4.tech = def_v2.tech; - def_v4.cost = def_v2.cost; - def_v4.item_flag = def_v2.item_flag; - - } else if (this->offsets_v3) { - const auto& co = this->r.pget(this->offsets_v3->tool_table + sizeof(ArrayRefBE) * data1_1); - if (data1_2 >= co.count) { - throw out_of_range("tool ID out of range"); - } - const auto& def_v3 = this->r.pget(co.offset + sizeof(ToolV3) * data1_2); - def_v4.base.id = def_v3.base.id.load(); - def_v4.base.type = def_v3.base.type.load(); - def_v4.base.skin = def_v3.base.skin.load(); - def_v4.amount = def_v3.amount.load(); - def_v4.tech = def_v3.tech.load(); - def_v4.cost = def_v3.cost.load(); - def_v4.item_flag = def_v3.item_flag.load(); - + 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, true>(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, false>(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, true>(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"); } @@ -411,10 +723,16 @@ pair ItemParameterTable::find_tool_by_id_t(uint32_t tool_table } pair ItemParameterTable::find_tool_by_id(uint32_t item_id) const { - if (this->offsets_v2) { - return this->find_tool_by_id_t(this->offsets_v2->tool_table, item_id); - } else if (this->offsets_v3) { - return this->find_tool_by_id_t(this->offsets_v3->tool_table, item_id); + 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, true>(this->offsets_gc_nte->tool_table, item_id); + } else if (this->offsets_v3_le) { + return this->find_tool_by_id_t, false>(this->offsets_v3_le->tool_table, item_id); + } else if (this->offsets_v3_be) { + return this->find_tool_by_id_t, true>(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 { @@ -422,9 +740,8 @@ pair ItemParameterTable::find_tool_by_id(uint32_t item_id) con } } -template -float ItemParameterTable::get_sale_divisor_t( - uint32_t weapon_table_offset, uint32_t non_weapon_table_offset, uint8_t data1_0, uint8_t data1_1) const { +template +float ItemParameterTable::get_sale_divisor_t(const OffsetsT* offsets, uint8_t data1_0, uint8_t data1_1) const { using FloatT = typename std::conditional::type; switch (data1_0) { @@ -432,10 +749,10 @@ float ItemParameterTable::get_sale_divisor_t( if (data1_1 >= this->num_weapon_classes) { return 0.0f; } - return this->r.pget(weapon_table_offset + data1_1 * sizeof(FloatT)); + return this->r.pget(offsets->weapon_sale_divisor_table + data1_1 * sizeof(FloatT)); case 1: { - const auto& divisors = this->r.pget>(non_weapon_table_offset); + const auto& divisors = this->r.pget>(offsets->sale_divisor_table); switch (data1_1) { case 1: return divisors.armor_divisor; @@ -448,7 +765,7 @@ float ItemParameterTable::get_sale_divisor_t( } case 2: { - const auto& divisors = this->r.pget>(non_weapon_table_offset); + const auto& divisors = this->r.pget>(offsets->sale_divisor_table); return divisors.mag_divisor; } @@ -458,15 +775,18 @@ float ItemParameterTable::get_sale_divisor_t( } float ItemParameterTable::get_sale_divisor(uint8_t data1_0, uint8_t data1_1) const { - if (this->offsets_v2) { - return this->get_sale_divisor_t( - this->offsets_v2->weapon_sale_divisor_table, this->offsets_v2->sale_divisor_table, data1_0, data1_1); - } else if (this->offsets_v3) { - return this->get_sale_divisor_t( - this->offsets_v3->weapon_sale_divisor_table, this->offsets_v3->sale_divisor_table, data1_0, data1_1); + 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->weapon_sale_divisor_table, this->offsets_v4->sale_divisor_table, data1_0, data1_1); + return this->get_sale_divisor_t(this->offsets_v4, data1_0, data1_1); } else { throw logic_error("table is not v2, v3, or v4"); } @@ -482,11 +802,20 @@ const ItemParameterTable::MagFeedResult& ItemParameterTable::get_mag_feed_result } uint32_t offset; - if (this->offsets_v2) { - const auto& table_offsets = this->r.pget>(this->offsets_v2->mag_feed_table); + 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_v3) { - const auto& table_offsets = this->r.pget>(this->offsets_v3->mag_feed_table); + } 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); @@ -500,10 +829,16 @@ const ItemParameterTable::MagFeedResult& ItemParameterTable::get_mag_feed_result uint8_t ItemParameterTable::get_item_stars(uint32_t item_id) const { uint32_t base_offset; - if (this->offsets_v2) { - base_offset = this->offsets_v2->star_value_table; - } else if (this->offsets_v3) { - base_offset = this->offsets_v3->star_value_table; + 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 { @@ -515,9 +850,9 @@ uint8_t ItemParameterTable::get_item_stars(uint32_t item_id) const { : 0; } -uint8_t ItemParameterTable::get_special_stars(uint8_t det) const { - return ((det & 0x3F) && !(det & 0x80)) - ? this->get_item_stars(det + this->special_stars_begin_index) +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; } @@ -527,14 +862,28 @@ const ItemParameterTable::Special& ItemParameterTable::get_special(uint8_ throw out_of_range("invalid special index"); } - if (this->offsets_v2) { - return this->r.pget>(this->offsets_v2->special_data_table + sizeof(Special) * special); - } else if (this->offsets_v3) { + 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_v3->special_data_table + sizeof(Special) * special); + const auto& sp_be = this->r.pget>(this->offsets_gc_nte->special_data_table + sizeof(Special) * special); + this->parsed_specials[special].type = sp_be.type.load(); + this->parsed_specials[special].amount = sp_be.amount.load(); + } + 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(Special) * special); this->parsed_specials[special].type = sp_be.type.load(); this->parsed_specials[special].amount = sp_be.amount.load(); } @@ -554,14 +903,18 @@ uint8_t ItemParameterTable::get_max_tech_level(uint8_t char_class, uint8_t tech_ throw out_of_range("invalid technique number"); } - if (this->offsets_v2) { + 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_v3) { - return r.pget_u8(this->offsets_v3->max_tech_level_table + tech_num * 12 + char_class); + } 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 { @@ -571,10 +924,16 @@ uint8_t ItemParameterTable::get_max_tech_level(uint8_t char_class, uint8_t tech_ uint8_t ItemParameterTable::get_weapon_v1_replacement(uint8_t data1_1) const { uint32_t offset; - if (this->offsets_v2) { - offset = this->offsets_v2->v1_replacement_table; - } else if (this->offsets_v3) { - offset = this->offsets_v3->v1_replacement_table; + 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 { @@ -682,12 +1041,16 @@ bool ItemParameterTable::is_item_rare(const ItemData& item) const { return (this->get_item_base_stars(item) >= 9); } -bool ItemParameterTable::is_unsealable_item(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_v2) { + if (this->offsets_dc_protos || this->offsets_v1_v2 || this->offsets_gc_nte) { return false; - } else if (this->offsets_v3) { - const auto& co = this->r.pget(this->offsets_v3->unsealable_table); + } 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) { @@ -700,15 +1063,19 @@ bool ItemParameterTable::is_unsealable_item(const ItemData& item) const { const auto* defs = &this->r.pget(offset, count * sizeof(UnsealableItem)); for (size_t z = 0; z < count; z++) { - if ((defs[z].item[0] == item.data1[0]) && - (defs[z].item[1] == item.data1[1]) && - (defs[z].item[2] == item.data1[2])) { + 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]); +} + const ItemParameterTable::ItemCombination& ItemParameterTable::get_item_combination( const ItemData& used_item, const ItemData& equipped_item) const { for (const auto& def : this->get_all_combinations_for_used_item(used_item)) { @@ -735,11 +1102,15 @@ 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_v2) { + 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) { - const auto& co = this->r.pget(this->offsets_v3->combination_table); + } 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) { @@ -760,6 +1131,25 @@ const std::map>& Item 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 { @@ -773,10 +1163,12 @@ std::pair ItemParameterTable::get_ } std::pair ItemParameterTable::get_event_items(uint8_t event_number) const { - if (this->offsets_v2) { + if (this->offsets_dc_protos || this->offsets_v1_v2 || this->offsets_gc_nte) { return make_pair(nullptr, 0); - } else if (this->offsets_v3) { - return this->get_event_items_t(this->offsets_v3->unwrap_table, event_number); + } 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 { diff --git a/src/ItemParameterTable.hh b/src/ItemParameterTable.hh index 68ada94d..cdf465b3 100644 --- a/src/ItemParameterTable.hh +++ b/src/ItemParameterTable.hh @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -13,11 +14,16 @@ 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. + template struct ArrayRef { using U32T = typename std::conditional::type; - U32T count; - U32T offset; + /* 00 */ U32T count; + /* 04 */ U32T offset; + /* 08 */ } __attribute__((packed)); struct ArrayRefLE : ArrayRef { } __attribute__((packed)); @@ -29,152 +35,240 @@ public: using U32T = typename std::conditional::type; // id specifies several things; notably, it doubles as the index of the // item's name in the text archive (e.g. TextEnglish) collection 0. - U32T id = 0xFFFFFFFF; + /* 00 */ U32T id = 0xFFFFFFFF; + /* 04 */ } __attribute__((packed)); template struct ItemBaseV3 : ItemBaseV2 { using U16T = typename std::conditional::type; - U16T type = 0; - U16T skin = 0; + /* 04 */ U16T type = 0; + /* 06 */ U16T skin = 0; + /* 08 */ } __attribute__((packed)); template struct ItemBaseV4 : ItemBaseV3 { using U32T = typename std::conditional::type; - U32T team_points = 0; + /* 08 */ U32T team_points = 0; + /* 0C */ } __attribute__((packed)); - struct WeaponV2 { - ItemBaseV2 base; - le_uint16_t class_flags = 0; - le_uint16_t atp_min = 0; - le_uint16_t atp_max = 0; - le_uint16_t atp_required = 0; - le_uint16_t mst_required = 0; - le_uint16_t ata_required = 0; - uint8_t max_grind = 0; - uint8_t photon = 0; - uint8_t special = 0; - uint8_t ata = 0; - uint8_t stat_boost = 0; // TODO: This could be larger (16 or 32 bits) - parray unknown_a9; + struct WeaponV4; + struct WeaponDCProtos { + /* 00 */ ItemBaseV2 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; + } __attribute__((packed)); + + struct WeaponV1V2 { + /* 00 */ ItemBaseV2 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 = 0; // TODO: This could be larger (16 or 32 bits) + /* 15 */ parray unknown_a9; + /* 18 */ + + WeaponV4 to_v4() const; + } __attribute__((packed)); + + struct WeaponGCNTE { + /* 00 */ ItemBaseV3 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 = 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 */ uint8_t unknown_a1 = 0; + /* 22 */ uint8_t unknown_a2 = 0; + /* 23 */ uint8_t unknown_a3 = 0; + /* 24 */ + + WeaponV4 to_v4() const; } __attribute__((packed)); template struct WeaponV3 { using U16T = typename std::conditional::type; - ItemBaseV3 base; - U16T class_flags = 0; - U16T atp_min = 0; - U16T atp_max = 0; - U16T atp_required = 0; - U16T mst_required = 0; - U16T ata_required = 0; - U16T mst = 0; - uint8_t max_grind = 0; - uint8_t photon = 0; - uint8_t special = 0; - uint8_t ata = 0; - uint8_t stat_boost = 0; - 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; - uint8_t unknown_a1 = 0; - uint8_t unknown_a2 = 0; - uint8_t unknown_a3 = 0; - uint8_t unknown_a4 = 0; - uint8_t unknown_a5 = 0; - uint8_t tech_boost = 0; - uint8_t combo_type = 0; + /* 00 */ ItemBaseV3 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 = 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 */ uint8_t unknown_a1 = 0; + /* 22 */ uint8_t unknown_a2 = 0; + /* 23 */ uint8_t unknown_a3 = 0; + /* 24 */ uint8_t unknown_a4 = 0; + /* 25 */ uint8_t unknown_a5 = 0; + /* 26 */ uint8_t tech_boost = 0; + /* 27 */ uint8_t combo_type = 0; + /* 28 */ + + WeaponV4 to_v4() const; } __attribute__((packed)); struct WeaponV4 { - ItemBaseV4 base; - le_uint16_t class_flags = 0x00FF; - le_uint16_t atp_min = 0; - le_uint16_t atp_max = 0; - le_uint16_t atp_required = 0; - le_uint16_t mst_required = 0; - le_uint16_t ata_required = 0; - le_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 = 0; - 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; - uint8_t unknown_a1 = 0; - uint8_t unknown_a2 = 0; - uint8_t unknown_a3 = 0; - uint8_t unknown_a4 = 0; - uint8_t unknown_a5 = 0; - uint8_t tech_boost = 0; - uint8_t combo_type = 0; + /* 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 = 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 */ uint8_t unknown_a1 = 0; + /* 26 */ uint8_t unknown_a2 = 0; + /* 27 */ uint8_t unknown_a3 = 0; + /* 28 */ uint8_t unknown_a4 = 0; + /* 29 */ uint8_t unknown_a5 = 0; + /* 2A */ uint8_t tech_boost = 0; + /* 2B */ uint8_t combo_type = 0; + /* 2C */ } __attribute__((packed)); template struct ArmorOrShield { using U16T = typename std::conditional::type; - BaseT base; - U16T dfp = 0; - U16T evp = 0; - uint8_t block_particle = 0; - uint8_t block_effect = 0; - U16T 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 = 0; - uint8_t tech_boost = 0; - U16T unknown_a2 = 0; + /* 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)); - struct ArmorOrShieldV2 : ArmorOrShield, false> { + + struct ArmorOrShieldV4; + struct ArmorOrShieldDCProtos : ArmorOrShield, false> { + ArmorOrShieldV4 to_v4() const; } __attribute__((packed)); - struct ArmorOrShieldV3 : ArmorOrShield, true> { + template + struct ArmorOrShieldFinal : ArmorOrShield { + using U16T = typename std::conditional::type; + /* 14 */ uint8_t stat_boost = 0; + /* 15 */ uint8_t tech_boost = 0; + /* 16 */ U16T unknown_a2 = 0; + /* 18 */ } __attribute__((packed)); - struct ArmorOrShieldV4 : ArmorOrShield, false> { + + struct ArmorOrShieldV1V2 : ArmorOrShieldFinal, false> { + ArmorOrShieldV4 to_v4() const; + } __attribute__((packed)); + template + struct ArmorOrShieldV3 : ArmorOrShieldFinal, IsBigEndian> { + ArmorOrShieldV4 to_v4() const; + } __attribute__((packed)); + struct ArmorOrShieldV4 : ArmorOrShieldFinal, false> { } __attribute__((packed)); template struct Unit { using U16T = typename std::conditional::type; using S16T = typename std::conditional::type; - BaseT base; - U16T stat = 0; - U16T stat_amount = 0; - S16T modifier_amount = 0; - parray unused; + /* V1/V2 offsets */ + /* 00 */ BaseT base; + /* 04 */ U16T stat = 0; + /* 06 */ U16T stat_amount = 0; + /* 08 */ } __attribute__((packed)); - struct UnitV2 : Unit, false> { + + struct UnitV4; + struct UnitDCProtos : Unit, false> { + UnitV4 to_v4() const; } __attribute__((packed)); - struct UnitV3 : Unit, true> { + template + struct UnitFinal : Unit { + using S16T = typename std::conditional::type; + /* 08 */ S16T modifier_amount = 0; + /* 0A */ parray unused; + /* 0C */ } __attribute__((packed)); - struct UnitV4 : Unit, false> { + struct UnitV1V2 : UnitFinal, false> { + UnitV4 to_v4() const; + } __attribute__((packed)); + template + struct UnitV3 : UnitFinal, IsBigEndian> { + UnitV4 to_v4() const; + } __attribute__((packed)); + struct UnitV4 : UnitFinal, false> { } __attribute__((packed)); template struct Mag { using U16T = typename std::conditional::type; - BaseT base; - U16T 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; + /* 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; // 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 @@ -190,18 +284,37 @@ public: // flag == 3 => activation - 10 // flag == 4 => step_synchro - 10 // anything else => 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; - U16T class_flags = 0x00FF; - parray unused; + /* 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)); + + struct MagV4; + struct MagV1 : Mag, false> { + MagV4 to_v4() const; } __attribute__((packed)); struct MagV2 : Mag, false> { + /* 10 */ le_uint16_t class_flags = 0x00FF; + /* 12 */ parray unused; + /* 14 */ + + MagV4 to_v4() const; } __attribute__((packed)); - struct MagV3 : Mag, true> { + template + struct MagV3 : Mag, IsBigEndian> { + using U16T = typename std::conditional::type; + /* 10 */ U16T class_flags = 0x00FF; + /* 12 */ parray unused; + /* 14 */ + + MagV4 to_v4() const; } __attribute__((packed)); struct MagV4 : Mag, false> { + /* 10 */ le_uint16_t class_flags = 0x00FF; + /* 12 */ parray unused; + /* 14 */ } __attribute__((packed)); template @@ -209,15 +322,22 @@ public: using U16T = typename std::conditional::type; using S32T = typename std::conditional::type; using U32T = typename std::conditional::type; - BaseT base; - U16T amount = 0; - U16T tech = 0; - S32T cost = 0; - U32T item_flag = 0; + /* V1/V2 offsets */ + /* 00 */ BaseT base; + /* 04 */ U16T amount = 0; + /* 06 */ U16T tech = 0; + /* 08 */ S32T cost = 0; + /* 0C */ U32T item_flag = 0; + /* 10 */ } __attribute__((packed)); - struct ToolV2 : Tool, false> { + + struct ToolV4; + struct ToolV1V2 : Tool, false> { + ToolV4 to_v4() const; } __attribute__((packed)); - struct ToolV3 : Tool, true> { + template + struct ToolV3 : Tool, IsBigEndian> { + ToolV4 to_v4() const; } __attribute__((packed)); struct ToolV4 : Tool, false> { } __attribute__((packed)); @@ -301,25 +421,28 @@ public: FloatT mag_divisor = 0.0f; } __attribute__((packed)); - enum class Version { - V2, - V3, - V4, - }; - ItemParameterTable(std::shared_ptr data, Version version); ~ItemParameterTable() = default; + void print(FILE* stream) const; + 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; + 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 det) const; + uint8_t get_special_stars(uint8_t special) const; const Special& get_special(uint8_t special) 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; @@ -329,10 +452,12 @@ public: uint8_t get_item_base_stars(const ItemData& item) const; uint8_t get_item_adjusted_stars(const ItemData& item) 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; 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; size_t price_for_item(const ItemData& item) const; @@ -344,10 +469,32 @@ public: size_t special_stars_begin_index; size_t num_specials; size_t first_rare_mag_index; - size_t star_value_table_size; private: - struct TableOffsetsV2 { + struct TableOffsetsDCProtos { + /* 00 */ le_uint32_t unknown_a0; + /* 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 v1_replacement_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 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_data_table; + /* 3C */ le_uint32_t stat_boost_table; + /* 40 */ le_uint32_t shield_effect_table; + /* 44 */ le_uint32_t unknown_a2; + /* 48 */ le_uint32_t unknown_a3; + /* 4C */ le_uint32_t unknown_a4; + } __attribute__((packed)); + + struct TableOffsetsV1V2 { // TODO: Is weapon count 0x89 or 0x8A? It could be that the last entry in // weapon_table is used for ???? items. /* 00 / 0013 */ le_uint32_t unknown_a0; @@ -363,14 +510,34 @@ private: /* 28 / 40A8 */ le_uint32_t sale_divisor_table; // -> NonWeaponSaleDivisors /* 2C / 5F4C */ le_uint32_t mag_feed_table; // -> MagFeedResultsTable /* 30 / 4378 */ le_uint32_t star_value_table; // -> [uint8_t](0x1C7) - /* 34 / 4540 */ le_uint32_t special_data_table; // -> [Special](0x29) - /* 38 / 45E4 */ le_uint32_t weapon_effect_table; // -> [16-byte structs] + /* 34 / 45E4 */ le_uint32_t unknown_a1; + /* 38 / 4540 */ le_uint32_t special_data_table; // -> [Special](0x29) /* 3C / 58DC */ le_uint32_t stat_boost_table; // -> [StatBoost] /* 40 / 5704 */ le_uint32_t shield_effect_table; // -> [8-byte structs] } __attribute__((packed)); - // TODO: The GC NTE ItemPMT format is intermediate between V2 and V3 - the - // Offsets struct is 0x50 bytes. Figure it out and add support here. + 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; // -> [TechniqueBoost] (always 0x2C of them? from counts struct?) + } __attribute__((packed)); template struct TableOffsetsV3V4 { @@ -401,10 +568,14 @@ private: /* 58 / F600 / 15024 */ U32T ranged_special_table; // -> {count, offset -> [4-byte structs]} } __attribute__((packed)); + Version version; std::shared_ptr data; StringReader r; - const TableOffsetsV2* offsets_v2; - const TableOffsetsV3V4* offsets_v3; + const TableOffsetsDCProtos* offsets_dc_protos; + const TableOffsetsV1V2* offsets_v1_v2; + const TableOffsetsGCNTE* offsets_gc_nte; + const TableOffsetsV3V4* offsets_v3_le; + const TableOffsetsV3V4* offsets_v3_be; const TableOffsetsV3V4* offsets_v4; // These are unused if offsets_v4 is not null (in that case, we just return @@ -423,8 +594,10 @@ private: 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 - float get_sale_divisor_t(uint32_t weapon_table_offset, uint32_t non_weapon_table_offset, uint8_t data1_0, uint8_t data1_1) const; + 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; }; diff --git a/src/Main.cc b/src/Main.cc index b3bdc330..7681b705 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -1402,27 +1402,27 @@ Action a_describe_item( item.data1[8], item.data1[9], item.data1[10], item.data1[11], item.data2[0], item.data2[1], item.data2[2], item.data2[3]); - ItemData item_v1 = item; - item_v1.encode_for_version(Version::PC_V2, s.item_parameter_table(Version::PC_V2)); - ItemData item_v1_decoded = item_v1; - item_v1_decoded.decode_for_version(Version::PC_V2); + ItemData item_v2 = item; + item_v2.encode_for_version(Version::PC_V2, s.item_parameter_table_for_encode(Version::PC_V2)); + ItemData item_v2_decoded = item_v2; + item_v2_decoded.decode_for_version(Version::PC_V2); - log_info("Data (V1-encoded): %02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX -------- %02hhX%02hhX%02hhX%02hhX", - item_v1.data1[0], item_v1.data1[1], item_v1.data1[2], item_v1.data1[3], - item_v1.data1[4], item_v1.data1[5], item_v1.data1[6], item_v1.data1[7], - item_v1.data1[8], item_v1.data1[9], item_v1.data1[10], item_v1.data1[11], - item_v1.data2[0], item_v1.data2[1], item_v1.data2[2], item_v1.data2[3]); - if (item_v1_decoded != item) { - log_warning("V1-decoded data does not match original data"); - log_warning("Data (V1-decoded): %02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX -------- %02hhX%02hhX%02hhX%02hhX", - item_v1_decoded.data1[0], item_v1_decoded.data1[1], item_v1_decoded.data1[2], item_v1_decoded.data1[3], - item_v1_decoded.data1[4], item_v1_decoded.data1[5], item_v1_decoded.data1[6], item_v1_decoded.data1[7], - item_v1_decoded.data1[8], item_v1_decoded.data1[9], item_v1_decoded.data1[10], item_v1_decoded.data1[11], - item_v1_decoded.data2[0], item_v1_decoded.data2[1], item_v1_decoded.data2[2], item_v1_decoded.data2[3]); + log_info("Data (V2-encoded): %02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX -------- %02hhX%02hhX%02hhX%02hhX", + item_v2.data1[0], item_v2.data1[1], item_v2.data1[2], item_v2.data1[3], + item_v2.data1[4], item_v2.data1[5], item_v2.data1[6], item_v2.data1[7], + item_v2.data1[8], item_v2.data1[9], item_v2.data1[10], item_v2.data1[11], + item_v2.data2[0], item_v2.data2[1], item_v2.data2[2], item_v2.data2[3]); + if (item_v2_decoded != item) { + log_warning("V2-decoded data does not match original data"); + log_warning("Data (V2-decoded): %02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX %02hhX%02hhX%02hhX%02hhX -------- %02hhX%02hhX%02hhX%02hhX", + item_v2_decoded.data1[0], item_v2_decoded.data1[1], item_v2_decoded.data1[2], item_v2_decoded.data1[3], + item_v2_decoded.data1[4], item_v2_decoded.data1[5], item_v2_decoded.data1[6], item_v2_decoded.data1[7], + item_v2_decoded.data1[8], item_v2_decoded.data1[9], item_v2_decoded.data1[10], item_v2_decoded.data1[11], + item_v2_decoded.data2[0], item_v2_decoded.data2[1], item_v2_decoded.data2[2], item_v2_decoded.data2[3]); } ItemData item_gc = item; - item_gc.encode_for_version(Version::GC_V3, s.item_parameter_table(Version::GC_V3)); + item_gc.encode_for_version(Version::GC_V3, s.item_parameter_table_for_encode(Version::GC_V3)); ItemData item_gc_decoded = item_gc; item_gc_decoded.decode_for_version(Version::GC_V3); @@ -1461,12 +1461,12 @@ Action a_name_all_items( } } - fprintf(stderr, "IDENT :"); + fprintf(stderr, "IDENT :"); for (size_t v_s = 0; v_s < NUM_VERSIONS; v_s++) { Version version = static_cast(v_s); const auto& index = s.item_name_indexes.at(v_s); if (index) { - fprintf(stderr, " %30s", name_for_enum(version)); + fprintf(stderr, " %30s ", name_for_enum(version)); } } fputc('\n', stderr); @@ -1492,6 +1492,20 @@ Action a_name_all_items( } }); +Action a_print_item_parameter_tables( + "print-item-tables", nullptr, +[](Arguments&) { + ServerState s; + s.load_objects_and_upstream_dependents("item_name_indexes"); + for (size_t v_s = 0; v_s < NUM_VERSIONS; v_s++) { + const auto& index = s.item_name_indexes.at(v_s); + if (index) { + Version v = static_cast(v_s); + fprintf(stdout, "======== %s\n", name_for_enum(v)); + index->print_table(stdout); + } + } + }); + Action a_show_ep3_cards( "show-ep3-cards", "\ show-ep3-cards\n\ diff --git a/src/PlayerSubordinates.cc b/src/PlayerSubordinates.cc index 32d9d783..a24e0564 100644 --- a/src/PlayerSubordinates.cc +++ b/src/PlayerSubordinates.cc @@ -659,7 +659,8 @@ void PlayerInventory::decode_from_client(shared_ptr c) { } void PlayerInventory::encode_for_client(shared_ptr c) { - if (c->version() == Version::DC_NTE) { + Version v = c->version(); + if (v == Version::DC_NTE) { // DC NTE has the item count as a 32-bit value here, whereas every other // version uses a single byte. To stop DC NTE from crashing by trying to // construct far more than 30 TItem objects, we clear the fields DC NTE @@ -668,7 +669,7 @@ void PlayerInventory::encode_for_client(shared_ptr c) { this->hp_from_materials = 0; this->tp_from_materials = 0; this->language = 0; - } else if ((c->version() != Version::PC_NTE) && (c->version() != Version::PC_V2)) { + } else if ((v != Version::PC_NTE) && (v != Version::PC_V2)) { if (this->language > 4) { this->language = 0; } @@ -678,9 +679,11 @@ void PlayerInventory::encode_for_client(shared_ptr c) { } } - auto item_parameter_table = c->require_server_state()->item_parameter_table(c->version()); + // For pre-V2 clients, use the V2 parameter table, since the V1 table doesn't + // have correct encodings for backward-compatible V2 items. + auto item_parameter_table = c->require_server_state()->item_parameter_table_for_encode(v); for (size_t z = 0; z < this->items.size(); z++) { - this->items[z].data.encode_for_version(c->version(), item_parameter_table); + this->items[z].data.encode_for_version(v, item_parameter_table); } } diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 0ae4ed48..b199e3ee 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -779,14 +779,16 @@ static void on_sync_joining_player_disp_and_inventory( // we need to synthesize a 6x71 command to tell the target all state has been // sent. (If both are pre-V1, the target won't expect this command; if both // are V1 or later, the leader will send this command itself.) - if (is_pre_v1(c->version()) && !is_pre_v1(target->version())) { + Version target_v = target->version(); + Version c_v = c->version(); + if (is_pre_v1(c_v) && !is_pre_v1(target_v)) { static const be_uint32_t data = 0x71010000; send_command(target, 0x62, target->lobby_client_id, &data, sizeof(data)); } unique_ptr parsed; - switch (c->version()) { + switch (c_v) { case Version::DC_NTE: parsed = make_unique( check_size_t(data, size), @@ -807,7 +809,7 @@ static void on_sync_joining_player_disp_and_inventory( parsed = make_unique( check_size_t(data, size), c->license->serial_number); - if (c->version() == Version::DC_V1) { + if (c_v == Version::DC_V1) { parsed->clear_v1_unused_item_fields(); } break; @@ -833,10 +835,10 @@ static void on_sync_joining_player_disp_and_inventory( throw logic_error("6x70 command from unknown game version"); } - parsed->transcode_inventory_items(c->version(), target->version(), s->item_parameter_table(target->version())); - parsed->visual.enforce_lobby_join_limits_for_version(target->version()); + parsed->transcode_inventory_items(c_v, target_v, s->item_parameter_table_for_encode(target_v)); + parsed->visual.enforce_lobby_join_limits_for_version(target_v); - switch (target->version()) { + switch (target_v) { case Version::DC_NTE: forward_subcommand_t(target, command, flag, parsed->as_dc_nte()); break; @@ -1410,7 +1412,7 @@ void forward_subcommand_with_item_transcode_t(shared_ptr c, uint8_t comm out_cmd.header.subcommand = translate_subcommand_number(lc->version(), c->version(), out_cmd.header.subcommand); if (out_cmd.header.subcommand) { out_cmd.item_data.decode_for_version(c->version()); - out_cmd.item_data.encode_for_version(lc->version(), s->item_parameter_table(lc->version())); + out_cmd.item_data.encode_for_version(lc->version(), s->item_parameter_table_for_encode(lc->version())); send_command_t(lc, command, flag, out_cmd); } else { lc->log.info("Subcommand cannot be translated to client\'s version"); @@ -1623,7 +1625,7 @@ static void on_box_or_enemy_item_drop_t(shared_ptr c, uint8_t command, u out_cmd.header.subcommand = translate_subcommand_number(lc->version(), c->version(), out_cmd.header.subcommand); if (out_cmd.header.subcommand) { out_cmd.item.item.decode_for_version(c->version()); - out_cmd.item.item.encode_for_version(lc->version(), s->item_parameter_table(lc->version())); + out_cmd.item.item.encode_for_version(lc->version(), s->item_parameter_table_for_encode(lc->version())); send_command_t(lc, command, flag, out_cmd); } else { lc->log.info("Subcommand cannot be translated to client\'s version"); @@ -2809,7 +2811,7 @@ void on_exchange_item_for_team_points_bb(shared_ptr c, uint8_t command, auto p = c->character(); auto item = p->remove_item(cmd.item_id, cmd.amount, c->version()); - size_t points = s->item_parameter_table_v4->get_item_team_points(item); + size_t points = s->item_parameter_table(Version::BB_V4)->get_item_team_points(item); s->team_index->add_member_points(c->license->serial_number, points); if (l->log.should_log(LogLevel::INFO)) { diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 2787c85e..8aa74124 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -2255,9 +2255,10 @@ void send_execute_item_trade(shared_ptr c, const vector& items } cmd.target_client_id = c->lobby_client_id; cmd.item_count = items.size(); + auto item_parameter_table = s->item_parameter_table_for_encode(c->version()); for (size_t x = 0; x < items.size(); x++) { cmd.item_datas[x] = items[x]; - cmd.item_datas[x].encode_for_version(c->version(), s->item_parameter_table(c->version())); + cmd.item_datas[x].encode_for_version(c->version(), item_parameter_table); } send_command_t(c, 0xD3, 0x00, cmd); } @@ -2433,7 +2434,7 @@ void send_game_item_state(shared_ptr c) { fi.unknown_a2 = 0; fi.drop_number = (floor == 0) ? 0xFFFF : (decompressed_header.next_drop_number_per_floor.at(floor - 1)++); fi.item = item->data; - fi.item.encode_for_version(c->version(), s->item_parameter_table(c->version())); + fi.item.encode_for_version(c->version(), s->item_parameter_table_for_encode(c->version())); floor_items_w.put(fi); decompressed_header.floor_item_count_per_floor.at(floor)++; @@ -2511,7 +2512,7 @@ void send_drop_item_to_channel(shared_ptr s, Channel& ch, const Ite uint8_t subcommand = get_pre_v1_subcommand(ch.version, 0x51, 0x58, 0x5F); G_DropItem_PC_V3_BB_6x5F cmd = { {{subcommand, 0x0B, 0x0000}, {floor, from_enemy, entity_id, x, z, 0, 0, item}}, 0}; - cmd.item.item.encode_for_version(ch.version, s->item_parameter_table(ch.version)); + cmd.item.item.encode_for_version(ch.version, s->item_parameter_table_for_encode(ch.version)); ch.send(0x60, 0x00, &cmd, sizeof(cmd)); } @@ -2530,7 +2531,7 @@ void send_drop_stacked_item_to_channel( shared_ptr s, Channel& ch, const ItemData& item, uint8_t floor, float x, float z) { uint8_t subcommand = get_pre_v1_subcommand(ch.version, 0x4F, 0x56, 0x5D); G_DropStackedItem_PC_V3_BB_6x5D cmd = {{{subcommand, 0x0A, 0x0000}, floor, 0, x, z, item}, 0}; - cmd.item_data.encode_for_version(ch.version, s->item_parameter_table(ch.version)); + cmd.item_data.encode_for_version(ch.version, s->item_parameter_table_for_encode(ch.version)); ch.send(0x60, 0x00, &cmd, sizeof(cmd)); } diff --git a/src/ServerState.cc b/src/ServerState.cc index 061f7ec1..ac48dd99 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -370,25 +370,19 @@ void ServerState::dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx) { } shared_ptr ServerState::item_parameter_table(Version version) const { - switch (version) { - case Version::DC_NTE: - case Version::DC_V1_11_2000_PROTOTYPE: - case Version::DC_V1: - case Version::DC_V2: - case Version::PC_NTE: - case Version::PC_V2: - return this->item_parameter_table_v2; - case Version::GC_NTE: - case Version::GC_V3: - case Version::GC_EP3_NTE: - case Version::GC_EP3: - case Version::XB_V3: - return this->item_parameter_table_v3; - case Version::BB_V4: - return this->item_parameter_table_v4; - default: - throw out_of_range("no item parameter table exists for this version"); + auto ret = this->item_parameter_tables.at(static_cast(version)); + if (ret == nullptr) { + throw runtime_error("no item parameter table exists for this version"); } + return ret; +} + +shared_ptr ServerState::item_parameter_table_for_encode(Version version) const { + return this->item_parameter_table(is_v1(version) ? Version::PC_V2 : version); +} + +void ServerState::set_item_parameter_table(Version version, shared_ptr table) { + this->item_parameter_tables.at(static_cast(version)) = table; } shared_ptr ServerState::item_name_index(Version version) const { @@ -1195,37 +1189,11 @@ shared_ptr ServerState::create_item_name_index_for_version( } void ServerState::load_item_name_indexes() { - config_log.info("Generating item name indexes"); - // TODO: Get ItemPMT files for the versions for which we don't have them - // (especially DC_V1) and add support for them. Currently we only have three - // ItemPMTs (PC, GC, and BB), so we can't use them to generate all the name - // indexes. - - auto pc_v2_index = create_item_name_index_for_version( - Version::PC_V2, this->item_parameter_table(Version::PC_V2), this->text_index); - this->set_item_name_index(Version::DC_NTE, pc_v2_index); - this->set_item_name_index(Version::DC_V1, pc_v2_index); - this->set_item_name_index(Version::DC_V2, pc_v2_index); - this->set_item_name_index(Version::PC_NTE, pc_v2_index); - this->set_item_name_index(Version::PC_V2, pc_v2_index); - - // All tools are stackable on 11/2000, so make a separate index (still using - // V2 data) with the correct version - auto dc_112000_index = make_shared( - Version::DC_V1_11_2000_PROTOTYPE, - this->item_parameter_table(Version::PC_V2), - this->text_index->get(Version::PC_V2, 1, 3)); - this->set_item_name_index(Version::DC_V1_11_2000_PROTOTYPE, dc_112000_index); - - auto gc_v3_index = create_item_name_index_for_version( - Version::GC_V3, this->item_parameter_table(Version::GC_V3), this->text_index); - this->set_item_name_index(Version::GC_NTE, gc_v3_index); - this->set_item_name_index(Version::GC_V3, gc_v3_index); - this->set_item_name_index(Version::XB_V3, gc_v3_index); - - auto bb_v4_index = create_item_name_index_for_version( - Version::BB_V4, this->item_parameter_table(Version::BB_V4), this->text_index); - this->set_item_name_index(Version::BB_V4, bb_v4_index); + for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) { + Version v = static_cast(v_s); + config_log.info("Generating item name index for %s", name_for_enum(v)); + this->set_item_name_index(v, this->create_item_name_index_for_version(v, this->item_parameter_table(v), this->text_index)); + } } void ServerState::load_drop_tables() { @@ -1281,27 +1249,27 @@ void ServerState::load_drop_tables() { this->rare_item_sets.swap(new_rare_item_sets); config_log.info("Loading v2 common item table"); - auto ct_data_v2 = make_shared(load_file("system/item-tables/ItemCT-v2.afs")); - auto pt_data_v2 = make_shared(load_file("system/item-tables/ItemPT-v2.afs")); + auto ct_data_v2 = make_shared(load_file("system/item-tables/ItemCT-pc-v2.afs")); + auto pt_data_v2 = make_shared(load_file("system/item-tables/ItemPT-pc-v2.afs")); this->common_item_set_v2 = make_shared(pt_data_v2, ct_data_v2); config_log.info("Loading v3+v4 common item table"); - auto pt_data_v3_v4 = make_shared(load_file("system/item-tables/ItemPT-gc-v4.gsl")); + auto pt_data_v3_v4 = make_shared(load_file("system/item-tables/ItemPT-gc-v3.gsl")); this->common_item_set_v3_v4 = make_shared(pt_data_v3_v4, true); config_log.info("Loading armor table"); - auto armor_data = make_shared(load_file("system/item-tables/ArmorRandom-gc.rel")); + auto armor_data = make_shared(load_file("system/item-tables/ArmorRandom-gc-v3.rel")); this->armor_random_set = make_shared(armor_data); config_log.info("Loading tool table"); - auto tool_data = make_shared(load_file("system/item-tables/ToolRandom-gc.rel")); + auto tool_data = make_shared(load_file("system/item-tables/ToolRandom-gc-v3.rel")); this->tool_random_set = make_shared(tool_data); config_log.info("Loading weapon tables"); const char* filenames[4] = { - "system/item-tables/WeaponRandomNormal-gc.rel", - "system/item-tables/WeaponRandomHard-gc.rel", - "system/item-tables/WeaponRandomVeryHard-gc.rel", - "system/item-tables/WeaponRandomUltimate-gc.rel", + "system/item-tables/WeaponRandomNormal-gc-v3.rel", + "system/item-tables/WeaponRandomHard-gc-v3.rel", + "system/item-tables/WeaponRandomVeryHard-gc-v3.rel", + "system/item-tables/WeaponRandomUltimate-gc-v3.rel", }; for (size_t z = 0; z < 4; z++) { auto weapon_data = make_shared(load_file(filenames[z])); @@ -1309,21 +1277,22 @@ void ServerState::load_drop_tables() { } config_log.info("Loading tekker adjustment table"); - auto tekker_data = make_shared(load_file("system/item-tables/JudgeItem-gc.rel")); + auto tekker_data = make_shared(load_file("system/item-tables/JudgeItem-gc-v3.rel")); this->tekker_adjustment_set = make_shared(tekker_data); } void ServerState::load_item_definitions() { - config_log.info("Loading item definition tables"); - auto pmt_data_v2 = make_shared(prs_decompress(load_file("system/item-tables/ItemPMT-v2.prs"))); - this->item_parameter_table_v2 = make_shared(pmt_data_v2, ItemParameterTable::Version::V2); - auto pmt_data_v3 = make_shared(prs_decompress(load_file("system/item-tables/ItemPMT-gc.prs"))); - this->item_parameter_table_v3 = make_shared(pmt_data_v3, ItemParameterTable::Version::V3); - auto pmt_data_v4 = make_shared(prs_decompress(load_file("system/item-tables/ItemPMT-bb.prs"))); - this->item_parameter_table_v4 = make_shared(pmt_data_v4, ItemParameterTable::Version::V4); + for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) { + Version v = static_cast(v_s); + string path = string_printf("system/item-tables/ItemPMT-%s.prs", file_path_token_for_version(v)); + config_log.info("Loading item definition table %s", path.c_str()); + auto data = make_shared(prs_decompress(load_file(path))); + this->set_item_parameter_table(v, make_shared(data, v)); + } + // TODO: We should probably load the tables for other versions too. config_log.info("Loading mag evolution table"); - auto mag_data = make_shared(prs_decompress(load_file("system/item-tables/ItemMagEdit-bb.prs"))); + auto mag_data = make_shared(prs_decompress(load_file("system/item-tables/ItemMagEdit-bb-v4.prs"))); this->mag_evolution_table = make_shared(mag_data); } diff --git a/src/ServerState.hh b/src/ServerState.hh index de1189b6..acf972a5 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -142,9 +142,7 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr tool_random_set; std::array, 4> weapon_random_sets; std::shared_ptr tekker_adjustment_set; - std::shared_ptr item_parameter_table_v2; - std::shared_ptr item_parameter_table_v3; - std::shared_ptr item_parameter_table_v4; + std::array, NUM_VERSIONS> item_parameter_tables; std::shared_ptr mag_evolution_table; std::shared_ptr text_index; std::array, NUM_VERSIONS> item_name_indexes; @@ -274,8 +272,10 @@ struct ServerState : public std::enable_shared_from_this { const std::vector>& proxy_destinations(Version version) const; std::shared_ptr item_parameter_table(Version version) const; + std::shared_ptr item_parameter_table_for_encode(Version version) const; + void set_item_parameter_table(Version version, std::shared_ptr table); std::shared_ptr item_name_index(Version version) const; - void set_item_name_index(Version version, std::shared_ptr); + void set_item_name_index(Version version, std::shared_ptr index); std::string describe_item(Version version, const ItemData& item, bool include_color_codes) const; ItemData parse_item_description(Version version, const std::string& description) const; diff --git a/system/item-tables/ArmorRandom-gc.rel b/system/item-tables/ArmorRandom-gc-v3.rel similarity index 100% rename from system/item-tables/ArmorRandom-gc.rel rename to system/item-tables/ArmorRandom-gc-v3.rel diff --git a/system/item-tables/ItemCT-dc-v2.afs b/system/item-tables/ItemCT-dc-v2.afs new file mode 120000 index 00000000..e8a6fc3e --- /dev/null +++ b/system/item-tables/ItemCT-dc-v2.afs @@ -0,0 +1 @@ +ItemCT-pc-v2.afs \ No newline at end of file diff --git a/system/item-tables/ItemCT-v2.afs b/system/item-tables/ItemCT-pc-v2.afs similarity index 100% rename from system/item-tables/ItemCT-v2.afs rename to system/item-tables/ItemCT-pc-v2.afs diff --git a/system/item-tables/ItemMagEdit-bb.prs b/system/item-tables/ItemMagEdit-bb-v4.prs similarity index 100% rename from system/item-tables/ItemMagEdit-bb.prs rename to system/item-tables/ItemMagEdit-bb-v4.prs diff --git a/system/item-tables/ItemMagEdit-dc-v1.prs b/system/item-tables/ItemMagEdit-dc-v1.prs new file mode 100644 index 00000000..b94de46e Binary files /dev/null and b/system/item-tables/ItemMagEdit-dc-v1.prs differ diff --git a/system/item-tables/ItemMagEdit-dc-v2.prs b/system/item-tables/ItemMagEdit-dc-v2.prs new file mode 120000 index 00000000..84d5c062 --- /dev/null +++ b/system/item-tables/ItemMagEdit-dc-v2.prs @@ -0,0 +1 @@ +ItemMagEdit-pc-v2.prs \ No newline at end of file diff --git a/system/item-tables/ItemMagEdit-gc-ep3-nte.prs b/system/item-tables/ItemMagEdit-gc-ep3-nte.prs new file mode 120000 index 00000000..80054eb8 --- /dev/null +++ b/system/item-tables/ItemMagEdit-gc-ep3-nte.prs @@ -0,0 +1 @@ +ItemMagEdit-gc-v3.prs \ No newline at end of file diff --git a/system/item-tables/ItemMagEdit-gc-ep3.prs b/system/item-tables/ItemMagEdit-gc-ep3.prs new file mode 120000 index 00000000..80054eb8 --- /dev/null +++ b/system/item-tables/ItemMagEdit-gc-ep3.prs @@ -0,0 +1 @@ +ItemMagEdit-gc-v3.prs \ No newline at end of file diff --git a/system/item-tables/ItemMagEdit-gc-nte.prs b/system/item-tables/ItemMagEdit-gc-nte.prs new file mode 100755 index 00000000..fbeb4c75 Binary files /dev/null and b/system/item-tables/ItemMagEdit-gc-nte.prs differ diff --git a/system/item-tables/ItemMagEdit-gc-v3.prs b/system/item-tables/ItemMagEdit-gc-v3.prs new file mode 100755 index 00000000..b71efbe8 Binary files /dev/null and b/system/item-tables/ItemMagEdit-gc-v3.prs differ diff --git a/system/item-tables/ItemMagEdit-pc-nte.prs b/system/item-tables/ItemMagEdit-pc-nte.prs new file mode 120000 index 00000000..84d5c062 --- /dev/null +++ b/system/item-tables/ItemMagEdit-pc-nte.prs @@ -0,0 +1 @@ +ItemMagEdit-pc-v2.prs \ No newline at end of file diff --git a/system/item-tables/ItemMagEdit-pc-v2.prs b/system/item-tables/ItemMagEdit-pc-v2.prs new file mode 100644 index 00000000..24a28fd1 Binary files /dev/null and b/system/item-tables/ItemMagEdit-pc-v2.prs differ diff --git a/system/item-tables/ItemMagEdit-xb-v3.prs b/system/item-tables/ItemMagEdit-xb-v3.prs new file mode 100644 index 00000000..13e844d2 Binary files /dev/null and b/system/item-tables/ItemMagEdit-xb-v3.prs differ diff --git a/system/item-tables/ItemPMT-bb.prs b/system/item-tables/ItemPMT-bb-v4.prs similarity index 100% rename from system/item-tables/ItemPMT-bb.prs rename to system/item-tables/ItemPMT-bb-v4.prs diff --git a/system/item-tables/ItemPMT-dc-11-2000.prs b/system/item-tables/ItemPMT-dc-11-2000.prs new file mode 100755 index 00000000..d6e163f0 Binary files /dev/null and b/system/item-tables/ItemPMT-dc-11-2000.prs differ diff --git a/system/item-tables/ItemPMT-dc-nte.prs b/system/item-tables/ItemPMT-dc-nte.prs new file mode 100755 index 00000000..631a379d Binary files /dev/null and b/system/item-tables/ItemPMT-dc-nte.prs differ diff --git a/system/item-tables/ItemPMT-dc-v1.prs b/system/item-tables/ItemPMT-dc-v1.prs new file mode 100755 index 00000000..a3fecfcc Binary files /dev/null and b/system/item-tables/ItemPMT-dc-v1.prs differ diff --git a/system/item-tables/ItemPMT-dc-v2.prs b/system/item-tables/ItemPMT-dc-v2.prs new file mode 120000 index 00000000..1e1d0196 --- /dev/null +++ b/system/item-tables/ItemPMT-dc-v2.prs @@ -0,0 +1 @@ +ItemPMT-pc-v2.prs \ No newline at end of file diff --git a/system/item-tables/ItemPMT-gc-ep3-nte.prs b/system/item-tables/ItemPMT-gc-ep3-nte.prs new file mode 120000 index 00000000..de681f45 --- /dev/null +++ b/system/item-tables/ItemPMT-gc-ep3-nte.prs @@ -0,0 +1 @@ +ItemPMT-gc-v3.prs \ No newline at end of file diff --git a/system/item-tables/ItemPMT-gc-ep3.prs b/system/item-tables/ItemPMT-gc-ep3.prs new file mode 120000 index 00000000..de681f45 --- /dev/null +++ b/system/item-tables/ItemPMT-gc-ep3.prs @@ -0,0 +1 @@ +ItemPMT-gc-v3.prs \ No newline at end of file diff --git a/system/item-tables/ItemPMT-gc-nte.prs b/system/item-tables/ItemPMT-gc-nte.prs new file mode 100755 index 00000000..3435fb34 Binary files /dev/null and b/system/item-tables/ItemPMT-gc-nte.prs differ diff --git a/system/item-tables/ItemPMT-gc.prs b/system/item-tables/ItemPMT-gc-v3.prs similarity index 100% rename from system/item-tables/ItemPMT-gc.prs rename to system/item-tables/ItemPMT-gc-v3.prs diff --git a/system/item-tables/ItemPMT-pc-nte.prs b/system/item-tables/ItemPMT-pc-nte.prs new file mode 120000 index 00000000..1e1d0196 --- /dev/null +++ b/system/item-tables/ItemPMT-pc-nte.prs @@ -0,0 +1 @@ +ItemPMT-pc-v2.prs \ No newline at end of file diff --git a/system/item-tables/ItemPMT-v2.prs b/system/item-tables/ItemPMT-pc-v2.prs similarity index 100% rename from system/item-tables/ItemPMT-v2.prs rename to system/item-tables/ItemPMT-pc-v2.prs diff --git a/system/item-tables/ItemPMT-xb-v3.prs b/system/item-tables/ItemPMT-xb-v3.prs new file mode 100644 index 00000000..fb00b644 Binary files /dev/null and b/system/item-tables/ItemPMT-xb-v3.prs differ diff --git a/system/item-tables/ItemPT-dc-11-2000.afs b/system/item-tables/ItemPT-dc-11-2000.afs new file mode 100644 index 00000000..4c3590d8 Binary files /dev/null and b/system/item-tables/ItemPT-dc-11-2000.afs differ diff --git a/system/item-tables/ItemPT-dc-nte.afs b/system/item-tables/ItemPT-dc-nte.afs new file mode 100644 index 00000000..2c041c47 Binary files /dev/null and b/system/item-tables/ItemPT-dc-nte.afs differ diff --git a/system/item-tables/ItemPT-dc-v1.afs b/system/item-tables/ItemPT-dc-v1.afs new file mode 100644 index 00000000..d5b2c018 Binary files /dev/null and b/system/item-tables/ItemPT-dc-v1.afs differ diff --git a/system/item-tables/ItemPT-dc-v2.afs b/system/item-tables/ItemPT-dc-v2.afs new file mode 120000 index 00000000..a021351d --- /dev/null +++ b/system/item-tables/ItemPT-dc-v2.afs @@ -0,0 +1 @@ +ItemPT-pc-v2.afs \ No newline at end of file diff --git a/system/item-tables/ItemPT-gc-ep3-nte.prs b/system/item-tables/ItemPT-gc-ep3-nte.prs new file mode 120000 index 00000000..846e97ba --- /dev/null +++ b/system/item-tables/ItemPT-gc-ep3-nte.prs @@ -0,0 +1 @@ +ItemPT-gc-v3.prs \ No newline at end of file diff --git a/system/item-tables/ItemPT-gc-ep3.prs b/system/item-tables/ItemPT-gc-ep3.prs new file mode 120000 index 00000000..846e97ba --- /dev/null +++ b/system/item-tables/ItemPT-gc-ep3.prs @@ -0,0 +1 @@ +ItemPT-gc-v3.prs \ No newline at end of file diff --git a/system/item-tables/ItemPT-gc-nte.gsl b/system/item-tables/ItemPT-gc-nte.gsl new file mode 100755 index 00000000..7b3432d0 Binary files /dev/null and b/system/item-tables/ItemPT-gc-nte.gsl differ diff --git a/system/item-tables/ItemPT-gc-v4.gsl b/system/item-tables/ItemPT-gc-v3.gsl similarity index 100% rename from system/item-tables/ItemPT-gc-v4.gsl rename to system/item-tables/ItemPT-gc-v3.gsl diff --git a/system/item-tables/ItemPT-pc-nte.afs b/system/item-tables/ItemPT-pc-nte.afs new file mode 120000 index 00000000..a021351d --- /dev/null +++ b/system/item-tables/ItemPT-pc-nte.afs @@ -0,0 +1 @@ +ItemPT-pc-v2.afs \ No newline at end of file diff --git a/system/item-tables/ItemPT-v2.afs b/system/item-tables/ItemPT-pc-v2.afs similarity index 100% rename from system/item-tables/ItemPT-v2.afs rename to system/item-tables/ItemPT-pc-v2.afs diff --git a/system/item-tables/ItemPT-xb-v3.gsl b/system/item-tables/ItemPT-xb-v3.gsl new file mode 100644 index 00000000..93aa5756 Binary files /dev/null and b/system/item-tables/ItemPT-xb-v3.gsl differ diff --git a/system/item-tables/ItemRT-bb.rel b/system/item-tables/ItemRT-bb-v4.rel similarity index 100% rename from system/item-tables/ItemRT-bb.rel rename to system/item-tables/ItemRT-bb-v4.rel diff --git a/system/item-tables/ItemRT-dc-11-2000.afs b/system/item-tables/ItemRT-dc-11-2000.afs new file mode 100644 index 00000000..adcdf19f Binary files /dev/null and b/system/item-tables/ItemRT-dc-11-2000.afs differ diff --git a/system/item-tables/ItemRT-dc-nte.afs b/system/item-tables/ItemRT-dc-nte.afs new file mode 100644 index 00000000..1e76474d Binary files /dev/null and b/system/item-tables/ItemRT-dc-nte.afs differ diff --git a/system/item-tables/ItemRT-dc-v1.afs b/system/item-tables/ItemRT-dc-v1.afs new file mode 100644 index 00000000..748ded74 Binary files /dev/null and b/system/item-tables/ItemRT-dc-v1.afs differ diff --git a/system/item-tables/ItemRT-dc-v2.afs b/system/item-tables/ItemRT-dc-v2.afs new file mode 120000 index 00000000..c861dbff --- /dev/null +++ b/system/item-tables/ItemRT-dc-v2.afs @@ -0,0 +1 @@ +ItemRT-pc-v2.afs \ No newline at end of file diff --git a/system/item-tables/ItemRT-gc-nte.gsl b/system/item-tables/ItemRT-gc-nte.gsl new file mode 100755 index 00000000..62fd37cf Binary files /dev/null and b/system/item-tables/ItemRT-gc-nte.gsl differ diff --git a/system/item-tables/ItemRT-pc-v2.afs b/system/item-tables/ItemRT-pc-v2.afs new file mode 100644 index 00000000..ca640453 Binary files /dev/null and b/system/item-tables/ItemRT-pc-v2.afs differ diff --git a/system/item-tables/ItemRT-xb-v3.gsl b/system/item-tables/ItemRT-xb-v3.gsl new file mode 100644 index 00000000..c024c995 Binary files /dev/null and b/system/item-tables/ItemRT-xb-v3.gsl differ diff --git a/system/item-tables/JudgeItem-gc.rel b/system/item-tables/JudgeItem-gc-v3.rel similarity index 100% rename from system/item-tables/JudgeItem-gc.rel rename to system/item-tables/JudgeItem-gc-v3.rel diff --git a/system/item-tables/ToolRandom-gc.rel b/system/item-tables/ToolRandom-gc-v3.rel similarity index 100% rename from system/item-tables/ToolRandom-gc.rel rename to system/item-tables/ToolRandom-gc-v3.rel diff --git a/system/item-tables/WeaponRandomHard-gc.rel b/system/item-tables/WeaponRandomHard-gc-v3.rel similarity index 100% rename from system/item-tables/WeaponRandomHard-gc.rel rename to system/item-tables/WeaponRandomHard-gc-v3.rel diff --git a/system/item-tables/WeaponRandomNormal-gc.rel b/system/item-tables/WeaponRandomNormal-gc-v3.rel similarity index 100% rename from system/item-tables/WeaponRandomNormal-gc.rel rename to system/item-tables/WeaponRandomNormal-gc-v3.rel diff --git a/system/item-tables/WeaponRandomUltimate-gc.rel b/system/item-tables/WeaponRandomUltimate-gc-v3.rel similarity index 100% rename from system/item-tables/WeaponRandomUltimate-gc.rel rename to system/item-tables/WeaponRandomUltimate-gc-v3.rel diff --git a/system/item-tables/WeaponRandomVeryHard-gc.rel b/system/item-tables/WeaponRandomVeryHard-gc-v3.rel similarity index 100% rename from system/item-tables/WeaponRandomVeryHard-gc.rel rename to system/item-tables/WeaponRandomVeryHard-gc-v3.rel