From a8c533f012a5b14c17507736eb9eb246e7f64b54 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Sun, 31 Aug 2025 22:10:29 -0500 Subject: [PATCH] clean EQPacket --- source/common/EQPacket.cpp | 984 ++++++++++++++++++++++++------------- source/common/EQPacket.h | 319 ++++++++---- 2 files changed, 856 insertions(+), 447 deletions(-) diff --git a/source/common/EQPacket.cpp b/source/common/EQPacket.cpp index c2f9e1f..4fb0359 100644 --- a/source/common/EQPacket.cpp +++ b/source/common/EQPacket.cpp @@ -1,120 +1,139 @@ -/* - EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - - This file is part of EQ2Emulator. - - EQ2Emulator is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - EQ2Emulator is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with EQ2Emulator. If not, see . -*/ +// EQ2Emulator: Everquest II Server Emulator +// Copyright (C) 2007 EQ2EMulator Development Team +// Licensed under GPL v3 - see #include "debug.h" #include #include #include #include +#include +#include +#include + #include "EQPacket.h" #include "misc.h" #include "op_codes.h" #include "CRC16.h" #include "opcodemgr.h" #include "packet_dump.h" -#include #include "Log.h" -#include using namespace std; -extern mapEQOpcodeManager; -uint8 EQApplicationPacket::default_opcode_size=2; +// Global opcode manager map +extern map EQOpcodeManager; -EQPacket::EQPacket(const uint16 op, const unsigned char *buf, uint32 len) +// Default opcode size for application packets +uint8 EQApplicationPacket::default_opcode_size = 2; + +/** + * EQPacket constructor - creates a packet with specified opcode and data. + * + * @param op - The packet opcode + * @param buf - Source buffer to copy data from (can be nullptr) + * @param len - Length of data to allocate/copy + */ +EQPacket::EQPacket(const uint16 op, const unsigned char* buf, uint32 len) { - this->opcode=op; - this->pBuffer=NULL; - this->size=0; + this->opcode = op; + this->pBuffer = nullptr; + this->size = 0; version = 0; - setTimeInfo(0,0); - if (len>0) { - this->size=len; - pBuffer= new unsigned char[this->size]; + setTimeInfo(0, 0); + + if (len > 0) { + this->size = len; + pBuffer = new unsigned char[this->size]; if (buf) { - memcpy(this->pBuffer,buf,this->size); - } else { - memset(this->pBuffer,0,this->size); + memcpy(this->pBuffer, buf, this->size); + } else { + memset(this->pBuffer, 0, this->size); } } } -const char* EQ2Packet::GetOpcodeName() { +/** + * Get the human-readable name of this EQ2 packet's opcode. + * + * @return Opcode name string, or nullptr if not found + */ +const char* EQ2Packet::GetOpcodeName() +{ int16 OpcodeVersion = GetOpcodeVersion(version); - if (EQOpcodeManager.count(OpcodeVersion) > 0) + if (EQOpcodeManager.count(OpcodeVersion) > 0) { return EQOpcodeManager[OpcodeVersion]->EmuToName(login_op); - else - return NULL; + } + return nullptr; } -int8 EQ2Packet::PreparePacket(int16 MaxLen) { +/** + * Prepare an EQ2 packet for transmission by adding protocol headers. + * Converts the emulator opcode to network opcode and adds necessary headers. + * + * @param MaxLen - Maximum allowed packet length + * @return Offset value for the prepared packet, or -1 on error + */ +int8 EQ2Packet::PreparePacket(int16 MaxLen) +{ int16 OpcodeVersion = GetOpcodeVersion(version); - // stops a crash for incorrect version - if (EQOpcodeManager.count(OpcodeVersion) == 0) - { - LogWrite(PACKET__ERROR, 0, "Packet", "Version %i is not listed in the opcodes table.", version); + // Validate that we have an opcode manager for this version + if (EQOpcodeManager.count(OpcodeVersion) == 0) { + LogWrite(PACKET__ERROR, 0, "Packet", + "Version %i is not listed in the opcodes table.", version); return -1; } packet_prepared = true; + // Convert emulator opcode to network opcode int16 login_opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(login_op); - if (login_opcode == 0xcdcd) - { - LogWrite(PACKET__ERROR, 0, "Packet", "Version %i is not listed in the opcodes table for opcode %s", version, EQOpcodeManager[OpcodeVersion]->EmuToName(login_op)); + if (login_opcode == 0xcdcd) { + LogWrite(PACKET__ERROR, 0, "Packet", + "Version %i is not listed in the opcodes table for opcode %s", + version, EQOpcodeManager[OpcodeVersion]->EmuToName(login_op)); return -1; } - int16 orig_opcode = login_opcode; int8 offset = 0; - //one of the int16s is for the seq, other is for the EQ2 opcode and compressed flag (OP_Packet is the header, not the opcode) + // Calculate new size: sequence (int16) + compressed flag (int8) + opcode + data int32 new_size = size + sizeof(int16) + sizeof(int8); bool oversized = false; + + // Handle different opcode sizes and formats if (login_opcode != 2) { - new_size += sizeof(int8); //for opcode + new_size += sizeof(int8); // for opcode type if (login_opcode >= 255) { - new_size += sizeof(int16); + new_size += sizeof(int16); // oversized opcode needs extra bytes oversized = true; - } - else + } else { login_opcode = ntohs(login_opcode); + } } + + // Allocate new buffer and build the packet uchar* new_buffer = new uchar[new_size]; memset(new_buffer, 0, new_size); - uchar* ptr = new_buffer + sizeof(int16); // sequence is first + uchar* ptr = new_buffer + sizeof(int16); // skip sequence field + if (login_opcode != 2) { if (oversized) { - ptr += sizeof(int8); //compressed flag - int8 addon = 0xff; + ptr += sizeof(int8); // compressed flag position + int8 addon = 0xff; // oversized marker memcpy(ptr, &addon, sizeof(int8)); ptr += sizeof(int8); } memcpy(ptr, &login_opcode, sizeof(int16)); ptr += sizeof(int16); - } - else { + } else { memcpy(ptr, &login_opcode, sizeof(int8)); ptr += sizeof(int8); } + + // Copy original packet data memcpy(ptr, pBuffer, size); + // Replace old buffer with new prepared buffer safe_delete_array(pBuffer); pBuffer = new_buffer; offset = new_size - size - 1; @@ -123,129 +142,194 @@ int8 EQ2Packet::PreparePacket(int16 MaxLen) { return offset; } -uint32 EQProtocolPacket::serialize(unsigned char *dest, int8 offset) const +/** + * Serialize this protocol packet into a destination buffer. + * + * @param dest - Destination buffer to write to + * @param offset - Offset into source buffer to start copying from + * @return Total bytes written to destination + */ +uint32 EQProtocolPacket::serialize(unsigned char* dest, int8 offset) const { - if (opcode>0xff) { - *(uint16 *)dest=opcode; + // Write opcode (2 bytes, handling both 8-bit and 16-bit opcodes) + if (opcode > 0xff) { + *(uint16*)dest = opcode; } else { - *(dest)=0; - *(dest+1)=opcode; + *(dest) = 0; + *(dest + 1) = opcode; } - memcpy(dest+2,pBuffer+offset,size-offset); - - return size+2; + + // Copy packet data after the opcode + memcpy(dest + 2, pBuffer + offset, size - offset); + + return size + 2; } -uint32 EQApplicationPacket::serialize(unsigned char *dest) const +/** + * Serialize this application packet into a destination buffer. + * Handles special opcode encoding rules for application-level packets. + * + * @param dest - Destination buffer to write to + * @return Total bytes written to destination + */ +uint32 EQApplicationPacket::serialize(unsigned char* dest) const { uint8 OpCodeBytes = app_opcode_size; - if (app_opcode_size==1) - *(unsigned char *)dest=opcode; - else - { - // Application opcodes with a low order byte of 0x00 require an extra 0x00 byte inserting prior to the opcode. - if ((opcode & 0x00ff) == 0) - { + if (app_opcode_size == 1) { + // Single-byte opcode + *(unsigned char*)dest = opcode; + } else { + // Two-byte opcode with special encoding rules + // Application opcodes with low byte = 0x00 need extra 0x00 prefix + if ((opcode & 0x00ff) == 0) { *(uint8*)dest = 0; *(uint16*)(dest + 1) = opcode; ++OpCodeBytes; - } - else + } else { *(uint16*)dest = opcode; + } } - memcpy(dest+app_opcode_size,pBuffer,size); + // Copy packet data after opcode + memcpy(dest + app_opcode_size, pBuffer, size); - return size+ OpCodeBytes; + return size + OpCodeBytes; } +/** + * EQPacket destructor - cleans up allocated buffer memory. + */ EQPacket::~EQPacket() { safe_delete_array(pBuffer); - pBuffer=NULL; + pBuffer = nullptr; } - +/** + * Dump packet header with timestamp information to file. + * + * @param seq - Sequence number to display + * @param to - File pointer to write to (default: stdout) + */ void EQPacket::DumpRawHeader(uint16 seq, FILE* to) const { - /*if (timestamp.tv_sec) { + // Note: Timestamp formatting code is commented out but preserved + // for potential future use in debugging + /* + if (timestamp.tv_sec) { char temp[20]; tm t; const time_t sec = timestamp.tv_sec; localtime_s(&t, &sec); strftime(temp, 20, "%F %T", &t); fprintf(to, "%s.%06lu ", temp, timestamp.tv_usec); - }*/ + } + */ DumpRawHeaderNoTime(seq, to); } -const char* EQPacket::GetOpcodeName(){ - int16 OpcodeVersion = GetOpcodeVersion(version); - if(EQOpcodeManager.count(OpcodeVersion) > 0) - return EQOpcodeManager[OpcodeVersion]->EQToName(opcode); - else - return NULL; -} -void EQPacket::DumpRawHeaderNoTime(uint16 seq, FILE *to) const +/** + * Get the human-readable name of this packet's opcode. + * + * @return Opcode name string, or nullptr if not found + */ +const char* EQPacket::GetOpcodeName() { - if (src_ip) { - string sIP,dIP;; - sIP=long2ip(src_ip); - dIP=long2ip(dst_ip); - fprintf(to, "[%s:%d->%s:%d] ",sIP.c_str(),src_port,dIP.c_str(),dst_port); - } - if (seq != 0xffff) - fprintf(to, "[Seq=%u] ",seq); - - string name; int16 OpcodeVersion = GetOpcodeVersion(version); - if(EQOpcodeManager.count(OpcodeVersion) > 0) - name = EQOpcodeManager[OpcodeVersion]->EQToName(opcode); - - fprintf(to, "[OpCode 0x%04x (%s) Size=%u]\n",opcode,name.c_str(),size); + if (EQOpcodeManager.count(OpcodeVersion) > 0) { + return EQOpcodeManager[OpcodeVersion]->EQToName(opcode); + } + return nullptr; } -void EQPacket::DumpRaw(FILE *to) const +/** + * Dump packet header without timestamp to file. + * Shows network addresses, sequence number, opcode, and size. + * + * @param seq - Sequence number to display (0xffff = don't show) + * @param to - File pointer to write to + */ +void EQPacket::DumpRawHeaderNoTime(uint16 seq, FILE* to) const { + // Show network addressing if available + if (src_ip) { + string sIP = long2ip(src_ip); + string dIP = long2ip(dst_ip); + fprintf(to, "[%s:%d->%s:%d] ", sIP.c_str(), src_port, dIP.c_str(), dst_port); + } + + // Show sequence number if valid + if (seq != 0xffff) { + fprintf(to, "[Seq=%u] ", seq); + } + + // Get opcode name for display + string name; + int16 OpcodeVersion = GetOpcodeVersion(version); + if (EQOpcodeManager.count(OpcodeVersion) > 0) { + name = EQOpcodeManager[OpcodeVersion]->EQToName(opcode); + } + + fprintf(to, "[OpCode 0x%04x (%s) Size=%u]\n", opcode, name.c_str(), size); +} + +/** + * Dump complete packet (header + data) to file in formatted columns. + * + * @param to - File pointer to write to (default: stdout) + */ +void EQPacket::DumpRaw(FILE* to) const { DumpRawHeader(); - if (pBuffer && size) + if (pBuffer && size) { dump_message_column(pBuffer, size, " ", to); + } fprintf(to, "\n"); } -EQProtocolPacket::EQProtocolPacket(const unsigned char *buf, uint32 len, int in_opcode) +/** + * EQProtocolPacket constructor from raw buffer. + * Parses opcode from buffer or uses provided opcode override. + * + * @param buf - Raw packet buffer + * @param len - Length of buffer + * @param in_opcode - Optional opcode override (-1 = parse from buffer) + */ +EQProtocolPacket::EQProtocolPacket(const unsigned char* buf, uint32 len, int in_opcode) { uint32 offset = 0; - if(in_opcode>=0) { + + if (in_opcode >= 0) { + // Use provided opcode override opcode = in_opcode; - } - else { - // Ensure there are at least 2 bytes for the opcode + } else { + // Parse opcode from buffer (first 2 bytes) if (len < 2 || buf == nullptr) { - // Not enough data to read opcode; set defaults or handle error appropriately - opcode = 0; // or set to a designated invalid opcode - offset = len; // no payload available + // Insufficient data - set safe defaults + opcode = 0; + offset = len; // consume entire buffer } else { offset = 2; - opcode = ntohs(*(const uint16 *)buf); + opcode = ntohs(*(const uint16*)buf); } } - // Check that there is payload data after the header + // Allocate and copy payload data after opcode if (len > offset) { size = len - offset; pBuffer = new unsigned char[size]; - if(buf) + if (buf) { memcpy(pBuffer, buf + offset, size); - else + } else { memset(pBuffer, 0, size); + } } else { pBuffer = nullptr; size = 0; } + // Initialize protocol packet state version = 0; eq2_compressed = false; packet_prepared = false; @@ -255,362 +339,553 @@ EQProtocolPacket::EQProtocolPacket(const unsigned char *buf, uint32 len, int in_ sequence = 0; } -bool EQ2Packet::AppCombine(EQ2Packet* rhs){ +/** + * Combine this EQ2 packet with another EQ2 packet for efficient transmission. + * Implements EQ2's application-level packet combining protocol. + * + * @param rhs - Right-hand side packet to combine with this one + * @return true if packets were successfully combined, false otherwise + */ +bool EQ2Packet::AppCombine(EQ2Packet* rhs) +{ bool result = false; - uchar* tmpbuffer = 0; + uchar* tmpbuffer = nullptr; bool over_sized_packet = false; int32 new_size = 0; - //bool whee = false; -// DumpPacket(this); -// DumpPacket(rhs); - /*if(rhs->size >= 255){ - DumpPacket(this); - DumpPacket(rhs); - whee = true; - }*/ - if (opcode==OP_AppCombined && ((size + rhs->size + 3) < 255)){ - int16 tmp_size = rhs->size - 2; - if(tmp_size >= 255){ - new_size = size+tmp_size+3; + + // Case 1: This packet is already a combined packet + if (opcode == OP_AppCombined && ((size + rhs->size + 3) < 255)) { + int16 tmp_size = rhs->size - 2; // Subtract opcode bytes + + // Check if we need oversized packet encoding + if (tmp_size >= 255) { + new_size = size + tmp_size + 3; // Extra bytes for oversized encoding over_sized_packet = true; + } else { + new_size = size + tmp_size + 1; // One byte for size prefix } - else - new_size = size+tmp_size+1; + tmpbuffer = new uchar[new_size]; uchar* ptr = tmpbuffer; + + // Copy existing combined packet data memcpy(ptr, pBuffer, size); ptr += size; - if(over_sized_packet){ - memset(ptr, 255, sizeof(int8)); - ptr += sizeof(int8); + + // Add size information for the new packet + if (over_sized_packet) { + *ptr++ = 255; // Oversized marker tmp_size = htons(tmp_size); memcpy(ptr, &tmp_size, sizeof(int16)); ptr += sizeof(int16); + } else { + *ptr++ = static_cast(tmp_size); } - else{ - memcpy(ptr, &tmp_size, sizeof(int8)); - ptr += sizeof(int8); - } - memcpy(ptr, rhs->pBuffer+2, rhs->size-2); + + // Copy packet data (skip opcode) + memcpy(ptr, rhs->pBuffer + 2, rhs->size - 2); + + // Replace buffer and clean up delete[] pBuffer; size = new_size; - pBuffer=tmpbuffer; + pBuffer = tmpbuffer; safe_delete(rhs); - result=true; + result = true; } + // Case 2: Neither packet is combined - create new combined packet else if (rhs->size > 2 && size > 2 && (size + rhs->size + 6) < 255) { int32 tmp_size = size - 2; int32 tmp_size2 = rhs->size - 2; - opcode=OP_AppCombined; bool over_sized_packet2 = false; - new_size = size; - if(tmp_size >= 255){ - new_size += 5; + + // Calculate new size with headers + new_size = 4; // Base combined packet header + + // First packet size encoding + if (tmp_size >= 255) { + new_size += tmp_size + 3; // Oversized encoding over_sized_packet = true; + } else { + new_size += tmp_size + 1; // Normal encoding } - else - new_size += 3; - if(tmp_size2 >= 255){ - new_size += tmp_size2+3; + + // Second packet size encoding + if (tmp_size2 >= 255) { + new_size += tmp_size2 + 3; // Oversized encoding over_sized_packet2 = true; + } else { + new_size += tmp_size2 + 1; // Normal encoding } - else - new_size += tmp_size2+1; + tmpbuffer = new uchar[new_size]; - tmpbuffer[2]=0; - tmpbuffer[3]=0x19; - uchar* ptr = tmpbuffer+4; - if(over_sized_packet){ - memset(ptr, 255, sizeof(int8)); - ptr += sizeof(int8); + + // Set combined packet header + tmpbuffer[2] = 0; + tmpbuffer[3] = 0x19; // Combined packet marker + uchar* ptr = tmpbuffer + 4; + + // Add first packet + if (over_sized_packet) { + *ptr++ = 255; // Oversized marker tmp_size = htons(tmp_size); memcpy(ptr, &tmp_size, sizeof(int16)); ptr += sizeof(int16); + } else { + *ptr++ = static_cast(tmp_size); } - else{ - memcpy(ptr, &tmp_size, sizeof(int8)); - ptr += sizeof(int8); - } - memcpy(ptr, pBuffer+2, size-2); - ptr += (size-2); - if(over_sized_packet2){ - memset(ptr, 255, sizeof(int8)); - ptr += sizeof(int8); + memcpy(ptr, pBuffer + 2, size - 2); + ptr += (size - 2); + + // Add second packet + if (over_sized_packet2) { + *ptr++ = 255; // Oversized marker tmp_size2 = htons(tmp_size2); memcpy(ptr, &tmp_size2, sizeof(int16)); ptr += sizeof(int16); + } else { + *ptr++ = static_cast(tmp_size2); } - else{ - memcpy(ptr, &tmp_size2, sizeof(int8)); - ptr += sizeof(int8); - } - memcpy(ptr, rhs->pBuffer+2, rhs->size-2); - size = new_size; + memcpy(ptr, rhs->pBuffer + 2, rhs->size - 2); + + // Replace buffer and update opcode delete[] pBuffer; - pBuffer=tmpbuffer; + size = new_size; + pBuffer = tmpbuffer; + opcode = OP_AppCombined; safe_delete(rhs); - result=true; + result = true; } - /*if(whee){ - DumpPacket(this); - cout << "fsdfsdf"; - }*/ - //DumpPacket(this); + return result; } -bool EQProtocolPacket::combine(const EQProtocolPacket *rhs) +/** + * Combine this protocol packet with another protocol packet. + * Used for efficient transmission of multiple small packets together. + * + * @param rhs - Right-hand side packet to combine with this one + * @return true if packets were successfully combined, false otherwise + */ +bool EQProtocolPacket::combine(const EQProtocolPacket* rhs) { - bool result=false; - //if(dont_combine) - // return false; - //if (opcode==OP_Combined && size+rhs->size+5<256) { + bool result = false; + + // Case 1: This packet is already combined - append to it if (opcode == OP_Combined && size + rhs->size + 5 < 256) { auto tmpbuffer = new unsigned char[size + rhs->size + 3]; + + // Copy existing combined data memcpy(tmpbuffer, pBuffer, size); uint32 offset = size; + + // Add size prefix for new packet tmpbuffer[offset++] = rhs->Size(); + + // Serialize and append new packet offset += rhs->serialize(tmpbuffer + offset); + + // Update buffer size = offset; delete[] pBuffer; pBuffer = tmpbuffer; result = true; } + // Case 2: Neither packet is combined - create new combined packet else if (size + rhs->size + 7 < 256) { auto tmpbuffer = new unsigned char[size + rhs->size + 6]; uint32 offset = 0; + + // Add first packet with size prefix tmpbuffer[offset++] = Size(); offset += serialize(tmpbuffer + offset); + + // Add second packet with size prefix tmpbuffer[offset++] = rhs->Size(); offset += rhs->serialize(tmpbuffer + offset); + + // Update buffer and mark as combined size = offset; delete[] pBuffer; pBuffer = tmpbuffer; opcode = OP_Combined; result = true; } + return result; } -EQApplicationPacket::EQApplicationPacket(const unsigned char *buf, uint32 len, uint8 opcode_size) +/** + * EQApplicationPacket constructor from raw buffer. + * Used by EQProtocolPacket to create application packets from network data. + * + * @param buf - Raw packet buffer (starts with opcode) + * @param len - Length of buffer + * @param opcode_size - Size of opcode in bytes (0 = use default) + */ +EQApplicationPacket::EQApplicationPacket(const unsigned char* buf, uint32 len, uint8 opcode_size) { -uint32 offset=0; - app_opcode_size=(opcode_size==0) ? EQApplicationPacket::default_opcode_size : opcode_size; + uint32 offset = 0; + app_opcode_size = (opcode_size == 0) ? EQApplicationPacket::default_opcode_size : opcode_size; - if (app_opcode_size==1) { - opcode=*(const unsigned char *)buf; + // Extract opcode based on size + if (app_opcode_size == 1) { + opcode = *(const unsigned char*)buf; offset++; } else { - opcode=*(const uint16 *)buf; - offset+=2; + opcode = *(const uint16*)buf; + offset += 2; } - if ((len-offset)>0) { - pBuffer=new unsigned char[len-offset]; - memcpy(pBuffer,buf+offset,len-offset); - size=len-offset; + // Copy remaining data as payload + if ((len - offset) > 0) { + pBuffer = new unsigned char[len - offset]; + memcpy(pBuffer, buf + offset, len - offset); + size = len - offset; } else { - pBuffer=NULL; - size=0; + pBuffer = nullptr; + size = 0; } emu_opcode = OP_Unknown; } -bool EQApplicationPacket::combine(const EQApplicationPacket *rhs) +/** + * Combine this application packet with another application packet. + * Currently not implemented for application-level packets. + * + * @param rhs - Right-hand side packet to combine + * @return false (combining not supported at application level) + */ +bool EQApplicationPacket::combine(const EQApplicationPacket* rhs) { -cout << "CALLED AP COMBINE!!!!\n"; + // Application packet combining is not implemented + // Use protocol-level combining instead return false; } -void EQApplicationPacket::SetOpcode(EmuOpcode emu_op) { - if(emu_op == OP_Unknown) { +/** + * Set the opcode for this application packet. + * Converts emulator opcode to network protocol opcode. + * + * @param emu_op - Emulator opcode to set + */ +void EQApplicationPacket::SetOpcode(EmuOpcode emu_op) +{ + if (emu_op == OP_Unknown) { opcode = 0; emu_opcode = OP_Unknown; return; } + // Convert emulator opcode to network opcode opcode = EQOpcodeManager[GetOpcodeVersion(version)]->EmuToEQ(emu_op); - if(opcode == OP_Unknown) { - LogWrite(PACKET__DEBUG, 0, "Packet", "Unable to convert Emu opcode %s (%d) into an EQ opcode.", OpcodeNames[emu_op], emu_op); + if (opcode == OP_Unknown) { + LogWrite(PACKET__DEBUG, 0, "Packet", + "Unable to convert Emu opcode %s (%d) into an EQ opcode.", + OpcodeNames[emu_op], emu_op); } - //save the emu opcode we just set. + // Cache the emulator opcode for future lookups emu_opcode = emu_op; } -const EmuOpcode EQApplicationPacket::GetOpcodeConst() const { - if(emu_opcode != OP_Unknown) { - return(emu_opcode); - } - if(opcode == 10000) { - return(OP_Unknown); - } - - EmuOpcode emu_op; - emu_op = EQOpcodeManager[GetOpcodeVersion(version)]->EQToEmu(opcode); - if(emu_op == OP_Unknown) { - LogWrite(PACKET__DEBUG, 1, "Packet", "Unable to convert EQ opcode 0x%.4X (%i) to an emu opcode (%s)", opcode, opcode, __FUNCTION__); +/** + * Get the emulator opcode for this application packet. + * Converts network opcode to emulator opcode if not cached. + * + * @return Emulator opcode constant + */ +const EmuOpcode EQApplicationPacket::GetOpcodeConst() const +{ + // Return cached opcode if available + if (emu_opcode != OP_Unknown) { + return emu_opcode; } - return(emu_op); + // Handle special invalid opcode case + if (opcode == 10000) { + return OP_Unknown; + } + + // Convert network opcode to emulator opcode + EmuOpcode emu_op = EQOpcodeManager[GetOpcodeVersion(version)]->EQToEmu(opcode); + if (emu_op == OP_Unknown) { + LogWrite(PACKET__DEBUG, 1, "Packet", + "Unable to convert EQ opcode 0x%.4X (%i) to an emu opcode (%s)", + opcode, opcode, __FUNCTION__); + } + + return emu_op; } -EQApplicationPacket *EQProtocolPacket::MakeApplicationPacket(uint8 opcode_size) const { - EQApplicationPacket *res = new EQApplicationPacket; - res->app_opcode_size=(opcode_size==0) ? EQApplicationPacket::default_opcode_size : opcode_size; - if (res->app_opcode_size==1) { - res->pBuffer= new unsigned char[size+1]; - memcpy(res->pBuffer+1,pBuffer,size); - *(res->pBuffer)=htons(opcode)&0xff; - res->opcode=opcode&0xff; - res->size=size+1; - } else { - res->pBuffer= new unsigned char[size]; - memcpy(res->pBuffer,pBuffer,size); - res->opcode=opcode; - res->size=size; - } - res->copyInfo(this); - return(res); -} -bool EQProtocolPacket::ValidateCRC(const unsigned char *buffer, int length, uint32 Key) +/** + * Convert this protocol packet to an application packet. + * Handles opcode format conversion between protocol and application layers. + * + * @param opcode_size - Size of application opcode (0 = use default) + * @return New application packet, or nullptr on failure + */ +EQApplicationPacket* EQProtocolPacket::MakeApplicationPacket(uint8 opcode_size) const { -bool valid=false; - // OP_SessionRequest, OP_SessionResponse, OP_OutOfSession are not CRC'd - if (buffer[0]==0x00 && (buffer[1]==OP_SessionRequest || buffer[1]==OP_SessionResponse || buffer[1]==OP_OutOfSession)) { - valid=true; - } else if(buffer[2] == 0x00 && buffer[3] == 0x19){ + auto res = new EQApplicationPacket; + res->app_opcode_size = (opcode_size == 0) ? EQApplicationPacket::default_opcode_size : opcode_size; + + if (res->app_opcode_size == 1) { + // Single-byte opcode format + res->pBuffer = new unsigned char[size + 1]; + memcpy(res->pBuffer + 1, pBuffer, size); + *(res->pBuffer) = htons(opcode) & 0xff; + res->opcode = opcode & 0xff; + res->size = size + 1; + } else { + // Two-byte opcode format + res->pBuffer = new unsigned char[size]; + memcpy(res->pBuffer, pBuffer, size); + res->opcode = opcode; + res->size = size; + } + + // Copy network and timing information + res->copyInfo(this); + return res; +} + +/** + * Validate the CRC checksum of a network packet. + * Some packet types (session packets) are exempt from CRC validation. + * + * @param buffer - Packet buffer to validate + * @param length - Length of packet buffer + * @param Key - CRC key for validation + * @return true if CRC is valid or packet is exempt, false otherwise + */ +bool EQProtocolPacket::ValidateCRC(const unsigned char* buffer, int length, uint32 Key) +{ + bool valid = false; + + // Session packets are not CRC protected + if (buffer[0] == 0x00 && + (buffer[1] == OP_SessionRequest || + buffer[1] == OP_SessionResponse || + buffer[1] == OP_OutOfSession)) { valid = true; } + // Combined application packets are also exempt + else if (buffer[2] == 0x00 && buffer[3] == 0x19) { + valid = true; + } + // All other packets must have valid CRC else { - uint16 comp_crc=CRC16(buffer,length-2,Key); - uint16 packet_crc=ntohs(*(const uint16 *)(buffer+length-2)); + uint16 comp_crc = CRC16(buffer, length - 2, Key); + uint16 packet_crc = ntohs(*(const uint16*)(buffer + length - 2)); + #ifdef EQN_DEBUG if (packet_crc && comp_crc != packet_crc) { - cout << "CRC mismatch: comp=" << hex << comp_crc << ", packet=" << packet_crc << dec << endl; + cout << "CRC mismatch: comp=" << hex << comp_crc + << ", packet=" << packet_crc << dec << endl; } #endif + // Valid if no CRC present (packet_crc == 0) or CRCs match valid = (!packet_crc || comp_crc == packet_crc); } + return valid; } -uint32 EQProtocolPacket::Decompress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize) +/** + * Decompress a network packet using zlib or simple encoding. + * Supports both zlib compression (0x5a) and simple encoding (0xa5). + * + * @param buffer - Compressed packet buffer + * @param length - Length of compressed buffer + * @param newbuf - Destination buffer for decompressed data + * @param newbufsize - Size of destination buffer + * @return Length of decompressed data + */ +uint32 EQProtocolPacket::Decompress(const unsigned char* buffer, uint32 length, unsigned char* newbuf, uint32 newbufsize) { -uint32 newlen=0; -uint32 flag_offset=0; - newbuf[0]=buffer[0]; - if (buffer[0]==0x00) { - flag_offset=2; - newbuf[1]=buffer[1]; - } else - flag_offset=1; + uint32 newlen = 0; + uint32 flag_offset = 0; + + // Copy opcode header + newbuf[0] = buffer[0]; + if (buffer[0] == 0x00) { + flag_offset = 2; // Two-byte opcode + newbuf[1] = buffer[1]; + } else { + flag_offset = 1; // One-byte opcode + } - if (length>2 && buffer[flag_offset]==0x5a) { - LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 1"); - newlen=Inflate(const_cast(buffer+flag_offset+1),length-(flag_offset+1)-2,newbuf+flag_offset,newbufsize-flag_offset)+2; + // Check compression type + if (length > 2 && buffer[flag_offset] == 0x5a) { + // Zlib compression + LogWrite(PACKET__DEBUG, 0, "Packet", "Decompressing zlib packet"); + newlen = Inflate(const_cast(buffer + flag_offset + 1), + length - (flag_offset + 1) - 2, // Subtract CRC bytes + newbuf + flag_offset, + newbufsize - flag_offset) + flag_offset; - // something went bad with zlib - if (newlen == -1) - { - LogWrite(PACKET__ERROR, 0, "Packet", "Debug Bad Inflate!"); + // Handle decompression failure + if (newlen == static_cast(-1)) { + LogWrite(PACKET__ERROR, 0, "Packet", "Zlib decompression failed!"); DumpPacket(buffer, length); + // Fallback: copy original buffer memcpy(newbuf, buffer, length); return length; } - newbuf[newlen++]=buffer[length-2]; - newbuf[newlen++]=buffer[length-1]; - } else if (length>2 && buffer[flag_offset]==0xa5) { - LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 2"); - memcpy(newbuf+flag_offset,buffer+flag_offset+1,length-(flag_offset+1)); - newlen=length-1; + // Copy CRC bytes to end + newbuf[newlen++] = buffer[length - 2]; + newbuf[newlen++] = buffer[length - 1]; + } else if (length > 2 && buffer[flag_offset] == 0xa5) { + // Simple encoding - just remove the encoding flag + LogWrite(PACKET__DEBUG, 0, "Packet", "Decompressing simple encoded packet"); + memcpy(newbuf + flag_offset, buffer + flag_offset + 1, length - (flag_offset + 1)); + newlen = length - 1; } else { - memcpy(newbuf,buffer,length); - newlen=length; + // No compression - direct copy + memcpy(newbuf, buffer, length); + newlen = length; } return newlen; } -uint32 EQProtocolPacket::Compress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize) { -uint32 flag_offset=1,newlength; - //dump_message_column(buffer,length,"Before: "); - newbuf[0]=buffer[0]; - if (buffer[0]==0) { - flag_offset=2; - newbuf[1]=buffer[1]; +/** + * Compress a network packet using zlib or simple encoding. + * Uses zlib for packets > 30 bytes, simple encoding for smaller packets. + * + * @param buffer - Source packet buffer + * @param length - Length of source buffer + * @param newbuf - Destination buffer for compressed data + * @param newbufsize - Size of destination buffer + * @return Length of compressed data + */ +uint32 EQProtocolPacket::Compress(const unsigned char* buffer, uint32 length, unsigned char* newbuf, uint32 newbufsize) +{ + uint32 flag_offset = 1; + uint32 newlength; + + // Copy opcode header + newbuf[0] = buffer[0]; + if (buffer[0] == 0) { + flag_offset = 2; // Two-byte opcode + newbuf[1] = buffer[1]; } - if (length>30) { - newlength=Deflate(const_cast(buffer+flag_offset),length-flag_offset,newbuf+flag_offset+1,newbufsize); - *(newbuf+flag_offset)=0x5a; - newlength+=flag_offset+1; + + // Choose compression method based on packet size + if (length > 30) { + // Use zlib compression for larger packets + newlength = Deflate(const_cast(buffer + flag_offset), + length - flag_offset, + newbuf + flag_offset + 1, + newbufsize); + *(newbuf + flag_offset) = 0x5a; // Zlib compression flag + newlength += flag_offset + 1; } else { - memmove(newbuf+flag_offset+1,buffer+flag_offset,length-flag_offset); - *(newbuf+flag_offset)=0xa5; - newlength=length+1; + // Use simple encoding for smaller packets + memmove(newbuf + flag_offset + 1, buffer + flag_offset, length - flag_offset); + *(newbuf + flag_offset) = 0xa5; // Simple encoding flag + newlength = length + 1; } - //dump_message_column(newbuf,length,"After: "); return newlength; } -void EQProtocolPacket::ChatDecode(unsigned char *buffer, int size, int DecodeKey) +/** + * Decode chat packet data using XOR encryption. + * Uses a rolling XOR key that updates with each 4-byte block. + * + * @param buffer - Buffer containing chat data to decode + * @param size - Size of buffer + * @param DecodeKey - Initial decoding key + */ +void EQProtocolPacket::ChatDecode(unsigned char* buffer, int size, int DecodeKey) { - if (buffer[1]!=0x01 && buffer[0]!=0x02 && buffer[0]!=0x1d) { - int Key=DecodeKey; - unsigned char *test=(unsigned char *)malloc(size); - buffer+=2; - size-=2; + // Skip decoding for certain packet types + if (buffer[1] != 0x01 && buffer[0] != 0x02 && buffer[0] != 0x1d) { + int Key = DecodeKey; + auto test = static_cast(malloc(size)); + + // Skip the first 2 bytes (opcode) + buffer += 2; + size -= 2; - int i; - for (i = 0 ; i+4 <= size ; i+=4) - { - int pt = (*(int*)&buffer[i])^(Key); - Key = (*(int*)&buffer[i]); - *(int*)&test[i]=pt; - } - unsigned char KC=Key&0xFF; - for ( ; i < size ; i++) - { - test[i]=buffer[i]^KC; - } - memcpy(buffer,test,size); - free(test); - } -} - -void EQProtocolPacket::ChatEncode(unsigned char *buffer, int size, int EncodeKey) -{ - if (buffer[1]!=0x01 && buffer[0]!=0x02 && buffer[0]!=0x1d) { - int Key=EncodeKey; - char *test=(char*)malloc(size); + // Decode 4-byte blocks with rolling key int i; - buffer+=2; - size-=2; - for ( i = 0 ; i+4 <= size ; i+=4) - { - int pt = (*(int*)&buffer[i])^(Key); - Key = pt; - *(int*)&test[i]=pt; + for (i = 0; i + 4 <= size; i += 4) { + int pt = (*(int*)&buffer[i]) ^ Key; + Key = (*(int*)&buffer[i]); // Update key with encrypted data + *(int*)&test[i] = pt; } - unsigned char KC=Key&0xFF; - for ( ; i < size ; i++) - { - test[i]=buffer[i]^KC; + + // Decode remaining bytes with last key byte + unsigned char KC = Key & 0xFF; + for (; i < size; i++) { + test[i] = buffer[i] ^ KC; } - memcpy(buffer,test,size); + + // Copy decoded data back + memcpy(buffer, test, size); free(test); } } -bool EQProtocolPacket::IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC) { +/** + * Encode chat packet data using XOR encryption. + * Uses a rolling XOR key that updates with each encrypted 4-byte block. + * + * @param buffer - Buffer containing chat data to encode + * @param size - Size of buffer + * @param EncodeKey - Initial encoding key + */ +void EQProtocolPacket::ChatEncode(unsigned char* buffer, int size, int EncodeKey) +{ + // Skip encoding for certain packet types + if (buffer[1] != 0x01 && buffer[0] != 0x02 && buffer[0] != 0x1d) { + int Key = EncodeKey; + auto test = static_cast(malloc(size)); + + // Skip the first 2 bytes (opcode) + buffer += 2; + size -= 2; + + // Encode 4-byte blocks with rolling key + int i; + for (i = 0; i + 4 <= size; i += 4) { + int pt = (*(int*)&buffer[i]) ^ Key; + Key = pt; // Update key with encrypted data + *(int*)&test[i] = pt; + } + + // Encode remaining bytes with last key byte + unsigned char KC = Key & 0xFF; + for (; i < size; i++) { + test[i] = buffer[i] ^ KC; + } + + // Copy encoded data back + memcpy(buffer, test, size); + free(test); + } +} + +/** + * Check if a buffer contains a valid EverQuest protocol packet. + * Validates the opcode against known protocol opcodes. + * + * @param in_buff - Input buffer to check + * @param len - Length of input buffer + * @param bTrimCRC - Whether CRC should be trimmed (unused) + * @return true if buffer contains a valid protocol packet + */ +bool EQProtocolPacket::IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC) +{ bool ret = false; uint16_t opcode = ntohs(*(uint16_t*)in_buff); - uint32_t offset = 2; + // Check against known protocol opcodes switch (opcode) { case OP_SessionRequest: case OP_SessionDisconnect: @@ -622,39 +897,72 @@ bool EQProtocolPacket::IsProtocolPacket(const unsigned char* in_buff, uint32_t l case OP_Ack: case OP_OutOfOrderAck: case OP_OutOfSession: - { - ret = true; - break; - } + ret = true; + break; + default: + // Unknown opcode - not a protocol packet + ret = false; + break; } return ret; } - - +/** + * Dump application packet data in hexadecimal format. + * + * @param app - Application packet to dump + */ void DumpPacketHex(const EQApplicationPacket* app) { DumpPacketHex(app->pBuffer, app->size); } +/** + * Dump application packet data in ASCII format. + * + * @param app - Application packet to dump + */ void DumpPacketAscii(const EQApplicationPacket* app) { DumpPacketAscii(app->pBuffer, app->size); } -void DumpPacket(const EQProtocolPacket* app) { + +/** + * Dump protocol packet data in hexadecimal format. + * + * @param app - Protocol packet to dump + */ +void DumpPacket(const EQProtocolPacket* app) +{ DumpPacketHex(app->pBuffer, app->size); } -void DumpPacket(const EQApplicationPacket* app, bool iShowInfo) { + +/** + * Dump application packet with optional header information. + * + * @param app - Application packet to dump + * @param iShowInfo - Whether to show packet information header + */ +void DumpPacket(const EQApplicationPacket* app, bool iShowInfo) +{ if (iShowInfo) { - cout << "Dumping Applayer: 0x" << hex << setfill('0') << setw(4) << app->GetOpcode() << dec; + cout << "Dumping Applayer: 0x" << hex << setfill('0') << setw(4) + << app->GetOpcode() << dec; cout << " size:" << app->size << endl; } DumpPacketHex(app->pBuffer, app->size); -// DumpPacketAscii(app->pBuffer, app->size); + // ASCII dump is commented out but available: + // DumpPacketAscii(app->pBuffer, app->size); } -void DumpPacketBin(const EQApplicationPacket* app) { +/** + * Dump application packet data in binary format. + * + * @param app - Application packet to dump + */ +void DumpPacketBin(const EQApplicationPacket* app) +{ DumpPacketBin(app->pBuffer, app->size); } diff --git a/source/common/EQPacket.h b/source/common/EQPacket.h index 455a72c..b1b308c 100644 --- a/source/common/EQPacket.h +++ b/source/common/EQPacket.h @@ -1,28 +1,13 @@ -/* - EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - - This file is part of EQ2Emulator. - - EQ2Emulator is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - EQ2Emulator is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with EQ2Emulator. If not, see . -*/ +// EQ2Emulator: Everquest II Server Emulator +// Copyright (C) 2007 EQ2EMulator Development Team +// Licensed under GPL v3 - see #ifndef _EQPACKET_H #define _EQPACKET_H #include "types.h" #include #include +#include #ifdef WIN32 #include @@ -36,59 +21,103 @@ #include "op_codes.h" #include "packet_dump.h" +// Forward declarations class OpcodeManager; - class EQStream; +/** + * Base class for all EverQuest packet types. + * Provides common functionality for packet management, opcode handling, + * and network information tracking. + */ class EQPacket { friend class EQStream; + public: - unsigned char *pBuffer; - uint32 size; - uint32 src_ip,dst_ip; - uint16 src_port,dst_port; - uint32 priority; - timeval timestamp; - int16 version; + // Packet data + unsigned char* pBuffer; // Raw packet data buffer + uint32 size; // Size of packet data + + // Network information + uint32 src_ip, dst_ip; // Source and destination IP addresses + uint16 src_port, dst_port; // Source and destination ports + uint32 priority; // Packet priority for processing + timeval timestamp; // Packet timestamp + int16 version; // Protocol version + ~EQPacket(); - void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; - void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; - void DumpRaw(FILE *to = stdout) const; + + // Debug and display methods + void DumpRawHeader(uint16 seq = 0xffff, FILE* to = stdout) const; + void DumpRawHeaderNoTime(uint16 seq = 0xffff, FILE* to = stdout) const; + void DumpRaw(FILE* to = stdout) const; const char* GetOpcodeName(); - void setVersion(int16 new_version){ version = new_version; } - void setSrcInfo(uint32 sip, uint16 sport) { src_ip=sip; src_port=sport; } - void setDstInfo(uint32 dip, uint16 dport) { dst_ip=dip; dst_port=dport; } - void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { timestamp.tv_sec=ts_sec; timestamp.tv_usec=ts_usec; } - void copyInfo(const EQPacket *p) { src_ip=p->src_ip; src_port=p->src_port; dst_ip=p->dst_ip; dst_port=p->dst_port; timestamp.tv_sec=p->timestamp.tv_sec; timestamp.tv_usec=p->timestamp.tv_usec; } - uint32 Size() const { return size+2; } - -//no reason to have this method in zone or world - - uint16 GetRawOpcode() const { return(opcode); } - - - inline bool operator<(const EQPacket &rhs) { - return (timestamp.tv_sec < rhs.timestamp.tv_sec || (timestamp.tv_sec==rhs.timestamp.tv_sec && timestamp.tv_usec < rhs.timestamp.tv_usec)); + // Packet information setters + void setVersion(int16 new_version) { version = new_version; } + void setSrcInfo(uint32 sip, uint16 sport) { src_ip = sip; src_port = sport; } + void setDstInfo(uint32 dip, uint16 dport) { dst_ip = dip; dst_port = dport; } + void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { + timestamp.tv_sec = ts_sec; + timestamp.tv_usec = ts_usec; } - void SetProtocolOpcode(int16 new_opcode){ + + // Copy network and timing info from another packet + void copyInfo(const EQPacket* p) { + src_ip = p->src_ip; + src_port = p->src_port; + dst_ip = p->dst_ip; + dst_port = p->dst_port; + timestamp.tv_sec = p->timestamp.tv_sec; + timestamp.tv_usec = p->timestamp.tv_usec; + } + + // Get total packet size including opcode + uint32 Size() const { return size + 2; } + + // Get raw opcode value + uint16 GetRawOpcode() const { return opcode; } + + // Comparison operator for timestamp-based sorting + bool operator<(const EQPacket& rhs) const { + return (timestamp.tv_sec < rhs.timestamp.tv_sec || + (timestamp.tv_sec == rhs.timestamp.tv_sec && + timestamp.tv_usec < rhs.timestamp.tv_usec)); + } + + // Set the protocol opcode + void SetProtocolOpcode(int16 new_opcode) { opcode = new_opcode; } protected: - uint16 opcode; - - EQPacket(const uint16 op, const unsigned char *buf, const uint32 len); - EQPacket(const EQPacket &p) { version = 0; } - EQPacket() { opcode=0; pBuffer=NULL; size=0; version = 0; setTimeInfo(0, 0); } - + uint16 opcode; // Packet opcode + + // Constructors (protected to enforce proper inheritance) + EQPacket(uint16 op, const unsigned char* buf, uint32 len); + EQPacket(const EQPacket& p) { version = 0; } + EQPacket() { + opcode = 0; + pBuffer = nullptr; + size = 0; + version = 0; + setTimeInfo(0, 0); + } }; +// Forward declaration class EQApplicationPacket; +/** + * Protocol-level packet class for EverQuest network communication. + * Handles low-level protocol features like compression, encryption, + * sequencing, and packet combining. + */ class EQProtocolPacket : public EQPacket { public: - EQProtocolPacket(uint16 op, const unsigned char *buf, uint32 len) : EQPacket(op,buf,len) { + // Constructor with opcode and buffer + EQProtocolPacket(uint16 op, const unsigned char* buf, uint32 len) + : EQPacket(op, buf, len) { eq2_compressed = false; packet_prepared = false; packet_encrypted = false; @@ -96,110 +125,182 @@ public: sent_time = 0; attempt_count = 0; acked = false; - } - EQProtocolPacket(const unsigned char *buf, uint32 len, int in_opcode = -1); - bool combine(const EQProtocolPacket *rhs); - uint32 serialize (unsigned char *dest, int8 offset = 0) const; - static bool ValidateCRC(const unsigned char *buffer, int length, uint32 Key); - static uint32 Decompress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize); - static uint32 Compress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize); - static void ChatDecode(unsigned char *buffer, int size, int DecodeKey); - static void ChatEncode(unsigned char *buffer, int size, int EncodeKey); + } + + // Constructor from raw buffer with optional opcode override + EQProtocolPacket(const unsigned char* buf, uint32 len, int in_opcode = -1); + + // Packet manipulation methods + bool combine(const EQProtocolPacket* rhs); + uint32 serialize(unsigned char* dest, int8 offset = 0) const; + + // Static utility methods for packet processing + static bool ValidateCRC(const unsigned char* buffer, int length, uint32 Key); + static uint32 Decompress(const unsigned char* buffer, uint32 length, + unsigned char* newbuf, uint32 newbufsize); + static uint32 Compress(const unsigned char* buffer, uint32 length, + unsigned char* newbuf, uint32 newbufsize); + static void ChatDecode(unsigned char* buffer, int size, int DecodeKey); + static void ChatEncode(unsigned char* buffer, int size, int EncodeKey); static bool IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC); - - EQProtocolPacket *Copy() { - EQProtocolPacket* new_packet = new EQProtocolPacket(opcode,pBuffer,size); + + // Create a copy of this packet + EQProtocolPacket* Copy() { + auto new_packet = new EQProtocolPacket(opcode, pBuffer, size); new_packet->eq2_compressed = this->eq2_compressed; new_packet->packet_prepared = this->packet_prepared; new_packet->packet_encrypted = this->packet_encrypted; return new_packet; } - EQApplicationPacket *MakeApplicationPacket(uint8 opcode_size=0) const; - bool eq2_compressed; - bool packet_prepared; - bool packet_encrypted; - bool acked; - int32 sent_time; - int8 attempt_count; - int32 sequence; + + // Convert to application-level packet + EQApplicationPacket* MakeApplicationPacket(uint8 opcode_size = 0) const; + + // Protocol packet state flags + bool eq2_compressed; // Packet is compressed + bool packet_prepared; // Packet has been prepared for sending + bool packet_encrypted; // Packet is encrypted + bool acked; // Packet has been acknowledged + + // Reliability and sequencing + int32 sent_time; // Timestamp when packet was sent + int8 attempt_count; // Number of send attempts + int32 sequence; // Sequence number for ordering private: - EQProtocolPacket(const EQProtocolPacket &p) { } - //bool dont_combine; + // Prevent copy construction + EQProtocolPacket(const EQProtocolPacket& p) = delete; + EQProtocolPacket& operator=(const EQProtocolPacket& p) = delete; }; + +/** + * EverQuest 2 specific packet class. + * Handles EQ2-specific packet operations like application combining + * and login opcode management. + */ class EQ2Packet : public EQProtocolPacket { public: - EQ2Packet(const EmuOpcode in_login_op, const unsigned char *buf, uint32 len) : EQProtocolPacket(OP_Packet,buf,len){ + // Constructor with EQ2 login opcode + EQ2Packet(EmuOpcode in_login_op, const unsigned char* buf, uint32 len) + : EQProtocolPacket(OP_Packet, buf, len) { login_op = in_login_op; eq2_compressed = false; packet_prepared = false; packet_encrypted = false; } + + // EQ2-specific packet operations bool AppCombine(EQ2Packet* rhs); + int8 PreparePacket(int16 MaxLen); + const char* GetOpcodeName(); + + // Create a copy of this EQ2 packet EQ2Packet* Copy() { - EQ2Packet* new_packet = new EQ2Packet(login_op,pBuffer,size); + auto new_packet = new EQ2Packet(login_op, pBuffer, size); new_packet->eq2_compressed = this->eq2_compressed; new_packet->packet_prepared = this->packet_prepared; new_packet->packet_encrypted = this->packet_encrypted; return new_packet; } - int8 PreparePacket(int16 MaxLen); - const char* GetOpcodeName(); - EmuOpcode login_op; + + EmuOpcode login_op; // EQ2 login/application opcode }; + +/** + * Application-level packet class for EverQuest. + * Handles high-level game opcodes and application data. + * This is the main packet type used by game logic. + */ class EQApplicationPacket : public EQPacket { friend class EQProtocolPacket; friend class EQStream; + public: - EQApplicationPacket() : EQPacket(0,NULL,0) { emu_opcode = OP_Unknown; app_opcode_size=default_opcode_size; } - EQApplicationPacket(const EmuOpcode op) : EQPacket(0,NULL,0) { SetOpcode(op); app_opcode_size=default_opcode_size; } - EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(0,NULL,len) { SetOpcode(op); app_opcode_size=default_opcode_size; } - EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(0,buf,len) { SetOpcode(op); app_opcode_size=default_opcode_size; } - bool combine(const EQApplicationPacket *rhs); - uint32 serialize (unsigned char *dest) const; - uint32 Size() const { return size+app_opcode_size; } - EQApplicationPacket *Copy() const { - EQApplicationPacket *it = new EQApplicationPacket; + // Default constructor + EQApplicationPacket() : EQPacket(0, nullptr, 0) { + emu_opcode = OP_Unknown; + app_opcode_size = default_opcode_size; + } + + // Constructor with opcode only + EQApplicationPacket(EmuOpcode op) : EQPacket(0, nullptr, 0) { + SetOpcode(op); + app_opcode_size = default_opcode_size; + } + + // Constructor with opcode and size + EQApplicationPacket(EmuOpcode op, uint32 len) : EQPacket(0, nullptr, len) { + SetOpcode(op); + app_opcode_size = default_opcode_size; + } + + // Constructor with opcode, buffer, and size + EQApplicationPacket(EmuOpcode op, const unsigned char* buf, uint32 len) + : EQPacket(0, buf, len) { + SetOpcode(op); + app_opcode_size = default_opcode_size; + } + + // Packet operations + bool combine(const EQApplicationPacket* rhs); + uint32 serialize(unsigned char* dest) const; + uint32 Size() const { return size + app_opcode_size; } + + // Create a deep copy of this packet + EQApplicationPacket* Copy() const { + auto it = std::make_unique(); try { - it->pBuffer= new unsigned char[size]; - memcpy(it->pBuffer,pBuffer,size); - it->size=size; + if (size > 0) { + it->pBuffer = new unsigned char[size]; + memcpy(it->pBuffer, pBuffer, size); + } + it->size = size; it->opcode = opcode; it->emu_opcode = emu_opcode; it->version = version; - return(it); + return it.release(); } - catch( bad_alloc &ba ) - { - cout << ba.what() << endl; - if( NULL != it ) - delete it; + catch (const std::bad_alloc& ba) { + // Memory allocation failed - return nullptr + return nullptr; } - return NULL; } - void SetOpcodeSize(uint8 s) { app_opcode_size=s; } + // Opcode management + void SetOpcodeSize(uint8 s) { app_opcode_size = s; } void SetOpcode(EmuOpcode op); const EmuOpcode GetOpcodeConst() const; - inline const EmuOpcode GetOpcode() const { return(GetOpcodeConst()); } - //caching version of get - inline const EmuOpcode GetOpcode() { EmuOpcode r = GetOpcodeConst(); emu_opcode = r; return(r); } - + + // Get opcode (const version) + const EmuOpcode GetOpcode() const { return GetOpcodeConst(); } + + // Get opcode (caching version for performance) + EmuOpcode GetOpcode() { + EmuOpcode r = GetOpcodeConst(); + emu_opcode = r; + return r; + } + + // Default opcode size for all application packets static uint8 default_opcode_size; protected: - //this is just a cache so we dont look it up several times on Get() + // Cached emulator opcode to avoid repeated lookups EmuOpcode emu_opcode; private: - //this constructor should only be used by EQProtocolPacket, as it - //assumes the first two bytes of buf are the opcode. - EQApplicationPacket(const unsigned char *buf, uint32 len, uint8 opcode_size=0); - EQApplicationPacket(const EQApplicationPacket &p) { emu_opcode = OP_Unknown; app_opcode_size=default_opcode_size; } - + // Constructor used by EQProtocolPacket - assumes first bytes are opcode + EQApplicationPacket(const unsigned char* buf, uint32 len, uint8 opcode_size = 0); + + // Prevent copy construction + EQApplicationPacket(const EQApplicationPacket& p) = delete; + EQApplicationPacket& operator=(const EQApplicationPacket& p) = delete; + + // Size of the opcode in bytes (1 or 2) uint8 app_opcode_size; }; +// Packet debugging and display functions void DumpPacketHex(const EQApplicationPacket* app); void DumpPacket(const EQProtocolPacket* app); void DumpPacketAscii(const EQApplicationPacket* app);