diff --git a/src/MagEvolutionTable.cc b/src/MagEvolutionTable.cc index f94a19fd..e3054e08 100644 --- a/src/MagEvolutionTable.cc +++ b/src/MagEvolutionTable.cc @@ -4,80 +4,120 @@ using namespace std; -struct MotionReference { - struct Side { - // This specifies which entry in ItemMagMotion.dat is used. The file is just a list of 0x64-byte structures. - // 0xFF = no TItemMagSub is created - uint8_t motion_table_entry = 0xFF; - parray unknown_a1 = 0; - } __packed_ws__(Side, 0x06); - parray sides; // [0] = right side, [1] = left side -} __packed_ws__(MotionReference, 0x0C); - template struct MotionReferenceTables { // It seems that there are two definition tables, but only the first is used on any version of PSO. On v3 and later, // the two offsets point to the same table, but on v2 they don't and the second table contains different data. // TODO: Figure out what the deal is with the different v2 tables. - U32T ref_table; // -> MotionReference[num_mags] - U32T unused_ref_table; // -> MotionReference[num_mags] + U32T ref_table; + U32T unused_ref_table; } __packed_ws_be__(MotionReferenceTables, 0x08); template struct ColorEntry { // Colors are specified as 4 floats, each in the range [0, 1], for each color channel. The default colors are: - // alpha red green blue color (see StaticGameData.cc) - // 1.0 1.0 0.2 0.1 red - // 1.0 0.2 0.2 1.0 blue - // 1.0 1.0 0.9 0.1 yellow - // 1.0 0.1 1.0 0.1 green - // 1.0 0.8 0.1 1.0 purple - // 1.0 0.1 0.1 0.2 black - // 1.0 0.9 1.0 1.0 white - // 1.0 0.1 0.9 1.0 cyan - // 1.0 0.5 0.3 0.2 brown - // 1.0 1.0 0.4 0.0 orange (v3+) - // 1.0 0.502 0.545 0.977 light-blue (v3+) - // 1.0 0.502 0.502 0.0 olive (v3+) - // 1.0 0.0 0.941 0.714 turquoise (v3+) - // 1.0 0.8 0.098 0.392 fuchsia (v3+) - // 1.0 0.498 0.498 0.498 grey (v3+) - // 1.0 0.996 0.996 0.832 cream (v3+) - // 1.0 0.996 0.498 0.784 pink (v3+) - // 1.0 0.0 0.498 0.322 dark-green (v3+) + // alpha red green blue color (see StaticGameData.cc) + // 00 => 1.0 1.0 0.2 0.1 red + // 01 => 1.0 0.2 0.2 1.0 blue + // 02 => 1.0 1.0 0.9 0.1 yellow + // 03 => 1.0 0.1 1.0 0.1 green + // 04 => 1.0 0.8 0.1 1.0 purple + // 05 => 1.0 0.1 0.1 0.2 black + // 06 => 1.0 0.9 1.0 1.0 white + // 07 => 1.0 0.1 0.9 1.0 cyan + // 08 => 1.0 0.5 0.3 0.2 brown + // 09 => 1.0 1.0 0.4 0.0 orange (v3+) + // 0A => 1.0 0.502 0.545 0.977 light-blue (v3+) + // 0B => 1.0 0.502 0.502 0.0 olive (v3+) + // 0C => 1.0 0.0 0.941 0.714 turquoise (v3+) + // 0D => 1.0 0.8 0.098 0.392 fuchsia (v3+) + // 0E => 1.0 0.498 0.498 0.498 grey (v3+) + // 0F => 1.0 0.996 0.996 0.832 cream (v3+) + // 10 => 1.0 0.996 0.498 0.784 pink (v3+) + // 11 => 1.0 0.0 0.498 0.322 dark-green (v3+) + // If a mag's color index is invalid (>= 0x12), it is reassigned at equip time using the following logic: + // - Set base_index to player->visual.skin if player is an android, or player->visual.costume otherwise + // - If (base_index % 9) < 7 (that is, if their costume or body color is one of the colored slots on the character + // creation screen), then set the mag color to either (base_index % 9) or (base_index % 9) + 9, with equal + // probability. + // - If (base_index % 9) >= 7 (that is, if their costume or body color is one of the last two blank-colored slots + // on the character creation screen), then set the mag color to any of the available colors, chosen at random. F32T alpha; F32T red; F32T green; F32T blue; + ColorEntry(const VectorXYZTF& c) : alpha(c.t), red(c.x), green(c.y), blue(c.z) {} + operator VectorXYZTF() const { + return VectorXYZTF{this->red.load(), this->green.load(), this->blue.load(), this->alpha.load()}; + } } __packed_ws_be__(ColorEntry, 0x10); template -struct UnknownA3Entry { +struct UnknownA3EntryT { uint8_t flags; uint8_t unknown_a2; U16T unknown_a3; U16T unknown_a4; U16T unknown_a5; -} __packed_ws_be__(UnknownA3Entry, 0x08); + UnknownA3EntryT(const MagEvolutionTable::UnknownA3Entry& e) + : flags(e.flags), + unknown_a2(e.unknown_a2), + unknown_a3(e.unknown_a3), + unknown_a4(e.unknown_a4), + unknown_a5(e.unknown_a5) {} + operator MagEvolutionTable::UnknownA3Entry() const { + return MagEvolutionTable::UnknownA3Entry{ + this->flags, this->unknown_a2, this->unknown_a3, this->unknown_a4, this->unknown_a5}; + } +} __packed_ws_be__(UnknownA3EntryT, 0x08); + +struct HeaderV1 { + parray unknown_a1 = {0x0F, 0xF0, 0x00, 0x00}; + le_uint32_t unknown_a2 = 0x00000003; + le_uint16_t unknown_a3 = 0x00C8; + le_uint16_t unknown_a4 = 0x0078; + // unknown_a5 added in V2 + le_float unknown_a6 = 0.25; + le_float unknown_a7 = 0.1; + le_uint32_t unknown_a8 = 0x00000C00; +} __packed_ws__(HeaderV1, 0x18); template -struct RootV2V3V4 { - /* -- / 112K / V1 / V2 / V3 / BB */ - /* 00 / 0438 / 0438 / 05BC / 0340 / 0400 */ U32T motion_tables; // -> MotionReferenceTables - /* 04 / 0440 / 0440 / 0594 / 0348 / 0408 */ U32T unknown_a2; // -> (uint8_t[2])[NumMags] (references into unknown_a3) - /* 08 / 0498 / 0498 / 0608 / 03CE / 04AE */ U32T unknown_a3; // -> UnknownA3Entry[max(unknown_a2) + 1] - /* 0C / 0510 / 0520 / 06B0 / 0476 / 0556 */ U32T unknown_a4; // -> uint8_t[NumMags] - /* 10 / 053C / 054C / 06EC / 04BC / 05AC */ U32T color_table; // -> ColorEntry[NumColors] - /* 14 / / / 077C / 05DC / 06CC */ U32T evolution_number_table; // -> uint8_t[NumMags] -} __packed_ws_be__(RootV2V3V4, 0x18); +struct HeaderV2V3V4 { + parray unknown_a1 = {0x0F, 0xF0, 0x00, 0x00}; + U32T unknown_a2 = 0x00000003; + U16T unknown_a3 = 0x00C8; + U16T unknown_a4 = 0x0078; + parray unknown_a5 = {0xC8, 0x00, 0x00, 0x00}; + F32T unknown_a6 = 0.25; + F32T unknown_a7 = 0.1; + U32T unknown_a8 = 0x00000C00; +} __packed_ws_be__(HeaderV2V3V4, 0x1C); +// Fields: +// 112K / V1 / V2 / V3 / BB R +// 0018 / 0018 / 001C / 001C / 001C motion_tables.ref_table // -> MotionReference[NumMags] +// 0228 / 0228 / 02D4 / 001C / 001C motion_tables.unused_ref_table // -> MotionReference[NumMags] +// 0438 / 0438 / 05BC / 0340 / 0400 * motion_tables; // -> MotionReferenceTables +// 0440 / 0440 / 0594 / 0348 / 0408 * unknown_a2; // -> (uint8_t[2])[NumMags] (references into unknown_a3) +// 0498 / 0498 / 0608 / 03CE / 04AE * unknown_a3; // -> UnknownA3Entry[max(unknown_a2) + 1] +// 0510 / 0520 / 06B0 / 0476 / 0556 * unknown_a4; // -> uint8_t[NumMags] +// 053C / 054C / 06EC / 04BC / 05AC * color_table; // -> ColorEntry[NumColors] +// ---- / ---- / 077C / 05DC / 06CC * evolution_number_table; // -> uint8_t[NumMags] + +template struct RootV1 { - le_uint32_t motion_tables; - le_uint32_t unknown_a2; - le_uint32_t unknown_a3; - le_uint32_t unknown_a4; - le_uint32_t color_table; -} __packed_ws__(RootV1, 0x14); + U32T motion_tables; + U32T unknown_a2; + U32T unknown_a3; + U32T unknown_a4; + U32T color_table; +} __packed_ws_be__(RootV1, 0x14); + +template +struct RootV2V3V4 : RootV1 { + U32T evolution_number_table; +} __packed_ws_be__(RootV2V3V4, 0x18); static uint8_t get_v1_mag_evolution_number(uint8_t data1_1) { static const std::array v1_evolution_number_table{ @@ -90,22 +130,81 @@ static uint8_t get_v1_mag_evolution_number(uint8_t data1_1) { return v1_evolution_number_table[data1_1]; } -template -class MagEvolutionTableT : public MagEvolutionTable { +template +class BinaryMagEvolutionTableT : public MagEvolutionTable { public: - explicit MagEvolutionTableT(std::shared_ptr data) + explicit BinaryMagEvolutionTableT(std::shared_ptr data) : data(data), r(*data), root(&r.pget(this->r.pget_u32l(this->data->size() - 0x10))) {} - virtual ~MagEvolutionTableT() = default; + virtual ~BinaryMagEvolutionTableT() = default; - virtual VectorXYZTF get_color_rgba(size_t index) const { + template + const ParsedT& add_to_vector_cache(std::vector& cache, size_t base_offset, size_t index) const { + while (cache.size() <= index) { + cache.emplace_back(this->r.pget(base_offset + sizeof(RawT) * cache.size())); + } + return cache[index]; + } + + virtual size_t num_mags() const { + return NumMags; + } + + virtual size_t num_motion_entries(bool use_second_table) const { + const auto& tables = this->r.pget>(this->root->motion_tables); + return get_rel_array_count( + this->all_start_offsets(), use_second_table ? tables.unused_ref_table : tables.ref_table); + } + + virtual const MotionReference& get_motion_reference(bool use_second_table, size_t index) const { + if (index >= this->num_motion_entries(use_second_table)) { + throw std::logic_error("Invalid motion reference index"); + } + const auto& tables = this->r.pget>(this->root->motion_tables); + uint32_t array_offset = use_second_table ? tables.unused_ref_table : tables.ref_table; + return this->r.pget(array_offset + sizeof(MotionReference) * index); + } + + virtual std::pair get_unknown_a2(size_t index) const { + if (index >= this->num_mags()) { + throw std::logic_error("Invalid unknown_a2 index"); + } + uint32_t base_offset = this->root->unknown_a2 + (index * 2); + return std::make_pair(this->r.pget_u8(base_offset), this->r.pget_u8(base_offset + 1)); + } + + virtual size_t num_unknown_a3_entries() const { + return get_rel_array_count>(this->all_start_offsets(), this->root->unknown_a3); + } + + virtual const UnknownA3Entry& get_unknown_a3(size_t index) const { + if (index >= this->num_unknown_a3_entries()) { + throw std::logic_error("Invalid unknown_a2 index"); + } + return this->add_to_vector_cache>(this->unknown_a3_entries, this->root->unknown_a3, index); + } + + virtual uint8_t get_unknown_a4(size_t index) const { + if (index >= this->num_mags()) { + throw std::logic_error("Invalid unknown_a4 index"); + } + return this->r.pget_u8(this->root->unknown_a2 + index); + } + + virtual size_t num_colors() const { + return NumColors; + } + + virtual const VectorXYZTF& get_color_rgba(size_t index) const { if (index >= NumColors) { throw runtime_error("invalid mag color index"); } - const auto& color = this->r.pget>(this->root->color_table + sizeof(ColorEntry) * index); - return {color.red.load(), color.green.load(), color.blue.load(), color.alpha.load()}; + return this->add_to_vector_cache>(this->colors, this->root->color_table, index); } virtual uint8_t get_evolution_number(uint8_t data1_1) const { + if (data1_1 >= this->num_mags()) { + throw std::logic_error("Invalid unknown_a4 index"); + } if constexpr (requires { this->root->evolution_number_table; }) { return this->r.pget_u8(this->root->evolution_number_table + data1_1); } else { @@ -113,10 +212,20 @@ public: } } + const std::set& all_start_offsets() const { + if (this->start_offsets.empty()) { + this->start_offsets = all_relocation_offsets_for_rel_file(r.pgetv(0, r.size()), r.size()); + } + return this->start_offsets; + } + protected: std::shared_ptr data; phosg::StringReader r; const RootT* root; + mutable std::set start_offsets; + mutable std::vector unknown_a3_entries; + mutable std::vector colors; }; class MagEvolutionTableDCNTE : public MagEvolutionTable { @@ -124,8 +233,37 @@ public: MagEvolutionTableDCNTE() = default; virtual ~MagEvolutionTableDCNTE() = default; - virtual VectorXYZTF get_color_rgba(size_t) const { - throw runtime_error("mag colors not available on DC NTE"); + virtual size_t num_mags() const { + return 0x2C; + } + + virtual size_t num_motion_entries(bool) const { + return 0; + } + virtual const MotionReference& get_motion_reference(bool, size_t) const { + throw runtime_error("Mag tables not available on DC NTE"); + } + + virtual std::pair get_unknown_a2(size_t) const { + throw runtime_error("Mag tables not available on DC NTE"); + } + + virtual size_t num_unknown_a3_entries() const { + return 0; + } + virtual const UnknownA3Entry& get_unknown_a3(size_t) const { + throw runtime_error("Mag tables not available on DC NTE"); + } + + virtual uint8_t get_unknown_a4(size_t) const { + throw runtime_error("Mag tables not available on DC NTE"); + } + + virtual size_t num_colors() const { + return 0; + } + virtual const VectorXYZTF& get_color_rgba(size_t) const { + throw runtime_error("Mag tables not available on DC NTE"); } virtual uint8_t get_evolution_number(uint8_t data1_1) const { @@ -133,13 +271,13 @@ public: } }; -using MagEvolutionTableDC112000 = MagEvolutionTableT, 0x28, 0x09, false>; -using MagEvolutionTableV1 = MagEvolutionTableT, 0x28, 0x09, false>; -using MagEvolutionTableV2 = MagEvolutionTableT, 0x3A, 0x09, false>; -using MagEvolutionTableGCNTE = MagEvolutionTableT, 0x3A, 0x09, true>; -using MagEvolutionTableGC = MagEvolutionTableT, 0x43, 0x12, true>; -using MagEvolutionTableXB = MagEvolutionTableT, 0x43, 0x12, false>; -using MagEvolutionTableV4 = MagEvolutionTableT, 0x53, 0x12, false>; +using MagEvolutionTableDC112000 = BinaryMagEvolutionTableT, 0x28, 0x09, false>; +using MagEvolutionTableV1 = BinaryMagEvolutionTableT, 0x28, 0x09, false>; +using MagEvolutionTableV2 = BinaryMagEvolutionTableT, RootV2V3V4, 0x3A, 0x09, false>; +using MagEvolutionTableGCNTE = BinaryMagEvolutionTableT, RootV2V3V4, 0x3A, 0x09, true>; +using MagEvolutionTableGC = BinaryMagEvolutionTableT, RootV2V3V4, 0x43, 0x12, true>; +using MagEvolutionTableXB = BinaryMagEvolutionTableT, RootV2V3V4, 0x43, 0x12, false>; +using MagEvolutionTableV4 = BinaryMagEvolutionTableT, RootV2V3V4, 0x53, 0x12, false>; std::shared_ptr MagEvolutionTable::create( std::shared_ptr data, Version version) { diff --git a/src/MagEvolutionTable.hh b/src/MagEvolutionTable.hh index e908278b..8a8a454c 100644 --- a/src/MagEvolutionTable.hh +++ b/src/MagEvolutionTable.hh @@ -14,11 +14,43 @@ class MagEvolutionTable { public: + struct MotionReference { + struct Side { + // This specifies which entry in ItemMagMotion.dat is used. The file is just a list of 0x64-byte structures. + // 0xFF = no TItemMagSub is created + uint8_t motion_table_entry = 0xFF; + parray unknown_a1 = 0; + } __packed_ws__(Side, 0x06); + parray sides; // [0] = right side, [1] = left side + } __packed_ws__(MotionReference, 0x0C); + + struct UnknownA3Entry { + uint8_t flags; + uint8_t unknown_a2; + uint16_t unknown_a3; + uint16_t unknown_a4; + uint16_t unknown_a5; + }; + virtual ~MagEvolutionTable() = default; static std::shared_ptr create(std::shared_ptr data, Version version); - virtual VectorXYZTF get_color_rgba(size_t index) const = 0; + virtual size_t num_mags() const = 0; + + virtual size_t num_motion_entries(bool use_second_table) const = 0; + virtual const MotionReference& get_motion_reference(bool use_second_table, size_t index) const = 0; + + virtual std::pair get_unknown_a2(size_t index) const = 0; + + virtual size_t num_unknown_a3_entries() const = 0; + virtual const UnknownA3Entry& get_unknown_a3(size_t index) const = 0; + + virtual uint8_t get_unknown_a4(size_t index) const = 0; + + virtual size_t num_colors() const = 0; + virtual const VectorXYZTF& get_color_rgba(size_t index) const = 0; + virtual uint8_t get_evolution_number(uint8_t data1_1) const = 0; protected: