rewrite MagEvolutionTable

This commit is contained in:
Martin Michelsen
2026-05-02 10:48:20 -07:00
parent 1fa3d18430
commit 6b636c4694
8 changed files with 216 additions and 107 deletions
+1
View File
@@ -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
-15
View File
@@ -1416,18 +1416,3 @@ std::shared_ptr<ItemParameterTable> ItemParameterTable::create(
throw std::logic_error("Cannot create item parameter table for this version");
}
}
MagEvolutionTable::MagEvolutionTable(shared_ptr<const string> 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<TableOffsets>(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);
}
-81
View File
@@ -355,84 +355,3 @@ protected:
explicit ItemParameterTable(std::shared_ptr<const std::string> 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<uint8_t, 5> unknown_a1 = 0;
} __packed_ws__(Side, 0x06);
parray<Side, 2> 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<const std::string> data, size_t num_mags);
~MagEvolutionTable() = default;
uint8_t get_evolution_number(uint8_t data1_1) const;
protected:
std::shared_ptr<const std::string> data;
size_t num_mags;
phosg::StringReader r;
const TableOffsets* offsets;
};
+2 -1
View File
@@ -133,7 +133,8 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<RandomG
} else if ((primary_identifier & 0xFFFF0000) == 0x030C0000) { // Non-combo mag cells
auto& mag = player->inventory.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;
+170
View File
@@ -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<uint8_t, 5> unknown_a1 = 0;
} __packed_ws__(Side, 0x06);
parray<Side, 2> sides; // [0] = right side, [1] = left side
} __packed_ws__(MotionReference, 0x0C);
template <bool BE>
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<BE> ref_table; // -> MotionReference[num_mags]
U32T<BE> unused_ref_table; // -> MotionReference[num_mags]
} __packed_ws_be__(MotionReferenceTables, 0x08);
template <bool BE>
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<BE> alpha;
F32T<BE> red;
F32T<BE> green;
F32T<BE> blue;
} __packed_ws_be__(ColorEntry, 0x10);
template <bool BE>
struct UnknownA3Entry {
uint8_t flags;
uint8_t unknown_a2;
U16T<BE> unknown_a3;
U16T<BE> unknown_a4;
U16T<BE> unknown_a5;
} __packed_ws_be__(UnknownA3Entry, 0x08);
template <bool BE>
struct RootV2V3V4 {
/* -- / 112K / V1 / V2 / V3 / BB */
/* 00 / 0438 / 0438 / 05BC / 0340 / 0400 */ U32T<BE> motion_tables; // -> MotionReferenceTables
/* 04 / 0440 / 0440 / 0594 / 0348 / 0408 */ U32T<BE> unknown_a2; // -> (uint8_t[2])[NumMags] (references into unknown_a3)
/* 08 / 0498 / 0498 / 0608 / 03CE / 04AE */ U32T<BE> unknown_a3; // -> UnknownA3Entry[max(unknown_a2) + 1]
/* 0C / 0510 / 0520 / 06B0 / 0476 / 0556 */ U32T<BE> unknown_a4; // -> uint8_t[NumMags]
/* 10 / 053C / 054C / 06EC / 04BC / 05AC */ U32T<BE> color_table; // -> ColorEntry[NumColors]
/* 14 / / / 077C / 05DC / 06CC */ U32T<BE> 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<uint8_t, 0x2C> 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 <typename RootT, size_t NumMags, size_t NumColors, bool BE>
class MagEvolutionTableT : public MagEvolutionTable {
public:
explicit MagEvolutionTableT(std::shared_ptr<const std::string> data)
: data(data), r(*data), root(&r.pget<RootT>(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<ColorEntry<BE>>(this->root->color_table + sizeof(ColorEntry<BE>) * 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<const std::string> 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<RootV2V3V4<false>, 0x28, 0x09, false>;
using MagEvolutionTableV1 = MagEvolutionTableT<RootV2V3V4<false>, 0x28, 0x09, false>;
using MagEvolutionTableV2 = MagEvolutionTableT<RootV2V3V4<false>, 0x3A, 0x09, false>;
using MagEvolutionTableGCNTE = MagEvolutionTableT<RootV2V3V4<true>, 0x3A, 0x09, true>;
using MagEvolutionTableGC = MagEvolutionTableT<RootV2V3V4<true>, 0x43, 0x12, true>;
using MagEvolutionTableXB = MagEvolutionTableT<RootV2V3V4<false>, 0x43, 0x12, false>;
using MagEvolutionTableV4 = MagEvolutionTableT<RootV2V3V4<false>, 0x53, 0x12, false>;
std::shared_ptr<MagEvolutionTable> MagEvolutionTable::create(
std::shared_ptr<const std::string> data, Version version) {
switch (version) {
case Version::DC_NTE:
return std::make_shared<MagEvolutionTableDCNTE>();
case Version::DC_11_2000:
return std::make_shared<MagEvolutionTableDC112000>(data);
case Version::DC_V1:
return std::make_shared<MagEvolutionTableV1>(data);
case Version::DC_V2:
case Version::PC_NTE:
case Version::PC_V2:
return std::make_shared<MagEvolutionTableV2>(data);
case Version::GC_NTE:
return std::make_shared<MagEvolutionTableGCNTE>(data);
case Version::GC_V3:
case Version::GC_EP3:
case Version::GC_EP3_NTE:
return std::make_shared<MagEvolutionTableGC>(data);
case Version::XB_V3:
return std::make_shared<MagEvolutionTableXB>(data);
case Version::BB_V4:
return std::make_shared<MagEvolutionTableV4>(data);
default:
throw std::logic_error("Cannot create mag evolution table for this version");
}
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "WindowsPlatform.hh"
#include <stdint.h>
#include <memory>
#include <string>
#include "CommonFileFormats.hh"
#include "Text.hh"
#include "Types.hh"
#include "Version.hh"
class MagEvolutionTable {
public:
virtual ~MagEvolutionTable() = default;
static std::shared_ptr<MagEvolutionTable> create(std::shared_ptr<const std::string> 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;
};
+14 -9
View File
@@ -476,8 +476,10 @@ shared_ptr<const ItemParameterTable> ServerState::item_parameter_table_for_encod
}
shared_ptr<const MagEvolutionTable> 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<ItemTranslationTable>(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<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-dc-v2.prs")));
auto new_table_v1_v2 = make_shared<MagEvolutionTable>(mag_data_v1_v2, 0x3A);
config_log.info_f("Loading v1 mag evolution table");
auto mag_data_v1 = make_shared<string>(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<string>(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<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-xb-v3.prs")));
auto new_table_v3 = make_shared<MagEvolutionTable>(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<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-bb-v4.prs")));
auto new_table_v4 = make_shared<MagEvolutionTable>(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);
}
+3 -1
View File
@@ -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<ServerState> {
std::array<std::shared_ptr<const ItemData::StackLimits>, NUM_VERSIONS> item_stack_limits_tables;
size_t bb_max_bank_items = 200;
size_t bb_max_bank_meseta = 999999;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v1_v2;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v1;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v2;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v3;
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v4;
std::shared_ptr<const TextIndex> text_index;