package eq2net import ( "bytes" "compress/zlib" "encoding/binary" "fmt" "git.sharkk.net/EQ2/Protocol/crypto" ) // ProtocolPacket handles low-level protocol operations including // compression, encryption, sequencing, and packet combining type ProtocolPacket struct { EQPacket Compressed bool Encrypted bool PacketPrepared bool Sequence uint16 Acknowledged bool SentTime int32 AttemptCount int8 SubPackets []*ProtocolPacket } // NewProtocolPacket creates a new protocol packet func NewProtocolPacket(opcode uint16, data []byte) *ProtocolPacket { p := &ProtocolPacket{ EQPacket: *NewEQPacket(opcode, data), } return p } // ParseProtocolPacket creates a protocol packet from raw network data func ParseProtocolPacket(data []byte) (*ProtocolPacket, error) { base, err := ParsePacket(data) if err != nil { return nil, err } p := &ProtocolPacket{ EQPacket: *base, } // Check for compression flag if len(p.Buffer) > 0 { switch p.Buffer[0] { case 0x5a: // Zlib compressed p.Compressed = true case 0xa5: // Simple encoding p.Compressed = true } } return p, nil } // Serialize writes the protocol packet to a byte buffer with optional offset func (p *ProtocolPacket) SerializeWithOffset(offset int8) []byte { opcodeSize := 2 // Protocol packets always use 2-byte opcodes buf := make([]byte, opcodeSize+len(p.Buffer)-int(offset)) if p.Opcode > 0xff { binary.BigEndian.PutUint16(buf[0:2], p.Opcode) } else { buf[0] = 0x00 buf[1] = byte(p.Opcode) } if len(p.Buffer) > int(offset) { copy(buf[opcodeSize:], p.Buffer[offset:]) } return buf } // ValidateCRC checks if the packet has a valid CRC // Returns true if CRC is valid or packet is exempt func (p *ProtocolPacket) ValidateCRC(key uint32) bool { // Session control packets are exempt from CRC if p.Opcode == OP_SessionRequest || p.Opcode == OP_SessionResponse || p.Opcode == OP_OutOfSession { return true } // Need full packet data including opcode for CRC check fullData := p.Serialize() if len(fullData) < 2 { return false } // CRC is in last 2 bytes if len(fullData) < 4 { // Minimum: 2 byte opcode + 2 byte CRC return false } dataLen := len(fullData) - 2 calculatedCRC := crypto.CalculateCRC(fullData[:dataLen], key) packetCRC := binary.BigEndian.Uint16(fullData[dataLen:]) // CRC of 0 means no CRC check required return packetCRC == 0 || calculatedCRC == packetCRC } // Compress compresses the packet data using zlib or simple encoding func (p *ProtocolPacket) Compress() error { if p.Compressed { return fmt.Errorf("packet already compressed") } // Only compress packets larger than 30 bytes if len(p.Buffer) > 30 { compressed, err := p.zlibCompress() if err != nil { return err } p.Buffer = compressed p.Compressed = true } else if len(p.Buffer) > 0 { // Simple encoding for small packets p.simpleEncode() p.Compressed = true } return nil } // Decompress decompresses the packet data func (p *ProtocolPacket) Decompress() error { if !p.Compressed { return fmt.Errorf("packet not compressed") } if len(p.Buffer) == 0 { return fmt.Errorf("no data to decompress") } var decompressed []byte var err error switch p.Buffer[0] { case 0x5a: // Zlib compressed decompressed, err = p.zlibDecompress() if err != nil { return err } case 0xa5: // Simple encoding decompressed = p.simpleDecoded() default: return fmt.Errorf("unknown compression flag: 0x%02x", p.Buffer[0]) } p.Buffer = decompressed p.Compressed = false return nil } // zlibCompress performs zlib compression func (p *ProtocolPacket) zlibCompress() ([]byte, error) { var compressed bytes.Buffer // Write compression flag compressed.WriteByte(0x5a) // Compress the data w := zlib.NewWriter(&compressed) _, err := w.Write(p.Buffer) if err != nil { return nil, err } err = w.Close() if err != nil { return nil, err } return compressed.Bytes(), nil } // zlibDecompress performs zlib decompression func (p *ProtocolPacket) zlibDecompress() ([]byte, error) { if len(p.Buffer) < 2 { return nil, fmt.Errorf("compressed data too small") } // Skip compression flag r, err := zlib.NewReader(bytes.NewReader(p.Buffer[1:])) if err != nil { return nil, err } defer r.Close() var decompressed bytes.Buffer _, err = decompressed.ReadFrom(r) if err != nil { return nil, err } return decompressed.Bytes(), nil } // simpleEncode adds simple encoding flag func (p *ProtocolPacket) simpleEncode() { // Add encoding flag at the beginning newBuffer := make([]byte, len(p.Buffer)+1) newBuffer[0] = 0xa5 copy(newBuffer[1:], p.Buffer) p.Buffer = newBuffer } // simpleDecoded removes simple encoding flag func (p *ProtocolPacket) simpleDecoded() []byte { if len(p.Buffer) > 1 { return p.Buffer[1:] } return []byte{} } // Combine bundles multiple protocol packets into a single combined packet func (p *ProtocolPacket) Combine(other *ProtocolPacket) bool { // Check if this is already a combined packet if p.Opcode != OP_Combined { // Convert to combined packet firstPacket := p.Clone() p.Opcode = OP_Combined p.SubPackets = []*ProtocolPacket{ {EQPacket: *firstPacket}, } p.Buffer = p.buildCombinedBuffer() } // Check size limit (max 255 bytes for combined packets) totalSize := len(p.Buffer) + int(other.TotalSize()) + 1 // +1 for size byte if totalSize > 255 { return false } // Add the new packet p.SubPackets = append(p.SubPackets, other) p.Buffer = p.buildCombinedBuffer() return true } // buildCombinedBuffer creates the buffer for a combined packet func (p *ProtocolPacket) buildCombinedBuffer() []byte { var buf bytes.Buffer for _, subPacket := range p.SubPackets { serialized := subPacket.Serialize() buf.WriteByte(byte(len(serialized))) // Size byte buf.Write(serialized) } return buf.Bytes() } // ExtractSubPackets extracts individual packets from a combined packet func (p *ProtocolPacket) ExtractSubPackets() ([]*ProtocolPacket, error) { if p.Opcode != OP_Combined { return nil, fmt.Errorf("not a combined packet") } var packets []*ProtocolPacket offset := 0 data := p.Buffer for offset < len(data) { // Read size byte if offset >= len(data) { break } size := int(data[offset]) offset++ // Extract sub-packet data if offset+size > len(data) { return nil, fmt.Errorf("invalid combined packet: size exceeds buffer") } subData := data[offset : offset+size] offset += size // Parse sub-packet subPacket, err := ParseProtocolPacket(subData) if err != nil { return nil, fmt.Errorf("failed to parse sub-packet: %w", err) } packets = append(packets, subPacket) } return packets, nil } // MakeApplicationPacket converts this protocol packet to an application packet // This is used when a protocol packet contains application-level data func (p *ProtocolPacket) MakeApplicationPacket(opcodeSize uint8) *AppPacket { if len(p.Buffer) < int(opcodeSize) { return nil } // Extract the application opcode from the buffer var appOpcode uint16 if opcodeSize == 1 { appOpcode = uint16(p.Buffer[0]) } else { appOpcode = binary.BigEndian.Uint16(p.Buffer[0:2]) } // Create application packet with remaining data app := &AppPacket{ EQPacket: *NewEQPacket(appOpcode, p.Buffer[opcodeSize:]), OpcodeSize: opcodeSize, } // Copy network info app.SrcIP = p.SrcIP app.SrcPort = p.SrcPort app.DstIP = p.DstIP app.DstPort = p.DstPort app.Timestamp = p.Timestamp app.Version = p.Version return app } // ChatEncode applies XOR-based chat encryption func (p *ProtocolPacket) ChatEncode(key uint32) { if len(p.Buffer) <= 2 { return // Skip opcode bytes } data := p.Buffer[2:] // Skip first 2 bytes (opcode) keyBytes := make([]byte, 4) binary.LittleEndian.PutUint32(keyBytes, key) // Process 4-byte blocks with rolling key i := 0 for ; i+4 <= len(data); i += 4 { // XOR with current key block := binary.LittleEndian.Uint32(data[i : i+4]) encrypted := block ^ key binary.LittleEndian.PutUint32(data[i:i+4], encrypted) // Update key with encrypted data key = encrypted } // Handle remaining bytes with last key byte keyByte := byte(key & 0xFF) for ; i < len(data); i++ { data[i] ^= keyByte } } // ChatDecode applies XOR-based chat decryption (same as encode due to XOR properties) func (p *ProtocolPacket) ChatDecode(key uint32) { p.ChatEncode(key) // XOR encryption is symmetric }