package packets import ( "encoding/binary" "fmt" "eq2emu/internal/opcodes" ) type ProtocolHandler struct { pool *PacketPool } func NewProtocolHandler(pool *PacketPool) *ProtocolHandler { return &ProtocolHandler{pool: pool} } // Parse EQ2 protocol packet from gnet data func (h *ProtocolHandler) ParsePacket(data []byte) (*EQ2Packet, error) { if len(data) < 1 { return nil, fmt.Errorf("packet too small") } packet := h.pool.GetEQ2Packet() packet.Opcode = opcodes.ProtocolOpcode(data[0]) if len(data) > 1 { packet.Data = make([]byte, len(data)-1) copy(packet.Data, data[1:]) } // Extract sequence if present if packet.Opcode == opcodes.OP_Packet || packet.Opcode == opcodes.OP_Combined { if len(packet.Data) >= 2 { packet.Sequence = binary.LittleEndian.Uint16(packet.Data[:2]) } } return packet, nil } // Extract application packets from protocol packet func (h *ProtocolHandler) ExtractAppPackets(packet *EQ2Packet, session *Session) ([]*AppPacket, error) { switch packet.Opcode { case opcodes.OP_Packet: return h.extractSingleApp(packet.Data) case opcodes.OP_Combined: return h.extractCombinedApps(packet.Data) case opcodes.OP_Fragment: return h.handleFragment(packet, session) default: return nil, nil } } func (h *ProtocolHandler) extractSingleApp(data []byte) ([]*AppPacket, error) { if len(data) < 4 { // seq(2) + opcode(2) return nil, fmt.Errorf("packet too small") } offset := 2 // Skip sequence // Check compression flag compressed := false if data[offset] == 0xFF { compressed = true offset++ } if len(data) < offset+2 { return nil, fmt.Errorf("insufficient data") } opcode := binary.LittleEndian.Uint16(data[offset:]) payload := data[offset+2:] app := h.pool.GetAppPacket() app.Opcode = opcodes.AppOpcode(opcode) app.Data = make([]byte, len(payload)) copy(app.Data, payload) app.Compressed = compressed return []*AppPacket{app}, nil } func (h *ProtocolHandler) extractCombinedApps(data []byte) ([]*AppPacket, error) { if len(data) < 2 { return nil, fmt.Errorf("combined packet too small") } remaining := data[2:] // Skip sequence var apps []*AppPacket for len(remaining) > 0 { if len(remaining) < 1 { break } // Get sub-packet length subLen := uint16(remaining[0]) offset := 1 if subLen == 0xFF { if len(remaining) < 3 { break } subLen = binary.LittleEndian.Uint16(remaining[1:3]) offset = 3 } if len(remaining) < offset+int(subLen) { break } subData := remaining[offset : offset+int(subLen)] remaining = remaining[offset+int(subLen):] if len(subData) < 2 { continue } opcode := binary.LittleEndian.Uint16(subData[:2]) payload := subData[2:] app := h.pool.GetAppPacket() app.Opcode = opcodes.AppOpcode(opcode) app.Data = make([]byte, len(payload)) copy(app.Data, payload) apps = append(apps, app) } return apps, nil } func (h *ProtocolHandler) handleFragment(packet *EQ2Packet, session *Session) ([]*AppPacket, error) { if len(packet.Data) < 6 { // seq(2) + fragInfo(2) + data return nil, fmt.Errorf("fragment too small") } sequence := packet.Sequence fragInfo := binary.LittleEndian.Uint16(packet.Data[2:4]) fragData := packet.Data[4:] current := fragInfo & 0xFF total := fragInfo >> 8 // Get or create fragment buffer fragBuffer := session.Fragments[sequence] if fragBuffer == nil { fragBuffer = &FragmentBuffer{ Fragments: make(map[uint16][]byte), Total: total, Received: 0, } session.Fragments[sequence] = fragBuffer } // Store fragment fragBuffer.Fragments[current] = make([]byte, len(fragData)) copy(fragBuffer.Fragments[current], fragData) fragBuffer.Received++ // Check if complete if fragBuffer.Received == fragBuffer.Total { // Reassemble var complete []byte for i := uint16(0); i < fragBuffer.Total; i++ { complete = append(complete, fragBuffer.Fragments[i]...) } // Clean up delete(session.Fragments, sequence) // Process as normal packet return h.extractSingleApp(complete) } return nil, nil } // Create protocol packets func (h *ProtocolHandler) CreateSessionRequest(sessionID uint32, maxLen uint32) []byte { data := make([]byte, 13) data[0] = byte(opcodes.OP_SessionRequest) binary.LittleEndian.PutUint32(data[1:5], sessionID) binary.LittleEndian.PutUint32(data[5:9], maxLen) return data } func (h *ProtocolHandler) CreateSessionResponse(sessionID uint32, key uint32, format uint8) []byte { data := make([]byte, 10) data[0] = byte(opcodes.OP_SessionResponse) binary.LittleEndian.PutUint32(data[1:5], sessionID) binary.LittleEndian.PutUint32(data[5:9], key) data[9] = format return data } func (h *ProtocolHandler) CreateKeepAlive() []byte { return []byte{byte(opcodes.OP_KeepAlive)} }