fix cross-version lobby appearance and name colors

This commit is contained in:
Martin Michelsen
2023-10-21 20:44:01 -07:00
parent 7b035d311f
commit c4790068ef
9 changed files with 259 additions and 83 deletions
+2 -2
View File
@@ -834,7 +834,7 @@ static void server_command_edit(shared_ptr<Client> c, const std::u16string& args
} else if (tokens.at(0) == "npc") {
if (tokens.at(1) == "none") {
p->disp.visual.extra_model = 0;
p->disp.visual.v2_flags &= 0xFD;
p->disp.visual.validation_flags &= 0xFD;
} else {
uint8_t npc = npc_for_name(decode_sjis(tokens.at(1)));
if (npc == 0xFF) {
@@ -842,7 +842,7 @@ static void server_command_edit(shared_ptr<Client> c, const std::u16string& args
return;
}
p->disp.visual.extra_model = npc;
p->disp.visual.v2_flags |= 0x02;
p->disp.visual.validation_flags |= 0x02;
}
} else if (tokens.at(0) == "tech") {
uint8_t level = stoul(tokens.at(2)) - 1;
+3 -2
View File
@@ -1258,10 +1258,11 @@ struct S_JoinGame_GC_Ep3_64 : S_JoinGame_GC_64 {
// This field is only present if the game (and client) is Episode 3. Similarly
// to lobby_data in the base struct, all four of these are always present and
// they are filled in in slot positions.
struct {
struct Ep3PlayerEntry {
PlayerInventory inventory;
PlayerDispDataDCPCV3 disp;
} __packed__ players_ep3[4];
} __packed__;
parray<Ep3PlayerEntry, 4> players_ep3;
} __packed__;
struct S_JoinGame_XB_64 : S_JoinGame_DC_PC<PlayerLobbyDataXB> {
+116 -18
View File
@@ -6,6 +6,7 @@
#include <phosg/Filesystem.hh>
#include <phosg/Hash.hh>
#include <phosg/Random.hh>
#include <stdexcept>
#include "ItemData.hh"
@@ -19,29 +20,126 @@ using namespace std;
FileContentsCache player_files_cache(300 * 1000 * 1000);
uint32_t PlayerVisualConfig::compute_name_color_checksum(uint32_t name_color) {
uint8_t x = (random_object<uint32_t>() % 0xFF) + 1;
uint8_t y = (random_object<uint32_t>() % 0xFF) + 1;
// name_color = ABCDEFGHabcdefghIJKLMNOPijklmnop
// name_color_checksum = ---------ijklmabcdeIJKLM-------- ^ (xxxxxxxxyyyyyyyyxxxxxxxxyyyyyyyy)
uint32_t xbrgx95558 = ((name_color << 15) & 0x007C0000) | ((name_color >> 6) & 0x0003E000) | ((name_color >> 3) & 0x00001F00);
uint32_t mask = (x << 24) | (y << 16) | (x << 8) | y;
return xbrgx95558 ^ mask;
}
void PlayerVisualConfig::compute_name_color_checksum() {
this->name_color_checksum = this->compute_name_color_checksum(this->name_color);
}
void PlayerDispDataDCPCV3::enforce_lobby_join_limits(GameVersion target_version) {
struct ClassMaxes {
uint16_t costume;
uint16_t skin;
uint16_t face;
uint16_t head;
uint16_t hair;
uint16_t hair_r;
uint16_t hair_g;
uint16_t hair_b;
};
static constexpr ClassMaxes v1_v2_class_maxes[14] = {
{0x0009, 0x0004, 0x0005, 0x0000, 0x0007, 0x0100, 0x0100, 0x0100},
{0x0009, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0000, 0x0009, 0x0000, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0009, 0x0004, 0x0005, 0x0000, 0x0007, 0x0100, 0x0100, 0x0100},
{0x0000, 0x0009, 0x0000, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0009, 0x0000, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0009, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0009, 0x0004, 0x0005, 0x0000, 0x0007, 0x0100, 0x0100, 0x0100},
{0x0009, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
};
static constexpr ClassMaxes v3_v4_class_maxes[19] = {
{0x0012, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0012, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0000, 0x0019, 0x0000, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0012, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0000, 0x0019, 0x0000, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0019, 0x0000, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0012, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0012, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0012, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0000, 0x0019, 0x0000, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0012, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0012, 0x0004, 0x0005, 0x0000, 0x000A, 0x0100, 0x0100, 0x0100},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0100, 0x0100, 0x0100},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0100, 0x0100, 0x0100},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}};
const ClassMaxes* maxes;
if ((target_version == GameVersion::PC) || (target_version == GameVersion::DC)) {
// V1/V2 have fewer classes, so we'll substitute some here
if (this->visual.char_class == 9) {
this->visual.char_class = 5; // HUcaseal -> RAcaseal
} else if (this->visual.char_class == 10) {
this->visual.char_class = 0; // FOmar -> HUmar
} else if (this->visual.char_class == 11) {
this->visual.char_class = 1; // RAmarl -> HUnewearl
switch (this->visual.char_class) {
case 0: // HUmar
case 1: // HUnewearl
case 2: // HUcast
case 3: // RAmar
case 4: // RAcast
case 5: // RAcaseal
case 6: // FOmarl
case 7: // FOnewm
case 8: // FOnewearl
case 12: // V3 custom 1
case 13: // V3 custom 2
break;
case 9: // HUcaseal
this->visual.char_class = 5; // HUcaseal -> RAcaseal
break;
case 10: // FOmar
this->visual.char_class = 0; // FOmar -> HUmar
break;
case 11: // RAmarl
this->visual.char_class = 1; // RAmarl -> HUnewearl
break;
case 14: // V2 custom 1 / V3 custom 3
case 15: // V2 custom 2 / V3 custom 4
case 16: // V2 custom 3 / V3 custom 5
case 17: // V2 custom 4 / V3 custom 6
case 18: // V2 custom 5 / V3 custom 7
this->visual.char_class -= 5;
break;
default:
this->visual.char_class = 0; // Invalid classes -> HUmar
}
// V1/V2 has fewer costumes and android skins, so substitute them here too
this->visual.costume %= 9;
this->visual.skin %= 9;
// If the player is somehow still not a valid class, make them appear as the
// "ninja" NPC
if (this->visual.char_class > 8) {
this->visual.extra_model = 0;
this->visual.v2_flags |= 2;
}
maxes = &v1_v2_class_maxes[this->visual.char_class];
this->visual.version = 2;
} else {
if (this->visual.char_class >= 19) {
this->visual.char_class = 0; // Invalid classes -> HUmar
}
maxes = &v3_v4_class_maxes[this->visual.char_class];
}
// V1/V2 has fewer costumes and android skins, so substitute them here
this->visual.costume %= maxes->costume;
this->visual.skin %= maxes->skin;
this->visual.face %= maxes->face;
this->visual.head %= maxes->head;
this->visual.hair %= maxes->hair;
this->visual.hair_r %= maxes->hair_r;
this->visual.hair_g %= maxes->hair_g;
this->visual.hair_b %= maxes->hair_b;
this->visual.compute_name_color_checksum();
this->visual.class_flags = class_flags_for_class(this->visual.char_class);
}
void PlayerDispDataBB::enforce_lobby_join_limits(GameVersion) {
@@ -90,10 +188,10 @@ void PlayerDispDataBB::apply_preview(const PlayerDispDataBBPreview& pre) {
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.unknown_a3 = pre.visual.unknown_a3;
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.v2_flags = pre.visual.v2_flags;
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;
+14 -3
View File
@@ -104,13 +104,21 @@ struct PlayerDispDataBB;
struct PlayerVisualConfig {
/* 00 */ ptext<char, 0x10> name;
/* 10 */ parray<uint8_t, 8> unknown_a2;
/* 18 */ le_uint32_t name_color = 0x00000000; // RGBA
/* 18 */ le_uint32_t name_color = 0x00000000; // ARGB
/* 1C */ uint8_t extra_model = 0;
/* 1D */ parray<uint8_t, 0x0F> unused;
/* 2C */ le_uint32_t unknown_a3 = 0;
// See compute_name_color_checksum for details on how this is computed. This
// field is ignored on V3 and BB.
/* 2C */ le_uint32_t name_color_checksum = 0;
/* 30 */ uint8_t section_id = 0;
/* 31 */ uint8_t char_class = 0;
/* 32 */ uint8_t v2_flags = 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;
// class_flags specifies features of the character's class. The bits are:
// -------- -------- -------- FRHANMfm
@@ -129,6 +137,9 @@ struct PlayerVisualConfig {
/* 48 */ le_float proportion_x = 0.0;
/* 4C */ le_float proportion_y = 0.0;
/* 50 */
static uint32_t compute_name_color_checksum(uint32_t name_color);
void compute_name_color_checksum();
} __attribute__((packed));
struct PlayerDispDataDCPCV3 {
+58 -58
View File
@@ -1398,76 +1398,76 @@ std::string disassemble_quest_script(const void* data, size_t size, QuestScriptV
print_as_struct.template operator()<Arg::DataType::PLAYER_VISUAL_CONFIG, PlayerVisualConfig>([&](const PlayerVisualConfig& visual) -> void {
lines.emplace_back(" // As PlayerVisualConfig");
string name = format_data_string(visual.name);
lines.emplace_back(string_printf(" %04zX name %s", l->offset + offsetof(PlayerVisualConfig, name), name.c_str()));
lines.emplace_back(string_printf(" %04zX name_color %08" PRIX32, l->offset + offsetof(PlayerVisualConfig, name_color), visual.name_color.load()));
lines.emplace_back(string_printf(" %04zX name %s", l->offset + offsetof(PlayerVisualConfig, name), name.c_str()));
lines.emplace_back(string_printf(" %04zX name_color %08" PRIX32, l->offset + offsetof(PlayerVisualConfig, name_color), visual.name_color.load()));
string a2_str = format_data_string(visual.unknown_a2.data(), sizeof(visual.unknown_a2));
lines.emplace_back(string_printf(" %04zX a2 %s", l->offset + offsetof(PlayerVisualConfig, unknown_a2), a2_str.c_str()));
lines.emplace_back(string_printf(" %04zX extra_model %02hhX", l->offset + offsetof(PlayerVisualConfig, extra_model), visual.extra_model));
lines.emplace_back(string_printf(" %04zX a2 %s", l->offset + offsetof(PlayerVisualConfig, unknown_a2), a2_str.c_str()));
lines.emplace_back(string_printf(" %04zX extra_model %02hhX", l->offset + offsetof(PlayerVisualConfig, extra_model), visual.extra_model));
string unused = format_data_string(visual.unused.data(), visual.unused.bytes());
lines.emplace_back(string_printf(" %04zX unused %s", l->offset + offsetof(PlayerVisualConfig, unused), unused.c_str()));
lines.emplace_back(string_printf(" %04zX a3 %08" PRIX32, l->offset + offsetof(PlayerVisualConfig, unknown_a3), visual.unknown_a3.load()));
lines.emplace_back(string_printf(" %04zX unused %s", l->offset + offsetof(PlayerVisualConfig, unused), unused.c_str()));
lines.emplace_back(string_printf(" %04zX name_color_checksum %08" PRIX32, l->offset + offsetof(PlayerVisualConfig, name_color_checksum), visual.name_color_checksum.load()));
string secid_name = name_for_section_id(visual.section_id);
lines.emplace_back(string_printf(" %04zX section_id %02hhX (%s)", l->offset + offsetof(PlayerVisualConfig, section_id), visual.section_id, secid_name.c_str()));
lines.emplace_back(string_printf(" %04zX char_class %02hhX (%s)", l->offset + offsetof(PlayerVisualConfig, char_class), visual.char_class, name_for_char_class(visual.char_class)));
lines.emplace_back(string_printf(" %04zX v2_flags %02hhX", l->offset + offsetof(PlayerVisualConfig, v2_flags), visual.v2_flags));
lines.emplace_back(string_printf(" %04zX version %02hhX", l->offset + offsetof(PlayerVisualConfig, version), visual.version));
lines.emplace_back(string_printf(" %04zX class_flags %08" PRIX32, l->offset + offsetof(PlayerVisualConfig, class_flags), visual.class_flags.load()));
lines.emplace_back(string_printf(" %04zX costume %04hX", l->offset + offsetof(PlayerVisualConfig, costume), visual.costume.load()));
lines.emplace_back(string_printf(" %04zX skin %04hX", l->offset + offsetof(PlayerVisualConfig, skin), visual.skin.load()));
lines.emplace_back(string_printf(" %04zX face %04hX", l->offset + offsetof(PlayerVisualConfig, face), visual.face.load()));
lines.emplace_back(string_printf(" %04zX head %04hX", l->offset + offsetof(PlayerVisualConfig, head), visual.head.load()));
lines.emplace_back(string_printf(" %04zX hair %04hX", l->offset + offsetof(PlayerVisualConfig, hair), visual.hair.load()));
lines.emplace_back(string_printf(" %04zX hair_color %04hX, %04hX, %04hX", l->offset + offsetof(PlayerVisualConfig, hair_r), visual.hair_r.load(), visual.hair_g.load(), visual.hair_b.load()));
lines.emplace_back(string_printf(" %04zX proportion %g, %g", l->offset + offsetof(PlayerVisualConfig, proportion_x), visual.proportion_x.load(), visual.proportion_y.load()));
lines.emplace_back(string_printf(" %04zX section_id %02hhX (%s)", l->offset + offsetof(PlayerVisualConfig, section_id), visual.section_id, secid_name.c_str()));
lines.emplace_back(string_printf(" %04zX char_class %02hhX (%s)", l->offset + offsetof(PlayerVisualConfig, char_class), visual.char_class, name_for_char_class(visual.char_class)));
lines.emplace_back(string_printf(" %04zX validation_flags %02hhX", l->offset + offsetof(PlayerVisualConfig, validation_flags), visual.validation_flags));
lines.emplace_back(string_printf(" %04zX version %02hhX", l->offset + offsetof(PlayerVisualConfig, version), visual.version));
lines.emplace_back(string_printf(" %04zX class_flags %08" PRIX32, l->offset + offsetof(PlayerVisualConfig, class_flags), visual.class_flags.load()));
lines.emplace_back(string_printf(" %04zX costume %04hX", l->offset + offsetof(PlayerVisualConfig, costume), visual.costume.load()));
lines.emplace_back(string_printf(" %04zX skin %04hX", l->offset + offsetof(PlayerVisualConfig, skin), visual.skin.load()));
lines.emplace_back(string_printf(" %04zX face %04hX", l->offset + offsetof(PlayerVisualConfig, face), visual.face.load()));
lines.emplace_back(string_printf(" %04zX head %04hX", l->offset + offsetof(PlayerVisualConfig, head), visual.head.load()));
lines.emplace_back(string_printf(" %04zX hair %04hX", l->offset + offsetof(PlayerVisualConfig, hair), visual.hair.load()));
lines.emplace_back(string_printf(" %04zX hair_color %04hX, %04hX, %04hX", l->offset + offsetof(PlayerVisualConfig, hair_r), visual.hair_r.load(), visual.hair_g.load(), visual.hair_b.load()));
lines.emplace_back(string_printf(" %04zX proportion %g, %g", l->offset + offsetof(PlayerVisualConfig, proportion_x), visual.proportion_x.load(), visual.proportion_y.load()));
});
print_as_struct.template operator()<Arg::DataType::PLAYER_STATS, PlayerStats>([&](const PlayerStats& stats) -> void {
lines.emplace_back(" // As PlayerStats");
lines.emplace_back(string_printf(" %04zX atp %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.atp), stats.char_stats.atp.load(), stats.char_stats.atp.load()));
lines.emplace_back(string_printf(" %04zX mst %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.mst), stats.char_stats.mst.load(), stats.char_stats.mst.load()));
lines.emplace_back(string_printf(" %04zX evp %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.evp), stats.char_stats.evp.load(), stats.char_stats.evp.load()));
lines.emplace_back(string_printf(" %04zX hp %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.hp), stats.char_stats.hp.load(), stats.char_stats.hp.load()));
lines.emplace_back(string_printf(" %04zX dfp %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.dfp), stats.char_stats.dfp.load(), stats.char_stats.dfp.load()));
lines.emplace_back(string_printf(" %04zX ata %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.ata), stats.char_stats.ata.load(), stats.char_stats.ata.load()));
lines.emplace_back(string_printf(" %04zX lck %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.lck), stats.char_stats.lck.load(), stats.char_stats.lck.load()));
lines.emplace_back(string_printf(" %04zX a1 %04hX /* %hu */", l->offset + offsetof(PlayerStats, unknown_a1), stats.unknown_a1.load(), stats.unknown_a1.load()));
lines.emplace_back(string_printf(" %04zX a2 %08" PRIX32 " /* %g */", l->offset + offsetof(PlayerStats, unknown_a2), stats.unknown_a2.load_raw(), stats.unknown_a2.load()));
lines.emplace_back(string_printf(" %04zX a3 %08" PRIX32 " /* %g */", l->offset + offsetof(PlayerStats, unknown_a3), stats.unknown_a3.load_raw(), stats.unknown_a3.load()));
lines.emplace_back(string_printf(" %04zX level %08" PRIX32 " /* level %" PRIu32 " */", l->offset + offsetof(PlayerStats, level), stats.level.load(), stats.level.load() + 1));
lines.emplace_back(string_printf(" %04zX experience %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(PlayerStats, experience), stats.experience.load(), stats.experience.load()));
lines.emplace_back(string_printf(" %04zX meseta %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(PlayerStats, meseta), stats.meseta.load(), stats.meseta.load()));
lines.emplace_back(string_printf(" %04zX atp %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.atp), stats.char_stats.atp.load(), stats.char_stats.atp.load()));
lines.emplace_back(string_printf(" %04zX mst %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.mst), stats.char_stats.mst.load(), stats.char_stats.mst.load()));
lines.emplace_back(string_printf(" %04zX evp %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.evp), stats.char_stats.evp.load(), stats.char_stats.evp.load()));
lines.emplace_back(string_printf(" %04zX hp %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.hp), stats.char_stats.hp.load(), stats.char_stats.hp.load()));
lines.emplace_back(string_printf(" %04zX dfp %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.dfp), stats.char_stats.dfp.load(), stats.char_stats.dfp.load()));
lines.emplace_back(string_printf(" %04zX ata %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.ata), stats.char_stats.ata.load(), stats.char_stats.ata.load()));
lines.emplace_back(string_printf(" %04zX lck %04hX /* %hu */", l->offset + offsetof(PlayerStats, char_stats.lck), stats.char_stats.lck.load(), stats.char_stats.lck.load()));
lines.emplace_back(string_printf(" %04zX a1 %04hX /* %hu */", l->offset + offsetof(PlayerStats, unknown_a1), stats.unknown_a1.load(), stats.unknown_a1.load()));
lines.emplace_back(string_printf(" %04zX a2 %08" PRIX32 " /* %g */", l->offset + offsetof(PlayerStats, unknown_a2), stats.unknown_a2.load_raw(), stats.unknown_a2.load()));
lines.emplace_back(string_printf(" %04zX a3 %08" PRIX32 " /* %g */", l->offset + offsetof(PlayerStats, unknown_a3), stats.unknown_a3.load_raw(), stats.unknown_a3.load()));
lines.emplace_back(string_printf(" %04zX level %08" PRIX32 " /* level %" PRIu32 " */", l->offset + offsetof(PlayerStats, level), stats.level.load(), stats.level.load() + 1));
lines.emplace_back(string_printf(" %04zX experience %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(PlayerStats, experience), stats.experience.load(), stats.experience.load()));
lines.emplace_back(string_printf(" %04zX meseta %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(PlayerStats, meseta), stats.meseta.load(), stats.meseta.load()));
});
print_as_struct.template operator()<Arg::DataType::RESIST_DATA, ResistData>([&](const ResistData& resist) -> void {
lines.emplace_back(" // As ResistData");
lines.emplace_back(string_printf(" %04zX evp_bonus %04hX /* %hu */", l->offset + offsetof(ResistData, evp_bonus), resist.evp_bonus.load(), resist.evp_bonus.load()));
lines.emplace_back(string_printf(" %04zX a1 %04hX /* %hu */", l->offset + offsetof(ResistData, unknown_a1), resist.unknown_a1.load(), resist.unknown_a1.load()));
lines.emplace_back(string_printf(" %04zX a2 %04hX /* %hu */", l->offset + offsetof(ResistData, unknown_a2), resist.unknown_a2.load(), resist.unknown_a2.load()));
lines.emplace_back(string_printf(" %04zX a3 %04hX /* %hu */", l->offset + offsetof(ResistData, unknown_a3), resist.unknown_a3.load(), resist.unknown_a3.load()));
lines.emplace_back(string_printf(" %04zX a4 %04hX /* %hu */", l->offset + offsetof(ResistData, unknown_a4), resist.unknown_a4.load(), resist.unknown_a4.load()));
lines.emplace_back(string_printf(" %04zX a5 %04hX /* %hu */", l->offset + offsetof(ResistData, unknown_a5), resist.unknown_a5.load(), resist.unknown_a5.load()));
lines.emplace_back(string_printf(" %04zX a6 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(ResistData, unknown_a6), resist.unknown_a6.load(), resist.unknown_a6.load()));
lines.emplace_back(string_printf(" %04zX a7 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(ResistData, unknown_a7), resist.unknown_a7.load(), resist.unknown_a7.load()));
lines.emplace_back(string_printf(" %04zX a8 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(ResistData, unknown_a8), resist.unknown_a8.load(), resist.unknown_a8.load()));
lines.emplace_back(string_printf(" %04zX a9 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(ResistData, unknown_a9), resist.unknown_a9.load(), resist.unknown_a9.load()));
lines.emplace_back(string_printf(" %04zX dfp_bonus %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(ResistData, dfp_bonus), resist.dfp_bonus.load(), resist.dfp_bonus.load()));
lines.emplace_back(string_printf(" %04zX evp_bonus %04hX /* %hu */", l->offset + offsetof(ResistData, evp_bonus), resist.evp_bonus.load(), resist.evp_bonus.load()));
lines.emplace_back(string_printf(" %04zX a1 %04hX /* %hu */", l->offset + offsetof(ResistData, unknown_a1), resist.unknown_a1.load(), resist.unknown_a1.load()));
lines.emplace_back(string_printf(" %04zX a2 %04hX /* %hu */", l->offset + offsetof(ResistData, unknown_a2), resist.unknown_a2.load(), resist.unknown_a2.load()));
lines.emplace_back(string_printf(" %04zX a3 %04hX /* %hu */", l->offset + offsetof(ResistData, unknown_a3), resist.unknown_a3.load(), resist.unknown_a3.load()));
lines.emplace_back(string_printf(" %04zX a4 %04hX /* %hu */", l->offset + offsetof(ResistData, unknown_a4), resist.unknown_a4.load(), resist.unknown_a4.load()));
lines.emplace_back(string_printf(" %04zX a5 %04hX /* %hu */", l->offset + offsetof(ResistData, unknown_a5), resist.unknown_a5.load(), resist.unknown_a5.load()));
lines.emplace_back(string_printf(" %04zX a6 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(ResistData, unknown_a6), resist.unknown_a6.load(), resist.unknown_a6.load()));
lines.emplace_back(string_printf(" %04zX a7 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(ResistData, unknown_a7), resist.unknown_a7.load(), resist.unknown_a7.load()));
lines.emplace_back(string_printf(" %04zX a8 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(ResistData, unknown_a8), resist.unknown_a8.load(), resist.unknown_a8.load()));
lines.emplace_back(string_printf(" %04zX a9 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(ResistData, unknown_a9), resist.unknown_a9.load(), resist.unknown_a9.load()));
lines.emplace_back(string_printf(" %04zX dfp_bonus %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(ResistData, dfp_bonus), resist.dfp_bonus.load(), resist.dfp_bonus.load()));
});
print_as_struct.template operator()<Arg::DataType::ATTACK_DATA, AttackData>([&](const AttackData& attack) -> void {
lines.emplace_back(" // As AttackData");
lines.emplace_back(string_printf(" %04zX a1 %04hX /* %hd */", l->offset + offsetof(AttackData, unknown_a1), attack.unknown_a1.load(), attack.unknown_a1.load()));
lines.emplace_back(string_printf(" %04zX a2 %04hX /* %hd */", l->offset + offsetof(AttackData, unknown_a2), attack.unknown_a2.load(), attack.unknown_a2.load()));
lines.emplace_back(string_printf(" %04zX a3 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a3), attack.unknown_a3.load(), attack.unknown_a3.load()));
lines.emplace_back(string_printf(" %04zX a4 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a4), attack.unknown_a4.load(), attack.unknown_a4.load()));
lines.emplace_back(string_printf(" %04zX a5 %08" PRIX32 " /* %g */", l->offset + offsetof(AttackData, unknown_a5), attack.unknown_a5.load_raw(), attack.unknown_a5.load()));
lines.emplace_back(string_printf(" %04zX a6 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a6), attack.unknown_a6.load(), attack.unknown_a6.load()));
lines.emplace_back(string_printf(" %04zX a7 %08" PRIX32 " /* %g */", l->offset + offsetof(AttackData, unknown_a7), attack.unknown_a7.load_raw(), attack.unknown_a7.load()));
lines.emplace_back(string_printf(" %04zX a8 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a8), attack.unknown_a8.load(), attack.unknown_a8.load()));
lines.emplace_back(string_printf(" %04zX a9 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a9), attack.unknown_a9.load(), attack.unknown_a9.load()));
lines.emplace_back(string_printf(" %04zX a10 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a10), attack.unknown_a10.load(), attack.unknown_a10.load()));
lines.emplace_back(string_printf(" %04zX a11 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a11), attack.unknown_a11.load(), attack.unknown_a11.load()));
lines.emplace_back(string_printf(" %04zX a12 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a12), attack.unknown_a12.load(), attack.unknown_a12.load()));
lines.emplace_back(string_printf(" %04zX a13 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a13), attack.unknown_a13.load(), attack.unknown_a13.load()));
lines.emplace_back(string_printf(" %04zX a14 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a14), attack.unknown_a14.load(), attack.unknown_a14.load()));
lines.emplace_back(string_printf(" %04zX a15 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a15), attack.unknown_a15.load(), attack.unknown_a15.load()));
lines.emplace_back(string_printf(" %04zX a16 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a16), attack.unknown_a16.load(), attack.unknown_a16.load()));
lines.emplace_back(string_printf(" %04zX a1 %04hX /* %hd */", l->offset + offsetof(AttackData, unknown_a1), attack.unknown_a1.load(), attack.unknown_a1.load()));
lines.emplace_back(string_printf(" %04zX a2 %04hX /* %hd */", l->offset + offsetof(AttackData, unknown_a2), attack.unknown_a2.load(), attack.unknown_a2.load()));
lines.emplace_back(string_printf(" %04zX a3 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a3), attack.unknown_a3.load(), attack.unknown_a3.load()));
lines.emplace_back(string_printf(" %04zX a4 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a4), attack.unknown_a4.load(), attack.unknown_a4.load()));
lines.emplace_back(string_printf(" %04zX a5 %08" PRIX32 " /* %g */", l->offset + offsetof(AttackData, unknown_a5), attack.unknown_a5.load_raw(), attack.unknown_a5.load()));
lines.emplace_back(string_printf(" %04zX a6 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a6), attack.unknown_a6.load(), attack.unknown_a6.load()));
lines.emplace_back(string_printf(" %04zX a7 %08" PRIX32 " /* %g */", l->offset + offsetof(AttackData, unknown_a7), attack.unknown_a7.load_raw(), attack.unknown_a7.load()));
lines.emplace_back(string_printf(" %04zX a8 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a8), attack.unknown_a8.load(), attack.unknown_a8.load()));
lines.emplace_back(string_printf(" %04zX a9 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a9), attack.unknown_a9.load(), attack.unknown_a9.load()));
lines.emplace_back(string_printf(" %04zX a10 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a10), attack.unknown_a10.load(), attack.unknown_a10.load()));
lines.emplace_back(string_printf(" %04zX a11 %04hX /* %hu */", l->offset + offsetof(AttackData, unknown_a11), attack.unknown_a11.load(), attack.unknown_a11.load()));
lines.emplace_back(string_printf(" %04zX a12 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a12), attack.unknown_a12.load(), attack.unknown_a12.load()));
lines.emplace_back(string_printf(" %04zX a13 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a13), attack.unknown_a13.load(), attack.unknown_a13.load()));
lines.emplace_back(string_printf(" %04zX a14 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a14), attack.unknown_a14.load(), attack.unknown_a14.load()));
lines.emplace_back(string_printf(" %04zX a15 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a15), attack.unknown_a15.load(), attack.unknown_a15.load()));
lines.emplace_back(string_printf(" %04zX a16 %08" PRIX32 " /* %" PRIu32 " */", l->offset + offsetof(AttackData, unknown_a16), attack.unknown_a16.load(), attack.unknown_a16.load()));
});
print_as_struct.template operator()<Arg::DataType::MOVEMENT_DATA, MovementData>([&](const MovementData& movement) -> void {
lines.emplace_back(" // As MovementData");
+50
View File
@@ -281,12 +281,62 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
mask_data, mask_size, sizeof(S_JoinGame_GC_Ep3_64));
mask.variations.clear(0);
mask.rare_seed = 0;
for (size_t offset = sizeof(S_JoinGame_GC_64) +
offsetof(S_JoinGame_GC_Ep3_64::Ep3PlayerEntry, disp.visual.name_color_checksum);
offset + 4 <= mask_size;
offset += sizeof(S_JoinGame_GC_Ep3_64::Ep3PlayerEntry)) {
*reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(mask_data) + offset) = 0;
}
}
break;
case 0xEB:
if (version != GameVersion::GC) {
break;
}
[[fallthrough]];
case 0x65:
case 0x67:
case 0x68:
if ((version == GameVersion::DC) || (version == GameVersion::GC)) {
for (size_t offset = offsetof(S_JoinLobby_DC_GC_65_67_68_Ep3_EB, entries) +
offsetof(S_JoinLobby_DC_GC_65_67_68_Ep3_EB::Entry, disp.visual.name_color_checksum);
offset + 4 <= mask_size;
offset += sizeof(S_JoinLobby_DC_GC_65_67_68_Ep3_EB::Entry)) {
*reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(mask_data) + offset) = 0;
}
} else if (version == GameVersion::XB) {
for (size_t offset = offsetof(S_JoinLobby_XB_65_67_68, entries) +
offsetof(S_JoinLobby_XB_65_67_68::Entry, disp.visual.name_color_checksum);
offset + 4 <= mask_size;
offset += sizeof(S_JoinLobby_XB_65_67_68::Entry)) {
*reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(mask_data) + offset) = 0;
}
} else if (version == GameVersion::PC) {
for (size_t offset = offsetof(S_JoinLobby_PC_65_67_68, entries) +
offsetof(S_JoinLobby_PC_65_67_68::Entry, disp.visual.name_color_checksum);
offset + 4 <= mask_size;
offset += sizeof(S_JoinLobby_PC_65_67_68::Entry)) {
*reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(mask_data) + offset) = 0;
}
} else if (version == GameVersion::BB) {
for (size_t offset = offsetof(S_JoinLobby_BB_65_67_68, entries) +
offsetof(S_JoinLobby_BB_65_67_68::Entry, disp.visual.name_color_checksum);
offset + 4 <= mask_size;
offset += sizeof(S_JoinLobby_BB_65_67_68::Entry)) {
*reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(mask_data) + offset) = 0;
}
}
break;
case 0xE8:
if (version == GameVersion::GC) {
auto& mask = check_size_t<S_JoinSpectatorTeam_GC_Ep3_E8>(mask_data, mask_size);
mask.rare_seed = 0;
for (size_t z = 0; z < 4; z++) {
mask.players[z].disp.visual.name_color_checksum = 0;
}
for (size_t z = 0; z < 8; z++) {
mask.spectator_players[z].disp.visual.name_color_checksum = 0;
}
}
break;
case 0xB1:
+5
View File
@@ -618,6 +618,7 @@ void send_complete_player_bb(shared_ptr<Client> c) {
SC_SyncCharacterSaveFile_BB_00E7 cmd;
cmd.inventory = player->inventory;
cmd.disp = player->disp;
cmd.disp.visual.compute_name_color_checksum();
cmd.disp.play_time = 0;
cmd.unknown_a1 = 0;
cmd.creation_timestamp = 0;
@@ -1451,6 +1452,7 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
p.inventory.encode_mags(c->version());
p.disp = wc_p->disp.to_dcpcv3();
remove_language_marker_inplace(p.disp.visual.name);
p.disp.enforce_lobby_join_limits(c->version());
auto& e = cmd.entries[z];
e.player_tag = 0x00010000;
@@ -1491,6 +1493,7 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
p.inventory.encode_mags(c->version());
p.disp = entry.disp;
remove_language_marker_inplace(p.disp.visual.name);
p.disp.enforce_lobby_join_limits(c->version());
auto& e = cmd.entries[client_id];
e.player_tag = 0x00010000;
@@ -1522,6 +1525,7 @@ static void send_join_spectator_team(shared_ptr<Client> c, shared_ptr<Lobby> l)
cmd_p.inventory = other_p->inventory;
cmd_p.disp = other_p->disp.to_dcpcv3();
remove_language_marker_inplace(cmd_p.disp.visual.name);
cmd_p.disp.enforce_lobby_join_limits(c->version());
cmd_e.player_tag = 0x00010000;
cmd_e.guild_card_number = other_c->license->serial_number;
@@ -1628,6 +1632,7 @@ void send_join_game(shared_ptr<Client> c, shared_ptr<Lobby> l) {
cmd.players_ep3[x].inventory = other_p->inventory;
cmd.players_ep3[x].inventory.encode_mags(c->version());
cmd.players_ep3[x].disp = convert_player_disp_data<PlayerDispDataDCPCV3>(other_p->disp);
cmd.players_ep3[x].disp.enforce_lobby_join_limits(c->version());
}
}
send_command_t(c, 0x64, player_count, cmd);
+9
View File
@@ -793,3 +793,12 @@ const char* name_for_area(Episode episode, uint8_t area) {
throw logic_error("invalid episode for drop area");
}
}
uint32_t class_flags_for_class(uint8_t char_class) {
static constexpr uint8_t flags[12] = {
0x25, 0x2A, 0x31, 0x45, 0x51, 0x52, 0x86, 0x89, 0x8A, 0x32, 0x85, 0x46};
if (char_class >= 12) {
throw runtime_error("invalid character class");
}
return flags[char_class];
}
+2
View File
@@ -84,3 +84,5 @@ extern const std::unordered_map<std::string, uint8_t> mag_color_for_name;
size_t area_limit_for_episode(Episode ep);
uint8_t area_for_name(const std::string& name);
const char* name_for_area(Episode episode, uint8_t area);
uint32_t class_flags_for_class(uint8_t char_class);