package eq2net import ( "bytes" "compress/zlib" "encoding/binary" "fmt" "net" "time" "unsafe" ) // Packet represents a base packet structure type Packet struct { Buffer []byte Size uint32 SrcIP net.IP SrcPort uint16 DstIP net.IP DstPort uint16 Priority uint32 Timestamp time.Time Version int16 Opcode uint16 } // NewPacket creates a new packet func NewPacket(op uint16, buf []byte) *Packet { p := &Packet{ Opcode: op, Size: uint32(len(buf)), Timestamp: time.Now(), } if buf != nil { p.Buffer = make([]byte, len(buf)) copy(p.Buffer, buf) } return p } // SetSrcInfo sets source IP and port func (p *Packet) SetSrcInfo(ip net.IP, port uint16) { p.SrcIP = ip p.SrcPort = port } // SetDstInfo sets destination IP and port func (p *Packet) SetDstInfo(ip net.IP, port uint16) { p.DstIP = ip p.DstPort = port } // GetRawOpcode returns the raw opcode func (p *Packet) GetRawOpcode() uint16 { return p.Opcode } // ProtocolPacket represents a protocol-level packet type ProtocolPacket struct { *Packet EQ2Compressed bool PacketPrepared bool PacketEncrypted bool Acked bool SentTime int32 AttemptCount int8 Sequence uint16 } // NewProtocolPacket creates a new protocol packet from raw data func NewProtocolPacket(buf []byte, opcode int) *ProtocolPacket { if len(buf) < 2 { return nil } var op uint16 if opcode >= 0 { op = uint16(opcode) } else { op = binary.BigEndian.Uint16(buf[:2]) buf = buf[2:] } pp := &ProtocolPacket{ Packet: NewPacket(op, buf), } return pp } // NewProtocolPacketWithOp creates a new protocol packet with specific opcode func NewProtocolPacketWithOp(op uint16, buf []byte) *ProtocolPacket { return &ProtocolPacket{ Packet: NewPacket(op, buf), } } // ValidateCRC validates the CRC of a packet func ValidateCRC(buffer []byte, length int, key uint32) bool { // SessionRequest, SessionResponse, OutOfSession are not CRC'd if len(buffer) >= 2 && buffer[0] == 0x00 && (buffer[1] == OPSessionRequest || buffer[1] == OPSessionResponse || buffer[1] == OPOutOfSession) { return true } // Check for special case if len(buffer) >= 4 && buffer[2] == 0x00 && buffer[3] == 0x19 { return true } // Calculate and compare CRC if length < 2 { return false } compCRC := CalculateCRC16(buffer[:length-2], key) packetCRC := binary.BigEndian.Uint16(buffer[length-2:]) return packetCRC == 0 || compCRC == packetCRC } // Serialize serializes the protocol packet func (p *ProtocolPacket) Serialize(offset int8) []byte { // Calculate size including opcode totalSize := len(p.Buffer) + 2 if offset != 0 { totalSize += int(offset) } result := make([]byte, totalSize) pos := 0 // Add offset if needed if offset != 0 { pos = int(offset) } // Write opcode binary.BigEndian.PutUint16(result[pos:], p.Opcode) pos += 2 // Copy buffer if p.Buffer != nil { copy(result[pos:], p.Buffer) } return result } // Copy creates a copy of the protocol packet func (p *ProtocolPacket) Copy() *ProtocolPacket { newPacket := &ProtocolPacket{ Packet: NewPacket(p.Opcode, p.Buffer), EQ2Compressed: p.EQ2Compressed, PacketPrepared: p.PacketPrepared, PacketEncrypted: p.PacketEncrypted, Acked: p.Acked, SentTime: p.SentTime, AttemptCount: p.AttemptCount, Sequence: p.Sequence, } newPacket.Version = p.Version newPacket.Priority = p.Priority return newPacket } // Combine combines two protocol packets func (p *ProtocolPacket) Combine(rhs *ProtocolPacket) bool { if rhs.Opcode != p.Opcode { return false } // Create new buffer with combined size newSize := len(p.Buffer) + len(rhs.Buffer) newBuffer := make([]byte, newSize) copy(newBuffer, p.Buffer) copy(newBuffer[len(p.Buffer):], rhs.Buffer) p.Buffer = newBuffer p.Size = uint32(newSize) return true } // IsProtocolPacket checks if a buffer contains a protocol packet func IsProtocolPacket(inBuff []byte, length uint32, trimCRC bool) bool { if length < 2 { return false } opcode := binary.BigEndian.Uint16(inBuff) switch uint8(opcode & 0xFF) { case OPSessionRequest, OPSessionResponse, OPCombined, OPSessionDisconnect, OPKeepAlive, OPServerKeyRequest, OPSessionStatResponse, OPPacket, OPFragment, OPOutOfOrderAck, OPAck, OPAppCombined, OPOutOfSession: return true } return false } // Decompress decompresses a buffer func Decompress(buffer []byte, length uint32, newbuf []byte, newbufsize uint32) (uint32, error) { reader := bytes.NewReader(buffer[:length]) zlibReader, err := zlib.NewReader(reader) if err != nil { return 0, err } defer zlibReader.Close() buf := bytes.NewBuffer(newbuf[:0]) n, err := buf.ReadFrom(zlibReader) if err != nil { return 0, err } if uint32(n) > newbufsize { return 0, fmt.Errorf("decompressed size exceeds buffer size") } return uint32(n), nil } // Compress compresses a buffer func Compress(buffer []byte, length uint32, newbuf []byte, newbufsize uint32) (uint32, error) { var buf bytes.Buffer writer := zlib.NewWriter(&buf) _, err := writer.Write(buffer[:length]) if err != nil { return 0, err } err = writer.Close() if err != nil { return 0, err } compressed := buf.Bytes() if uint32(len(compressed)) > newbufsize { return 0, fmt.Errorf("compressed size exceeds buffer size") } copy(newbuf, compressed) return uint32(len(compressed)), nil } // ChatDecode decodes chat data func ChatDecode(buffer []byte, size int, decodeKey int) { for i := 0; i+4 <= size; i++ { c := buffer[i] buffer[i] = buffer[i+2] buffer[i+2] = c c = buffer[i+1] buffer[i+1] = buffer[i+3] buffer[i+3] = c key := uint32(decodeKey) p := (*uint32)(unsafe.Pointer(&buffer[i])) *p = (*p) ^ key i += 3 } } // ChatEncode encodes chat data func ChatEncode(buffer []byte, size int, encodeKey int) { for i := 0; i+4 <= size; i++ { key := uint32(encodeKey) p := (*uint32)(unsafe.Pointer(&buffer[i])) *p = (*p) ^ key c := buffer[i] buffer[i] = buffer[i+2] buffer[i+2] = c c = buffer[i+1] buffer[i+1] = buffer[i+3] buffer[i+3] = c i += 3 } } // ApplicationPacket represents an application-level packet type ApplicationPacket struct { *Packet EmuOpcode EmuOpcode AppOpcodeSize uint8 } // NewApplicationPacket creates a new application packet func NewApplicationPacket(op EmuOpcode, buf []byte) *ApplicationPacket { ap := &ApplicationPacket{ Packet: NewPacket(0, buf), EmuOpcode: op, AppOpcodeSize: 2, // Default for world/zone } return ap } // SetOpcode sets the opcode func (p *ApplicationPacket) SetOpcode(op EmuOpcode) { p.EmuOpcode = op p.Opcode = uint16(op) } // GetOpcode returns the opcode func (p *ApplicationPacket) GetOpcode() EmuOpcode { return p.EmuOpcode } // Copy creates a copy of the application packet func (p *ApplicationPacket) Copy() *ApplicationPacket { newPacket := &ApplicationPacket{ Packet: NewPacket(p.Opcode, p.Buffer), EmuOpcode: p.EmuOpcode, AppOpcodeSize: p.AppOpcodeSize, } newPacket.Version = p.Version return newPacket } // Serialize serializes the application packet func (p *ApplicationPacket) Serialize() []byte { size := len(p.Buffer) + int(p.AppOpcodeSize) result := make([]byte, size) // Write opcode based on size if p.AppOpcodeSize == 1 { result[0] = uint8(p.Opcode) copy(result[1:], p.Buffer) } else { binary.LittleEndian.PutUint16(result, p.Opcode) copy(result[2:], p.Buffer) } return result } // Combine combines two application packets func (p *ApplicationPacket) Combine(rhs *ApplicationPacket) bool { if rhs.EmuOpcode != p.EmuOpcode { return false } newSize := len(p.Buffer) + len(rhs.Buffer) newBuffer := make([]byte, newSize) copy(newBuffer, p.Buffer) copy(newBuffer[len(p.Buffer):], rhs.Buffer) p.Buffer = newBuffer p.Size = uint32(newSize) return true }