1
0
EQ2Emu/internal/packets/protocol.go

203 lines
4.7 KiB
Go

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)}
}