lsgo/binutils.go

540 lines
11 KiB
Go

package lsgo
import (
"bytes"
"compress/zlib"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"github.com/go-kit/kit/log"
"github.com/google/uuid"
"github.com/pierrec/lz4/v4"
"gonum.org/v1/gonum/mat"
)
func reverse(nums []byte) {
i := 0
j := len(nums) - 1
for i < j {
nums[i], nums[j] = nums[j], nums[i]
i++
j--
}
}
func clen(n []byte) int {
for i := 0; i < len(n); i++ {
if n[i] == 0 {
return i
}
}
return len(n)
}
func CompressionFlagsToMethod(flags byte) CompressionMethod {
switch CompressionMethod(flags & 0x0f) {
case CMNone:
return CMNone
case CMZlib:
return CMZlib
case CMLZ4:
return CMLZ4
default:
return CMInvalid
}
}
func CompressionFlagsToLevel(flags byte) CompressionLevel {
switch CompressionLevel(flags & 0xf0) {
case FastCompression:
return FastCompression
case DefaultCompression:
return DefaultCompression
case MaxCompression:
return MaxCompression
default:
panic(errors.New("invalid compression flags"))
}
// return 0
}
func MakeCompressionFlags(method CompressionMethod, level CompressionLevel) int {
if method == CMNone || method == CMInvalid {
return 0
}
var flags int = 0
if method == CMZlib {
flags = 0x1
} else if method == CMLZ4 {
flags = 0x2
}
return flags | int(level)
}
func Decompress(compressed io.Reader, uncompressedSize int, compressionFlags byte, chunked bool) io.ReadSeeker {
switch CompressionMethod(compressionFlags & 0x0f) {
case CMNone:
if v, ok := compressed.(io.ReadSeeker); ok {
return v
}
panic(errors.New("compressed must be an io.ReadSeeker if there is no compression"))
case CMZlib:
zr, _ := zlib.NewReader(compressed)
v, _ := ioutil.ReadAll(zr)
return bytes.NewReader(v)
case CMLZ4:
if chunked {
zr := lz4.NewReader(compressed)
p := make([]byte, uncompressedSize)
_, err := zr.Read(p)
if err != nil {
panic(err)
}
return bytes.NewReader(p)
}
src, _ := ioutil.ReadAll(compressed)
dst := make([]byte, uncompressedSize*2)
_, err := lz4.UncompressBlock(src, dst)
if err != nil {
panic(err)
}
return bytes.NewReader(dst)
default:
panic(fmt.Errorf("no decompressor found for this format: %v", compressionFlags))
}
}
func ReadCString(r io.Reader, length int) (string, error) {
var err error
buf := make([]byte, length)
_, err = r.Read(buf)
if err != nil {
return string(buf[:clen(buf)]), err
}
return string(buf[:clen(buf)]), nil
}
func ReadAttribute(r io.ReadSeeker, name string, DT DataType, length uint, l log.Logger) (NodeAttribute, error) {
var (
attr = NodeAttribute{
Type: DT,
Name: name,
}
err error
pos int64
n int
)
pos, _ = r.Seek(0, io.SeekCurrent)
switch DT {
case DTNone:
l.Log("member", name, "read", length, "start position", pos, "value", nil)
return attr, nil
case DTByte:
p := make([]byte, 1)
n, err = r.Read(p)
attr.Value = p[0]
l.Log("member", name, "read", n, "start position", pos, "value", attr.Value)
return attr, err
case DTShort:
var v int16
err = binary.Read(r, binary.LittleEndian, &v)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTUShort:
var v uint16
err = binary.Read(r, binary.LittleEndian, &v)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTInt:
var v int32
err = binary.Read(r, binary.LittleEndian, &v)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTUInt:
var v uint32
err = binary.Read(r, binary.LittleEndian, &v)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTFloat:
var v float32
err = binary.Read(r, binary.LittleEndian, &v)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTDouble:
var v float64
err = binary.Read(r, binary.LittleEndian, &v)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTIVec2, DTIVec3, DTIVec4:
var col int
col, err = attr.GetColumns()
if err != nil {
return attr, err
}
vec := make(Ivec, col)
for i := range vec {
var v int32
err = binary.Read(r, binary.LittleEndian, &v)
if err != nil {
return attr, err
}
vec[i] = int(v)
}
attr.Value = vec
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, nil
case DTVec2, DTVec3, DTVec4:
var col int
col, err = attr.GetColumns()
if err != nil {
return attr, err
}
vec := make(Vec, col)
for i := range vec {
var v float32
err = binary.Read(r, binary.LittleEndian, &v)
if err != nil {
return attr, err
}
vec[i] = float64(v)
}
attr.Value = vec
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, nil
case DTMat2, DTMat3, DTMat3x4, DTMat4x3, DTMat4:
var (
row int
col int
)
col, err = attr.GetColumns()
if err != nil {
return attr, err
}
row, err = attr.GetRows()
if err != nil {
return attr, err
}
vec := make(Vec, col*row)
for c := 0; c < col; c++ {
for ro := 0; ro < row; ro++ {
var v float32
err = binary.Read(r, binary.LittleEndian, &v)
if err != nil {
return attr, err
}
vec[ro*col+c] = float64(v)
}
}
attr.Value = (*Mat)(mat.NewDense(row, col, []float64(vec)))
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, nil
case DTBool:
var v bool
err = binary.Read(r, binary.LittleEndian, &v)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTULongLong:
var v uint64
err = binary.Read(r, binary.LittleEndian, &v)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTLong, DTInt64:
var v int64
err = binary.Read(r, binary.LittleEndian, &v)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTInt8:
var v int8
err = binary.Read(r, binary.LittleEndian, &v)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTUUID:
var v uuid.UUID
p := make([]byte, 16)
n, err = r.Read(p)
if err != nil {
return attr, err
}
reverse(p[:4])
reverse(p[4:6])
reverse(p[6:8])
v, err = uuid.FromBytes(p)
attr.Value = v
l.Log("member", name, "read", n, "start position", pos, "value", attr.Value)
return attr, err
default:
// Strings are serialized differently for each file format and should be
// handled by the format-specific ReadAttribute()
// pretty.Log(attr)
return attr, fmt.Errorf("readAttribute() not implemented for type %v", DT)
}
// return attr, nil
}
// LimitReadSeeker returns a Reader that reads from r
// but stops with EOF after n bytes.
// The underlying implementation is a *LimitedReader.
func LimitReadSeeker(r io.ReadSeeker, n int64) io.ReadSeeker { return &LimitedReadSeeker{r, n} }
// A LimitedReadSeeker reads from R but limits the amount of
// data returned to just N bytes. Each call to Read
// updates N to reflect the new amount remaining.
// Read returns EOF when N <= 0 or when the underlying R returns EOF.
type LimitedReadSeeker struct {
R io.ReadSeeker // underlying reader
N int64 // max bytes remaining
}
func (l *LimitedReadSeeker) Read(p []byte) (n int, err error) {
if l.N <= 0 {
return 0, io.EOF
}
if int64(len(p)) > l.N {
p = p[0:l.N]
}
n, err = l.R.Read(p)
l.N -= int64(n)
return
}
func (l *LimitedReadSeeker) Seek(offset int64, whence int) (int64, error) {
switch whence {
case io.SeekStart:
n, err := l.R.Seek(0, io.SeekCurrent)
if err != nil {
return n, err
}
l.N += n - offset
return l.R.Seek(offset, whence)
case io.SeekEnd:
n, err := l.R.Seek(l.N, io.SeekCurrent)
if err != nil {
return n, err
}
l.N = 0 - offset
return l.R.Seek(offset, io.SeekCurrent)
case io.SeekCurrent:
l.N -= offset
return l.R.Seek(offset, whence)
default:
return -1, io.ErrNoProgress
}
}
func ReadTranslatedString(r io.ReadSeeker, version FileVersion, engineVersion uint32) (TranslatedString, error) {
var (
str TranslatedString
err error
)
if version >= VerBG3 || engineVersion == 0x4000001d {
var version uint16
err = binary.Read(r, binary.LittleEndian, &version)
if err != nil {
return str, err
}
str.Version = version
err = binary.Read(r, binary.LittleEndian, &version)
if err != nil {
return str, err
}
if version == 0 {
str.Value, err = ReadCString(r, int(str.Version))
if err != nil {
return str, err
}
str.Version = 0
} else {
_, _ = r.Seek(-2, io.SeekCurrent)
}
} else {
str.Version = 0
var (
vlength int32
v []byte
// n int
)
err = binary.Read(r, binary.LittleEndian, &vlength)
if err != nil {
return str, err
}
v = make([]byte, vlength)
_, err = r.Read(v)
if err != nil {
return str, err
}
str.Value = string(v)
}
var handleLength int32
err = binary.Read(r, binary.LittleEndian, &handleLength)
if err != nil {
return str, err
}
str.Handle, err = ReadCString(r, int(handleLength))
if err != nil {
return str, err
}
return str, nil
}
func ReadTranslatedFSString(r io.Reader, version FileVersion) (TranslatedFSString, error) {
var (
str = TranslatedFSString{}
err error
)
if version >= VerBG3 {
var version uint16
err = binary.Read(r, binary.LittleEndian, &version)
if err != nil {
return str, err
}
str.Version = version
} else {
str.Version = 0
var length int32
err = binary.Read(r, binary.LittleEndian, &length)
if err != nil {
return str, err
}
str.Value, err = ReadCString(r, int(length))
if err != nil {
return str, err
}
}
var handleLength int32
err = binary.Read(r, binary.LittleEndian, &handleLength)
if err != nil {
return str, err
}
str.Handle, err = ReadCString(r, int(handleLength))
if err != nil {
return str, err
}
var arguments int32
err = binary.Read(r, binary.LittleEndian, &arguments)
if err != nil {
return str, err
}
str.Arguments = make([]TranslatedFSStringArgument, 0, arguments)
for i := 0; i < int(arguments); i++ {
arg := TranslatedFSStringArgument{}
var argKeyLength int32
err = binary.Read(r, binary.LittleEndian, &argKeyLength)
if err != nil {
return str, err
}
arg.Key, err = ReadCString(r, int(argKeyLength))
if err != nil {
return str, err
}
arg.String, err = ReadTranslatedFSString(r, version)
if err != nil {
return str, err
}
var argValueLength int32
err = binary.Read(r, binary.LittleEndian, &argValueLength)
if err != nil {
return str, err
}
arg.Value, err = ReadCString(r, int(argValueLength))
if err != nil {
return str, err
}
str.Arguments = append(str.Arguments, arg)
}
return str, nil
}