356 lines
7.7 KiB
Go
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
|
|
} |