add support for all versions in ItemParameterTable

This commit is contained in:
Martin Michelsen
2024-01-07 21:33:10 -08:00
parent d3bc2dad4f
commit 695404165b
61 changed files with 1431 additions and 580 deletions
+1
View File
@@ -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
+320 -40
View File
@@ -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<ItemMetadata>();
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<uint64_t>(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<ItemMetadata>();
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<uint8_t>(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);
}
}
}
+2
View File
@@ -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;
+687 -295
View File
File diff suppressed because it is too large Load Diff
+311 -138
View File
@@ -5,6 +5,7 @@
#include <map>
#include <memory>
#include <phosg/Encoding.hh>
#include <set>
#include <string>
#include <vector>
@@ -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 <bool IsBigEndian>
struct ArrayRef {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
U32T count;
U32T offset;
/* 00 */ U32T count;
/* 04 */ U32T offset;
/* 08 */
} __attribute__((packed));
struct ArrayRefLE : ArrayRef<false> {
} __attribute__((packed));
@@ -29,152 +35,240 @@ public:
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::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 <bool IsBigEndian>
struct ItemBaseV3 : ItemBaseV2<IsBigEndian> {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
U16T type = 0;
U16T skin = 0;
/* 04 */ U16T type = 0;
/* 06 */ U16T skin = 0;
/* 08 */
} __attribute__((packed));
template <bool IsBigEndian>
struct ItemBaseV4 : ItemBaseV3<IsBigEndian> {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
U32T team_points = 0;
/* 08 */ U32T team_points = 0;
/* 0C */
} __attribute__((packed));
struct WeaponV2 {
ItemBaseV2<false> 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<uint8_t, 3> unknown_a9;
struct WeaponV4;
struct WeaponDCProtos {
/* 00 */ ItemBaseV2<false> 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<false> 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<uint8_t, 3> unknown_a9;
/* 18 */
WeaponV4 to_v4() const;
} __attribute__((packed));
struct WeaponGCNTE {
/* 00 */ ItemBaseV3<true> 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 <bool IsBigEndian>
struct WeaponV3 {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
ItemBaseV3<IsBigEndian> 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<IsBigEndian> 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<false> 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<false> 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 <typename BaseT, bool IsBigEndian>
struct ArmorOrShield {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::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<ItemBaseV2<false>, false> {
struct ArmorOrShieldV4;
struct ArmorOrShieldDCProtos : ArmorOrShield<ItemBaseV2<false>, false> {
ArmorOrShieldV4 to_v4() const;
} __attribute__((packed));
struct ArmorOrShieldV3 : ArmorOrShield<ItemBaseV3<true>, true> {
template <typename BaseT, bool IsBigEndian>
struct ArmorOrShieldFinal : ArmorOrShield<BaseT, IsBigEndian> {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
/* 14 */ uint8_t stat_boost = 0;
/* 15 */ uint8_t tech_boost = 0;
/* 16 */ U16T unknown_a2 = 0;
/* 18 */
} __attribute__((packed));
struct ArmorOrShieldV4 : ArmorOrShield<ItemBaseV4<false>, false> {
struct ArmorOrShieldV1V2 : ArmorOrShieldFinal<ItemBaseV2<false>, false> {
ArmorOrShieldV4 to_v4() const;
} __attribute__((packed));
template <bool IsBigEndian>
struct ArmorOrShieldV3 : ArmorOrShieldFinal<ItemBaseV3<IsBigEndian>, IsBigEndian> {
ArmorOrShieldV4 to_v4() const;
} __attribute__((packed));
struct ArmorOrShieldV4 : ArmorOrShieldFinal<ItemBaseV4<false>, false> {
} __attribute__((packed));
template <typename BaseT, bool IsBigEndian>
struct Unit {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using S16T = typename std::conditional<IsBigEndian, be_int16_t, le_int16_t>::type;
BaseT base;
U16T stat = 0;
U16T stat_amount = 0;
S16T modifier_amount = 0;
parray<uint8_t, 2> unused;
/* V1/V2 offsets */
/* 00 */ BaseT base;
/* 04 */ U16T stat = 0;
/* 06 */ U16T stat_amount = 0;
/* 08 */
} __attribute__((packed));
struct UnitV2 : Unit<ItemBaseV2<false>, false> {
struct UnitV4;
struct UnitDCProtos : Unit<ItemBaseV2<false>, false> {
UnitV4 to_v4() const;
} __attribute__((packed));
struct UnitV3 : Unit<ItemBaseV3<true>, true> {
template <typename BaseT, bool IsBigEndian>
struct UnitFinal : Unit<BaseT, IsBigEndian> {
using S16T = typename std::conditional<IsBigEndian, be_int16_t, le_int16_t>::type;
/* 08 */ S16T modifier_amount = 0;
/* 0A */ parray<uint8_t, 2> unused;
/* 0C */
} __attribute__((packed));
struct UnitV4 : Unit<ItemBaseV4<false>, false> {
struct UnitV1V2 : UnitFinal<ItemBaseV2<false>, false> {
UnitV4 to_v4() const;
} __attribute__((packed));
template <bool IsBigEndian>
struct UnitV3 : UnitFinal<ItemBaseV3<IsBigEndian>, IsBigEndian> {
UnitV4 to_v4() const;
} __attribute__((packed));
struct UnitV4 : UnitFinal<ItemBaseV4<false>, false> {
} __attribute__((packed));
template <typename BaseT, bool IsBigEndian>
struct Mag {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::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<uint8_t, 2> 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<ItemBaseV2<false>, false> {
MagV4 to_v4() const;
} __attribute__((packed));
struct MagV2 : Mag<ItemBaseV2<false>, false> {
/* 10 */ le_uint16_t class_flags = 0x00FF;
/* 12 */ parray<uint8_t, 2> unused;
/* 14 */
MagV4 to_v4() const;
} __attribute__((packed));
struct MagV3 : Mag<ItemBaseV3<true>, true> {
template <bool IsBigEndian>
struct MagV3 : Mag<ItemBaseV3<IsBigEndian>, IsBigEndian> {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
/* 10 */ U16T class_flags = 0x00FF;
/* 12 */ parray<uint8_t, 2> unused;
/* 14 */
MagV4 to_v4() const;
} __attribute__((packed));
struct MagV4 : Mag<ItemBaseV4<false>, false> {
/* 10 */ le_uint16_t class_flags = 0x00FF;
/* 12 */ parray<uint8_t, 2> unused;
/* 14 */
} __attribute__((packed));
template <typename BaseT, bool IsBigEndian>
@@ -209,15 +322,22 @@ public:
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using S32T = typename std::conditional<IsBigEndian, be_int32_t, le_int32_t>::type;
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::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<ItemBaseV2<false>, false> {
struct ToolV4;
struct ToolV1V2 : Tool<ItemBaseV2<false>, false> {
ToolV4 to_v4() const;
} __attribute__((packed));
struct ToolV3 : Tool<ItemBaseV3<true>, true> {
template <bool IsBigEndian>
struct ToolV3 : Tool<ItemBaseV3<IsBigEndian>, IsBigEndian> {
ToolV4 to_v4() const;
} __attribute__((packed));
struct ToolV4 : Tool<ItemBaseV4<false>, 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<const std::string> data, Version version);
~ItemParameterTable() = default;
void print(FILE* stream) const;
std::set<uint32_t> 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<uint8_t, uint8_t> 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<false>& 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<ItemCombination>& get_all_combinations_for_used_item(const ItemData& used_item) const;
const std::map<uint32_t, std::vector<ItemCombination>>& get_all_item_combinations() const;
size_t num_events() const;
std::pair<const EventItem*, size_t> 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 <bool IsBigEndian>
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<const std::string> data;
StringReader r;
const TableOffsetsV2* offsets_v2;
const TableOffsetsV3V4<true>* offsets_v3;
const TableOffsetsDCProtos* offsets_dc_protos;
const TableOffsetsV1V2* offsets_v1_v2;
const TableOffsetsGCNTE* offsets_gc_nte;
const TableOffsetsV3V4<false>* offsets_v3_le;
const TableOffsetsV3V4<true>* offsets_v3_be;
const TableOffsetsV3V4<false>* offsets_v4;
// These are unused if offsets_v4 is not null (in that case, we just return
@@ -423,8 +594,10 @@ private:
template <typename ToolT, bool IsBigEndian>
std::pair<uint8_t, uint8_t> find_tool_by_id_t(uint32_t tool_table_offset, uint32_t id) const;
template <bool IsBigEndian, typename OffsetsT>
float get_sale_divisor_t(const OffsetsT* offsets, uint8_t data1_0, uint8_t data1_1) const;
template <bool IsBigEndian>
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 <bool IsBigEndian>
std::pair<const ItemParameterTable::EventItem*, size_t> get_event_items_t(uint32_t base_offset, uint8_t event_number) const;
};
+33 -19
View File
@@ -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<Version>(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<Version>(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\
+7 -4
View File
@@ -659,7 +659,8 @@ void PlayerInventory::decode_from_client(shared_ptr<Client> c) {
}
void PlayerInventory::encode_for_client(shared_ptr<Client> 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<Client> 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<Client> 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);
}
}
+11 -9
View File
@@ -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<Parsed6x70Data> parsed;
switch (c->version()) {
switch (c_v) {
case Version::DC_NTE:
parsed = make_unique<Parsed6x70Data>(
check_size_t<G_SyncPlayerDispAndInventory_DCNTE_6x70>(data, size),
@@ -807,7 +809,7 @@ static void on_sync_joining_player_disp_and_inventory(
parsed = make_unique<Parsed6x70Data>(
check_size_t<G_SyncPlayerDispAndInventory_DC_PC_6x70>(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<Client> 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<Client> 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<Client> 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)) {
+5 -4
View File
@@ -2255,9 +2255,10 @@ void send_execute_item_trade(shared_ptr<Client> c, const vector<ItemData>& 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<Client> 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<ServerState> 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<ServerState> 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));
}
+36 -67
View File
@@ -370,25 +370,19 @@ void ServerState::dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx) {
}
shared_ptr<const ItemParameterTable> 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<size_t>(version));
if (ret == nullptr) {
throw runtime_error("no item parameter table exists for this version");
}
return ret;
}
shared_ptr<const ItemParameterTable> 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<const ItemParameterTable> table) {
this->item_parameter_tables.at(static_cast<size_t>(version)) = table;
}
shared_ptr<const ItemNameIndex> ServerState::item_name_index(Version version) const {
@@ -1195,37 +1189,11 @@ shared_ptr<ItemNameIndex> 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<ItemNameIndex>(
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<Version>(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<string>(load_file("system/item-tables/ItemCT-v2.afs"));
auto pt_data_v2 = make_shared<string>(load_file("system/item-tables/ItemPT-v2.afs"));
auto ct_data_v2 = make_shared<string>(load_file("system/item-tables/ItemCT-pc-v2.afs"));
auto pt_data_v2 = make_shared<string>(load_file("system/item-tables/ItemPT-pc-v2.afs"));
this->common_item_set_v2 = make_shared<AFSV2CommonItemSet>(pt_data_v2, ct_data_v2);
config_log.info("Loading v3+v4 common item table");
auto pt_data_v3_v4 = make_shared<string>(load_file("system/item-tables/ItemPT-gc-v4.gsl"));
auto pt_data_v3_v4 = make_shared<string>(load_file("system/item-tables/ItemPT-gc-v3.gsl"));
this->common_item_set_v3_v4 = make_shared<GSLV3V4CommonItemSet>(pt_data_v3_v4, true);
config_log.info("Loading armor table");
auto armor_data = make_shared<string>(load_file("system/item-tables/ArmorRandom-gc.rel"));
auto armor_data = make_shared<string>(load_file("system/item-tables/ArmorRandom-gc-v3.rel"));
this->armor_random_set = make_shared<ArmorRandomSet>(armor_data);
config_log.info("Loading tool table");
auto tool_data = make_shared<string>(load_file("system/item-tables/ToolRandom-gc.rel"));
auto tool_data = make_shared<string>(load_file("system/item-tables/ToolRandom-gc-v3.rel"));
this->tool_random_set = make_shared<ToolRandomSet>(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<string>(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<string>(load_file("system/item-tables/JudgeItem-gc.rel"));
auto tekker_data = make_shared<string>(load_file("system/item-tables/JudgeItem-gc-v3.rel"));
this->tekker_adjustment_set = make_shared<TekkerAdjustmentSet>(tekker_data);
}
void ServerState::load_item_definitions() {
config_log.info("Loading item definition tables");
auto pmt_data_v2 = make_shared<string>(prs_decompress(load_file("system/item-tables/ItemPMT-v2.prs")));
this->item_parameter_table_v2 = make_shared<ItemParameterTable>(pmt_data_v2, ItemParameterTable::Version::V2);
auto pmt_data_v3 = make_shared<string>(prs_decompress(load_file("system/item-tables/ItemPMT-gc.prs")));
this->item_parameter_table_v3 = make_shared<ItemParameterTable>(pmt_data_v3, ItemParameterTable::Version::V3);
auto pmt_data_v4 = make_shared<string>(prs_decompress(load_file("system/item-tables/ItemPMT-bb.prs")));
this->item_parameter_table_v4 = make_shared<ItemParameterTable>(pmt_data_v4, ItemParameterTable::Version::V4);
for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) {
Version v = static_cast<Version>(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<string>(prs_decompress(load_file(path)));
this->set_item_parameter_table(v, make_shared<ItemParameterTable>(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<string>(prs_decompress(load_file("system/item-tables/ItemMagEdit-bb.prs")));
auto mag_data = make_shared<string>(prs_decompress(load_file("system/item-tables/ItemMagEdit-bb-v4.prs")));
this->mag_evolution_table = make_shared<MagEvolutionTable>(mag_data);
}
+4 -4
View File
@@ -142,9 +142,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<const ToolRandomSet> tool_random_set;
std::array<std::shared_ptr<const WeaponRandomSet>, 4> weapon_random_sets;
std::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set;
std::shared_ptr<const ItemParameterTable> item_parameter_table_v2;
std::shared_ptr<const ItemParameterTable> item_parameter_table_v3;
std::shared_ptr<const ItemParameterTable> item_parameter_table_v4;
std::array<std::shared_ptr<const ItemParameterTable>, NUM_VERSIONS> item_parameter_tables;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table;
std::shared_ptr<const TextIndex> text_index;
std::array<std::shared_ptr<const ItemNameIndex>, NUM_VERSIONS> item_name_indexes;
@@ -274,8 +272,10 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
const std::vector<std::pair<std::string, uint16_t>>& proxy_destinations(Version version) const;
std::shared_ptr<const ItemParameterTable> item_parameter_table(Version version) const;
std::shared_ptr<const ItemParameterTable> item_parameter_table_for_encode(Version version) const;
void set_item_parameter_table(Version version, std::shared_ptr<const ItemParameterTable> table);
std::shared_ptr<const ItemNameIndex> item_name_index(Version version) const;
void set_item_name_index(Version version, std::shared_ptr<const ItemNameIndex>);
void set_item_name_index(Version version, std::shared_ptr<const ItemNameIndex> 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;
+1
View File
@@ -0,0 +1 @@
ItemCT-pc-v2.afs
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
ItemMagEdit-pc-v2.prs
+1
View File
@@ -0,0 +1 @@
ItemMagEdit-gc-v3.prs
+1
View File
@@ -0,0 +1 @@
ItemMagEdit-gc-v3.prs
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
ItemMagEdit-pc-v2.prs
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
ItemPMT-pc-v2.prs
+1
View File
@@ -0,0 +1 @@
ItemPMT-gc-v3.prs
+1
View File
@@ -0,0 +1 @@
ItemPMT-gc-v3.prs
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
ItemPMT-pc-v2.prs
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
ItemPT-pc-v2.afs
+1
View File
@@ -0,0 +1 @@
ItemPT-gc-v3.prs
+1
View File
@@ -0,0 +1 @@
ItemPT-gc-v3.prs
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
ItemPT-pc-v2.afs
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
ItemRT-pc-v2.afs
Binary file not shown.
Binary file not shown.
Binary file not shown.