fix simple encoding
This commit is contained in:
parent
fd2ebfd6cc
commit
1a9b7effa6
@ -43,34 +43,44 @@ func StripCRC(buffer []byte) []byte {
|
|||||||
return buffer[:len(buffer)-2]
|
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) {
|
func Compress(src []byte) ([]byte, error) {
|
||||||
if len(src) == 0 {
|
if len(src) == 0 {
|
||||||
return src, nil
|
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
|
// Write uncompressed length first (4 bytes) - EQ protocol requirement
|
||||||
if err := binary.Write(&buf, binary.BigEndian, uint32(len(src))); err != nil {
|
if err := binary.Write(&buf, binary.BigEndian, uint32(len(src))); err != nil {
|
||||||
return nil, err
|
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
|
// Use simple encoding for smaller packets (just return data as-is)
|
||||||
w := zlib.NewWriter(&buf)
|
// The 0xa5 flag is added by the caller, not here
|
||||||
if _, err := w.Write(src); err != nil {
|
return src, nil
|
||||||
w.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.Close(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decompress decompresses packet data using zlib
|
// 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) {
|
func Decompress(src []byte) ([]byte, error) {
|
||||||
if len(src) < 4 {
|
if len(src) < 4 {
|
||||||
return nil, fmt.Errorf("compressed data too small")
|
return nil, fmt.Errorf("compressed data too small")
|
||||||
@ -100,6 +110,110 @@ func Decompress(src []byte) ([]byte, error) {
|
|||||||
return decompressed, nil
|
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
|
// ChatEncode encodes chat data using EQ's XOR-based encoding
|
||||||
func ChatEncode(buffer []byte, encodeKey int) {
|
func ChatEncode(buffer []byte, encodeKey int) {
|
||||||
if len(buffer) == 0 || encodeKey == 0 {
|
if len(buffer) == 0 || encodeKey == 0 {
|
||||||
|
@ -189,7 +189,7 @@ func (p *ProtoPacket) IsCompressed() bool {
|
|||||||
return p.eq2Compressed
|
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 {
|
func (p *ProtoPacket) EQ2Compress(offset int8) int8 {
|
||||||
if offset <= 0 || int(offset) >= len(p.Buffer) {
|
if offset <= 0 || int(offset) >= len(p.Buffer) {
|
||||||
return 0
|
return 0
|
||||||
@ -197,28 +197,53 @@ func (p *ProtoPacket) EQ2Compress(offset int8) int8 {
|
|||||||
|
|
||||||
// Compress data from offset onwards
|
// Compress data from offset onwards
|
||||||
dataToCompress := p.Buffer[offset:]
|
dataToCompress := p.Buffer[offset:]
|
||||||
compressed, err := Compress(dataToCompress)
|
dataLen := len(dataToCompress)
|
||||||
if err != nil || len(compressed) >= len(dataToCompress) {
|
|
||||||
return 0 // Compression failed or didn't reduce size
|
// 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)
|
// 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)
|
// MakeApplicationPacket converts to app packet (matches EQProtocolPacket::MakeApplicationPacket)
|
||||||
func (p *ProtoPacket) MakeApplicationPacket(opcodeSize uint8) *AppPacket {
|
func (p *ProtoPacket) MakeApplicationPacket(opcodeSize uint8) *AppPacket {
|
||||||
// Decompress if needed
|
// Decompress if needed - handle both zlib and simple encoding
|
||||||
if p.eq2Compressed {
|
if p.eq2Compressed && len(p.Buffer) > 2 {
|
||||||
if decompressed, err := Decompress(p.Buffer); err == nil {
|
// Check compression type at position 2 (after sequence bytes)
|
||||||
p.Buffer = decompressed
|
compressionFlag := byte(0)
|
||||||
p.eq2Compressed = false
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user