diff --git a/src/Episode3/BattleRecord.cc b/src/Episode3/BattleRecord.cc index 81b24783..86f619a6 100644 --- a/src/Episode3/BattleRecord.cc +++ b/src/Episode3/BattleRecord.cc @@ -9,6 +9,12 @@ using namespace std; namespace Episode3 { +void BattleRecord::PlayerEntry::print(FILE* stream) const { + // TODO: Format this nicely somehow. Maybe factor out the functions in + // QuestScript that format some of these structures + print_data(stream, this, sizeof(this)); +} + BattleRecord::Event::Event(StringReader& r) { this->type = r.get(); this->timestamp = r.get_u64l(); @@ -74,6 +80,51 @@ void BattleRecord::Event::serialize(StringWriter& w) const { } } +void BattleRecord::Event::print(FILE* stream) const { + string time_str = format_time(this->timestamp); + fprintf(stream, "Event @%016" PRIX64 " (%s) ", this->timestamp, time_str.c_str()); + switch (this->type) { + case Type::PLAYER_JOIN: + fprintf(stream, "PLAYER_JOIN %02" PRIX32 "\n", this->players[0].lobby_data.client_id.load()); + this->players[0].print(stream); + break; + case Type::PLAYER_LEAVE: + fprintf(stream, "PLAYER_LEAVE %02hhu\n", this->leaving_client_id); + break; + case Type::SET_INITIAL_PLAYERS: + fprintf(stream, "SET_INITIAL_PLAYERS"); + for (const auto& player : this->players) { + fprintf(stream, " %02" PRIX32, player.lobby_data.client_id.load()); + } + for (const auto& player : this->players) { + player.print(stream); + } + break; + case Type::BATTLE_COMMAND: + fprintf(stream, "BATTLE_COMMAND\n"); + print_data(stream, this->data); + break; + case Type::GAME_COMMAND: + fprintf(stream, "GAME_COMMAND\n"); + print_data(stream, this->data); + break; + case Type::EP3_GAME_COMMAND: + fprintf(stream, "EP3_GAME_COMMAND\n"); + print_data(stream, this->data); + break; + case Type::CHAT_MESSAGE: + fprintf(stream, "CHAT_MESSAGE %08" PRIX32 "\n", this->guild_card_number); + print_data(stream, this->data); + break; + case Type::SERVER_DATA_COMMAND: + fprintf(stream, "SERVER_DATA_COMMAND\n"); + print_data(stream, this->data); + break; + default: + throw runtime_error("unknown event type in batlte record"); + } +} + BattleRecord::BattleRecord(uint32_t behavior_flags) : is_writable(true), behavior_flags(behavior_flags), @@ -276,6 +327,21 @@ void BattleRecord::set_battle_end_timestamp() { this->battle_end_timestamp = now(); } +void BattleRecord::print(FILE* stream) const { + string start_str = format_time(this->battle_start_timestamp); + string end_str = format_time(this->battle_end_timestamp); + fprintf(stream, "BattleRecord %s behavior_flags=%08" PRIX32 " start=%016" PRIX64 " (%s) end=%016" PRIX64 " (%s); %zu events\n", + this->is_writable ? "writable" : "read-only", + this->behavior_flags, + this->battle_start_timestamp, + start_str.c_str(), + this->battle_end_timestamp, + end_str.c_str(), this->events.size()); + for (const auto& event : this->events) { + event.print(stream); + } +} + BattleRecordPlayer::BattleRecordPlayer( shared_ptr rec, shared_ptr base) diff --git a/src/Episode3/BattleRecord.hh b/src/Episode3/BattleRecord.hh index c751da64..92ab34df 100644 --- a/src/Episode3/BattleRecord.hh +++ b/src/Episode3/BattleRecord.hh @@ -24,6 +24,8 @@ public: PlayerInventory inventory; PlayerDispDataDCPCV3 disp; le_uint32_t level; + + void print(FILE* stream) const; } __attribute__((packed)); struct Event { @@ -53,6 +55,7 @@ public: Event() = default; explicit Event(StringReader& r); void serialize(StringWriter& w) const; + void print(FILE* stream) const; }; explicit BattleRecord(uint32_t behavior_flags); @@ -80,6 +83,8 @@ public: void set_battle_start_timestamp(); void set_battle_end_timestamp(); + void print(FILE* stream) const; + private: static constexpr uint64_t SIGNATURE = 0x14C946D56D1DAC50; diff --git a/src/Main.cc b/src/Main.cc index 82556783..da5b48eb 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -2285,6 +2285,13 @@ Action a_diff_dol_files( } }); +Action a_format_ep3_battle_record( + "format-ep3-battle-record", nullptr, +[](Arguments& args) { + string data = read_input_data(args); + Episode3::BattleRecord rec(data); + rec.print(stdout); + }); + Action a_replay_ep3_battle_commands( "replay-ep3-battle-commands", nullptr, +[](Arguments& args) { auto s = make_shared();