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

74 lines
1.7 KiB
Go

package udp
// EQ2EMu uses a specific CRC32 polynomial (reversed)
const crcPolynomial = 0xEDB88320
// Pre-computed CRC32 lookup table for fast calculation
var crcTable [256]uint32
// init builds the CRC lookup table at package initialization
func init() {
for i := range crcTable {
crc := uint32(i)
for range 8 {
if crc&1 == 1 {
crc = (crc >> 1) ^ crcPolynomial
} else {
crc >>= 1
}
}
crcTable[i] = crc
}
}
// CalculateCRC32 computes CRC32 using EQ2EMu's algorithm
// Returns 16-bit value by truncating the upper bits
func CalculateCRC32(data []byte) uint16 {
crc := uint32(0xFFFFFFFF)
// Use lookup table for efficient calculation
for _, b := range data {
crc = crcTable[byte(crc)^b] ^ (crc >> 8)
}
// Return inverted result truncated to 16 bits
return uint16(^crc)
}
// ValidateCRC checks if packet has valid CRC
// Expects CRC to be the last 2 bytes of data
func ValidateCRC(data []byte) bool {
if len(data) < 2 {
return false
}
// Split payload and CRC
payload := data[:len(data)-2]
expectedCRC := uint16(data[len(data)-2]) | (uint16(data[len(data)-1]) << 8)
// Calculate and compare
actualCRC := CalculateCRC32(payload)
return expectedCRC == actualCRC
}
// AppendCRC adds 16-bit CRC to the end of data
func AppendCRC(data []byte) []byte {
crc := CalculateCRC32(data)
result := make([]byte, len(data)+2)
copy(result, data)
// Append CRC in little-endian format
result[len(data)] = byte(crc)
result[len(data)+1] = byte(crc >> 8)
return result
}
// ValidateAndStrip validates CRC and returns data without CRC suffix
func ValidateAndStrip(data []byte) ([]byte, bool) {
if !ValidateCRC(data) {
return nil, false
}
return data[:len(data)-2], true
}