update PlayerVisualConfigV4 struct to match client implementation

This commit is contained in:
Martin Michelsen
2026-05-30 09:47:52 -07:00
parent e4054d95d9
commit 9187a3ceb0
28 changed files with 876 additions and 935 deletions
+20 -24
View File
@@ -224,7 +224,7 @@ static asio::awaitable<void> server_command_announce_inner(const Args& a, bool m
send_text_or_scrolling_message(s, a.text, a.text);
}
} else {
auto from_name = a.c->character_file()->disp.name.decode(a.c->language());
auto from_name = a.c->character_file()->disp.visual.name.decode(a.c->language());
if (mail) {
send_simple_mail(s, 0, from_name, a.text);
} else {
@@ -333,7 +333,7 @@ ChatCommandDefinition cc_auction(
static std::string name_for_client(std::shared_ptr<Client> c) {
auto player = c->character_file(false);
if (player.get()) {
return escape_player_name(player->disp.name.decode(player->inventory.language));
return escape_player_name(player->disp.visual.name.decode(player->inventory.language));
}
if (c->login) {
@@ -514,13 +514,9 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
// Client sent 61; generate a BB-format player from the information we have and save that instead
if (ch.character) {
auto bb_player = PSOBBCharacterFile::create_from_config(
a.c->login->account->account_id,
a.c->language(),
ch.character->disp.visual,
ch.character->disp.name.decode(a.c->language()),
s->level_table(a.c->version()));
bb_player->disp.visual.version = 4;
bb_player->disp.visual.name_color_checksum = 0x00000000;
a.c->login->account->account_id, a.c->language(), ch.character->disp.visual, s->level_table(a.c->version()));
bb_player->disp.visual.sh.version = 4;
bb_player->disp.visual.sh.name_color_checksum = 0x00000000;
bb_player->inventory = ch.character->inventory;
// Before V3, player stats can't be correctly computed from other fields because material usage isn't stored
// anywhere. For these versions, we have to trust the stats field from the player's data.
@@ -530,7 +526,7 @@ static asio::awaitable<void> server_command_bbchar_savechar(const Args& a, bool
bb_player->import_tethealla_material_usage(level_table);
} else {
level_table->advance_to_level(
bb_player->disp.stats, ch.character->disp.stats.level, bb_player->disp.visual.char_class);
bb_player->disp.stats, ch.character->disp.stats.level, bb_player->disp.visual.sh.char_class);
bb_player->disp.stats.char_stats.atp += bb_player->get_material_usage(PSOBBCharacterFile::MaterialType::POWER) * 2;
bb_player->disp.stats.char_stats.mst += bb_player->get_material_usage(PSOBBCharacterFile::MaterialType::MIND) * 2;
bb_player->disp.stats.char_stats.evp += bb_player->get_material_usage(PSOBBCharacterFile::MaterialType::EVADE) * 2;
@@ -629,15 +625,15 @@ ChatCommandDefinition cc_checkchar(
auto ch = phosg::load_object_file<PSOGCEp3CharacterFile::Character>(filename);
send_text_message_fmt(a.c, "Slot {}: $C6{}$C7\n{} {}\nCLv: on {}.{}, off {}.{}",
index + 1, ch.disp.visual.name.decode(),
name_for_section_id(ch.disp.visual.section_id), name_for_char_class(ch.disp.visual.char_class),
name_for_section_id(ch.disp.visual.sh.section_id), name_for_char_class(ch.disp.visual.sh.char_class),
(ch.ep3_config.online_clv_exp / 100) + 1, ch.ep3_config.online_clv_exp % 100,
(ch.ep3_config.offline_clv_exp / 100) + 1, ch.ep3_config.offline_clv_exp % 100);
} else {
std::string filename = a.c->backup_character_filename(a.c->login->account->account_id, index, false);
auto ch = PSOCHARFile::load_shared(filename, false).character_file;
send_text_message_fmt(a.c, "Slot {}: $C6{}$C7\n{} {}\nLevel {}",
index + 1, ch->disp.name.decode(),
name_for_section_id(ch->disp.visual.section_id), name_for_char_class(ch->disp.visual.char_class),
index + 1, ch->disp.visual.name.decode(),
name_for_section_id(ch->disp.visual.sh.section_id), name_for_char_class(ch->disp.visual.sh.char_class),
ch->disp.stats.level + 1);
}
} catch (const phosg::cannot_open_file&) {
@@ -955,7 +951,7 @@ ChatCommandDefinition cc_edit(
}
p->recompute_stats(s->level_table(a.c->version()), false);
} else if (tokens.at(0) == "namecolor") {
p->disp.visual.name_color = std::stoul(tokens.at(1), nullptr, 16);
p->disp.visual.sh.name_color = std::stoul(tokens.at(1), nullptr, 16);
} else if (tokens.at(0) == "language" || tokens.at(0) == "lang") {
if (tokens.at(1).size() != 1) {
throw std::runtime_error("invalid language");
@@ -976,24 +972,24 @@ ChatCommandDefinition cc_edit(
if (secid == 0xFF) {
throw precondition_failed("$C6No such section ID");
} else {
p->disp.visual.section_id = secid;
p->disp.visual.sh.section_id = secid;
}
} else if (tokens.at(0) == "name") {
std::vector<std::string> orig_tokens = phosg::split(a.text, ' ', 1);
p->disp.name.encode(orig_tokens.at(1), p->inventory.language);
p->disp.visual.name.encode(orig_tokens.at(1), p->inventory.language);
} else if (tokens.at(0) == "npc") {
if (tokens.at(1) == "none") {
p->disp.visual.extra_model = 0;
p->disp.visual.validation_flags &= 0xFD;
p->disp.visual.restore_npc_saved_fields();
p->disp.visual.sh.extra_model = 0;
p->disp.visual.sh.validation_flags &= 0xFD;
p->disp.visual.sh.restore_npc_saved_fields();
} else {
uint8_t npc = npc_for_name(tokens.at(1), a.c->version());
if (npc == 0xFF) {
throw precondition_failed("$C6No such NPC");
}
p->disp.visual.backup_npc_saved_fields();
p->disp.visual.extra_model = npc;
p->disp.visual.validation_flags |= 0x02;
p->disp.visual.sh.backup_npc_saved_fields();
p->disp.visual.sh.extra_model = npc;
p->disp.visual.sh.validation_flags |= 0x02;
}
} else if (tokens.at(0) == "tech" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
uint8_t level = std::stoul(tokens.at(2)) - 1;
@@ -2701,7 +2697,7 @@ ChatCommandDefinition cc_surrender(
if (!ps || !ps->is_alive()) {
throw precondition_failed("$C6Defeated players\ncannot surrender");
}
std::string name = remove_color(a.c->character_file()->disp.name.decode(a.c->language()));
std::string name = remove_color(a.c->character_file()->disp.visual.name.decode(a.c->language()));
send_text_message_fmt(l, "$C6{} has\nsurrendered", name);
for (const auto& watcher_l : l->watcher_lobbies) {
send_text_message_fmt(watcher_l, "$C6{} has\nsurrendered", name);
@@ -3114,7 +3110,7 @@ ChatCommandDefinition cc_where(
if (!a.c->proxy_session && l && l->is_game()) {
for (auto lc : l->clients) {
if (lc && (lc != a.c)) {
std::string name = lc->character_file()->disp.name.decode(lc->language());
std::string name = lc->character_file()->disp.visual.name.decode(lc->language());
send_text_message_fmt(a.c, "$C6{}$C7 {:X}:{}",
name, lc->floor, FloorDefinition::get(l->episode, lc->floor).short_name);
}
+4 -4
View File
@@ -78,13 +78,13 @@ const std::vector<ChoiceSearchCategory> CHOICE_SEARCH_CATEGORIES({
case 0x0000:
return true;
case 0x0010:
return target_c->character_file()->disp.visual.class_flags & 0x20;
return target_c->character_file()->disp.visual.sh.class_flags & 0x20;
case 0x0011:
return target_c->character_file()->disp.visual.class_flags & 0x40;
return target_c->character_file()->disp.visual.sh.class_flags & 0x40;
case 0x0012:
return target_c->character_file()->disp.visual.class_flags & 0x80;
return target_c->character_file()->disp.visual.sh.class_flags & 0x80;
default:
return ((choice_id - 1) == target_c->character_file()->disp.visual.char_class);
return ((choice_id - 1) == target_c->character_file()->disp.visual.sh.char_class);
}
},
},
+13 -13
View File
@@ -242,7 +242,7 @@ void Client::update_channel_name() {
auto player = this->character_file(false, false);
if (player) {
this->channel->name = std::format("C-{:X} ({} Lv.{}) @ {}",
this->id, player->disp.name.decode(this->language()), player->disp.stats.level + 1, default_name);
this->id, player->disp.visual.name.decode(this->language()), player->disp.stats.level + 1, default_name);
} else {
this->channel->name = std::format("C-{:X} @ {}", this->id, default_name);
}
@@ -349,7 +349,7 @@ std::shared_ptr<const TeamIndex::Team> Client::team() const {
// The team membership is valid, but the player name may be different; update the team membership if needed
if (p) {
auto& m = member_it->second;
std::string name = p->disp.name.decode(this->language());
std::string name = p->disp.visual.name.decode(this->language());
if (m.name != name) {
this->log.info_f("Updating player name in team config");
s->team_index->update_member_name(this->login->account->account_id, name);
@@ -625,10 +625,10 @@ void Client::save_character_file() {
void Client::create_character_file(
uint32_t guild_card_number,
Language language,
const PlayerDispDataBBPreview& preview,
const PlayerVisualConfigV4& visual,
std::shared_ptr<const LevelTable> level_table) {
this->log.info_f("Creating new character file");
this->character_data = PSOBBCharacterFile::create_from_preview(guild_card_number, language, preview, level_table);
this->character_data = PSOBBCharacterFile::create_from_config(guild_card_number, language, visual, level_table);
this->save_character_file();
this->log.info_f("Deleting bank file");
this->bank_data.reset();
@@ -654,7 +654,7 @@ void Client::create_battle_overlay(std::shared_ptr<const BattleRules> rules, std
this->overlay_character_data->inventory.tp_from_materials = 0;
uint32_t target_level = std::clamp<uint32_t>(rules->char_level, 0, 199);
uint8_t char_class = this->overlay_character_data->disp.visual.char_class;
uint8_t char_class = this->overlay_character_data->disp.visual.sh.char_class;
auto& stats = this->overlay_character_data->disp.stats;
level_table->reset_to_base(stats, char_class);
@@ -687,7 +687,7 @@ void Client::create_battle_overlay(std::shared_ptr<const BattleRules> rules, std
void Client::create_challenge_overlay(
Version version, size_t template_index, std::shared_ptr<const LevelTable> level_table) {
auto p = this->character_file(true, false);
const auto& tpl = get_challenge_template_definition(version, p->disp.visual.class_flags, template_index);
const auto& tpl = get_challenge_template_definition(version, p->disp.visual.sh.class_flags, template_index);
this->overlay_character_data = std::make_shared<PSOBBCharacterFile>(*p);
auto overlay = this->overlay_character_data;
@@ -704,11 +704,11 @@ void Client::create_challenge_overlay(
overlay->inventory.items[13].extension_data2 = 1;
level_table->reset_to_base(overlay->disp.stats, overlay->disp.visual.char_class);
level_table->advance_to_level(overlay->disp.stats, tpl.level, overlay->disp.visual.char_class);
level_table->reset_to_base(overlay->disp.stats, overlay->disp.visual.sh.char_class);
level_table->advance_to_level(overlay->disp.stats, tpl.level, overlay->disp.visual.sh.char_class);
const auto& stats_delta = level_table->stats_delta_for_level(
overlay->disp.visual.char_class, overlay->disp.stats.level);
overlay->disp.visual.sh.char_class, overlay->disp.stats.level);
overlay->disp.stats.esp = 40;
overlay->disp.stats.attack_range = 10.0;
overlay->disp.stats.exp = stats_delta.exp;
@@ -968,12 +968,12 @@ void Client::load_all_files() {
this->character_data->death_count = nsc_data.death_count;
this->character_data->bank = nsc_data.bank;
this->character_data->guild_card.guild_card_number = this->login->account->account_id;
this->character_data->guild_card.name = nsc_data.disp.name;
this->character_data->guild_card.name = nsc_data.disp.visual.name;
this->character_data->guild_card.description = nsc_data.guild_card_description;
this->character_data->guild_card.present = 1;
this->character_data->guild_card.language = nsc_data.inventory.language;
this->character_data->guild_card.section_id = nsc_data.disp.visual.section_id;
this->character_data->guild_card.char_class = nsc_data.disp.visual.char_class;
this->character_data->guild_card.section_id = nsc_data.disp.visual.sh.section_id;
this->character_data->guild_card.char_class = nsc_data.disp.visual.sh.char_class;
this->character_data->auto_reply = nsc_data.auto_reply;
this->character_data->info_board = nsc_data.info_board;
this->character_data->battle_records = nsc_data.battle_records;
@@ -1023,7 +1023,7 @@ void Client::load_all_files() {
if (this->character_data) {
// Clear legacy play_time field
this->character_data->disp.name.clear_after_bytes(0x18);
this->character_data->disp.visual.name.clear_after_bytes(0x18);
this->character_data->inventory.enforce_stack_limits(stack_limits);
this->login->account->auto_reply_message = this->character_data->auto_reply.decode();
this->login->account->save();
+2 -2
View File
@@ -187,7 +187,7 @@ public:
};
bool should_update_play_time;
std::unordered_set<uint32_t> blocked_senders;
std::unique_ptr<PlayerDispDataDCPCV3> v1_v2_last_reported_disp;
std::unique_ptr<PlayerDispDataV123> v1_v2_last_reported_disp;
std::shared_ptr<Parsed6x70Data> last_reported_6x70;
std::unordered_set<uint16_t> expected_game_state_sync_commands; // (command_num << 8) | target_client_id
// These are null unless the client is within the trade sequence (D0-D4 or EE commands)
@@ -312,7 +312,7 @@ public:
void create_character_file(
uint32_t guild_card_number,
Language language,
const PlayerDispDataBBPreview& preview,
const PlayerVisualConfigV4& visual,
std::shared_ptr<const LevelTable> level_table);
void create_battle_overlay(std::shared_ptr<const BattleRules> rules, std::shared_ptr<const LevelTable> level_table);
void create_challenge_overlay(Version version, size_t template_index, std::shared_ptr<const LevelTable> level_table);
+31 -29
View File
@@ -412,18 +412,18 @@ struct S_UpdateClientConfig_DC_PC_04 {
le_uint32_t guild_card_number = 0;
} __packed_ws__(S_UpdateClientConfig_DC_PC_04, 8);
struct S_UpdateClientConfig_V3_04 {
template <size_t ClientConfigBytes>
struct S_UpdateClientConfigT_V3_BB_04 {
le_uint32_t player_tag = 0x00010000;
le_uint32_t guild_card_number = 0;
// This field is opaque to the client; it will send back the contents verbatim in subsequent 9E or 9F commands.
parray<uint8_t, 0x20> client_config;
} __packed_ws__(S_UpdateClientConfig_V3_04, 0x28);
parray<uint8_t, ClientConfigBytes> client_config;
} __attribute__((packed));
struct S_UpdateClientConfig_BB_04 {
le_uint32_t player_tag = 0x00010000;
le_uint32_t guild_card_number = 0;
parray<uint8_t, 0x28> client_config;
} __packed_ws__(S_UpdateClientConfig_BB_04, 0x30);
using S_UpdateClientConfig_V3_04 = S_UpdateClientConfigT_V3_BB_04<0x20>;
using S_UpdateClientConfig_BB_04 = S_UpdateClientConfigT_V3_BB_04<0x28>;
check_struct_size(S_UpdateClientConfig_V3_04, 0x28);
check_struct_size(S_UpdateClientConfig_BB_04, 0x30);
// 05: Disconnect
// Internal name: SndLogout
@@ -1053,13 +1053,13 @@ struct PlayerRecordsEntry_BB {
struct C_CharacterData_DCv1_61_98 {
/* 0000 */ PlayerInventory inventory;
/* 034C */ PlayerDispDataDCPCV3 disp;
/* 034C */ PlayerDispDataV123 disp;
/* 041C */
} __packed_ws__(C_CharacterData_DCv1_61_98, 0x041C);
struct C_CharacterData_DCv2_61_98 {
/* 0000 */ PlayerInventory inventory;
/* 034C */ PlayerDispDataDCPCV3 disp;
/* 034C */ PlayerDispDataV123 disp;
/* 041C */ PlayerRecordsEntry_DC records;
/* 04D8 */ ChoiceSearchConfig choice_search_config;
/* 04F0 */
@@ -1067,7 +1067,7 @@ struct C_CharacterData_DCv2_61_98 {
struct C_CharacterData_PC_61_98 {
/* 0000 */ PlayerInventory inventory;
/* 034C */ PlayerDispDataDCPCV3 disp;
/* 034C */ PlayerDispDataV123 disp;
/* 041C */ PlayerRecordsEntry_PC records;
/* 0510 */ ChoiceSearchConfig choice_search_config;
/* 0528 */ parray<le_uint32_t, 0x1E> blocked_senders;
@@ -1079,7 +1079,7 @@ struct C_CharacterData_PC_61_98 {
struct C_CharacterData_GCNTE_61_98 {
/* 0000 */ PlayerInventory inventory;
/* 034C */ PlayerDispDataDCPCV3 disp;
/* 034C */ PlayerDispDataV123 disp;
/* 041C */ PlayerRecordsEntry_DC records;
/* 04D8 */ ChoiceSearchConfig choice_search_config;
/* 04F0 */ parray<le_uint32_t, 0x1E> blocked_senders;
@@ -1091,7 +1091,7 @@ struct C_CharacterData_GCNTE_61_98 {
struct C_CharacterData_V3_61_98 {
/* 0000 */ PlayerInventory inventory;
/* 034C */ PlayerDispDataDCPCV3 disp;
/* 034C */ PlayerDispDataV123 disp;
/* 041C */ PlayerRecordsEntry_V3 records;
/* 0538 */ ChoiceSearchConfig choice_search_config;
/* 0550 */ pstring<TextEncoding::MARKED, 0xAC> info_board;
@@ -1104,7 +1104,7 @@ struct C_CharacterData_V3_61_98 {
struct C_CharacterData_Ep3_61_98 {
/* 0000 */ PlayerInventory inventory;
/* 034C */ PlayerDispDataDCPCV3 disp;
/* 034C */ PlayerDispDataV123 disp;
/* 041C */ PlayerRecordsEntry_V3 records;
/* 0538 */ ChoiceSearchConfig choice_search_config;
/* 0550 */ pstring<TextEncoding::MARKED, 0xAC> info_board;
@@ -1117,7 +1117,7 @@ struct C_CharacterData_Ep3_61_98 {
struct C_CharacterData_BB_61_98 {
/* 0000 */ PlayerInventory inventory;
/* 034C */ PlayerDispDataBB disp;
/* 034C */ PlayerDispDataV4 disp;
/* 04DC */ PlayerRecordsEntry_BB records;
/* 0638 */ ChoiceSearchConfig choice_search_config;
/* 0650 */ pstring<TextEncoding::UTF16, 0xAC> info_board;
@@ -1215,7 +1215,7 @@ struct S_JoinGame_Ep3_64 : S_JoinGame_GC_64 {
// four of these are always present and they are filled in in slot positions.
struct Ep3PlayerEntry {
PlayerInventory inventory;
PlayerDispDataDCPCV3 disp;
PlayerDispDataV123 disp;
} __packed_ws__(Ep3PlayerEntry, 0x41C);
parray<Ep3PlayerEntry, 4> players_ep3;
} __packed_ws__(S_JoinGame_Ep3_64, 0x1180);
@@ -1292,10 +1292,10 @@ struct S_JoinLobbyT {
return offsetof(S_JoinLobbyT, entries) + used_entries * sizeof(Entry);
}
} __attribute__((packed));
using S_JoinLobby_DCNTE_65_67_68 = S_JoinLobbyT<LobbyFlagsDCNTE, PlayerLobbyDataDCGC, PlayerDispDataDCPCV3>;
using S_JoinLobby_PC_65_67_68 = S_JoinLobbyT<LobbyFlags, PlayerLobbyDataPC, PlayerDispDataDCPCV3>;
using S_JoinLobby_DC_GC_65_67_68_Ep3_EB = S_JoinLobbyT<LobbyFlags, PlayerLobbyDataDCGC, PlayerDispDataDCPCV3>;
using S_JoinLobby_BB_65_67_68 = S_JoinLobbyT<LobbyFlags, PlayerLobbyDataBB, PlayerDispDataBB>;
using S_JoinLobby_DCNTE_65_67_68 = S_JoinLobbyT<LobbyFlagsDCNTE, PlayerLobbyDataDCGC, PlayerDispDataV123>;
using S_JoinLobby_PC_65_67_68 = S_JoinLobbyT<LobbyFlags, PlayerLobbyDataPC, PlayerDispDataV123>;
using S_JoinLobby_DC_GC_65_67_68_Ep3_EB = S_JoinLobbyT<LobbyFlags, PlayerLobbyDataDCGC, PlayerDispDataV123>;
using S_JoinLobby_BB_65_67_68 = S_JoinLobbyT<LobbyFlags, PlayerLobbyDataBB, PlayerDispDataV4>;
check_struct_size(S_JoinLobby_DCNTE_65_67_68, 0x32D4);
check_struct_size(S_JoinLobby_PC_65_67_68, 0x339C);
check_struct_size(S_JoinLobby_DC_GC_65_67_68_Ep3_EB, 0x32DC);
@@ -1307,7 +1307,7 @@ struct S_JoinLobby_XB_65_67_68 {
struct Entry {
PlayerLobbyDataXB lobby_data;
PlayerInventory inventory;
PlayerDispDataDCPCV3 disp;
PlayerDispDataV123 disp;
} __packed_ws__(Entry, 0x468);
// Note: not all of these will be filled in and sent if the lobby isn't full (the command size will be shorter than
// this struct's size)
@@ -2948,7 +2948,7 @@ struct S_CardBattleTableConfirmation_Ep3_E5 {
struct SC_PlayerPreview_CreateCharacter_BB_00E5 {
le_int32_t character_index = 0;
PlayerDispDataBBPreview preview;
PlayerDispDataV4Preview preview;
} __packed_ws__(SC_PlayerPreview_CreateCharacter_BB_00E5, 0x80);
// E6 (C->S): Spectator team control (Episode 3)
@@ -3034,7 +3034,7 @@ struct S_JoinSpectatorTeam_Ep3_E8 {
struct PlayerEntry {
/* 0000 */ PlayerLobbyDataDCGC lobby_data;
/* 0020 */ PlayerInventory inventory;
/* 036C */ PlayerDispDataDCPCV3 disp;
/* 036C */ PlayerDispDataV123 disp;
/* 043C */
} __packed_ws__(PlayerEntry, 0x43C);
/* 0080 */ parray<PlayerEntry, 4> players;
@@ -4761,7 +4761,7 @@ struct G_SyncPlayerDispAndInventory_DCNTE_6x70 {
/* 0054 */ PlayerHoldState_DCProtos hold_state;
/* 0064 */ le_uint32_t area = 0;
/* 0068 */ le_uint32_t player_flags = 0;
/* 006C */ PlayerVisualConfig visual;
/* 006C */ PlayerVisualConfigV123 visual;
/* 00BC */ PlayerStats stats;
/* 00E0 */ le_uint32_t num_items = 0;
/* 00E4 */ parray<PlayerInventoryItem, 0x1E> items;
@@ -4779,7 +4779,7 @@ struct G_SyncPlayerDispAndInventory_DC112000_6x70 {
/* 0060 */ PlayerHoldState_DCProtos hold_state;
/* 0070 */ le_uint32_t area = 0;
/* 0074 */ le_uint32_t player_flags = 0;
/* 0078 */ PlayerVisualConfig visual;
/* 0078 */ PlayerVisualConfigV123 visual;
/* 00C8 */ PlayerStats stats;
/* 00EC */ le_uint32_t num_items = 0;
/* 00F0 */ parray<PlayerInventoryItem, 0x1E> items;
@@ -4810,13 +4810,13 @@ struct G_6x70_Base_V1 {
/* 00AC */ le_uint32_t area = 0;
/* 00B0 */ le_uint32_t player_flags = 0;
/* 00B4 */ parray<uint8_t, 0x14> technique_levels_v1 = 0xFF; // Last byte is uninitialized
/* 00C8 */ PlayerVisualConfig visual;
/* 0118 */
} __packed_ws__(G_6x70_Base_V1, 0x118);
/* 00C8 */
} __packed_ws__(G_6x70_Base_V1, 0xC8);
struct G_SyncPlayerDispAndInventory_DC_PC_6x70 {
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC_PC_6x70)};
/* 0008 */ G_6x70_Base_V1 base;
/* 00D0 */ PlayerVisualConfigV123 visual;
/* 0120 */ PlayerStats stats;
/* 0144 */ le_uint32_t num_items = 0;
/* 0148 */ parray<PlayerInventoryItem, 0x1E> items;
@@ -4827,6 +4827,7 @@ struct G_SyncPlayerDispAndInventory_DC_PC_6x70 {
struct G_SyncPlayerDispAndInventory_GC_6x70 {
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_GC_6x70)};
/* 0008 */ G_6x70_Base_V1 base;
/* 00D0 */ PlayerVisualConfigV123 visual;
/* 0120 */ PlayerStats stats;
/* 0144 */ le_uint32_t num_items = 0;
/* 0148 */ parray<PlayerInventoryItem, 0x1E> items;
@@ -4837,6 +4838,7 @@ struct G_SyncPlayerDispAndInventory_GC_6x70 {
struct G_SyncPlayerDispAndInventory_XB_6x70 {
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_XB_6x70)};
/* 0008 */ G_6x70_Base_V1 base;
/* 00D0 */ PlayerVisualConfigV123 visual;
/* 0120 */ PlayerStats stats;
/* 0144 */ le_uint32_t num_items = 0;
/* 0148 */ parray<PlayerInventoryItem, 0x1E> items;
@@ -4850,7 +4852,7 @@ struct G_SyncPlayerDispAndInventory_XB_6x70 {
struct G_SyncPlayerDispAndInventory_BB_6x70 {
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_BB_6x70)};
/* 0008 */ G_6x70_Base_V1 base;
/* 0120 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> name;
/* 00D0 */ PlayerVisualConfigV4 visual;
/* 0140 */ PlayerStats stats;
/* 0164 */ le_uint32_t num_items = 0;
/* 0168 */ parray<PlayerInventoryItem, 0x1E> items;
+8 -8
View File
@@ -149,7 +149,7 @@ void DownloadSession::send_93_9D_9E(bool extended) {
ret.serial_number.encode(std::format("{:08X}", this->serial_number));
ret.access_key.encode(this->access_key);
ret.serial_number2.encode(std::format("{:08X}", this->serial_number2));
ret.login_character_name.encode(this->character->disp.name.decode());
ret.login_character_name.encode(this->character->disp.visual.name.decode());
this->channel->send(0x93, 0x01, &ret, extended ? sizeof(ret) : sizeof(C_LoginV1_DC_93));
} else if (is_v2(this->version)) {
@@ -164,7 +164,7 @@ void DownloadSession::send_93_9D_9E(bool extended) {
ret.access_key.encode(this->access_key);
ret.serial_number2 = ret.serial_number;
ret.access_key2 = ret.access_key;
ret.login_character_name.encode(this->character->disp.name.decode());
ret.login_character_name.encode(this->character->disp.visual.name.decode());
size_t data_size = extended
? ((this->version == Version::PC_V2) ? sizeof(ret) : sizeof(C_LoginExtended_DC_GC_9D))
: sizeof(C_Login_DC_PC_GC_9D);
@@ -182,7 +182,7 @@ void DownloadSession::send_93_9D_9E(bool extended) {
ret.access_key.encode(this->access_key);
ret.serial_number2 = ret.serial_number;
ret.access_key2 = ret.access_key;
ret.login_character_name.encode(this->character->disp.name.decode());
ret.login_character_name.encode(this->character->disp.visual.name.decode());
ret.client_config = this->client_config;
this->channel->send(0x9E, 0x01, &ret, extended ? sizeof(ret) : sizeof(C_Login_PC_GC_9E));
@@ -198,7 +198,7 @@ void DownloadSession::send_93_9D_9E(bool extended) {
ret.access_key.encode(std::format("{:016X}", this->xb_user_id));
ret.serial_number2 = ret.serial_number;
ret.access_key2 = ret.access_key;
ret.login_character_name.encode(this->character->disp.name.decode());
ret.login_character_name.encode(this->character->disp.visual.name.decode());
ret.xb_netloc.internal_ipv4_address = phosg::random_object<uint32_t>();
ret.xb_netloc.external_ipv4_address = phosg::random_object<uint32_t>();
ret.xb_netloc.port = 9500;
@@ -222,13 +222,13 @@ void DownloadSession::send_61_98(bool is_98) {
if (is_v1(this->version)) {
C_CharacterData_DCv1_61_98 ret;
ret.inventory = this->character->inventory;
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
ret.disp = convert_player_disp_data<PlayerDispDataV123, PlayerDispDataV4>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
this->channel->send(command, 0x01, ret);
} else if (this->version == Version::DC_V2) {
C_CharacterData_DCv2_61_98 ret;
ret.inventory = this->character->inventory;
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
ret.disp = convert_player_disp_data<PlayerDispDataV123, PlayerDispDataV4>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
ret.records.challenge = this->character->challenge_records;
ret.records.battle = this->character->battle_records;
ret.choice_search_config = this->character->choice_search_config;
@@ -237,7 +237,7 @@ void DownloadSession::send_61_98(bool is_98) {
} else if (this->version == Version::PC_V2) {
C_CharacterData_PC_61_98 ret;
ret.inventory = this->character->inventory;
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
ret.disp = convert_player_disp_data<PlayerDispDataV123, PlayerDispDataV4>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
ret.records.challenge = this->character->challenge_records;
ret.records.battle = this->character->battle_records;
ret.choice_search_config = this->character->choice_search_config;
@@ -246,7 +246,7 @@ void DownloadSession::send_61_98(bool is_98) {
} else if (is_v3(this->version)) {
C_CharacterData_V3_61_98 ret;
ret.inventory = this->character->inventory;
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
ret.disp = convert_player_disp_data<PlayerDispDataV123, PlayerDispDataV4>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
ret.records.challenge = this->character->challenge_records;
ret.records.battle = this->character->battle_records;
ret.choice_search_config = this->character->choice_search_config;
+1 -1
View File
@@ -170,7 +170,7 @@ std::string BattleRecord::serialize() const {
void BattleRecord::add_player(
const PlayerLobbyDataDCGC& lobby_data,
const PlayerInventory& inventory,
const PlayerDispDataDCPCV3& disp,
const PlayerDispDataV123& disp,
uint32_t level) {
if (!this->is_writable) {
throw std::logic_error("cannot write to battle record");
+2 -2
View File
@@ -22,7 +22,7 @@ public:
struct PlayerEntry {
PlayerLobbyDataDCGC lobby_data;
PlayerInventory inventory;
PlayerDispDataDCPCV3 disp;
PlayerDispDataV123 disp;
le_uint32_t level;
void print(FILE* stream) const;
@@ -85,7 +85,7 @@ public:
void add_player(
const PlayerLobbyDataDCGC& lobby_data,
const PlayerInventory& inventory,
const PlayerDispDataDCPCV3& disp,
const PlayerDispDataV123& disp,
uint32_t level);
void delete_player(uint8_t client_id);
void add_command(Event::Type type, const void* data, size_t size);
+1 -1
View File
@@ -905,7 +905,7 @@ struct PlayerConfig {
/* 2270:211C */ be_uint32_t unknown_t3;
// This visual config is copied to the player's main visual config when the player's name or proportions have
// changed, or when certain buttons on the controller (L, R, X, Y) are held at game start time.
/* 2274:2120 */ PlayerVisualConfig backup_visual;
/* 2274:2120 */ PlayerVisualConfigV123 backup_visual;
/* 22C4:2170 */ parray<uint8_t, 0x8C> unknown_a14;
/* 2350:21FC */
+1 -1
View File
@@ -15,7 +15,7 @@ Tournament::PlayerEntry::PlayerEntry(uint32_t account_id, const std::string& pla
Tournament::PlayerEntry::PlayerEntry(std::shared_ptr<Client> c)
: account_id(c->login->account->account_id),
client(c),
player_name(c->character_file()->disp.name.decode(c->language())) {}
player_name(c->character_file()->disp.visual.name.decode(c->language())) {}
Tournament::PlayerEntry::PlayerEntry(std::shared_ptr<const COMDeckDefinition> com_deck)
: account_id(0), com_deck(com_deck) {}
+1 -1
View File
@@ -93,7 +93,7 @@ std::vector<std::shared_ptr<Client>> GameServer::get_clients_by_identifier(const
}
auto p = c->character_file(false, false);
if (p && p->disp.name.eq(ident, p->inventory.language)) {
if (p && p->disp.visual.name.eq(ident, p->inventory.language)) {
results.emplace_back(c);
continue;
}
+18 -18
View File
@@ -160,22 +160,22 @@ HTTPServer::HTTPServer(std::shared_ptr<ServerState> state)
client_json.emplace("TechniqueLevels", std::move(tech_levels_json));
}
client_json.emplace("Level", p->disp.stats.level.load() + 1);
client_json.emplace("NameColor", p->disp.visual.name_color.load());
client_json.emplace("ExtraModel", (p->disp.visual.validation_flags & 2) ? p->disp.visual.extra_model : phosg::JSON(nullptr));
client_json.emplace("SectionID", name_for_section_id(p->disp.visual.section_id));
client_json.emplace("CharClass", name_for_char_class(p->disp.visual.char_class));
client_json.emplace("Costume", p->disp.visual.costume.load());
client_json.emplace("Skin", p->disp.visual.skin.load());
client_json.emplace("Face", p->disp.visual.face.load());
client_json.emplace("Head", p->disp.visual.head.load());
client_json.emplace("Hair", p->disp.visual.hair.load());
client_json.emplace("HairR", p->disp.visual.hair_r.load());
client_json.emplace("HairG", p->disp.visual.hair_g.load());
client_json.emplace("HairB", p->disp.visual.hair_b.load());
client_json.emplace("ProportionX", p->disp.visual.proportion_x.load());
client_json.emplace("ProportionY", p->disp.visual.proportion_y.load());
client_json.emplace("NameColor", p->disp.visual.sh.name_color.load());
client_json.emplace("ExtraModel", (p->disp.visual.sh.validation_flags & 2) ? p->disp.visual.sh.extra_model : phosg::JSON(nullptr));
client_json.emplace("SectionID", name_for_section_id(p->disp.visual.sh.section_id));
client_json.emplace("CharClass", name_for_char_class(p->disp.visual.sh.char_class));
client_json.emplace("Costume", p->disp.visual.sh.costume.load());
client_json.emplace("Skin", p->disp.visual.sh.skin.load());
client_json.emplace("Face", p->disp.visual.sh.face.load());
client_json.emplace("Head", p->disp.visual.sh.head.load());
client_json.emplace("Hair", p->disp.visual.sh.hair.load());
client_json.emplace("HairR", p->disp.visual.sh.hair_r.load());
client_json.emplace("HairG", p->disp.visual.sh.hair_g.load());
client_json.emplace("HairB", p->disp.visual.sh.hair_b.load());
client_json.emplace("ProportionX", p->disp.visual.sh.proportion_x.load());
client_json.emplace("ProportionY", p->disp.visual.sh.proportion_y.load());
client_json.emplace("Name", p->disp.name.decode(c->language()));
client_json.emplace("Name", p->disp.visual.name.decode(c->language()));
client_json.emplace("PlayTimeSeconds", p->play_time_seconds.load());
client_json.emplace("AutoReply", p->auto_reply.decode(c->language()));
@@ -589,12 +589,12 @@ HTTPServer::HTTPServer(std::shared_ptr<ServerState> state)
clients_json.emplace_back(phosg::JSON::dict({
{"ID", c->id},
{"AccountID", c->login ? c->login->account->account_id : phosg::JSON(nullptr)},
{"Name", p ? p->disp.name.decode(c->language()) : phosg::JSON(nullptr)},
{"Name", p ? p->disp.visual.name.decode(c->language()) : phosg::JSON(nullptr)},
{"Version", phosg::name_for_enum(c->version())},
{"Language", name_for_language(c->language())},
{"Level", p ? (p->disp.stats.level + 1) : phosg::JSON(nullptr)},
{"Class", p ? name_for_char_class(p->disp.visual.char_class) : phosg::JSON(nullptr)},
{"SectionID", p ? name_for_section_id(p->disp.visual.section_id) : phosg::JSON(nullptr)},
{"Class", p ? name_for_char_class(p->disp.visual.sh.char_class) : phosg::JSON(nullptr)},
{"SectionID", p ? name_for_section_id(p->disp.visual.sh.section_id) : phosg::JSON(nullptr)},
{"LobbyID", l ? l->lobby_id : phosg::JSON(nullptr)},
{"IsOnProxy", c->proxy_session ? true : false},
}));
+6 -6
View File
@@ -22,7 +22,7 @@ void player_use_item(std::shared_ptr<Client> c, size_t item_index, std::shared_p
} else if ((primary_identifier & 0xFFFF0000) == 0x03020000) { // Technique disk
auto item_parameter_table = s->item_parameter_table(c->version());
uint8_t max_level = item_parameter_table->get_max_tech_level(player->disp.visual.char_class, item.data.data1[4]);
uint8_t max_level = item_parameter_table->get_max_tech_level(player->disp.visual.sh.char_class, item.data.data1[4]);
if (item.data.data1[2] > max_level) {
throw std::runtime_error("technique level too high");
}
@@ -135,10 +135,10 @@ void player_use_item(std::shared_ptr<Client> c, size_t item_index, std::shared_p
if (evolution_number < 4) {
switch (item.data.data1[2]) {
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.sh.section_id & 1) ? 0x1D : 0x21;
break;
case 0x01: // Cell of MAG 213
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x27 : 0x22;
mag.data.data1[1] = (player->disp.visual.sh.section_id & 1) ? 0x27 : 0x22;
break;
case 0x02: // Parts of RoboChao
mag.data.data1[1] = 0x28;
@@ -215,7 +215,7 @@ void player_use_item(std::shared_ptr<Client> c, size_t item_index, std::shared_p
try {
auto item_parameter_table = s->item_parameter_table(c->version());
const auto& combo = item_parameter_table->get_item_combination(item.data, inv_item.data);
if (combo.char_class != 0xFF && combo.char_class != player->disp.visual.char_class) {
if (combo.char_class != 0xFF && combo.char_class != player->disp.visual.sh.char_class) {
throw std::runtime_error("item combination requires specific char_class");
}
if (combo.mag_level != 0xFF) {
@@ -490,7 +490,7 @@ void player_feed_mag(std::shared_ptr<Client> c, size_t mag_item_index, size_t fe
player->inventory.items[fed_item_index].data,
s->item_parameter_table(c->version()),
s->mag_evolution_table(c->version()),
player->disp.visual.char_class,
player->disp.visual.section_id,
player->disp.visual.sh.char_class,
player->disp.visual.sh.section_id,
!is_v1_or_v2(c->version()));
}
+4 -4
View File
@@ -242,7 +242,7 @@ uint8_t Lobby::effective_section_id() const {
}
auto leader = this->clients.at(this->leader_id);
if (leader) {
return leader->character_file()->disp.visual.section_id;
return leader->character_file()->disp.visual.sh.section_id;
}
return 0xFF;
}
@@ -459,11 +459,11 @@ void Lobby::add_client(std::shared_ptr<Client> c, ssize_t required_client_id) {
PlayerLobbyDataDCGC lobby_data;
lobby_data.player_tag = 0x00010000;
lobby_data.guild_card_number = c->login->account->account_id;
lobby_data.name.encode(p->disp.name.decode(c->language()), c->language());
lobby_data.name.encode(p->disp.visual.name.decode(c->language()), c->language());
this->battle_record->add_player(
lobby_data,
p->inventory,
p->disp.to_dcpcv3<false>(c->language(), c->language()),
p->disp.to_v123<false>(c->language(), c->language()),
c->ep3_config ? (c->ep3_config->online_clv_exp / 100) : 0);
}
@@ -591,7 +591,7 @@ std::shared_ptr<Client> Lobby::find_client(const std::string* identifier, uint64
if (account_id && lc->login && (lc->login->account->account_id == account_id)) {
return lc;
}
if (identifier && (lc->character_file()->disp.name.eq(*identifier, lc->language()))) {
if (identifier && (lc->character_file()->disp.visual.name.eq(*identifier, lc->language()))) {
return lc;
}
}
+1 -1
View File
@@ -2890,7 +2890,7 @@ static const std::vector<DATEntityDefinition> dat_enemy_definitions({
// Delsaber. Params:
// param1 = jump distance delta (value used is param1 + 100)
// param2 = prejudice flag (these correspond to the bits in PlayerVisualConfig::class_flags):
// param2 = prejudice flag (these correspond to the bits in PlayerVisualConfigV123T::class_flags):
// 0 = males
// 1 = females
// 2 = humans
+20 -20
View File
@@ -20,26 +20,26 @@
#include "Text.hh"
#include "Version.hh"
void PlayerDispDataBB::apply_dressing_room(const PlayerDispDataBBPreview& pre) {
this->visual.name_color = pre.visual.name_color;
this->visual.extra_model = pre.visual.extra_model;
this->visual.name_color_checksum = pre.visual.name_color_checksum;
this->visual.section_id = pre.visual.section_id;
this->visual.char_class = pre.visual.char_class;
this->visual.validation_flags = pre.visual.validation_flags;
this->visual.version = pre.visual.version;
this->visual.class_flags = pre.visual.class_flags;
this->visual.costume = pre.visual.costume;
this->visual.skin = pre.visual.skin;
this->visual.face = pre.visual.face;
this->visual.head = pre.visual.head;
this->visual.hair = pre.visual.hair;
this->visual.hair_r = pre.visual.hair_r;
this->visual.hair_g = pre.visual.hair_g;
this->visual.hair_b = pre.visual.hair_b;
this->visual.proportion_x = pre.visual.proportion_x;
this->visual.proportion_y = pre.visual.proportion_y;
this->name = pre.name;
void PlayerVisualConfigV4::apply_dressing_room(const PlayerVisualConfigV4& new_visual) {
this->sh.name_color = new_visual.sh.name_color;
this->sh.extra_model = new_visual.sh.extra_model;
this->sh.name_color_checksum = new_visual.sh.name_color_checksum;
this->sh.section_id = new_visual.sh.section_id;
this->sh.char_class = new_visual.sh.char_class;
this->sh.validation_flags = new_visual.sh.validation_flags;
this->sh.version = new_visual.sh.version;
this->sh.class_flags = new_visual.sh.class_flags;
this->sh.costume = new_visual.sh.costume;
this->sh.skin = new_visual.sh.skin;
this->sh.face = new_visual.sh.face;
this->sh.head = new_visual.sh.head;
this->sh.hair = new_visual.sh.hair;
this->sh.hair_r = new_visual.sh.hair_r;
this->sh.hair_g = new_visual.sh.hair_g;
this->sh.hair_b = new_visual.sh.hair_b;
this->sh.proportion_x = new_visual.sh.proportion_x;
this->sh.proportion_y = new_visual.sh.proportion_y;
this->name = new_visual.name;
}
void GuildCardBB::clear() {
+120 -81
View File
@@ -23,64 +23,64 @@
class Client;
class ItemParameterTable;
struct PlayerDispDataBB;
struct PlayerDispDataV4;
struct PlayerVisualConfigV4;
template <bool BE>
struct PlayerVisualConfigT {
/* 00 */ pstring<TextEncoding::ASCII, 0x10> name;
/* 10 */ parray<uint8_t, 8> unknown_a2;
/* 18 */ U32T<BE> name_color = 0xFFFFFFFF; // ARGB
/* 1C */ uint8_t extra_model = 0;
struct PlayerVisualConfigSharedT {
/* 00 */ parray<uint8_t, 8> unknown_a2;
/* 08 */ U32T<BE> name_color = 0xFFFFFFFF; // ARGB
/* 0C */ uint8_t extra_model = 0;
// Some NPCs can crash the client if the character's class is incorrect. To handle this, we save the affected fields
// in the unused bytes after extra_model. This is a newserv-specific extension; it appears the following 15 bytes
// were simply never used by Sega.
/* 1D */ uint8_t npc_saved_data_type = 0;
/* 1E */ uint8_t npc_saved_costume = 0;
/* 1F */ uint8_t npc_saved_skin = 0;
/* 20 */ uint8_t npc_saved_face = 0;
/* 21 */ uint8_t npc_saved_head = 0;
/* 22 */ uint8_t npc_saved_hair = 0;
/* 23 */ uint8_t npc_saved_hair_r = 0;
/* 24 */ uint8_t npc_saved_hair_g = 0;
/* 25 */ uint8_t npc_saved_hair_b = 0;
/* 26 */ parray<uint8_t, 2> unused;
/* 28 */ F32T<BE> npc_saved_proportion_y = 0.0;
/* 0D */ uint8_t npc_saved_data_type = 0;
/* 0E */ uint8_t npc_saved_costume = 0;
/* 0F */ uint8_t npc_saved_skin = 0;
/* 10 */ uint8_t npc_saved_face = 0;
/* 11 */ uint8_t npc_saved_head = 0;
/* 12 */ uint8_t npc_saved_hair = 0;
/* 13 */ uint8_t npc_saved_hair_r = 0;
/* 14 */ uint8_t npc_saved_hair_g = 0;
/* 15 */ uint8_t npc_saved_hair_b = 0;
/* 16 */ parray<uint8_t, 2> unused;
/* 18 */ F32T<BE> npc_saved_proportion_y = 0.0;
// See compute_name_color_checksum for details on how this is computed. If the value is incorrect, V1 and V2 will
// ignore the name_color field and use the default color instead. This field is ignored on GC; on BB (and presumably
// Xbox), if this has a nonzero value, the "Change Name" option appears in the character selection menu.
/* 2C */ U32T<BE> name_color_checksum = 0;
/* 30 */ uint8_t section_id = 0;
/* 31 */ uint8_t char_class = 0;
/* 1C */ U32T<BE> name_color_checksum = 0;
/* 20 */ uint8_t section_id = 0;
/* 21 */ uint8_t char_class = 0;
// validation_flags specifies that some parts of this structure are not valid and should be ignored. The bits are:
// -----FCS
// F = class_flags is incorrect for the character's char_class value
// C = char_class is out of range
// S = section_id is out of range
/* 32 */ uint8_t validation_flags = 0;
/* 33 */ uint8_t version = 0;
/* 22 */ uint8_t validation_flags = 0;
/* 23 */ uint8_t version = 0;
// class_flags specifies features of the character's class. The bits are:
// -------- -------- -------- FRHANMfm
// F = force, R = ranger, H = hunter
// A = android, N = newman, M = human
// f = female, m = male
// Enemies also have a class_flags field, though it isn't part of PlayerVisualConfig. The bits for enemies are:
// Enemies also have a class_flags field, though it isn't part of PlayerVisualConfigV123T. The bits for enemies are:
// -------- -------- -------- ----DMAN
// D = Dark attribute
// M = Machine attribute
// A = Altered Beast attribute
// N = Native attribute
/* 34 */ U32T<BE> class_flags = 0;
/* 38 */ U16T<BE> costume = 0;
/* 3A */ U16T<BE> skin = 0;
/* 3C */ U16T<BE> face = 0;
/* 3E */ U16T<BE> head = 0;
/* 40 */ U16T<BE> hair = 0;
/* 42 */ U16T<BE> hair_r = 0;
/* 44 */ U16T<BE> hair_g = 0;
/* 46 */ U16T<BE> hair_b = 0;
/* 48 */ F32T<BE> proportion_x = 0.0;
/* 4C */ F32T<BE> proportion_y = 0.0;
/* 50 */
/* 24 */ U32T<BE> class_flags = 0;
/* 28 */ U16T<BE> costume = 0;
/* 2A */ U16T<BE> skin = 0;
/* 2C */ U16T<BE> face = 0;
/* 2E */ U16T<BE> head = 0;
/* 30 */ U16T<BE> hair = 0;
/* 32 */ U16T<BE> hair_r = 0;
/* 34 */ U16T<BE> hair_g = 0;
/* 36 */ U16T<BE> hair_b = 0;
/* 38 */ F32T<BE> proportion_x = 0.0;
/* 3C */ F32T<BE> proportion_y = 0.0;
/* 40 */
static uint32_t compute_name_color_checksum(uint32_t name_color) {
uint8_t x = (phosg::random_object<uint32_t>() % 0xFF) + 1;
@@ -283,12 +283,10 @@ struct PlayerVisualConfigT {
this->name_color_checksum = 0;
}
this->class_flags = class_flags_for_class(this->char_class);
this->name.clear_after_bytes(0x0C);
}
operator PlayerVisualConfigT<!BE>() const {
PlayerVisualConfigT<!BE> ret;
ret.name = this->name;
operator PlayerVisualConfigSharedT<!BE>() const {
PlayerVisualConfigSharedT<!BE> ret;
ret.unknown_a2 = this->unknown_a2;
ret.name_color = this->name_color;
ret.extra_model = this->extra_model;
@@ -311,14 +309,65 @@ struct PlayerVisualConfigT {
ret.proportion_y = this->proportion_y;
return ret;
}
} __packed_ws_be__(PlayerVisualConfigT, 0x50);
using PlayerVisualConfig = PlayerVisualConfigT<false>;
using PlayerVisualConfigBE = PlayerVisualConfigT<true>;
} __packed_ws_be__(PlayerVisualConfigSharedT, 0x40);
template <bool BE>
struct PlayerDispDataDCPCV3T {
struct PlayerVisualConfigV123T {
/* 00 */ pstring<TextEncoding::ASCII, 0x10> name;
/* 10 */ PlayerVisualConfigSharedT<BE> sh;
/* 50 */
operator PlayerVisualConfigV123T<!BE>() const {
PlayerVisualConfigV123T<!BE> ret;
ret.name = this->name;
ret.sh = this->sh;
return ret;
}
void enforce_lobby_join_limits_for_version(Version v) {
this->sh.enforce_lobby_join_limits_for_version(v);
this->name.clear_after_bytes(0x0C);
}
PlayerVisualConfigV4 to_v4(Language to_language, Language from_language) const;
} __packed_ws_be__(PlayerVisualConfigV123T, 0x50);
using PlayerVisualConfigV123 = PlayerVisualConfigV123T<false>;
using PlayerVisualConfigV123BE = PlayerVisualConfigV123T<true>;
struct PlayerVisualConfigV4 {
/* 00 */ pstring<TextEncoding::ASCII, 0x10> guild_card_number;
/* 10 */ PlayerVisualConfigSharedT<false> sh;
/* 50 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> name;
/* 70 */
void enforce_lobby_join_limits_for_version(Version v) {
this->sh.enforce_lobby_join_limits_for_version(v);
this->guild_card_number.clear_after_bytes(0x0C);
}
void apply_dressing_room(const PlayerVisualConfigV4& new_visual);
template <bool BE>
PlayerVisualConfigV123T<BE> to_v123(Language to_language, Language from_language) const {
PlayerVisualConfigV123T<BE> ret;
ret.name.encode(this->name.decode(from_language), to_language);
ret.sh = this->sh;
return ret;
}
} __packed_ws__(PlayerVisualConfigV4, 0x70);
template <bool BE>
PlayerVisualConfigV4 PlayerVisualConfigV123T<BE>::to_v4(Language to_language, Language from_language) const {
PlayerVisualConfigV4 ret;
ret.guild_card_number.encode(" 0", Language::ENGLISH);
ret.sh = this->sh;
ret.name.encode(this->name.decode(from_language), to_language);
return ret;
}
template <bool BE>
struct PlayerDispDataV123T {
/* 00 */ PlayerStatsT<BE> stats;
/* 24 */ PlayerVisualConfigT<BE> visual;
/* 24 */ PlayerVisualConfigV123T<BE> visual;
/* 74 */ parray<uint8_t, 0x48> config;
/* BC */ parray<uint8_t, 0x14> technique_levels_v1;
/* D0 */
@@ -327,60 +376,50 @@ struct PlayerDispDataDCPCV3T {
this->visual.enforce_lobby_join_limits_for_version(v);
}
PlayerDispDataBB to_bb(Language to_language, Language from_language) const;
} __packed_ws_be__(PlayerDispDataDCPCV3T, 0xD0);
using PlayerDispDataDCPCV3 = PlayerDispDataDCPCV3T<false>;
using PlayerDispDataDCPCV3BE = PlayerDispDataDCPCV3T<true>;
PlayerDispDataV4 to_v4(Language to_language, Language from_language) const;
} __packed_ws_be__(PlayerDispDataV123T, 0xD0);
using PlayerDispDataV123 = PlayerDispDataV123T<false>;
using PlayerDispDataV123BE = PlayerDispDataV123T<true>;
struct PlayerDispDataBBPreview {
struct PlayerDispDataV4Preview {
/* 00 */ le_uint32_t exp = 0;
/* 04 */ le_uint32_t level = 0;
// The name field in this structure is used for the player's Guild Card number, apparently (possibly because it's a
// char array and this is BB)
/* 08 */ PlayerVisualConfig visual;
/* 58 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> name;
/* 08 */ PlayerVisualConfigV4 visual;
/* 78 */ uint32_t play_time_seconds = 0;
/* 7C */
} __packed_ws__(PlayerDispDataBBPreview, 0x7C);
} __packed_ws__(PlayerDispDataV4Preview, 0x7C);
// BB player appearance and stats data
struct PlayerDispDataBB {
struct PlayerDispDataV4 {
/* 0000 */ PlayerStats stats;
/* 0024 */ PlayerVisualConfig visual;
/* 0074 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> name;
/* 0024 */ PlayerVisualConfigV4 visual;
/* 0094 */ parray<uint8_t, 0xE8> config;
/* 017C */ parray<uint8_t, 0x14> technique_levels_v1;
/* 0190 */
void enforce_lobby_join_limits_for_version(Version v) {
this->visual.enforce_lobby_join_limits_for_version(v);
this->name.clear_after_bytes(0x18); // 12 characters
this->visual.sh.enforce_lobby_join_limits_for_version(v);
this->visual.name.clear_after_bytes(0x18); // 12 characters
}
template <bool BE>
PlayerDispDataDCPCV3T<BE> to_dcpcv3(Language to_language, Language from_language) const {
PlayerDispDataDCPCV3T<BE> ret;
PlayerDispDataV123T<BE> to_v123(Language to_language, Language from_language) const {
PlayerDispDataV123T<BE> ret;
ret.stats = this->stats;
ret.visual = this->visual;
std::string decoded_name = this->name.decode(from_language);
ret.visual.name.encode(decoded_name, to_language);
ret.visual = this->visual.to_v123<BE>(to_language, from_language);
ret.config = this->config;
ret.technique_levels_v1 = this->technique_levels_v1;
return ret;
}
void apply_preview(const PlayerDispDataBBPreview&);
void apply_dressing_room(const PlayerDispDataBBPreview&);
} __packed_ws__(PlayerDispDataBB, 0x190);
} __packed_ws__(PlayerDispDataV4, 0x190);
template <bool BE>
PlayerDispDataBB PlayerDispDataDCPCV3T<BE>::to_bb(Language to_language, Language from_language) const {
PlayerDispDataBB bb;
PlayerDispDataV4 PlayerDispDataV123T<BE>::to_v4(Language to_language, Language from_language) const {
PlayerDispDataV4 bb;
bb.stats = this->stats;
bb.visual = this->visual;
bb.visual.name.encode(" 0");
std::string decoded_name = this->visual.name.decode(from_language);
bb.name.encode(decoded_name, to_language);
bb.visual = this->visual.to_v4(to_language, from_language);
bb.config = this->config;
bb.technique_levels_v1 = this->technique_levels_v1;
return bb;
@@ -834,25 +873,25 @@ DestT convert_player_disp_data(const SrcT&, Language, Language) {
}
template <>
inline PlayerDispDataDCPCV3 convert_player_disp_data<PlayerDispDataDCPCV3>(
const PlayerDispDataDCPCV3& src, Language, Language) {
inline PlayerDispDataV123 convert_player_disp_data<PlayerDispDataV123>(
const PlayerDispDataV123& src, Language, Language) {
return src;
}
template <>
inline PlayerDispDataDCPCV3 convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(
const PlayerDispDataBB& src, Language to_language, Language from_language) {
return src.to_dcpcv3<false>(to_language, from_language);
inline PlayerDispDataV123 convert_player_disp_data<PlayerDispDataV123, PlayerDispDataV4>(
const PlayerDispDataV4& src, Language to_language, Language from_language) {
return src.to_v123<false>(to_language, from_language);
}
template <>
inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB, PlayerDispDataDCPCV3>(
const PlayerDispDataDCPCV3& src, Language to_language, Language from_language) {
return src.to_bb(to_language, from_language);
inline PlayerDispDataV4 convert_player_disp_data<PlayerDispDataV4, PlayerDispDataV123>(
const PlayerDispDataV123& src, Language to_language, Language from_language) {
return src.to_v4(to_language, from_language);
}
template <>
inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB>(const PlayerDispDataBB& src, Language, Language) {
inline PlayerDispDataV4 convert_player_disp_data<PlayerDispDataV4>(const PlayerDispDataV4& src, Language, Language) {
return src;
}
+7 -7
View File
@@ -1637,8 +1637,8 @@ static asio::awaitable<HandlerResult> S_65_67_68_EB(std::shared_ptr<Client> c, C
p.guild_card_number = entry.lobby_data.guild_card_number;
p.name = name;
p.language = entry.inventory.language;
p.section_id = entry.disp.visual.section_id;
p.char_class = entry.disp.visual.char_class;
p.section_id = entry.disp.visual.sh.section_id;
p.char_class = entry.disp.visual.sh.char_class;
c->log.info_f("Added lobby player: ({}) {} {}", index, p.guild_card_number, p.name);
}
}
@@ -1763,7 +1763,7 @@ static asio::awaitable<HandlerResult> S_64(std::shared_ptr<Client> c, Channel::M
} else {
c->proxy_session->lobby_event = 0;
c->proxy_session->lobby_difficulty = Difficulty::NORMAL;
c->proxy_session->lobby_section_id = c->character_file()->disp.visual.section_id;
c->proxy_session->lobby_section_id = c->character_file()->disp.visual.sh.section_id;
c->proxy_session->lobby_mode = GameMode::NORMAL;
c->proxy_session->lobby_random_seed = phosg::random_object<uint32_t>();
}
@@ -1814,8 +1814,8 @@ static asio::awaitable<HandlerResult> S_64(std::shared_ptr<Client> c, Channel::M
const auto& p_ep3 = cmd_ep3->players_ep3[x];
p.language = p_ep3.inventory.language;
p.name = p_ep3.disp.visual.name.decode(p.language);
p.section_id = p_ep3.disp.visual.section_id;
p.char_class = p_ep3.disp.visual.char_class;
p.section_id = p_ep3.disp.visual.sh.section_id;
p.char_class = p_ep3.disp.visual.sh.char_class;
} else {
p.name.clear();
}
@@ -1873,8 +1873,8 @@ static asio::awaitable<HandlerResult> S_E8(std::shared_ptr<Client> c, Channel::M
p.guild_card_number = player_entry.lobby_data.guild_card_number;
p.language = player_entry.inventory.language;
p.name = player_entry.disp.visual.name.decode(p.language);
p.section_id = player_entry.disp.visual.section_id;
p.char_class = player_entry.disp.visual.char_class;
p.section_id = player_entry.disp.visual.sh.section_id;
p.char_class = player_entry.disp.visual.sh.char_class;
c->log.info_f("Added lobby player: ({}) {} {}", x, p.guild_card_number, p.name);
}
+119 -99
View File
@@ -1398,7 +1398,7 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
{0xF83E, {"set_current_area_number", "delete_area_title?"}, {I32}, F_V2_V4 | F_ARGS},
// Loads a custom visual config for creating NPCs. Generally the sequence should go like this:
// prepare_npc_visual label_containing_PlayerVisualConfig
// prepare_npc_visual label_containing_PlayerVisualConfig (either V123 or V4)
// enable_npc_visual
// <opcode that creates an NPC>
// After any NPC is created, the effects of these opcodes are undone; if the script wants to create another NPC
@@ -2958,34 +2958,41 @@ std::string disassemble_quest_script(
// Phase 4: Disassemble all referenced label regions, starting with label 0
auto add = [](std::shared_ptr<Label> l, std::string&& s) -> void {
l->lines.emplace_back(std::move(s));
};
auto addf = []<typename... ArgTs>(std::shared_ptr<Label> l, std::format_string<ArgTs...> fmt, ArgTs&&... args) -> void {
l->lines.emplace_back(std::format(fmt, std::forward<ArgTs>(args)...));
};
auto disassemble_label_as_struct = [&]<typename StructT>(std::shared_ptr<Label> l, auto print_fn) {
if (reassembly_mode) {
l->lines.emplace_back(" .data " + phosg::format_data_string(text_r.pgetv(l->offset, l->size), l->size));
addf(l, " .data {}", phosg::format_data_string(text_r.pgetv(l->offset, l->size), l->size));
} else if (l->size >= sizeof(StructT)) {
print_fn(text_r.pget<StructT>(l->offset));
if (l->size > sizeof(StructT)) {
size_t struct_end_offset = l->offset + sizeof(StructT);
size_t remaining_size = l->size - sizeof(StructT);
l->lines.emplace_back(" // Extra data after structure");
l->lines.emplace_back(format_and_indent_data(text_r.pgetv(struct_end_offset, remaining_size), remaining_size, struct_end_offset));
addf(l, " // Extra data after structure");
add(l, format_and_indent_data(text_r.pgetv(struct_end_offset, remaining_size), remaining_size, struct_end_offset));
}
} else {
l->lines.emplace_back(std::format(" // As raw data (0x{:X} bytes; too small for referenced type)", l->size));
l->lines.emplace_back(format_and_indent_data(text_r.pgetv(l->offset, l->size), l->size, l->offset));
addf(l, " // As raw data (0x{:X} bytes; too small for referenced type)", l->size);
add(l, format_and_indent_data(text_r.pgetv(l->offset, l->size), l->size, l->offset));
}
};
auto disassemble_label_as_data = [&](std::shared_ptr<Label> l) -> void {
l->lines.emplace_back(std::format(" // As raw data (0x{:X} bytes)", l->size));
addf(l, " // As raw data (0x{:X} bytes)", l->size);
if (reassembly_mode) {
l->lines.emplace_back(" .data " + phosg::format_data_string(text_r.pgetv(l->offset, l->size), l->size));
addf(l, " .data {}", phosg::format_data_string(text_r.pgetv(l->offset, l->size), l->size));
} else {
l->lines.emplace_back(format_and_indent_data(text_r.pgetv(l->offset, l->size), l->size, l->offset));
add(l, format_and_indent_data(text_r.pgetv(l->offset, l->size), l->size, l->offset));
}
};
auto disassemble_label_as_cstring = [&](std::shared_ptr<Label> l) -> void {
l->lines.emplace_back(" // As C string");
addf(l, " // As C string");
std::string str_data = text_r.pread(l->offset, l->size);
phosg::strip_trailing_zeroes(str_data);
@@ -3009,121 +3016,134 @@ std::string disassemble_quest_script(
formatted = escape_string(str_data, encoding_for_language(language));
}
if (reassembly_mode) {
l->lines.emplace_back(std::format(" .cstr {}", formatted));
addf(l, " .cstr {}", formatted);
if (extra_zero_bytes) {
l->lines.emplace_back(std::format(" .data {}", std::string(extra_zero_bytes * 2, '0')));
addf(l, " .data {}", std::string(extra_zero_bytes * 2, '0'));
}
} else {
l->lines.emplace_back(std::format(" {:04X} .cstr {}", l->offset, formatted));
addf(l, " {:04X} .cstr {}", l->offset, formatted);
if (extra_zero_bytes) {
l->lines.emplace_back(std::format(" {:04X} .data {}", l->offset + l->size - extra_zero_bytes, std::string(extra_zero_bytes * 2, '0')));
addf(l, " {:04X} .data {}", l->offset + l->size - extra_zero_bytes, std::string(extra_zero_bytes * 2, '0'));
}
}
};
auto disassemble_label_as_player_visual_config = [&](std::shared_ptr<Label> l) -> void {
disassemble_label_as_struct.template operator()<PlayerVisualConfig>(l, [&](const PlayerVisualConfig& visual) -> void {
l->lines.emplace_back(" // As PlayerVisualConfig");
l->lines.emplace_back(std::format(" {:04X} name {}", l->offset + offsetof(PlayerVisualConfig, name), escape_string(visual.name.decode(language))));
l->lines.emplace_back(std::format(" {:04X} name_color {:08X}", l->offset + offsetof(PlayerVisualConfig, name_color), visual.name_color));
l->lines.emplace_back(std::format(" {:04X} a2 {}", l->offset + offsetof(PlayerVisualConfig, unknown_a2), phosg::format_data_string(visual.unknown_a2.data(), sizeof(visual.unknown_a2))));
l->lines.emplace_back(std::format(" {:04X} extra_model {:02X}", l->offset + offsetof(PlayerVisualConfig, extra_model), visual.extra_model));
l->lines.emplace_back(std::format(" {:04X} unused {}", l->offset + offsetof(PlayerVisualConfig, unused), phosg::format_data_string(visual.unused.data(), visual.unused.bytes())));
l->lines.emplace_back(std::format(" {:04X} name_color_cs {:08X}", l->offset + offsetof(PlayerVisualConfig, name_color_checksum), visual.name_color_checksum));
l->lines.emplace_back(std::format(" {:04X} section_id {:02X} ({})", l->offset + offsetof(PlayerVisualConfig, section_id), visual.section_id, name_for_section_id(visual.section_id)));
l->lines.emplace_back(std::format(" {:04X} char_class {:02X} ({})", l->offset + offsetof(PlayerVisualConfig, char_class), visual.char_class, name_for_char_class(visual.char_class)));
l->lines.emplace_back(std::format(" {:04X} validation_flags {:02X}", l->offset + offsetof(PlayerVisualConfig, validation_flags), visual.validation_flags));
l->lines.emplace_back(std::format(" {:04X} version {:02X}", l->offset + offsetof(PlayerVisualConfig, version), visual.version));
l->lines.emplace_back(std::format(" {:04X} class_flags {:08X}", l->offset + offsetof(PlayerVisualConfig, class_flags), visual.class_flags));
l->lines.emplace_back(std::format(" {:04X} costume {:04X}", l->offset + offsetof(PlayerVisualConfig, costume), visual.costume));
l->lines.emplace_back(std::format(" {:04X} skin {:04X}", l->offset + offsetof(PlayerVisualConfig, skin), visual.skin));
l->lines.emplace_back(std::format(" {:04X} face {:04X}", l->offset + offsetof(PlayerVisualConfig, face), visual.face));
l->lines.emplace_back(std::format(" {:04X} head {:04X}", l->offset + offsetof(PlayerVisualConfig, head), visual.head));
l->lines.emplace_back(std::format(" {:04X} hair {:04X}", l->offset + offsetof(PlayerVisualConfig, hair), visual.hair));
l->lines.emplace_back(std::format(" {:04X} hair_color {:04X}, {:04X}, {:04X}", l->offset + offsetof(PlayerVisualConfig, hair_r), visual.hair_r, visual.hair_g, visual.hair_b));
l->lines.emplace_back(std::format(" {:04X} proportion {:g}, {:g}", l->offset + offsetof(PlayerVisualConfig, proportion_x), visual.proportion_x, visual.proportion_y));
});
auto add_shared_fields = [&](const PlayerVisualConfigSharedT<false>& sh, size_t offset) -> void {
using SH = PlayerVisualConfigSharedT<false>;
addf(l, " {:04X} name_color {:08X}", offset + offsetof(SH, name_color), sh.name_color);
addf(l, " {:04X} a2 {}", offset + offsetof(SH, unknown_a2), phosg::format_data_string(sh.unknown_a2.data(), sizeof(sh.unknown_a2)));
addf(l, " {:04X} extra_model {:02X}", offset + offsetof(SH, extra_model), sh.extra_model);
addf(l, " {:04X} unused {}", offset + offsetof(SH, unused), phosg::format_data_string(sh.unused.data(), sh.unused.bytes()));
addf(l, " {:04X} name_color_cs {:08X}", offset + offsetof(SH, name_color_checksum), sh.name_color_checksum);
addf(l, " {:04X} section_id {:02X} ({})", offset + offsetof(SH, section_id), sh.section_id, name_for_section_id(sh.section_id));
addf(l, " {:04X} char_class {:02X} ({})", offset + offsetof(SH, char_class), sh.char_class, name_for_char_class(sh.char_class));
addf(l, " {:04X} validation_flags {:02X}", offset + offsetof(SH, validation_flags), sh.validation_flags);
addf(l, " {:04X} version {:02X}", offset + offsetof(SH, version), sh.version);
addf(l, " {:04X} class_flags {:08X}", offset + offsetof(SH, class_flags), sh.class_flags);
addf(l, " {:04X} costume {:04X}", offset + offsetof(SH, costume), sh.costume);
addf(l, " {:04X} skin {:04X}", offset + offsetof(SH, skin), sh.skin);
addf(l, " {:04X} face {:04X}", offset + offsetof(SH, face), sh.face);
addf(l, " {:04X} head {:04X}", offset + offsetof(SH, head), sh.head);
addf(l, " {:04X} hair {:04X}", offset + offsetof(SH, hair), sh.hair);
addf(l, " {:04X} hair_color {:04X}, {:04X}, {:04X}", offset + offsetof(SH, hair_r), sh.hair_r, sh.hair_g, sh.hair_b);
addf(l, " {:04X} proportion {:g}, {:g}", offset + offsetof(SH, proportion_x), sh.proportion_x, sh.proportion_y);
};
if (version != Version::BB_V4) {
disassemble_label_as_struct.template operator()<PlayerVisualConfigV123>(l, [&](const PlayerVisualConfigV123& visual) -> void {
l->lines.emplace_back(" // As PlayerVisualConfigV123");
addf(l, " {:04X} name {}", l->offset + offsetof(PlayerVisualConfigV123, name), escape_string(visual.name.decode(language)));
add_shared_fields(visual.sh, offsetof(PlayerVisualConfigV123, sh));
});
} else {
disassemble_label_as_struct.template operator()<PlayerVisualConfigV4>(l, [&](const PlayerVisualConfigV4& visual) -> void {
l->lines.emplace_back(" // As PlayerVisualConfigV4");
addf(l, " {:04X} __unused_name__ {}", l->offset + offsetof(PlayerVisualConfigV4, guild_card_number), escape_string(visual.guild_card_number.decode(language)));
add_shared_fields(visual.sh, offsetof(PlayerVisualConfigV4, sh));
addf(l, " {:04X} name {}", l->offset + offsetof(PlayerVisualConfigV4, name), escape_string(visual.name.decode(language)));
});
}
};
auto disassemble_label_as_player_stats = [&](std::shared_ptr<Label> l) -> void {
disassemble_label_as_struct.template operator()<PlayerStats>(l, [&](const PlayerStats& stats) -> void {
l->lines.emplace_back(" // As PlayerStats");
l->lines.emplace_back(std::format(" {:04X} atp {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.atp), stats.char_stats.atp, stats.char_stats.atp));
l->lines.emplace_back(std::format(" {:04X} mst {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.mst), stats.char_stats.mst, stats.char_stats.mst));
l->lines.emplace_back(std::format(" {:04X} evp {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.evp), stats.char_stats.evp, stats.char_stats.evp));
l->lines.emplace_back(std::format(" {:04X} hp {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.hp), stats.char_stats.hp, stats.char_stats.hp));
l->lines.emplace_back(std::format(" {:04X} dfp {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.dfp), stats.char_stats.dfp, stats.char_stats.dfp));
l->lines.emplace_back(std::format(" {:04X} ata {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.ata), stats.char_stats.ata, stats.char_stats.ata));
l->lines.emplace_back(std::format(" {:04X} lck {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.lck), stats.char_stats.lck, stats.char_stats.lck));
l->lines.emplace_back(std::format(" {:04X} esp {:04X} /* {} */", l->offset + offsetof(PlayerStats, esp), stats.esp, stats.esp));
l->lines.emplace_back(std::format(" {:04X} attack_range {:08X} /* {:g} */", l->offset + offsetof(PlayerStats, attack_range), stats.attack_range.load_raw(), stats.attack_range));
l->lines.emplace_back(std::format(" {:04X} knockback_range {:08X} /* {:g} */", l->offset + offsetof(PlayerStats, knockback_range), stats.knockback_range.load_raw(), stats.knockback_range));
l->lines.emplace_back(std::format(" {:04X} level {:08X} /* level {} */", l->offset + offsetof(PlayerStats, level), stats.level, stats.level + 1));
l->lines.emplace_back(std::format(" {:04X} exp {:08X} /* {} */", l->offset + offsetof(PlayerStats, exp), stats.exp, stats.exp));
l->lines.emplace_back(std::format(" {:04X} meseta {:08X} /* {} */", l->offset + offsetof(PlayerStats, meseta), stats.meseta, stats.meseta));
addf(l, " {:04X} atp {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.atp), stats.char_stats.atp, stats.char_stats.atp);
addf(l, " {:04X} mst {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.mst), stats.char_stats.mst, stats.char_stats.mst);
addf(l, " {:04X} evp {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.evp), stats.char_stats.evp, stats.char_stats.evp);
addf(l, " {:04X} hp {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.hp), stats.char_stats.hp, stats.char_stats.hp);
addf(l, " {:04X} dfp {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.dfp), stats.char_stats.dfp, stats.char_stats.dfp);
addf(l, " {:04X} ata {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.ata), stats.char_stats.ata, stats.char_stats.ata);
addf(l, " {:04X} lck {:04X} /* {} */", l->offset + offsetof(PlayerStats, char_stats.lck), stats.char_stats.lck, stats.char_stats.lck);
addf(l, " {:04X} esp {:04X} /* {} */", l->offset + offsetof(PlayerStats, esp), stats.esp, stats.esp);
addf(l, " {:04X} attack_range {:08X} /* {:g} */", l->offset + offsetof(PlayerStats, attack_range), stats.attack_range.load_raw(), stats.attack_range);
addf(l, " {:04X} knockback_range {:08X} /* {:g} */", l->offset + offsetof(PlayerStats, knockback_range), stats.knockback_range.load_raw(), stats.knockback_range);
addf(l, " {:04X} level {:08X} /* level {} */", l->offset + offsetof(PlayerStats, level), stats.level, stats.level + 1);
addf(l, " {:04X} exp {:08X} /* {} */", l->offset + offsetof(PlayerStats, exp), stats.exp, stats.exp);
addf(l, " {:04X} meseta {:08X} /* {} */", l->offset + offsetof(PlayerStats, meseta), stats.meseta, stats.meseta);
});
};
auto disassemble_label_as_resist_data = [&](std::shared_ptr<Label> l) -> void {
disassemble_label_as_struct.template operator()<ResistData>(l, [&](const ResistData& resist) -> void {
l->lines.emplace_back(" // As ResistData");
l->lines.emplace_back(std::format(" {:04X} evp_bonus {:04X} /* {} */", l->offset + offsetof(ResistData, evp_bonus), resist.evp_bonus, resist.evp_bonus));
l->lines.emplace_back(std::format(" {:04X} efr {:04X} /* {} */", l->offset + offsetof(ResistData, efr), resist.efr, resist.efr));
l->lines.emplace_back(std::format(" {:04X} eic {:04X} /* {} */", l->offset + offsetof(ResistData, eic), resist.eic, resist.eic));
l->lines.emplace_back(std::format(" {:04X} eth {:04X} /* {} */", l->offset + offsetof(ResistData, eth), resist.eth, resist.eth));
l->lines.emplace_back(std::format(" {:04X} elt {:04X} /* {} */", l->offset + offsetof(ResistData, elt), resist.elt, resist.elt));
l->lines.emplace_back(std::format(" {:04X} edk {:04X} /* {} */", l->offset + offsetof(ResistData, edk), resist.edk, resist.edk));
l->lines.emplace_back(std::format(" {:04X} a6 {:08X} /* {} */", l->offset + offsetof(ResistData, unknown_a6), resist.unknown_a6, resist.unknown_a6));
l->lines.emplace_back(std::format(" {:04X} a7 {:08X} /* {} */", l->offset + offsetof(ResistData, unknown_a7), resist.unknown_a7, resist.unknown_a7));
l->lines.emplace_back(std::format(" {:04X} a8 {:08X} /* {} */", l->offset + offsetof(ResistData, unknown_a8), resist.unknown_a8, resist.unknown_a8));
l->lines.emplace_back(std::format(" {:04X} a9 {:08X} /* {} */", l->offset + offsetof(ResistData, unknown_a9), resist.unknown_a9, resist.unknown_a9));
l->lines.emplace_back(std::format(" {:04X} dfp_bonus {:08X} /* {} */", l->offset + offsetof(ResistData, dfp_bonus), resist.dfp_bonus, resist.dfp_bonus));
addf(l, " {:04X} evp_bonus {:04X} /* {} */", l->offset + offsetof(ResistData, evp_bonus), resist.evp_bonus, resist.evp_bonus);
addf(l, " {:04X} efr {:04X} /* {} */", l->offset + offsetof(ResistData, efr), resist.efr, resist.efr);
addf(l, " {:04X} eic {:04X} /* {} */", l->offset + offsetof(ResistData, eic), resist.eic, resist.eic);
addf(l, " {:04X} eth {:04X} /* {} */", l->offset + offsetof(ResistData, eth), resist.eth, resist.eth);
addf(l, " {:04X} elt {:04X} /* {} */", l->offset + offsetof(ResistData, elt), resist.elt, resist.elt);
addf(l, " {:04X} edk {:04X} /* {} */", l->offset + offsetof(ResistData, edk), resist.edk, resist.edk);
addf(l, " {:04X} a6 {:08X} /* {} */", l->offset + offsetof(ResistData, unknown_a6), resist.unknown_a6, resist.unknown_a6);
addf(l, " {:04X} a7 {:08X} /* {} */", l->offset + offsetof(ResistData, unknown_a7), resist.unknown_a7, resist.unknown_a7);
addf(l, " {:04X} a8 {:08X} /* {} */", l->offset + offsetof(ResistData, unknown_a8), resist.unknown_a8, resist.unknown_a8);
addf(l, " {:04X} a9 {:08X} /* {} */", l->offset + offsetof(ResistData, unknown_a9), resist.unknown_a9, resist.unknown_a9);
addf(l, " {:04X} dfp_bonus {:08X} /* {} */", l->offset + offsetof(ResistData, dfp_bonus), resist.dfp_bonus, resist.dfp_bonus);
});
};
auto disassemble_label_as_attack_data = [&](std::shared_ptr<Label> l) -> void {
disassemble_label_as_struct.template operator()<AttackData>(l, [&](const AttackData& attack) -> void {
l->lines.emplace_back(" // As AttackData");
l->lines.emplace_back(std::format(" {:04X} atp_min {:04X} /* {} */", l->offset + offsetof(AttackData, min_atp), attack.min_atp, attack.min_atp));
l->lines.emplace_back(std::format(" {:04X} atp_max {:04X} /* {} */", l->offset + offsetof(AttackData, max_atp), attack.max_atp, attack.max_atp));
l->lines.emplace_back(std::format(" {:04X} ata_min {:04X} /* {} */", l->offset + offsetof(AttackData, min_ata), attack.min_ata, attack.min_ata));
l->lines.emplace_back(std::format(" {:04X} ata_max {:04X} /* {} */", l->offset + offsetof(AttackData, max_ata), attack.max_ata, attack.max_ata));
l->lines.emplace_back(std::format(" {:04X} distance_x {:08X} /* {:g} */", l->offset + offsetof(AttackData, distance_x), attack.distance_x.load_raw(), attack.distance_x));
l->lines.emplace_back(std::format(" {:04X} angle {:08X} /* {}/65536 */", l->offset + offsetof(AttackData, angle), attack.angle.load_raw(), attack.angle));
l->lines.emplace_back(std::format(" {:04X} distance_y {:08X} /* {:g} */", l->offset + offsetof(AttackData, distance_y), attack.distance_y.load_raw(), attack.distance_y));
l->lines.emplace_back(std::format(" {:04X} a8 {:04X} /* {} */", l->offset + offsetof(AttackData, unknown_a8), attack.unknown_a8, attack.unknown_a8));
l->lines.emplace_back(std::format(" {:04X} a9 {:04X} /* {} */", l->offset + offsetof(AttackData, unknown_a9), attack.unknown_a9, attack.unknown_a9));
l->lines.emplace_back(std::format(" {:04X} a10 {:04X} /* {} */", l->offset + offsetof(AttackData, unknown_a10), attack.unknown_a10, attack.unknown_a10));
l->lines.emplace_back(std::format(" {:04X} a11 {:04X} /* {} */", l->offset + offsetof(AttackData, unknown_a11), attack.unknown_a11, attack.unknown_a11));
l->lines.emplace_back(std::format(" {:04X} a12 {:08X} /* {} */", l->offset + offsetof(AttackData, unknown_a12), attack.unknown_a12, attack.unknown_a12));
l->lines.emplace_back(std::format(" {:04X} a13 {:08X} /* {} */", l->offset + offsetof(AttackData, unknown_a13), attack.unknown_a13, attack.unknown_a13));
l->lines.emplace_back(std::format(" {:04X} a14 {:08X} /* {} */", l->offset + offsetof(AttackData, unknown_a14), attack.unknown_a14, attack.unknown_a14));
l->lines.emplace_back(std::format(" {:04X} a15 {:08X} /* {} */", l->offset + offsetof(AttackData, unknown_a15), attack.unknown_a15, attack.unknown_a15));
l->lines.emplace_back(std::format(" {:04X} a16 {:08X} /* {} */", l->offset + offsetof(AttackData, unknown_a16), attack.unknown_a16, attack.unknown_a16));
addf(l, " {:04X} atp_min {:04X} /* {} */", l->offset + offsetof(AttackData, min_atp), attack.min_atp, attack.min_atp);
addf(l, " {:04X} atp_max {:04X} /* {} */", l->offset + offsetof(AttackData, max_atp), attack.max_atp, attack.max_atp);
addf(l, " {:04X} ata_min {:04X} /* {} */", l->offset + offsetof(AttackData, min_ata), attack.min_ata, attack.min_ata);
addf(l, " {:04X} ata_max {:04X} /* {} */", l->offset + offsetof(AttackData, max_ata), attack.max_ata, attack.max_ata);
addf(l, " {:04X} distance_x {:08X} /* {:g} */", l->offset + offsetof(AttackData, distance_x), attack.distance_x.load_raw(), attack.distance_x);
addf(l, " {:04X} angle {:08X} /* {}/65536 */", l->offset + offsetof(AttackData, angle), attack.angle.load_raw(), attack.angle);
addf(l, " {:04X} distance_y {:08X} /* {:g} */", l->offset + offsetof(AttackData, distance_y), attack.distance_y.load_raw(), attack.distance_y);
addf(l, " {:04X} a8 {:04X} /* {} */", l->offset + offsetof(AttackData, unknown_a8), attack.unknown_a8, attack.unknown_a8);
addf(l, " {:04X} a9 {:04X} /* {} */", l->offset + offsetof(AttackData, unknown_a9), attack.unknown_a9, attack.unknown_a9);
addf(l, " {:04X} a10 {:04X} /* {} */", l->offset + offsetof(AttackData, unknown_a10), attack.unknown_a10, attack.unknown_a10);
addf(l, " {:04X} a11 {:04X} /* {} */", l->offset + offsetof(AttackData, unknown_a11), attack.unknown_a11, attack.unknown_a11);
addf(l, " {:04X} a12 {:08X} /* {} */", l->offset + offsetof(AttackData, unknown_a12), attack.unknown_a12, attack.unknown_a12);
addf(l, " {:04X} a13 {:08X} /* {} */", l->offset + offsetof(AttackData, unknown_a13), attack.unknown_a13, attack.unknown_a13);
addf(l, " {:04X} a14 {:08X} /* {} */", l->offset + offsetof(AttackData, unknown_a14), attack.unknown_a14, attack.unknown_a14);
addf(l, " {:04X} a15 {:08X} /* {} */", l->offset + offsetof(AttackData, unknown_a15), attack.unknown_a15, attack.unknown_a15);
addf(l, " {:04X} a16 {:08X} /* {} */", l->offset + offsetof(AttackData, unknown_a16), attack.unknown_a16, attack.unknown_a16);
});
};
auto disassemble_label_as_movement_data = [&](std::shared_ptr<Label> l) -> void {
disassemble_label_as_struct.template operator()<MovementData>(l, [&](const MovementData& movement) -> void {
l->lines.emplace_back(" // As MovementData");
l->lines.emplace_back(std::format(" {:04X} fparam1 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam1), movement.fparam1.load_raw(), movement.fparam1));
l->lines.emplace_back(std::format(" {:04X} fparam2 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam2), movement.fparam2.load_raw(), movement.fparam2));
l->lines.emplace_back(std::format(" {:04X} fparam3 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam3), movement.fparam3.load_raw(), movement.fparam3));
l->lines.emplace_back(std::format(" {:04X} fparam4 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam4), movement.fparam4.load_raw(), movement.fparam4));
l->lines.emplace_back(std::format(" {:04X} fparam5 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam5), movement.fparam5.load_raw(), movement.fparam5));
l->lines.emplace_back(std::format(" {:04X} fparam6 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam6), movement.fparam6.load_raw(), movement.fparam6));
l->lines.emplace_back(std::format(" {:04X} iparam1 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam1), movement.iparam1, movement.iparam1));
l->lines.emplace_back(std::format(" {:04X} iparam2 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam2), movement.iparam2, movement.iparam2));
l->lines.emplace_back(std::format(" {:04X} iparam3 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam3), movement.iparam3, movement.iparam3));
l->lines.emplace_back(std::format(" {:04X} iparam4 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam4), movement.iparam4, movement.iparam4));
l->lines.emplace_back(std::format(" {:04X} iparam5 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam5), movement.iparam5, movement.iparam5));
l->lines.emplace_back(std::format(" {:04X} iparam6 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam6), movement.iparam6, movement.iparam6));
addf(l, " {:04X} fparam1 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam1), movement.fparam1.load_raw(), movement.fparam1);
addf(l, " {:04X} fparam2 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam2), movement.fparam2.load_raw(), movement.fparam2);
addf(l, " {:04X} fparam3 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam3), movement.fparam3.load_raw(), movement.fparam3);
addf(l, " {:04X} fparam4 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam4), movement.fparam4.load_raw(), movement.fparam4);
addf(l, " {:04X} fparam5 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam5), movement.fparam5.load_raw(), movement.fparam5);
addf(l, " {:04X} fparam6 {:08X} /* {:g} */", l->offset + offsetof(MovementData, fparam6), movement.fparam6.load_raw(), movement.fparam6);
addf(l, " {:04X} iparam1 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam1), movement.iparam1, movement.iparam1);
addf(l, " {:04X} iparam2 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam2), movement.iparam2, movement.iparam2);
addf(l, " {:04X} iparam3 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam3), movement.iparam3, movement.iparam3);
addf(l, " {:04X} iparam4 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam4), movement.iparam4, movement.iparam4);
addf(l, " {:04X} iparam5 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam5), movement.iparam5, movement.iparam5);
addf(l, " {:04X} iparam6 {:08X} /* {} */", l->offset + offsetof(MovementData, iparam6), movement.iparam6, movement.iparam6);
});
};
auto disassemble_label_as_image_data = [&](std::shared_ptr<Label> l) -> void {
if (reassembly_mode) {
l->lines.emplace_back(std::format(" // As compressed image data (0x{:X} bytes)", l->size));
addf(l, " // As compressed image data (0x{:X} bytes)", l->size);
l->lines.emplace_back(" .data " + phosg::format_data_string(text_r.pgetv(l->offset, l->size), l->size));
} else {
const void* data = text_r.pgetv(l->offset, l->size);
auto decompressed = prs_decompress_with_meta(data, l->size);
l->lines.emplace_back(std::format(" // As decompressed image data (0x{:X} bytes)", decompressed.data.size()));
addf(l, " // As decompressed image data (0x{:X} bytes)", decompressed.data.size());
l->lines.emplace_back(format_and_indent_data(decompressed.data.data(), decompressed.data.size(), 0));
if (decompressed.input_bytes_used < l->size) {
size_t compressed_end_offset = l->offset + decompressed.input_bytes_used;
@@ -3135,7 +3155,7 @@ std::string disassemble_quest_script(
};
auto disassemble_label_as_vector4f_list = [&](std::shared_ptr<Label> l) -> void {
if (reassembly_mode) {
l->lines.emplace_back(std::format(" // As VectorXYZTF list (0x{:X} bytes)", l->size));
addf(l, " // As VectorXYZTF list (0x{:X} bytes)", l->size);
l->lines.emplace_back(" .data " + phosg::format_data_string(text_r.pgetv(l->offset, l->size), l->size));
} else {
phosg::StringReader r = text_r.sub(l->offset, l->size);
@@ -3143,13 +3163,13 @@ std::string disassemble_quest_script(
while (r.remaining() >= sizeof(VectorXYZTF)) {
size_t offset = l->offset + r.where();
const auto& e = r.get<VectorXYZTF>();
l->lines.emplace_back(std::format(" {:04X} vector x={:g}, y={:g}, z={:g}, t={:g}", offset, e.x, e.y, e.z, e.t));
addf(l, " {:04X} vector x={:g}, y={:g}, z={:g}, t={:g}", offset, e.x, e.y, e.z, e.t);
}
if (r.remaining() > 0) {
size_t struct_end_offset = l->offset + r.where();
size_t remaining_size = r.remaining();
l->lines.emplace_back(" // Extra data after structures");
l->lines.emplace_back(format_and_indent_data(r.getv(remaining_size), remaining_size, struct_end_offset));
addf(l, " // Extra data after structures");
add(l, format_and_indent_data(r.getv(remaining_size), remaining_size, struct_end_offset));
}
}
};
@@ -4783,7 +4803,7 @@ void populate_quest_metadata_from_script(
meta.episode = Episode::EP1;
meta.max_players = 4;
meta.name = header.name.decode(language);
if (meta.quest_number == 0xFFFFFFFF) {
if (meta.quest_number == 0) {
meta.quest_number = phosg::fnv1a32(meta.name);
}
meta.text_offset = header.text_offset;
@@ -4802,7 +4822,7 @@ void populate_quest_metadata_from_script(
meta.name = header.name.decode(meta.language);
meta.short_description = header.short_description.decode(meta.language);
meta.long_description = header.long_description.decode(meta.language);
if (meta.quest_number == 0xFFFFFFFF) {
if (meta.quest_number == 0) {
meta.quest_number = phosg::fnv1a32(meta.name);
}
meta.text_offset = header.text_offset;
@@ -4823,7 +4843,7 @@ void populate_quest_metadata_from_script(
meta.name = header.name.decode(meta.language);
meta.short_description = header.short_description.decode(meta.language);
meta.long_description = header.long_description.decode(meta.language);
if (meta.quest_number == 0xFFFFFFFF) {
if (meta.quest_number == 0) {
meta.quest_number = header.quest_number;
}
meta.text_offset = header.text_offset;
@@ -4840,7 +4860,7 @@ void populate_quest_metadata_from_script(
meta.header_unknown_a3 = header.unknown_a3;
meta.episode = Episode::EP1;
meta.max_players = 4;
if (meta.quest_number == 0xFFFFFFFF) {
if (meta.quest_number == 0) {
meta.quest_number = header.quest_number;
}
meta.language = (language != Language::UNKNOWN) ? language : ((static_cast<size_t>(header.language) < 8) ? header.language : Language::ENGLISH);
@@ -4867,7 +4887,7 @@ void populate_quest_metadata_from_script(
meta.header_unknown_a3 = header.unknown_a3;
meta.episode = Episode::EP1;
meta.max_players = 4;
if (meta.quest_number == 0xFFFFFFFF) {
if (meta.quest_number == 0) {
meta.quest_number = header.quest_number;
}
meta.language = (language != Language::UNKNOWN) ? language : ((static_cast<size_t>(header.language) < 5) ? header.language : Language::ENGLISH);
@@ -4889,7 +4909,7 @@ void populate_quest_metadata_from_script(
meta.episode = episode_for_quest_episode_number(header.episode);
meta.joinable |= header.joinable;
meta.max_players = header.max_players;
if (meta.quest_number == 0xFFFFFFFF) {
if (meta.quest_number == 0) {
meta.quest_number = header.quest_number;
}
meta.language = (language != Language::UNKNOWN) ? language : Language::ENGLISH;
+36 -35
View File
@@ -1962,13 +1962,13 @@ static asio::awaitable<void> on_CA_Ep3(std::shared_ptr<Client> c, Channel::Messa
if (existing_c) {
auto existing_p = existing_c->character_file();
PlayerLobbyDataDCGC lobby_data;
lobby_data.name.encode(existing_p->disp.name.decode(existing_c->language()), c->language());
lobby_data.name.encode(existing_p->disp.visual.name.decode(existing_c->language()), c->language());
lobby_data.player_tag = 0x00010000;
lobby_data.guild_card_number = existing_c->login->account->account_id;
l->battle_record->add_player(
lobby_data,
existing_p->inventory,
existing_p->disp.to_dcpcv3<false>(c->language(), c->language()),
existing_p->disp.to_v123<false>(c->language(), c->language()),
c->ep3_config ? (c->ep3_config->online_clv_exp / 100) : 0);
}
}
@@ -2198,11 +2198,11 @@ static asio::awaitable<void> on_09(std::shared_ptr<Client> c, Channel::Message&
? version_tokens.at(static_cast<size_t>(game_c->version()))
: "";
auto player = game_c->character_file();
std::string name = escape_player_name(player->disp.name.decode(game_c->language()));
std::string name = escape_player_name(player->disp.visual.name.decode(game_c->language()));
info += std::format("{}{}\n {} Lv{} {}\n",
name,
version_token,
name_for_char_class(player->disp.visual.char_class),
name_for_char_class(player->disp.visual.sh.char_class),
player->disp.stats.level + 1,
char_for_language(game_c->language()));
}
@@ -2945,7 +2945,7 @@ static void on_10_tournament_entries(
return;
}
if (team_name.empty()) {
team_name = c->character_file()->disp.name.decode(c->language());
team_name = c->character_file()->disp.visual.name.decode(c->language());
team_name += std::format("/{:X}", c->login->account->account_id);
}
uint16_t tourn_num = item_id >> 16;
@@ -3369,29 +3369,29 @@ static asio::awaitable<void> on_61_98(std::shared_ptr<Client> c, Channel::Messag
case Version::DC_11_2000:
case Version::DC_V1: {
const auto& cmd = check_size_t<C_CharacterData_DCv1_61_98>(msg.data);
c->v1_v2_last_reported_disp = std::make_unique<PlayerDispDataDCPCV3>(cmd.disp);
c->v1_v2_last_reported_disp = std::make_unique<PlayerDispDataV123>(cmd.disp);
player->inventory = cmd.inventory;
player->disp = cmd.disp.to_bb(player->inventory.language, player->inventory.language);
c->login->account->last_player_name = player->disp.name.decode(player->inventory.language);
player->disp = cmd.disp.to_v4(player->inventory.language, player->inventory.language);
c->login->account->last_player_name = player->disp.visual.name.decode(player->inventory.language);
break;
}
case Version::DC_V2: {
const auto& cmd = check_size_t<C_CharacterData_DCv2_61_98>(msg.data, 0xFFFF);
c->v1_v2_last_reported_disp = std::make_unique<PlayerDispDataDCPCV3>(cmd.disp);
c->v1_v2_last_reported_disp = std::make_unique<PlayerDispDataV123>(cmd.disp);
player->inventory = cmd.inventory;
player->disp = cmd.disp.to_bb(player->inventory.language, player->inventory.language);
player->disp = cmd.disp.to_v4(player->inventory.language, player->inventory.language);
player->battle_records = cmd.records.battle;
player->challenge_records = cmd.records.challenge;
player->choice_search_config = cmd.choice_search_config;
c->login->account->last_player_name = player->disp.name.decode(player->inventory.language);
c->login->account->last_player_name = player->disp.visual.name.decode(player->inventory.language);
break;
}
case Version::PC_NTE:
case Version::PC_V2: {
const auto& cmd = check_size_t<C_CharacterData_PC_61_98>(msg.data, 0xFFFF);
c->v1_v2_last_reported_disp = std::make_unique<PlayerDispDataDCPCV3>(cmd.disp);
c->v1_v2_last_reported_disp = std::make_unique<PlayerDispDataV123>(cmd.disp);
player->inventory = cmd.inventory;
player->disp = cmd.disp.to_bb(player->inventory.language, player->inventory.language);
player->disp = cmd.disp.to_v4(player->inventory.language, player->inventory.language);
player->battle_records = cmd.records.battle;
player->challenge_records = cmd.records.challenge;
player->choice_search_config = cmd.choice_search_config;
@@ -3412,16 +3412,16 @@ static asio::awaitable<void> on_61_98(std::shared_ptr<Client> c, Channel::Messag
player->auto_reply.clear();
c->login->account->auto_reply_message.clear();
}
c->login->account->last_player_name = player->disp.name.decode(player->inventory.language);
c->login->account->last_player_name = player->disp.visual.name.decode(player->inventory.language);
break;
}
case Version::GC_NTE: {
const auto& cmd = check_size_t<C_CharacterData_GCNTE_61_98>(msg.data, 0xFFFF);
c->v1_v2_last_reported_disp = std::make_unique<PlayerDispDataDCPCV3>(cmd.disp);
c->v1_v2_last_reported_disp = std::make_unique<PlayerDispDataV123>(cmd.disp);
auto s = c->require_server_state();
player->inventory = cmd.inventory;
player->disp = cmd.disp.to_bb(player->inventory.language, player->inventory.language);
player->disp = cmd.disp.to_v4(player->inventory.language, player->inventory.language);
player->battle_records = cmd.records.battle;
player->challenge_records = cmd.records.challenge;
player->choice_search_config = cmd.choice_search_config;
@@ -3440,7 +3440,7 @@ static asio::awaitable<void> on_61_98(std::shared_ptr<Client> c, Channel::Messag
player->auto_reply.clear();
c->login->account->auto_reply_message.clear();
}
c->login->account->last_player_name = player->disp.name.decode(player->inventory.language);
c->login->account->last_player_name = player->disp.visual.name.decode(player->inventory.language);
break;
}
@@ -3479,7 +3479,7 @@ static asio::awaitable<void> on_61_98(std::shared_ptr<Client> c, Channel::Messag
}
player->inventory = cmd->inventory;
player->disp = cmd->disp.to_bb(player->inventory.language, player->inventory.language);
player->disp = cmd->disp.to_v4(player->inventory.language, player->inventory.language);
player->battle_records = cmd->records.battle;
player->challenge_records = cmd->records.challenge;
player->choice_search_config = cmd->choice_search_config;
@@ -3499,7 +3499,7 @@ static asio::awaitable<void> on_61_98(std::shared_ptr<Client> c, Channel::Messag
player->auto_reply.clear();
c->login->account->auto_reply_message.clear();
}
c->login->account->last_player_name = player->disp.name.decode(player->inventory.language);
c->login->account->last_player_name = player->disp.visual.name.decode(player->inventory.language);
break;
}
case Version::BB_V4: {
@@ -3527,7 +3527,7 @@ static asio::awaitable<void> on_61_98(std::shared_ptr<Client> c, Channel::Messag
player->auto_reply.clear();
c->login->account->auto_reply_message.clear();
}
c->login->account->last_player_name = player->disp.name.decode(player->inventory.language);
c->login->account->last_player_name = player->disp.visual.name.decode(player->inventory.language);
break;
}
default:
@@ -3601,8 +3601,8 @@ static asio::awaitable<void> on_30(std::shared_ptr<Client> c, Channel::Message&
default:
throw std::logic_error("extended player data command not implemented for version");
}
ch->disp.visual.version = 4;
ch->disp.visual.name_color_checksum = 0x00000000;
ch->disp.visual.sh.version = 4;
ch->disp.visual.sh.name_color_checksum = 0x00000000;
}
c->character_data_ready_promise->set_value(GetPlayerInfoResult{
@@ -3669,7 +3669,7 @@ static asio::awaitable<void> on_06(std::shared_ptr<Client> c, Channel::Message&
}
auto p = c->character_file();
std::string from_name = p->disp.name.decode(c->language());
std::string from_name = p->disp.visual.name.decode(c->language());
static const std::string whisper_text = "(whisper)";
for (size_t x = 0; x < l->max_clients; x++) {
if (l->clients[x]) {
@@ -3700,7 +3700,7 @@ static asio::awaitable<void> on_06(std::shared_ptr<Client> c, Channel::Message&
c->version(),
c->language(),
c->lobby_client_id,
p->disp.name.decode(c->language()),
p->disp.visual.name.decode(c->language()),
text,
private_flags);
l->battle_record->add_chat_message(c->login->account->account_id, std::move(prepared_message));
@@ -3961,7 +3961,7 @@ static asio::awaitable<void> on_E5_BB(std::shared_ptr<Client> c, Channel::Messag
bool should_send_approve = true;
if (c->bb_connection_phase == 0x03) { // Dressing room
try {
c->character_file()->disp.apply_dressing_room(cmd.preview);
c->character_file()->disp.visual.apply_dressing_room(cmd.preview.visual);
} catch (const std::exception& e) {
send_message_box(c, std::format("$C6Character could not be modified:\n{}", e.what()));
should_send_approve = false;
@@ -3969,7 +3969,8 @@ static asio::awaitable<void> on_E5_BB(std::shared_ptr<Client> c, Channel::Messag
} else {
try {
auto s = c->require_server_state();
c->create_character_file(c->login->account->account_id, c->language(), cmd.preview, s->level_table(c->version()));
c->create_character_file(
c->login->account->account_id, c->language(), cmd.preview.visual, s->level_table(c->version()));
} catch (const std::exception& e) {
send_message_box(c, std::format("$C6New character could not be created:\n{}", e.what()));
should_send_approve = false;
@@ -4271,11 +4272,11 @@ static void on_choice_search_t(std::shared_ptr<Client> c, const ChoiceSearchConf
auto lp = lc->character_file();
auto& result = results.emplace_back();
result.guild_card_number = lc->login->account->account_id;
result.name.encode(lp->disp.name.decode(lc->language()), c->language());
result.name.encode(lp->disp.visual.name.decode(lc->language()), c->language());
std::string info_string = std::format("{} Lv{}\n{}\n",
name_for_char_class(lp->disp.visual.char_class),
name_for_char_class(lp->disp.visual.sh.char_class),
static_cast<size_t>(lp->disp.stats.level + 1),
name_for_section_id(lp->disp.visual.section_id));
name_for_section_id(lp->disp.visual.sh.section_id));
result.info_string.encode(info_string, c->language());
std::string location_string;
if (l->is_game()) {
@@ -4293,7 +4294,7 @@ static void on_choice_search_t(std::shared_ptr<Client> c, const ChoiceSearchConf
result.reconnect_command.port = s->game_server_port_for_version(c->version());
result.meet_user.lobby_refs[0].menu_id = MenuID::LOBBY;
result.meet_user.lobby_refs[0].item_id = l->lobby_id;
result.meet_user.player_name.encode(lp->disp.name.decode(lc->language()), c->language());
result.meet_user.player_name.encode(lp->disp.visual.name.decode(lc->language()), c->language());
// The client can only handle 32 results
if (results.size() >= 0x20) {
break;
@@ -4411,14 +4412,14 @@ static asio::awaitable<void> on_81(std::shared_ptr<Client> c, Channel::Message&
send_simple_mail(
c,
target->login->account->account_id,
target_p->disp.name.decode(target_p->inventory.language),
target_p->disp.visual.name.decode(target_p->inventory.language),
target_p->auto_reply.decode(target_p->inventory.language));
}
}
// Forward the message
send_simple_mail(
target, c->login->account->account_id, c->character_file()->disp.name.decode(c->language()), message);
target, c->login->account->account_id, c->character_file()->disp.visual.name.decode(c->language()), message);
}
}
@@ -4592,7 +4593,7 @@ std::shared_ptr<Lobby> create_game_generic(
}
game->password = password;
game->creator_section_id = p->disp.visual.section_id;
game->creator_section_id = p->disp.visual.sh.section_id;
game->override_section_id = creator_c->override_section_id;
if (game->mode == GameMode::CHALLENGE) {
game->challenge_params = std::make_shared<Lobby::ChallengeParameters>();
@@ -5373,7 +5374,7 @@ static asio::awaitable<void> on_EA_BB(std::shared_ptr<Client> c, Channel::Messag
// TODO: What's the right error code to use here?
send_command(c, 0x02EA, 0x00000001);
} else {
std::string player_name = c->character_file()->disp.name.decode(c->language());
std::string player_name = c->character_file()->disp.visual.name.decode(c->language());
auto team = s->team_index->create(team_name, c->login->account->account_id, player_name);
c->login->account->bb_team_id = team->team_id;
c->login->account->save();
@@ -5412,7 +5413,7 @@ static asio::awaitable<void> on_EA_BB(std::shared_ptr<Client> c, Channel::Messag
s->team_index->add_member(
team->team_id,
added_c->login->account->account_id,
added_c->character_file()->disp.name.decode(added_c->language()));
added_c->character_file()->disp.visual.name.decode(added_c->language()));
send_command(c, 0x04EA, 0x00000000);
send_command(added_c, 0x04EA, 0x00000000);
send_team_metadata_change_notifications(
+325 -396
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -65,7 +65,7 @@ public:
uint32_t player_flags = 0;
bool player_flags_is_v3 = false;
parray<uint8_t, 0x14> technique_levels_v1 = 0xFF;
PlayerVisualConfig visual;
PlayerVisualConfigSharedT<false> visual_sh;
std::string name;
PlayerStats stats;
uint32_t num_items = 0;
+8 -8
View File
@@ -154,7 +154,7 @@ void ReplaySession::apply_default_mask(std::shared_ptr<Event> ev) {
mask.variations = Variations();
mask.random_seed = 0;
for (size_t offset = sizeof(S_JoinGame_GC_64) +
offsetof(S_JoinGame_Ep3_64::Ep3PlayerEntry, disp.visual.name_color_checksum);
offsetof(S_JoinGame_Ep3_64::Ep3PlayerEntry, disp.visual.sh.name_color_checksum);
offset + 4 <= mask_size;
offset += sizeof(S_JoinGame_Ep3_64::Ep3PlayerEntry)) {
*reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(mask_data) + offset) = 0;
@@ -170,7 +170,7 @@ void ReplaySession::apply_default_mask(std::shared_ptr<Event> ev) {
case 0x67:
case 0x68: {
auto update_mask = [&]<typename CmdT>() -> void {
for (size_t offset = offsetof(CmdT, entries) + offsetof(typename CmdT::Entry, disp.visual.name_color_checksum);
for (size_t offset = offsetof(CmdT, entries) + offsetof(typename CmdT::Entry, disp.visual.sh.name_color_checksum);
offset + 4 <= mask_size;
offset += sizeof(typename CmdT::Entry)) {
*reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(mask_data) + offset) = 0;
@@ -192,10 +192,10 @@ void ReplaySession::apply_default_mask(std::shared_ptr<Event> ev) {
auto& mask = check_size_t<S_JoinSpectatorTeam_Ep3_E8>(mask_data, mask_size);
mask.random_seed = 0;
for (size_t z = 0; z < 4; z++) {
mask.players[z].disp.visual.name_color_checksum = 0;
mask.players[z].disp.visual.sh.name_color_checksum = 0;
}
for (size_t z = 0; z < 8; z++) {
mask.spectator_players[z].disp.visual.name_color_checksum = 0;
mask.spectator_players[z].disp.visual.sh.name_color_checksum = 0;
}
}
break;
@@ -265,19 +265,19 @@ void ReplaySession::apply_default_mask(std::shared_ptr<Event> ev) {
const auto& header = check_size_t<G_UnusedHeader>(cmd_data, cmd_size, 0xFFFF);
if (header.subcommand == 0x60) {
auto& mask = check_size_t<G_SyncPlayerDispAndInventory_DCNTE_6x70>(mask_data, mask_size, 0xFFFF);
mask.visual.name_color_checksum = 0;
mask.visual.sh.name_color_checksum = 0;
}
} else if (version == Version::DC_11_2000) {
const auto& header = check_size_t<G_UnusedHeader>(cmd_data, cmd_size, 0xFFFF);
if (header.subcommand == 0x67) {
auto& mask = check_size_t<G_SyncPlayerDispAndInventory_DC112000_6x70>(mask_data, mask_size, 0xFFFF);
mask.visual.name_color_checksum = 0;
mask.visual.sh.name_color_checksum = 0;
}
} else if (!is_pre_v1(version)) {
const auto& header = check_size_t<G_UnusedHeader>(cmd_data, cmd_size, 0xFFFF);
if (header.subcommand == 0x70) {
auto& mask = check_size_t<G_SyncPlayerDispAndInventory_DC_PC_6x70>(mask_data, mask_size, 0xFFFF);
mask.base.visual.name_color_checksum = 0;
mask.visual.sh.name_color_checksum = 0;
}
}
break;
@@ -578,7 +578,7 @@ asio::awaitable<void> ReplaySession::run() {
replay_log.error_f("Expected command:");
phosg::print_data(stderr, ev->data, 0, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
replay_log.error_f("Received command:");
phosg::print_data(stderr, full_command, 0, ev->data, phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
phosg::print_data(stderr, full_command, 0, ev->data, phosg::FormatDataFlags::USE_COLOR | phosg::FormatDataFlags::PRINT_ASCII | phosg::FormatDataFlags::OFFSET_16_BITS);
throw std::runtime_error(std::format("(ev-line {}) received command data does not match expected data", ev->line_num));
}
}
+47 -99
View File
@@ -365,12 +365,11 @@ PSOBBBaseSystemFile::PSOBBBaseSystemFile() {
}
}
PlayerDispDataBBPreview PSOBBCharacterFile::to_preview() const {
PlayerDispDataBBPreview pre;
PlayerDispDataV4Preview PSOBBCharacterFile::to_preview() const {
PlayerDispDataV4Preview pre;
pre.level = this->disp.stats.level;
pre.exp = this->disp.stats.exp;
pre.visual = this->disp.visual;
pre.name = this->disp.name;
pre.play_time_seconds = this->play_time_seconds;
return pre;
}
@@ -378,8 +377,7 @@ PlayerDispDataBBPreview PSOBBCharacterFile::to_preview() const {
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
uint32_t guild_card_number,
Language language,
const PlayerVisualConfig& visual,
const std::string& name,
const PlayerVisualConfigV4& visual,
std::shared_ptr<const LevelTable> level_table) {
static const std::array<std::array<PlayerInventoryItem, 5>, 12> initial_inventory{{
{
@@ -503,9 +501,8 @@ std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
auto ret = std::make_shared<PSOBBCharacterFile>();
ret->disp.visual = visual;
ret->disp.name.encode(name, language);
const auto& initial_items = initial_inventory.at(visual.char_class);
const auto& initial_items = initial_inventory.at(visual.sh.char_class);
ret->inventory.num_items = initial_items.size();
for (size_t z = 0; z < initial_items.size(); z++) {
ret->inventory.items[z] = initial_items[z];
@@ -526,36 +523,36 @@ std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
{0x00, 0x01, 0x0B, 0x0C, 0x04, 0x05, 0x06, 0x08, 0x0A, 0x0D, 0x07, 0x02, 0x11, 0x0A, 0x05, 0x06, 0x01, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x07, 0x02, 0x11, 0x04, 0x05, 0x06, 0x09, 0x0C, 0x00, 0x01, 0x02, 0x11, 0x0D, 0x05, 0x10, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
}};
uint8_t char_class = (visual.char_class > 0x0B) ? 0 : visual.char_class;
uint8_t char_class = (visual.sh.char_class > 0x0B) ? 0 : visual.sh.char_class;
uint8_t mag_color_index;
if (char_class == 2 || char_class == 4 || char_class == 5 || char_class == 9) {
mag_color_index = (visual.skin >= 25) ? 0 : visual.skin.load();
mag_color_index = (visual.sh.skin >= 25) ? 0 : visual.sh.skin.load();
} else {
mag_color_index = (visual.costume >= 18) ? 0 : visual.costume.load();
mag_color_index = (visual.sh.costume >= 18) ? 0 : visual.sh.costume.load();
}
ret->inventory.items[2].data.data2[3] = mag_colors.at(char_class).at(mag_color_index);
ret->inventory.items[13].extension_data2 = 1;
const auto& config = (ret->disp.visual.class_flags & 0x80) ? config_force : config_hunter_ranger;
const auto& config = (ret->disp.visual.sh.class_flags & 0x80) ? config_force : config_hunter_ranger;
for (size_t z = 0; z < config.size(); z++) {
ret->disp.config[z] = config[z];
}
if (level_table) {
level_table->reset_to_base(ret->disp.stats, ret->disp.visual.char_class);
level_table->reset_to_base(ret->disp.stats, ret->disp.visual.sh.char_class);
}
ret->disp.technique_levels_v1.clear(0xFF);
if (ret->disp.visual.class_flags & 0x80) {
if (ret->disp.visual.sh.class_flags & 0x80) {
ret->disp.technique_levels_v1[0] = 0x00; // Forces start with Foie Lv.1
}
ret->inventory.language = language;
ret->guild_card.guild_card_number = guild_card_number;
ret->guild_card.name = ret->disp.name;
ret->guild_card.name = ret->disp.visual.name;
ret->guild_card.present = 1;
ret->guild_card.language = ret->inventory.language;
ret->guild_card.section_id = ret->disp.visual.section_id;
ret->guild_card.char_class = ret->disp.visual.char_class;
ret->guild_card.section_id = ret->disp.visual.sh.section_id;
ret->guild_card.char_class = ret->disp.visual.sh.char_class;
for (size_t z = 0; z < DEFAULT_SYMBOL_CHATS.size(); z++) {
ret->symbol_chats[z] = DEFAULT_SYMBOL_CHATS[z].to_entry(language);
}
@@ -565,26 +562,12 @@ std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
return ret;
}
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
uint32_t guild_card_number,
Language language,
const PlayerDispDataBBPreview& preview,
std::shared_ptr<const LevelTable> level_table) {
return PSOBBCharacterFile::create_from_config(
guild_card_number, language, preview.visual, preview.name.decode(language), level_table);
}
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCNTECharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
Language::JAPANESE,
src.disp.visual,
src.disp.visual.name.decode(Language::JAPANESE),
nullptr);
src.guild_card.guild_card_number, Language::JAPANESE, src.disp.visual);
ret->inventory = src.inventory;
ret->inventory.decode_from_client(Version::DC_V1);
Language language = ret->inventory.language;
ret->disp = src.disp.to_bb(language, language);
ret->disp = src.disp.to_v4(Language::JAPANESE, Language::JAPANESE);
ret->validation_flags = 0;
ret->creation_timestamp = src.creation_timestamp;
ret->play_time_seconds = src.play_time_seconds;
@@ -608,16 +591,11 @@ std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const P
}
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODC112000CharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
src.disp.visual,
src.disp.visual.name.decode(Language::JAPANESE),
nullptr);
Language language = src.inventory.language;
auto ret = PSOBBCharacterFile::create_from_config(src.guild_card.guild_card_number, language, src.disp.visual);
ret->inventory = src.inventory;
ret->inventory.decode_from_client(Version::DC_V1);
Language language = ret->inventory.language;
ret->disp = src.disp.to_bb(language, language);
ret->disp = src.disp.to_v4(language, language);
ret->validation_flags = 0;
ret->creation_timestamp = src.creation_timestamp;
ret->play_time_seconds = src.play_time_seconds;
@@ -651,16 +629,11 @@ std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const P
}
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCV1CharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
src.disp.visual,
src.disp.visual.name.decode(Language::JAPANESE),
nullptr);
Language language = src.inventory.language;
auto ret = PSOBBCharacterFile::create_from_config(src.guild_card.guild_card_number, language, src.disp.visual);
ret->inventory = src.inventory;
ret->inventory.decode_from_client(Version::DC_V1);
Language language = ret->inventory.language;
ret->disp = src.disp.to_bb(language, language);
ret->disp = src.disp.to_v4(language, language);
ret->validation_flags = src.validation_flags;
ret->creation_timestamp = src.creation_timestamp;
ret->play_time_seconds = src.play_time_seconds;
@@ -684,16 +657,11 @@ std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const P
}
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCV2CharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
src.disp.visual,
src.disp.visual.name.decode(Language::JAPANESE),
nullptr);
Language language = src.inventory.language;
auto ret = PSOBBCharacterFile::create_from_config(src.guild_card.guild_card_number, language, src.disp.visual);
ret->inventory = src.inventory;
ret->inventory.decode_from_client(Version::DC_V2);
Language language = ret->inventory.language;
ret->disp = src.disp.to_bb(language, language);
ret->disp = src.disp.to_v4(language, language);
ret->validation_flags = src.validation_flags;
ret->creation_timestamp = src.creation_timestamp;
ret->play_time_seconds = src.play_time_seconds;
@@ -724,18 +692,13 @@ std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const P
}
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCNTECharacterFileCharacter& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
src.disp.visual,
src.disp.visual.name.decode(Language::JAPANESE),
nullptr);
Language language = src.inventory.language;
auto ret = PSOBBCharacterFile::create_from_config(src.guild_card.guild_card_number, language, src.disp.visual);
ret->inventory = src.inventory;
// Note: We intentionally do not call ret->inventory.decode_from_client here. This is because the GC client byteswaps
// data2 in each item before sending it to the server in the 61 and 98 commands, but GetExtendedPlayerInfo does not
// do this, so the data2 fields are already in the correct order here.
Language language = ret->inventory.language;
ret->disp = src.disp.to_bb(language, language);
ret->disp = src.disp.to_v4(language, language);
ret->validation_flags = src.validation_flags;
ret->creation_timestamp = src.creation_timestamp;
ret->play_time_seconds = src.play_time_seconds;
@@ -764,18 +727,13 @@ std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const P
}
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCCharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
src.disp.visual,
src.disp.visual.name.decode(Language::JAPANESE),
nullptr);
Language language = src.inventory.language;
auto ret = PSOBBCharacterFile::create_from_config(src.guild_card.guild_card_number, language, src.disp.visual);
ret->inventory = src.inventory;
// Note: We intentionally do not call ret->inventory.decode_from_client here. This is because the GC client byteswaps
// data2 in each item before sending it to the server in the 61 and 98 commands, but GetExtendedPlayerInfo does not
// do this, so the data2 fields are already in the correct order here.
Language language = ret->inventory.language;
ret->disp = src.disp.to_bb(language, language);
ret->disp = src.disp.to_v4(language, language);
ret->validation_flags = src.validation_flags;
ret->creation_timestamp = src.creation_timestamp;
ret->play_time_seconds = src.play_time_seconds;
@@ -814,15 +772,10 @@ std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const P
}
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCEp3CharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
src.disp.visual,
src.disp.visual.name.decode(Language::JAPANESE),
nullptr);
Language language = src.inventory.language;
auto ret = PSOBBCharacterFile::create_from_config(src.guild_card.guild_card_number, language, src.disp.visual);
ret->inventory = src.inventory;
Language language = ret->inventory.language;
ret->disp = src.disp.to_bb(language, language);
ret->disp = src.disp.to_v4(language, language);
ret->validation_flags = src.validation_flags;
ret->creation_timestamp = src.creation_timestamp;
ret->play_time_seconds = src.play_time_seconds;
@@ -862,16 +815,11 @@ std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const P
}
std::shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOXBCharacterFile::Character& src) {
auto ret = PSOBBCharacterFile::create_from_config(
src.guild_card.guild_card_number,
src.inventory.language,
src.disp.visual,
src.disp.visual.name.decode(Language::JAPANESE),
nullptr);
Language language = src.inventory.language;
auto ret = PSOBBCharacterFile::create_from_config(src.guild_card.guild_card_number, language, src.disp.visual);
ret->inventory = src.inventory;
ret->inventory.decode_from_client(Version::XB_V3);
Language language = ret->inventory.language;
ret->disp = src.disp.to_bb(language, language);
ret->disp = src.disp.to_v4(language, language);
ret->validation_flags = src.validation_flags;
ret->creation_timestamp = src.creation_timestamp;
ret->play_time_seconds = src.play_time_seconds;
@@ -918,7 +866,7 @@ PSODCNTECharacterFile::Character PSOBBCharacterFile::as_dc_nte(uint64_t hardware
// We don't need to do the v1-compatible encoding (hence it is OK to pass nullptr here) but we do need to encode mag
// stats in the v2 format
ret.inventory.encode_for_client(Version::DC_NTE, nullptr);
ret.disp = this->disp.to_dcpcv3<false>(language, language);
ret.disp = this->disp.to_v123<false>(language, language);
ret.disp.visual.enforce_lobby_join_limits_for_version(Version::DC_V2);
ret.masked_creation_timestamp = this->creation_timestamp ^ static_cast<uint32_t>(hardware_id >> 16);
ret.creation_timestamp = this->creation_timestamp;
@@ -944,7 +892,7 @@ PSODC112000CharacterFile::Character PSOBBCharacterFile::as_11_2000(uint64_t hard
// We don't need to do the v1-compatible encoding (hence it is OK to pass nullptr here) but we do need to encode mag
// stats in the v2 format
ret.inventory.encode_for_client(Version::DC_11_2000, nullptr);
ret.disp = this->disp.to_dcpcv3<false>(language, language);
ret.disp = this->disp.to_v123<false>(language, language);
ret.disp.visual.enforce_lobby_join_limits_for_version(Version::DC_V2);
ret.masked_creation_timestamp = this->creation_timestamp ^ static_cast<uint32_t>(hardware_id >> 16);
ret.creation_timestamp = this->creation_timestamp;
@@ -981,7 +929,7 @@ PSOBBCharacterFile::operator PSODCV1CharacterFile::Character() const {
// We don't need to do the v1-compatible encoding (hence it is OK to pass nullptr here) but we do need to encode mag
// stats in the v2 format
ret.inventory.encode_for_client(Version::DC_V1, nullptr);
ret.disp = this->disp.to_dcpcv3<false>(language, language);
ret.disp = this->disp.to_v123<false>(language, language);
ret.disp.visual.enforce_lobby_join_limits_for_version(Version::DC_V2);
ret.validation_flags = this->validation_flags;
ret.creation_timestamp = this->creation_timestamp;
@@ -1013,7 +961,7 @@ PSOBBCharacterFile::operator PSODCV2CharacterFile::Character() const {
// We don't need to do the v1-compatible encoding (hence it is OK to pass nullptr here) but we do need to encode mag
// stats in the v2 format
ret.inventory.encode_for_client(Version::DC_V2, nullptr);
ret.disp = this->disp.to_dcpcv3<false>(language, language);
ret.disp = this->disp.to_v123<false>(language, language);
ret.disp.visual.enforce_lobby_join_limits_for_version(Version::DC_V2);
ret.validation_flags = this->validation_flags;
ret.creation_timestamp = this->creation_timestamp;
@@ -1054,7 +1002,7 @@ PSOBBCharacterFile::operator PSOGCNTECharacterFileCharacter() const {
// Note: We intentionally do not call ret.inventory.encode_for_client here. This is because the GC client byteswaps
// data2 in each item before sending it to the server in the 61 and 98 commands, but GetExtendedPlayerInfo does not
// do this, so the data2 fields are already in the correct order here.
ret.disp = this->disp.to_dcpcv3<true>(language, language);
ret.disp = this->disp.to_v123<true>(language, language);
ret.disp.visual.enforce_lobby_join_limits_for_version(Version::GC_V3);
ret.validation_flags = this->validation_flags;
ret.creation_timestamp = this->creation_timestamp;
@@ -1091,7 +1039,7 @@ PSOBBCharacterFile::operator PSOGCCharacterFile::Character() const {
// Note: We intentionally do not call ret.inventory.encode_for_client here. This is because the GC client byteswaps
// data2 in each item before sending it to the server in the 61 and 98 commands, but GetExtendedPlayerInfo does not
// do this, so the data2 fields are already in the correct order here.
ret.disp = this->disp.to_dcpcv3<true>(language, language);
ret.disp = this->disp.to_v123<true>(language, language);
ret.disp.visual.enforce_lobby_join_limits_for_version(Version::GC_V3);
ret.validation_flags = this->validation_flags;
ret.creation_timestamp = this->creation_timestamp;
@@ -1136,7 +1084,7 @@ PSOBBCharacterFile::operator PSOXBCharacterFile::Character() const {
PSOXBCharacterFile::Character ret;
ret.inventory = this->inventory;
ret.inventory.encode_for_client(Version::XB_V3, nullptr);
ret.disp = this->disp.to_dcpcv3<false>(language, language);
ret.disp = this->disp.to_v123<false>(language, language);
ret.disp.visual.enforce_lobby_join_limits_for_version(Version::XB_V3);
ret.validation_flags = this->validation_flags;
ret.creation_timestamp = this->creation_timestamp;
@@ -1406,8 +1354,8 @@ void PSOBBCharacterFile::import_tethealla_material_usage(std::shared_ptr<const L
}
PlayerStats level_base_stats = this->disp.stats;
level_table->reset_to_base(level_base_stats, this->disp.visual.char_class);
level_table->advance_to_level(level_base_stats, this->disp.stats.level, this->disp.visual.char_class);
level_table->reset_to_base(level_base_stats, this->disp.visual.sh.char_class);
level_table->advance_to_level(level_base_stats, this->disp.stats.level, this->disp.visual.sh.char_class);
uint64_t pow = (this->disp.stats.char_stats.atp - level_base_stats.char_stats.atp) / 2;
uint64_t mind = (this->disp.stats.char_stats.mst - level_base_stats.char_stats.mst) / 2;
@@ -1428,8 +1376,8 @@ void PSOBBCharacterFile::import_tethealla_material_usage(std::shared_ptr<const L
void PSOBBCharacterFile::recompute_stats(std::shared_ptr<const LevelTable> level_table, bool reset_exp) {
uint32_t level = this->disp.stats.level;
uint32_t exp = this->disp.stats.exp;
level_table->reset_to_base(this->disp.stats, this->disp.visual.char_class);
level_table->advance_to_level(this->disp.stats, level, this->disp.visual.char_class);
level_table->reset_to_base(this->disp.stats, this->disp.visual.sh.char_class);
level_table->advance_to_level(this->disp.stats, level, this->disp.visual.sh.char_class);
if (!reset_exp) {
this->disp.stats.exp = exp;
}
+25 -20
View File
@@ -349,7 +349,7 @@ struct PSODCNTECharacterFile {
struct Character {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 034C:---- */ PlayerDispDataV123 disp;
// masked_creation_timestamp is expected to contain the value (creation_timestamp ^ hardware_id_mid), where
// hardware_id_mid contains the middle 32 bits of the 64-bit hardware ID returned by the SYSINFO_ID syscall (the
// top and bottom 16 bits are ignored for this purpose).
@@ -385,7 +385,7 @@ struct PSODC112000CharacterFile {
struct Character {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 034C:---- */ PlayerDispDataV123 disp;
/* 041C:0000 */ le_uint32_t masked_creation_timestamp = 0;
/* 0420:0004 */ le_uint32_t creation_timestamp = 0;
/* 0424:0008 */ le_uint32_t signature = 0xBB40711D;
@@ -417,7 +417,7 @@ struct PSODCV1CharacterFile {
struct Character {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 034C:---- */ PlayerDispDataV123 disp;
/* 041C:0000 */ le_uint32_t validation_flags = 0;
/* 0420:0004 */ le_uint32_t creation_timestamp = 0;
/* 0424:0008 */ le_uint32_t signature = 0xA205B064;
@@ -445,7 +445,7 @@ struct PSODCV2CharacterFile {
struct Character {
// See PSOGCCharacterFile::Character for descriptions of fields' meanings.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 034C:---- */ PlayerDispDataV123 disp;
/* 041C:0000 */ le_uint32_t validation_flags = 0;
/* 0420:0004 */ le_uint32_t creation_timestamp = 0;
/* 0424:0008 */ le_uint32_t signature = 0xA205B064;
@@ -491,7 +491,7 @@ struct PSOPCCharacterFile { // PSO______SYS and PSO______SYD
/* 0000 */ le_uint32_t checksum = 0;
struct Character {
/* 0000 */ PlayerInventory inventory;
/* 034C */ PlayerDispDataDCPCV3 disp;
/* 034C */ PlayerDispDataV123 disp;
/* 041C */ be_uint32_t validation_flags = 0;
/* 0420 */ be_uint32_t creation_timestamp = 0;
/* 0424 */ be_uint32_t signature = 0x6C5D889E;
@@ -528,7 +528,7 @@ struct PSOPCCharacterFile { // PSO______SYS and PSO______SYD
struct PSOGCNTECharacterFileCharacter {
/* 0000:---- */ PlayerInventoryBE inventory;
/* 034C:---- */ PlayerDispDataDCPCV3BE disp;
/* 034C:---- */ PlayerDispDataV123BE disp;
/* 041C:0000 */ be_uint32_t validation_flags = 0;
/* 0420:0004 */ be_uint32_t creation_timestamp = 0;
/* 0424:0008 */ be_uint32_t signature = 0xA205B064;
@@ -558,7 +558,7 @@ struct PSOGCCharacterFile {
// This structure is internally split into two by the game. The offsets here are relative to the start of this
// structure (first column), and relative to the start of the second internal structure (second column).
/* 0000:---- */ PlayerInventoryBE inventory;
/* 034C:---- */ PlayerDispDataDCPCV3BE disp;
/* 034C:---- */ PlayerDispDataV123BE disp;
// Known bits in the validation_flags field:
// 00000001: Character was not saved after disconnecting (and the message about items being deleted is shown in
// the select menu)
@@ -638,7 +638,7 @@ struct PSOGCEp3NTECharacter {
// This structure is internally split into two by the game. The offsets here are relative to the start of this
// structure (first column), and relative to the start of the second internal structure (second column).
/* 0000:---- */ PlayerInventoryBE inventory;
/* 034C:---- */ PlayerDispDataDCPCV3BE disp;
/* 034C:---- */ PlayerDispDataV123BE disp;
/* 041C:0000 */ be_uint32_t validation_flags = 0;
/* 0420:0004 */ be_uint32_t creation_timestamp = 0;
/* 0424:0008 */ be_uint32_t signature = 0xA205B064;
@@ -673,7 +673,7 @@ struct PSOGCEp3CharacterFile {
// This structure is internally split into two by the game. The offsets here are relative to the start of this
// structure (first column), and relative to the start of the second internal structure (second column).
/* 0000:---- */ PlayerInventoryBE inventory;
/* 034C:---- */ PlayerDispDataDCPCV3BE disp;
/* 034C:---- */ PlayerDispDataV123BE disp;
/* 041C:0000 */ be_uint32_t validation_flags = 0;
/* 0420:0004 */ be_uint32_t creation_timestamp = 0;
/* 0424:0008 */ be_uint32_t signature = 0xA205B064;
@@ -735,7 +735,7 @@ struct PSOXBCharacterFile {
// structure (first column), and relative to the start of the second internal structure (second column). Most
// fields have the same meanings as in PSOGCCharacterFile::Character.
/* 0000:---- */ PlayerInventory inventory;
/* 034C:---- */ PlayerDispDataDCPCV3 disp;
/* 034C:---- */ PlayerDispDataV123 disp;
/* 041C:0000 */ le_uint32_t validation_flags = 0;
/* 0420:0004 */ le_uint32_t creation_timestamp = 0;
/* 0424:0008 */ le_uint32_t signature = 0xC87ED5B1;
@@ -794,7 +794,7 @@ struct PSOBBCharacterFile {
// .psochar file. See PSOCHARFile below for the full file format.
/* 0000 */ PlayerInventory inventory;
/* 034C */ PlayerDispDataBB disp;
/* 034C */ PlayerDispDataV4 disp;
/* 04DC */ le_uint32_t validation_flags = 0;
/* 04E0 */ le_uint32_t creation_timestamp = 0;
/* 04E4 */ le_uint32_t signature = 0xC87ED5B1;
@@ -823,19 +823,24 @@ struct PSOBBCharacterFile {
PSOBBCharacterFile() = default;
PlayerDispDataBBPreview to_preview() const;
PlayerDispDataV4Preview to_preview() const;
static std::shared_ptr<PSOBBCharacterFile> create_from_config(
uint32_t guild_card_number,
Language language,
const PlayerVisualConfig& visual,
const std::string& name,
std::shared_ptr<const LevelTable> level_table);
static std::shared_ptr<PSOBBCharacterFile> create_from_preview(
const PlayerVisualConfigV4& visual,
std::shared_ptr<const LevelTable> level_table = nullptr);
template <bool BE>
static std::shared_ptr<PSOBBCharacterFile> create_from_config(
uint32_t guild_card_number,
Language language,
const PlayerDispDataBBPreview& preview,
std::shared_ptr<const LevelTable> level_table);
const PlayerVisualConfigV123T<BE>& visual,
std::shared_ptr<const LevelTable> level_table = nullptr) {
return PSOBBCharacterFile::create_from_config(
guild_card_number, language, visual.to_v4(language, language), level_table);
}
static std::shared_ptr<PSOBBCharacterFile> create_from_file(const PSODCNTECharacterFile::Character& src);
static std::shared_ptr<PSOBBCharacterFile> create_from_file(const PSODC112000CharacterFile::Character& src);
static std::shared_ptr<PSOBBCharacterFile> create_from_file(const PSODCV1CharacterFile::Character& src);
@@ -1034,11 +1039,11 @@ struct LegacySavedPlayerDataBB { // .nsc file format
/* 0000 */ be_uint64_t signature = SIGNATURE_V1;
/* 0008 */ parray<uint8_t, 0x20> unused;
/* 0028 */ PlayerRecordsBattle battle_records;
/* 0040 */ PlayerDispDataBBPreview preview;
/* 0040 */ PlayerDispDataV4Preview preview;
/* 00BC */ pstring<TextEncoding::UTF16, 0x00AC> auto_reply;
/* 0214 */ PlayerBank200 bank;
/* 14DC */ PlayerRecordsChallengeBB challenge_records;
/* 161C */ PlayerDispDataBB disp;
/* 161C */ PlayerDispDataV4 disp;
/* 17AC */ pstring<TextEncoding::UTF16, 0x0058> guild_card_description;
/* 185C */ pstring<TextEncoding::UTF16, 0x00AC> info_board;
/* 19B4 */ PlayerInventory inventory;
+47 -51
View File
@@ -665,7 +665,7 @@ void send_system_file_bb(std::shared_ptr<Client> c) {
send_command_t(c, 0x00E2, 0x00000000, cmd);
}
void send_player_preview_bb(std::shared_ptr<Client> c, int8_t character_index, const PlayerDispDataBBPreview* preview) {
void send_player_preview_bb(std::shared_ptr<Client> c, int8_t character_index, const PlayerDispDataV4Preview* preview) {
if (!preview) { // No player exists
S_PlayerPreview_NoPlayer_BB_00E4 cmd = {character_index, 0x00000002};
send_command_t(c, 0x00E4, 0x00000000, cmd);
@@ -754,7 +754,7 @@ void send_complete_player_bb(std::shared_ptr<Client> c) {
}
send_command_t(c, 0x00E7, 0x00000000, cmd);
c->login->account->last_player_name = p->disp.name.decode(p->inventory.language);
c->login->account->last_player_name = p->disp.visual.name.decode(p->inventory.language);
}
enum class ColorMode {
@@ -1109,7 +1109,7 @@ void send_info_board_t(std::shared_ptr<Client> c) {
if (lc) {
auto lp = lc->character_file(true, false);
auto& e = entries.emplace_back();
e.name.encode(lp->disp.name.decode(lp->inventory.language), c->language());
e.name.encode(lp->disp.visual.name.decode(lp->inventory.language), c->language());
e.message.encode(add_color(lp->info_board.decode(lp->inventory.language)), c->language());
}
}
@@ -1199,7 +1199,7 @@ void send_card_search_result_t(std::shared_ptr<Client> c, std::shared_ptr<Client
cmd.extension.lobby_refs[0].menu_id = MenuID::LOBBY;
cmd.extension.lobby_refs[0].item_id = result_lobby->lobby_id;
auto rp = result->character_file(true, false);
cmd.extension.player_name.encode(rp->disp.name.decode(rp->inventory.language), c->language());
cmd.extension.player_name.encode(rp->disp.visual.name.decode(rp->inventory.language), c->language());
send_command_t(c, 0x41, 0x00, cmd);
}
@@ -1363,12 +1363,12 @@ void send_guild_card(std::shared_ptr<Client> c, std::shared_ptr<Client> source)
c->channel,
source->login->account->account_id,
xb_user_id,
source_p->disp.name.decode(source->language()),
source_p->disp.visual.name.decode(source->language()),
source_team ? source_team->name : "",
source_p->guild_card.description.decode(source->language()),
source->language(),
source_p->disp.visual.section_id,
source_p->disp.visual.char_class);
source_p->disp.visual.sh.section_id,
source_p->disp.visual.sh.char_class);
}
template <typename EntryT>
@@ -1803,8 +1803,7 @@ void populate_lobby_data_for_client(LobbyDataT& ret, std::shared_ptr<const Clien
ret.player_tag = 0x00010000;
ret.guild_card_number = c->login->account->account_id;
ret.client_id = c->lobby_client_id;
std::string name = c->character_file()->disp.name.decode(c->language());
ret.name.encode(name, viewer_c->language());
ret.name.encode(c->character_file()->disp.visual.name.decode(c->language()), viewer_c->language());
}
template <>
@@ -1818,8 +1817,7 @@ void populate_lobby_data_for_client(
ret.netloc.account_id = 0xAE00000000000000 | c->login->account->account_id;
}
ret.client_id = c->lobby_client_id;
std::string name = c->character_file()->disp.name.decode(c->language());
ret.name.encode(name, viewer_c->language());
ret.name.encode(c->character_file()->disp.visual.name.decode(c->language()), viewer_c->language());
}
template <>
@@ -1836,8 +1834,7 @@ void populate_lobby_data_for_client<PlayerLobbyDataBB>(
ret.team_master_guild_card_number = 0;
ret.team_id = 0;
}
std::string name = c->character_file()->disp.name.decode(c->language());
ret.name.encode(name, viewer_c->language());
ret.name.encode(c->character_file()->disp.visual.name.decode(c->language()), viewer_c->language());
}
static void send_join_spectator_team(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l) {
@@ -1877,22 +1874,22 @@ static void send_join_spectator_team(std::shared_ptr<Client> c, std::shared_ptr<
populate_lobby_data_for_client(p.lobby_data, wc, c);
p.inventory = wc_p->inventory;
p.inventory.encode_for_client(c->version(), s->item_parameter_table_for_encode(c->version()));
p.disp = wc_p->disp.to_dcpcv3<false>(c->language(), p.inventory.language);
p.disp = wc_p->disp.to_v123<false>(c->language(), p.inventory.language);
p.disp.enforce_lobby_join_limits_for_version(c->version());
auto& e = cmd.entries[z];
e.player_tag = 0x00010000;
e.guild_card_number = wc->login->account->account_id;
e.name.encode(wc_p->disp.name.decode(wc_p->inventory.language), c->language());
e.name.encode(wc_p->disp.visual.name.decode(wc_p->inventory.language), c->language());
e.present = 1;
e.level = wc->ep3_config
? (wc->ep3_config->online_clv_exp / 100)
: wc_p->disp.stats.level.load();
e.name_color = wc_p->disp.visual.name_color;
e.name_color = wc_p->disp.visual.sh.name_color;
uint32_t name_color = s->name_color_for_client(wc);
if (name_color) {
p.disp.visual.name_color = name_color;
p.disp.visual.sh.name_color = name_color;
e.name_color = name_color;
}
@@ -1930,7 +1927,7 @@ static void send_join_spectator_team(std::shared_ptr<Client> c, std::shared_ptr<
e.name = entry.disp.visual.name;
e.present = 1;
e.level = entry.level;
e.name_color = entry.disp.visual.name_color;
e.name_color = entry.disp.visual.sh.name_color;
player_count++;
}
@@ -1947,7 +1944,7 @@ static void send_join_spectator_team(std::shared_ptr<Client> c, std::shared_ptr<
auto& cmd_e = cmd.entries[z];
populate_lobby_data_for_client(cmd_p.lobby_data, other_c, c);
cmd_p.inventory = other_p->inventory;
cmd_p.disp = other_p->disp.to_dcpcv3<false>(c->language(), cmd_p.inventory.language);
cmd_p.disp = other_p->disp.to_v123<false>(c->language(), cmd_p.inventory.language);
cmd_p.disp.enforce_lobby_join_limits_for_version(c->version());
cmd_e.player_tag = 0x00010000;
@@ -1957,11 +1954,11 @@ static void send_join_spectator_team(std::shared_ptr<Client> c, std::shared_ptr<
cmd_e.level = other_c->ep3_config
? (other_c->ep3_config->online_clv_exp / 100)
: other_p->disp.stats.level.load();
cmd_e.name_color = other_p->disp.visual.name_color;
cmd_e.name_color = other_p->disp.visual.sh.name_color;
uint32_t name_color = s->name_color_for_client(other_c);
if (name_color) {
cmd_p.disp.visual.name_color = name_color;
cmd_p.disp.visual.sh.name_color = name_color;
cmd_e.name_color = name_color;
}
@@ -2070,12 +2067,12 @@ void send_join_game(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l) {
auto& cmd_p = cmd.players_ep3[x];
cmd_p.inventory = other_p->inventory;
cmd_p.inventory.encode_for_client(c->version(), s->item_parameter_table_for_encode(c->version()));
cmd_p.disp = convert_player_disp_data<PlayerDispDataDCPCV3>(
cmd_p.disp = convert_player_disp_data<PlayerDispDataV123>(
other_p->disp, c->language(), other_p->inventory.language);
cmd_p.disp.enforce_lobby_join_limits_for_version(c->version());
uint32_t name_color = s->name_color_for_client(lc);
if (name_color) {
cmd_p.disp.visual.name_color = name_color;
cmd_p.disp.visual.sh.name_color = name_color;
}
}
}
@@ -2191,9 +2188,9 @@ void send_join_lobby_t(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l, std:
e.disp.enforce_lobby_join_limits_for_version(c->version());
uint32_t name_color = s->name_color_for_client(lc);
if (name_color) {
e.disp.visual.name_color = name_color;
e.disp.visual.sh.name_color = name_color;
if (is_v1_or_v2(c->version())) {
e.disp.visual.compute_name_color_checksum();
e.disp.visual.sh.compute_name_color_checksum();
}
}
}
@@ -2257,11 +2254,11 @@ void send_join_lobby_xb(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l, std
populate_lobby_data_for_client(e.lobby_data, lc, c);
e.inventory = lp->inventory;
e.inventory.encode_for_client(c->version(), s->item_parameter_table_for_encode(c->version()));
e.disp = convert_player_disp_data<PlayerDispDataDCPCV3>(lp->disp, c->language(), lp->inventory.language);
e.disp = convert_player_disp_data<PlayerDispDataV123>(lp->disp, c->language(), lp->inventory.language);
e.disp.enforce_lobby_join_limits_for_version(c->version());
uint32_t name_color = s->name_color_for_client(lc);
if (name_color) {
e.disp.visual.name_color = name_color;
e.disp.visual.sh.name_color = name_color;
}
}
@@ -2306,14 +2303,14 @@ void send_join_lobby_dc_nte(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l,
e.inventory = lp->inventory;
e.inventory.encode_for_client(c->version(), s->item_parameter_table_for_encode(c->version()));
if ((lc == c) && is_v1_or_v2(c->version()) && lc->v1_v2_last_reported_disp) {
e.disp = convert_player_disp_data<PlayerDispDataDCPCV3>(*lc->v1_v2_last_reported_disp, c->language(), lp->inventory.language);
e.disp = convert_player_disp_data<PlayerDispDataV123>(*lc->v1_v2_last_reported_disp, c->language(), lp->inventory.language);
} else {
e.disp = convert_player_disp_data<PlayerDispDataDCPCV3>(lp->disp, c->language(), lp->inventory.language);
e.disp = convert_player_disp_data<PlayerDispDataV123>(lp->disp, c->language(), lp->inventory.language);
e.disp.enforce_lobby_join_limits_for_version(c->version());
uint32_t name_color = s->name_color_for_client(lc);
if (name_color) {
e.disp.visual.name_color = name_color;
e.disp.visual.compute_name_color_checksum();
e.disp.visual.sh.name_color = name_color;
e.disp.visual.sh.compute_name_color_checksum();
}
}
}
@@ -2332,23 +2329,23 @@ void send_join_lobby(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l) {
break;
case Version::DC_V1:
case Version::DC_V2:
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3, PlayerRecordsEntry_DC>(c, l);
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataV123, PlayerRecordsEntry_DC>(c, l);
break;
case Version::PC_NTE:
case Version::PC_V2:
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataDCPCV3, PlayerRecordsEntry_PC>(c, l);
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataV123, PlayerRecordsEntry_PC>(c, l);
break;
case Version::GC_NTE:
case Version::GC_V3:
case Version::GC_EP3_NTE:
case Version::GC_EP3:
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3, PlayerRecordsEntry_V3>(c, l);
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataV123, PlayerRecordsEntry_V3>(c, l);
break;
case Version::XB_V3:
send_join_lobby_xb(c, l);
break;
case Version::BB_V4:
send_join_lobby_t<PlayerLobbyDataBB, PlayerDispDataBB, PlayerRecordsEntry_BB>(c, l);
send_join_lobby_t<PlayerLobbyDataBB, PlayerDispDataV4, PlayerRecordsEntry_BB>(c, l);
break;
default:
throw std::logic_error("unimplemented versioned command");
@@ -2371,23 +2368,23 @@ void send_player_join_notification(std::shared_ptr<Client> c,
break;
case Version::DC_V1:
case Version::DC_V2:
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3, PlayerRecordsEntry_DC>(c, l, joining_client);
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataV123, PlayerRecordsEntry_DC>(c, l, joining_client);
break;
case Version::PC_NTE:
case Version::PC_V2:
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataDCPCV3, PlayerRecordsEntry_PC>(c, l, joining_client);
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataV123, PlayerRecordsEntry_PC>(c, l, joining_client);
break;
case Version::GC_NTE:
case Version::GC_V3:
case Version::GC_EP3_NTE:
case Version::GC_EP3:
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataDCPCV3, PlayerRecordsEntry_V3>(c, l, joining_client);
send_join_lobby_t<PlayerLobbyDataDCGC, PlayerDispDataV123, PlayerRecordsEntry_V3>(c, l, joining_client);
break;
case Version::XB_V3:
send_join_lobby_xb(c, l, joining_client);
break;
case Version::BB_V4:
send_join_lobby_t<PlayerLobbyDataBB, PlayerDispDataBB, PlayerRecordsEntry_BB>(c, l, joining_client);
send_join_lobby_t<PlayerLobbyDataBB, PlayerDispDataV4, PlayerRecordsEntry_BB>(c, l, joining_client);
break;
default:
throw std::logic_error("unimplemented versioned command");
@@ -3030,11 +3027,8 @@ void send_game_player_state(std::shared_ptr<Client> to_c, std::shared_ptr<Client
// created/destroyed, but currently we don't.
to_send.area = from_c->floor;
to_send.technique_levels_v1 = from_p->disp.technique_levels_v1;
to_send.visual = from_p->disp.visual;
to_send.name = from_p->disp.name.decode(from_c->language());
if (to_c->version() != Version::BB_V4) {
to_send.visual.name.encode(to_send.name, to_c->language());
}
to_send.visual_sh = from_p->disp.visual.sh;
to_send.name = from_p->disp.visual.name.decode(from_c->language());
to_send.stats = from_p->disp.stats;
to_send.num_items = from_p->inventory.num_items;
to_send.items = from_p->inventory.items;
@@ -3513,7 +3507,7 @@ std::string ep3_description_for_client(std::shared_ptr<Client> c) {
auto p = c->character_file();
return std::format(
"{} CLv{} {}",
name_for_char_class(p->disp.visual.char_class),
name_for_char_class(p->disp.visual.sh.char_class),
p->disp.stats.level + 1,
char_for_language(p->inventory.language));
}
@@ -3560,7 +3554,7 @@ void send_ep3_game_details_t(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l
if (player.is_human()) {
try {
auto other_c = account_id_to_client.at(player.account_id);
entry.name.encode(other_c->character_file()->disp.name.decode(other_c->language()), c->language());
entry.name.encode(other_c->character_file()->disp.visual.name.decode(other_c->language()), c->language());
entry.description.encode(ep3_description_for_client(other_c), c->language());
} catch (const std::out_of_range&) {
entry.name.encode(player.player_name, c->language());
@@ -3581,7 +3575,7 @@ void send_ep3_game_details_t(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l
for (auto spec_c : l->clients) {
if (spec_c) {
auto& entry = cmd.spectator_entries[cmd.num_spectators++];
entry.name.encode(spec_c->character_file()->disp.name.decode(spec_c->language()), c->language());
entry.name.encode(spec_c->character_file()->disp.visual.name.decode(spec_c->language()), c->language());
entry.description.encode(ep3_description_for_client(spec_c), c->language());
}
}
@@ -3598,7 +3592,8 @@ void send_ep3_game_details_t(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l
size_t num_players = 0;
for (const auto& opp_c : primary_lobby->clients) {
if (opp_c) {
cmd.player_entries[num_players].name.encode(opp_c->character_file()->disp.name.decode(opp_c->language()), c->language());
cmd.player_entries[num_players].name.encode(
opp_c->character_file()->disp.visual.name.decode(opp_c->language()), c->language());
cmd.player_entries[num_players].description.encode(ep3_description_for_client(opp_c), c->language());
num_players++;
}
@@ -3611,7 +3606,7 @@ void send_ep3_game_details_t(std::shared_ptr<Client> c, std::shared_ptr<Lobby> l
for (auto spec_c : l->clients) {
if (spec_c) {
auto& entry = cmd.spectator_entries[num_spectators++];
entry.name.encode(spec_c->character_file()->disp.name.decode(spec_c->language()), c->language());
entry.name.encode(spec_c->character_file()->disp.visual.name.decode(spec_c->language()), c->language());
entry.description.encode(ep3_description_for_client(spec_c), c->language());
}
}
@@ -3730,7 +3725,8 @@ void send_ep3_tournament_match_result(std::shared_ptr<Lobby> l, uint32_t meseta_
if (player.is_human()) {
try {
auto pc = account_id_to_client.at(player.account_id);
entry.player_names[z].encode(pc->character_file()->disp.name.decode(pc->language()), lc->language());
entry.player_names[z].encode(
pc->character_file()->disp.visual.name.decode(pc->language()), lc->language());
} catch (const std::out_of_range&) {
entry.player_names[z].encode(player.player_name, lc->language());
}
@@ -4203,7 +4199,7 @@ static S_TeamInfoForPlayer_BB_13EA_15EA_Entry team_metadata_for_client(std::shar
S_TeamInfoForPlayer_BB_13EA_15EA_Entry cmd;
cmd.lobby_client_id = c->lobby_client_id;
cmd.guild_card_number = c->login->account->account_id;
cmd.player_name = c->character_file()->disp.name;
cmd.player_name = c->character_file()->disp.visual.name;
if (team) {
cmd.membership = team->base_membership_for_member(c->login->account->account_id);
if (team->flag_data) {
+1 -1
View File
@@ -192,7 +192,7 @@ void send_pc_console_split_reconnect(
void send_client_init_bb(std::shared_ptr<Client> c, uint32_t error);
void send_system_file_bb(std::shared_ptr<Client> c);
void send_player_preview_bb(std::shared_ptr<Client> c, int8_t character_index, const PlayerDispDataBBPreview* preview);
void send_player_preview_bb(std::shared_ptr<Client> c, int8_t character_index, const PlayerDispDataV4Preview* preview);
void send_accept_client_checksum_bb(std::shared_ptr<Client> c);
void send_guild_card_header_bb(std::shared_ptr<Client> c);
void send_guild_card_chunk_bb(std::shared_ptr<Client> c, size_t chunk_index);
+7 -2
View File
@@ -972,7 +972,8 @@ asio::awaitable<std::deque<std::string>> fn_chat(ShellCommand::Args& args) {
auto l = c->require_lobby();
for (auto& lc : l->clients) {
if (lc) {
send_chat_message(lc, c->login->account->account_id, c->character_file()->disp.name.decode(c->language()), text, 0);
send_chat_message(
lc, c->login->account->account_id, c->character_file()->disp.visual.name.decode(c->language()), text, 0);
}
}
}
@@ -1006,7 +1007,11 @@ asio::awaitable<std::deque<std::string>> fn_wchat(ShellCommand::Args& args) {
for (auto& lc : l->clients) {
if (lc) {
send_chat_message(
lc, c->login->account->account_id, c->character_file()->disp.name.decode(c->language()), args.args, 0x40);
lc,
c->login->account->account_id,
c->character_file()->disp.visual.name.decode(c->language()),
args.args,
0x40);
}
}
}