Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
c497f64376
|
|||
|
90a1f0f938
|
|||
|
71fc272133
|
|||
|
e94fcf035e
|
|||
|
0abdb50eca
|
@@ -97,7 +97,6 @@ set(SOURCES
|
|||||||
src/LevelTable.cc
|
src/LevelTable.cc
|
||||||
src/Lobby.cc
|
src/Lobby.cc
|
||||||
src/Loggers.cc
|
src/Loggers.cc
|
||||||
src/MagEvolutionTable.cc
|
|
||||||
src/Main.cc
|
src/Main.cc
|
||||||
src/Map.cc
|
src/Map.cc
|
||||||
src/Menu.cc
|
src/Menu.cc
|
||||||
|
|||||||
+6
-2
@@ -14,9 +14,11 @@ struct BMLHeaderT {
|
|||||||
parray<uint8_t, 0x04> unknown_a1;
|
parray<uint8_t, 0x04> unknown_a1;
|
||||||
U32T<BE> num_entries;
|
U32T<BE> num_entries;
|
||||||
parray<uint8_t, 0x38> unknown_a2;
|
parray<uint8_t, 0x38> unknown_a2;
|
||||||
} __packed_ws_be__(BMLHeaderT, 0x40);
|
} __attribute__((packed));
|
||||||
using BMLHeader = BMLHeaderT<false>;
|
using BMLHeader = BMLHeaderT<false>;
|
||||||
using BMLHeaderBE = BMLHeaderT<true>;
|
using BMLHeaderBE = BMLHeaderT<true>;
|
||||||
|
check_struct_size(BMLHeader, 0x40);
|
||||||
|
check_struct_size(BMLHeaderBE, 0x40);
|
||||||
|
|
||||||
template <bool BE>
|
template <bool BE>
|
||||||
struct BMLHeaderEntryT {
|
struct BMLHeaderEntryT {
|
||||||
@@ -27,9 +29,11 @@ struct BMLHeaderEntryT {
|
|||||||
U32T<BE> compressed_gvm_size;
|
U32T<BE> compressed_gvm_size;
|
||||||
U32T<BE> decompressed_gvm_size;
|
U32T<BE> decompressed_gvm_size;
|
||||||
parray<uint8_t, 0x0C> unknown_a2;
|
parray<uint8_t, 0x0C> unknown_a2;
|
||||||
} __packed_ws_be__(BMLHeaderEntryT, 0x40);
|
} __attribute__((packed));
|
||||||
using BMLHeaderEntry = BMLHeaderEntryT<false>;
|
using BMLHeaderEntry = BMLHeaderEntryT<false>;
|
||||||
using BMLHeaderEntryBE = BMLHeaderEntryT<true>;
|
using BMLHeaderEntryBE = BMLHeaderEntryT<true>;
|
||||||
|
check_struct_size(BMLHeaderEntry, 0x40);
|
||||||
|
check_struct_size(BMLHeaderEntryBE, 0x40);
|
||||||
|
|
||||||
template <bool BE>
|
template <bool BE>
|
||||||
void BMLArchive::load_t() {
|
void BMLArchive::load_t() {
|
||||||
|
|||||||
+40
-9
@@ -439,12 +439,14 @@ ChatCommandDefinition cc_bank(
|
|||||||
});
|
});
|
||||||
|
|
||||||
static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool is_bb_conversion) {
|
static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool is_bb_conversion) {
|
||||||
// TODO: We could support this in proxy sessions; we'd just have to handle the 61/30 correctly
|
// Allow $savechar in proxy sessions for character migration/testing.
|
||||||
|
// Keep $bbchar blocked in proxy sessions because it writes to BB account slots.
|
||||||
|
if (is_bb_conversion) {
|
||||||
a.check_is_proxy(false);
|
a.check_is_proxy(false);
|
||||||
|
}
|
||||||
a.check_is_game(false);
|
a.check_is_game(false);
|
||||||
|
|
||||||
auto s = a.c->require_server_state();
|
auto s = a.c->require_server_state();
|
||||||
auto l = a.c->require_lobby();
|
|
||||||
|
|
||||||
if (is_bb_conversion && is_ep3(a.c->version())) {
|
if (is_bb_conversion && is_ep3(a.c->version())) {
|
||||||
throw precondition_failed("$C6Episode 3 players\ncannot be converted\nto BB format");
|
throw precondition_failed("$C6Episode 3 players\ncannot be converted\nto BB format");
|
||||||
@@ -481,9 +483,14 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
|
|||||||
dest_account = a.c->login->account;
|
dest_account = a.c->login->account;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the client isn't BB, request the player info. (If they are BB, the server already has it)
|
// In direct sessions, request player info from the client.
|
||||||
|
// In proxy sessions, don't use the 61/30 request path; use the server-side
|
||||||
|
// character state already tracked for the proxied client.
|
||||||
GetPlayerInfoResult ch;
|
GetPlayerInfoResult ch;
|
||||||
if (a.c->version() == Version::BB_V4) {
|
if ((a.c->version() == Version::BB_V4) || a.c->proxy_session) {
|
||||||
|
if (is_ep3(a.c->version())) {
|
||||||
|
throw precondition_failed("$C6Proxy savechar for\nEpisode 3 is not\nimplemented yet");
|
||||||
|
}
|
||||||
ch.character = a.c->character_file();
|
ch.character = a.c->character_file();
|
||||||
ch.is_full_info = true;
|
ch.is_full_info = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -1528,14 +1535,32 @@ ChatCommandDefinition cc_ln(
|
|||||||
ChatCommandDefinition cc_loadchar(
|
ChatCommandDefinition cc_loadchar(
|
||||||
{"$loadchar"},
|
{"$loadchar"},
|
||||||
+[](const Args& a) -> asio::awaitable<void> {
|
+[](const Args& a) -> asio::awaitable<void> {
|
||||||
a.check_is_proxy(false);
|
if (a.c->proxy_session && (a.c->version() == Version::BB_V4)) {
|
||||||
|
throw precondition_failed("$C6This command cannot\nbe used in proxy\nsessions in BB games");
|
||||||
|
}
|
||||||
a.check_is_game(false);
|
a.check_is_game(false);
|
||||||
if (a.check_permissions && a.c->login->account->check_flag(Account::Flag::IS_SHARED_ACCOUNT)) {
|
if (a.check_permissions && a.c->login->account->check_flag(Account::Flag::IS_SHARED_ACCOUNT)) {
|
||||||
throw precondition_failed("$C7This command cannot\nbe used on a shared\naccount");
|
throw precondition_failed("$C7This command cannot\nbe used on a shared\naccount");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto s = a.c->require_server_state();
|
auto s = a.c->require_server_state();
|
||||||
auto l = a.c->require_lobby();
|
shared_ptr<Lobby> l;
|
||||||
|
if (a.c->proxy_session) {
|
||||||
|
if (!a.c->proxy_session->is_in_lobby) {
|
||||||
|
throw precondition_failed("$C6This command can\nonly be used from\nthe lobby");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
l = a.c->require_lobby();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto send_proxy_lobby_refresh = [&]() {
|
||||||
|
auto temp_l = make_shared<Lobby>(s, 0xFFFFFFFF, false);
|
||||||
|
temp_l->block = 1;
|
||||||
|
temp_l->event = a.c->proxy_session ? a.c->proxy_session->lobby_event : 0;
|
||||||
|
temp_l->leader_id = a.c->lobby_client_id;
|
||||||
|
temp_l->clients.at(a.c->lobby_client_id) = a.c;
|
||||||
|
send_join_lobby(a.c, temp_l);
|
||||||
|
};
|
||||||
|
|
||||||
size_t index = stoull(a.text, nullptr, 0) - 1;
|
size_t index = stoull(a.text, nullptr, 0) - 1;
|
||||||
if (index >= s->num_backup_character_slots) {
|
if (index >= s->num_backup_character_slots) {
|
||||||
@@ -1568,7 +1593,7 @@ ChatCommandDefinition cc_loadchar(
|
|||||||
throw precondition_failed("Can\'t load character\ndata on this game\nversion");
|
throw precondition_failed("Can\'t load character\ndata on this game\nversion");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto send_set_extended_player_info = [&a, &s]<typename CharT>(const CharT& char_file) -> asio::awaitable<void> {
|
auto send_set_extended_player_info = [&a, &s, &send_proxy_lobby_refresh]<typename CharT>(const CharT& char_file) -> asio::awaitable<void> {
|
||||||
co_await prepare_client_for_patches(a.c);
|
co_await prepare_client_for_patches(a.c);
|
||||||
try {
|
try {
|
||||||
auto fn = s->function_code_index->get_patch("SetExtendedPlayerInfo", a.c->specific_version);
|
auto fn = s->function_code_index->get_patch("SetExtendedPlayerInfo", a.c->specific_version);
|
||||||
@@ -1577,6 +1602,8 @@ ChatCommandDefinition cc_loadchar(
|
|||||||
if (l) {
|
if (l) {
|
||||||
send_player_leave_notification(l, a.c->lobby_client_id);
|
send_player_leave_notification(l, a.c->lobby_client_id);
|
||||||
s->send_lobby_join_notifications(l, a.c);
|
s->send_lobby_join_notifications(l, a.c);
|
||||||
|
} else if (a.c->proxy_session) {
|
||||||
|
send_proxy_lobby_refresh();
|
||||||
}
|
}
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
a.c->log.warning_f("Failed to set extended player info: {}", e.what());
|
a.c->log.warning_f("Failed to set extended player info: {}", e.what());
|
||||||
@@ -1611,12 +1638,16 @@ ChatCommandDefinition cc_loadchar(
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// On v1 and v2, the client will assign its character data from the lobby join command, so it suffices to just
|
// On PC V2 and older DC versions, the client will assign its character data
|
||||||
// resend the join notification.
|
// from the lobby join command, so resend a lobby join notification.
|
||||||
|
if (a.c->proxy_session) {
|
||||||
|
send_proxy_lobby_refresh();
|
||||||
|
} else {
|
||||||
auto s = a.c->require_server_state();
|
auto s = a.c->require_server_state();
|
||||||
send_player_leave_notification(l, a.c->lobby_client_id);
|
send_player_leave_notification(l, a.c->lobby_client_id);
|
||||||
s->send_lobby_join_notifications(l, a.c);
|
s->send_lobby_join_notifications(l, a.c);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
co_return;
|
co_return;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -40,9 +40,11 @@ struct ChoiceSearchConfigT {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(ChoiceSearchConfigT, 0x18);
|
} __attribute__((packed));
|
||||||
using ChoiceSearchConfig = ChoiceSearchConfigT<false>;
|
using ChoiceSearchConfig = ChoiceSearchConfigT<false>;
|
||||||
using ChoiceSearchConfigBE = ChoiceSearchConfigT<true>;
|
using ChoiceSearchConfigBE = ChoiceSearchConfigT<true>;
|
||||||
|
check_struct_size(ChoiceSearchConfig, 0x18);
|
||||||
|
check_struct_size(ChoiceSearchConfigBE, 0x18);
|
||||||
|
|
||||||
struct ChoiceSearchCategory {
|
struct ChoiceSearchCategory {
|
||||||
struct Choice {
|
struct Choice {
|
||||||
|
|||||||
+1
-1
@@ -158,7 +158,7 @@ public:
|
|||||||
std::weak_ptr<Lobby> lobby;
|
std::weak_ptr<Lobby> lobby;
|
||||||
uint8_t lobby_client_id = 0;
|
uint8_t lobby_client_id = 0;
|
||||||
uint8_t lobby_arrow_color = 0;
|
uint8_t lobby_arrow_color = 0;
|
||||||
int64_t preferred_lobby_id = -1; // <0 = none chosen
|
int64_t preferred_lobby_id = -1; // <0 = no preference
|
||||||
|
|
||||||
asio::steady_timer save_game_data_timer;
|
asio::steady_timer save_game_data_timer;
|
||||||
asio::steady_timer send_ping_timer;
|
asio::steady_timer send_ping_timer;
|
||||||
|
|||||||
+23
-11
@@ -251,13 +251,14 @@ struct S_StartFileDownloads_Patch_11 {
|
|||||||
// 14 (S->C): Reconnect
|
// 14 (S->C): Reconnect
|
||||||
// Same format and usage as command 19 on the game server (described below), except the port field is big-endian.
|
// Same format and usage as command 19 on the game server (described below), except the port field is big-endian.
|
||||||
|
|
||||||
template <bool BE>
|
template <typename PortT>
|
||||||
struct S_ReconnectT {
|
struct S_ReconnectT {
|
||||||
be_uint32_t address = 0;
|
be_uint32_t address = 0;
|
||||||
U16T<BE> port = 0;
|
PortT port = 0;
|
||||||
le_uint16_t unused = 0;
|
le_uint16_t unused = 0;
|
||||||
} __packed_ws_be__(S_ReconnectT, 0x08);
|
} __attribute__((packed));
|
||||||
using S_Reconnect_Patch_14 = S_ReconnectT<false>;
|
using S_Reconnect_Patch_14 = S_ReconnectT<be_uint16_t>;
|
||||||
|
check_struct_size(S_Reconnect_Patch_14, 0x08);
|
||||||
|
|
||||||
// 15 (S->C): Login failure
|
// 15 (S->C): Login failure
|
||||||
// No arguments. The client shows a message like "Incorrect game ID or password" and disconnects.
|
// No arguments. The client shows a message like "Incorrect game ID or password" and disconnects.
|
||||||
@@ -723,7 +724,8 @@ struct C_WriteFileConfirmation_V3_BB_13_A7 {
|
|||||||
// connection; the server should send an appropriate command to enable it when the client connects. PSO Xbox seems to
|
// connection; the server should send an appropriate command to enable it when the client connects. PSO Xbox seems to
|
||||||
// ignore the address field, which makes sense given its networking architecture.
|
// ignore the address field, which makes sense given its networking architecture.
|
||||||
|
|
||||||
using S_Reconnect_19 = S_ReconnectT<false>;
|
using S_Reconnect_19 = S_ReconnectT<le_uint16_t>;
|
||||||
|
check_struct_size(S_Reconnect_19, 8);
|
||||||
|
|
||||||
// Sylverant implements an IPv6 version of this command, but it's not obvious why. IPv6 technically did exist as a
|
// Sylverant implements an IPv6 version of this command, but it's not obvious why. IPv6 technically did exist as a
|
||||||
// draft standard at the time of PSO's development, but it wasn't widely used until over a decade later. IPv6 support
|
// draft standard at the time of PSO's development, but it wasn't widely used until over a decade later. IPv6 support
|
||||||
@@ -3765,10 +3767,12 @@ struct G_UpdateEnemyStateT_6x0A {
|
|||||||
// 40000000 = entity is object (some entities have both this and 20000000 set; this appears to make TWindowLockOn
|
// 40000000 = entity is object (some entities have both this and 20000000 set; this appears to make TWindowLockOn
|
||||||
// not show anything but the entity is still attackable, see TWindowLockOn_should_show_for_entity)
|
// not show anything but the entity is still attackable, see TWindowLockOn_should_show_for_entity)
|
||||||
// 80000000 = entity is item
|
// 80000000 = entity is item
|
||||||
U32T<BE> game_flags = 0;
|
typename std::conditional_t<BE, be_uint32_t, le_uint32_t> game_flags = 0;
|
||||||
} __packed_ws_be__(G_UpdateEnemyStateT_6x0A, 0x0C);
|
} __attribute__((packed));
|
||||||
using G_UpdateEnemyState_GC_6x0A = G_UpdateEnemyStateT_6x0A<true>;
|
using G_UpdateEnemyState_GC_6x0A = G_UpdateEnemyStateT_6x0A<true>;
|
||||||
using G_UpdateEnemyState_DC_PC_XB_BB_6x0A = G_UpdateEnemyStateT_6x0A<false>;
|
using G_UpdateEnemyState_DC_PC_XB_BB_6x0A = G_UpdateEnemyStateT_6x0A<false>;
|
||||||
|
check_struct_size(G_UpdateEnemyState_GC_6x0A, 0x0C);
|
||||||
|
check_struct_size(G_UpdateEnemyState_DC_PC_XB_BB_6x0A, 0x0C);
|
||||||
|
|
||||||
// 6x0B: Update object state
|
// 6x0B: Update object state
|
||||||
|
|
||||||
@@ -3834,9 +3838,11 @@ struct G_DragonBossActionsT_6x12 {
|
|||||||
le_uint32_t target_client_id = 0xFFFF; // 0xFFFF (not 0xFFFFFFFF) means no target
|
le_uint32_t target_client_id = 0xFFFF; // 0xFFFF (not 0xFFFFFFFF) means no target
|
||||||
F32T<BE> x = 0.0f;
|
F32T<BE> x = 0.0f;
|
||||||
F32T<BE> z = 0.0f;
|
F32T<BE> z = 0.0f;
|
||||||
} __packed_ws_be__(G_DragonBossActionsT_6x12, 0x14);
|
} __attribute__((packed));
|
||||||
using G_DragonBossActions_DC_PC_XB_BB_6x12 = G_DragonBossActionsT_6x12<false>;
|
using G_DragonBossActions_DC_PC_XB_BB_6x12 = G_DragonBossActionsT_6x12<false>;
|
||||||
using G_DragonBossActions_GC_6x12 = G_DragonBossActionsT_6x12<true>;
|
using G_DragonBossActions_GC_6x12 = G_DragonBossActionsT_6x12<true>;
|
||||||
|
check_struct_size(G_DragonBossActions_DC_PC_XB_BB_6x12, 0x14);
|
||||||
|
check_struct_size(G_DragonBossActions_GC_6x12, 0x14);
|
||||||
|
|
||||||
// 6x13: De Rol Le boss actions (not valid on Episode 3)
|
// 6x13: De Rol Le boss actions (not valid on Episode 3)
|
||||||
|
|
||||||
@@ -4869,9 +4875,11 @@ struct G_WordSelectT_6x74 {
|
|||||||
uint8_t size = 0;
|
uint8_t size = 0;
|
||||||
U16T<BE> client_id = 0;
|
U16T<BE> client_id = 0;
|
||||||
WordSelectMessage message;
|
WordSelectMessage message;
|
||||||
} __packed_ws_be__(G_WordSelectT_6x74, 0x20);
|
} __attribute__((packed));
|
||||||
using G_WordSelect_6x74 = G_WordSelectT_6x74<false>;
|
using G_WordSelect_6x74 = G_WordSelectT_6x74<false>;
|
||||||
using G_WordSelectBE_6x74 = G_WordSelectT_6x74<true>;
|
using G_WordSelectBE_6x74 = G_WordSelectT_6x74<true>;
|
||||||
|
check_struct_size(G_WordSelect_6x74, 0x20);
|
||||||
|
check_struct_size(G_WordSelectBE_6x74, 0x20);
|
||||||
|
|
||||||
// 6x75: Update quest flag
|
// 6x75: Update quest flag
|
||||||
// This command does nothing on Episode 3.
|
// This command does nothing on Episode 3.
|
||||||
@@ -4999,9 +5007,11 @@ struct G_BattleScoresT_6x7F {
|
|||||||
} __packed_ws__(Entry, 8);
|
} __packed_ws__(Entry, 8);
|
||||||
G_UnusedHeader header;
|
G_UnusedHeader header;
|
||||||
parray<Entry, 4> entries;
|
parray<Entry, 4> entries;
|
||||||
} __packed_ws_be__(G_BattleScoresT_6x7F, 0x24);
|
} __attribute__((packed));
|
||||||
using G_BattleScores_6x7F = G_BattleScoresT_6x7F<false>;
|
using G_BattleScores_6x7F = G_BattleScoresT_6x7F<false>;
|
||||||
using G_BattleScoresBE_6x7F = G_BattleScoresT_6x7F<true>;
|
using G_BattleScoresBE_6x7F = G_BattleScoresT_6x7F<true>;
|
||||||
|
check_struct_size(G_BattleScores_6x7F, 0x24);
|
||||||
|
check_struct_size(G_BattleScoresBE_6x7F, 0x24);
|
||||||
|
|
||||||
// 6x80: Trigger trap (not valid on Episode 3)
|
// 6x80: Trigger trap (not valid on Episode 3)
|
||||||
|
|
||||||
@@ -5360,9 +5370,11 @@ struct G_GolDragonBossActionsT_6xA8 {
|
|||||||
F32T<BE> z = 0.0f;
|
F32T<BE> z = 0.0f;
|
||||||
uint8_t unknown_a5 = 0;
|
uint8_t unknown_a5 = 0;
|
||||||
parray<uint8_t, 3> unused;
|
parray<uint8_t, 3> unused;
|
||||||
} __packed_ws_be__(G_GolDragonBossActionsT_6xA8, 0x18);
|
} __attribute__((packed));
|
||||||
using G_GolDragonBossActions_XB_BB_6xA8 = G_GolDragonBossActionsT_6xA8<false>;
|
using G_GolDragonBossActions_XB_BB_6xA8 = G_GolDragonBossActionsT_6xA8<false>;
|
||||||
using G_GolDragonBossActions_GC_6xA8 = G_GolDragonBossActionsT_6xA8<true>;
|
using G_GolDragonBossActions_GC_6xA8 = G_GolDragonBossActionsT_6xA8<true>;
|
||||||
|
check_struct_size(G_GolDragonBossActions_XB_BB_6xA8, 0x18);
|
||||||
|
check_struct_size(G_GolDragonBossActions_GC_6xA8, 0x18);
|
||||||
|
|
||||||
// 6xA9: Barba Ray boss actions (not valid on pre-V3 or Episode 3)
|
// 6xA9: Barba Ray boss actions (not valid on pre-V3 or Episode 3)
|
||||||
|
|
||||||
|
|||||||
@@ -131,9 +131,11 @@ struct ArrayRefT {
|
|||||||
/* 00 */ U32T<BE> count;
|
/* 00 */ U32T<BE> count;
|
||||||
/* 04 */ U32T<BE> offset;
|
/* 04 */ U32T<BE> offset;
|
||||||
/* 08 */
|
/* 08 */
|
||||||
} __packed_ws_be__(ArrayRefT, 8);
|
} __attribute__((packed));
|
||||||
using ArrayRef = ArrayRefT<false>;
|
using ArrayRef = ArrayRefT<false>;
|
||||||
using ArrayRefBE = ArrayRefT<true>;
|
using ArrayRefBE = ArrayRefT<true>;
|
||||||
|
check_struct_size(ArrayRef, 8);
|
||||||
|
check_struct_size(ArrayRefBE, 8);
|
||||||
|
|
||||||
template <bool BE>
|
template <bool BE>
|
||||||
struct RELFileFooterT {
|
struct RELFileFooterT {
|
||||||
@@ -157,6 +159,8 @@ struct RELFileFooterT {
|
|||||||
parray<U32T<BE>, 2> unused1;
|
parray<U32T<BE>, 2> unused1;
|
||||||
U32T<BE> root_offset = 0;
|
U32T<BE> root_offset = 0;
|
||||||
parray<U32T<BE>, 3> unused2;
|
parray<U32T<BE>, 3> unused2;
|
||||||
} __packed_ws_be__(RELFileFooterT, 0x20);
|
} __attribute__((packed));
|
||||||
using RELFileFooter = RELFileFooterT<false>;
|
using RELFileFooter = RELFileFooterT<false>;
|
||||||
using RELFileFooterBE = RELFileFooterT<true>;
|
using RELFileFooterBE = RELFileFooterT<true>;
|
||||||
|
check_struct_size(RELFileFooter, 0x20);
|
||||||
|
check_struct_size(RELFileFooterBE, 0x20);
|
||||||
|
|||||||
@@ -244,9 +244,11 @@ public:
|
|||||||
/* 50 */ U32T<BE> box_item_class_prob_table_offset;
|
/* 50 */ U32T<BE> box_item_class_prob_table_offset;
|
||||||
|
|
||||||
// There are several unused fields here.
|
// There are several unused fields here.
|
||||||
} __packed_ws_be__(OffsetsT, 0x54);
|
} __attribute__((packed));
|
||||||
using Offsets = OffsetsT<false>;
|
using Offsets = OffsetsT<false>;
|
||||||
using OffsetsBE = OffsetsT<true>;
|
using OffsetsBE = OffsetsT<true>;
|
||||||
|
check_struct_size(Offsets, 0x54);
|
||||||
|
check_struct_size(OffsetsBE, 0x54);
|
||||||
};
|
};
|
||||||
|
|
||||||
bool operator==(const CommonItemSet& other) const = default;
|
bool operator==(const CommonItemSet& other) const = default;
|
||||||
@@ -335,6 +337,7 @@ public:
|
|||||||
ValueT value;
|
ValueT value;
|
||||||
WeightT weight;
|
WeightT weight;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
using WeightTableEntry8 = WeightTableEntry<uint8_t>;
|
using WeightTableEntry8 = WeightTableEntry<uint8_t>;
|
||||||
using WeightTableEntry32 = WeightTableEntry<be_uint32_t>;
|
using WeightTableEntry32 = WeightTableEntry<be_uint32_t>;
|
||||||
check_struct_size(WeightTableEntry8, 2);
|
check_struct_size(WeightTableEntry8, 2);
|
||||||
|
|||||||
+4
-1
@@ -15,9 +15,12 @@ struct GSLHeaderEntryT {
|
|||||||
U32T<BE> offset; // In pages, so actual offset is this * 0x800
|
U32T<BE> offset; // In pages, so actual offset is this * 0x800
|
||||||
U32T<BE> size;
|
U32T<BE> size;
|
||||||
uint64_t unused;
|
uint64_t unused;
|
||||||
} __packed_ws_be__(GSLHeaderEntryT, 0x30);
|
} __attribute__((packed));
|
||||||
|
|
||||||
using GSLHeaderEntry = GSLHeaderEntryT<false>;
|
using GSLHeaderEntry = GSLHeaderEntryT<false>;
|
||||||
using GSLHeaderEntryBE = GSLHeaderEntryT<true>;
|
using GSLHeaderEntryBE = GSLHeaderEntryT<true>;
|
||||||
|
check_struct_size(GSLHeaderEntry, 0x30);
|
||||||
|
check_struct_size(GSLHeaderEntryBE, 0x30);
|
||||||
|
|
||||||
template <bool BE>
|
template <bool BE>
|
||||||
void GSLArchive::load_t() {
|
void GSLArchive::load_t() {
|
||||||
|
|||||||
+1
-1
@@ -835,7 +835,7 @@ uint8_t ItemCreator::choose_weapon_special(uint8_t det) {
|
|||||||
uint8_t det2 = this->rand_int(maxes[det]);
|
uint8_t det2 = this->rand_int(maxes[det]);
|
||||||
this->log.info_f("Choosing special with det {:02X} and det2 {:02X}", det, det2);
|
this->log.info_f("Choosing special with det {:02X} and det2 {:02X}", det, det2);
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
for (size_t z = 1; z < this->item_parameter_table->num_specials(); z++) {
|
for (size_t z = 1; z < this->item_parameter_table->num_specials; z++) {
|
||||||
if (det + 1 == this->item_parameter_table->get_special_stars(z)) {
|
if (det + 1 == this->item_parameter_table->get_special_stars(z)) {
|
||||||
if (index == det2) {
|
if (index == det2) {
|
||||||
this->log.info_f("Chose special {:02X}", z);
|
this->log.info_f("Chose special {:02X}", z);
|
||||||
|
|||||||
+1
-1
@@ -486,7 +486,7 @@ void ItemData::encode_for_version(Version to_version, shared_ptr<const ItemParam
|
|||||||
if (should_encode_v2_data && (this->data1[1] > 0x26)) {
|
if (should_encode_v2_data && (this->data1[1] > 0x26)) {
|
||||||
if (this->data1[1] < 0x89) {
|
if (this->data1[1] < 0x89) {
|
||||||
this->data1[5] = this->data1[1];
|
this->data1[5] = this->data1[1];
|
||||||
this->data1[1] = item_parameter_table->get_weapon_class(this->data1[1]);
|
this->data1[1] = item_parameter_table->get_weapon_v1_replacement(this->data1[1]);
|
||||||
if (this->data1[1] == 0x00) {
|
if (this->data1[1] == 0x00) {
|
||||||
this->data1[1] = 0x0F;
|
this->data1[1] = 0x0F;
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-37
@@ -787,9 +787,9 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
auto pmt = this->item_parameter_table;
|
auto pmt = this->item_parameter_table;
|
||||||
|
|
||||||
phosg::fwrite_fmt(stream, "WEAPONS\n");
|
phosg::fwrite_fmt(stream, "WEAPONS\n");
|
||||||
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS FLAG ATPLO ATPHI ATPRQ MSTRQ ATARQ -MST- GND PH SP ATA SB(S1:AMT1,S2:AMT2) PJ 1X 1Y 2X 2Y CR --A1-- A4 A5 TB BF CL ST* USL ---DIVISOR--- NAME\n");
|
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS FLAG ATPLO ATPHI ATPRQ MSTRQ ATARQ -MST- GND PH SP ATA SB(S1:AMT1,S2:AMT2) PJ 1X 1Y 2X 2Y CL --A1-- A4 A5 TB BF V1 ST* USL ---DIVISOR--- NAME\n");
|
||||||
for (size_t data1_1 = 0; data1_1 < pmt->num_weapon_classes(); data1_1++) {
|
for (size_t data1_1 = 0; data1_1 < pmt->num_weapon_classes; data1_1++) {
|
||||||
uint8_t weapon_class = pmt->get_weapon_class(data1_1);
|
uint8_t v1_replacement = pmt->get_weapon_v1_replacement(data1_1);
|
||||||
float sale_divisor = pmt->get_sale_divisor(0x00, data1_1);
|
float sale_divisor = pmt->get_sale_divisor(0x00, data1_1);
|
||||||
string divisor_str = std::format("{:g}", sale_divisor);
|
string divisor_str = std::format("{:g}", sale_divisor);
|
||||||
divisor_str.resize(13, ' ');
|
divisor_str.resize(13, ' ');
|
||||||
@@ -797,7 +797,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
size_t data1_2_limit = pmt->num_weapons_in_class(data1_1);
|
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++) {
|
for (size_t data1_2 = 0; data1_2 < data1_2_limit; data1_2++) {
|
||||||
const auto& w = pmt->get_weapon(data1_1, data1_2);
|
const auto& w = pmt->get_weapon(data1_1, data1_2);
|
||||||
uint8_t stars = pmt->get_item_stars(w.id);
|
uint8_t stars = pmt->get_item_stars(w.base.id);
|
||||||
bool is_unsealable = pmt->is_unsealable_item(0x00, data1_1, data1_2);
|
bool is_unsealable = pmt->is_unsealable_item(0x00, data1_1, data1_2);
|
||||||
|
|
||||||
ItemData item;
|
ItemData item;
|
||||||
@@ -810,10 +810,10 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
phosg::fwrite_fmt(stream, " 00{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:04X} {:5} {:5} {:5} {:5} {:5} {:5} {:3} {:02X} {:02X} {:3} {:02X}({:02X}:{:04X},{:02X}:{:04X}) {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}{:02X}{:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:2}* {} {} {}\n",
|
phosg::fwrite_fmt(stream, " 00{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:04X} {:5} {:5} {:5} {:5} {:5} {:5} {:3} {:02X} {:02X} {:3} {:02X}({:02X}:{:04X},{:02X}:{:04X}) {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}{:02X}{:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:2}* {} {} {}\n",
|
||||||
data1_1,
|
data1_1,
|
||||||
data1_2,
|
data1_2,
|
||||||
w.id,
|
w.base.id,
|
||||||
w.type,
|
w.base.type,
|
||||||
w.skin,
|
w.base.skin,
|
||||||
w.team_points,
|
w.base.team_points,
|
||||||
w.class_flags,
|
w.class_flags,
|
||||||
w.atp_min,
|
w.atp_min,
|
||||||
w.atp_max,
|
w.atp_max,
|
||||||
@@ -826,10 +826,10 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
w.special,
|
w.special,
|
||||||
w.ata,
|
w.ata,
|
||||||
w.stat_boost_entry_index,
|
w.stat_boost_entry_index,
|
||||||
stat_boost.stat1,
|
stat_boost.stats[0],
|
||||||
stat_boost.amount1,
|
stat_boost.amounts[0],
|
||||||
stat_boost.stat2,
|
stat_boost.stats[1],
|
||||||
stat_boost.amount2,
|
stat_boost.amounts[1],
|
||||||
w.projectile,
|
w.projectile,
|
||||||
w.trail1_x,
|
w.trail1_x,
|
||||||
w.trail1_y,
|
w.trail1_y,
|
||||||
@@ -843,7 +843,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
w.unknown_a5,
|
w.unknown_a5,
|
||||||
w.tech_boost,
|
w.tech_boost,
|
||||||
w.behavior_flags,
|
w.behavior_flags,
|
||||||
weapon_class,
|
v1_replacement,
|
||||||
stars,
|
stars,
|
||||||
is_unsealable ? "YES" : " no",
|
is_unsealable ? "YES" : " no",
|
||||||
divisor_str,
|
divisor_str,
|
||||||
@@ -861,7 +861,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
size_t data1_2_limit = pmt->num_armors_or_shields_in_class(data1_1);
|
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++) {
|
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);
|
const auto& a = pmt->get_armor_or_shield(data1_1, data1_2);
|
||||||
uint8_t stars = pmt->get_item_stars(a.id);
|
uint8_t stars = pmt->get_item_stars(a.base.id);
|
||||||
|
|
||||||
ItemData item;
|
ItemData item;
|
||||||
item.data1[0] = 0x01;
|
item.data1[0] = 0x01;
|
||||||
@@ -873,10 +873,10 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
phosg::fwrite_fmt(stream, " 01{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:5} {:5} {:02X} {:02X} {:04X} {:3} {:3} {:3} {:3} {:3} {:3} {:3} {:3} {:02X}({:02X}:{:04X},{:02X}:{:04X}) {:02X} {:02X} {:02X} {:2}* {} {}\n",
|
phosg::fwrite_fmt(stream, " 01{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:5} {:5} {:02X} {:02X} {:04X} {:3} {:3} {:3} {:3} {:3} {:3} {:3} {:3} {:02X}({:02X}:{:04X},{:02X}:{:04X}) {:02X} {:02X} {:02X} {:2}* {} {}\n",
|
||||||
data1_1,
|
data1_1,
|
||||||
data1_2,
|
data1_2,
|
||||||
a.id,
|
a.base.id,
|
||||||
a.type,
|
a.base.type,
|
||||||
a.skin,
|
a.base.skin,
|
||||||
a.team_points,
|
a.base.team_points,
|
||||||
a.dfp,
|
a.dfp,
|
||||||
a.evp,
|
a.evp,
|
||||||
a.block_particle,
|
a.block_particle,
|
||||||
@@ -891,10 +891,10 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
a.dfp_range,
|
a.dfp_range,
|
||||||
a.evp_range,
|
a.evp_range,
|
||||||
a.stat_boost_entry_index,
|
a.stat_boost_entry_index,
|
||||||
stat_boost.stat1,
|
stat_boost.stats[0],
|
||||||
stat_boost.amount1,
|
stat_boost.amounts[0],
|
||||||
stat_boost.stat2,
|
stat_boost.stats[1],
|
||||||
stat_boost.amount2,
|
stat_boost.amounts[1],
|
||||||
a.tech_boost,
|
a.tech_boost,
|
||||||
a.flags_type,
|
a.flags_type,
|
||||||
a.unknown_a4,
|
a.unknown_a4,
|
||||||
@@ -914,7 +914,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
size_t data1_2_limit = pmt->num_units();
|
size_t data1_2_limit = pmt->num_units();
|
||||||
for (size_t data1_2 = 0; data1_2 < data1_2_limit; data1_2++) {
|
for (size_t data1_2 = 0; data1_2 < data1_2_limit; data1_2++) {
|
||||||
const auto& u = pmt->get_unit(data1_2);
|
const auto& u = pmt->get_unit(data1_2);
|
||||||
uint8_t stars = pmt->get_item_stars(u.id);
|
uint8_t stars = pmt->get_item_stars(u.base.id);
|
||||||
|
|
||||||
ItemData item;
|
ItemData item;
|
||||||
item.data1[0] = 0x01;
|
item.data1[0] = 0x01;
|
||||||
@@ -924,10 +924,10 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
|
|
||||||
phosg::fwrite_fmt(stream, " 0103{:02X} => {:08X} {:04X} {:04X} {:6} {:04X} {:5} {:6} {:2}* {} {}\n",
|
phosg::fwrite_fmt(stream, " 0103{:02X} => {:08X} {:04X} {:04X} {:6} {:04X} {:5} {:6} {:2}* {} {}\n",
|
||||||
data1_2,
|
data1_2,
|
||||||
u.id,
|
u.base.id,
|
||||||
u.type,
|
u.base.type,
|
||||||
u.skin,
|
u.base.skin,
|
||||||
u.team_points,
|
u.base.team_points,
|
||||||
u.stat,
|
u.stat,
|
||||||
u.stat_amount,
|
u.stat_amount,
|
||||||
u.modifier_amount,
|
u.modifier_amount,
|
||||||
@@ -956,10 +956,10 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
|
|
||||||
phosg::fwrite_fmt(stream, " 02{:02X}00 => {:08X} {:04X} {:04X} {:6} {:04X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:04X} {} {}\n",
|
phosg::fwrite_fmt(stream, " 02{:02X}00 => {:08X} {:04X} {:04X} {:6} {:04X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:04X} {} {}\n",
|
||||||
data1_1,
|
data1_1,
|
||||||
m.id,
|
m.base.id,
|
||||||
m.type,
|
m.base.type,
|
||||||
m.skin,
|
m.base.skin,
|
||||||
m.team_points,
|
m.base.team_points,
|
||||||
m.feed_table,
|
m.feed_table,
|
||||||
m.photon_blast,
|
m.photon_blast,
|
||||||
m.activation,
|
m.activation,
|
||||||
@@ -979,7 +979,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
|
|
||||||
phosg::fwrite_fmt(stream, "TOOLS\n");
|
phosg::fwrite_fmt(stream, "TOOLS\n");
|
||||||
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS COUNT TECH -COST- ITEMFLAG ---DIVISOR--- NAME\n");
|
phosg::fwrite_fmt(stream, " CODE => ---ID--- TYPE SKIN POINTS COUNT TECH -COST- ITEMFLAG ---DIVISOR--- NAME\n");
|
||||||
for (size_t data1_1 = 0; data1_1 < pmt->num_tool_classes(); data1_1++) {
|
for (size_t data1_1 = 0; data1_1 < pmt->num_tool_classes; data1_1++) {
|
||||||
float sale_divisor = pmt->get_sale_divisor(0x03, data1_1);
|
float sale_divisor = pmt->get_sale_divisor(0x03, data1_1);
|
||||||
string divisor_str = std::format("{:g}", sale_divisor);
|
string divisor_str = std::format("{:g}", sale_divisor);
|
||||||
divisor_str.resize(13, ' ');
|
divisor_str.resize(13, ' ');
|
||||||
@@ -998,10 +998,10 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
phosg::fwrite_fmt(stream, " 03{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:5} {:04X} {:6} {:08X} {} {}\n",
|
phosg::fwrite_fmt(stream, " 03{:02X}{:02X} => {:08X} {:04X} {:04X} {:6} {:5} {:04X} {:6} {:08X} {} {}\n",
|
||||||
data1_1,
|
data1_1,
|
||||||
data1_2,
|
data1_2,
|
||||||
t.id,
|
t.base.id,
|
||||||
t.type,
|
t.base.type,
|
||||||
t.skin,
|
t.base.skin,
|
||||||
t.team_points,
|
t.base.team_points,
|
||||||
t.amount,
|
t.amount,
|
||||||
t.tech,
|
t.tech,
|
||||||
t.cost,
|
t.cost,
|
||||||
@@ -1041,7 +1041,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
|
|||||||
|
|
||||||
phosg::fwrite_fmt(stream, "SPECIAL DEFINITIONS\n");
|
phosg::fwrite_fmt(stream, "SPECIAL DEFINITIONS\n");
|
||||||
phosg::fwrite_fmt(stream, " SPECIAL => TYPE COUNT ST* NAME\n");
|
phosg::fwrite_fmt(stream, " SPECIAL => TYPE COUNT ST* NAME\n");
|
||||||
for (size_t index = 0; index < pmt->num_specials(); index++) {
|
for (size_t index = 0; index < pmt->num_specials; index++) {
|
||||||
const auto& sp = pmt->get_special(index);
|
const auto& sp = pmt->get_special(index);
|
||||||
uint8_t stars = pmt->get_special_stars(index);
|
uint8_t stars = pmt->get_special_stars(index);
|
||||||
const char* name = "";
|
const char* name = "";
|
||||||
|
|||||||
+1051
-1113
File diff suppressed because it is too large
Load Diff
+596
-200
@@ -13,7 +13,6 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "CommonFileFormats.hh"
|
|
||||||
#include "ItemData.hh"
|
#include "ItemData.hh"
|
||||||
#include "Text.hh"
|
#include "Text.hh"
|
||||||
#include "Types.hh"
|
#include "Types.hh"
|
||||||
@@ -41,113 +40,310 @@ const char* phosg::name_for_enum<ServerDropMode>(ServerDropMode value);
|
|||||||
|
|
||||||
class ItemParameterTable {
|
class ItemParameterTable {
|
||||||
public:
|
public:
|
||||||
// These structures are all parsed representations of the file's data. The file's actual structures are defined in
|
// TODO: This implementation is ugly. We should use real classes and virtual functions instead of manually branching
|
||||||
// ItemParamterTable.cc, with analogous names to these structures.
|
// on various offset table pointers being null or not in each public function. Rewrite this and make it better.
|
||||||
|
|
||||||
struct ItemBase {
|
template <bool BE>
|
||||||
|
struct ItemBaseV2T {
|
||||||
// id specifies several things; notably, it doubles as the index of the item's name in the text archive (e.g.
|
// id specifies several things; notably, it doubles as the index of the item's name in the text archive (e.g.
|
||||||
// TextEnglish) collection 0.
|
// TextEnglish) collection 0.
|
||||||
uint32_t id = 0xFFFFFFFF;
|
/* 00 */ U32T<BE> id = 0xFFFFFFFF;
|
||||||
uint16_t type = 0; // "Model" in Soly's ItemPMT editor
|
/* 04 */
|
||||||
uint16_t skin = 0; // "Texture" in Soly's ItemPMT editor
|
} __attribute__((packed));
|
||||||
uint32_t team_points = 0;
|
template <bool BE>
|
||||||
};
|
struct ItemBaseV3T : ItemBaseV2T<BE> {
|
||||||
struct Weapon : ItemBase {
|
/* 04 */ U16T<BE> type = 0; // "Model" in Soly's ItemPMT editor
|
||||||
uint16_t class_flags = 0;
|
/* 06 */ U16T<BE> skin = 0; // "Texture" in Soly's ItemPMT editor
|
||||||
uint16_t atp_min = 0;
|
/* 08 */
|
||||||
uint16_t atp_max = 0;
|
} __attribute__((packed));
|
||||||
uint16_t atp_required = 0;
|
template <bool BE>
|
||||||
uint16_t mst_required = 0;
|
struct ItemBaseV4T : ItemBaseV3T<BE> {
|
||||||
uint16_t ata_required = 0;
|
/* 08 */ U32T<BE> team_points = 0;
|
||||||
uint16_t mst = 0;
|
/* 0C */
|
||||||
uint8_t max_grind = 0;
|
} __attribute__((packed));
|
||||||
uint8_t photon = 0;
|
|
||||||
uint8_t special = 0;
|
struct WeaponV4;
|
||||||
uint8_t ata = 0;
|
struct WeaponDCProtos {
|
||||||
uint8_t stat_boost_entry_index = 0; // TODO: This could be larger (16 or 32 bits)
|
/* 00 */ ItemBaseV2T<false> base;
|
||||||
uint8_t projectile = 0;
|
/* 04 */ le_uint16_t class_flags = 0;
|
||||||
int8_t trail1_x = 0;
|
/* 06 */ le_uint16_t atp_min = 0;
|
||||||
int8_t trail1_y = 0;
|
/* 08 */ le_uint16_t atp_max = 0;
|
||||||
int8_t trail2_x = 0;
|
/* 0A */ le_uint16_t atp_required = 0;
|
||||||
int8_t trail2_y = 0;
|
/* 0C */ le_uint16_t mst_required = 0;
|
||||||
int8_t color = 0;
|
/* 0E */ le_uint16_t ata_required = 0;
|
||||||
parray<uint8_t, 3> unknown_a1 = 0;
|
/* 10 */ uint8_t max_grind = 0;
|
||||||
uint8_t unknown_a4 = 0;
|
/* 11 */ uint8_t photon = 0;
|
||||||
uint8_t unknown_a5 = 0;
|
/* 12 */ uint8_t special = 0;
|
||||||
uint8_t tech_boost = 0;
|
/* 13 */ uint8_t ata = 0;
|
||||||
// Bits in behavior_flags:
|
/* 14 */
|
||||||
|
|
||||||
|
WeaponV4 to_v4() const;
|
||||||
|
} __packed_ws__(WeaponDCProtos, 0x14);
|
||||||
|
|
||||||
|
struct WeaponV1V2 {
|
||||||
|
/* 00 */ ItemBaseV2T<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_entry_index = 0; // TODO: This could be larger (16 or 32 bits)
|
||||||
|
/* 15 */ parray<uint8_t, 3> unknown_a9;
|
||||||
|
/* 18 */
|
||||||
|
|
||||||
|
WeaponV4 to_v4() const;
|
||||||
|
} __packed_ws__(WeaponV1V2, 0x18);
|
||||||
|
|
||||||
|
struct WeaponGCNTE {
|
||||||
|
/* 00 */ ItemBaseV3T<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_entry_index = 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 */ parray<uint8_t, 3> unknown_a1 = 0;
|
||||||
|
/* 24 */
|
||||||
|
|
||||||
|
WeaponV4 to_v4() const;
|
||||||
|
} __packed_ws__(WeaponGCNTE, 0x24);
|
||||||
|
|
||||||
|
template <bool BE>
|
||||||
|
struct WeaponV3T {
|
||||||
|
/* 00 */ ItemBaseV3T<BE> base;
|
||||||
|
/* 08 */ U16T<BE> class_flags = 0;
|
||||||
|
/* 0A */ U16T<BE> atp_min = 0;
|
||||||
|
/* 0C */ U16T<BE> atp_max = 0;
|
||||||
|
/* 0E */ U16T<BE> atp_required = 0;
|
||||||
|
/* 10 */ U16T<BE> mst_required = 0;
|
||||||
|
/* 12 */ U16T<BE> ata_required = 0;
|
||||||
|
/* 14 */ U16T<BE> 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_entry_index = 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 */ parray<uint8_t, 3> unknown_a1 = 0;
|
||||||
|
/* 24 */ uint8_t unknown_a4 = 0;
|
||||||
|
/* 25 */ uint8_t unknown_a5 = 0;
|
||||||
|
/* 26 */ uint8_t tech_boost = 0;
|
||||||
|
// Flags in this field:
|
||||||
// 01 = disable combos (weapon can only be used once in a row)
|
// 01 = disable combos (weapon can only be used once in a row)
|
||||||
// 02 = TODO (sets TItemWeapon flag 40000; used in TItemWeapon_v1E)
|
// 02 = TODO (sets TItemWeapon flag 40000; used in TItemWeapon_v1E)
|
||||||
// 04 = TODO (sets TItemWeapon flag 80000; used in TItemWeapon_v1E)
|
// 04 = TODO (sets TItemWeapon flag 80000; used in TItemWeapon_v1E)
|
||||||
// 08 = weapon cannot have attributes (they are ignored if present)
|
// 08 = weapon cannot have attributes (they are ignored if present)
|
||||||
uint8_t behavior_flags = 0;
|
/* 27 */ uint8_t behavior_flags = 0;
|
||||||
};
|
/* 28 */
|
||||||
|
|
||||||
struct ArmorOrShield : ItemBase {
|
WeaponV4 to_v4() const;
|
||||||
uint16_t dfp = 0;
|
} __attribute__((packed));
|
||||||
uint16_t evp = 0;
|
|
||||||
uint8_t block_particle = 0;
|
using WeaponV3 = WeaponV3T<false>;
|
||||||
uint8_t block_effect = 0;
|
using WeaponV3BE = WeaponV3T<true>;
|
||||||
uint16_t class_flags = 0x00FF;
|
check_struct_size(WeaponV3, 0x28);
|
||||||
uint8_t required_level = 0;
|
check_struct_size(WeaponV3BE, 0x28);
|
||||||
uint8_t efr = 0;
|
|
||||||
uint8_t eth = 0;
|
struct WeaponV4 {
|
||||||
uint8_t eic = 0;
|
/* 00 */ ItemBaseV4T<false> base;
|
||||||
uint8_t edk = 0;
|
/* 0C */ le_uint16_t class_flags = 0x00FF;
|
||||||
uint8_t elt = 0;
|
/* 0E */ le_uint16_t atp_min = 0;
|
||||||
uint8_t dfp_range = 0;
|
/* 10 */ le_uint16_t atp_max = 0;
|
||||||
uint8_t evp_range = 0;
|
/* 12 */ le_uint16_t atp_required = 0;
|
||||||
uint8_t stat_boost_entry_index = 0;
|
/* 14 */ le_uint16_t mst_required = 0;
|
||||||
uint8_t tech_boost = 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_entry_index = 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 */ parray<uint8_t, 3> unknown_a1 = 0;
|
||||||
|
/* 28 */ uint8_t unknown_a4 = 0;
|
||||||
|
/* 29 */ uint8_t unknown_a5 = 0;
|
||||||
|
/* 2A */ uint8_t tech_boost = 0;
|
||||||
|
/* 2B */ uint8_t behavior_flags = 0;
|
||||||
|
/* 2C */
|
||||||
|
} __packed_ws__(WeaponV4, 0x2C);
|
||||||
|
|
||||||
|
template <typename BaseT, bool BE>
|
||||||
|
struct ArmorOrShieldT {
|
||||||
|
/* V1/V2 offsets */
|
||||||
|
/* 00 */ BaseT base;
|
||||||
|
/* 04 */ U16T<BE> dfp = 0;
|
||||||
|
/* 06 */ U16T<BE> evp = 0;
|
||||||
|
/* 08 */ uint8_t block_particle = 0;
|
||||||
|
/* 09 */ uint8_t block_effect = 0;
|
||||||
|
/* 0A */ U16T<BE> 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));
|
||||||
|
|
||||||
|
template <typename BaseT, bool BE>
|
||||||
|
struct ArmorOrShieldFinalT : ArmorOrShieldT<BaseT, BE> {
|
||||||
|
/* 14 */ uint8_t stat_boost_entry_index = 0;
|
||||||
|
/* 15 */ uint8_t tech_boost = 0;
|
||||||
// TODO: Figure out what this does. Only a few values appear to do anything:
|
// TODO: Figure out what this does. Only a few values appear to do anything:
|
||||||
// Armors:
|
|
||||||
// 01 sets item->flags |= 1 (used in TItemProArmor_v10)
|
|
||||||
// 02 constructs TItemProArmorParticle instead of TItemProArmor
|
|
||||||
// Shields:
|
// Shields:
|
||||||
// 01 sets item->flags |= 4 (used in TItemProShield_v10)
|
// 01 sets item->flags |= 4 (used in TItemProShield_v10)
|
||||||
// 03 sets item->flags |= 8 (used in TItemProShield_v1A)
|
// 03 sets item->flags |= 8 (used in TItemProShield_v1A)
|
||||||
uint8_t flags_type = 0;
|
// Armors:
|
||||||
uint8_t unknown_a4 = 0;
|
// 01 sets item->flags |= 1 (used in TItemProArmor_v10)
|
||||||
};
|
// 02 constructs TItemProArmorParticle instead of TItemProArmor
|
||||||
|
/* 16 */ uint8_t flags_type = 0;
|
||||||
|
/* 17 */ uint8_t unknown_a4 = 0;
|
||||||
|
/* 18 */
|
||||||
|
} __attribute__((packed));
|
||||||
|
using ArmorOrShieldV4 = ArmorOrShieldFinalT<ItemBaseV4T<false>, false>;
|
||||||
|
check_struct_size(ArmorOrShieldV4, 0x20);
|
||||||
|
struct ArmorOrShieldDCProtos : ArmorOrShieldT<ItemBaseV2T<false>, false> {
|
||||||
|
ArmorOrShieldV4 to_v4() const;
|
||||||
|
} __packed_ws__(ArmorOrShieldDCProtos, 0x14);
|
||||||
|
|
||||||
struct Unit : ItemBase {
|
struct ArmorOrShieldV1V2 : ArmorOrShieldFinalT<ItemBaseV2T<false>, false> {
|
||||||
uint16_t stat = 0;
|
ArmorOrShieldV4 to_v4() const;
|
||||||
uint16_t stat_amount = 0;
|
} __packed_ws__(ArmorOrShieldV1V2, 0x18);
|
||||||
int16_t modifier_amount = 0;
|
template <bool BE>
|
||||||
};
|
struct ArmorOrShieldV3T : ArmorOrShieldFinalT<ItemBaseV3T<BE>, BE> {
|
||||||
|
ArmorOrShieldV4 to_v4() const;
|
||||||
|
} __attribute__((packed));
|
||||||
|
using ArmorOrShieldV3 = ArmorOrShieldV3T<false>;
|
||||||
|
using ArmorOrShieldV3BE = ArmorOrShieldV3T<true>;
|
||||||
|
check_struct_size(ArmorOrShieldV3, 0x1C);
|
||||||
|
check_struct_size(ArmorOrShieldV3BE, 0x1C);
|
||||||
|
|
||||||
struct Mag : ItemBase {
|
template <typename BaseT, bool BE>
|
||||||
uint16_t feed_table = 0;
|
struct UnitT {
|
||||||
uint8_t photon_blast = 0;
|
/* V1/V2 offsets */
|
||||||
uint8_t activation = 0;
|
/* 00 */ BaseT base;
|
||||||
uint8_t on_pb_full = 0;
|
/* 04 */ U16T<BE> stat = 0;
|
||||||
uint8_t on_low_hp = 0;
|
/* 06 */ U16T<BE> stat_amount = 0;
|
||||||
uint8_t on_death = 0;
|
/* 08 */
|
||||||
uint8_t on_boss = 0;
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
template <typename BaseT, bool BE>
|
||||||
|
struct UnitFinalT : UnitT<BaseT, BE> {
|
||||||
|
/* 08 */ S16T<BE> modifier_amount = 0;
|
||||||
|
/* 0A */ parray<uint8_t, 2> unused;
|
||||||
|
/* 0C */
|
||||||
|
} __attribute__((packed));
|
||||||
|
using UnitV4 = UnitFinalT<ItemBaseV4T<false>, false>;
|
||||||
|
check_struct_size(UnitV4, 0x14);
|
||||||
|
struct UnitDCProtos : UnitT<ItemBaseV2T<false>, false> {
|
||||||
|
UnitV4 to_v4() const;
|
||||||
|
} __packed_ws__(UnitDCProtos, 0x08);
|
||||||
|
struct UnitV1V2 : UnitFinalT<ItemBaseV2T<false>, false> {
|
||||||
|
UnitV4 to_v4() const;
|
||||||
|
} __packed_ws__(UnitV1V2, 0x0C);
|
||||||
|
template <bool BE>
|
||||||
|
struct UnitV3T : UnitFinalT<ItemBaseV3T<BE>, BE> {
|
||||||
|
UnitV4 to_v4() const;
|
||||||
|
} __attribute__((packed));
|
||||||
|
using UnitV3 = UnitV3T<false>;
|
||||||
|
using UnitV3BE = UnitV3T<true>;
|
||||||
|
check_struct_size(UnitV3, 0x10);
|
||||||
|
check_struct_size(UnitV3BE, 0x10);
|
||||||
|
|
||||||
|
template <typename BaseT, bool BE>
|
||||||
|
struct MagT {
|
||||||
|
/* V1/V2 offsets */
|
||||||
|
/* 00 */ BaseT base;
|
||||||
|
/* 04 */ U16T<BE> 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:
|
// These flags control how likely each effect is to activate. First, the game computes step_synchro as follows:
|
||||||
// If synchro is in [0, 30], set step_synchro = 0
|
// if synchro in [0, 30], step_synchro = 0
|
||||||
// If synchro is in [31, 60], set step_synchro = 15
|
// if synchro in [31, 60], step_synchro = 15
|
||||||
// If synchro is in [61, 80], set step_synchro = 25
|
// if synchro in [61, 80], step_synchro = 25
|
||||||
// If synchro is in [81, 100], set step_synchro = 30
|
// if synchro in [81, 100], step_synchro = 30
|
||||||
// If synchro is in [101, 120], set step_synchro = 35
|
// if synchro in [101, 120], step_synchro = 35
|
||||||
// Then, the percent chance of the effect occurring upon its trigger (e.g. entering a boss arena) is:
|
// Then, the percent chance of the effect occurring upon its trigger (e.g. entering a boss arena) is:
|
||||||
// flag == 0 => chance is (activation)
|
// flag == 0 => activation
|
||||||
// flag == 1 => chance is (activation + step_synchro)
|
// flag == 1 => activation + step_synchro
|
||||||
// flag == 2 => chance is (step_synchro)
|
// flag == 2 => step_synchro
|
||||||
// flag == 3 => chance is (activation - 10)
|
// flag == 3 => activation - 10
|
||||||
// flag == 4 => chance is (step_synchro - 10)
|
// flag == 4 => step_synchro - 10
|
||||||
// anything else => chance is 0 (effect never occurs)
|
// anything else => 0 (effect never occurs)
|
||||||
uint8_t on_pb_full_flag = 0;
|
/* 0C */ uint8_t on_pb_full_flag = 0;
|
||||||
uint8_t on_low_hp_flag = 0;
|
/* 0D */ uint8_t on_low_hp_flag = 0;
|
||||||
uint8_t on_death_flag = 0;
|
/* 0E */ uint8_t on_death_flag = 0;
|
||||||
uint8_t on_boss_flag = 0;
|
/* 0F */ uint8_t on_boss_flag = 0;
|
||||||
uint16_t class_flags = 0x00FF;
|
/* 10 */
|
||||||
};
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct Tool : ItemBase {
|
struct MagV4 : MagT<ItemBaseV4T<false>, false> {
|
||||||
uint16_t amount = 0;
|
le_uint16_t class_flags = 0x00FF;
|
||||||
uint16_t tech = 0;
|
parray<uint8_t, 2> unused;
|
||||||
int32_t cost = 0;
|
} __packed_ws__(MagV4, 0x1C);
|
||||||
|
struct MagV1 : MagT<ItemBaseV2T<false>, false> {
|
||||||
|
MagV4 to_v4() const;
|
||||||
|
} __packed_ws__(MagV1, 0x10);
|
||||||
|
struct MagV2 : MagT<ItemBaseV2T<false>, false> {
|
||||||
|
/* 10 */ le_uint16_t class_flags = 0x00FF;
|
||||||
|
/* 12 */ parray<uint8_t, 2> unused;
|
||||||
|
/* 14 */
|
||||||
|
|
||||||
|
MagV4 to_v4() const;
|
||||||
|
} __packed_ws__(MagV2, 0x14);
|
||||||
|
template <bool BE>
|
||||||
|
struct MagV3T : MagT<ItemBaseV3T<BE>, BE> {
|
||||||
|
/* 10 */ U16T<BE> class_flags = 0x00FF;
|
||||||
|
/* 12 */ parray<uint8_t, 2> unused;
|
||||||
|
/* 14 */
|
||||||
|
|
||||||
|
MagV4 to_v4() const;
|
||||||
|
} __attribute__((packed));
|
||||||
|
using MagV3 = MagV3T<false>;
|
||||||
|
using MagV3BE = MagV3T<true>;
|
||||||
|
check_struct_size(MagV3, 0x18);
|
||||||
|
check_struct_size(MagV3BE, 0x18);
|
||||||
|
|
||||||
|
template <typename BaseT, bool BE>
|
||||||
|
struct ToolT {
|
||||||
|
/* V1/V2 offsets */
|
||||||
|
/* 00 */ BaseT base;
|
||||||
|
/* 04 */ U16T<BE> amount = 0;
|
||||||
|
/* 06 */ U16T<BE> tech = 0;
|
||||||
|
/* 08 */ S32T<BE> cost = 0;
|
||||||
// Bits in item_flags:
|
// Bits in item_flags:
|
||||||
// 00000001 - ever usable by player ("Use" appears in inventory menu)
|
// 00000001 - ever usable by player ("Use" appears in inventory menu)
|
||||||
// 00000002 - unknown
|
// 00000002 - unknown
|
||||||
@@ -156,9 +352,24 @@ public:
|
|||||||
// 00000010 - usable in Pioneer 2 / Lab
|
// 00000010 - usable in Pioneer 2 / Lab
|
||||||
// 00000020 - usable in boss arenas
|
// 00000020 - usable in boss arenas
|
||||||
// 00000040 - usable in Challenge mode
|
// 00000040 - usable in Challenge mode
|
||||||
// 00000080 - is rare (renders as red box; V3+ only)
|
// 00000080 - is rare (renders as red box; V3+)
|
||||||
/* 0C */ uint32_t item_flags = 0;
|
/* 0C */ U32T<BE> item_flags = 0;
|
||||||
};
|
/* 10 */
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
using ToolV4 = ToolT<ItemBaseV4T<false>, false>;
|
||||||
|
check_struct_size(ToolV4, 0x18);
|
||||||
|
struct ToolV1V2 : ToolT<ItemBaseV2T<false>, false> {
|
||||||
|
ToolV4 to_v4() const;
|
||||||
|
} __packed_ws__(ToolV1V2, 0x10);
|
||||||
|
template <bool BE>
|
||||||
|
struct ToolV3T : ToolT<ItemBaseV3T<BE>, BE> {
|
||||||
|
ToolV4 to_v4() const;
|
||||||
|
} __attribute__((packed));
|
||||||
|
using ToolV3 = ToolV3T<false>;
|
||||||
|
using ToolV3BE = ToolV3T<true>;
|
||||||
|
check_struct_size(ToolV3, 0x14);
|
||||||
|
check_struct_size(ToolV3BE, 0x14);
|
||||||
|
|
||||||
struct MagFeedResult {
|
struct MagFeedResult {
|
||||||
int8_t def = 0;
|
int8_t def = 0;
|
||||||
@@ -170,12 +381,29 @@ public:
|
|||||||
parray<uint8_t, 2> unused;
|
parray<uint8_t, 2> unused;
|
||||||
} __packed_ws__(MagFeedResult, 8);
|
} __packed_ws__(MagFeedResult, 8);
|
||||||
|
|
||||||
struct Special {
|
using MagFeedResultsList = parray<MagFeedResult, 11>;
|
||||||
uint16_t type = 0xFFFF;
|
|
||||||
uint16_t amount = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StatBoost {
|
template <bool BE>
|
||||||
|
struct MagFeedResultsListOffsetsT {
|
||||||
|
parray<U32T<BE>, 8> offsets; // Offsets of MagFeedResultsList objects
|
||||||
|
} __attribute__((packed));
|
||||||
|
using MagFeedResultsListOffsets = MagFeedResultsListOffsetsT<false>;
|
||||||
|
using MagFeedResultsListOffsetsBE = MagFeedResultsListOffsetsT<true>;
|
||||||
|
check_struct_size(MagFeedResultsListOffsets, 0x20);
|
||||||
|
check_struct_size(MagFeedResultsListOffsetsBE, 0x20);
|
||||||
|
|
||||||
|
template <bool BE>
|
||||||
|
struct SpecialT {
|
||||||
|
U16T<BE> type = 0xFFFF;
|
||||||
|
U16T<BE> amount = 0;
|
||||||
|
} __attribute__((packed));
|
||||||
|
using Special = SpecialT<false>;
|
||||||
|
using SpecialBE = SpecialT<true>;
|
||||||
|
check_struct_size(Special, 4);
|
||||||
|
check_struct_size(SpecialBE, 4);
|
||||||
|
|
||||||
|
template <bool BE>
|
||||||
|
struct StatBoostT {
|
||||||
// Only the first of these stat/amount pairs is used in most versions of the game. In DC 11/2000 Sega apparently
|
// Only the first of these stat/amount pairs is used in most versions of the game. In DC 11/2000 Sega apparently
|
||||||
// changed the loop from `for (z = 0; z != 2; z++)` to `for (z = 0; z != 1; z++)`, so only the first stat/amount
|
// changed the loop from `for (z = 0; z != 2; z++)` to `for (z = 0; z != 1; z++)`, so only the first stat/amount
|
||||||
// pair is used on all versions after DC NTE.
|
// pair is used on all versions after DC NTE.
|
||||||
@@ -197,11 +425,13 @@ public:
|
|||||||
// 0F = LCK penalty
|
// 0F = LCK penalty
|
||||||
// 10 = all of the above penalties except HP
|
// 10 = all of the above penalties except HP
|
||||||
// Anything else (including 00) = no bonus or penalty
|
// Anything else (including 00) = no bonus or penalty
|
||||||
uint8_t stat1 = 0;
|
parray<uint8_t, 2> stats = 0;
|
||||||
uint16_t amount1 = 0;
|
parray<U16T<BE>, 2> amounts;
|
||||||
uint8_t stat2 = 0;
|
|
||||||
uint16_t amount2 = 0;
|
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
using StatBoost = StatBoostT<false>;
|
||||||
|
using StatBoostBE = StatBoostT<true>;
|
||||||
|
check_struct_size(StatBoost, 6);
|
||||||
|
check_struct_size(StatBoostBE, 6);
|
||||||
|
|
||||||
// Indexed as [tech_num][char_class]
|
// Indexed as [tech_num][char_class]
|
||||||
using MaxTechniqueLevels = parray<parray<uint8_t, 12>, 19>;
|
using MaxTechniqueLevels = parray<parray<uint8_t, 12>, 19>;
|
||||||
@@ -217,12 +447,19 @@ public:
|
|||||||
parray<uint8_t, 3> unused;
|
parray<uint8_t, 3> unused;
|
||||||
} __packed_ws__(ItemCombination, 0x10);
|
} __packed_ws__(ItemCombination, 0x10);
|
||||||
|
|
||||||
struct TechniqueBoost {
|
template <bool BE>
|
||||||
|
struct TechniqueBoostEntryT {
|
||||||
uint8_t tech_num = 0;
|
uint8_t tech_num = 0;
|
||||||
// It appears that only one bit in the flags field is used: 01 = enable piercing (for Megid)
|
// It appears that only one bit in the flags field is used:
|
||||||
|
// 01 = enable piercing (for Megid)
|
||||||
uint8_t flags = 0;
|
uint8_t flags = 0;
|
||||||
float amount = 0.0f;
|
parray<uint8_t, 2> unused;
|
||||||
};
|
F32T<BE> amount = 0.0f;
|
||||||
|
} __attribute__((packed));
|
||||||
|
using TechniqueBoostEntry = TechniqueBoostEntryT<false>;
|
||||||
|
using TechniqueBoostEntryBE = TechniqueBoostEntryT<true>;
|
||||||
|
check_struct_size(TechniqueBoostEntry, 0x08);
|
||||||
|
check_struct_size(TechniqueBoostEntryBE, 0x08);
|
||||||
|
|
||||||
struct EventItem {
|
struct EventItem {
|
||||||
parray<uint8_t, 3> item;
|
parray<uint8_t, 3> item;
|
||||||
@@ -234,124 +471,283 @@ public:
|
|||||||
uint8_t unused = 0;
|
uint8_t unused = 0;
|
||||||
} __packed_ws__(UnsealableItem, 4);
|
} __packed_ws__(UnsealableItem, 4);
|
||||||
|
|
||||||
struct NonWeaponSaleDivisors {
|
template <bool BE>
|
||||||
float armor_divisor = 0.0f;
|
struct NonWeaponSaleDivisorsT {
|
||||||
float shield_divisor = 0.0f;
|
F32T<BE> armor_divisor = 0.0f;
|
||||||
float unit_divisor = 0.0f;
|
F32T<BE> shield_divisor = 0.0f;
|
||||||
float mag_divisor = 0.0f;
|
F32T<BE> unit_divisor = 0.0f;
|
||||||
};
|
F32T<BE> mag_divisor = 0.0f;
|
||||||
|
} __attribute__((packed));
|
||||||
|
using NonWeaponSaleDivisors = NonWeaponSaleDivisorsT<false>;
|
||||||
|
using NonWeaponSaleDivisorsBE = NonWeaponSaleDivisorsT<true>;
|
||||||
|
check_struct_size(NonWeaponSaleDivisors, 0x10);
|
||||||
|
check_struct_size(NonWeaponSaleDivisorsBE, 0x10);
|
||||||
|
|
||||||
struct ShieldEffect {
|
ItemParameterTable(std::shared_ptr<const std::string> data, Version version);
|
||||||
uint32_t sound_id;
|
~ItemParameterTable() = default;
|
||||||
uint32_t unknown_a1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PhotonColorEntry {
|
|
||||||
uint32_t unknown_a1;
|
|
||||||
VectorXYZTF unknown_a2;
|
|
||||||
VectorXYZTF unknown_a3;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UnknownA1 {
|
|
||||||
uint16_t unknown_a1;
|
|
||||||
uint16_t unknown_a2;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UnknownA5 {
|
|
||||||
uint32_t target_param; // For players, char_class; for enemies, rt_index; for objects, 0x30
|
|
||||||
uint32_t unknown_a2;
|
|
||||||
uint32_t unknown_a3;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WeaponEffect {
|
|
||||||
uint32_t sound_id1;
|
|
||||||
uint32_t eff_value1;
|
|
||||||
uint32_t sound_id2;
|
|
||||||
uint32_t eff_value2;
|
|
||||||
parray<uint8_t, 0x10> unknown_a5;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WeaponRange {
|
|
||||||
float unknown_a1;
|
|
||||||
float unknown_a2;
|
|
||||||
uint32_t unknown_a3; // Angle
|
|
||||||
uint32_t unknown_a4; // Angle
|
|
||||||
uint32_t unknown_a5;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RangedSpecial {
|
|
||||||
uint8_t data1_1;
|
|
||||||
uint8_t data1_2;
|
|
||||||
uint8_t weapon_range_index;
|
|
||||||
uint8_t unknown_a1;
|
|
||||||
} __packed_ws__(RangedSpecial, 4);
|
|
||||||
|
|
||||||
ItemParameterTable() = delete;
|
|
||||||
virtual ~ItemParameterTable() = default;
|
|
||||||
|
|
||||||
static std::shared_ptr<ItemParameterTable> create(std::shared_ptr<const std::string> data, Version version);
|
|
||||||
|
|
||||||
std::set<uint32_t> compute_all_valid_primary_identifiers() const;
|
std::set<uint32_t> compute_all_valid_primary_identifiers() const;
|
||||||
|
|
||||||
virtual size_t num_weapon_classes() const = 0;
|
size_t num_weapons_in_class(uint8_t data1_1) const;
|
||||||
virtual size_t num_weapons_in_class(uint8_t data1_1) const = 0;
|
const WeaponV4& get_weapon(uint8_t data1_1, uint8_t data1_2) const;
|
||||||
virtual const Weapon& get_weapon(uint8_t data1_1, uint8_t data1_2) const = 0;
|
size_t num_armors_or_shields_in_class(uint8_t data1_1) const;
|
||||||
virtual size_t num_armors_or_shields_in_class(uint8_t data1_1) const = 0;
|
const ArmorOrShieldV4& get_armor_or_shield(uint8_t data1_1, uint8_t data1_2) const;
|
||||||
virtual const ArmorOrShield& get_armor_or_shield(uint8_t data1_1, uint8_t data1_2) const = 0;
|
size_t num_units() const;
|
||||||
virtual size_t num_units() const = 0;
|
const UnitV4& get_unit(uint8_t data1_2) const;
|
||||||
virtual const Unit& get_unit(uint8_t data1_2) const = 0;
|
size_t num_mags() const;
|
||||||
virtual size_t num_mags() const = 0;
|
const MagV4& get_mag(uint8_t data1_1) const;
|
||||||
virtual const Mag& get_mag(uint8_t data1_1) const = 0;
|
size_t num_tools_in_class(uint8_t data1_1) const;
|
||||||
virtual size_t num_tool_classes() const = 0;
|
const ToolV4& get_tool(uint8_t data1_1, uint8_t data1_2) const;
|
||||||
virtual size_t num_tools_in_class(uint8_t data1_1) const = 0;
|
std::pair<uint8_t, uint8_t> find_tool_by_id(uint32_t id) const;
|
||||||
virtual const Tool& get_tool(uint8_t data1_1, uint8_t data1_2) const = 0;
|
|
||||||
virtual std::pair<uint8_t, uint8_t> find_tool_by_id(uint32_t id) const = 0;
|
|
||||||
|
|
||||||
std::variant<const Weapon*, const ArmorOrShield*, const Unit*, const Mag*, const Tool*>
|
std::variant<const WeaponV4*, const ArmorOrShieldV4*, const UnitV4*, const MagV4*, const ToolV4*>
|
||||||
definition_for_primary_identifier(uint32_t primary_identifier) const;
|
definition_for_primary_identifier(uint32_t primary_identifier) const;
|
||||||
|
|
||||||
virtual float get_sale_divisor(uint8_t data1_0, uint8_t data1_1) const = 0;
|
float get_sale_divisor(uint8_t data1_0, uint8_t data1_1) const;
|
||||||
virtual const MagFeedResult& get_mag_feed_result(uint8_t table_index, uint8_t which) const = 0;
|
const MagFeedResult& get_mag_feed_result(uint8_t table_index, uint8_t which) const;
|
||||||
virtual uint8_t get_item_stars(uint32_t id) const = 0;
|
uint8_t get_item_stars(uint32_t id) const;
|
||||||
virtual uint8_t get_special_stars(uint8_t special) const = 0;
|
uint8_t get_special_stars(uint8_t special) const;
|
||||||
virtual size_t num_specials() const = 0;
|
const Special& get_special(uint8_t special) const;
|
||||||
virtual const Special& get_special(uint8_t special) const = 0;
|
const StatBoost& get_stat_boost(uint8_t entry_index) const;
|
||||||
virtual const StatBoost& get_stat_boost(uint8_t entry_index) const = 0;
|
uint8_t get_max_tech_level(uint8_t char_class, uint8_t tech_num) const;
|
||||||
virtual uint8_t get_max_tech_level(uint8_t char_class, uint8_t tech_num) const = 0;
|
uint8_t get_weapon_v1_replacement(uint8_t data1_1) const;
|
||||||
virtual uint8_t get_weapon_class(uint8_t data1_1) const = 0;
|
|
||||||
|
|
||||||
uint32_t get_item_id(const ItemData& item) const;
|
uint32_t get_item_id(const ItemData& item) const;
|
||||||
uint32_t get_item_team_points(const ItemData& item) const;
|
uint32_t get_item_team_points(const ItemData& item) const;
|
||||||
uint8_t get_item_base_stars(const ItemData& item) const;
|
uint8_t get_item_base_stars(const ItemData& item) const;
|
||||||
uint8_t get_item_adjusted_stars(const ItemData& item, bool ignore_unidentified = false) const;
|
uint8_t get_item_adjusted_stars(const ItemData& item, bool ignore_unidentified = false) const;
|
||||||
bool is_item_rare(const ItemData& item) const;
|
bool is_item_rare(const ItemData& item) const;
|
||||||
virtual bool is_unsealable_item(uint8_t data1_0, uint8_t data1_1, uint8_t data1_2) const = 0;
|
bool is_unsealable_item(uint8_t data1_0, uint8_t data1_1, uint8_t data1_2) const;
|
||||||
bool is_unsealable_item(const ItemData& item) 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 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::vector<ItemCombination>& get_all_combinations_for_used_item(const ItemData& used_item) const;
|
||||||
virtual const std::map<uint32_t, std::vector<ItemCombination>>& get_all_item_combinations() const = 0;
|
const std::map<uint32_t, std::vector<ItemCombination>>& get_all_item_combinations() const;
|
||||||
virtual size_t num_events() const = 0;
|
size_t num_events() const;
|
||||||
virtual std::pair<const EventItem*, size_t> get_event_items(uint8_t event_number) const = 0;
|
std::pair<const EventItem*, size_t> get_event_items(uint8_t event_number) const;
|
||||||
|
|
||||||
size_t price_for_item(const ItemData& item) const;
|
size_t price_for_item(const ItemData& item) const;
|
||||||
|
|
||||||
|
size_t num_weapon_classes;
|
||||||
|
size_t num_tool_classes;
|
||||||
|
size_t item_stars_first_id;
|
||||||
|
size_t item_stars_last_id;
|
||||||
|
size_t special_stars_begin_index;
|
||||||
|
size_t num_specials;
|
||||||
|
size_t first_rare_mag_index;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
struct TableOffsetsDCProtos {
|
||||||
|
/* ## / NTE / 11/2000 */
|
||||||
|
/* 00 / 0013 / 0013 */ le_uint32_t unknown_a0;
|
||||||
|
/* 04 / 2E1C / 2EB8 */ le_uint32_t weapon_table;
|
||||||
|
/* 08 / 2D94 / 2E28 */ le_uint32_t armor_table;
|
||||||
|
/* 0C / 2DA4 / 2E38 */ le_uint32_t unit_table;
|
||||||
|
/* 10 / 2DB4 / 2E48 */ le_uint32_t tool_table;
|
||||||
|
/* 14 / 2DAC / 2E40 */ le_uint32_t mag_table;
|
||||||
|
/* 18 / 1F98 / 202C */ le_uint32_t v1_replacement_table;
|
||||||
|
/* 1C / 1994 / 1A28 */ le_uint32_t photon_color_table;
|
||||||
|
/* 20 / 1C64 / 1CF8 */ le_uint32_t weapon_range_table;
|
||||||
|
/* 24 / 1FBF / 2053 */ le_uint32_t weapon_sale_divisor_table;
|
||||||
|
/* 28 / 1FE6 / 207A */ le_uint32_t sale_divisor_table;
|
||||||
|
/* 2C / 2F54 / 2FF0 */ le_uint32_t mag_feed_table;
|
||||||
|
/* 30 / 22A9 / 233D */ le_uint32_t star_value_table;
|
||||||
|
/* 34 / 23EE / 2484 */ le_uint32_t unknown_a1;
|
||||||
|
/* 38 / 275E / 27F4 */ le_uint32_t special_data_table;
|
||||||
|
/* 3C / 2804 / 2898 */ le_uint32_t stat_boost_table;
|
||||||
|
/* 40 / 1908 / 199C */ le_uint32_t shield_effect_table;
|
||||||
|
/* 44 / 0668 / 0668 */ le_uint32_t unknown_a2;
|
||||||
|
/* 48 / 030C / 030C */ le_uint32_t unknown_a3;
|
||||||
|
/* 4C / 2CE4 / 2D78 */ le_uint32_t unknown_a4;
|
||||||
|
} __packed_ws__(TableOffsetsDCProtos, 0x50);
|
||||||
|
|
||||||
|
struct TableOffsetsV1V2 {
|
||||||
|
// TODO: Is weapon count 0x89 or 0x8A? It could be that the last entry in weapon_table is used for ???? items.
|
||||||
|
/* ## / V1 / V2*/
|
||||||
|
/* 00 / 0013 / 0013 */ le_uint32_t unknown_a0;
|
||||||
|
/* 04 / 32E8 / 5AFC */ le_uint32_t weapon_table; // -> [{count, offset -> [WeaponV2]}](0x89)
|
||||||
|
/* 08 / 3258 / 5A5C */ le_uint32_t armor_table; // -> [{count, offset -> [ArmorOrShieldV2]}](2; armors and shields)
|
||||||
|
/* 0C / 3268 / 5A6C */ le_uint32_t unit_table; // -> {count, offset -> [UnitV2]} (last if out of range)
|
||||||
|
/* 10 / 3278 / 5A7C */ le_uint32_t tool_table; // -> [{count, offset -> [ToolV2]}](0x10) (last if out of range)
|
||||||
|
/* 14 / 3270 / 5A74 */ le_uint32_t mag_table; // -> {count, offset -> [MagV2]}
|
||||||
|
/* 18 / 23C8 / 3DF8 */ le_uint32_t v1_replacement_table; // -> [uint8_t](0x89)
|
||||||
|
/* 1C / 1DB0 / 2E4C */ le_uint32_t photon_color_table; // -> [0x24-byte structs](0x20)
|
||||||
|
/* 20 / 2080 / 32CC */ le_uint32_t weapon_range_table; // -> ???
|
||||||
|
/* 24 / 23F0 / 3E84 */ le_uint32_t weapon_sale_divisor_table; // -> [float](0x89)
|
||||||
|
/* 28 / 248C / 40A8 */ le_uint32_t sale_divisor_table; // -> NonWeaponSaleDivisors
|
||||||
|
/* 2C / 3420 / 5F4C */ le_uint32_t mag_feed_table; // -> MagFeedResultsTable
|
||||||
|
/* 30 / 275C / 4378 */ le_uint32_t star_value_table; // -> [uint8_t](0x1C7)
|
||||||
|
/* 34 / 28A2 / 45E4 */ le_uint32_t unknown_a1;
|
||||||
|
/* 38 / 2C12 / 4540 */ le_uint32_t special_data_table; // -> [Special](0x29)
|
||||||
|
/* 3C / 2CB8 / 58DC */ le_uint32_t stat_boost_table; // -> [StatBoost]
|
||||||
|
/* 40 / 3198 / 5704 */ le_uint32_t shield_effect_table; // -> [8-byte structs]
|
||||||
|
} __packed_ws__(TableOffsetsV1V2, 0x44);
|
||||||
|
|
||||||
|
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; // -> [TechniqueBoostEntry[3]]
|
||||||
|
} __packed_ws__(TableOffsetsGCNTE, 0x50);
|
||||||
|
|
||||||
|
template <bool BE>
|
||||||
|
struct TableOffsetsV3V4T {
|
||||||
|
/* ## / GC / BB */
|
||||||
|
/* 00 / F078 / 14884 */ U32T<BE> weapon_table; // -> [{count, offset -> [WeaponV3/WeaponV4]}](0xED)
|
||||||
|
/* 04 / EF90 / 1478C */ U32T<BE> armor_table; // -> [{count, offset -> [ArmorOrShieldV3/ArmorOrShieldV4]}](2; armors and shields)
|
||||||
|
/* 08 / EFA0 / 1479C */ U32T<BE> unit_table; // -> {count, offset -> [UnitV3/UnitV4]} (last if out of range)
|
||||||
|
/* 0C / EFB0 / 147AC */ U32T<BE> tool_table; // -> [{count, offset -> [ToolV3/ToolV4]}](0x1A) (last if out of range)
|
||||||
|
/* 10 / EFA8 / 147A4 */ U32T<BE> mag_table; // -> {count, offset -> [MagV3/MagV4]}
|
||||||
|
/* 14 / B88C / 0F4B8 */ U32T<BE> v1_replacement_table; // -> [uint8_t](0xED)
|
||||||
|
/* 18 / A7FC / 0DE7C */ U32T<BE> photon_color_table; // -> [0x24-byte structs](0x20)
|
||||||
|
/* 1C / AACC / 0E194 */ U32T<BE> weapon_range_table; // -> ???
|
||||||
|
/* 20 / B938 / 0F5A8 */ U32T<BE> weapon_sale_divisor_table; // -> [float](0xED)
|
||||||
|
/* 24 / BBCC / 0F83C */ U32T<BE> sale_divisor_table; // -> NonWeaponSaleDivisors
|
||||||
|
/* 28 / F608 / 1502C */ U32T<BE> mag_feed_table; // -> MagFeedResultsTable
|
||||||
|
/* 2C / BE9C / 0FB0C */ U32T<BE> star_value_table; // -> [uint8_t](0x330) (indexed by .id from weapon, armor, etc.)
|
||||||
|
/* 30 / C100 / 0FE3C */ U32T<BE> special_data_table; // -> [Special]
|
||||||
|
/* 34 / C1A4 / 0FEE0 */ U32T<BE> weapon_effect_table; // -> [16-byte structs]
|
||||||
|
/* 38 / DE50 / 1275C */ U32T<BE> stat_boost_table; // -> [StatBoost]
|
||||||
|
/* 3C / D6E4 / 11C80 */ U32T<BE> shield_effect_table; // -> [8-byte structs]
|
||||||
|
/* 40 / DF88 / 12894 */ U32T<BE> max_tech_level_table; // -> MaxTechniqueLevels
|
||||||
|
/* 44 / F5D0 / 14FF4 */ U32T<BE> combination_table; // -> {count, offset -> [ItemCombination]}
|
||||||
|
/* 48 / DE48 / 12754 */ U32T<BE> unknown_a1;
|
||||||
|
/* 4C / EB8C / 14278 */ U32T<BE> tech_boost_table; // -> [TechniqueBoost[3]]
|
||||||
|
/* 50 / F5F0 / 15014 */ U32T<BE> unwrap_table; // -> {count, offset -> [{count, offset -> [EventItem]}]}
|
||||||
|
/* 54 / F5F8 / 1501C */ U32T<BE> unsealable_table; // -> {count, offset -> [UnsealableItem]}
|
||||||
|
/* 58 / F600 / 15024 */ U32T<BE> ranged_special_table; // -> {count, offset -> [4-byte structs]}
|
||||||
|
} __attribute__((packed));
|
||||||
|
using TableOffsetsV3V4 = TableOffsetsV3V4T<false>;
|
||||||
|
using TableOffsetsV3V4BE = TableOffsetsV3V4T<true>;
|
||||||
|
check_struct_size(TableOffsetsV3V4, 0x5C);
|
||||||
|
check_struct_size(TableOffsetsV3V4BE, 0x5C);
|
||||||
|
|
||||||
|
Version version;
|
||||||
std::shared_ptr<const std::string> data;
|
std::shared_ptr<const std::string> data;
|
||||||
phosg::StringReader r;
|
phosg::StringReader r;
|
||||||
|
const TableOffsetsDCProtos* offsets_dc_protos;
|
||||||
|
const TableOffsetsV1V2* offsets_v1_v2;
|
||||||
|
const TableOffsetsGCNTE* offsets_gc_nte;
|
||||||
|
const TableOffsetsV3V4* offsets_v3_le;
|
||||||
|
const TableOffsetsV3V4BE* offsets_v3_be;
|
||||||
|
const TableOffsetsV3V4* offsets_v4;
|
||||||
|
|
||||||
mutable std::unordered_map<uint16_t, Weapon> weapons;
|
// These are unused if offsets_v4 is not null (in that case, we just return references to within the data string)
|
||||||
mutable std::vector<ArmorOrShield> armors;
|
mutable std::unordered_map<uint16_t, WeaponV4> parsed_weapons;
|
||||||
mutable std::vector<ArmorOrShield> shields;
|
mutable std::vector<ArmorOrShieldV4> parsed_armors;
|
||||||
mutable std::vector<Unit> units;
|
mutable std::vector<ArmorOrShieldV4> parsed_shields;
|
||||||
mutable std::vector<Mag> mags;
|
mutable std::vector<UnitV4> parsed_units;
|
||||||
mutable std::unordered_map<uint16_t, Tool> tools;
|
mutable std::vector<MagV4> parsed_mags;
|
||||||
|
mutable std::unordered_map<uint16_t, ToolV4> parsed_tools;
|
||||||
mutable std::vector<Special> specials;
|
mutable std::vector<Special> parsed_specials;
|
||||||
mutable std::vector<StatBoost> stat_boosts;
|
mutable std::vector<StatBoost> parsed_stat_boosts;
|
||||||
|
|
||||||
// Key is used_item. We can't index on (used_item, equipped_item) because equipped_item may contain wildcards, and
|
// Key is used_item. We can't index on (used_item, equipped_item) because equipped_item may contain wildcards, and
|
||||||
// the matching order matters.
|
// the matching order matters.
|
||||||
mutable std::map<uint32_t, std::vector<ItemCombination>> item_combination_index;
|
mutable std::map<uint32_t, std::vector<ItemCombination>> item_combination_index;
|
||||||
|
|
||||||
explicit ItemParameterTable(std::shared_ptr<const std::string> data);
|
template <typename ToolDefT, bool BE>
|
||||||
|
std::pair<uint8_t, uint8_t> find_tool_by_id_t(uint32_t tool_table_offset, uint32_t id) const;
|
||||||
|
template <bool BE, typename OffsetsT>
|
||||||
|
float get_sale_divisor_t(const OffsetsT* offsets, uint8_t data1_0, uint8_t data1_1) const;
|
||||||
|
template <bool BE>
|
||||||
|
size_t num_events_t(uint32_t base_offset) const;
|
||||||
|
template <bool BE>
|
||||||
|
std::pair<const ItemParameterTable::EventItem*, size_t> get_event_items_t(uint32_t base_offset, uint8_t event_number) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-2
@@ -133,8 +133,7 @@ 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
|
} else if ((primary_identifier & 0xFFFF0000) == 0x030C0000) { // Non-combo mag cells
|
||||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
||||||
uint8_t evolution_number = s->mag_evolution_table(c->version())->get_evolution_number(mag.data.data1[1]);
|
if (s->mag_evolution_table(c->version())->get_evolution_number(mag.data.data1[1]) < 4) {
|
||||||
if (evolution_number < 4) {
|
|
||||||
switch (item.data.data1[2]) {
|
switch (item.data.data1[2]) {
|
||||||
case 0x00: // Cell of MAG 502
|
case 0x00: // Cell of MAG 502
|
||||||
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x1D : 0x21;
|
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x1D : 0x21;
|
||||||
|
|||||||
+9
-3
@@ -33,9 +33,11 @@ struct CharacterStatsT {
|
|||||||
ret.lck = this->lck;
|
ret.lck = this->lck;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(CharacterStatsT, 0x0E);
|
} __attribute__((packed));
|
||||||
using CharacterStats = CharacterStatsT<false>;
|
using CharacterStats = CharacterStatsT<false>;
|
||||||
using CharacterStatsBE = CharacterStatsT<true>;
|
using CharacterStatsBE = CharacterStatsT<true>;
|
||||||
|
check_struct_size(CharacterStats, 0x0E);
|
||||||
|
check_struct_size(CharacterStatsBE, 0x0E);
|
||||||
|
|
||||||
template <bool BE>
|
template <bool BE>
|
||||||
struct PlayerStatsT {
|
struct PlayerStatsT {
|
||||||
@@ -59,9 +61,11 @@ struct PlayerStatsT {
|
|||||||
ret.meseta = this->meseta;
|
ret.meseta = this->meseta;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(PlayerStatsT, 0x24);
|
} __attribute__((packed));
|
||||||
using PlayerStats = PlayerStatsT<false>;
|
using PlayerStats = PlayerStatsT<false>;
|
||||||
using PlayerStatsBE = PlayerStatsT<true>;
|
using PlayerStatsBE = PlayerStatsT<true>;
|
||||||
|
check_struct_size(PlayerStats, 0x24);
|
||||||
|
check_struct_size(PlayerStatsBE, 0x24);
|
||||||
|
|
||||||
template <bool BE>
|
template <bool BE>
|
||||||
struct LevelStatsDeltaT {
|
struct LevelStatsDeltaT {
|
||||||
@@ -85,9 +89,11 @@ struct LevelStatsDeltaT {
|
|||||||
ps.mst += this->mst;
|
ps.mst += this->mst;
|
||||||
ps.lck += this->lck;
|
ps.lck += this->lck;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(LevelStatsDeltaT, 0x0C);
|
} __attribute__((packed));
|
||||||
using LevelStatsDelta = LevelStatsDeltaT<false>;
|
using LevelStatsDelta = LevelStatsDeltaT<false>;
|
||||||
using LevelStatsDeltaBE = LevelStatsDeltaT<true>;
|
using LevelStatsDeltaBE = LevelStatsDeltaT<true>;
|
||||||
|
check_struct_size(LevelStatsDelta, 0x0C);
|
||||||
|
check_struct_size(LevelStatsDeltaBE, 0x0C);
|
||||||
|
|
||||||
class LevelTable {
|
class LevelTable {
|
||||||
// This is the base class for all the LevelTable implementations. The public interface here only defines functions
|
// This is the base class for all the LevelTable implementations. The public interface here only defines functions
|
||||||
|
|||||||
@@ -840,35 +840,3 @@ bool Lobby::compare_shared(const shared_ptr<const Lobby>& a, const shared_ptr<co
|
|||||||
|
|
||||||
return a->name < b->name;
|
return a->name < b->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
|
||||||
const char* phosg::name_for_enum<Lobby::JoinError>(Lobby::JoinError value) {
|
|
||||||
switch (value) {
|
|
||||||
case Lobby::JoinError::ALLOWED:
|
|
||||||
return "ALLOWED";
|
|
||||||
case Lobby::JoinError::FULL:
|
|
||||||
return "FULL";
|
|
||||||
case Lobby::JoinError::VERSION_CONFLICT:
|
|
||||||
return "VERSION_CONFLICT";
|
|
||||||
case Lobby::JoinError::QUEST_SELECTION_IN_PROGRESS:
|
|
||||||
return "QUEST_SELECTION_IN_PROGRESS";
|
|
||||||
case Lobby::JoinError::QUEST_IN_PROGRESS:
|
|
||||||
return "QUEST_IN_PROGRESS";
|
|
||||||
case Lobby::JoinError::BATTLE_IN_PROGRESS:
|
|
||||||
return "BATTLE_IN_PROGRESS";
|
|
||||||
case Lobby::JoinError::LOADING:
|
|
||||||
return "LOADING";
|
|
||||||
case Lobby::JoinError::SOLO:
|
|
||||||
return "SOLO";
|
|
||||||
case Lobby::JoinError::INCORRECT_PASSWORD:
|
|
||||||
return "INCORRECT_PASSWORD";
|
|
||||||
case Lobby::JoinError::LEVEL_TOO_LOW:
|
|
||||||
return "LEVEL_TOO_LOW";
|
|
||||||
case Lobby::JoinError::LEVEL_TOO_HIGH:
|
|
||||||
return "LEVEL_TOO_HIGH";
|
|
||||||
case Lobby::JoinError::NO_ACCESS_TO_QUEST:
|
|
||||||
return "NO_ACCESS_TO_QUEST";
|
|
||||||
default:
|
|
||||||
throw runtime_error("invalid drop mode");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+3
-1
@@ -281,4 +281,6 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
const char* phosg::name_for_enum<Lobby::JoinError>(Lobby::JoinError value);
|
ServerDropMode phosg::enum_for_name<ServerDropMode>(const char* name);
|
||||||
|
template <>
|
||||||
|
const char* phosg::name_for_enum<ServerDropMode>(ServerDropMode value);
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
#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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#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;
|
|
||||||
};
|
|
||||||
@@ -343,9 +343,11 @@ public:
|
|||||||
ret.store_raw(this->value);
|
ret.store_raw(this->value);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(ChallengeTimeT, 4);
|
} __attribute__((packed));
|
||||||
using ChallengeTime = ChallengeTimeT<false>;
|
using ChallengeTime = ChallengeTimeT<false>;
|
||||||
using ChallengeTimeBE = ChallengeTimeT<true>;
|
using ChallengeTimeBE = ChallengeTimeT<true>;
|
||||||
|
check_struct_size(ChallengeTime, 4);
|
||||||
|
check_struct_size(ChallengeTimeBE, 4);
|
||||||
|
|
||||||
std::string decrypt_v2_registry_value(const void* data, size_t size);
|
std::string decrypt_v2_registry_value(const void* data, size_t size);
|
||||||
|
|
||||||
|
|||||||
@@ -76,9 +76,11 @@ struct PlayerInventoryItemT {
|
|||||||
bool is_equipped() const {
|
bool is_equipped() const {
|
||||||
return (this->flags & 8);
|
return (this->flags & 8);
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(PlayerInventoryItemT, 0x1C);
|
} __attribute__((packed));
|
||||||
using PlayerInventoryItem = PlayerInventoryItemT<false>;
|
using PlayerInventoryItem = PlayerInventoryItemT<false>;
|
||||||
using PlayerInventoryItemBE = PlayerInventoryItemT<true>;
|
using PlayerInventoryItemBE = PlayerInventoryItemT<true>;
|
||||||
|
check_struct_size(PlayerInventoryItem, 0x1C);
|
||||||
|
check_struct_size(PlayerInventoryItemBE, 0x1C);
|
||||||
|
|
||||||
template <bool BE>
|
template <bool BE>
|
||||||
struct PlayerBankItemT {
|
struct PlayerBankItemT {
|
||||||
@@ -98,9 +100,11 @@ struct PlayerBankItemT {
|
|||||||
ret.present = this->present;
|
ret.present = this->present;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(PlayerBankItemT, 0x18);
|
} __attribute__((packed));
|
||||||
using PlayerBankItem = PlayerBankItemT<false>;
|
using PlayerBankItem = PlayerBankItemT<false>;
|
||||||
using PlayerBankItemBE = PlayerBankItemT<true>;
|
using PlayerBankItemBE = PlayerBankItemT<true>;
|
||||||
|
check_struct_size(PlayerBankItem, 0x18);
|
||||||
|
check_struct_size(PlayerBankItemBE, 0x18);
|
||||||
|
|
||||||
template <bool BE>
|
template <bool BE>
|
||||||
struct PlayerInventoryT {
|
struct PlayerInventoryT {
|
||||||
@@ -287,9 +291,11 @@ struct PlayerInventoryT {
|
|||||||
ret.items = this->items;
|
ret.items = this->items;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(PlayerInventoryT, 0x34C);
|
} __attribute__((packed));
|
||||||
using PlayerInventory = PlayerInventoryT<false>;
|
using PlayerInventory = PlayerInventoryT<false>;
|
||||||
using PlayerInventoryBE = PlayerInventoryT<true>;
|
using PlayerInventoryBE = PlayerInventoryT<true>;
|
||||||
|
check_struct_size(PlayerInventory, 0x34C);
|
||||||
|
check_struct_size(PlayerInventoryBE, 0x34C);
|
||||||
|
|
||||||
template <size_t SlotCount, bool BE>
|
template <size_t SlotCount, bool BE>
|
||||||
struct PlayerBankT {
|
struct PlayerBankT {
|
||||||
|
|||||||
@@ -312,9 +312,11 @@ struct PlayerVisualConfigT {
|
|||||||
ret.proportion_y = this->proportion_y;
|
ret.proportion_y = this->proportion_y;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(PlayerVisualConfigT, 0x50);
|
} __attribute__((packed));
|
||||||
using PlayerVisualConfig = PlayerVisualConfigT<false>;
|
using PlayerVisualConfig = PlayerVisualConfigT<false>;
|
||||||
using PlayerVisualConfigBE = PlayerVisualConfigT<true>;
|
using PlayerVisualConfigBE = PlayerVisualConfigT<true>;
|
||||||
|
check_struct_size(PlayerVisualConfig, 0x50);
|
||||||
|
check_struct_size(PlayerVisualConfigBE, 0x50);
|
||||||
|
|
||||||
template <bool BE>
|
template <bool BE>
|
||||||
struct PlayerDispDataDCPCV3T {
|
struct PlayerDispDataDCPCV3T {
|
||||||
@@ -329,9 +331,11 @@ struct PlayerDispDataDCPCV3T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PlayerDispDataBB to_bb(Language to_language, Language from_language) const;
|
PlayerDispDataBB to_bb(Language to_language, Language from_language) const;
|
||||||
} __packed_ws_be__(PlayerDispDataDCPCV3T, 0xD0);
|
} __attribute__((packed));
|
||||||
using PlayerDispDataDCPCV3 = PlayerDispDataDCPCV3T<false>;
|
using PlayerDispDataDCPCV3 = PlayerDispDataDCPCV3T<false>;
|
||||||
using PlayerDispDataDCPCV3BE = PlayerDispDataDCPCV3T<true>;
|
using PlayerDispDataDCPCV3BE = PlayerDispDataDCPCV3T<true>;
|
||||||
|
check_struct_size(PlayerDispDataDCPCV3, 0xD0);
|
||||||
|
check_struct_size(PlayerDispDataDCPCV3BE, 0xD0);
|
||||||
|
|
||||||
struct PlayerDispDataBBPreview {
|
struct PlayerDispDataBBPreview {
|
||||||
/* 00 */ le_uint32_t experience = 0;
|
/* 00 */ le_uint32_t experience = 0;
|
||||||
@@ -594,9 +598,11 @@ struct ChallengeAwardStateT {
|
|||||||
ret.maximum_rank = this->maximum_rank;
|
ret.maximum_rank = this->maximum_rank;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(ChallengeAwardStateT, 8);
|
} __attribute__((packed));
|
||||||
using ChallengeAwardState = ChallengeAwardStateT<false>;
|
using ChallengeAwardState = ChallengeAwardStateT<false>;
|
||||||
using ChallengeAwardStateBE = ChallengeAwardStateT<true>;
|
using ChallengeAwardStateBE = ChallengeAwardStateT<true>;
|
||||||
|
check_struct_size(ChallengeAwardState, 8);
|
||||||
|
check_struct_size(ChallengeAwardStateBE, 8);
|
||||||
|
|
||||||
template <TextEncoding UnencryptedEncoding, TextEncoding EncryptedEncoding>
|
template <TextEncoding UnencryptedEncoding, TextEncoding EncryptedEncoding>
|
||||||
struct PlayerRecordsChallengeDCPCT {
|
struct PlayerRecordsChallengeDCPCT {
|
||||||
@@ -667,9 +673,11 @@ struct PlayerRecordsChallengeV3T {
|
|||||||
/* 00D8:00F4 */ pstring<TextEncoding::CHALLENGE8, 0x0C> rank_title;
|
/* 00D8:00F4 */ pstring<TextEncoding::CHALLENGE8, 0x0C> rank_title;
|
||||||
/* 00E4:0100 */ parray<uint8_t, 0x1C> unknown_l7;
|
/* 00E4:0100 */ parray<uint8_t, 0x1C> unknown_l7;
|
||||||
/* 0100:011C */
|
/* 0100:011C */
|
||||||
} __packed_ws_be__(PlayerRecordsChallengeV3T, 0x100);
|
} __attribute__((packed));
|
||||||
using PlayerRecordsChallengeV3 = PlayerRecordsChallengeV3T<false>;
|
using PlayerRecordsChallengeV3 = PlayerRecordsChallengeV3T<false>;
|
||||||
using PlayerRecordsChallengeV3BE = PlayerRecordsChallengeV3T<true>;
|
using PlayerRecordsChallengeV3BE = PlayerRecordsChallengeV3T<true>;
|
||||||
|
check_struct_size(PlayerRecordsChallengeV3, 0x100);
|
||||||
|
check_struct_size(PlayerRecordsChallengeV3BE, 0x100);
|
||||||
|
|
||||||
struct PlayerRecordsChallengeEp3 {
|
struct PlayerRecordsChallengeEp3 {
|
||||||
/* 00:1C */ be_uint16_t title_color = 0x7FFF; // XRGB1555
|
/* 00:1C */ be_uint16_t title_color = 0x7FFF; // XRGB1555
|
||||||
@@ -696,7 +704,8 @@ struct PlayerRecordsChallengeEp3 {
|
|||||||
/* C8:E4 */ ChallengeAwardStateT<true> ep2_online_award_state;
|
/* C8:E4 */ ChallengeAwardStateT<true> ep2_online_award_state;
|
||||||
/* D0:EC */ ChallengeAwardStateT<true> ep1_offline_award_state;
|
/* D0:EC */ ChallengeAwardStateT<true> ep1_offline_award_state;
|
||||||
/* D8:F4 */
|
/* D8:F4 */
|
||||||
} __packed_ws__(PlayerRecordsChallengeEp3, 0xD8);
|
} __attribute__((packed));
|
||||||
|
check_struct_size(PlayerRecordsChallengeEp3, 0xD8);
|
||||||
|
|
||||||
struct PlayerRecordsChallengeBB {
|
struct PlayerRecordsChallengeBB {
|
||||||
/* 0000 */ le_uint16_t title_color = 0x7FFF; // XRGB1555
|
/* 0000 */ le_uint16_t title_color = 0x7FFF; // XRGB1555
|
||||||
@@ -824,9 +833,11 @@ struct PlayerRecordsBattleT {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(PlayerRecordsBattleT, 0x18);
|
} __attribute__((packed));
|
||||||
using PlayerRecordsBattle = PlayerRecordsBattleT<false>;
|
using PlayerRecordsBattle = PlayerRecordsBattleT<false>;
|
||||||
using PlayerRecordsBattleBE = PlayerRecordsBattleT<true>;
|
using PlayerRecordsBattleBE = PlayerRecordsBattleT<true>;
|
||||||
|
check_struct_size(PlayerRecordsBattle, 0x18);
|
||||||
|
check_struct_size(PlayerRecordsBattleBE, 0x18);
|
||||||
|
|
||||||
template <typename DestT, typename SrcT = DestT>
|
template <typename DestT, typename SrcT = DestT>
|
||||||
DestT convert_player_disp_data(const SrcT&, Language, Language) {
|
DestT convert_player_disp_data(const SrcT&, Language, Language) {
|
||||||
@@ -1101,9 +1112,11 @@ struct SymbolChatT {
|
|||||||
ret.face_parts = this->face_parts;
|
ret.face_parts = this->face_parts;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(SymbolChatT, 0x3C);
|
} __attribute__((packed));
|
||||||
using SymbolChat = SymbolChatT<false>;
|
using SymbolChat = SymbolChatT<false>;
|
||||||
using SymbolChatBE = SymbolChatT<true>;
|
using SymbolChatBE = SymbolChatT<true>;
|
||||||
|
check_struct_size(SymbolChat, 0x3C);
|
||||||
|
check_struct_size(SymbolChatBE, 0x3C);
|
||||||
|
|
||||||
struct TelepipeState {
|
struct TelepipeState {
|
||||||
/* 00 */ le_uint16_t owner_client_id = 0xFFFF;
|
/* 00 */ le_uint16_t owner_client_id = 0xFFFF;
|
||||||
|
|||||||
+3
-1
@@ -51,9 +51,11 @@ struct PSOMemCardDLQFileEncryptedHeaderT {
|
|||||||
le_uint32_t decompressed_size;
|
le_uint32_t decompressed_size;
|
||||||
le_uint32_t round3_seed;
|
le_uint32_t round3_seed;
|
||||||
// Data follows here.
|
// Data follows here.
|
||||||
} __packed_ws_be__(PSOMemCardDLQFileEncryptedHeaderT, 0x10);
|
} __attribute__((packed));
|
||||||
using PSOVMSDLQFileEncryptedHeader = PSOMemCardDLQFileEncryptedHeaderT<false>;
|
using PSOVMSDLQFileEncryptedHeader = PSOMemCardDLQFileEncryptedHeaderT<false>;
|
||||||
using PSOGCIDLQFileEncryptedHeader = PSOMemCardDLQFileEncryptedHeaderT<true>;
|
using PSOGCIDLQFileEncryptedHeader = PSOMemCardDLQFileEncryptedHeaderT<true>;
|
||||||
|
check_struct_size(PSOVMSDLQFileEncryptedHeader, 0x10);
|
||||||
|
check_struct_size(PSOGCIDLQFileEncryptedHeader, 0x10);
|
||||||
|
|
||||||
template <bool BE>
|
template <bool BE>
|
||||||
string decrypt_download_quest_data_section(
|
string decrypt_download_quest_data_section(
|
||||||
|
|||||||
+3
-1
@@ -93,9 +93,11 @@ protected:
|
|||||||
/* 08 */ U32T<BE> box_areas_offset; // -> parray<uint8_t, 0x1E>
|
/* 08 */ U32T<BE> box_areas_offset; // -> parray<uint8_t, 0x1E>
|
||||||
/* 0C */ U32T<BE> box_rares_offset; // -> parray<PackedDrop, 0x1E>
|
/* 0C */ U32T<BE> box_rares_offset; // -> parray<PackedDrop, 0x1E>
|
||||||
/* 10 */
|
/* 10 */
|
||||||
} __packed_ws_be__(OffsetsT, 0x10);
|
} __attribute__((packed));
|
||||||
using Offsets = OffsetsT<false>;
|
using Offsets = OffsetsT<false>;
|
||||||
using OffsetsBE = OffsetsT<true>;
|
using OffsetsBE = OffsetsT<true>;
|
||||||
|
check_struct_size(Offsets, 0x10);
|
||||||
|
check_struct_size(OffsetsBE, 0x10);
|
||||||
|
|
||||||
struct BoxRare {
|
struct BoxRare {
|
||||||
uint8_t area_norm_plus_1;
|
uint8_t area_norm_plus_1;
|
||||||
|
|||||||
+9
-15
@@ -524,13 +524,7 @@ asio::awaitable<void> start_login_server_procedure(shared_ptr<Client> c) {
|
|||||||
s->ep3_tournament_index->link_client(c);
|
s->ep3_tournament_index->link_client(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->preferred_lobby_id >= 0) {
|
if (s->welcome_message.empty() ||
|
||||||
s->add_client_to_available_lobby(c, true);
|
|
||||||
if (c->require_lobby()->is_game()) {
|
|
||||||
c->set_flag(Client::Flag::LOADING);
|
|
||||||
c->log.info_f("LOADING flag set");
|
|
||||||
}
|
|
||||||
} else if (s->welcome_message.empty() ||
|
|
||||||
c->check_flag(Client::Flag::NO_D6) ||
|
c->check_flag(Client::Flag::NO_D6) ||
|
||||||
!c->check_flag(Client::Flag::AT_WELCOME_MESSAGE)) {
|
!c->check_flag(Client::Flag::AT_WELCOME_MESSAGE)) {
|
||||||
c->clear_flag(Client::Flag::AT_WELCOME_MESSAGE);
|
c->clear_flag(Client::Flag::AT_WELCOME_MESSAGE);
|
||||||
@@ -2760,7 +2754,7 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
|
|||||||
co_await enable_save_if_needed(c);
|
co_await enable_save_if_needed(c);
|
||||||
send_lobby_list(c);
|
send_lobby_list(c);
|
||||||
if (!c->lobby.lock()) {
|
if (!c->lobby.lock()) {
|
||||||
s->add_client_to_available_lobby(c, false);
|
s->add_client_to_available_lobby(c);
|
||||||
}
|
}
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
@@ -2775,7 +2769,7 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
|
|||||||
co_await send_get_player_info(c);
|
co_await send_get_player_info(c);
|
||||||
}
|
}
|
||||||
if (!c->lobby.lock()) {
|
if (!c->lobby.lock()) {
|
||||||
s->add_client_to_available_lobby(c, false);
|
s->add_client_to_available_lobby(c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2790,7 +2784,7 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
|
|||||||
co_await enable_save_if_needed(c);
|
co_await enable_save_if_needed(c);
|
||||||
send_lobby_list(c);
|
send_lobby_list(c);
|
||||||
if (!c->lobby.lock()) {
|
if (!c->lobby.lock()) {
|
||||||
s->add_client_to_available_lobby(c, false);
|
s->add_client_to_available_lobby(c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2816,7 +2810,7 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
|
|||||||
co_await enable_save_if_needed(c);
|
co_await enable_save_if_needed(c);
|
||||||
send_lobby_list(c);
|
send_lobby_list(c);
|
||||||
if (!c->lobby.lock()) {
|
if (!c->lobby.lock()) {
|
||||||
s->add_client_to_available_lobby(c, false);
|
s->add_client_to_available_lobby(c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2835,7 +2829,7 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
|
|||||||
co_await enable_save_if_needed(c);
|
co_await enable_save_if_needed(c);
|
||||||
send_lobby_list(c);
|
send_lobby_list(c);
|
||||||
if (!c->lobby.lock()) {
|
if (!c->lobby.lock()) {
|
||||||
s->add_client_to_available_lobby(c, false);
|
s->add_client_to_available_lobby(c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2864,7 +2858,7 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
|
|||||||
co_await enable_save_if_needed(c);
|
co_await enable_save_if_needed(c);
|
||||||
send_lobby_list(c);
|
send_lobby_list(c);
|
||||||
if (!c->lobby.lock()) {
|
if (!c->lobby.lock()) {
|
||||||
s->add_client_to_available_lobby(c, false);
|
s->add_client_to_available_lobby(c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2892,7 +2886,7 @@ static asio::awaitable<void> on_10_main_menu(shared_ptr<Client> c, uint32_t item
|
|||||||
co_await enable_save_if_needed(c);
|
co_await enable_save_if_needed(c);
|
||||||
send_lobby_list(c);
|
send_lobby_list(c);
|
||||||
if (!c->lobby.lock()) {
|
if (!c->lobby.lock()) {
|
||||||
s->add_client_to_available_lobby(c, false);
|
s->add_client_to_available_lobby(c);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3507,7 +3501,7 @@ static asio::awaitable<void> on_84(shared_ptr<Client> c, Channel::Message& msg)
|
|||||||
// If the client isn't in any lobby, then they just left a game. Add them to the lobby they requested, but fall
|
// If the client isn't in any lobby, then they just left a game. Add them to the lobby they requested, but fall
|
||||||
// back to another lobby if it's full.
|
// back to another lobby if it's full.
|
||||||
c->preferred_lobby_id = cmd.item_id;
|
c->preferred_lobby_id = cmd.item_id;
|
||||||
s->add_client_to_available_lobby(c, false);
|
s->add_client_to_available_lobby(c);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// If the client already is in a lobby, then they're using the lobby teleporter; add them to the lobby they
|
// If the client already is in a lobby, then they're using the lobby teleporter; add them to the lobby they
|
||||||
|
|||||||
@@ -158,9 +158,11 @@ struct WordSelectMessageT {
|
|||||||
ret.unknown_a4 = this->unknown_a4;
|
ret.unknown_a4 = this->unknown_a4;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} __packed_ws_be__(WordSelectMessageT, 0x1C);
|
} __attribute__((packed));
|
||||||
using WordSelectMessage = WordSelectMessageT<false>;
|
using WordSelectMessage = WordSelectMessageT<false>;
|
||||||
using WordSelectMessageBE = WordSelectMessageT<true>;
|
using WordSelectMessageBE = WordSelectMessageT<true>;
|
||||||
|
check_struct_size(WordSelectMessage, 0x1C);
|
||||||
|
check_struct_size(WordSelectMessageBE, 0x1C);
|
||||||
|
|
||||||
template <bool BE, TextEncoding Encoding, size_t MaxChars>
|
template <bool BE, TextEncoding Encoding, size_t MaxChars>
|
||||||
struct SaveFileChatShortcutEntryT {
|
struct SaveFileChatShortcutEntryT {
|
||||||
|
|||||||
+27
-47
@@ -83,46 +83,31 @@ ServerState::ServerState(const string& config_filename, bool is_replay)
|
|||||||
bb_system_cache(new FileContentsCache(3600000000ULL)),
|
bb_system_cache(new FileContentsCache(3600000000ULL)),
|
||||||
gba_files_cache(new FileContentsCache(3600000000ULL)) {}
|
gba_files_cache(new FileContentsCache(3600000000ULL)) {}
|
||||||
|
|
||||||
void ServerState::add_client_to_available_lobby(shared_ptr<Client> c, bool allow_games) {
|
void ServerState::add_client_to_available_lobby(shared_ptr<Client> c) {
|
||||||
shared_ptr<Lobby> added_to_lobby;
|
shared_ptr<Lobby> added_to_lobby;
|
||||||
|
|
||||||
auto try_join_lobby = [&](uint32_t lobby_id) -> std::shared_ptr<Lobby> {
|
|
||||||
auto l = this->find_lobby(lobby_id);
|
|
||||||
if (!l) {
|
|
||||||
c->log.info_f("Cannot join lobby {:08X}: lobby does not exist", lobby_id);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (!allow_games && l->is_game()) {
|
|
||||||
c->log.info_f("Cannot join lobby {:08X}: lobby is a game", lobby_id);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
static const std::string password = "";
|
|
||||||
auto join_error = l->join_error_for_client(c, &password);
|
|
||||||
if (join_error == Lobby::JoinError::ALLOWED) {
|
|
||||||
try {
|
|
||||||
l->add_client(c);
|
|
||||||
c->log.info_f("Joined lobby {:08X}", lobby_id);
|
|
||||||
return l;
|
|
||||||
} catch (const out_of_range& e) {
|
|
||||||
c->log.info_f("Cannot join lobby {:08X}: {}", lobby_id, e.what());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c->log.info_f("Cannot join lobby {:08X}: {}", lobby_id, phosg::name_for_enum(join_error));
|
|
||||||
return nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (c->preferred_lobby_id >= 0) {
|
if (c->preferred_lobby_id >= 0) {
|
||||||
added_to_lobby = try_join_lobby(c->preferred_lobby_id);
|
try {
|
||||||
c->preferred_lobby_id = -1;
|
auto l = this->find_lobby(c->preferred_lobby_id);
|
||||||
|
if (l && !l->is_game() && l->check_flag(Lobby::Flag::PUBLIC) && l->version_is_allowed(c->version())) {
|
||||||
|
l->add_client(c);
|
||||||
|
added_to_lobby = l;
|
||||||
|
}
|
||||||
|
} catch (const out_of_range&) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!added_to_lobby) {
|
if (!added_to_lobby.get()) {
|
||||||
for (const auto& lobby_id : this->public_lobby_search_order(c)) {
|
for (const auto& lobby_id : this->public_lobby_search_order(c)) {
|
||||||
added_to_lobby = try_join_lobby(lobby_id);
|
try {
|
||||||
if (added_to_lobby) {
|
auto l = this->find_lobby(lobby_id);
|
||||||
|
if (l && !l->is_game() && l->check_flag(Lobby::Flag::PUBLIC) && l->version_is_allowed(c->version())) {
|
||||||
|
l->add_client(c);
|
||||||
|
added_to_lobby = l;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} catch (const out_of_range&) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,10 +461,8 @@ shared_ptr<const ItemParameterTable> ServerState::item_parameter_table_for_encod
|
|||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<const MagEvolutionTable> ServerState::mag_evolution_table(Version version) const {
|
shared_ptr<const MagEvolutionTable> ServerState::mag_evolution_table(Version version) const {
|
||||||
if (is_v1(version)) {
|
if (is_v1_or_v2(version)) {
|
||||||
return this->mag_evolution_table_v1;
|
return this->mag_evolution_table_v1_v2;
|
||||||
} else if (is_v2(version)) {
|
|
||||||
return this->mag_evolution_table_v2;
|
|
||||||
} else if (!is_v4(version)) {
|
} else if (!is_v4(version)) {
|
||||||
return this->mag_evolution_table_v3;
|
return this->mag_evolution_table_v3;
|
||||||
} else {
|
} else {
|
||||||
@@ -2167,29 +2150,26 @@ void ServerState::load_item_definitions() {
|
|||||||
string path = std::format("system/item-tables/ItemPMT-{}.prs", file_path_token_for_version(v));
|
string path = std::format("system/item-tables/ItemPMT-{}.prs", file_path_token_for_version(v));
|
||||||
config_log.debug_f("Loading item definition table {}", path);
|
config_log.debug_f("Loading item definition table {}", path);
|
||||||
auto data = make_shared<string>(prs_decompress(phosg::load_file(path)));
|
auto data = make_shared<string>(prs_decompress(phosg::load_file(path)));
|
||||||
new_item_parameter_tables[v_s] = ItemParameterTable::create(data, v);
|
new_item_parameter_tables[v_s] = make_shared<ItemParameterTable>(data, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto json = phosg::JSON::parse(phosg::load_file("system/item-tables/translation-table.json"));
|
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);
|
auto new_item_translation_table = make_shared<ItemTranslationTable>(json, new_item_parameter_tables);
|
||||||
|
|
||||||
config_log.info_f("Loading v1 mag evolution table");
|
// TODO: We should probably load the tables for other versions too.
|
||||||
auto mag_data_v1 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-dc-v1.prs")));
|
config_log.info_f("Loading v1/v2 mag evolution table");
|
||||||
auto new_table_v1 = MagEvolutionTable::create(mag_data_v1, Version::DC_V1);
|
auto mag_data_v1_v2 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-dc-v2.prs")));
|
||||||
config_log.info_f("Loading v2 mag evolution table");
|
auto new_table_v1_v2 = make_shared<MagEvolutionTable>(mag_data_v1_v2, 0x3A);
|
||||||
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");
|
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 mag_data_v3 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-xb-v3.prs")));
|
||||||
auto new_table_v3 = MagEvolutionTable::create(mag_data_v3, Version::XB_V3);
|
auto new_table_v3 = make_shared<MagEvolutionTable>(mag_data_v3, 0x43);
|
||||||
config_log.info_f("Loading v4 mag evolution table");
|
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 mag_data_v4 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-bb-v4.prs")));
|
||||||
auto new_table_v4 = MagEvolutionTable::create(mag_data_v4, Version::BB_V4);
|
auto new_table_v4 = make_shared<MagEvolutionTable>(mag_data_v4, 0x53);
|
||||||
|
|
||||||
this->item_parameter_tables = std::move(new_item_parameter_tables);
|
this->item_parameter_tables = std::move(new_item_parameter_tables);
|
||||||
this->item_translation_table = std::move(new_item_translation_table);
|
this->item_translation_table = std::move(new_item_translation_table);
|
||||||
this->mag_evolution_table_v1 = std::move(new_table_v1);
|
this->mag_evolution_table_v1_v2 = std::move(new_table_v1_v2);
|
||||||
this->mag_evolution_table_v2 = std::move(new_table_v2);
|
|
||||||
this->mag_evolution_table_v3 = std::move(new_table_v3);
|
this->mag_evolution_table_v3 = std::move(new_table_v3);
|
||||||
this->mag_evolution_table_v4 = std::move(new_table_v4);
|
this->mag_evolution_table_v4 = std::move(new_table_v4);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -23,7 +23,6 @@
|
|||||||
#include "ItemTranslationTable.hh"
|
#include "ItemTranslationTable.hh"
|
||||||
#include "LevelTable.hh"
|
#include "LevelTable.hh"
|
||||||
#include "Lobby.hh"
|
#include "Lobby.hh"
|
||||||
#include "MagEvolutionTable.hh"
|
|
||||||
#include "Menu.hh"
|
#include "Menu.hh"
|
||||||
#include "Quest.hh"
|
#include "Quest.hh"
|
||||||
#include "TeamIndex.hh"
|
#include "TeamIndex.hh"
|
||||||
@@ -217,8 +216,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
|||||||
std::array<std::shared_ptr<const ItemData::StackLimits>, NUM_VERSIONS> item_stack_limits_tables;
|
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_items = 200;
|
||||||
size_t bb_max_bank_meseta = 999999;
|
size_t bb_max_bank_meseta = 999999;
|
||||||
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v1;
|
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v1_v2;
|
||||||
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_v3;
|
||||||
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v4;
|
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v4;
|
||||||
std::shared_ptr<const TextIndex> text_index;
|
std::shared_ptr<const TextIndex> text_index;
|
||||||
@@ -326,7 +324,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
|||||||
ServerState& operator=(const ServerState&) = delete;
|
ServerState& operator=(const ServerState&) = delete;
|
||||||
ServerState& operator=(ServerState&&) = delete;
|
ServerState& operator=(ServerState&&) = delete;
|
||||||
|
|
||||||
void add_client_to_available_lobby(std::shared_ptr<Client> c, bool allow_games);
|
void add_client_to_available_lobby(std::shared_ptr<Client> c);
|
||||||
void remove_client_from_lobby(std::shared_ptr<Client> c);
|
void remove_client_from_lobby(std::shared_ptr<Client> c);
|
||||||
bool change_client_lobby(
|
bool change_client_lobby(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
|
|||||||
@@ -23,11 +23,6 @@
|
|||||||
__attribute__((packed)); \
|
__attribute__((packed)); \
|
||||||
check_struct_size(StructT, Size)
|
check_struct_size(StructT, Size)
|
||||||
|
|
||||||
#define __packed_ws_be__(StructT, Size) \
|
|
||||||
__attribute__((packed)); \
|
|
||||||
check_struct_size(StructT<false>, Size); \
|
|
||||||
check_struct_size(StructT<true>, Size)
|
|
||||||
|
|
||||||
// Conversion functions
|
// Conversion functions
|
||||||
|
|
||||||
std::string encode_utf8_char(uint32_t ch);
|
std::string encode_utf8_char(uint32_t ch);
|
||||||
|
|||||||
@@ -40,9 +40,12 @@ struct NonWindowsRootT {
|
|||||||
U32T<BE> table4;
|
U32T<BE> table4;
|
||||||
U32T<BE> article_types_table;
|
U32T<BE> article_types_table;
|
||||||
U32T<BE> table6;
|
U32T<BE> table6;
|
||||||
} __packed_ws_be__(NonWindowsRootT, 0x1C);
|
} __attribute__((packed));
|
||||||
|
|
||||||
using NonWindowsRoot = NonWindowsRootT<false>;
|
using NonWindowsRoot = NonWindowsRootT<false>;
|
||||||
using NonWindowsRootBE = NonWindowsRootT<true>;
|
using NonWindowsRootBE = NonWindowsRootT<true>;
|
||||||
|
check_struct_size(NonWindowsRoot, 0x1C);
|
||||||
|
check_struct_size(NonWindowsRootBE, 0x1C);
|
||||||
|
|
||||||
struct PCV2Root {
|
struct PCV2Root {
|
||||||
le_uint32_t unknown_a1;
|
le_uint32_t unknown_a1;
|
||||||
|
|||||||
Reference in New Issue
Block a user