From ef5532cf15a5b7659f9c56fc0a99d18bf229f90c Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Mar 2026 11:23:37 -0700 Subject: [PATCH] fix: add TBC chat message parser to prevent 12-byte misalignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TBC 2.4.3 SMSG_MESSAGECHAT has no senderGuid(u64) or unknown(u32) prefix before type-specific data. The WotLK base parser reads these 12 bytes unconditionally, causing complete misalignment of all chat message fields — every chat message on a TBC server would parse garbage for sender, channel, and message content. --- include/game/packet_parsers.hpp | 2 + src/game/packet_parsers_tbc.cpp | 100 ++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/include/game/packet_parsers.hpp b/include/game/packet_parsers.hpp index d2a8203a..b229dc80 100644 --- a/include/game/packet_parsers.hpp +++ b/include/game/packet_parsers.hpp @@ -370,6 +370,8 @@ public: bool parseGuildRoster(network::Packet& packet, GuildRosterData& data) override; // TBC 2.4.3 SMSG_QUESTGIVER_STATUS: uint32 status (WotLK uses uint8) uint8_t readQuestGiverStatus(network::Packet& packet) override; + // TBC 2.4.3 SMSG_MESSAGECHAT: no senderGuid/unknown prefix before type-specific data + bool parseMessageChat(network::Packet& packet, MessageChatData& data) override; // TBC 2.4.3 SMSG_GAMEOBJECT_QUERY_RESPONSE: 2 extra strings after names // (iconName + castBarCaption); WotLK has 3 (adds unk1) bool parseGameObjectQueryResponse(network::Packet& packet, GameObjectQueryResponseData& data) override; diff --git a/src/game/packet_parsers_tbc.cpp b/src/game/packet_parsers_tbc.cpp index 4dac23ec..792484fb 100644 --- a/src/game/packet_parsers_tbc.cpp +++ b/src/game/packet_parsers_tbc.cpp @@ -1537,6 +1537,106 @@ bool TbcPacketParsers::parseSpellHealLog(network::Packet& packet, SpellHealLogDa return true; } +// ============================================================================ +// TBC 2.4.3 SMSG_MESSAGECHAT +// TBC format: type(u8) + language(u32) + [type-specific data] + msgLen(u32) + msg + tag(u8) +// WotLK adds senderGuid(u64) + unknown(u32) before type-specific data. +// ============================================================================ + +bool TbcPacketParsers::parseMessageChat(network::Packet& packet, MessageChatData& data) { + if (packet.getSize() < 10) { + LOG_ERROR("[TBC] SMSG_MESSAGECHAT packet too small: ", packet.getSize(), " bytes"); + return false; + } + + uint8_t typeVal = packet.readUInt8(); + data.type = static_cast(typeVal); + + uint32_t langVal = packet.readUInt32(); + data.language = static_cast(langVal); + + // TBC: NO senderGuid or unknown field here (WotLK has senderGuid(u64) + unk(u32)) + + switch (data.type) { + case ChatType::MONSTER_SAY: + case ChatType::MONSTER_YELL: + case ChatType::MONSTER_EMOTE: + case ChatType::MONSTER_WHISPER: + case ChatType::MONSTER_PARTY: + case ChatType::RAID_BOSS_EMOTE: { + // senderGuid(u64) + nameLen(u32) + name + targetGuid(u64) + data.senderGuid = packet.readUInt64(); + uint32_t nameLen = packet.readUInt32(); + if (nameLen > 0 && nameLen < 256) { + data.senderName.resize(nameLen); + for (uint32_t i = 0; i < nameLen; ++i) { + data.senderName[i] = static_cast(packet.readUInt8()); + } + if (!data.senderName.empty() && data.senderName.back() == '\0') { + data.senderName.pop_back(); + } + } + data.receiverGuid = packet.readUInt64(); + break; + } + + case ChatType::SAY: + case ChatType::PARTY: + case ChatType::YELL: + case ChatType::WHISPER: + case ChatType::WHISPER_INFORM: + case ChatType::GUILD: + case ChatType::OFFICER: + case ChatType::RAID: + case ChatType::RAID_LEADER: + case ChatType::RAID_WARNING: + case ChatType::EMOTE: + case ChatType::TEXT_EMOTE: { + // senderGuid(u64) + senderGuid(u64) — written twice by server + data.senderGuid = packet.readUInt64(); + /*duplicateGuid*/ packet.readUInt64(); + break; + } + + case ChatType::CHANNEL: { + // channelName(string) + rank(u32) + senderGuid(u64) + data.channelName = packet.readString(); + /*uint32_t rank =*/ packet.readUInt32(); + data.senderGuid = packet.readUInt64(); + break; + } + + default: { + // All other types: senderGuid(u64) + senderGuid(u64) — written twice + data.senderGuid = packet.readUInt64(); + /*duplicateGuid*/ packet.readUInt64(); + break; + } + } + + // Read message length + message + uint32_t messageLen = packet.readUInt32(); + if (messageLen > 0 && messageLen < 8192) { + data.message.resize(messageLen); + for (uint32_t i = 0; i < messageLen; ++i) { + data.message[i] = static_cast(packet.readUInt8()); + } + if (!data.message.empty() && data.message.back() == '\0') { + data.message.pop_back(); + } + } + + // Read chat tag + if (packet.getReadPos() < packet.getSize()) { + data.chatTag = packet.readUInt8(); + } + + LOG_DEBUG("[TBC] SMSG_MESSAGECHAT: type=", getChatTypeString(data.type), + " sender=", data.senderName.empty() ? std::to_string(data.senderGuid) : data.senderName); + + return true; +} + // ============================================================================ // TBC 2.4.3 quest giver status // TBC sends uint32 (like Classic), WotLK changed to uint8.