1
0

fix simple encoding

This commit is contained in:
Sky Johnson 2025-09-03 20:41:44 -05:00
parent fd2ebfd6cc
commit 1a9b7effa6
2 changed files with 207 additions and 44 deletions

View File

@ -43,34 +43,44 @@ func StripCRC(buffer []byte) []byte {
return buffer[:len(buffer)-2]
}
// Compress compresses packet data using zlib (matches EQ compression)
// Compress compresses packet data using zlib or simple encoding (matches C++ EQProtocolPacket::Compress)
// Uses zlib for packets > 30 bytes, simple encoding for smaller packets
func Compress(src []byte) ([]byte, error) {
if len(src) == 0 {
return src, nil
}
var buf bytes.Buffer
// C++ uses 30 bytes as threshold between simple encoding and zlib
if len(src) > 30 {
// Use zlib compression for larger packets
var buf bytes.Buffer
// Write uncompressed length first (4 bytes) - EQ protocol requirement
if err := binary.Write(&buf, binary.BigEndian, uint32(len(src))); err != nil {
return nil, err
// Write uncompressed length first (4 bytes) - EQ protocol requirement
if err := binary.Write(&buf, binary.BigEndian, uint32(len(src))); err != nil {
return nil, err
}
// Compress the data
w := zlib.NewWriter(&buf)
if _, err := w.Write(src); err != nil {
w.Close()
return nil, err
}
if err := w.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Compress the data
w := zlib.NewWriter(&buf)
if _, err := w.Write(src); err != nil {
w.Close()
return nil, err
}
if err := w.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
// Use simple encoding for smaller packets (just return data as-is)
// The 0xa5 flag is added by the caller, not here
return src, nil
}
// Decompress decompresses packet data using zlib
// This is called after the compression flag has already been checked and removed
func Decompress(src []byte) ([]byte, error) {
if len(src) < 4 {
return nil, fmt.Errorf("compressed data too small")
@ -100,6 +110,110 @@ func Decompress(src []byte) ([]byte, error) {
return decompressed, nil
}
// CompressPacket adds compression with proper flag handling (matches C++ EQProtocolPacket::Compress)
// Uses zlib (0x5a) for packets > 30 bytes, simple encoding (0xa5) for smaller packets
func CompressPacket(buffer []byte) ([]byte, error) {
if len(buffer) < 2 {
return buffer, nil
}
// Determine flag offset based on opcode size
flagOffset := 1
if buffer[0] == 0x00 {
flagOffset = 2 // Two-byte opcode
}
if len(buffer) <= flagOffset {
return buffer, nil // Too small to compress
}
// Get data to compress (after opcode)
dataToCompress := buffer[flagOffset:]
// Choose compression method based on size (C++ uses 30 byte threshold)
if len(dataToCompress) > 30 {
// Use zlib compression
compressed, err := Compress(dataToCompress)
if err != nil {
return nil, err
}
// Only use if compression actually reduced size
if len(compressed) < len(dataToCompress) {
result := make([]byte, flagOffset+1+len(compressed))
copy(result[:flagOffset], buffer[:flagOffset]) // Copy opcode
result[flagOffset] = 0x5a // Zlib flag
copy(result[flagOffset+1:], compressed) // Compressed data
return result, nil
}
} else {
// Use simple encoding - just add flag
result := make([]byte, len(buffer)+1)
copy(result[:flagOffset], buffer[:flagOffset]) // Copy opcode
result[flagOffset] = 0xa5 // Simple encoding flag
copy(result[flagOffset+1:], dataToCompress) // Original data
return result, nil
}
// No compression if it doesn't help
return buffer, nil
}
// DecompressPacket handles decompression with flag checking (matches C++ EQProtocolPacket::Decompress)
// Supports both zlib compression (0x5a) and simple encoding (0xa5)
func DecompressPacket(buffer []byte) ([]byte, error) {
if len(buffer) < 3 {
return buffer, nil // Too small to be compressed
}
// Determine flag offset based on opcode size
flagOffset := uint32(1)
if buffer[0] == 0x00 {
flagOffset = 2 // Two-byte opcode
}
if uint32(len(buffer)) <= flagOffset {
return buffer, nil // No room for compression flag
}
compressionFlag := buffer[flagOffset]
// Check compression type
if compressionFlag == 0x5a {
// Zlib compression
// Decompress data after flag, excluding last 2 CRC bytes
dataStart := flagOffset + 1
dataEnd := uint32(len(buffer)) - 2
if dataEnd <= dataStart {
return nil, fmt.Errorf("invalid compressed packet size")
}
decompressed, err := Decompress(buffer[dataStart:dataEnd])
if err != nil {
return nil, fmt.Errorf("zlib decompression failed: %w", err)
}
// Rebuild packet: opcode + decompressed data + CRC
result := make([]byte, flagOffset+uint32(len(decompressed))+2)
copy(result[:flagOffset], buffer[:flagOffset]) // Copy opcode
copy(result[flagOffset:], decompressed) // Copy decompressed data
// Copy CRC bytes
result[len(result)-2] = buffer[len(buffer)-2]
result[len(result)-1] = buffer[len(buffer)-1]
return result, nil
} else if compressionFlag == 0xa5 {
// Simple encoding - just remove the encoding flag
result := make([]byte, len(buffer)-1)
copy(result[:flagOffset], buffer[:flagOffset]) // Copy opcode
copy(result[flagOffset:], buffer[flagOffset+1:]) // Skip flag, copy rest
return result, nil
}
// No compression
return buffer, nil
}
// ChatEncode encodes chat data using EQ's XOR-based encoding
func ChatEncode(buffer []byte, encodeKey int) {
if len(buffer) == 0 || encodeKey == 0 {

View File

@ -189,7 +189,7 @@ func (p *ProtoPacket) IsCompressed() bool {
return p.eq2Compressed
}
// EQ2Compress compresses packet data (matches EQStream::EQ2_Compress)
// EQ2Compress compresses packet data (matches EQStream::EQ2_Compress and C++ EQProtocolPacket::Compress)
func (p *ProtoPacket) EQ2Compress(offset int8) int8 {
if offset <= 0 || int(offset) >= len(p.Buffer) {
return 0
@ -197,28 +197,53 @@ func (p *ProtoPacket) EQ2Compress(offset int8) int8 {
// Compress data from offset onwards
dataToCompress := p.Buffer[offset:]
compressed, err := Compress(dataToCompress)
if err != nil || len(compressed) >= len(dataToCompress) {
return 0 // Compression failed or didn't reduce size
dataLen := len(dataToCompress)
// C++ uses 30 bytes as threshold - match this exactly
if dataLen > 30 {
// Use zlib compression for larger packets
compressed, err := Compress(dataToCompress)
if err != nil || len(compressed) >= dataLen {
return 0 // Compression failed or didn't reduce size
}
// Rebuild buffer with zlib compression flag (0x5a)
newSize := int(offset) + len(compressed)
newBuffer := make([]byte, newSize)
// Copy header bytes before offset
copy(newBuffer[:offset], p.Buffer[:offset])
// Set zlib compression flag at offset-1
newBuffer[offset-1] = 0x5a
// Copy compressed data
copy(newBuffer[offset:], compressed)
p.Buffer = newBuffer
p.eq2Compressed = true
return offset - 1 // Return compression flag position
} else {
// Use simple encoding for smaller packets (0xa5)
// Simple encoding just adds a flag, doesn't change data
newSize := len(p.Buffer) + 1
newBuffer := make([]byte, newSize)
// Copy header bytes before offset
copy(newBuffer[:offset], p.Buffer[:offset])
// Set simple encoding flag at offset-1
newBuffer[offset-1] = 0xa5
// Copy data after flag (shift by 1 byte)
copy(newBuffer[offset:], p.Buffer[offset-1:])
p.Buffer = newBuffer
p.eq2Compressed = true
return offset - 1 // Return compression flag position
}
// Rebuild buffer with compression flag
newSize := int(offset) + len(compressed)
newBuffer := make([]byte, newSize)
// Copy header bytes before offset
copy(newBuffer[:offset], p.Buffer[:offset])
// Set compression flag at offset-1
newBuffer[offset-1] = 1 // Compression flag
// Copy compressed data
copy(newBuffer[offset:], compressed)
p.Buffer = newBuffer
p.eq2Compressed = true
return offset - 1 // Return compression flag position
}
// EncryptPacket encrypts packet data (matches EQStream::EncryptPacket)
@ -431,11 +456,35 @@ func (p *ProtoPacket) AppCombine(rhs *ProtoPacket) bool {
// MakeApplicationPacket converts to app packet (matches EQProtocolPacket::MakeApplicationPacket)
func (p *ProtoPacket) MakeApplicationPacket(opcodeSize uint8) *AppPacket {
// Decompress if needed
if p.eq2Compressed {
if decompressed, err := Decompress(p.Buffer); err == nil {
p.Buffer = decompressed
p.eq2Compressed = false
// Decompress if needed - handle both zlib and simple encoding
if p.eq2Compressed && len(p.Buffer) > 2 {
// Check compression type at position 2 (after sequence bytes)
compressionFlag := byte(0)
if len(p.Buffer) > 2 {
compressionFlag = p.Buffer[2]
}
if compressionFlag == 0x5a {
// Zlib compression - decompress data after flag
if len(p.Buffer) > 3 {
if decompressed, err := Decompress(p.Buffer[3:]); err == nil {
// Rebuild buffer without compression
newBuffer := make([]byte, 2+len(decompressed))
copy(newBuffer[:2], p.Buffer[:2]) // Copy sequence
copy(newBuffer[2:], decompressed)
p.Buffer = newBuffer
p.eq2Compressed = false
}
}
} else if compressionFlag == 0xa5 {
// Simple encoding - just remove the flag
if len(p.Buffer) > 3 {
newBuffer := make([]byte, len(p.Buffer)-1)
copy(newBuffer[:2], p.Buffer[:2]) // Copy sequence
copy(newBuffer[2:], p.Buffer[3:]) // Skip flag
p.Buffer = newBuffer
p.eq2Compressed = false
}
}
}