fix chat encoding
This commit is contained in:
parent
1a9b7effa6
commit
4ecdcc868f
@ -8,22 +8,38 @@ import (
|
||||
"io"
|
||||
|
||||
"git.sharkk.net/EQ2/Protocol/crypto"
|
||||
"git.sharkk.net/EQ2/Protocol/opcodes"
|
||||
)
|
||||
|
||||
// ValidateCRC validates packet CRC using EQ2's custom CRC16
|
||||
// ValidateCRC validates packet CRC using EQ2's custom CRC16 (matches C++ EQProtocolPacket::ValidateCRC)
|
||||
func ValidateCRC(buffer []byte, key uint32) bool {
|
||||
if len(buffer) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Extract CRC from last 2 bytes (EQ2 uses CRC16)
|
||||
// Session packets are not CRC protected
|
||||
if len(buffer) >= 2 && buffer[0] == 0x00 &&
|
||||
(buffer[1] == byte(opcodes.OP_SessionRequest) ||
|
||||
buffer[1] == byte(opcodes.OP_SessionResponse) ||
|
||||
buffer[1] == byte(opcodes.OP_OutOfSession)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Combined application packets are also exempt (OP_AppCombined = 0x0019)
|
||||
if len(buffer) >= 4 && buffer[2] == 0x00 && buffer[3] == 0x19 {
|
||||
return true
|
||||
}
|
||||
|
||||
// All other packets must have valid CRC
|
||||
// Extract CRC from last 2 bytes (network byte order)
|
||||
packetCRC := binary.BigEndian.Uint16(buffer[len(buffer)-2:])
|
||||
|
||||
// Calculate CRC on data portion (excluding CRC bytes)
|
||||
data := buffer[:len(buffer)-2]
|
||||
calculatedCRC := crypto.CalculateCRC(data, key)
|
||||
|
||||
return packetCRC == calculatedCRC
|
||||
// Valid if no CRC present (packetCRC == 0) or CRCs match
|
||||
return packetCRC == 0 || calculatedCRC == packetCRC
|
||||
}
|
||||
|
||||
// AppendCRC appends CRC16 to packet buffer using EQ2's custom CRC
|
||||
@ -214,27 +230,72 @@ func DecompressPacket(buffer []byte) ([]byte, error) {
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
// ChatEncode encodes chat data using EQ's XOR-based encoding
|
||||
// ChatEncode encodes chat data using EQ's XOR-based encoding (matches C++ EQProtocolPacket::ChatEncode)
|
||||
// Uses 4-byte block XOR with rolling key that updates from encrypted data
|
||||
func ChatEncode(buffer []byte, encodeKey int) {
|
||||
if len(buffer) == 0 || encodeKey == 0 {
|
||||
if len(buffer) <= 2 || encodeKey == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
key := byte(encodeKey & 0xFF)
|
||||
for i := range buffer {
|
||||
buffer[i] ^= key
|
||||
// Rotate key for next byte
|
||||
key = ((key << 1) | (key >> 7)) & 0xFF
|
||||
// Add position-based variation
|
||||
if i%3 == 0 {
|
||||
key ^= byte(i & 0xFF)
|
||||
}
|
||||
// Skip encoding for certain packet types (matches C++ conditions)
|
||||
if buffer[1] == 0x01 || buffer[0] == 0x02 || buffer[0] == 0x1d {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip first 2 bytes (opcode)
|
||||
data := buffer[2:]
|
||||
size := len(data)
|
||||
key := int32(encodeKey)
|
||||
|
||||
// Encode 4-byte blocks with rolling key
|
||||
i := 0
|
||||
for ; i+4 <= size; i += 4 {
|
||||
// Read 4 bytes as int32 (little-endian like C++)
|
||||
pt := binary.LittleEndian.Uint32(data[i:i+4])
|
||||
encrypted := pt ^ uint32(key)
|
||||
key = int32(encrypted) // Update key with encrypted data
|
||||
binary.LittleEndian.PutUint32(data[i:i+4], encrypted)
|
||||
}
|
||||
|
||||
// Encode remaining bytes with last key byte
|
||||
keyByte := byte(key & 0xFF)
|
||||
for ; i < size; i++ {
|
||||
data[i] ^= keyByte
|
||||
}
|
||||
}
|
||||
|
||||
// ChatDecode decodes chat data (XOR is symmetric)
|
||||
// ChatDecode decodes chat data using EQ's XOR-based encoding (matches C++ EQProtocolPacket::ChatDecode)
|
||||
// Uses 4-byte block XOR with rolling key that updates from encrypted data
|
||||
func ChatDecode(buffer []byte, decodeKey int) {
|
||||
ChatEncode(buffer, decodeKey)
|
||||
if len(buffer) <= 2 || decodeKey == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip decoding for certain packet types (matches C++ conditions)
|
||||
if buffer[1] == 0x01 || buffer[0] == 0x02 || buffer[0] == 0x1d {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip first 2 bytes (opcode)
|
||||
data := buffer[2:]
|
||||
size := len(data)
|
||||
key := int32(decodeKey)
|
||||
|
||||
// Decode 4-byte blocks with rolling key
|
||||
i := 0
|
||||
for ; i+4 <= size; i += 4 {
|
||||
// Read 4 bytes as int32 (little-endian like C++)
|
||||
encrypted := binary.LittleEndian.Uint32(data[i:i+4])
|
||||
decrypted := encrypted ^ uint32(key)
|
||||
key = int32(encrypted) // Update key with encrypted data (before decryption)
|
||||
binary.LittleEndian.PutUint32(data[i:i+4], decrypted)
|
||||
}
|
||||
|
||||
// Decode remaining bytes with last key byte
|
||||
keyByte := byte(key & 0xFF)
|
||||
for ; i < size; i++ {
|
||||
data[i] ^= keyByte
|
||||
}
|
||||
}
|
||||
|
||||
// IsChatPacket checks if opcode is a chat-related packet
|
||||
@ -256,7 +317,7 @@ func longToIP(ip uint32) string {
|
||||
byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip))
|
||||
}
|
||||
|
||||
// IsProtocolPacket checks if buffer contains a valid protocol packet
|
||||
// IsProtocolPacket checks if buffer contains a valid protocol packet (matches C++ EQProtocolPacket::IsProtocolPacket)
|
||||
func IsProtocolPacket(buffer []byte) bool {
|
||||
if len(buffer) < 2 {
|
||||
return false
|
||||
@ -264,21 +325,23 @@ func IsProtocolPacket(buffer []byte) bool {
|
||||
|
||||
opcode := binary.BigEndian.Uint16(buffer[:2])
|
||||
|
||||
validOpcodes := map[uint16]bool{
|
||||
0x0001: true, // OP_SessionRequest
|
||||
0x0002: true, // OP_SessionResponse
|
||||
0x0003: true, // OP_Combined
|
||||
0x0005: true, // OP_SessionDisconnect
|
||||
0x0006: true, // OP_KeepAlive
|
||||
0x0007: true, // OP_SessionStatRequest
|
||||
0x0008: true, // OP_SessionStatResponse
|
||||
0x0009: true, // OP_Packet
|
||||
0x000d: true, // OP_Fragment
|
||||
0x0015: true, // OP_Ack
|
||||
0x0019: true, // OP_AppCombined
|
||||
0x001d: true, // OP_OutOfOrderAck
|
||||
0x001e: true, // OP_OutOfSession
|
||||
// Check against known protocol opcodes
|
||||
switch opcode {
|
||||
case opcodes.OP_SessionRequest,
|
||||
opcodes.OP_SessionResponse,
|
||||
opcodes.OP_Combined,
|
||||
opcodes.OP_SessionDisconnect,
|
||||
opcodes.OP_KeepAlive,
|
||||
opcodes.OP_SessionStatRequest,
|
||||
opcodes.OP_SessionStatResponse,
|
||||
opcodes.OP_Packet,
|
||||
opcodes.OP_Fragment,
|
||||
opcodes.OP_Ack,
|
||||
opcodes.OP_AppCombined,
|
||||
opcodes.OP_OutOfOrderAck,
|
||||
opcodes.OP_OutOfSession:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return validOpcodes[opcode]
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package packets
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.sharkk.net/EQ2/Protocol/crypto"
|
||||
@ -516,3 +517,45 @@ func (p *ProtoPacket) Copy() *ProtoPacket {
|
||||
newPacket.Packet.CopyInfo(p.Packet)
|
||||
return newPacket
|
||||
}
|
||||
|
||||
// GetOpcodeName returns human-readable name for the opcode (matches C++ EQ2Packet::GetOpcodeName)
|
||||
func (p *ProtoPacket) GetOpcodeName() string {
|
||||
// Use manager to get opcode name if available
|
||||
if p.manager != nil {
|
||||
if name := p.manager.EmuToName(p.LoginOp); name != "" {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to protocol opcode names
|
||||
switch p.Opcode {
|
||||
case opcodes.OP_SessionRequest:
|
||||
return "OP_SessionRequest"
|
||||
case opcodes.OP_SessionResponse:
|
||||
return "OP_SessionResponse"
|
||||
case opcodes.OP_Combined:
|
||||
return "OP_Combined"
|
||||
case opcodes.OP_SessionDisconnect:
|
||||
return "OP_SessionDisconnect"
|
||||
case opcodes.OP_KeepAlive:
|
||||
return "OP_KeepAlive"
|
||||
case opcodes.OP_SessionStatRequest:
|
||||
return "OP_SessionStatRequest"
|
||||
case opcodes.OP_SessionStatResponse:
|
||||
return "OP_SessionStatResponse"
|
||||
case opcodes.OP_Packet:
|
||||
return "OP_Packet"
|
||||
case opcodes.OP_Fragment:
|
||||
return "OP_Fragment"
|
||||
case opcodes.OP_Ack:
|
||||
return "OP_Ack"
|
||||
case opcodes.OP_AppCombined:
|
||||
return "OP_AppCombined"
|
||||
case opcodes.OP_OutOfOrderAck:
|
||||
return "OP_OutOfOrderAck"
|
||||
case opcodes.OP_OutOfSession:
|
||||
return "OP_OutOfSession"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown(0x%04X)", p.Opcode)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user