update PlayerVisualConfigV4 struct to match client implementation
This commit is contained in:
+20
-24
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user