209 lines
4.7 KiB
Go
209 lines
4.7 KiB
Go
package eq2net
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/zlib"
|
|
"encoding/binary"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
// Compression flags
|
|
CompressionFlagZlib = 0x5A // Zlib compression
|
|
CompressionFlagSimple = 0xA5 // Simple encoding (no actual compression)
|
|
|
|
// Compression threshold - packets larger than this use zlib
|
|
CompressionThreshold = 30
|
|
)
|
|
|
|
// CompressPacket compresses a packet using zlib or simple encoding
|
|
func CompressPacket(data []byte) ([]byte, error) {
|
|
if len(data) < 2 {
|
|
return data, nil
|
|
}
|
|
|
|
// Determine opcode size
|
|
flagOffset := 1
|
|
if data[0] == 0 {
|
|
flagOffset = 2 // Two-byte opcode
|
|
}
|
|
|
|
// Don't compress if too small
|
|
if len(data) <= flagOffset {
|
|
return data, nil
|
|
}
|
|
|
|
result := make([]byte, 0, len(data)+1)
|
|
|
|
// Copy opcode bytes
|
|
result = append(result, data[:flagOffset]...)
|
|
|
|
if len(data) > CompressionThreshold {
|
|
// Use zlib compression for larger packets
|
|
result = append(result, CompressionFlagZlib)
|
|
|
|
// Compress the data after opcode
|
|
var compressed bytes.Buffer
|
|
w := zlib.NewWriter(&compressed)
|
|
if _, err := w.Write(data[flagOffset:]); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := w.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result = append(result, compressed.Bytes()...)
|
|
} else {
|
|
// Use simple encoding for smaller packets
|
|
result = append(result, CompressionFlagSimple)
|
|
result = append(result, data[flagOffset:]...)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// DecompressPacket decompresses a packet
|
|
func DecompressPacket(data []byte) ([]byte, error) {
|
|
if len(data) < 3 {
|
|
return data, nil
|
|
}
|
|
|
|
// Determine opcode size and compression flag position
|
|
flagOffset := 1
|
|
if data[0] == 0 {
|
|
flagOffset = 2
|
|
}
|
|
|
|
if len(data) <= flagOffset {
|
|
return data, nil
|
|
}
|
|
|
|
compressionFlag := data[flagOffset]
|
|
|
|
// Check compression type
|
|
switch compressionFlag {
|
|
case CompressionFlagZlib:
|
|
// Zlib decompression
|
|
result := make([]byte, 0, len(data)*2)
|
|
|
|
// Copy opcode
|
|
result = append(result, data[:flagOffset]...)
|
|
|
|
// Decompress data (skip flag byte)
|
|
r, err := zlib.NewReader(bytes.NewReader(data[flagOffset+1:]))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer r.Close()
|
|
|
|
decompressed, err := io.ReadAll(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result = append(result, decompressed...)
|
|
return result, nil
|
|
|
|
case CompressionFlagSimple:
|
|
// Simple encoding - just remove the flag byte
|
|
result := make([]byte, 0, len(data)-1)
|
|
result = append(result, data[:flagOffset]...)
|
|
result = append(result, data[flagOffset+1:]...)
|
|
return result, nil
|
|
|
|
default:
|
|
// No compression
|
|
return data, nil
|
|
}
|
|
}
|
|
|
|
// IsCompressed checks if a packet is compressed
|
|
func IsCompressed(data []byte) bool {
|
|
if len(data) < 2 {
|
|
return false
|
|
}
|
|
|
|
flagOffset := 1
|
|
if data[0] == 0 {
|
|
flagOffset = 2
|
|
}
|
|
|
|
if len(data) <= flagOffset {
|
|
return false
|
|
}
|
|
|
|
flag := data[flagOffset]
|
|
return flag == CompressionFlagZlib || flag == CompressionFlagSimple
|
|
}
|
|
|
|
// ChatEncode encodes chat data using XOR encryption with rolling key
|
|
func ChatEncode(data []byte, encodeKey uint32) []byte {
|
|
// Skip certain packet types
|
|
if len(data) >= 2 && (data[1] == 0x01 || data[0] == 0x02 || data[0] == 0x1d) {
|
|
return data
|
|
}
|
|
|
|
// Work with data after opcode
|
|
if len(data) <= 2 {
|
|
return data
|
|
}
|
|
|
|
result := make([]byte, len(data))
|
|
copy(result[:2], data[:2]) // Copy opcode
|
|
|
|
key := encodeKey
|
|
offset := 2
|
|
|
|
// Process 4-byte blocks with rolling key
|
|
for i := offset; i+4 <= len(data); i += 4 {
|
|
block := binary.LittleEndian.Uint32(data[i : i+4])
|
|
encrypted := block ^ key
|
|
binary.LittleEndian.PutUint32(result[i:i+4], encrypted)
|
|
key = encrypted // Update key with encrypted data
|
|
}
|
|
|
|
// Handle remaining bytes
|
|
keyByte := byte(key & 0xFF)
|
|
alignedEnd := offset + ((len(data)-offset)/4)*4
|
|
for i := alignedEnd; i < len(data); i++ {
|
|
result[i] = data[i] ^ keyByte
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// ChatDecode decodes chat data using XOR encryption with rolling key
|
|
func ChatDecode(data []byte, decodeKey uint32) []byte {
|
|
// Skip certain packet types
|
|
if len(data) >= 2 && (data[1] == 0x01 || data[0] == 0x02 || data[0] == 0x1d) {
|
|
return data
|
|
}
|
|
|
|
// Work with data after opcode
|
|
if len(data) <= 2 {
|
|
return data
|
|
}
|
|
|
|
result := make([]byte, len(data))
|
|
copy(result[:2], data[:2]) // Copy opcode
|
|
|
|
key := decodeKey
|
|
offset := 2
|
|
|
|
// Process 4-byte blocks with rolling key
|
|
for i := offset; i+4 <= len(data); i += 4 {
|
|
encrypted := binary.LittleEndian.Uint32(data[i : i+4])
|
|
decrypted := encrypted ^ key
|
|
binary.LittleEndian.PutUint32(result[i:i+4], decrypted)
|
|
key = encrypted // Update key with encrypted data (before decryption)
|
|
}
|
|
|
|
// Handle remaining bytes
|
|
keyByte := byte(key & 0xFF)
|
|
alignedEnd := offset + ((len(data)-offset)/4)*4
|
|
for i := alignedEnd; i < len(data); i++ {
|
|
result[i] = data[i] ^ keyByte
|
|
}
|
|
|
|
return result
|
|
} |