1
0
Protocol/packet.go
2025-09-01 12:31:14 -05:00

356 lines
7.7 KiB
Go

package eq2net
import (
"bytes"
"compress/zlib"
"encoding/binary"
"fmt"
"net"
"time"
"unsafe"
)
// Packet represents a base packet structure
type Packet struct {
Buffer []byte
Size uint32
SrcIP net.IP
SrcPort uint16
DstIP net.IP
DstPort uint16
Priority uint32
Timestamp time.Time
Version int16
Opcode uint16
}
// NewPacket creates a new packet
func NewPacket(op uint16, buf []byte) *Packet {
p := &Packet{
Opcode: op,
Size: uint32(len(buf)),
Timestamp: time.Now(),
}
if buf != nil {
p.Buffer = make([]byte, len(buf))
copy(p.Buffer, buf)
}
return p
}
// SetSrcInfo sets source IP and port
func (p *Packet) SetSrcInfo(ip net.IP, port uint16) {
p.SrcIP = ip
p.SrcPort = port
}
// SetDstInfo sets destination IP and port
func (p *Packet) SetDstInfo(ip net.IP, port uint16) {
p.DstIP = ip
p.DstPort = port
}
// GetRawOpcode returns the raw opcode
func (p *Packet) GetRawOpcode() uint16 {
return p.Opcode
}
// ProtocolPacket represents a protocol-level packet
type ProtocolPacket struct {
*Packet
EQ2Compressed bool
PacketPrepared bool
PacketEncrypted bool
Acked bool
SentTime int32
AttemptCount int8
Sequence uint16
}
// NewProtocolPacket creates a new protocol packet from raw data
func NewProtocolPacket(buf []byte, opcode int) *ProtocolPacket {
if len(buf) < 2 {
return nil
}
var op uint16
if opcode >= 0 {
op = uint16(opcode)
} else {
op = binary.BigEndian.Uint16(buf[:2])
buf = buf[2:]
}
pp := &ProtocolPacket{
Packet: NewPacket(op, buf),
}
return pp
}
// NewProtocolPacketWithOp creates a new protocol packet with specific opcode
func NewProtocolPacketWithOp(op uint16, buf []byte) *ProtocolPacket {
return &ProtocolPacket{
Packet: NewPacket(op, buf),
}
}
// ValidateCRC validates the CRC of a packet
func ValidateCRC(buffer []byte, length int, key uint32) bool {
// SessionRequest, SessionResponse, OutOfSession are not CRC'd
if len(buffer) >= 2 && buffer[0] == 0x00 &&
(buffer[1] == OPSessionRequest || buffer[1] == OPSessionResponse || buffer[1] == OPOutOfSession) {
return true
}
// Check for special case
if len(buffer) >= 4 && buffer[2] == 0x00 && buffer[3] == 0x19 {
return true
}
// Calculate and compare CRC
if length < 2 {
return false
}
compCRC := CalculateCRC16(buffer[:length-2], key)
packetCRC := binary.BigEndian.Uint16(buffer[length-2:])
return packetCRC == 0 || compCRC == packetCRC
}
// Serialize serializes the protocol packet
func (p *ProtocolPacket) Serialize(offset int8) []byte {
// Calculate size including opcode
totalSize := len(p.Buffer) + 2
if offset != 0 {
totalSize += int(offset)
}
result := make([]byte, totalSize)
pos := 0
// Add offset if needed
if offset != 0 {
pos = int(offset)
}
// Write opcode
binary.BigEndian.PutUint16(result[pos:], p.Opcode)
pos += 2
// Copy buffer
if p.Buffer != nil {
copy(result[pos:], p.Buffer)
}
return result
}
// Copy creates a copy of the protocol packet
func (p *ProtocolPacket) Copy() *ProtocolPacket {
newPacket := &ProtocolPacket{
Packet: NewPacket(p.Opcode, p.Buffer),
EQ2Compressed: p.EQ2Compressed,
PacketPrepared: p.PacketPrepared,
PacketEncrypted: p.PacketEncrypted,
Acked: p.Acked,
SentTime: p.SentTime,
AttemptCount: p.AttemptCount,
Sequence: p.Sequence,
}
newPacket.Version = p.Version
newPacket.Priority = p.Priority
return newPacket
}
// Combine combines two protocol packets
func (p *ProtocolPacket) Combine(rhs *ProtocolPacket) bool {
if rhs.Opcode != p.Opcode {
return false
}
// Create new buffer with combined size
newSize := len(p.Buffer) + len(rhs.Buffer)
newBuffer := make([]byte, newSize)
copy(newBuffer, p.Buffer)
copy(newBuffer[len(p.Buffer):], rhs.Buffer)
p.Buffer = newBuffer
p.Size = uint32(newSize)
return true
}
// IsProtocolPacket checks if a buffer contains a protocol packet
func IsProtocolPacket(inBuff []byte, length uint32, trimCRC bool) bool {
if length < 2 {
return false
}
opcode := binary.BigEndian.Uint16(inBuff)
switch uint8(opcode & 0xFF) {
case OPSessionRequest, OPSessionResponse, OPCombined,
OPSessionDisconnect, OPKeepAlive, OPServerKeyRequest,
OPSessionStatResponse, OPPacket, OPFragment,
OPOutOfOrderAck, OPAck, OPAppCombined, OPOutOfSession:
return true
}
return false
}
// Decompress decompresses a buffer
func Decompress(buffer []byte, length uint32, newbuf []byte, newbufsize uint32) (uint32, error) {
reader := bytes.NewReader(buffer[:length])
zlibReader, err := zlib.NewReader(reader)
if err != nil {
return 0, err
}
defer zlibReader.Close()
buf := bytes.NewBuffer(newbuf[:0])
n, err := buf.ReadFrom(zlibReader)
if err != nil {
return 0, err
}
if uint32(n) > newbufsize {
return 0, fmt.Errorf("decompressed size exceeds buffer size")
}
return uint32(n), nil
}
// Compress compresses a buffer
func Compress(buffer []byte, length uint32, newbuf []byte, newbufsize uint32) (uint32, error) {
var buf bytes.Buffer
writer := zlib.NewWriter(&buf)
_, err := writer.Write(buffer[:length])
if err != nil {
return 0, err
}
err = writer.Close()
if err != nil {
return 0, err
}
compressed := buf.Bytes()
if uint32(len(compressed)) > newbufsize {
return 0, fmt.Errorf("compressed size exceeds buffer size")
}
copy(newbuf, compressed)
return uint32(len(compressed)), nil
}
// ChatDecode decodes chat data
func ChatDecode(buffer []byte, size int, decodeKey int) {
for i := 0; i+4 <= size; i++ {
c := buffer[i]
buffer[i] = buffer[i+2]
buffer[i+2] = c
c = buffer[i+1]
buffer[i+1] = buffer[i+3]
buffer[i+3] = c
key := uint32(decodeKey)
p := (*uint32)(unsafe.Pointer(&buffer[i]))
*p = (*p) ^ key
i += 3
}
}
// ChatEncode encodes chat data
func ChatEncode(buffer []byte, size int, encodeKey int) {
for i := 0; i+4 <= size; i++ {
key := uint32(encodeKey)
p := (*uint32)(unsafe.Pointer(&buffer[i]))
*p = (*p) ^ key
c := buffer[i]
buffer[i] = buffer[i+2]
buffer[i+2] = c
c = buffer[i+1]
buffer[i+1] = buffer[i+3]
buffer[i+3] = c
i += 3
}
}
// ApplicationPacket represents an application-level packet
type ApplicationPacket struct {
*Packet
EmuOpcode EmuOpcode
AppOpcodeSize uint8
}
// NewApplicationPacket creates a new application packet
func NewApplicationPacket(op EmuOpcode, buf []byte) *ApplicationPacket {
ap := &ApplicationPacket{
Packet: NewPacket(0, buf),
EmuOpcode: op,
AppOpcodeSize: 2, // Default for world/zone
}
return ap
}
// SetOpcode sets the opcode
func (p *ApplicationPacket) SetOpcode(op EmuOpcode) {
p.EmuOpcode = op
p.Opcode = uint16(op)
}
// GetOpcode returns the opcode
func (p *ApplicationPacket) GetOpcode() EmuOpcode {
return p.EmuOpcode
}
// Copy creates a copy of the application packet
func (p *ApplicationPacket) Copy() *ApplicationPacket {
newPacket := &ApplicationPacket{
Packet: NewPacket(p.Opcode, p.Buffer),
EmuOpcode: p.EmuOpcode,
AppOpcodeSize: p.AppOpcodeSize,
}
newPacket.Version = p.Version
return newPacket
}
// Serialize serializes the application packet
func (p *ApplicationPacket) Serialize() []byte {
size := len(p.Buffer) + int(p.AppOpcodeSize)
result := make([]byte, size)
// Write opcode based on size
if p.AppOpcodeSize == 1 {
result[0] = uint8(p.Opcode)
copy(result[1:], p.Buffer)
} else {
binary.LittleEndian.PutUint16(result, p.Opcode)
copy(result[2:], p.Buffer)
}
return result
}
// Combine combines two application packets
func (p *ApplicationPacket) Combine(rhs *ApplicationPacket) bool {
if rhs.EmuOpcode != p.EmuOpcode {
return false
}
newSize := len(p.Buffer) + len(rhs.Buffer)
newBuffer := make([]byte, newSize)
copy(newBuffer, p.Buffer)
copy(newBuffer[len(p.Buffer):], rhs.Buffer)
p.Buffer = newBuffer
p.Size = uint32(newSize)
return true
}