eq2go/internal/udp/compression.go
2025-07-21 23:18:39 -05:00

102 lines
2.3 KiB
Go

package udp
import (
"bytes"
"compress/zlib"
"io"
)
// Compression markers used by EQ2EMu protocol
const (
CompressedMarker = 0x5A // Marks zlib compressed data
UncompressedMarker = 0xA5 // Marks uncompressed data
)
// Compress applies zlib compression with EQ2EMu protocol markers
// Returns compressed data only if compression provides space savings
func Compress(data []byte) ([]byte, error) {
// Empty data gets uncompressed marker
if len(data) == 0 {
return []byte{UncompressedMarker}, nil
}
var buf bytes.Buffer
buf.WriteByte(CompressedMarker)
// Compress data using zlib
writer := zlib.NewWriter(&buf)
if _, err := writer.Write(data); err != nil {
return nil, err
}
if err := writer.Close(); err != nil {
return nil, err
}
// Only use compression if it actually saves space
if buf.Len() >= len(data)+1 {
result := make([]byte, len(data)+1)
result[0] = UncompressedMarker
copy(result[1:], data)
return result, nil
}
return buf.Bytes(), nil
}
// Decompress handles both compressed and uncompressed data based on markers
func Decompress(data []byte) ([]byte, error) {
if len(data) == 0 {
return data, nil
}
switch data[0] {
case UncompressedMarker:
// Data is uncompressed - return without marker
return data[1:], nil
case CompressedMarker:
// Data is zlib compressed
return decompressZlib(data[1:])
default:
// No compression marker - return as-is
return data, nil
}
}
// decompressZlib handles zlib decompression
func decompressZlib(data []byte) ([]byte, error) {
if len(data) == 0 {
return nil, io.ErrUnexpectedEOF
}
reader := bytes.NewReader(data)
zlibReader, err := zlib.NewReader(reader)
if err != nil {
return nil, err
}
defer zlibReader.Close()
var buf bytes.Buffer
if _, err := io.Copy(&buf, zlibReader); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// IsCompressed checks if data has compression marker
func IsCompressed(data []byte) bool {
return len(data) > 0 && data[0] == CompressedMarker
}
// IsUncompressed checks if data has uncompressed marker
func IsUncompressed(data []byte) bool {
return len(data) > 0 && data[0] == UncompressedMarker
}
// HasCompressionMarker checks if data has any compression marker
func HasCompressionMarker(data []byte) bool {
return IsCompressed(data) || IsUncompressed(data)
}