diff --git a/CMakeLists.txt b/CMakeLists.txt index 70c86951..5773b953 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ set(SOURCES src/LevelTable.cc src/Lobby.cc src/Loggers.cc + src/MagEvolutionTable.cc src/Main.cc src/Map.cc src/Menu.cc diff --git a/src/ItemParameterTable.cc b/src/ItemParameterTable.cc index f3ddf82e..05356337 100644 --- a/src/ItemParameterTable.cc +++ b/src/ItemParameterTable.cc @@ -1416,18 +1416,3 @@ std::shared_ptr ItemParameterTable::create( throw std::logic_error("Cannot create item parameter table for this version"); } } - -MagEvolutionTable::MagEvolutionTable(shared_ptr data, size_t num_mags) - : data(data), - num_mags(num_mags), - r(*data) { - size_t offset_table_offset = this->r.pget_u32l(this->data->size() - 0x10); - this->offsets = &r.pget(offset_table_offset); -} - -uint8_t MagEvolutionTable::get_evolution_number(uint8_t data1_1) const { - if (data1_1 >= this->num_mags) { - throw runtime_error("invalid mag number"); - } - return this->r.pget_u8(this->offsets->evolution_number + data1_1); -} diff --git a/src/ItemParameterTable.hh b/src/ItemParameterTable.hh index 68035b93..6c9605a8 100644 --- a/src/ItemParameterTable.hh +++ b/src/ItemParameterTable.hh @@ -355,84 +355,3 @@ protected: explicit ItemParameterTable(std::shared_ptr data); }; - -class MagEvolutionTable { -public: - // TODO: V1 format is different! Offsets are 0438 0440 0498 0520 054C - 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 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. - le_uint32_t ref_table; // -> MotionReference[num_mags] - le_uint32_t unused_ref_table; // -> MotionReference[num_mags] - } __packed_ws__(MotionReferenceTables, 0x08); - - 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+) - le_float alpha; - le_float red; - le_float green; - le_float blue; - } __packed_ws__(ColorEntry, 0x10); - - struct UnknownA3Entry { - uint8_t flags; - uint8_t unknown_a2; - le_uint16_t unknown_a3; - le_uint16_t unknown_a4; - le_uint16_t unknown_a5; - } __packed_ws__(UnknownA3Entry, 0x08); - - struct TableOffsets { - // num_mags = 0x3A in v2 and GC NTE, 0x43 in V3, 0x53 in BB - // num_colors = 0x09 in v2 and GC NTE, 0x12 in V3/BB - // TODO: GC NTE uses the v2 format but is big-endian - /* -- / V2 / V3 / BB */ - /* 00 / 05BC / 0340 / 0400 */ le_uint32_t motion_tables; // -> MotionReferenceTables - /* 04 / 0594 / 0348 / 0408 */ le_uint32_t unknown_a2; // -> (uint8_t[2])[num_mags] (references into unknown_a3) - /* 08 / 0608 / 03CE / 04AE */ le_uint32_t unknown_a3; // -> UnknownA3Entry[max(unknown_a2) + 1] - /* 0C / 06B0 / 0476 / 0556 */ le_uint32_t unknown_a4; // -> (uint8_t)[num_mags] - /* 10 / 06EC / 04BC / 05AC */ le_uint32_t color_table; // -> ColorEntry[num_colors] - /* 14 / 077C / 05DC / 06CC */ le_uint32_t evolution_number; // -> uint8_t[num_mags] - } __packed_ws__(TableOffsets, 0x18); - - MagEvolutionTable(std::shared_ptr data, size_t num_mags); - ~MagEvolutionTable() = default; - - uint8_t get_evolution_number(uint8_t data1_1) const; - -protected: - std::shared_ptr data; - size_t num_mags; - phosg::StringReader r; - const TableOffsets* offsets; -}; diff --git a/src/Items.cc b/src/Items.cc index dc654f8e..5dc4e1a8 100644 --- a/src/Items.cc +++ b/src/Items.cc @@ -133,7 +133,8 @@ void player_use_item(shared_ptr c, size_t item_index, shared_ptrinventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)]; - if (s->mag_evolution_table(c->version())->get_evolution_number(mag.data.data1[1]) < 4) { + uint8_t evolution_number = s->mag_evolution_table(c->version())->get_evolution_number(mag.data.data1[1]); + if (evolution_number < 4) { switch (item.data.data1[2]) { case 0x00: // Cell of MAG 502 mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x1D : 0x21; diff --git a/src/MagEvolutionTable.cc b/src/MagEvolutionTable.cc new file mode 100644 index 00000000..3071eae8 --- /dev/null +++ b/src/MagEvolutionTable.cc @@ -0,0 +1,170 @@ +#include "MagEvolutionTable.hh" + +#include "CommonFileFormats.hh" + +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] +} __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+) + F32T alpha; + F32T red; + F32T green; + F32T blue; +} __packed_ws_be__(ColorEntry, 0x10); + +template +struct UnknownA3Entry { + uint8_t flags; + uint8_t unknown_a2; + U16T unknown_a3; + U16T unknown_a4; + U16T unknown_a5; +} __packed_ws_be__(UnknownA3Entry, 0x08); + +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; // -> uint8_t[NumMags] +} __packed_ws_be__(RootV2V3V4, 0x18); + +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); + +static uint8_t get_v1_mag_evolution_number(uint8_t data1_1) { + static const std::array v1_evolution_number_table{ + /* 00 */ 0, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 2, + /* 10 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 2, 2, 3, 4, 3, 3, + /* 20 */ 3, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4}; + if (data1_1 >= v1_evolution_number_table.size()) { + throw runtime_error("invalid mag number"); + } + return v1_evolution_number_table[data1_1]; +} + +template +class MagEvolutionTableT : public MagEvolutionTable { +public: + explicit MagEvolutionTableT(std::shared_ptr data) + : data(data), r(*data), root(&r.pget(this->r.pget_u32l(this->data->size() - 0x10))) {} + virtual ~MagEvolutionTableT() = default; + + virtual 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()}; + } + + virtual uint8_t get_evolution_number(uint8_t data1_1) const { + if constexpr (requires { this->root->evolution_number_table; }) { + return this->r.pget_u8(this->root->evolution_number_table + data1_1); + } else { + return get_v1_mag_evolution_number(data1_1); + } + } + +protected: + std::shared_ptr data; + phosg::StringReader r; + const RootT* root; +}; + +class MagEvolutionTableDCNTE : public MagEvolutionTable { +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 uint8_t get_evolution_number(uint8_t data1_1) const { + return get_v1_mag_evolution_number(data1_1); + } +}; + +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>; + +std::shared_ptr MagEvolutionTable::create( + std::shared_ptr data, Version version) { + switch (version) { + case Version::DC_NTE: + return std::make_shared(); + case Version::DC_11_2000: + return std::make_shared(data); + case Version::DC_V1: + return std::make_shared(data); + case Version::DC_V2: + case Version::PC_NTE: + case Version::PC_V2: + return std::make_shared(data); + case Version::GC_NTE: + return std::make_shared(data); + case Version::GC_V3: + case Version::GC_EP3: + case Version::GC_EP3_NTE: + return std::make_shared(data); + case Version::XB_V3: + return std::make_shared(data); + case Version::BB_V4: + return std::make_shared(data); + default: + throw std::logic_error("Cannot create mag evolution table for this version"); + } +} diff --git a/src/MagEvolutionTable.hh b/src/MagEvolutionTable.hh new file mode 100644 index 00000000..e908278b --- /dev/null +++ b/src/MagEvolutionTable.hh @@ -0,0 +1,26 @@ +#pragma once + +#include "WindowsPlatform.hh" + +#include + +#include +#include + +#include "CommonFileFormats.hh" +#include "Text.hh" +#include "Types.hh" +#include "Version.hh" + +class MagEvolutionTable { +public: + 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 uint8_t get_evolution_number(uint8_t data1_1) const = 0; + +protected: + MagEvolutionTable() = default; +}; diff --git a/src/ServerState.cc b/src/ServerState.cc index 2ac4f4c6..1f6d8b71 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -476,8 +476,10 @@ shared_ptr ServerState::item_parameter_table_for_encod } shared_ptr ServerState::mag_evolution_table(Version version) const { - if (is_v1_or_v2(version)) { - return this->mag_evolution_table_v1_v2; + if (is_v1(version)) { + return this->mag_evolution_table_v1; + } else if (is_v2(version)) { + return this->mag_evolution_table_v2; } else if (!is_v4(version)) { return this->mag_evolution_table_v3; } else { @@ -2155,20 +2157,23 @@ void ServerState::load_item_definitions() { auto json = phosg::JSON::parse(phosg::load_file("system/item-tables/translation-table.json")); auto new_item_translation_table = make_shared(json, new_item_parameter_tables); - // TODO: We should probably load the tables for other versions too. - config_log.info_f("Loading v1/v2 mag evolution table"); - auto mag_data_v1_v2 = make_shared(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-dc-v2.prs"))); - auto new_table_v1_v2 = make_shared(mag_data_v1_v2, 0x3A); + config_log.info_f("Loading v1 mag evolution table"); + auto mag_data_v1 = make_shared(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-dc-v1.prs"))); + auto new_table_v1 = MagEvolutionTable::create(mag_data_v1, Version::DC_V1); + config_log.info_f("Loading v2 mag evolution table"); + auto mag_data_v2 = make_shared(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-dc-v2.prs"))); + auto new_table_v2 = MagEvolutionTable::create(mag_data_v2, Version::DC_V2); config_log.info_f("Loading v3 mag evolution table"); auto mag_data_v3 = make_shared(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-xb-v3.prs"))); - auto new_table_v3 = make_shared(mag_data_v3, 0x43); + auto new_table_v3 = MagEvolutionTable::create(mag_data_v3, Version::XB_V3); config_log.info_f("Loading v4 mag evolution table"); auto mag_data_v4 = make_shared(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-bb-v4.prs"))); - auto new_table_v4 = make_shared(mag_data_v4, 0x53); + auto new_table_v4 = MagEvolutionTable::create(mag_data_v4, Version::BB_V4); this->item_parameter_tables = std::move(new_item_parameter_tables); this->item_translation_table = std::move(new_item_translation_table); - this->mag_evolution_table_v1_v2 = std::move(new_table_v1_v2); + this->mag_evolution_table_v1 = std::move(new_table_v1); + this->mag_evolution_table_v2 = std::move(new_table_v2); this->mag_evolution_table_v3 = std::move(new_table_v3); this->mag_evolution_table_v4 = std::move(new_table_v4); } diff --git a/src/ServerState.hh b/src/ServerState.hh index 6268e30d..41365247 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -23,6 +23,7 @@ #include "ItemTranslationTable.hh" #include "LevelTable.hh" #include "Lobby.hh" +#include "MagEvolutionTable.hh" #include "Menu.hh" #include "Quest.hh" #include "TeamIndex.hh" @@ -208,7 +209,8 @@ struct ServerState : public std::enable_shared_from_this { std::array, NUM_VERSIONS> item_stack_limits_tables; size_t bb_max_bank_items = 200; size_t bb_max_bank_meseta = 999999; - std::shared_ptr mag_evolution_table_v1_v2; + std::shared_ptr mag_evolution_table_v1; + std::shared_ptr mag_evolution_table_v2; std::shared_ptr mag_evolution_table_v3; std::shared_ptr mag_evolution_table_v4; std::shared_ptr text_index;