diff --git a/Erupe/common/byteframe/LICENSE b/Erupe/common/byteframe/LICENSE new file mode 100644 index 000000000..0284ad7d3 --- /dev/null +++ b/Erupe/common/byteframe/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Andrew Gutekanst + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Erupe/common/byteframe/byteframe.go b/Erupe/common/byteframe/byteframe.go new file mode 100644 index 000000000..ffeb66dc1 --- /dev/null +++ b/Erupe/common/byteframe/byteframe.go @@ -0,0 +1,375 @@ +package byteframe + +/* + This is HEAVILY based on the code from + https://github.com/sinni800/sgemu/blob/master/Core/Packet.go +*/ + +import ( + "bytes" + "encoding/binary" + "errors" + "io" + "math" +) + +// ByteFrame is a struct for reading and writing raw byte data. +type ByteFrame struct { + index uint + usedSize uint + buf []byte + byteOrder binary.ByteOrder +} + +// NewByteFrame creates a new ByteFrame with valid default values. +// byteOrder defaults to big endian. +func NewByteFrame() *ByteFrame { + b := &ByteFrame{ + index: 0, + usedSize: 0, + buf: make([]byte, 4), + byteOrder: binary.BigEndian, + } + return b +} + +// NewByteFrameFromBytes creates a new ByteFrame with valid default values. +// makes a copy of the given buf and initalizes with it. +// byteOrder defaults to big endian. +func NewByteFrameFromBytes(buf []byte) *ByteFrame { + b := &ByteFrame{ + index: 0, + usedSize: uint(len(buf)), + buf: make([]byte, len(buf)), + byteOrder: binary.BigEndian, + } + copy(b.buf, buf) + return b +} + +// grow either doubles the backing buffer size, or grows it by the size specified, whichever is larger. +func (b *ByteFrame) grow(size uint) { + bytesToAdd := uint(0) + if size > uint(len(b.buf)) { + bytesToAdd = size + } else { + bytesToAdd = uint(len(b.buf)) + } + + newBuf := make([]byte, uint(len(b.buf))+bytesToAdd) + copy(newBuf, b.buf) + b.buf = newBuf +} + +// wcheck checks if we have enough space to write. +func (b *ByteFrame) wcheck(size uint) { + if b.index+size > uint(len(b.buf)) { + b.grow(size) + } +} + +// wprologue is a helpler function to update state after a write. +func (b *ByteFrame) wprologue(size uint) { + + tmp := int(b.index+size) - int(b.usedSize) + if tmp > 0 { + b.usedSize += uint(tmp) + } + + b.index += size +} + +// rcheck checks if we have enough data to read. +func (b *ByteFrame) rcheck(size uint) bool { + if b.index+size > uint(len(b.buf)) || b.index+size > b.usedSize+1 { + return false + } + return true +} + +func (b *ByteFrame) rprologue(size uint) { + b.index += size +} + +func (b *ByteFrame) rerr() { + panic("Error while reading!") +} + +// Seek (implements the io.Seeker interface) +func (b *ByteFrame) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekStart: + if offset > int64(b.usedSize) { + return int64(b.index), errors.New("cannot seek beyond the max index") + } + b.index = uint(offset) + break + case io.SeekCurrent: + newPos := int64(b.index) + offset + if newPos > int64(b.usedSize) { + return int64(b.index), errors.New("cannot seek beyond the max index") + } else if newPos < 0 { + return int64(b.index), errors.New("cannot seek before the buffer start") + } + b.index = uint(newPos) + break + case io.SeekEnd: + newPos := int64(b.usedSize) + offset + if newPos > int64(b.usedSize) { + return int64(b.index), errors.New("cannot seek beyond the max index") + } else if newPos < 0 { + return int64(b.index), errors.New("cannot seek before the buffer start") + } + b.index = uint(newPos) + break + + } + + return int64(b.index), nil +} + +// Data returns the data from the buffer start up to the max index. +func (b *ByteFrame) Data() []byte { + return b.buf[:b.usedSize] +} + +// DataFromCurrent returns the data from the current index up to the max index. +func (b *ByteFrame) DataFromCurrent() []byte { + return b.buf[b.index:b.usedSize] +} + +// SetLE sets the byte order to litte endian. +func (b *ByteFrame) SetLE() { + b.byteOrder = binary.LittleEndian +} + +// SetBE sets the byte order to big endian. +func (b *ByteFrame) SetBE() { + b.byteOrder = binary.BigEndian +} + +// WriteUint8 writes a uint8 at the current index. +func (b *ByteFrame) WriteUint8(x uint8) { + b.wcheck(1) + b.buf[b.index] = x + b.wprologue(1) +} + +// WriteBool writes a bool at the current index +// (1 byte. true -> 1, false -> 0) +func (b *ByteFrame) WriteBool(x bool) { + if x { + b.WriteUint8(1) + } else { + b.WriteUint8(0) + } +} + +// WriteUint16 writes a uint16 at the current index. +func (b *ByteFrame) WriteUint16(x uint16) { + b.wcheck(2) + b.byteOrder.PutUint16(b.buf[b.index:], x) + b.wprologue(2) +} + +// WriteUint32 writes a uint32 at the current index. +func (b *ByteFrame) WriteUint32(x uint32) { + b.wcheck(4) + b.byteOrder.PutUint32(b.buf[b.index:], x) + b.wprologue(4) +} + +// WriteUint64 writes a uint64 at the current index. +func (b *ByteFrame) WriteUint64(x uint64) { + b.wcheck(8) + b.byteOrder.PutUint64(b.buf[b.index:], x) + b.wprologue(8) +} + +// WriteInt8 writes a int8 at the current index. +func (b *ByteFrame) WriteInt8(x int8) { + b.wcheck(1) + b.buf[b.index] = byte(x) + b.wprologue(1) +} + +// WriteInt16 writes a int16 at the current index. +func (b *ByteFrame) WriteInt16(x int16) { + b.wcheck(2) + b.byteOrder.PutUint16(b.buf[b.index:], uint16(x)) + b.wprologue(2) +} + +// WriteInt32 writes a int32 at the current index. +func (b *ByteFrame) WriteInt32(x int32) { + b.wcheck(4) + b.byteOrder.PutUint32(b.buf[b.index:], uint32(x)) + b.wprologue(4) +} + +// WriteInt64 writes a int64 at the current index. +func (b *ByteFrame) WriteInt64(x int64) { + b.wcheck(8) + b.byteOrder.PutUint64(b.buf[b.index:], uint64(x)) + b.wprologue(8) +} + +// WriteFloat32 writes a float32 at the current index. +func (b *ByteFrame) WriteFloat32(x float32) { + b.wcheck(4) + tmp := math.Float32bits(x) + b.byteOrder.PutUint32(b.buf[b.index:], tmp) + b.wprologue(4) +} + +// WriteFloat64 writes a float64 at the current index +func (b *ByteFrame) WriteFloat64(x float64) { + b.wcheck(8) + tmp := math.Float64bits(x) + b.byteOrder.PutUint64(b.buf[b.index:], tmp) + b.wprologue(8) +} + +// WriteBytes writes a slice of bytes at the current index. +func (b *ByteFrame) WriteBytes(x []byte) { + b.wcheck(uint(len(x))) + copy(b.buf[b.index:], x) + b.wprologue(uint(len(x))) +} + +// WriteNullTerminatedBytes write a slice bytes with an additional NULL terminator. +func (b *ByteFrame) WriteNullTerminatedBytes(x []byte) { + b.WriteBytes(x) + b.WriteUint8(0) +} + +// ReadUint8 reads a uint8 at the current index. +func (b *ByteFrame) ReadUint8() (x uint8) { + if !b.rcheck(1) { + b.rerr() + } + x = uint8(b.buf[b.index]) + b.rprologue(1) + return +} + +// ReadBool reads a bool at the current index +// (1 byte. b > 0 -> true, b == 0 -> false) +func (b *ByteFrame) ReadBool() (x bool) { + tmp := b.ReadUint8() + x = tmp > 0 + return +} + +// ReadUint16 reads a uint16 at the current index. +func (b *ByteFrame) ReadUint16() (x uint16) { + if !b.rcheck(2) { + b.rerr() + } + x = b.byteOrder.Uint16(b.buf[b.index:]) + b.rprologue(2) + return +} + +// ReadUint32 reads a uint32 at the current index. +func (b *ByteFrame) ReadUint32() (x uint32) { + if !b.rcheck(4) { + b.rerr() + } + x = b.byteOrder.Uint32(b.buf[b.index:]) + b.rprologue(4) + return +} + +// ReadUint64 reads a uint64 at the current index. +func (b *ByteFrame) ReadUint64() (x uint64) { + if !b.rcheck(8) { + b.rerr() + } + x = b.byteOrder.Uint64(b.buf[b.index:]) + b.rprologue(8) + return +} + +// ReadInt8 reads a int8 at the current index. +func (b *ByteFrame) ReadInt8() (x int8) { + if !b.rcheck(1) { + b.rerr() + } + x = int8(b.buf[b.index]) + b.rprologue(1) + return +} + +// ReadInt16 reads a int16 at the current index. +func (b *ByteFrame) ReadInt16() (x int16) { + if !b.rcheck(2) { + b.rerr() + } + x = int16(b.byteOrder.Uint16(b.buf[b.index:])) + b.rprologue(2) + return +} + +// ReadInt32 reads a int32 at the current index. +func (b *ByteFrame) ReadInt32() (x int32) { + if !b.rcheck(4) { + b.rerr() + } + x = int32(b.byteOrder.Uint32(b.buf[b.index:])) + b.rprologue(4) + return +} + +// ReadInt64 reads a int64 at the current index. +func (b *ByteFrame) ReadInt64() (x int64) { + if !b.rcheck(8) { + b.rerr() + } + x = int64(b.byteOrder.Uint64(b.buf[b.index:])) + b.rprologue(8) + return +} + +// ReadFloat32 reads a float32 at the current index. +func (b *ByteFrame) ReadFloat32() (x float32) { + if !b.rcheck(4) { + b.rerr() + } + x = math.Float32frombits(b.byteOrder.Uint32(b.buf[b.index:])) + b.rprologue(4) + return +} + +// ReadFloat64 reads a float64 at the current index. +func (b *ByteFrame) ReadFloat64() (x float64) { + if !b.rcheck(8) { + b.rerr() + } + x = math.Float64frombits(b.byteOrder.Uint64(b.buf[b.index:])) + b.rprologue(8) + return +} + +// ReadBytes reads `size` many bytes at the current index. +func (b *ByteFrame) ReadBytes(size uint) (x []byte) { + if !b.rcheck(size) { + b.rerr() + } + x = b.buf[b.index : b.index+size] + b.rprologue(size) + return +} + +// ReadNullTerminatedBytes reads bytes up to a NULL terminator. +func (b *ByteFrame) ReadNullTerminatedBytes() []byte { + tmpData := b.DataFromCurrent() + tmp := bytes.SplitN(tmpData, []byte{0x00}, 2)[0] + + if len(tmp) == len(tmpData) { + return []byte{} + } + + b.rprologue(uint(len(tmp)) + 1) + return tmp +}