eq2go/old/common/packet/eq_packet.hpp
2025-08-06 19:00:30 -05:00

891 lines
24 KiB
C++

// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3+ License
#pragma once
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <string>
#include <cstring>
#include <map>
#include <sys/time.h>
#include <netinet/in.h>
#include <memory>
#include "packet_dump.hpp"
#include "../log.hpp"
#include "../misc.hpp"
#include "../debug.hpp"
#include "../types.hpp"
#include "../crc16.hpp"
#include "../opcodes/opcodes.hpp"
#include "../opcodes/emu_opcodes.hpp"
#include "../opcodes/opcode_manager.hpp"
using namespace std;
extern map<int16,OpcodeManager*>EQOpcodeManager;
class OpcodeManager;
class EQStream;
// Base packet class for all EverQuest protocol packets
class EQPacket
{
friend class EQStream;
public:
unsigned char *pBuffer; // Raw packet data buffer
uint32 size; // Size of packet data
uint32 src_ip, dst_ip; // Source and destination IP addresses
uint16 src_port, dst_port; // Source and destination ports
uint32 priority; // Packet priority for queuing
timeval timestamp; // Timestamp when packet was created/received
int16 version; // Protocol version
// Destructor - cleans up allocated buffer
~EQPacket()
{
safe_delete_array(pBuffer);
pBuffer = nullptr;
}
// Dumps packet header with timestamp information to file
void DumpRawHeader(uint16 seq = 0xffff, FILE* to = stdout) const
{
DumpRawHeaderNoTime(seq, to);
}
// Dumps packet header without timestamp information to file
void DumpRawHeaderNoTime(uint16 seq = 0xffff, FILE* to = stdout) const
{
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);
}
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);
}
// Dumps complete packet with header and data in hex format
void DumpRaw(FILE* to = stdout) const
{
DumpRawHeader();
if (pBuffer && size)
dump_message_column(pBuffer, size, " ", to);
fprintf(to, "\n");
}
// Gets human-readable name for packet opcode
const char* GetOpcodeName()
{
int16 OpcodeVersion = GetOpcodeVersion(version);
if (EQOpcodeManager.count(OpcodeVersion) > 0)
return EQOpcodeManager[OpcodeVersion]->EQToName(opcode);
else
return nullptr;
}
// Sets protocol version for this packet
void setVersion(int16 new_version) { version = new_version; }
// Sets source IP and port information
void setSrcInfo(uint32 sip, uint16 sport) { src_ip = sip; src_port = sport; }
// Sets destination IP and port information
void setDstInfo(uint32 dip, uint16 dport) { dst_ip = dip; dst_port = dport; }
// Sets timestamp information for packet timing
void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { timestamp.tv_sec = ts_sec; timestamp.tv_usec = ts_usec; }
// Copies connection 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;
}
// Returns total packet size including opcode header
uint32 Size() const { return size + 2; }
// Gets raw opcode value without translation
uint16 GetRawOpcode() const { return opcode; }
// Comparison operator for timestamp-based sorting
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));
}
// Sets the protocol-level opcode
void SetProtocolOpcode(int16 new_opcode) { opcode = new_opcode; }
protected:
uint16 opcode; // Packet opcode identifier
// Constructor for creating packet with opcode and data buffer
EQPacket(const uint16 op, const unsigned char* buf, uint32 len)
{
opcode = op;
pBuffer = nullptr;
size = 0;
version = 0;
setTimeInfo(0, 0);
if (len > 0) {
size = len;
pBuffer = new unsigned char[size];
if (buf) {
memcpy(pBuffer, buf, size);
} else {
memset(pBuffer, 0, size);
}
}
}
// Copy constructor - disabled to prevent accidental copies
EQPacket(const EQPacket& p) { version = 0; }
// Default constructor
EQPacket()
{
opcode = 0;
pBuffer = nullptr;
size = 0;
version = 0;
setTimeInfo(0, 0);
}
};
class EQApplicationPacket;
// Protocol-level packet handling EverQuest network protocol specifics
class EQProtocolPacket : public EQPacket
{
public:
bool eq2_compressed; // Flag indicating if packet is compressed
bool packet_prepared; // Flag indicating if packet has been prepared for sending
bool packet_encrypted; // Flag indicating if packet is encrypted
bool acked; // Flag indicating if packet has been acknowledged
int32 sent_time; // Timestamp when packet was sent
int8 attempt_count; // Number of send attempts for this packet
int32 sequence; // Sequence number for ordering
// Constructor with opcode and data buffer
EQProtocolPacket(uint16 op, const unsigned char* buf, uint32 len) : EQPacket(op, buf, len)
{
eq2_compressed = false;
packet_prepared = false;
packet_encrypted = false;
sequence = 0;
sent_time = 0;
attempt_count = 0;
acked = false;
}
// Constructor from raw buffer with optional opcode override
EQProtocolPacket(const unsigned char* buf, uint32 len, int in_opcode = -1)
{
uint32 offset = 0;
if (in_opcode >= 0) {
opcode = in_opcode;
} else {
// Ensure there are at least 2 bytes for the opcode
if (len < 2 || buf == nullptr) {
opcode = 0;
offset = len;
} else {
offset = 2;
opcode = ntohs(*(const uint16*)buf);
}
}
// Check that there is payload data after the header
if (len > offset) {
size = len - offset;
pBuffer = new unsigned char[size];
if (buf)
memcpy(pBuffer, buf + offset, size);
else
memset(pBuffer, 0, size);
} else {
pBuffer = nullptr;
size = 0;
}
version = 0;
eq2_compressed = false;
packet_prepared = false;
packet_encrypted = false;
sent_time = 0;
attempt_count = 0;
sequence = 0;
}
// Attempts to combine this packet with another protocol packet for efficiency
bool combine(const EQProtocolPacket* rhs)
{
bool result = false;
// Check if this is already a combined packet and we can add more
if (opcode == OP_Combined && size + rhs->size + 5 < 256) {
auto tmpbuffer = new unsigned char[size + rhs->size + 3];
memcpy(tmpbuffer, pBuffer, size);
uint32 offset = size;
tmpbuffer[offset++] = rhs->Size();
offset += rhs->serialize(tmpbuffer + offset);
size = offset;
delete[] pBuffer;
pBuffer = tmpbuffer;
result = true;
}
// Check if we can create a new combined packet from two individual packets
else if (size + rhs->size + 7 < 256) {
auto tmpbuffer = new unsigned char[size + rhs->size + 6];
uint32 offset = 0;
tmpbuffer[offset++] = Size();
offset += serialize(tmpbuffer + offset);
tmpbuffer[offset++] = rhs->Size();
offset += rhs->serialize(tmpbuffer + offset);
size = offset;
delete[] pBuffer;
pBuffer = tmpbuffer;
opcode = OP_Combined;
result = true;
}
return result;
}
// Serializes packet data to destination buffer with optional offset
uint32 serialize(unsigned char* dest, int8 offset = 0) const
{
if (opcode > 0xff) {
*(uint16*)dest = opcode;
} else {
*(dest) = 0;
*(dest + 1) = opcode;
}
memcpy(dest + 2, pBuffer + offset, size - offset);
return size + 2;
}
// Creates a deep copy of this protocol packet
EQProtocolPacket* Copy()
{
EQProtocolPacket* 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;
}
// Converts protocol packet to application packet for higher-level processing
EQApplicationPacket* MakeApplicationPacket(uint8 opcode_size = 0) const;
// Validates CRC checksum on received packet data
static bool ValidateCRC(const unsigned char* buffer, int length, uint32 Key)
{
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) {
valid = true;
} else {
uint16 comp_crc = CRC16(buffer, length - 2, Key);
uint16 packet_crc = ntohs(*(const uint16*)(buffer + length - 2));
valid = (!packet_crc || comp_crc == packet_crc);
}
return valid;
}
// Decompresses packet data using zlib inflation
static uint32 Decompress(const unsigned char* buffer, const 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;
}
if (length > 2 && buffer[flag_offset] == 0x5a) {
LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 1");
newlen = Inflate(const_cast<unsigned char*>(buffer + flag_offset + 1), length - (flag_offset + 1) - 2, newbuf + flag_offset, newbufsize - flag_offset) + 2;
// something went bad with zlib
if (newlen == -1) {
LogWrite(PACKET__ERROR, 0, "Packet", "Debug Bad Inflate!");
DumpPacket(buffer, length);
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;
} else {
memcpy(newbuf, buffer, length);
newlen = length;
}
return newlen;
}
// Compresses packet data using zlib deflation
static uint32 Compress(const unsigned char* buffer, const uint32 length, unsigned char* newbuf, uint32 newbufsize)
{
uint32 flag_offset = 1, newlength;
newbuf[0] = buffer[0];
if (buffer[0] == 0) {
flag_offset = 2;
newbuf[1] = buffer[1];
}
if (length > 30) {
newlength = Deflate(const_cast<unsigned char*>(buffer + flag_offset), length - flag_offset, newbuf + flag_offset + 1, newbufsize);
*(newbuf + flag_offset) = 0x5a;
newlength += flag_offset + 1;
} else {
memmove(newbuf + flag_offset + 1, buffer + flag_offset, length - flag_offset);
*(newbuf + flag_offset) = 0xa5;
newlength = length + 1;
}
return newlength;
}
// Decodes chat message encryption
static void 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;
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);
}
}
// Encodes chat message with encryption
static void 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);
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;
}
unsigned char KC = Key & 0xFF;
for (; i < size; i++) {
test[i] = buffer[i] ^ KC;
}
memcpy(buffer, test, size);
free(test);
}
}
// Determines if buffer contains a valid protocol packet
static bool IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC)
{
bool ret = false;
uint16_t opcode = ntohs(*(uint16_t*)in_buff);
switch (opcode) {
case OP_SessionRequest:
case OP_SessionDisconnect:
case OP_KeepAlive:
case OP_SessionStatResponse:
case OP_Packet:
case OP_Combined:
case OP_Fragment:
case OP_Ack:
case OP_OutOfOrderAck:
case OP_OutOfSession:
ret = true;
break;
}
return ret;
}
private:
// Copy constructor disabled to prevent accidental copies
EQProtocolPacket(const EQProtocolPacket& p) {}
};
// EverQuest 2 specific packet handling with login opcodes
class EQ2Packet : public EQProtocolPacket
{
public:
EmuOpcode login_op; // EmuOpcode for this packet
// Constructor with login opcode and data buffer
EQ2Packet(const 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;
}
// Attempts to combine this EQ2 packet with another for transmission efficiency
bool AppCombine(EQ2Packet* rhs)
{
bool result = false;
uchar* tmpbuffer = nullptr;
bool over_sized_packet = false;
int32 new_size = 0;
// If this is already a combined packet and we can add more
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;
over_sized_packet = true;
} else {
new_size = size + tmp_size + 1;
}
tmpbuffer = new uchar[new_size];
uchar* ptr = tmpbuffer;
memcpy(ptr, pBuffer, size);
ptr += size;
if (over_sized_packet) {
memset(ptr, 255, sizeof(int8));
ptr += sizeof(int8);
tmp_size = htons(tmp_size);
memcpy(ptr, &tmp_size, sizeof(int16));
ptr += sizeof(int16);
} else {
memcpy(ptr, &tmp_size, sizeof(int8));
ptr += sizeof(int8);
}
memcpy(ptr, rhs->pBuffer + 2, rhs->size - 2);
delete[] pBuffer;
size = new_size;
pBuffer = tmpbuffer;
safe_delete(rhs);
result = true;
}
// Create a new combined packet from two individual packets
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;
over_sized_packet = true;
} else {
new_size += 3;
}
if (tmp_size2 >= 255) {
new_size += tmp_size2 + 3;
over_sized_packet2 = true;
} 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);
tmp_size = htons(tmp_size);
memcpy(ptr, &tmp_size, sizeof(int16));
ptr += sizeof(int16);
} 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);
tmp_size2 = htons(tmp_size2);
memcpy(ptr, &tmp_size2, sizeof(int16));
ptr += sizeof(int16);
} else {
memcpy(ptr, &tmp_size2, sizeof(int8));
ptr += sizeof(int8);
}
memcpy(ptr, rhs->pBuffer + 2, rhs->size - 2);
size = new_size;
delete[] pBuffer;
pBuffer = tmpbuffer;
safe_delete(rhs);
result = true;
}
return result;
}
// Creates a deep copy of this EQ2 packet
EQ2Packet* Copy()
{
EQ2Packet* 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;
}
// Prepares packet for transmission by adding headers and opcodes
int8 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);
return -1;
}
packet_prepared = true;
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));
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)
int32 new_size = size + sizeof(int16) + sizeof(int8);
bool oversized = false;
if (login_opcode != 2) {
new_size += sizeof(int8); // for opcode
if (login_opcode >= 255) {
new_size += sizeof(int16);
oversized = true;
} else {
login_opcode = ntohs(login_opcode);
}
}
uchar* new_buffer = new uchar[new_size];
memset(new_buffer, 0, new_size);
uchar* ptr = new_buffer + sizeof(int16); // sequence is first
if (login_opcode != 2) {
if (oversized) {
ptr += sizeof(int8); // compressed flag
int8 addon = 0xff;
memcpy(ptr, &addon, sizeof(int8));
ptr += sizeof(int8);
}
memcpy(ptr, &login_opcode, sizeof(int16));
ptr += sizeof(int16);
} else {
memcpy(ptr, &login_opcode, sizeof(int8));
ptr += sizeof(int8);
}
memcpy(ptr, pBuffer, size);
safe_delete_array(pBuffer);
pBuffer = new_buffer;
offset = new_size - size - 1;
size = new_size;
return offset;
}
// Gets human-readable name for the login opcode
const char* GetOpcodeName()
{
int16 OpcodeVersion = GetOpcodeVersion(version);
if (EQOpcodeManager.count(OpcodeVersion) > 0)
return EQOpcodeManager[OpcodeVersion]->EmuToName(login_op);
else
return nullptr;
}
};
// Application-level packet handling for game logic
class EQApplicationPacket : public EQPacket
{
friend class EQProtocolPacket;
friend class EQStream;
public:
static uint8 default_opcode_size; // Default size for opcodes in bytes
// Default constructor
EQApplicationPacket() : EQPacket(0, nullptr, 0)
{
emu_opcode = OP_Unknown;
app_opcode_size = default_opcode_size;
}
// Constructor with opcode only
EQApplicationPacket(const EmuOpcode op) : EQPacket(0, nullptr, 0)
{
SetOpcode(op);
app_opcode_size = default_opcode_size;
}
// Constructor with opcode and length
EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(0, nullptr, len)
{
SetOpcode(op);
app_opcode_size = default_opcode_size;
}
// Constructor with opcode, buffer and length
EQApplicationPacket(const EmuOpcode op, const unsigned char* buf, const uint32 len) : EQPacket(0, buf, len)
{
SetOpcode(op);
app_opcode_size = default_opcode_size;
}
// Attempts to combine this application packet with another (currently not implemented)
bool combine(const EQApplicationPacket* rhs)
{
cout << "CALLED AP COMBINE!!!!\n";
return false;
}
// Serializes application packet to destination buffer
uint32 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) {
*(uint8*)dest = 0;
*(uint16*)(dest + 1) = opcode;
++OpCodeBytes;
} else {
*(uint16*)dest = opcode;
}
}
memcpy(dest + app_opcode_size, pBuffer, size);
return size + OpCodeBytes;
}
// Returns total packet size including opcode
uint32 Size() const { return size + app_opcode_size; }
// Creates a deep copy of this application packet
EQApplicationPacket* Copy() const
{
EQApplicationPacket* it = new EQApplicationPacket;
try {
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;
}
catch (bad_alloc& ba) {
cout << ba.what() << endl;
if (it != nullptr)
delete it;
}
return nullptr;
}
// Sets the opcode size for this packet
void SetOpcodeSize(uint8 s) { app_opcode_size = s; }
// Sets the opcode for this packet using EmuOpcode
void SetOpcode(EmuOpcode emu_op)
{
if (emu_op == OP_Unknown) {
opcode = 0;
emu_opcode = OP_Unknown;
return;
}
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);
}
// save the emu opcode we just set.
emu_opcode = emu_op;
}
// Gets the constant opcode value for this packet
const EmuOpcode 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__);
}
return emu_op;
}
// Gets the opcode for this packet (const version)
inline const EmuOpcode GetOpcode() const { return GetOpcodeConst(); }
// Gets the opcode for this packet (caching version)
inline const EmuOpcode GetOpcode() { EmuOpcode r = GetOpcodeConst(); emu_opcode = r; return r; }
protected:
EmuOpcode emu_opcode; // Cached emu opcode to avoid repeated lookups
private:
uint8 app_opcode_size; // Size of opcode in bytes
// Constructor from raw buffer - used internally by EQProtocolPacket
EQApplicationPacket(const unsigned char* buf, uint32 len, uint8 opcode_size = 0)
{
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;
offset++;
} else {
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;
} else {
pBuffer = nullptr;
size = 0;
}
emu_opcode = OP_Unknown;
}
// Copy constructor disabled to prevent accidental copies
EQApplicationPacket(const EQApplicationPacket& p) { emu_opcode = OP_Unknown; app_opcode_size = default_opcode_size; }
};
// Implementation of MakeApplicationPacket that was forward declared
inline 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;
}
// Global packet dumping functions for debugging
// Dumps application packet data in hexadecimal format
void DumpPacketHex(const EQApplicationPacket* app)
{
DumpPacketHex(app->pBuffer, app->size);
}
// Dumps application packet data in ASCII format
void DumpPacketAscii(const EQApplicationPacket* app)
{
DumpPacketAscii(app->pBuffer, app->size);
}
// Dumps protocol packet data in hexadecimal format
void DumpPacket(const EQProtocolPacket* app)
{
DumpPacketHex(app->pBuffer, app->size);
}
// Dumps application packet with optional info display
void DumpPacket(const EQApplicationPacket* app, bool iShowInfo = false)
{
if (iShowInfo) {
cout << "Dumping Applayer: 0x" << hex << setfill('0') << setw(4) << app->GetOpcode() << dec;
cout << " size:" << app->size << endl;
}
DumpPacketHex(app->pBuffer, app->size);
}
// Dumps application packet data in binary format
void DumpPacketBin(const EQApplicationPacket* app)
{
DumpPacketBin(app->pBuffer, app->size);
}