diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index edad7a5f..7bec7927 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -2204,8 +2204,11 @@ struct C_ExecuteCodeResult_B3 { // B7 (S->C): Rank update (Episode 3) struct S_RankUpdate_GC_Ep3_B7 { + // If rank is not zero, the client sets its rank text to ":", + // truncated to 11 characters. If rank is zero, the client uses rank_text + // without modifying it. le_uint32_t rank = 0; - ptext rank_text; + ptext rank_text; // Encrypted (with encrypt_challenge_rank_text) le_uint32_t meseta = 0; le_uint32_t max_meseta = 0; le_uint32_t unlocked_jukebox_songs = 0xFFFFFFFF; diff --git a/src/Player.cc b/src/Player.cc index b66d3fb4..29714a00 100644 --- a/src/Player.cc +++ b/src/Player.cc @@ -445,19 +445,19 @@ PlayerRecordsBB_Challenge::PlayerRecordsBB_Challenge(const PlayerRecordsPC_Chall unknown_l7(0) {} PlayerRecordsBB_Challenge::PlayerRecordsBB_Challenge(const PlayerRecordsV3_Challenge& rec) - : title_color(rec.title_color), - unknown_u0(rec.unknown_u0), - times_ep1_online(rec.times_ep1_online), - times_ep2_online(rec.times_ep2_online), - times_ep1_offline(rec.times_ep1_offline), - unknown_g3(rec.unknown_g3), - grave_deaths(rec.grave_deaths), - unknown_u4(rec.unknown_u4), - grave_coords_time(rec.grave_coords_time), - grave_team(rec.grave_team), - grave_message(rec.grave_message), - unknown_m5(rec.unknown_m5), - unknown_t6(rec.unknown_t6), + : title_color(rec.stats.title_color), + unknown_u0(rec.stats.unknown_u0), + times_ep1_online(rec.stats.times_ep1_online), + times_ep2_online(rec.stats.times_ep2_online), + times_ep1_offline(rec.stats.times_ep1_offline), + unknown_g3(rec.stats.unknown_g3), + grave_deaths(rec.stats.grave_deaths), + unknown_u4(rec.stats.unknown_u4), + grave_coords_time(rec.stats.grave_coords_time), + grave_team(rec.stats.grave_team), + grave_message(rec.stats.grave_message), + unknown_m5(rec.stats.unknown_m5), + unknown_t6(rec.stats.unknown_t6), rank_title(encrypt_challenge_rank_text(decode_sjis(decrypt_challenge_rank_text(rec.rank_title)))), unknown_l7(rec.unknown_l7) {} @@ -495,19 +495,19 @@ PlayerRecordsBB_Challenge::operator PlayerRecordsPC_Challenge() const { PlayerRecordsBB_Challenge::operator PlayerRecordsV3_Challenge() const { PlayerRecordsV3_Challenge ret; - ret.title_color = this->title_color; - ret.unknown_u0 = this->unknown_u0; - ret.times_ep1_online = this->times_ep1_online; - ret.times_ep2_online = this->times_ep2_online; - ret.times_ep1_offline = this->times_ep1_offline; - ret.unknown_g3 = this->unknown_g3; - ret.grave_deaths = this->grave_deaths; - ret.unknown_u4 = this->unknown_u4; - ret.grave_coords_time = this->grave_coords_time; - ret.grave_team = this->grave_team; - ret.grave_message = this->grave_message; - ret.unknown_m5 = this->unknown_m5; - ret.unknown_t6 = this->unknown_t6; + ret.stats.title_color = this->title_color; + ret.stats.unknown_u0 = this->unknown_u0; + ret.stats.times_ep1_online = this->times_ep1_online; + ret.stats.times_ep2_online = this->times_ep2_online; + ret.stats.times_ep1_offline = this->times_ep1_offline; + ret.stats.unknown_g3 = this->unknown_g3; + ret.stats.grave_deaths = this->grave_deaths; + ret.stats.unknown_u4 = this->unknown_u4; + ret.stats.grave_coords_time = this->grave_coords_time; + ret.stats.grave_team = this->grave_team; + ret.stats.grave_message = this->grave_message; + ret.stats.unknown_m5 = this->unknown_m5; + ret.stats.unknown_t6 = this->unknown_t6; ret.rank_title = encrypt_challenge_rank_text(encode_sjis(decrypt_challenge_rank_text(this->rank_title))); ret.unknown_l7 = this->unknown_l7; return ret; diff --git a/src/Player.hh b/src/Player.hh index fc3171f7..c2be3bf6 100644 --- a/src/Player.hh +++ b/src/Player.hh @@ -357,20 +357,32 @@ struct PlayerRecordsV3_Challenge { // Offsets are (1) relative to start of C5 entry, and (2) relative to start // of save file structure - /* 0000:001C */ U16T title_color = 0x7FFF; // XRGB1555 - /* 0002:001E */ parray unknown_u0; - /* 0004:0020 */ parray times_ep1_online; // Encrypted; see decrypt_challenge_time - /* 0028:0044 */ parray times_ep2_online; // Encrypted; see decrypt_challenge_time - /* 003C:0058 */ parray times_ep1_offline; // Encrypted; see decrypt_challenge_time - /* 0060:007C */ parray unknown_g3; - /* 0064:0080 */ U16T grave_deaths = 0; - /* 0066:0082 */ parray unknown_u4; - /* 0068:0084 */ parray grave_coords_time; - /* 007C:0098 */ ptext grave_team; - /* 0090:00AC */ ptext grave_message; - /* 00B0:00CC */ parray unknown_m5; - /* 00B4:00D0 */ parray unknown_t6; - /* 00D8:00F4 */ ptext rank_title; // Encrypted; see decrypt_challenge_rank_text + struct Stats { + /* 00:1C */ U16T title_color = 0x7FFF; // XRGB1555 + /* 02:1E */ parray unknown_u0; + /* 04:20 */ parray times_ep1_online; // Encrypted; see decrypt_challenge_time + /* 28:44 */ parray times_ep2_online; // Encrypted; see decrypt_challenge_time + /* 3C:58 */ parray times_ep1_offline; // Encrypted; see decrypt_challenge_time + /* 60:7C */ parray unknown_g3; + /* 64:80 */ U16T grave_deaths = 0; + /* 66:82 */ parray unknown_u4; + /* 68:84 */ parray grave_coords_time; + /* 7C:98 */ ptext grave_team; + /* 90:AC */ ptext grave_message; + /* B0:CC */ parray unknown_m5; + /* B4:D0 */ parray unknown_t6; + /* D8:F4 */ + } __attribute__((packed)); + /* 0000:001C */ Stats stats; + // On Episode 3, there are special cases that apply to this field - if the + // text ends with certain strings (after decrypt_challenge_rank_text), the + // player will have particle effects emanate from their character in the + // lobby every 2 seconds. These effects are: + // Ends with ":GOD" => blue circle + // Ends with ":KING" => white particles + // Ends with ":LORD" => rising yellow sparkles + // Ends with ":CHAMP" => green circle + /* 00D8:00F4 */ ptext rank_title; /* 00E4:0100 */ parray unknown_l7; /* 0100:011C */ } __attribute__((packed)); @@ -409,10 +421,11 @@ struct PlayerRecordsBB_Challenge { template struct PlayerRecords_Battle { using U16T = typename std::conditional::type; - // On Episode 3, battle_place_counts[0] is win count and [1] is loss count + // On Episode 3, place_counts[0] is win count and [1] is loss count /* 00 */ parray place_counts; /* 08 */ U16T disconnect_count; - /* 0A */ parray unknown_a1; + /* 0A */ parray unknown_a1; + /* 10 */ parray unknown_a2; /* 18 */ } __attribute__((packed)); diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index 97c06df1..c5cfa105 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -1069,14 +1069,14 @@ static HandlerResult C_GXB_61(shared_ptr, } if (session.options.red_name && pd->disp.visual.name_color != 0xFFFF0000) { pd->disp.visual.name_color = 0xFFFF0000; - pd->records.challenge.title_color = 0x7C00; + pd->records.challenge.stats.title_color = 0x7C00; modified = true; } else if (session.options.blank_name && pd->disp.visual.name_color != 0x00000000) { pd->disp.visual.name_color = 0x00000000; modified = true; } if (!session.challenge_rank_title_override.empty()) { - pd->records.challenge.title_color = encode_xrgb1555(session.challenge_rank_color_override); + pd->records.challenge.stats.title_color = encode_xrgb1555(session.challenge_rank_color_override); pd->records.challenge.rank_title = encrypt_challenge_rank_text(session.challenge_rank_title_override); } } diff --git a/src/SaveFileFormats.hh b/src/SaveFileFormats.hh index 129b8010..1f89b01c 100644 --- a/src/SaveFileFormats.hh +++ b/src/SaveFileFormats.hh @@ -212,12 +212,10 @@ struct PSOGCEp3CharacterFile { /* 0D7C:0960 */ parray chat_shortcuts; /* 140C:0FF0 */ ptext auto_reply; /* 14B8:109C */ ptext info_board; - /* 1564:1148 */ be_uint16_t win_count; - /* 1566:114A */ be_uint16_t lose_count; - /* 1568:114C */ parray unknown_a8; - /* 1572:1156 */ parray unused; - /* 1574:1158 */ parray unknown_a9; - /* 157C:1160 */ parray unknown_a10; + // In this struct, place_counts[0] is win_count and [1] is loss_count + /* 1564:1148 */ PlayerRecords_Battle battle_records; + /* 157C:1160 */ parray unknown_a10; + /* 1580:1164 */ PlayerRecordsV3_Challenge::Stats challenge_record_stats; /* 1658:123C */ Episode3::PlayerConfig ep3_config; /* 39A8:358C */ be_uint32_t unknown_a11; /* 39AC:3590 */ be_uint32_t unknown_a12; @@ -226,7 +224,7 @@ struct PSOGCEp3CharacterFile { } __attribute__((packed)); /* 00004 */ parray characters; /* 193F0 */ ptext serial_number; // As %08X (not decimal) - /* 19400 */ ptext access_key; + /* 19400 */ ptext access_key; // As 12 ASCII characters (decimal) /* 19410 */ ptext password; // In Episode 3, this field still exists, but is unused since BGM test was // removed from the options menu in favor of the jukebox. The jukebox is diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 42957e6d..bca85ec2 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -1382,7 +1382,7 @@ void send_lobby_list(shared_ptr c, shared_ptr s) { // lobby joining template -void send_player_records(shared_ptr c, shared_ptr l, shared_ptr joining_client) { +void send_player_records_t(shared_ptr c, shared_ptr l, shared_ptr joining_client) { vector entries; auto add_client = [&](shared_ptr lc) -> void { auto lp = lc->game_data.player(); @@ -1635,7 +1635,7 @@ void send_join_lobby_t(shared_ptr c, shared_ptr l, } if ((c->version() != GameVersion::DC) || !(c->flags & Client::Flag::IS_DC_V1)) { - send_player_records(c, l, joining_client); + send_player_records_t(c, l, joining_client); } uint8_t lobby_type = (l->type > 14) ? (l->block - 1) : l->type;