From 45eabab958c7f22e3386cb969c657069bc688907 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Wed, 18 May 2022 12:15:02 -0700 Subject: [PATCH] parse episode 3 player data --- src/Player.hh | 50 +++++++++++++++++++++++++++++++++++++++++- src/ReceiveCommands.cc | 20 ++++++++--------- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/Player.hh b/src/Player.hh index a9ce1158..d4a80ecb 100644 --- a/src/Player.hh +++ b/src/Player.hh @@ -254,6 +254,41 @@ struct KeyAndTeamConfigBB { +struct Ep3Deck { + // TODO: are the last 4 bytes actually part of this? They don't seem to be + // used for anything else, but the game limits the name to 14 chars + a + // language marker, which equals exactly 0x10 characters. + ptext name; + // List of card IDs. The card count is the number of nonzero entries here + // before a zero entry (or 50 if no entries are nonzero). The first card ID is + // the SC card, which the game implicitly subtracts from the limit - so a + // valid deck should actually have 31 cards in it. + le_uint16_t card_ids[50]; + uint32_t unknown_a1; + // Last modification time + le_uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t unknown_a2; +} __attribute__((packed)); + +struct Ep3Config { + parray unknown_a1; // at 728 in 61/98 command + Ep3Deck decks[25]; // at 1B5C in 61/98 command + uint64_t unknown_a2; // at 2840 in 61/98 command + be_uint32_t offline_clv_exp; // CLvOff = this / 100 + be_uint32_t online_clv_exp; // CLvOn = this / 100 + parray unknown_a3; // at 2850 in 61/98 command + ptext name; // at 299C in 61/98 command + // Other records are probably somewhere in here - e.g. win/loss, play time, etc. + parray unknown_a4; // at 29AC in 61/98 command +} __attribute__((packed)); + + + struct PlayerLobbyDataPC { le_uint32_t player_tag; le_uint32_t guild_card; @@ -303,6 +338,17 @@ struct PSOPlayerDataGC { // For command 61 char auto_reply[0]; } __attribute__((packed)); +struct PSOPlayerDataGCEp3 { // For command 61 + PlayerInventory inventory; + PlayerDispDataPCGC disp; + parray unknown; + ptext info_board; + parray blocked_senders; + le_uint32_t auto_reply_enabled; + char auto_reply[0xAC]; + Ep3Config ep3_config; +} __attribute__((packed)); + struct PSOPlayerDataBB { // For command 61 PlayerInventory inventory; PlayerDispDataBB disp; @@ -375,6 +421,8 @@ struct SavedAccountDataBB { // .nsa file format ptext team_name; } __attribute__((packed)); + + class ClientGameData { private: std::shared_ptr account_data; @@ -386,7 +434,7 @@ public: // The following fields are not saved, and are only used in certain situations // Null unless the client is Episode 3 and has sent its config already - std::shared_ptr> ep3_config; + std::shared_ptr ep3_config; // These are only used if the client is BB std::string bb_username; diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 1f3c3649..c699340f 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1085,28 +1085,28 @@ void process_player_data(shared_ptr s, shared_ptr c, // autoreply text is a variable length switch (c->version) { case GameVersion::PC: { - const auto& disp = check_size_t(data, + const auto& pd = check_size_t(data, sizeof(PSOPlayerDataPC), 0xFFFF); - c->game_data.import_player(disp); + c->game_data.import_player(pd); break; } case GameVersion::GC: { - const PSOPlayerDataGC* disp; + const PSOPlayerDataGC* pd; if (flag == 4) { // Episode 3 - disp = &check_size_t(data, - sizeof(PSOPlayerDataGC) + 0x23FC, sizeof(PSOPlayerDataGC) + 0x23FC); - // TODO: import Episode 3 data somewhere + const auto* pd3 = &check_size_t(data); + c->game_data.ep3_config.reset(new Ep3Config(pd3->ep3_config)); + pd = reinterpret_cast(pd3); } else { - disp = &check_size_t(data, sizeof(PSOPlayerDataGC), + pd = &check_size_t(data, sizeof(PSOPlayerDataGC), sizeof(PSOPlayerDataGC) + c->game_data.player()->auto_reply.bytes()); } - c->game_data.import_player(*disp); + c->game_data.import_player(*pd); break; } case GameVersion::BB: { - const auto& disp = check_size_t(data, sizeof(PSOPlayerDataBB), + const auto& pd = check_size_t(data, sizeof(PSOPlayerDataBB), sizeof(PSOPlayerDataBB) + c->game_data.player()->auto_reply.bytes()); - c->game_data.import_player(disp); + c->game_data.import_player(pd); break; } default: