203 lines
4.7 KiB
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)}
|
|
}
|