fix memcpy call that gcc is unhappy with

This commit is contained in:
Martin Michelsen
2025-09-11 16:17:38 -07:00
parent 55cbf6e20b
commit 526bfb64e5
4 changed files with 55 additions and 83 deletions
+11 -13
View File
@@ -4520,7 +4520,8 @@ struct G_ClearTemporaryPhotonBlastStateFlags_6x3B {
// This appears to be a base class for 6x46, 6x47, and 6x49 (and possibly other
// subcommands), but it is never sent on the wire. Its likely purpose is to
// provide the TargetEntry structure and related functions to derived classes,
// but it does still have a structure of its own, as described here.
// but it does still have a subcommand number and a structure of its own, as
// described here.
struct TargetEntry {
le_uint16_t entity_id = 0;
@@ -4591,17 +4592,16 @@ struct G_Attack_6x43_6x44_6x45 {
// targets is too large, the client will byteswap the function's return address
// on the stack, and it will crash.
struct G_AttackFinished_6x46 {
struct G_AttackFinished_Header_6x46 {
G_ClientIDHeader header;
le_uint32_t target_count = 0;
// The client may send a shorter command if not all of these are used.
parray<TargetEntry, 10> targets;
} __packed_ws__(G_AttackFinished_6x46, 0x30);
// Up to 10 TargetEntries are sent here
} __packed_ws__(G_AttackFinished_Header_6x46, 8);
// 6x47: Cast technique (protected on V3/V4)
// On GC, this command has the same bounds-check bug as 6x46.
struct G_CastTechnique_6x47 {
struct G_CastTechnique_Header_6x47 {
G_ClientIDHeader header;
uint8_t technique_number = 0;
uint8_t unused = 0; // Must not be negative
@@ -4612,9 +4612,8 @@ struct G_CastTechnique_6x47 {
// cleaned it up.
uint8_t level = 0;
uint8_t target_count = 0; // Must be in [0, 10]
// The client may send a shorter command if not all of these are used.
parray<TargetEntry, 10> targets;
} __packed_ws__(G_CastTechnique_6x47, 0x30);
// Up to 10 TargetEntries are sent here
} __packed_ws__(G_CastTechnique_Header_6x47, 8);
// 6x48: Cast technique complete (protected on V3/V4)
@@ -4629,16 +4628,15 @@ struct G_CastTechniqueComplete_6x48 {
// 6x49: Execute Photon Blast (protected on V3/V4)
// On GC, this command has the same bounds-check bug as 6x46.
struct G_ExecutePhotonBlast_6x49 {
struct G_ExecutePhotonBlast_Header_6x49 {
G_ClientIDHeader header;
uint8_t unknown_a1 = 0;
uint8_t unknown_a2 = 0;
le_uint16_t target_count = 0;
le_uint16_t unknown_a3 = 0;
le_uint16_t unknown_a4 = 0;
// The client may send a shorter command if not all of these are used.
parray<TargetEntry, 10> targets;
} __packed_ws__(G_ExecutePhotonBlast_6x49, 0x34);
// Up to 10 TargetEntries are sent here
} __packed_ws__(G_ExecutePhotonBlast_Header_6x49, 0x0C);
// 6x4A: Fully shield attack (protected on V3/V4)
+6 -9
View File
@@ -916,9 +916,8 @@ static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Messag
co_return HandlerResult::SUPPRESS;
case 0x46: {
const auto& cmd = msg.check_size_t<G_AttackFinished_6x46>(
offsetof(G_AttackFinished_6x46, targets), sizeof(G_AttackFinished_6x46));
if (cmd.target_count > min<size_t>(cmd.header.size - 2, cmd.targets.size())) {
const auto& header = msg.check_size_t<G_AttackFinished_Header_6x46>(0xFFFF);
if (header.target_count > min<size_t>(header.header.size - sizeof(G_AttackFinished_Header_6x46) / 4, 10)) {
c->log.warning_f("Blocking subcommand 6x46 with invalid count");
co_return HandlerResult::SUPPRESS;
}
@@ -926,9 +925,8 @@ static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Messag
}
case 0x47: {
const auto& cmd = msg.check_size_t<G_CastTechnique_6x47>(
offsetof(G_CastTechnique_6x47, targets), sizeof(G_CastTechnique_6x47));
if (cmd.target_count > min<size_t>(cmd.header.size - 2, cmd.targets.size())) {
const auto& header = msg.check_size_t<G_CastTechnique_Header_6x47>(0xFFFF);
if (header.target_count > min<size_t>(header.header.size - sizeof(G_CastTechnique_Header_6x47) / 4, 10)) {
c->log.warning_f("Blocking subcommand 6x47 with invalid count");
co_return HandlerResult::SUPPRESS;
}
@@ -936,9 +934,8 @@ static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Messag
}
case 0x49: {
const auto& cmd = msg.check_size_t<G_ExecutePhotonBlast_6x49>(
offsetof(G_ExecutePhotonBlast_6x49, targets), sizeof(G_ExecutePhotonBlast_6x49));
if (cmd.target_count > min<size_t>(cmd.header.size - 3, cmd.targets.size())) {
const auto& header = msg.check_size_t<G_ExecutePhotonBlast_Header_6x49>(0xFFFF);
if (header.target_count > min<size_t>(header.header.size - sizeof(G_ExecutePhotonBlast_Header_6x49) / 4, 10)) {
c->log.warning_f("Blocking subcommand 6x49 with invalid count");
co_return HandlerResult::SUPPRESS;
}
+32 -59
View File
@@ -414,18 +414,23 @@ asio::awaitable<void> forward_subcommand_with_entity_id_transcode_t(shared_ptr<C
co_return;
}
template <typename CmdT>
void forward_subcommand_with_entity_targets_transcode_t(shared_ptr<Client> c, SubcommandMessage& msg) {
template <typename HeaderT>
asio::awaitable<void> forward_subcommand_with_entity_targets_transcode_t(shared_ptr<Client> c, SubcommandMessage& msg) {
// I'm lazy and this should never happen for item commands (since all players
// need to stay in sync)
if (command_is_private(msg.command)) {
throw runtime_error("entity subcommand sent via private command");
}
const auto& cmd = msg.check_size_t<CmdT>(offsetof(CmdT, targets), sizeof(CmdT));
if (cmd.target_count > min<size_t>(cmd.header.size - offsetof(CmdT, targets) / 4, cmd.targets.size())) {
throw runtime_error("invalid attack finished command");
phosg::StringReader r(msg.data, msg.size);
const auto& header = r.get<HeaderT>();
if (header.target_count > 10) {
throw runtime_error("invalid target count");
}
if (header.target_count > std::min<size_t>(header.header.size - sizeof(HeaderT) / 4, 10)) {
throw runtime_error("invalid target list command");
}
const auto* targets = r.get_array<TargetEntry>(header.target_count);
auto l = c->require_lobby();
if (!l->is_game()) {
@@ -438,8 +443,8 @@ void forward_subcommand_with_entity_targets_transcode_t(shared_ptr<Client> c, Su
uint16_t entity_id;
};
vector<TargetResolution> resolutions;
for (size_t z = 0; z < cmd.target_count; z++) {
auto& res = resolutions.emplace_back(TargetResolution{nullptr, nullptr, cmd.targets[z].entity_id});
for (size_t z = 0; z < header.target_count; z++) {
auto& res = resolutions.emplace_back(TargetResolution{nullptr, nullptr, targets[z].entity_id});
if ((res.entity_id >= 0x1000) && (res.entity_id < 0x4000)) {
res.ene_st = l->map_state->enemy_state_for_index(c->version(), c->floor, res.entity_id - 0x1000);
} else if ((res.entity_id >= 0x4000) && (res.entity_id < 0xFFFF)) {
@@ -452,32 +457,29 @@ void forward_subcommand_with_entity_targets_transcode_t(shared_ptr<Client> c, Su
continue;
}
if (c->version() != lc->version()) {
// NOTE: We can't just do `CmdT out_cmd = cmd` here because cmd may not
// point to a full command; it is likely shorter than the full structure
CmdT out_cmd;
memcpy(&out_cmd, &cmd, msg.size);
out_cmd.header.subcommand = translate_subcommand_number(lc->version(), c->version(), cmd.header.subcommand);
if (out_cmd.header.subcommand) {
size_t valid_targets = 0;
for (size_t z = 0; z < cmd.target_count; z++) {
HeaderT out_header = header;
vector<TargetEntry> out_targets;
out_header.header.subcommand = translate_subcommand_number(lc->version(), c->version(), header.header.subcommand);
out_header.target_count = 0;
if (out_header.header.subcommand) {
for (size_t z = 0; z < header.target_count; z++) {
uint16_t entity_id;
const auto& res = resolutions[z];
auto& target = out_cmd.targets[valid_targets];
if (res.ene_st) {
target.entity_id = 0x1000 | l->map_state->index_for_enemy_state(lc->version(), res.ene_st);
entity_id = 0x1000 | l->map_state->index_for_enemy_state(lc->version(), res.ene_st);
} else if (res.obj_st) {
target.entity_id = 0x4000 | l->map_state->index_for_object_state(lc->version(), res.obj_st);
entity_id = 0x4000 | l->map_state->index_for_object_state(lc->version(), res.obj_st);
} else {
target.entity_id = res.entity_id;
entity_id = res.entity_id;
}
if (target.entity_id != 0xFFFF) {
target.unknown_a2 = cmd.targets[z].unknown_a2;
valid_targets++;
if (entity_id != 0xFFFF) {
out_targets.emplace_back(TargetEntry{entity_id, targets[z].unknown_a2});
}
}
size_t out_size = offsetof(CmdT, targets) + sizeof(TargetEntry) * valid_targets;
out_cmd.header.size = out_size >> 2;
out_cmd.target_count = valid_targets;
send_command(lc, msg.command, msg.flag, &out_cmd, out_size);
size_t out_size = sizeof(HeaderT) + sizeof(TargetEntry) * out_targets.size();
out_header.header.size = out_size >> 2;
out_header.target_count = out_targets.size();
send_command_t_vt(lc, msg.command, msg.flag, out_header, out_targets);
} else {
lc->log.info_f("Subcommand cannot be translated to client\'s version");
}
@@ -485,6 +487,7 @@ void forward_subcommand_with_entity_targets_transcode_t(shared_ptr<Client> c, Su
send_command(lc, msg.command, msg.flag, msg.data, msg.size);
}
}
co_return;
}
static shared_ptr<Client> get_sync_target(shared_ptr<Client> sender_c, uint8_t command, uint8_t flag, bool allow_if_not_loading) {
@@ -1734,36 +1737,6 @@ static asio::awaitable<void> on_cast_technique_finished(shared_ptr<Client> c, Su
co_return;
}
static asio::awaitable<void> on_attack_finished(shared_ptr<Client> c, SubcommandMessage& msg) {
const auto& cmd = msg.check_size_t<G_AttackFinished_6x46>(
offsetof(G_AttackFinished_6x46, targets), sizeof(G_AttackFinished_6x46));
if (cmd.target_count > min<size_t>(cmd.header.size - 2, cmd.targets.size())) {
throw runtime_error("invalid attack finished command");
}
forward_subcommand_with_entity_targets_transcode_t<G_AttackFinished_6x46>(c, msg);
co_return;
}
static asio::awaitable<void> on_cast_technique(shared_ptr<Client> c, SubcommandMessage& msg) {
const auto& cmd = msg.check_size_t<G_CastTechnique_6x47>(
offsetof(G_CastTechnique_6x47, targets), sizeof(G_CastTechnique_6x47));
if (cmd.target_count > min<size_t>(cmd.header.size - 2, cmd.targets.size())) {
throw runtime_error("invalid cast technique command");
}
forward_subcommand_with_entity_targets_transcode_t<G_CastTechnique_6x47>(c, msg);
co_return;
}
static asio::awaitable<void> on_execute_photon_blast(shared_ptr<Client> c, SubcommandMessage& msg) {
const auto& cmd = msg.check_size_t<G_ExecutePhotonBlast_6x49>(
offsetof(G_ExecutePhotonBlast_6x49, targets), sizeof(G_ExecutePhotonBlast_6x49));
if (cmd.target_count > min<size_t>(cmd.header.size - 3, cmd.targets.size())) {
throw runtime_error("invalid subtract PB energy command");
}
forward_subcommand_with_entity_targets_transcode_t<G_ExecutePhotonBlast_6x49>(c, msg);
co_return;
}
static asio::awaitable<void> on_npc_control(shared_ptr<Client> c, SubcommandMessage& msg) {
const auto& cmd = msg.check_size_t<G_NPCControl_6x69>();
// Don't allow NPC control commands if there is a player in the relevant slot
@@ -5272,10 +5245,10 @@ const vector<SubcommandDefinition> subcommand_definitions{
/* 6x43 */ {0x3A, 0x3F, 0x43, on_forward_check_game_client},
/* 6x44 */ {0x3B, 0x40, 0x44, on_forward_check_game_client},
/* 6x45 */ {0x3C, 0x41, 0x45, on_forward_check_game_client},
/* 6x46 */ {NONE, 0x42, 0x46, on_attack_finished},
/* 6x47 */ {0x3D, 0x43, 0x47, on_cast_technique},
/* 6x46 */ {NONE, 0x42, 0x46, forward_subcommand_with_entity_targets_transcode_t<G_AttackFinished_Header_6x46>},
/* 6x47 */ {0x3D, 0x43, 0x47, forward_subcommand_with_entity_targets_transcode_t<G_CastTechnique_Header_6x47>},
/* 6x48 */ {NONE, NONE, 0x48, on_cast_technique_finished},
/* 6x49 */ {0x3E, 0x44, 0x49, on_execute_photon_blast},
/* 6x49 */ {0x3E, 0x44, 0x49, forward_subcommand_with_entity_targets_transcode_t<G_ExecutePhotonBlast_Header_6x49>},
/* 6x4A */ {0x3F, 0x45, 0x4A, on_change_hp<G_ClientIDHeader>},
/* 6x4B */ {0x40, 0x46, 0x4B, on_change_hp<G_ClientIDHeader>},
/* 6x4C */ {0x41, 0x47, 0x4C, on_change_hp<G_ClientIDHeader>},
+6 -2
View File
@@ -118,8 +118,12 @@ void send_command_vt(std::shared_ptr<Channel> ch, uint16_t command, uint32_t fla
}
template <typename TargetT, typename StructT, typename EntryT>
void send_command_t_vt(std::shared_ptr<TargetT> c, uint16_t command,
uint32_t flag, const StructT& data, const std::vector<EntryT>& array_data) {
void send_command_t_vt(
std::shared_ptr<TargetT> c,
uint16_t command,
uint32_t flag,
const StructT& data,
const std::vector<EntryT>& array_data) {
std::string all_data(reinterpret_cast<const char*>(&data), sizeof(StructT));
all_data.append(reinterpret_cast<const char*>(array_data.data()),
array_data.size() * sizeof(EntryT));