From 286997188e53e0fa0763724ca87e8040c1b1adcb Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sat, 30 Jul 2022 13:08:22 -0700 Subject: [PATCH] skip guild card file download if checksums match --- src/CommandFormats.hh | 17 +++++++++++++++-- src/Player.cc | 5 +++++ src/Player.hh | 2 ++ src/ReceiveCommands.cc | 13 +++++++------ src/SendCommands.cc | 3 +-- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index dde7b3ea..fcb9448c 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -2284,9 +2284,13 @@ struct C_GuildCardChecksum_01E8 { }; // 02E8 (S->C): Accept/decline guild card file checksum +// If needs_update is nonzero, the client will request the guild card file by +// sending an 03E8 command. If needs_update is zero, the client will skip +// downloading the guild card file and send a 04EB command (requesting the +// stream file) instead. struct S_GuildCardChecksumResponse_BB_02E8 { - le_uint32_t verify; + le_uint32_t needs_update; le_uint32_t unused; }; @@ -2436,7 +2440,7 @@ struct S_Unknown_GC_Ep3_EB { PlayerEntry players[12]; }; -// EB (S->C): Send stream file index and chunks (BB) +// 01EB (S->C): Send stream file index (BB) // Command is a list of these; header.flag is the entry count. struct S_StreamFileIndexEntry_BB_01EB { @@ -2446,11 +2450,20 @@ struct S_StreamFileIndexEntry_BB_01EB { ptext filename; }; +// 02EB (S->C): Send stream file chunk (BB) + struct S_StreamFileChunk_BB_02EB { le_uint32_t chunk_index; uint8_t data[0x6800]; }; +// 03EB (C->S): Request a specific stream file chunk +// header.flag is the chunk index. Server should respond with a 02EB command. + +// 04EB (C->S): Request stream file header +// No arguments +// Server should respond with a 01EB command. + // EC: Create game (Episode 3) // Same format as C1; some fields are unused (e.g. episode, difficulty). diff --git a/src/Player.cc b/src/Player.cc index 5fcec9a4..774d2fdd 100644 --- a/src/Player.cc +++ b/src/Player.cc @@ -6,6 +6,7 @@ #include #include +#include #include "FileContentsCache.hh" #include "Loggers.hh" @@ -298,6 +299,10 @@ void GuildCardEntryBB::clear() { this->unknown_a1.clear(); } +uint32_t GuildCardFileBB::checksum() const { + return crc32(this, sizeof(*this)); +} + void PlayerBank::load(const string& filename) { diff --git a/src/Player.hh b/src/Player.hh index 3fea0cf8..253ef20d 100644 --- a/src/Player.hh +++ b/src/Player.hh @@ -252,6 +252,8 @@ struct GuildCardFileBB { GuildCardBB blocked[0x1C]; parray unknown_a2; GuildCardEntryBB entries[0x69]; + + uint32_t checksum() const; } __attribute__((packed)); struct KeyAndTeamConfigBB { diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 26e6fe76..f03c075c 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1587,12 +1587,13 @@ void process_client_checksum_bb(shared_ptr, shared_ptr c, constexpr size_t max_blocked = sizeof(GuildCardFileBB::blocked) / sizeof(GuildCardBB); switch (command) { case 0x01E8: { // Check guild card file checksum - check_size_v(data.size(), sizeof(C_GuildCardChecksum_01E8)); - // TODO: Presumably this response tells the client whether the file needs - // to be downloaded at all. In the future, we could actually use the - // checksum to skip downloading if the file isn't modified, to save time. - S_GuildCardChecksumResponse_BB_02E8 cmd = {1, 0}; - send_command_t(c, 0x02E8, 0x00000000, cmd); + const auto& cmd = check_size_t(data); + uint32_t checksum = c->game_data.account()->guild_cards.checksum(); + c->log.info("(Guild card file) Server checksum = %08" PRIX32 ", client checksum = %08" PRIX32, + checksum, cmd.checksum.load()); + S_GuildCardChecksumResponse_BB_02E8 response = { + (cmd.checksum != checksum), 0}; + send_command_t(c, 0x02E8, 0x00000000, response); break; } case 0x03E8: // Download guild card file diff --git a/src/SendCommands.cc b/src/SendCommands.cc index bf6367da..84194693 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -322,8 +322,7 @@ void send_player_preview_bb(shared_ptr c, uint8_t player_index, } void send_guild_card_header_bb(shared_ptr c) { - uint32_t checksum = crc32( - &c->game_data.account()->guild_cards, sizeof(GuildCardFileBB)); + uint32_t checksum = c->game_data.account()->guild_cards.checksum(); S_GuildCardHeader_BB_01DC cmd = {1, sizeof(GuildCardFileBB), checksum}; send_command_t(c, 0x01DC, 0x00000000, cmd); }