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:
Kelsi 2026-03-25 12:42:56 -07:00
parent b66033c6d8
commit 376d0a0f77
5 changed files with 657 additions and 656 deletions

View file

@ -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();