// 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 "Log.h" using namespace std; // Global opcode manager map extern map EQOpcodeManager; // 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 = nullptr; this->size = 0; version = 0; 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); } } } /** * 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) { return EQOpcodeManager[OpcodeVersion]->EmuToName(login_op); } return nullptr; } /** * 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); // 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)); return -1; } int8 offset = 0; // 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 type if (login_opcode >= 255) { new_size += sizeof(int16); // oversized opcode needs extra bytes oversized = true; } 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); // skip sequence field if (login_opcode != 2) { if (oversized) { 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 { 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; size = new_size; return offset; } /** * 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 { // Write opcode (2 bytes, handling both 8-bit and 16-bit opcodes) if (opcode > 0xff) { *(uint16*)dest = opcode; } else { *(dest) = 0; *(dest + 1) = opcode; } // Copy packet data after the opcode memcpy(dest + 2, pBuffer + offset, size - offset); return size + 2; } /** * 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) { // 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 { *(uint16*)dest = opcode; } } // Copy packet data after opcode memcpy(dest + app_opcode_size, pBuffer, size); return size + OpCodeBytes; } /** * EQPacket destructor - cleans up allocated buffer memory. */ EQPacket::~EQPacket() { safe_delete_array(pBuffer); 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 { // 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); } /** * Get the human-readable name of this packet's opcode. * * @return Opcode name string, or nullptr if not found */ const char* EQPacket::GetOpcodeName() { int16 OpcodeVersion = GetOpcodeVersion(version); if (EQOpcodeManager.count(OpcodeVersion) > 0) { return EQOpcodeManager[OpcodeVersion]->EQToName(opcode); } return nullptr; } /** * 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) { dump_message_column(pBuffer, size, " ", to); } fprintf(to, "\n"); } /** * 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) { // Use provided opcode override opcode = in_opcode; } else { // Parse opcode from buffer (first 2 bytes) if (len < 2 || buf == nullptr) { // Insufficient data - set safe defaults opcode = 0; offset = len; // consume entire buffer } else { offset = 2; opcode = ntohs(*(const uint16*)buf); } } // Allocate and copy payload data after opcode 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; } // Initialize protocol packet state version = 0; eq2_compressed = false; packet_prepared = false; packet_encrypted = false; sent_time = 0; attempt_count = 0; sequence = 0; } /** * 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 = nullptr; bool over_sized_packet = false; int32 new_size = 0; // 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 } tmpbuffer = new uchar[new_size]; uchar* ptr = tmpbuffer; // Copy existing combined packet data memcpy(ptr, pBuffer, size); ptr += size; // 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); } // 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; safe_delete(rhs); 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; bool over_sized_packet2 = false; // 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 } // 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 } tmpbuffer = new uchar[new_size]; // 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); } 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); } memcpy(ptr, rhs->pBuffer + 2, rhs->size - 2); // Replace buffer and update opcode delete[] pBuffer; size = new_size; pBuffer = tmpbuffer; opcode = OP_AppCombined; safe_delete(rhs); result = true; } return result; } /** * 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; // 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 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; // Extract opcode based on size if (app_opcode_size == 1) { opcode = *(const unsigned char*)buf; offset++; } else { opcode = *(const uint16*)buf; offset += 2; } // 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 = nullptr; size = 0; } emu_opcode = OP_Unknown; } /** * 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) { // Application packet combining is not implemented // Use protocol-level combining instead return false; } /** * 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); } // Cache the emulator opcode for future lookups emu_opcode = emu_op; } /** * 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; } // 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; } /** * 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 { 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)); #ifdef EQN_DEBUG if (packet_crc && comp_crc != packet_crc) { 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; } /** * 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; // 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 } // 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; // 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; } // 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 { // No compression - direct copy memcpy(newbuf, buffer, length); newlen = length; } return newlen; } /** * 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]; } // 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 { // 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; } return newlength; } /** * 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) { // 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; // Decode 4-byte blocks with rolling key int i; 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; } // Decode remaining bytes with last key byte unsigned char KC = Key & 0xFF; for (; i < size; i++) { test[i] = buffer[i] ^ KC; } // Copy decoded data back memcpy(buffer, test, size); free(test); } } /** * 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); // Check against known protocol opcodes 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; 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); } /** * Dump protocol packet data in hexadecimal format. * * @param app - Protocol packet to dump */ void DumpPacket(const EQProtocolPacket* app) { DumpPacketHex(app->pBuffer, app->size); } /** * 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 << " size:" << app->size << endl; } DumpPacketHex(app->pBuffer, app->size); // ASCII dump is commented out but available: // DumpPacketAscii(app->pBuffer, app->size); } /** * Dump application packet data in binary format. * * @param app - Application packet to dump */ void DumpPacketBin(const EQApplicationPacket* app) { DumpPacketBin(app->pBuffer, app->size); }