package packets import ( "encoding/binary" "fmt" "io" "unsafe" ) // PacketReader for efficient packet parsing type PacketReader struct { data []byte offset int } func NewPacketReader(data []byte) *PacketReader { return &PacketReader{ data: data, offset: 0, } } func (r *PacketReader) Remaining() int { return len(r.data) - r.offset } func (r *PacketReader) Position() int { return r.offset } func (r *PacketReader) Seek(pos int) error { if pos < 0 || pos > len(r.data) { return fmt.Errorf("seek position out of bounds") } r.offset = pos return nil } func (r *PacketReader) Skip(n int) error { if r.offset+n > len(r.data) { return io.ErrUnexpectedEOF } r.offset += n return nil } // Read raw bytes func (r *PacketReader) ReadBytes(n int) ([]byte, error) { if r.offset+n > len(r.data) { return nil, io.ErrUnexpectedEOF } result := make([]byte, n) copy(result, r.data[r.offset:r.offset+n]) r.offset += n return result, nil } func (r *PacketReader) ReadBytesRef(n int) ([]byte, error) { if r.offset+n > len(r.data) { return nil, io.ErrUnexpectedEOF } result := r.data[r.offset : r.offset+n] r.offset += n return result, nil } // Read integers func (r *PacketReader) ReadUint8() (uint8, error) { if r.offset >= len(r.data) { return 0, io.ErrUnexpectedEOF } result := r.data[r.offset] r.offset++ return result, nil } func (r *PacketReader) ReadInt8() (int8, error) { val, err := r.ReadUint8() return int8(val), err } func (r *PacketReader) ReadUint16() (uint16, error) { if r.offset+2 > len(r.data) { return 0, io.ErrUnexpectedEOF } result := binary.LittleEndian.Uint16(r.data[r.offset:]) r.offset += 2 return result, nil } func (r *PacketReader) ReadInt16() (int16, error) { val, err := r.ReadUint16() return int16(val), err } func (r *PacketReader) ReadUint32() (uint32, error) { if r.offset+4 > len(r.data) { return 0, io.ErrUnexpectedEOF } result := binary.LittleEndian.Uint32(r.data[r.offset:]) r.offset += 4 return result, nil } func (r *PacketReader) ReadInt32() (int32, error) { val, err := r.ReadUint32() return int32(val), err } func (r *PacketReader) ReadUint64() (uint64, error) { if r.offset+8 > len(r.data) { return 0, io.ErrUnexpectedEOF } result := binary.LittleEndian.Uint64(r.data[r.offset:]) r.offset += 8 return result, nil } func (r *PacketReader) ReadInt64() (int64, error) { val, err := r.ReadUint64() return int64(val), err } func (r *PacketReader) ReadFloat32() (float32, error) { if r.offset+4 > len(r.data) { return 0, io.ErrUnexpectedEOF } bits := binary.LittleEndian.Uint32(r.data[r.offset:]) result := *(*float32)(unsafe.Pointer(&bits)) r.offset += 4 return result, nil } func (r *PacketReader) ReadFloat64() (float64, error) { if r.offset+8 > len(r.data) { return 0, io.ErrUnexpectedEOF } bits := binary.LittleEndian.Uint64(r.data[r.offset:]) result := *(*float64)(unsafe.Pointer(&bits)) r.offset += 8 return result, nil } // Read strings func (r *PacketReader) ReadString() (string, error) { length, err := r.ReadUint16() if err != nil { return "", err } if r.offset+int(length) > len(r.data) { return "", io.ErrUnexpectedEOF } result := string(r.data[r.offset : r.offset+int(length)]) r.offset += int(length) return result, nil } func (r *PacketReader) ReadStringFixed(length int) (string, error) { if r.offset+length > len(r.data) { return "", io.ErrUnexpectedEOF } // Find null terminator end := r.offset + length for i := r.offset; i < end; i++ { if r.data[i] == 0 { end = i break } } result := string(r.data[r.offset:end]) r.offset += length return result, nil } func (r *PacketReader) ReadCString() (string, error) { start := r.offset for r.offset < len(r.data) && r.data[r.offset] != 0 { r.offset++ } if r.offset >= len(r.data) { return "", io.ErrUnexpectedEOF } result := string(r.data[start:r.offset]) r.offset++ // Skip null terminator return result, nil } // EQ2-specific reads func (r *PacketReader) ReadEQ2String() (string, error) { // EQ2 strings are prefixed with length as uint16 return r.ReadString() } func (r *PacketReader) ReadPackedInt() (uint32, error) { // EQ2 packed integer format val, err := r.ReadUint8() if err != nil { return 0, err } if val < 0xFF { return uint32(val), nil } // Extended format val2, err := r.ReadUint16() if err != nil { return 0, err } if val2 < 0xFFFF { return uint32(val2), nil } // Full 32-bit return r.ReadUint32() } func (r *PacketReader) ReadBool() (bool, error) { val, err := r.ReadUint8() return val != 0, err } // Peek without advancing func (r *PacketReader) PeekUint8() (uint8, error) { if r.offset >= len(r.data) { return 0, io.ErrUnexpectedEOF } return r.data[r.offset], nil } func (r *PacketReader) PeekUint16() (uint16, error) { if r.offset+2 > len(r.data) { return 0, io.ErrUnexpectedEOF } return binary.LittleEndian.Uint16(r.data[r.offset:]), nil } func (r *PacketReader) PeekUint32() (uint32, error) { if r.offset+4 > len(r.data) { return 0, io.ErrUnexpectedEOF } return binary.LittleEndian.Uint32(r.data[r.offset:]), nil } // Buffer management func (r *PacketReader) Reset(data []byte) { r.data = data r.offset = 0 } func (r *PacketReader) Clone() *PacketReader { return &PacketReader{ data: r.data, offset: r.offset, } }