fix cross-version lobby appearance and name colors
This commit is contained in:
+2
-2
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user