mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-14 00:23:50 +00:00
refactor: add Packet::getRemainingSize() to replace 656 arithmetic expressions
Add getRemainingSize() one-liner to Packet class and replace all 656 instances of getSize()-getReadPos() across game_handler, world_packets, and both packet parser files.
This commit is contained in:
parent
b66033c6d8
commit
376d0a0f77
5 changed files with 657 additions and 656 deletions
|
|
@ -33,7 +33,7 @@ namespace {
|
|||
++guidBytes;
|
||||
}
|
||||
}
|
||||
return packet.getSize() - packet.getReadPos() >= guidBytes;
|
||||
return packet.getRemainingSize() >= guidBytes;
|
||||
}
|
||||
|
||||
const char* updateTypeName(wowee::game::UpdateType type) {
|
||||
|
|
@ -402,7 +402,7 @@ network::Packet CharCreatePacket::build(const CharCreateData& data) {
|
|||
|
||||
bool CharCreateResponseParser::parse(network::Packet& packet, CharCreateResponseData& data) {
|
||||
// Validate minimum packet size: result(1)
|
||||
if (packet.getSize() - packet.getReadPos() < 1) {
|
||||
if (packet.getRemainingSize() < 1) {
|
||||
LOG_WARNING("SMSG_CHAR_CREATE: packet too small (", packet.getSize(), " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -423,7 +423,7 @@ network::Packet CharEnumPacket::build() {
|
|||
|
||||
bool CharEnumParser::parse(network::Packet& packet, CharEnumResponse& response) {
|
||||
// Upfront validation: count(1) + at least minimal character data
|
||||
if (packet.getSize() - packet.getReadPos() < 1) return false;
|
||||
if (packet.getRemainingSize() < 1) return false;
|
||||
|
||||
// Read character count
|
||||
uint8_t count = packet.readUInt8();
|
||||
|
|
@ -629,13 +629,13 @@ bool AccountDataTimesParser::parse(network::Packet& packet, AccountDataTimesData
|
|||
data.serverTime = packet.readUInt32();
|
||||
data.unknown = packet.readUInt8();
|
||||
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
uint32_t mask = 0xFF;
|
||||
if (remaining >= 4 && ((remaining - 4) % 4) == 0) {
|
||||
// Treat first dword as slot mask when payload shape matches.
|
||||
mask = packet.readUInt32();
|
||||
}
|
||||
remaining = packet.getSize() - packet.getReadPos();
|
||||
remaining = packet.getRemainingSize();
|
||||
size_t slotWords = std::min<size_t>(8, remaining / 4);
|
||||
|
||||
LOG_DEBUG("Parsed SMSG_ACCOUNT_DATA_TIMES:");
|
||||
|
|
@ -650,7 +650,7 @@ bool AccountDataTimesParser::parse(network::Packet& packet, AccountDataTimesData
|
|||
}
|
||||
}
|
||||
if (packet.getReadPos() != packet.getSize()) {
|
||||
LOG_DEBUG(" AccountDataTimes trailing bytes: ", packet.getSize() - packet.getReadPos());
|
||||
LOG_DEBUG(" AccountDataTimes trailing bytes: ", packet.getRemainingSize());
|
||||
packet.setReadPos(packet.getSize());
|
||||
}
|
||||
|
||||
|
|
@ -881,7 +881,7 @@ bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock
|
|||
// 1. UpdateFlags (1 byte, sometimes 2)
|
||||
// 2. Movement data depends on update flags
|
||||
|
||||
auto rem = [&]() -> size_t { return packet.getSize() - packet.getReadPos(); };
|
||||
auto rem = [&]() -> size_t { return packet.getRemainingSize(); };
|
||||
if (rem() < 2) return false;
|
||||
|
||||
// Update flags (3.3.5a uses 2 bytes for flags)
|
||||
|
|
@ -1554,7 +1554,7 @@ bool MessageChatParser::parse(network::Packet& packet, MessageChatData& data) {
|
|||
packet.setReadPos(start);
|
||||
return false;
|
||||
}
|
||||
if ((packet.getSize() - packet.getReadPos()) < (static_cast<size_t>(len) + minTrailingBytes)) {
|
||||
if ((packet.getRemainingSize()) < (static_cast<size_t>(len) + minTrailingBytes)) {
|
||||
packet.setReadPos(start);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1761,7 +1761,7 @@ network::Packet TextEmotePacket::build(uint32_t textEmoteId, uint64_t targetGuid
|
|||
}
|
||||
|
||||
bool TextEmoteParser::parse(network::Packet& packet, TextEmoteData& data, bool legacyFormat) {
|
||||
size_t bytesLeft = packet.getSize() - packet.getReadPos();
|
||||
size_t bytesLeft = packet.getRemainingSize();
|
||||
if (bytesLeft < 20) {
|
||||
LOG_WARNING("SMSG_TEXT_EMOTE too short: ", bytesLeft, " bytes");
|
||||
return false;
|
||||
|
|
@ -1813,7 +1813,7 @@ network::Packet LeaveChannelPacket::build(const std::string& channelName) {
|
|||
}
|
||||
|
||||
bool ChannelNotifyParser::parse(network::Packet& packet, ChannelNotifyData& data) {
|
||||
size_t bytesLeft = packet.getSize() - packet.getReadPos();
|
||||
size_t bytesLeft = packet.getRemainingSize();
|
||||
if (bytesLeft < 2) {
|
||||
LOG_WARNING("SMSG_CHANNEL_NOTIFY too short");
|
||||
return false;
|
||||
|
|
@ -1821,7 +1821,7 @@ bool ChannelNotifyParser::parse(network::Packet& packet, ChannelNotifyData& data
|
|||
data.notifyType = static_cast<ChannelNotifyType>(packet.readUInt8());
|
||||
data.channelName = packet.readString();
|
||||
// Some notification types have additional fields (guid, etc.)
|
||||
bytesLeft = packet.getSize() - packet.getReadPos();
|
||||
bytesLeft = packet.getRemainingSize();
|
||||
if (bytesLeft >= 8) {
|
||||
data.senderGuid = packet.readUInt64();
|
||||
}
|
||||
|
|
@ -1874,7 +1874,7 @@ network::Packet QueryTimePacket::build() {
|
|||
|
||||
bool QueryTimeResponseParser::parse(network::Packet& packet, QueryTimeResponseData& data) {
|
||||
// Validate minimum packet size: serverTime(4) + timeOffset(4)
|
||||
if (packet.getSize() - packet.getReadPos() < 8) {
|
||||
if (packet.getRemainingSize() < 8) {
|
||||
LOG_WARNING("SMSG_QUERY_TIME_RESPONSE: packet too small (", packet.getSize(), " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1895,14 +1895,14 @@ network::Packet RequestPlayedTimePacket::build(bool sendToChat) {
|
|||
bool PlayedTimeParser::parse(network::Packet& packet, PlayedTimeData& data) {
|
||||
// Classic/Turtle may omit the trailing trigger-message byte and send only
|
||||
// totalTime(4) + levelTime(4). Later expansions append triggerMsg(1).
|
||||
if (packet.getSize() - packet.getReadPos() < 8) {
|
||||
if (packet.getRemainingSize() < 8) {
|
||||
LOG_WARNING("SMSG_PLAYED_TIME: packet too small (", packet.getSize(), " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
||||
data.totalTimePlayed = packet.readUInt32();
|
||||
data.levelTimePlayed = packet.readUInt32();
|
||||
data.triggerMessage = (packet.getSize() - packet.getReadPos() >= 1) && (packet.readUInt8() != 0);
|
||||
data.triggerMessage = (packet.getRemainingSize() >= 1) && (packet.readUInt8() != 0);
|
||||
LOG_DEBUG("Parsed SMSG_PLAYED_TIME: total=", data.totalTimePlayed, " level=", data.levelTimePlayed);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1956,7 +1956,7 @@ network::Packet SetContactNotesPacket::build(uint64_t friendGuid, const std::str
|
|||
|
||||
bool FriendStatusParser::parse(network::Packet& packet, FriendStatusData& data) {
|
||||
// Validate minimum packet size: status(1) + guid(8)
|
||||
if (packet.getSize() - packet.getReadPos() < 9) {
|
||||
if (packet.getRemainingSize() < 9) {
|
||||
LOG_WARNING("SMSG_FRIEND_STATUS: packet too small (", packet.getSize(), " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2008,7 +2008,7 @@ network::Packet LogoutCancelPacket::build() {
|
|||
|
||||
bool LogoutResponseParser::parse(network::Packet& packet, LogoutResponseData& data) {
|
||||
// Validate minimum packet size: result(4) + instant(1)
|
||||
if (packet.getSize() - packet.getReadPos() < 5) {
|
||||
if (packet.getRemainingSize() < 5) {
|
||||
LOG_WARNING("SMSG_LOGOUT_RESPONSE: packet too small (", packet.getSize(), " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2259,7 +2259,7 @@ bool PetitionShowlistParser::parse(network::Packet& packet, PetitionShowlistData
|
|||
data.displayId = packet.readUInt32();
|
||||
data.cost = packet.readUInt32();
|
||||
// Skip unused fields if present
|
||||
if ((packet.getSize() - packet.getReadPos()) >= 8) {
|
||||
if ((packet.getRemainingSize()) >= 8) {
|
||||
data.charterType = packet.readUInt32();
|
||||
data.requiredSigs = packet.readUInt32();
|
||||
}
|
||||
|
|
@ -2320,7 +2320,7 @@ bool GuildQueryResponseParser::parse(network::Packet& packet, GuildQueryResponse
|
|||
data.borderColor = packet.readUInt32();
|
||||
data.backgroundColor = packet.readUInt32();
|
||||
|
||||
if ((packet.getSize() - packet.getReadPos()) >= 4) {
|
||||
if ((packet.getRemainingSize()) >= 4) {
|
||||
data.rankCount = packet.readUInt32();
|
||||
}
|
||||
LOG_INFO("Parsed SMSG_GUILD_QUERY_RESPONSE: guild=", data.guildName, " id=", data.guildId);
|
||||
|
|
@ -2475,7 +2475,7 @@ bool GuildEventParser::parse(network::Packet& packet, GuildEventData& data) {
|
|||
for (uint8_t i = 0; i < data.numStrings && i < 3; ++i) {
|
||||
data.strings[i] = packet.readString();
|
||||
}
|
||||
if ((packet.getSize() - packet.getReadPos()) >= 8) {
|
||||
if ((packet.getRemainingSize()) >= 8) {
|
||||
data.guid = packet.readUInt64();
|
||||
}
|
||||
LOG_INFO("Parsed SMSG_GUILD_EVENT: type=", static_cast<int>(data.eventType), " strings=", static_cast<int>(data.numStrings));
|
||||
|
|
@ -2670,7 +2670,7 @@ network::Packet RandomRollPacket::build(uint32_t minRoll, uint32_t maxRoll) {
|
|||
|
||||
bool RandomRollParser::parse(network::Packet& packet, RandomRollData& data) {
|
||||
// Validate minimum packet size: rollerGuid(8) + targetGuid(8) + minRoll(4) + maxRoll(4) + result(4)
|
||||
if (packet.getSize() - packet.getReadPos() < 28) {
|
||||
if (packet.getRemainingSize() < 28) {
|
||||
LOG_WARNING("SMSG_RANDOM_ROLL: packet too small (", packet.getSize(), " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2696,13 +2696,13 @@ bool NameQueryResponseParser::parse(network::Packet& packet, NameQueryResponseDa
|
|||
// 3.3.5a: packedGuid, uint8 found
|
||||
// If found==0: CString name, CString realmName, uint8 race, uint8 gender, uint8 classId
|
||||
// Validation: packed GUID (1-8 bytes) + found flag (1 byte minimum)
|
||||
if (packet.getSize() - packet.getReadPos() < 2) return false; // At least 1 for packed GUID + 1 for found
|
||||
if (packet.getRemainingSize() < 2) return false; // At least 1 for packed GUID + 1 for found
|
||||
|
||||
size_t startPos = packet.getReadPos();
|
||||
data.guid = UpdateObjectParser::readPackedGuid(packet);
|
||||
|
||||
// Validate found flag read
|
||||
if (packet.getSize() - packet.getReadPos() < 1) {
|
||||
if (packet.getRemainingSize() < 1) {
|
||||
packet.setReadPos(startPos);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2714,7 +2714,7 @@ bool NameQueryResponseParser::parse(network::Packet& packet, NameQueryResponseDa
|
|||
}
|
||||
|
||||
// Validate strings: need at least 2 null terminators for empty strings
|
||||
if (packet.getSize() - packet.getReadPos() < 2) {
|
||||
if (packet.getRemainingSize() < 2) {
|
||||
data.name.clear();
|
||||
data.realmName.clear();
|
||||
return !data.name.empty(); // Fail if name was required
|
||||
|
|
@ -2724,7 +2724,7 @@ bool NameQueryResponseParser::parse(network::Packet& packet, NameQueryResponseDa
|
|||
data.realmName = packet.readString();
|
||||
|
||||
// Validate final 3 uint8 fields (race, gender, classId)
|
||||
if (packet.getSize() - packet.getReadPos() < 3) {
|
||||
if (packet.getRemainingSize() < 3) {
|
||||
LOG_WARNING("Name query: truncated fields after realmName, expected 3 uint8s");
|
||||
data.race = 0;
|
||||
data.gender = 0;
|
||||
|
|
@ -2776,7 +2776,7 @@ bool CreatureQueryResponseParser::parse(network::Packet& packet, CreatureQueryRe
|
|||
|
||||
// WotLK: 4 fixed fields after iconName (typeFlags, creatureType, family, rank)
|
||||
// Validate minimum size for these fields: 4×4 = 16 bytes
|
||||
if (packet.getSize() - packet.getReadPos() < 16) {
|
||||
if (packet.getRemainingSize() < 16) {
|
||||
LOG_WARNING("SMSG_CREATURE_QUERY_RESPONSE: truncated before typeFlags (entry=", data.entry, ")");
|
||||
data.typeFlags = 0;
|
||||
data.creatureType = 0;
|
||||
|
|
@ -2826,7 +2826,7 @@ bool GameObjectQueryResponseParser::parse(network::Packet& packet, GameObjectQue
|
|||
}
|
||||
|
||||
// Validate minimum size for fixed fields: type(4) + displayId(4)
|
||||
if (packet.getSize() - packet.getReadPos() < 8) {
|
||||
if (packet.getRemainingSize() < 8) {
|
||||
LOG_ERROR("SMSG_GAMEOBJECT_QUERY_RESPONSE: truncated before names (entry=", data.entry, ")");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2846,7 +2846,7 @@ bool GameObjectQueryResponseParser::parse(network::Packet& packet, GameObjectQue
|
|||
packet.readString(); // unk1
|
||||
|
||||
// Read 24 type-specific data fields
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
if (remaining >= 24 * 4) {
|
||||
for (int i = 0; i < 24; i++) {
|
||||
data.data[i] = packet.readUInt32();
|
||||
|
|
@ -2876,10 +2876,10 @@ network::Packet PageTextQueryPacket::build(uint32_t pageId, uint64_t guid) {
|
|||
}
|
||||
|
||||
bool PageTextQueryResponseParser::parse(network::Packet& packet, PageTextQueryResponseData& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 4) return false;
|
||||
if (packet.getRemainingSize() < 4) return false;
|
||||
data.pageId = packet.readUInt32();
|
||||
data.text = normalizeWowTextTokens(packet.readString());
|
||||
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||
if (packet.getRemainingSize() >= 4) {
|
||||
data.nextPageId = packet.readUInt32();
|
||||
} else {
|
||||
data.nextPageId = 0;
|
||||
|
|
@ -2941,7 +2941,7 @@ bool ItemQueryResponseParser::parse(network::Packet& packet, ItemQueryResponseDa
|
|||
|
||||
// Validate minimum size for fixed fields before reading: itemClass(4) + subClass(4) + soundOverride(4)
|
||||
// + 4 name strings + displayInfoId(4) + quality(4) = at least 24 bytes more
|
||||
if (packet.getSize() - packet.getReadPos() < 24) {
|
||||
if (packet.getRemainingSize() < 24) {
|
||||
LOG_ERROR("SMSG_ITEM_QUERY_SINGLE_RESPONSE: truncated before displayInfoId (entry=", data.entry, ")");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2967,7 +2967,7 @@ bool ItemQueryResponseParser::parse(network::Packet& packet, ItemQueryResponseDa
|
|||
// Some server variants omit BuyCount (4 fields instead of 5).
|
||||
// Read 5 fields and validate InventoryType; if it looks implausible, rewind and try 4.
|
||||
const size_t postQualityPos = packet.getReadPos();
|
||||
if (packet.getSize() - packet.getReadPos() < 24) {
|
||||
if (packet.getRemainingSize() < 24) {
|
||||
LOG_ERROR("SMSG_ITEM_QUERY_SINGLE_RESPONSE: truncated before flags (entry=", data.entry, ")");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2989,7 +2989,7 @@ bool ItemQueryResponseParser::parse(network::Packet& packet, ItemQueryResponseDa
|
|||
}
|
||||
|
||||
// Validate minimum size for remaining fixed fields before inventoryType through containerSlots: 13×4 = 52 bytes
|
||||
if (packet.getSize() - packet.getReadPos() < 52) {
|
||||
if (packet.getRemainingSize() < 52) {
|
||||
LOG_ERROR("SMSG_ITEM_QUERY_SINGLE_RESPONSE: truncated before statsCount (entry=", data.entry, ")");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3009,7 +3009,7 @@ bool ItemQueryResponseParser::parse(network::Packet& packet, ItemQueryResponseDa
|
|||
data.containerSlots = packet.readUInt32();
|
||||
|
||||
// Read statsCount with bounds validation
|
||||
if (packet.getSize() - packet.getReadPos() < 4) {
|
||||
if (packet.getRemainingSize() < 4) {
|
||||
LOG_WARNING("SMSG_ITEM_QUERY_SINGLE_RESPONSE: truncated at statsCount (entry=", data.entry, ")");
|
||||
return true; // Have enough for core fields; stats are optional
|
||||
}
|
||||
|
|
@ -3027,7 +3027,7 @@ bool ItemQueryResponseParser::parse(network::Packet& packet, ItemQueryResponseDa
|
|||
uint32_t statsToRead = std::min(statsCount, 10u);
|
||||
for (uint32_t i = 0; i < statsToRead; i++) {
|
||||
// Each stat is 2 uint32s (type + value) = 8 bytes
|
||||
if (packet.getSize() - packet.getReadPos() < 8) {
|
||||
if (packet.getRemainingSize() < 8) {
|
||||
LOG_WARNING("SMSG_ITEM_QUERY_SINGLE_RESPONSE: stat ", i, " truncated (entry=", data.entry, ")");
|
||||
break;
|
||||
}
|
||||
|
|
@ -3047,7 +3047,7 @@ bool ItemQueryResponseParser::parse(network::Packet& packet, ItemQueryResponseDa
|
|||
}
|
||||
|
||||
// ScalingStatDistribution and ScalingStatValue
|
||||
if (packet.getSize() - packet.getReadPos() < 8) {
|
||||
if (packet.getRemainingSize() < 8) {
|
||||
LOG_WARNING("SMSG_ITEM_QUERY_SINGLE_RESPONSE: truncated before scaling stats (entry=", data.entry, ")");
|
||||
return true; // Have core fields; scaling is optional
|
||||
}
|
||||
|
|
@ -3387,7 +3387,7 @@ bool AttackStopParser::parse(network::Packet& packet, AttackStopData& data) {
|
|||
|
||||
bool AttackerStateUpdateParser::parse(network::Packet& packet, AttackerStateUpdateData& data) {
|
||||
// Upfront validation: hitInfo(4) + packed GUIDs(1-8 each) + totalDamage(4) + subDamageCount(1) = 13 bytes minimum
|
||||
if (packet.getSize() - packet.getReadPos() < 13) return false;
|
||||
if (packet.getRemainingSize() < 13) return false;
|
||||
|
||||
size_t startPos = packet.getReadPos();
|
||||
data.hitInfo = packet.readUInt32();
|
||||
|
|
@ -3403,7 +3403,7 @@ bool AttackerStateUpdateParser::parse(network::Packet& packet, AttackerStateUpda
|
|||
data.targetGuid = UpdateObjectParser::readPackedGuid(packet);
|
||||
|
||||
// Validate totalDamage + subDamageCount can be read (5 bytes)
|
||||
if (packet.getSize() - packet.getReadPos() < 5) {
|
||||
if (packet.getRemainingSize() < 5) {
|
||||
packet.setReadPos(startPos);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3416,7 +3416,7 @@ bool AttackerStateUpdateParser::parse(network::Packet& packet, AttackerStateUpda
|
|||
// (off by one byte), causing the school-mask byte to be read as count.
|
||||
// In that case clamp to the number of full entries that fit.
|
||||
{
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
size_t maxFit = remaining / 20;
|
||||
if (data.subDamageCount > maxFit) {
|
||||
data.subDamageCount = static_cast<uint8_t>(std::min<size_t>(maxFit, 64));
|
||||
|
|
@ -3429,7 +3429,7 @@ bool AttackerStateUpdateParser::parse(network::Packet& packet, AttackerStateUpda
|
|||
data.subDamages.reserve(data.subDamageCount);
|
||||
for (uint8_t i = 0; i < data.subDamageCount; ++i) {
|
||||
// Each sub-damage entry needs 20 bytes: schoolMask(4) + damage(4) + intDamage(4) + absorbed(4) + resisted(4)
|
||||
if (packet.getSize() - packet.getReadPos() < 20) {
|
||||
if (packet.getRemainingSize() < 20) {
|
||||
data.subDamageCount = i;
|
||||
break;
|
||||
}
|
||||
|
|
@ -3443,7 +3443,7 @@ bool AttackerStateUpdateParser::parse(network::Packet& packet, AttackerStateUpda
|
|||
}
|
||||
|
||||
// Validate victimState + overkill fields (8 bytes)
|
||||
if (packet.getSize() - packet.getReadPos() < 8) {
|
||||
if (packet.getRemainingSize() < 8) {
|
||||
data.victimState = 0;
|
||||
data.overkill = 0;
|
||||
return !data.subDamages.empty();
|
||||
|
|
@ -3452,7 +3452,7 @@ bool AttackerStateUpdateParser::parse(network::Packet& packet, AttackerStateUpda
|
|||
data.victimState = packet.readUInt32();
|
||||
// WotLK (AzerothCore): two unknown uint32 fields follow victimState before overkill.
|
||||
// Older parsers omitted these, reading overkill from the wrong offset.
|
||||
auto rem = [&]() { return packet.getSize() - packet.getReadPos(); };
|
||||
auto rem = [&]() { return packet.getRemainingSize(); };
|
||||
if (rem() >= 4) packet.readUInt32(); // unk1 (always 0)
|
||||
if (rem() >= 4) packet.readUInt32(); // unk2 (melee spell ID, 0 for auto-attack)
|
||||
data.overkill = (rem() >= 4) ? static_cast<int32_t>(packet.readUInt32()) : -1;
|
||||
|
|
@ -3475,7 +3475,7 @@ bool SpellDamageLogParser::parse(network::Packet& packet, SpellDamageLogData& da
|
|||
// packed GUIDs(1-8 each) + spellId(4) + damage(4) + overkill(4) + schoolMask(1)
|
||||
// + absorbed(4) + resisted(4) + periodicLog(1) + unused(1) + blocked(4) + flags(4)
|
||||
// = 33 bytes minimum.
|
||||
if (packet.getSize() - packet.getReadPos() < 33) return false;
|
||||
if (packet.getRemainingSize() < 33) return false;
|
||||
|
||||
size_t startPos = packet.getReadPos();
|
||||
if (!hasFullPackedGuid(packet)) {
|
||||
|
|
@ -3490,7 +3490,7 @@ bool SpellDamageLogParser::parse(network::Packet& packet, SpellDamageLogData& da
|
|||
data.attackerGuid = UpdateObjectParser::readPackedGuid(packet);
|
||||
|
||||
// Validate core fields (spellId + damage + overkill + schoolMask + absorbed + resisted = 21 bytes)
|
||||
if (packet.getSize() - packet.getReadPos() < 21) {
|
||||
if (packet.getRemainingSize() < 21) {
|
||||
packet.setReadPos(startPos);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3504,7 +3504,7 @@ bool SpellDamageLogParser::parse(network::Packet& packet, SpellDamageLogData& da
|
|||
|
||||
// Remaining fields are required for a complete event.
|
||||
// Reject truncated packets so we do not emit partial/incorrect combat entries.
|
||||
if (packet.getSize() - packet.getReadPos() < 10) {
|
||||
if (packet.getRemainingSize() < 10) {
|
||||
packet.setReadPos(startPos);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3525,7 +3525,7 @@ bool SpellDamageLogParser::parse(network::Packet& packet, SpellDamageLogData& da
|
|||
|
||||
bool SpellHealLogParser::parse(network::Packet& packet, SpellHealLogData& data) {
|
||||
// Upfront validation: packed GUIDs(1-8 each) + spellId(4) + heal(4) + overheal(4) + absorbed(4) + critFlag(1) = 21 bytes minimum
|
||||
if (packet.getSize() - packet.getReadPos() < 21) return false;
|
||||
if (packet.getRemainingSize() < 21) return false;
|
||||
|
||||
size_t startPos = packet.getReadPos();
|
||||
if (!hasFullPackedGuid(packet)) {
|
||||
|
|
@ -3540,7 +3540,7 @@ bool SpellHealLogParser::parse(network::Packet& packet, SpellHealLogData& data)
|
|||
data.casterGuid = UpdateObjectParser::readPackedGuid(packet);
|
||||
|
||||
// Validate remaining fields (spellId + heal + overheal + absorbed + critFlag = 17 bytes)
|
||||
if (packet.getSize() - packet.getReadPos() < 17) {
|
||||
if (packet.getRemainingSize() < 17) {
|
||||
packet.setReadPos(startPos);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3563,7 +3563,7 @@ bool SpellHealLogParser::parse(network::Packet& packet, SpellHealLogData& data)
|
|||
|
||||
bool XpGainParser::parse(network::Packet& packet, XpGainData& data) {
|
||||
// Validate minimum packet size: victimGuid(8) + totalXp(4) + type(1)
|
||||
if (packet.getSize() - packet.getReadPos() < 13) {
|
||||
if (packet.getRemainingSize() < 13) {
|
||||
LOG_WARNING("SMSG_LOG_XPGAIN: packet too small (", packet.getSize(), " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3594,7 +3594,7 @@ bool XpGainParser::parse(network::Packet& packet, XpGainData& data) {
|
|||
bool InitialSpellsParser::parse(network::Packet& packet, InitialSpellsData& data,
|
||||
bool vanillaFormat) {
|
||||
// Validate minimum packet size for header: talentSpec(1) + spellCount(2)
|
||||
if (packet.getSize() - packet.getReadPos() < 3) {
|
||||
if (packet.getRemainingSize() < 3) {
|
||||
LOG_ERROR("SMSG_INITIAL_SPELLS: packet too small (", packet.getSize(), " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3620,7 +3620,7 @@ bool InitialSpellsParser::parse(network::Packet& packet, InitialSpellsData& data
|
|||
// Vanilla spell: spellId(2) + slot(2) = 4 bytes
|
||||
// TBC/WotLK spell: spellId(4) + unknown(2) = 6 bytes
|
||||
size_t spellEntrySize = vanillaFormat ? 4 : 6;
|
||||
if (packet.getSize() - packet.getReadPos() < spellEntrySize) {
|
||||
if (packet.getRemainingSize() < spellEntrySize) {
|
||||
LOG_WARNING("SMSG_INITIAL_SPELLS: spell ", i, " truncated (", spellCount, " expected)");
|
||||
break;
|
||||
}
|
||||
|
|
@ -3639,7 +3639,7 @@ bool InitialSpellsParser::parse(network::Packet& packet, InitialSpellsData& data
|
|||
}
|
||||
|
||||
// Validate minimum packet size for cooldownCount (2 bytes)
|
||||
if (packet.getSize() - packet.getReadPos() < 2) {
|
||||
if (packet.getRemainingSize() < 2) {
|
||||
LOG_WARNING("SMSG_INITIAL_SPELLS: truncated before cooldownCount (parsed ", data.spellIds.size(),
|
||||
" spells)");
|
||||
return true; // Have spells; cooldowns are optional
|
||||
|
|
@ -3662,7 +3662,7 @@ bool InitialSpellsParser::parse(network::Packet& packet, InitialSpellsData& data
|
|||
// Vanilla cooldown: spellId(2) + itemId(2) + categoryId(2) + cooldownMs(4) + categoryCooldownMs(4) = 14 bytes
|
||||
// TBC/WotLK cooldown: spellId(4) + itemId(2) + categoryId(2) + cooldownMs(4) + categoryCooldownMs(4) = 16 bytes
|
||||
size_t cooldownEntrySize = vanillaFormat ? 14 : 16;
|
||||
if (packet.getSize() - packet.getReadPos() < cooldownEntrySize) {
|
||||
if (packet.getRemainingSize() < cooldownEntrySize) {
|
||||
LOG_WARNING("SMSG_INITIAL_SPELLS: cooldown ", i, " truncated (", cooldownCount, " expected)");
|
||||
break;
|
||||
}
|
||||
|
|
@ -3748,7 +3748,7 @@ network::Packet PetActionPacket::build(uint64_t petGuid, uint32_t action, uint64
|
|||
|
||||
bool CastFailedParser::parse(network::Packet& packet, CastFailedData& data) {
|
||||
// WotLK format: castCount(1) + spellId(4) + result(1) = 6 bytes minimum
|
||||
if (packet.getSize() - packet.getReadPos() < 6) return false;
|
||||
if (packet.getRemainingSize() < 6) return false;
|
||||
|
||||
data.castCount = packet.readUInt8();
|
||||
data.spellId = packet.readUInt32();
|
||||
|
|
@ -3762,7 +3762,7 @@ bool SpellStartParser::parse(network::Packet& packet, SpellStartData& data) {
|
|||
|
||||
// Packed GUIDs are variable-length; only require minimal packet shape up front:
|
||||
// two GUID masks + castCount(1) + spellId(4) + castFlags(4) + castTime(4).
|
||||
if (packet.getSize() - packet.getReadPos() < 15) return false;
|
||||
if (packet.getRemainingSize() < 15) return false;
|
||||
|
||||
size_t startPos = packet.getReadPos();
|
||||
if (!hasFullPackedGuid(packet)) {
|
||||
|
|
@ -3776,7 +3776,7 @@ bool SpellStartParser::parse(network::Packet& packet, SpellStartData& data) {
|
|||
data.casterUnit = UpdateObjectParser::readPackedGuid(packet);
|
||||
|
||||
// Validate remaining fixed fields (castCount + spellId + castFlags + castTime = 13 bytes)
|
||||
if (packet.getSize() - packet.getReadPos() < 13) {
|
||||
if (packet.getRemainingSize() < 13) {
|
||||
packet.setReadPos(startPos);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3787,7 +3787,7 @@ bool SpellStartParser::parse(network::Packet& packet, SpellStartData& data) {
|
|||
data.castTime = packet.readUInt32();
|
||||
|
||||
// SpellCastTargets starts with target flags and is mandatory.
|
||||
if (packet.getSize() - packet.getReadPos() < 4) {
|
||||
if (packet.getRemainingSize() < 4) {
|
||||
LOG_WARNING("Spell start: missing targetFlags");
|
||||
packet.setReadPos(startPos);
|
||||
return false;
|
||||
|
|
@ -3807,7 +3807,7 @@ bool SpellStartParser::parse(network::Packet& packet, SpellStartData& data) {
|
|||
auto skipPackedAndFloats3 = [&]() -> bool {
|
||||
if (!hasFullPackedGuid(packet)) return false;
|
||||
UpdateObjectParser::readPackedGuid(packet); // transport GUID (may be zero)
|
||||
if (packet.getSize() - packet.getReadPos() < 12) return false;
|
||||
if (packet.getRemainingSize() < 12) return false;
|
||||
packet.readFloat(); packet.readFloat(); packet.readFloat();
|
||||
return true;
|
||||
};
|
||||
|
|
@ -3843,7 +3843,7 @@ bool SpellGoParser::parse(network::Packet& packet, SpellGoData& data) {
|
|||
|
||||
// Packed GUIDs are variable-length, so only require the smallest possible
|
||||
// shape up front: 2 GUID masks + fixed fields through hitCount.
|
||||
if (packet.getSize() - packet.getReadPos() < 16) return false;
|
||||
if (packet.getRemainingSize() < 16) return false;
|
||||
|
||||
size_t startPos = packet.getReadPos();
|
||||
if (!hasFullPackedGuid(packet)) {
|
||||
|
|
@ -3857,7 +3857,7 @@ bool SpellGoParser::parse(network::Packet& packet, SpellGoData& data) {
|
|||
data.casterUnit = UpdateObjectParser::readPackedGuid(packet);
|
||||
|
||||
// Validate remaining fixed fields up to hitCount/missCount
|
||||
if (packet.getSize() - packet.getReadPos() < 14) { // castCount(1) + spellId(4) + castFlags(4) + timestamp(4) + hitCount(1)
|
||||
if (packet.getRemainingSize() < 14) { // castCount(1) + spellId(4) + castFlags(4) + timestamp(4) + hitCount(1)
|
||||
packet.setReadPos(startPos);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3879,7 +3879,7 @@ bool SpellGoParser::parse(network::Packet& packet, SpellGoData& data) {
|
|||
data.hitTargets.reserve(storedHitLimit);
|
||||
for (uint16_t i = 0; i < rawHitCount; ++i) {
|
||||
// WotLK 3.3.5a hit targets are full uint64 GUIDs (not PackedGuid).
|
||||
if (packet.getSize() - packet.getReadPos() < 8) {
|
||||
if (packet.getRemainingSize() < 8) {
|
||||
LOG_WARNING("Spell go: truncated hit targets at index ", i, "/", static_cast<int>(rawHitCount));
|
||||
truncatedTargets = true;
|
||||
break;
|
||||
|
|
@ -3896,7 +3896,7 @@ bool SpellGoParser::parse(network::Packet& packet, SpellGoData& data) {
|
|||
data.hitCount = static_cast<uint8_t>(data.hitTargets.size());
|
||||
|
||||
// missCount is mandatory in SMSG_SPELL_GO. Missing byte means truncation.
|
||||
if (packet.getSize() - packet.getReadPos() < 1) {
|
||||
if (packet.getRemainingSize() < 1) {
|
||||
LOG_WARNING("Spell go: missing missCount after hit target list");
|
||||
packet.setReadPos(startPos);
|
||||
return false;
|
||||
|
|
@ -3926,7 +3926,7 @@ bool SpellGoParser::parse(network::Packet& packet, SpellGoData& data) {
|
|||
if (rawMissCount > 128) {
|
||||
LOG_WARNING("Spell go: missCount capped (requested=", static_cast<int>(rawMissCount),
|
||||
") spell=", data.spellId, " hits=", static_cast<int>(data.hitCount),
|
||||
" remaining=", packet.getSize() - packet.getReadPos());
|
||||
" remaining=", packet.getRemainingSize());
|
||||
}
|
||||
const uint8_t storedMissLimit = std::min<uint8_t>(rawMissCount, 128);
|
||||
|
||||
|
|
@ -3934,7 +3934,7 @@ bool SpellGoParser::parse(network::Packet& packet, SpellGoData& data) {
|
|||
for (uint16_t i = 0; i < rawMissCount; ++i) {
|
||||
// WotLK 3.3.5a miss targets are full uint64 GUIDs + uint8 missType.
|
||||
// REFLECT additionally appends uint8 reflectResult.
|
||||
if (packet.getSize() - packet.getReadPos() < 9) { // 8 GUID + 1 missType
|
||||
if (packet.getRemainingSize() < 9) { // 8 GUID + 1 missType
|
||||
LOG_WARNING("Spell go: truncated miss targets at index ", i, "/", static_cast<int>(rawMissCount),
|
||||
" spell=", data.spellId, " hits=", static_cast<int>(data.hitCount));
|
||||
truncatedTargets = true;
|
||||
|
|
@ -3944,7 +3944,7 @@ bool SpellGoParser::parse(network::Packet& packet, SpellGoData& data) {
|
|||
m.targetGuid = packet.readUInt64();
|
||||
m.missType = packet.readUInt8();
|
||||
if (m.missType == 11) { // SPELL_MISS_REFLECT
|
||||
if (packet.getSize() - packet.getReadPos() < 1) {
|
||||
if (packet.getRemainingSize() < 1) {
|
||||
LOG_WARNING("Spell go: truncated reflect payload at miss index ", i, "/", static_cast<int>(rawMissCount));
|
||||
truncatedTargets = true;
|
||||
break;
|
||||
|
|
@ -3970,7 +3970,7 @@ bool SpellGoParser::parse(network::Packet& packet, SpellGoData& data) {
|
|||
// any trailing fields after the target section are not misaligned for
|
||||
// ground-targeted or AoE spells. Same layout as SpellStartParser.
|
||||
if (packet.getReadPos() < packet.getSize()) {
|
||||
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||
if (packet.getRemainingSize() >= 4) {
|
||||
uint32_t targetFlags = packet.readUInt32();
|
||||
|
||||
auto readPackedTarget = [&](uint64_t* out) -> bool {
|
||||
|
|
@ -3982,7 +3982,7 @@ bool SpellGoParser::parse(network::Packet& packet, SpellGoData& data) {
|
|||
auto skipPackedAndFloats3 = [&]() -> bool {
|
||||
if (!hasFullPackedGuid(packet)) return false;
|
||||
UpdateObjectParser::readPackedGuid(packet); // transport GUID
|
||||
if (packet.getSize() - packet.getReadPos() < 12) return false;
|
||||
if (packet.getRemainingSize() < 12) return false;
|
||||
packet.readFloat(); packet.readFloat(); packet.readFloat();
|
||||
return true;
|
||||
};
|
||||
|
|
@ -4017,7 +4017,7 @@ bool SpellGoParser::parse(network::Packet& packet, SpellGoData& data) {
|
|||
|
||||
bool AuraUpdateParser::parse(network::Packet& packet, AuraUpdateData& data, bool isAll) {
|
||||
// Validation: packed GUID (1-8 bytes minimum for reading)
|
||||
if (packet.getSize() - packet.getReadPos() < 1) return false;
|
||||
if (packet.getRemainingSize() < 1) return false;
|
||||
|
||||
data.guid = UpdateObjectParser::readPackedGuid(packet);
|
||||
|
||||
|
|
@ -4027,7 +4027,7 @@ bool AuraUpdateParser::parse(network::Packet& packet, AuraUpdateData& data, bool
|
|||
|
||||
while (packet.getReadPos() < packet.getSize() && auraCount < maxAuras) {
|
||||
// Validate we can read slot (1) + spellId (4) = 5 bytes minimum
|
||||
if (packet.getSize() - packet.getReadPos() < 5) {
|
||||
if (packet.getRemainingSize() < 5) {
|
||||
LOG_DEBUG("Aura update: truncated entry at position ", auraCount);
|
||||
break;
|
||||
}
|
||||
|
|
@ -4041,7 +4041,7 @@ bool AuraUpdateParser::parse(network::Packet& packet, AuraUpdateData& data, bool
|
|||
aura.spellId = spellId;
|
||||
|
||||
// Validate flags + level + charges (3 bytes)
|
||||
if (packet.getSize() - packet.getReadPos() < 3) {
|
||||
if (packet.getRemainingSize() < 3) {
|
||||
LOG_WARNING("Aura update: truncated flags/level/charges at entry ", auraCount);
|
||||
aura.flags = 0;
|
||||
aura.level = 0;
|
||||
|
|
@ -4054,7 +4054,7 @@ bool AuraUpdateParser::parse(network::Packet& packet, AuraUpdateData& data, bool
|
|||
|
||||
if (!(aura.flags & 0x08)) { // NOT_CASTER flag
|
||||
// Validate space for packed GUID read (minimum 1 byte)
|
||||
if (packet.getSize() - packet.getReadPos() < 1) {
|
||||
if (packet.getRemainingSize() < 1) {
|
||||
aura.casterGuid = 0;
|
||||
} else {
|
||||
aura.casterGuid = UpdateObjectParser::readPackedGuid(packet);
|
||||
|
|
@ -4062,7 +4062,7 @@ bool AuraUpdateParser::parse(network::Packet& packet, AuraUpdateData& data, bool
|
|||
}
|
||||
|
||||
if (aura.flags & 0x20) { // DURATION - need 8 bytes (two uint32s)
|
||||
if (packet.getSize() - packet.getReadPos() < 8) {
|
||||
if (packet.getRemainingSize() < 8) {
|
||||
LOG_WARNING("Aura update: truncated duration fields at entry ", auraCount);
|
||||
aura.maxDurationMs = 0;
|
||||
aura.durationMs = 0;
|
||||
|
|
@ -4076,7 +4076,7 @@ bool AuraUpdateParser::parse(network::Packet& packet, AuraUpdateData& data, bool
|
|||
// Only read amounts for active effect indices (flags 0x01, 0x02, 0x04)
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (aura.flags & (1 << i)) {
|
||||
if (packet.getSize() - packet.getReadPos() >= 4) {
|
||||
if (packet.getRemainingSize() >= 4) {
|
||||
packet.readUInt32();
|
||||
} else {
|
||||
LOG_WARNING("Aura update: truncated effect amount ", i, " at entry ", auraCount);
|
||||
|
|
@ -4104,7 +4104,7 @@ bool AuraUpdateParser::parse(network::Packet& packet, AuraUpdateData& data, bool
|
|||
|
||||
bool SpellCooldownParser::parse(network::Packet& packet, SpellCooldownData& data) {
|
||||
// Upfront validation: guid(8) + flags(1) = 9 bytes minimum
|
||||
if (packet.getSize() - packet.getReadPos() < 9) return false;
|
||||
if (packet.getRemainingSize() < 9) return false;
|
||||
|
||||
data.guid = packet.readUInt64();
|
||||
data.flags = packet.readUInt8();
|
||||
|
|
@ -4142,7 +4142,7 @@ network::Packet GroupInvitePacket::build(const std::string& playerName) {
|
|||
|
||||
bool GroupInviteResponseParser::parse(network::Packet& packet, GroupInviteResponseData& data) {
|
||||
// Validate minimum packet size: canAccept(1)
|
||||
if (packet.getSize() - packet.getReadPos() < 1) {
|
||||
if (packet.getRemainingSize() < 1) {
|
||||
LOG_WARNING("SMSG_GROUP_INVITE: packet too small (", packet.getSize(), " bytes)");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -4166,7 +4166,7 @@ network::Packet GroupDeclinePacket::build() {
|
|||
}
|
||||
|
||||
bool GroupListParser::parse(network::Packet& packet, GroupListData& data, bool hasRoles) {
|
||||
auto rem = [&]() { return packet.getSize() - packet.getReadPos(); };
|
||||
auto rem = [&]() { return packet.getRemainingSize(); };
|
||||
|
||||
if (rem() < 3) return false;
|
||||
data.groupType = packet.readUInt8();
|
||||
|
|
@ -4250,13 +4250,13 @@ bool GroupListParser::parse(network::Packet& packet, GroupListData& data, bool h
|
|||
|
||||
bool PartyCommandResultParser::parse(network::Packet& packet, PartyCommandResultData& data) {
|
||||
// Upfront validation: command(4) + name(var) + result(4) = 8 bytes minimum (plus name string)
|
||||
if (packet.getSize() - packet.getReadPos() < 8) return false;
|
||||
if (packet.getRemainingSize() < 8) return false;
|
||||
|
||||
data.command = static_cast<PartyCommand>(packet.readUInt32());
|
||||
data.name = packet.readString();
|
||||
|
||||
// Validate result field exists (4 bytes)
|
||||
if (packet.getSize() - packet.getReadPos() < 4) {
|
||||
if (packet.getRemainingSize() < 4) {
|
||||
data.result = static_cast<PartyResult>(0);
|
||||
return true; // Partial read is acceptable
|
||||
}
|
||||
|
|
@ -4268,7 +4268,7 @@ bool PartyCommandResultParser::parse(network::Packet& packet, PartyCommandResult
|
|||
|
||||
bool GroupDeclineResponseParser::parse(network::Packet& packet, GroupDeclineData& data) {
|
||||
// Upfront validation: playerName is a CString (minimum 1 null terminator)
|
||||
if (packet.getSize() - packet.getReadPos() < 1) return false;
|
||||
if (packet.getRemainingSize() < 1) return false;
|
||||
|
||||
data.playerName = packet.readString();
|
||||
LOG_INFO("Group decline from: ", data.playerName);
|
||||
|
|
@ -4360,7 +4360,7 @@ network::Packet LootReleasePacket::build(uint64_t lootGuid) {
|
|||
|
||||
bool LootResponseParser::parse(network::Packet& packet, LootResponseData& data, bool isWotlkFormat) {
|
||||
data = LootResponseData{};
|
||||
size_t avail = packet.getSize() - packet.getReadPos();
|
||||
size_t avail = packet.getRemainingSize();
|
||||
|
||||
// Minimum is guid(8)+lootType(1) = 9 bytes. Servers send a short packet with
|
||||
// lootType=0 (LOOT_NONE) when loot is unavailable (e.g. chest not yet opened,
|
||||
|
|
@ -4375,7 +4375,7 @@ bool LootResponseParser::parse(network::Packet& packet, LootResponseData& data,
|
|||
data.lootType = packet.readUInt8();
|
||||
|
||||
// Short failure packet — no gold/item data follows.
|
||||
avail = packet.getSize() - packet.getReadPos();
|
||||
avail = packet.getRemainingSize();
|
||||
if (avail < 5) {
|
||||
LOG_DEBUG("LootResponseParser: lootType=", static_cast<int>(data.lootType), " (empty/failure response)");
|
||||
return false;
|
||||
|
|
@ -4390,7 +4390,7 @@ bool LootResponseParser::parse(network::Packet& packet, LootResponseData& data,
|
|||
|
||||
auto parseLootItemList = [&](uint8_t listCount, bool markQuestItems) -> bool {
|
||||
for (uint8_t i = 0; i < listCount; ++i) {
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
if (remaining < kItemSize) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -4417,7 +4417,7 @@ bool LootResponseParser::parse(network::Packet& packet, LootResponseData& data,
|
|||
|
||||
// Quest item section only present in WotLK 3.3.5a
|
||||
uint8_t questItemCount = 0;
|
||||
if (isWotlkFormat && packet.getSize() - packet.getReadPos() >= 1) {
|
||||
if (isWotlkFormat && packet.getRemainingSize() >= 1) {
|
||||
questItemCount = packet.readUInt8();
|
||||
data.items.reserve(data.items.size() + questItemCount);
|
||||
if (!parseLootItemList(questItemCount, true)) {
|
||||
|
|
@ -4549,7 +4549,7 @@ bool QuestDetailsParser::parse(network::Packet& packet, QuestDetailsData& data)
|
|||
|
||||
bool GossipMessageParser::parse(network::Packet& packet, GossipMessageData& data) {
|
||||
// Upfront validation: npcGuid(8) + menuId(4) + titleTextId(4) + optionCount(4) = 20 bytes minimum
|
||||
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
||||
if (packet.getRemainingSize() < 20) return false;
|
||||
|
||||
data.npcGuid = packet.readUInt64();
|
||||
data.menuId = packet.readUInt32();
|
||||
|
|
@ -4568,7 +4568,7 @@ bool GossipMessageParser::parse(network::Packet& packet, GossipMessageData& data
|
|||
for (uint32_t i = 0; i < optionCount; ++i) {
|
||||
// Each option: id(4) + icon(1) + isCoded(1) + boxMoney(4) + text(var) + boxText(var)
|
||||
// Minimum: 10 bytes + 2 empty strings (2 null terminators) = 12 bytes
|
||||
if (packet.getSize() - packet.getReadPos() < 12) {
|
||||
if (packet.getRemainingSize() < 12) {
|
||||
LOG_WARNING("GossipMessageParser: truncated options at index ", i, "/", optionCount);
|
||||
break;
|
||||
}
|
||||
|
|
@ -4583,7 +4583,7 @@ bool GossipMessageParser::parse(network::Packet& packet, GossipMessageData& data
|
|||
}
|
||||
|
||||
// Validate questCount field exists (4 bytes)
|
||||
if (packet.getSize() - packet.getReadPos() < 4) {
|
||||
if (packet.getRemainingSize() < 4) {
|
||||
LOG_DEBUG("Gossip: ", data.options.size(), " options (no quest data)");
|
||||
return true;
|
||||
}
|
||||
|
|
@ -4601,7 +4601,7 @@ bool GossipMessageParser::parse(network::Packet& packet, GossipMessageData& data
|
|||
for (uint32_t i = 0; i < questCount; ++i) {
|
||||
// Each quest: questId(4) + questIcon(4) + questLevel(4) + questFlags(4) + isRepeatable(1) + title(var)
|
||||
// Minimum: 17 bytes + empty string (1 null terminator) = 18 bytes
|
||||
if (packet.getSize() - packet.getReadPos() < 18) {
|
||||
if (packet.getRemainingSize() < 18) {
|
||||
LOG_WARNING("GossipMessageParser: truncated quests at index ", i, "/", questCount);
|
||||
break;
|
||||
}
|
||||
|
|
@ -4640,7 +4640,7 @@ bool BindPointUpdateParser::parse(network::Packet& packet, BindPointUpdateData&
|
|||
}
|
||||
|
||||
bool QuestRequestItemsParser::parse(network::Packet& packet, QuestRequestItemsData& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
||||
if (packet.getRemainingSize() < 20) return false;
|
||||
data.npcGuid = packet.readUInt64();
|
||||
data.questId = packet.readUInt32();
|
||||
data.title = normalizeWowTextTokens(packet.readString());
|
||||
|
|
@ -4694,7 +4694,7 @@ bool QuestRequestItemsParser::parse(network::Packet& packet, QuestRequestItemsDa
|
|||
else if (out.requiredMoney <= 100000) out.score += 2; // <=10g is common
|
||||
else if (out.requiredMoney >= 1000000) out.score -= 3; // implausible for most quests
|
||||
if (!out.requiredItems.empty()) out.score += 1;
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
if (remaining <= 16) out.score += 3;
|
||||
else if (remaining <= 32) out.score += 2;
|
||||
else if (remaining <= 64) out.score += 1;
|
||||
|
|
@ -4729,7 +4729,7 @@ bool QuestRequestItemsParser::parse(network::Packet& packet, QuestRequestItemsDa
|
|||
}
|
||||
|
||||
bool QuestOfferRewardParser::parse(network::Packet& packet, QuestOfferRewardData& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
||||
if (packet.getRemainingSize() < 20) return false;
|
||||
data.npcGuid = packet.readUInt64();
|
||||
data.questId = packet.readUInt32();
|
||||
data.title = normalizeWowTextTokens(packet.readString());
|
||||
|
|
@ -4834,7 +4834,7 @@ bool QuestOfferRewardParser::parse(network::Packet& packet, QuestOfferRewardData
|
|||
if (nonZeroChoice <= choiceCount) out.score += 2;
|
||||
if (nonZeroFixed <= rewardCount) out.score += 2;
|
||||
// No bytes left over (or only a few)
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
if (remaining == 0) out.score += 5;
|
||||
else if (remaining <= 4) out.score += 3;
|
||||
else if (remaining <= 8) out.score += 2;
|
||||
|
|
@ -4937,7 +4937,7 @@ network::Packet BuybackItemPacket::build(uint64_t vendorGuid, uint32_t slot) {
|
|||
|
||||
bool ListInventoryParser::parse(network::Packet& packet, ListInventoryData& data) {
|
||||
data = ListInventoryData{};
|
||||
if (packet.getSize() - packet.getReadPos() < 9) {
|
||||
if (packet.getRemainingSize() < 9) {
|
||||
LOG_WARNING("ListInventoryParser: packet too short");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -4953,7 +4953,7 @@ bool ListInventoryParser::parse(network::Packet& packet, ListInventoryData& data
|
|||
// Auto-detect whether server sends 7 fields (28 bytes/item) or 8 fields (32 bytes/item).
|
||||
// Some servers omit the extendedCost field entirely; reading 8 fields on a 7-field packet
|
||||
// misaligns every item after the first and produces garbage prices.
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
const size_t bytesPerItemNoExt = 28;
|
||||
const size_t bytesPerItemWithExt = 32;
|
||||
bool hasExtendedCost = false;
|
||||
|
|
@ -4969,7 +4969,7 @@ bool ListInventoryParser::parse(network::Packet& packet, ListInventoryData& data
|
|||
data.items.reserve(itemCount);
|
||||
for (uint8_t i = 0; i < itemCount; ++i) {
|
||||
const size_t perItemBytes = hasExtendedCost ? bytesPerItemWithExt : bytesPerItemNoExt;
|
||||
if (packet.getSize() - packet.getReadPos() < perItemBytes) {
|
||||
if (packet.getRemainingSize() < perItemBytes) {
|
||||
LOG_WARNING("ListInventoryParser: item ", static_cast<int>(i), " truncated");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -4999,7 +4999,7 @@ bool TrainerListParser::parse(network::Packet& packet, TrainerListData& data, bo
|
|||
// Classic per-entry: spellId(4) + state(1) + cost(4) + reqLevel(1) +
|
||||
// reqSkill(4) + reqSkillValue(4) + chain×3(12) + unk(4) = 34 bytes
|
||||
data = TrainerListData{};
|
||||
if (packet.getSize() - packet.getReadPos() < 16) return false; // guid(8) + type(4) + count(4)
|
||||
if (packet.getRemainingSize() < 16) return false; // guid(8) + type(4) + count(4)
|
||||
|
||||
data.trainerGuid = packet.readUInt64();
|
||||
data.trainerType = packet.readUInt32();
|
||||
|
|
@ -5117,7 +5117,7 @@ bool TalentsInfoParser::parse(network::Packet& packet, TalentsInfoData& data) {
|
|||
data.talents.reserve(entryCount);
|
||||
|
||||
for (uint16_t i = 0; i < entryCount; ++i) {
|
||||
if (packet.getSize() - packet.getReadPos() < 5) {
|
||||
if (packet.getRemainingSize() < 5) {
|
||||
LOG_ERROR("SMSG_TALENTS_INFO: truncated entry list at i=", i);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -5129,7 +5129,7 @@ bool TalentsInfoParser::parse(network::Packet& packet, TalentsInfoData& data) {
|
|||
}
|
||||
|
||||
// Parse glyph tail: glyphSlots + glyphIds[]
|
||||
if (packet.getSize() - packet.getReadPos() < 1) {
|
||||
if (packet.getRemainingSize() < 1) {
|
||||
LOG_WARNING("SMSG_TALENTS_INFO: no glyph tail data");
|
||||
return true; // Not fatal, older formats may not have glyphs
|
||||
}
|
||||
|
|
@ -5148,7 +5148,7 @@ bool TalentsInfoParser::parse(network::Packet& packet, TalentsInfoData& data) {
|
|||
data.glyphs.reserve(glyphSlots);
|
||||
|
||||
for (uint8_t i = 0; i < glyphSlots; ++i) {
|
||||
if (packet.getSize() - packet.getReadPos() < 2) {
|
||||
if (packet.getRemainingSize() < 2) {
|
||||
LOG_ERROR("SMSG_TALENTS_INFO: truncated glyph list at i=", i);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -5160,7 +5160,7 @@ bool TalentsInfoParser::parse(network::Packet& packet, TalentsInfoData& data) {
|
|||
}
|
||||
|
||||
LOG_INFO("SMSG_TALENTS_INFO: bytesConsumed=", (packet.getReadPos() - startPos),
|
||||
" bytesRemaining=", (packet.getSize() - packet.getReadPos()));
|
||||
" bytesRemaining=", (packet.getRemainingSize()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -5221,7 +5221,7 @@ network::Packet ResurrectResponsePacket::build(uint64_t casterGuid, bool accept)
|
|||
|
||||
bool ShowTaxiNodesParser::parse(network::Packet& packet, ShowTaxiNodesData& data) {
|
||||
// Minimum: windowInfo(4) + npcGuid(8) + nearestNode(4) + at least 1 mask uint32(4)
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
if (remaining < 4 + 8 + 4 + 4) {
|
||||
LOG_ERROR("ShowTaxiNodesParser: packet too short (", remaining, " bytes)");
|
||||
return false;
|
||||
|
|
@ -5230,7 +5230,7 @@ bool ShowTaxiNodesParser::parse(network::Packet& packet, ShowTaxiNodesData& data
|
|||
data.npcGuid = packet.readUInt64();
|
||||
data.nearestNode = packet.readUInt32();
|
||||
// Read as many mask uint32s as available (Classic/Vanilla=4, WotLK=12)
|
||||
size_t maskBytes = packet.getSize() - packet.getReadPos();
|
||||
size_t maskBytes = packet.getRemainingSize();
|
||||
uint32_t maskCount = static_cast<uint32_t>(maskBytes / 4);
|
||||
if (maskCount > TLK_TAXI_MASK_SIZE) maskCount = TLK_TAXI_MASK_SIZE;
|
||||
for (uint32_t i = 0; i < maskCount; ++i) {
|
||||
|
|
@ -5242,7 +5242,7 @@ bool ShowTaxiNodesParser::parse(network::Packet& packet, ShowTaxiNodesData& data
|
|||
}
|
||||
|
||||
bool ActivateTaxiReplyParser::parse(network::Packet& packet, ActivateTaxiReplyData& data) {
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
if (remaining >= 4) {
|
||||
data.result = packet.readUInt32();
|
||||
} else if (remaining >= 1) {
|
||||
|
|
@ -5350,7 +5350,7 @@ network::Packet MailMarkAsReadPacket::build(uint64_t mailboxGuid, uint32_t mailI
|
|||
// PacketParsers::parseMailList — WotLK 3.3.5a format (base/default)
|
||||
// ============================================================================
|
||||
bool PacketParsers::parseMailList(network::Packet& packet, std::vector<MailMessage>& inbox) {
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
if (remaining < 5) return false;
|
||||
|
||||
uint32_t totalCount = packet.readUInt32();
|
||||
|
|
@ -5363,7 +5363,7 @@ bool PacketParsers::parseMailList(network::Packet& packet, std::vector<MailMessa
|
|||
inbox.reserve(shownCount);
|
||||
|
||||
for (uint8_t i = 0; i < shownCount; ++i) {
|
||||
remaining = packet.getSize() - packet.getReadPos();
|
||||
remaining = packet.getRemainingSize();
|
||||
if (remaining < 2) break;
|
||||
|
||||
uint16_t msgSize = packet.readUInt16();
|
||||
|
|
@ -5546,7 +5546,7 @@ network::Packet GuildBankSwapItemsPacket::buildInventoryToBank(
|
|||
}
|
||||
|
||||
bool GuildBankListParser::parse(network::Packet& packet, GuildBankData& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 14) return false;
|
||||
if (packet.getRemainingSize() < 14) return false;
|
||||
|
||||
data.money = packet.readUInt64();
|
||||
data.tabId = packet.readUInt8();
|
||||
|
|
@ -5648,7 +5648,7 @@ network::Packet AuctionHelloPacket::build(uint64_t guid) {
|
|||
}
|
||||
|
||||
bool AuctionHelloParser::parse(network::Packet& packet, AuctionHelloData& data) {
|
||||
size_t remaining = packet.getSize() - packet.getReadPos();
|
||||
size_t remaining = packet.getRemainingSize();
|
||||
if (remaining < 12) {
|
||||
LOG_WARNING("AuctionHelloParser: too small, remaining=", remaining);
|
||||
return false;
|
||||
|
|
@ -5748,7 +5748,7 @@ bool AuctionListResultParser::parse(network::Packet& packet, AuctionListResult&
|
|||
// bidderGuid(8) + curBid(4)
|
||||
// Classic: numEnchantSlots=1 → 80 bytes/entry
|
||||
// TBC/WotLK: numEnchantSlots=3 → 104 bytes/entry
|
||||
if (packet.getSize() - packet.getReadPos() < 4) return false;
|
||||
if (packet.getRemainingSize() < 4) return false;
|
||||
|
||||
uint32_t count = packet.readUInt32();
|
||||
// Cap auction count to prevent unbounded memory allocation
|
||||
|
|
@ -5792,7 +5792,7 @@ bool AuctionListResultParser::parse(network::Packet& packet, AuctionListResult&
|
|||
data.auctions.push_back(e);
|
||||
}
|
||||
|
||||
if (packet.getSize() - packet.getReadPos() >= 8) {
|
||||
if (packet.getRemainingSize() >= 8) {
|
||||
data.totalCount = packet.readUInt32();
|
||||
data.searchDelay = packet.readUInt32();
|
||||
}
|
||||
|
|
@ -5800,7 +5800,7 @@ bool AuctionListResultParser::parse(network::Packet& packet, AuctionListResult&
|
|||
}
|
||||
|
||||
bool AuctionCommandResultParser::parse(network::Packet& packet, AuctionCommandResult& data) {
|
||||
if (packet.getSize() - packet.getReadPos() < 12) return false;
|
||||
if (packet.getRemainingSize() < 12) return false;
|
||||
data.auctionId = packet.readUInt32();
|
||||
data.action = packet.readUInt32();
|
||||
data.errorCode = packet.readUInt32();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue