// 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 #include #else #include #include #endif #include "emu_opcodes.h" #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: // 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(); // 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(); // 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; } // 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; // 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: // 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; 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); // 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); // 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; } // 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: // 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: // 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() { 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; } 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: // 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 { 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.release(); } catch (const std::bad_alloc& ba) { // Memory allocation failed - return nullptr return nullptr; } } // Opcode management void SetOpcodeSize(uint8 s) { app_opcode_size = s; } void SetOpcode(EmuOpcode op); const EmuOpcode GetOpcodeConst() const; // 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: // Cached emulator opcode to avoid repeated lookups EmuOpcode emu_opcode; private: // 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); void DumpPacket(const EQApplicationPacket* app, bool iShowInfo = false); void DumpPacketBin(const EQApplicationPacket* app); #endif