Compare commits

...

3 Commits

Author SHA1 Message Date
lordwelch
1523c67ee8 Add .gitignore 2021-01-11 19:05:41 -08:00
lordwelch
f56ee42b79 Change signatures to constant strings
Fix issues from golangci-lint

Add sublime project file
2021-01-11 01:24:36 -08:00
lordwelch
2f87df40d6 Move LSF and LSB decoding into their own packages
Adapt format.go from the image package
Change the only package level state variable to a function parameter
Load entire files into memory for performance
2021-01-06 01:08:20 -08:00
55 changed files with 604 additions and 474 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
lsconvert
*.sublime-workspace

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015 Norbyte Copyright (c) 2020 lordwelch
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -108,7 +108,6 @@ func (v Vec) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
switch i { switch i {
case 0: case 0:
name.Local = "x" name.Local = "x"
// start.Name = "float1"
case 1: case 1:
name.Local = "y" name.Local = "y"
start.Name.Local = "float2" start.Name.Local = "float2"
@ -385,9 +384,7 @@ func (na *NodeAttribute) FromString(str string) error {
} }
} }
var ( var err error
err error
)
switch na.Type { switch na.Type {
case DTNone: case DTNone:

View File

@ -85,21 +85,18 @@ func MakeCompressionFlags(method CompressionMethod, level CompressionLevel) int
func Decompress(compressed io.Reader, uncompressedSize int, compressionFlags byte, chunked bool) io.ReadSeeker { func Decompress(compressed io.Reader, uncompressedSize int, compressionFlags byte, chunked bool) io.ReadSeeker {
switch CompressionMethod(compressionFlags & 0x0f) { switch CompressionMethod(compressionFlags & 0x0f) {
case CMNone: case CMNone:
// logger.Println("No compression")
if v, ok := compressed.(io.ReadSeeker); ok { if v, ok := compressed.(io.ReadSeeker); ok {
return v return v
} }
panic(errors.New("compressed must be an io.ReadSeeker if there is no compression")) panic(errors.New("compressed must be an io.ReadSeeker if there is no compression"))
case CMZlib: case CMZlib:
// logger.Println("zlib compression")
zr, _ := zlib.NewReader(compressed) zr, _ := zlib.NewReader(compressed)
v, _ := ioutil.ReadAll(zr) v, _ := ioutil.ReadAll(zr)
return bytes.NewReader(v) return bytes.NewReader(v)
case CMLZ4: case CMLZ4:
if chunked { if chunked {
// logger.Println("lz4 stream compressed")
zr := lz4.NewReader(compressed) zr := lz4.NewReader(compressed)
p := make([]byte, uncompressedSize) p := make([]byte, uncompressedSize)
_, err := zr.Read(p) _, err := zr.Read(p)
@ -108,10 +105,7 @@ func Decompress(compressed io.Reader, uncompressedSize int, compressionFlags byt
} }
return bytes.NewReader(p) return bytes.NewReader(p)
} }
// logger.Println("lz4 block compressed")
// panic(errors.New("not implemented"))
src, _ := ioutil.ReadAll(compressed) src, _ := ioutil.ReadAll(compressed)
// logger.Println(len(src))
dst := make([]byte, uncompressedSize*2) dst := make([]byte, uncompressedSize*2)
_, err := lz4.UncompressBlock(src, dst) _, err := lz4.UncompressBlock(src, dst)
if err != nil { if err != nil {
@ -147,7 +141,7 @@ func ReadAttribute(r io.ReadSeeker, name string, DT DataType, length uint, l log
pos int64 pos int64
n int n int
) )
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
switch DT { switch DT {
case DTNone: case DTNone:
@ -406,3 +400,140 @@ func (l *LimitedReadSeeker) Seek(offset int64, whence int) (int64, error) {
return -1, io.ErrNoProgress 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
}

View File

@ -1,16 +1,20 @@
package main package main
import ( import (
"bytes"
"encoding/xml" "encoding/xml"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"git.narnian.us/lordwelch/lsgo" "git.narnian.us/lordwelch/lsgo"
_ "git.narnian.us/lordwelch/lsgo/lsb"
_ "git.narnian.us/lordwelch/lsgo/lsf"
"github.com/go-kit/kit/log" "github.com/go-kit/kit/log"
"github.com/kr/pretty" "github.com/kr/pretty"
@ -35,7 +39,6 @@ func init() {
} }
func main() { func main() {
for _, v := range flag.Args() { for _, v := range flag.Args() {
fi, err := os.Stat(v) fi, err := os.Stat(v)
if err != nil { if err != nil {
@ -43,7 +46,6 @@ func main() {
os.Exit(1) os.Exit(1)
} }
switch { switch {
case !fi.IsDir(): case !fi.IsDir():
err = openLSF(v) err = openLSF(v)
if err != nil && !errors.As(err, &lsgo.HeaderError{}) { if err != nil && !errors.As(err, &lsgo.HeaderError{}) {
@ -75,6 +77,7 @@ func main() {
} }
} }
} }
func openLSF(filename string) error { func openLSF(filename string) error {
var ( var (
l *lsgo.Resource l *lsgo.Resource
@ -118,17 +121,65 @@ func openLSF(filename string) error {
func readLSF(filename string) (*lsgo.Resource, error) { func readLSF(filename string) (*lsgo.Resource, error) {
var ( var (
l lsgo.Resource l lsgo.Resource
f *os.File r io.ReadSeeker
err error file *os.File
fi os.FileInfo
err error
) )
f, err = os.Open(filename) switch filepath.Ext(filename) {
if err != nil { case ".lsf", ".lsb":
return nil, err var b []byte
} fi, err = os.Stat(filename)
defer f.Close() if err != nil {
return nil, err
}
// Arbitrary size, no lsf file should reach 100 MB (I haven't found one over 90 KB)
// and if you don't have 100 MB of ram free you shouldn't be using this
if fi.Size() <= 100*1024*1024 {
b, err = ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
r = bytes.NewReader(b)
break
}
fallthrough
default:
b := make([]byte, 4)
file, err = os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
l, err = lsgo.ReadLSF(f) _, err = file.Read(b)
if err != nil {
return nil, err
}
if !lsgo.SupportedFormat(b) {
return nil, lsgo.ErrFormat
}
_, err = file.Seek(0, io.SeekStart)
if err != nil {
return nil, err
}
fi, _ = os.Stat(filename)
// I have never seen a valid "ls*" file over 90 KB
if fi.Size() < 1*1024*1024 {
b, err = ioutil.ReadAll(file)
if err != nil {
return nil, err
}
r = bytes.NewReader(b)
} else {
r = file
}
}
l, _, err = lsgo.Decode(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -159,9 +210,7 @@ func marshalXML(l *lsgo.Resource) (string, error) {
} }
func writeXML(f io.StringWriter, n string) error { func writeXML(f io.StringWriter, n string) error {
var ( var err error
err error
)
_, err = f.WriteString(strings.ToLower(xml.Header)) _, err = f.WriteString(strings.ToLower(xml.Header))
if err != nil { if err != nil {
return err return err

View File

@ -1,6 +1,9 @@
package lsgo package lsgo
import "errors" import (
"errors"
"fmt"
)
type FileVersion uint32 type FileVersion uint32
@ -43,3 +46,12 @@ var (
ErrInvalidNameKey = errors.New("invalid name key") ErrInvalidNameKey = errors.New("invalid name key")
ErrKeyDoesNotMatch = errors.New("key for this node does not match") ErrKeyDoesNotMatch = errors.New("key for this node does not match")
) )
type HeaderError struct {
Expected string
Got []byte
}
func (he HeaderError) Error() string {
return fmt.Sprintf("Invalid LSF signature; expected % X, got % X", he.Expected, he.Got)
}

92
format.go Normal file
View File

@ -0,0 +1,92 @@
// Adapted from the image package
package lsgo
import (
"errors"
"fmt"
"io"
"os"
"sync"
"sync/atomic"
)
// ErrFormat indicates that decoding encountered an unknown format.
var ErrFormat = errors.New("lsgo: unknown format")
// A format holds an image format's name, magic header and how to decode it.
type format struct {
name, magic string
decode func(io.ReadSeeker) (Resource, error)
}
// Formats is the list of registered formats.
var (
formatsMu sync.Mutex
atomicFormats atomic.Value
)
// RegisterFormat registers an image format for use by Decode.
// Name is the name of the format, like "jpeg" or "png".
// Magic is the magic prefix that identifies the format's encoding. The magic
// string can contain "?" wildcards that each match any one byte.
// Decode is the function that decodes the encoded image.
// DecodeConfig is the function that decodes just its configuration.
func RegisterFormat(name, magic string, decode func(io.ReadSeeker) (Resource, error)) {
formatsMu.Lock()
formats, _ := atomicFormats.Load().([]format)
atomicFormats.Store(append(formats, format{name, magic, decode}))
formatsMu.Unlock()
}
// Match reports whether magic matches b. Magic may contain "?" wildcards.
func match(magic string, b []byte) bool {
if len(magic) != len(b) {
return false
}
for i, c := range b {
if magic[i] != c && magic[i] != '?' {
return false
}
}
return true
}
// Sniff determines the format of r's data.
func sniff(r io.ReadSeeker) format {
var (
b []byte = make([]byte, 4)
err error
)
formats, _ := atomicFormats.Load().([]format)
for _, f := range formats {
if len(b) < len(f.magic) {
fmt.Fprintln(os.Stderr, f.magic)
b = make([]byte, len(f.magic))
}
_, err = r.Read(b)
_, _ = r.Seek(0, io.SeekStart)
if err == nil && match(f.magic, b) {
return f
}
}
return format{}
}
func Decode(r io.ReadSeeker) (Resource, string, error) {
f := sniff(r)
if f.decode == nil {
return Resource{}, "", ErrFormat
}
m, err := f.decode(r)
return m, f.name, err
}
func SupportedFormat(signature []byte) bool {
formats, _ := atomicFormats.Load().([]format)
for _, f := range formats {
if match(f.magic, signature) {
return true
}
}
return false
}

8
go.mod
View File

@ -2,12 +2,12 @@ module git.narnian.us/lordwelch/lsgo
go 1.15 go 1.15
replace github.com/pierrec/lz4/v4 v4.1.1 => ./lz4 replace github.com/pierrec/lz4/v4 v4.1.3 => ./third_party/lz4
require ( require (
github.com/go-kit/kit v0.10.0 github.com/go-kit/kit v0.10.0
github.com/google/uuid v1.1.2 github.com/google/uuid v1.1.4
github.com/kr/pretty v0.2.1 github.com/kr/pretty v0.2.1
github.com/pierrec/lz4/v4 v4.1.1 github.com/pierrec/lz4/v4 v4.1.3
gonum.org/v1/gonum v0.8.1 gonum.org/v1/gonum v0.8.2
) )

8
go.sum
View File

@ -86,8 +86,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@ -329,8 +329,8 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.1 h1:wGtP3yGpc5mCLOLeTeBdjeui9oZSz5De0eOjMLC/QuQ= gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
gonum.org/v1/gonum v0.8.1/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=

View File

@ -1,4 +1,4 @@
package lsgo package lsb
import ( import (
"encoding/binary" "encoding/binary"
@ -7,94 +7,101 @@ import (
"io" "io"
"sort" "sort"
"git.narnian.us/lordwelch/lsgo"
"github.com/go-kit/kit/log" "github.com/go-kit/kit/log"
) )
type LSBHeader struct { const (
Signature = "LSFM"
PreBG3Signature = "\x00\x00\x00\x40"
)
type Header struct {
Signature [4]byte Signature [4]byte
Size uint32 Size uint32
Endianness uint32 Endianness uint32
Unknown uint32 Unknown uint32
Version LSMetadata Version lsgo.LSMetadata
} }
func (lsbh *LSBHeader) Read(r io.ReadSeeker) error { func (h *Header) Read(r io.ReadSeeker) error {
var ( var (
l log.Logger l log.Logger
pos int64 pos int64
n int n int
err error err error
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "header") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "header")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
n, err = r.Read(lsbh.Signature[:]) n, err = r.Read(h.Signature[:])
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Signature", "read", n, "start position", pos, "value", fmt.Sprintf("%#x", lsbh.Signature[:])) l.Log("member", "Signature", "read", n, "start position", pos, "value", fmt.Sprintf("%#x", h.Signature[:]))
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsbh.Size) err = binary.Read(r, binary.LittleEndian, &h.Size)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Size", "read", n, "start position", pos, "value", lsbh.Size) l.Log("member", "Size", "read", n, "start position", pos, "value", h.Size)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsbh.Endianness) err = binary.Read(r, binary.LittleEndian, &h.Endianness)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Endianness", "read", n, "start position", pos, "value", lsbh.Endianness) l.Log("member", "Endianness", "read", n, "start position", pos, "value", h.Endianness)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsbh.Unknown) err = binary.Read(r, binary.LittleEndian, &h.Unknown)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Unknown", "read", n, "start position", pos, "value", lsbh.Unknown) l.Log("member", "Unknown", "read", n, "start position", pos, "value", h.Unknown)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Timestamp) err = binary.Read(r, binary.LittleEndian, &h.Version.Timestamp)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Version.Timestamp", "read", n, "start position", pos, "value", lsbh.Version.Timestamp) l.Log("member", "Version.Timestamp", "read", n, "start position", pos, "value", h.Version.Timestamp)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Major) err = binary.Read(r, binary.LittleEndian, &h.Version.Major)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Version.Major", "read", n, "start position", pos, "value", lsbh.Version.Major) l.Log("member", "Version.Major", "read", n, "start position", pos, "value", h.Version.Major)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Minor) err = binary.Read(r, binary.LittleEndian, &h.Version.Minor)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Version.Minor", "read", n, "start position", pos, "value", lsbh.Version.Minor) l.Log("member", "Version.Minor", "read", n, "start position", pos, "value", h.Version.Minor)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Revision) err = binary.Read(r, binary.LittleEndian, &h.Version.Revision)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Version.Revision", "read", n, "start position", pos, "value", lsbh.Version.Revision) l.Log("member", "Version.Revision", "read", n, "start position", pos, "value", h.Version.Revision)
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Build) err = binary.Read(r, binary.LittleEndian, &h.Version.Build)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Version.Build", "read", n, "start position", pos, "value", lsbh.Version.Build) l.Log("member", "Version.Build", "read", n, "start position", pos, "value", h.Version.Build)
pos += int64(n) pos += int64(n)
return nil return nil
@ -102,43 +109,42 @@ func (lsbh *LSBHeader) Read(r io.ReadSeeker) error {
type IdentifierDictionary map[int]string type IdentifierDictionary map[int]string
func ReadLSB(r io.ReadSeeker) (Resource, error) { func Read(r io.ReadSeeker) (lsgo.Resource, error) {
var ( var (
hdr = &LSBHeader{} hdr = &Header{}
h = [4]byte{0x00, 0x00, 0x00, 0x40}
err error err error
d IdentifierDictionary d IdentifierDictionary
res Resource res lsgo.Resource
l log.Logger l log.Logger
pos int64 pos int64
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "file") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "file")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
l.Log("member", "header", "start position", pos) l.Log("member", "header", "start position", pos)
err = hdr.Read(r) err = hdr.Read(r)
if err != nil { if err != nil {
return Resource{}, err return lsgo.Resource{}, err
} }
if !(hdr.Signature == [4]byte{'L', 'S', 'F', 'M'} || hdr.Signature == h) { if !(string(hdr.Signature[:]) == Signature || string(hdr.Signature[:]) == PreBG3Signature) {
return Resource{}, HeaderError{ return lsgo.Resource{}, lsgo.HeaderError{
Expected: []byte("LSFM"), Expected: Signature,
Got: hdr.Signature[:], Got: hdr.Signature[:],
} }
} }
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
l.Log("member", "string dictionary", "start position", pos) l.Log("member", "string dictionary", "start position", pos)
d, err = ReadLSBDictionary(r, binary.LittleEndian) d, err = ReadLSBDictionary(r, binary.LittleEndian)
if err != nil { if err != nil {
return Resource{}, err return lsgo.Resource{}, err
} }
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
l.Log("member", "Regions", "start position", pos) l.Log("member", "Regions", "start position", pos)
res, err = ReadLSBRegions(r, d, binary.LittleEndian, FileVersion(hdr.Version.Major)) res, err = ReadLSBRegions(r, d, binary.LittleEndian, lsgo.FileVersion(hdr.Version.Major))
res.Metadata = hdr.Version res.Metadata = hdr.Version
return res, err return res, err
} }
@ -153,8 +159,8 @@ func ReadLSBDictionary(r io.ReadSeeker, endianness binary.ByteOrder) (Identifier
pos int64 pos int64
n int n int
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "dictionary") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "dictionary")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, endianness, &length) err = binary.Read(r, endianness, &length)
n = 4 n = 4
@ -179,7 +185,7 @@ func ReadLSBDictionary(r io.ReadSeeker, endianness binary.ByteOrder) (Identifier
l.Log("member", "stringLength", "read", n, "start position", pos, "value", stringLength) l.Log("member", "stringLength", "read", n, "start position", pos, "value", stringLength)
pos += int64(n) pos += int64(n)
str, err = ReadCString(r, int(stringLength)) str, err = lsgo.ReadCString(r, int(stringLength))
n += int(stringLength) n += int(stringLength)
if err != nil { if err != nil {
return dict, err return dict, err
@ -199,7 +205,7 @@ func ReadLSBDictionary(r io.ReadSeeker, endianness binary.ByteOrder) (Identifier
return dict, nil return dict, nil
} }
func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version FileVersion) (Resource, error) { func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version lsgo.FileVersion) (lsgo.Resource, error) {
var ( var (
regions []struct { regions []struct {
name string name string
@ -212,13 +218,13 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
pos int64 pos int64
n int n int
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "region") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "region")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, endianness, &regionCount) err = binary.Read(r, endianness, &regionCount)
n = 4 n = 4
if err != nil { if err != nil {
return Resource{}, err return lsgo.Resource{}, err
} }
l.Log("member", "regionCount", "read", n, "start position", pos, "value", regionCount) l.Log("member", "regionCount", "read", n, "start position", pos, "value", regionCount)
pos += int64(n) pos += int64(n)
@ -235,17 +241,17 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
err = binary.Read(r, endianness, &key) err = binary.Read(r, endianness, &key)
n = 4 n = 4
if err != nil { if err != nil {
return Resource{}, err return lsgo.Resource{}, err
} }
l.Log("member", "key", "read", n, "start position", pos, "value", d[int(key)], "key", key) l.Log("member", "key", "read", n, "start position", pos, "value", d[int(key)], "key", key)
pos += int64(n) pos += int64(n)
if regions[i].name, ok = d[int(key)]; !ok { if regions[i].name, ok = d[int(key)]; !ok {
return Resource{}, ErrInvalidNameKey return lsgo.Resource{}, lsgo.ErrInvalidNameKey
} }
err = binary.Read(r, endianness, &regions[i].offset) err = binary.Read(r, endianness, &regions[i].offset)
n = 4 n = 4
if err != nil { if err != nil {
return Resource{}, err return lsgo.Resource{}, err
} }
l.Log("member", "offset", "read", n, "start position", pos, "value", regions[i].offset) l.Log("member", "offset", "read", n, "start position", pos, "value", regions[i].offset)
pos += int64(n) pos += int64(n)
@ -253,11 +259,11 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
sort.Slice(regions, func(i, j int) bool { sort.Slice(regions, func(i, j int) bool {
return regions[i].offset < regions[j].offset return regions[i].offset < regions[j].offset
}) })
res := Resource{ res := lsgo.Resource{
Regions: make([]*Node, 0, regionCount), Regions: make([]*lsgo.Node, 0, regionCount),
} }
for _, re := range regions { for _, re := range regions {
var node *Node var node *lsgo.Node
node, err = readLSBNode(r, d, endianness, version, re.offset) node, err = readLSBNode(r, d, endianness, version, re.offset)
if err != nil { if err != nil {
return res, err return res, err
@ -268,12 +274,12 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
return res, nil return res, nil
} }
func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version FileVersion, offset uint32) (*Node, error) { func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version lsgo.FileVersion, offset uint32) (*lsgo.Node, error) {
var ( var (
key uint32 key uint32
attrCount uint32 attrCount uint32
childCount uint32 childCount uint32
node = new(Node) node = new(lsgo.Node)
err error err error
ok bool ok bool
@ -281,8 +287,8 @@ func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.Byte
pos int64 pos int64
n int n int
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "node") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "node")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
if pos != int64(offset) && offset != 0 { if pos != int64(offset) && offset != 0 {
panic("shit") panic("shit")
@ -315,7 +321,7 @@ func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.Byte
} }
l.Log("member", "childCount", "read", n, "start position", pos, "value", childCount) l.Log("member", "childCount", "read", n, "start position", pos, "value", childCount)
node.Attributes = make([]NodeAttribute, int(attrCount)) node.Attributes = make([]lsgo.NodeAttribute, int(attrCount))
for i := range node.Attributes { for i := range node.Attributes {
node.Attributes[i], err = readLSBAttribute(r, d, endianness, version) node.Attributes[i], err = readLSBAttribute(r, d, endianness, version)
@ -324,7 +330,7 @@ func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.Byte
} }
} }
node.Children = make([]*Node, int(childCount)) node.Children = make([]*lsgo.Node, int(childCount))
for i := range node.Children { for i := range node.Children {
node.Children[i], err = readLSBNode(r, d, endianness, version, 0) node.Children[i], err = readLSBNode(r, d, endianness, version, 0)
if err != nil { if err != nil {
@ -334,12 +340,12 @@ func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.Byte
return node, nil return node, nil
} }
func readLSBAttribute(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version FileVersion) (NodeAttribute, error) { func readLSBAttribute(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version lsgo.FileVersion) (lsgo.NodeAttribute, error) {
var ( var (
key uint32 key uint32
name string name string
attrType uint32 attrType uint32
attr NodeAttribute attr lsgo.NodeAttribute
err error err error
ok bool ok bool
) )
@ -348,21 +354,21 @@ func readLSBAttribute(r io.ReadSeeker, d IdentifierDictionary, endianness binary
return attr, err return attr, err
} }
if name, ok = d[int(key)]; !ok { if name, ok = d[int(key)]; !ok {
return attr, ErrInvalidNameKey return attr, lsgo.ErrInvalidNameKey
} }
err = binary.Read(r, endianness, &attrType) err = binary.Read(r, endianness, &attrType)
if err != nil { if err != nil {
return attr, err return attr, err
} }
return ReadLSBAttr(r, name, DataType(attrType), endianness, version) return ReadLSBAttr(r, name, lsgo.DataType(attrType), endianness, version)
} }
func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.ByteOrder, version FileVersion) (NodeAttribute, error) { func ReadLSBAttr(r io.ReadSeeker, name string, dt lsgo.DataType, endianness binary.ByteOrder, version lsgo.FileVersion) (lsgo.NodeAttribute, error) {
// LSF and LSB serialize the buffer types differently, so specialized // LSF and LSB serialize the buffer types differently, so specialized
// code is added to the LSB and LSf serializers, and the common code is // code is added to the LSB and LSf serializers, and the common code is
// available in BinUtils.ReadAttribute() // available in BinUtils.ReadAttribute()
var ( var (
attr = NodeAttribute{ attr = lsgo.NodeAttribute{
Type: dt, Type: dt,
Name: name, Name: name,
} }
@ -372,38 +378,38 @@ func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.By
l log.Logger l log.Logger
pos int64 pos int64
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "attribute") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "attribute")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
switch dt { switch dt {
case DTString, DTPath, DTFixedString, DTLSString: //, DTLSWString: case lsgo.DTString, lsgo.DTPath, lsgo.DTFixedString, lsgo.DTLSString: // DTLSWString:
var v string var v string
err = binary.Read(r, endianness, &length) err = binary.Read(r, endianness, &length)
if err != nil { if err != nil {
return attr, err return attr, err
} }
v, err = ReadCString(r, int(length)) v, err = lsgo.ReadCString(r, int(length))
attr.Value = v attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value) l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err return attr, err
case DTWString: case lsgo.DTWString:
panic("Not implemented") panic("Not implemented")
case DTTranslatedString: case lsgo.DTTranslatedString:
var v TranslatedString var v lsgo.TranslatedString
v, err = ReadTranslatedString(r, version, 0) v, err = lsgo.ReadTranslatedString(r, version, 0)
attr.Value = v attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value) l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err return attr, err
case DTTranslatedFSString: case lsgo.DTTranslatedFSString:
panic("Not implemented") panic("Not implemented")
var v TranslatedFSString var v lsgo.TranslatedFSString
// v, err = ReadTranslatedFSString(r, Version) // v, err = ReadTranslatedFSString(r, Version)
attr.Value = v attr.Value = v
@ -411,7 +417,7 @@ func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.By
return attr, err return attr, err
case DTScratchBuffer: case lsgo.DTScratchBuffer:
panic("Not implemented") panic("Not implemented")
v := make([]byte, length) v := make([]byte, length)
@ -423,6 +429,11 @@ func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.By
return attr, err return attr, err
default: default:
return ReadAttribute(r, name, dt, uint(length), l) return lsgo.ReadAttribute(r, name, dt, uint(length), l)
} }
} }
func init() {
lsgo.RegisterFormat("lsb", Signature, Read)
lsgo.RegisterFormat("lsb", PreBG3Signature, Read)
}

View File

@ -1,61 +1,24 @@
package lsgo package lsf
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"strconv" "strconv"
"strings"
"git.narnian.us/lordwelch/lsgo"
"github.com/go-kit/kit/log" "github.com/go-kit/kit/log"
) )
var ( const Signature = "LSOF"
LSFSignature = [4]byte{0x4C, 0x53, 0x4F, 0x46}
Logger log.Logger = log.NewNopLogger()
)
// NewFilter allows filtering of l type Header struct {
func NewFilter(f map[string][]string, l log.Logger) log.Logger {
return filter{
filter: f,
next: l,
}
}
type filter struct {
next log.Logger
filter map[string][]string
}
func (f filter) Log(keyvals ...interface{}) error {
var allowed = true // allow everything
for i := 0; i < len(keyvals)-1; i += 2 {
if v, ok := keyvals[i].(string); ok { // key
if fil, ok := f.filter[v]; ok { // key has a filter
if v, ok = keyvals[i+1].(string); ok { // value is a string
allowed = false // this key has a filter deny everything except what the filter allows
for _, fi := range fil {
if strings.Contains(v, fi) {
allowed = true
}
}
}
}
}
}
if allowed {
return f.next.Log(keyvals...)
}
return nil
}
type LSFHeader struct {
// LSOF file signature // LSOF file signature
Signature [4]byte Signature [4]byte
// Version of the LSOF file D:OS EE is version 1/2, D:OS 2 is version 3 // Version of the LSOF file D:OS EE is version 1/2, D:OS 2 is version 3
Version FileVersion Version lsgo.FileVersion
// Possibly version number? (major, minor, rev, build) // Possibly version number? (major, minor, rev, build)
EngineVersion uint32 EngineVersion uint32
@ -83,7 +46,6 @@ type LSFHeader struct {
// Compressed size of the raw value buffer // Compressed size of the raw value buffer
ValuesSizeOnDisk uint32 ValuesSizeOnDisk uint32
// summary
// Uses the same format as packages (see BinUtils.MakeCompressionFlags) // Uses the same format as packages (see BinUtils.MakeCompressionFlags)
CompressionFlags byte CompressionFlags byte
@ -96,166 +58,158 @@ type LSFHeader struct {
Extended uint32 Extended uint32
} }
func (lsfh *LSFHeader) Read(r io.ReadSeeker) error { func (h *Header) Read(r io.ReadSeeker) error {
var ( var (
l log.Logger l log.Logger
pos int64 pos int64
n int n int
err error err error
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "header") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "header")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
n, err = r.Read(lsfh.Signature[:]) n, err = r.Read(h.Signature[:])
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Signature", "read", n, "start position", pos, "value", string(lsfh.Signature[:])) l.Log("member", "Signature", "read", n, "start position", pos, "value", string(h.Signature[:]))
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.Version) err = binary.Read(r, binary.LittleEndian, &h.Version)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Version", "read", n, "start position", pos, "value", lsfh.Version) l.Log("member", "Version", "read", n, "start position", pos, "value", h.Version)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.EngineVersion) err = binary.Read(r, binary.LittleEndian, &h.EngineVersion)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "EngineVersion", "read", n, "start position", pos, "value", fmt.Sprintf("%d.%d.%d.%d", (lsfh.EngineVersion&0xf0000000)>>28, (lsfh.EngineVersion&0xf000000)>>24, (lsfh.EngineVersion&0xff0000)>>16, (lsfh.EngineVersion&0xffff))) l.Log("member", "EngineVersion", "read", n, "start position", pos, "value", fmt.Sprintf("%d.%d.%d.%d", (h.EngineVersion&0xf0000000)>>28, (h.EngineVersion&0xf000000)>>24, (h.EngineVersion&0xff0000)>>16, (h.EngineVersion&0xffff)))
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.StringsUncompressedSize) err = binary.Read(r, binary.LittleEndian, &h.StringsUncompressedSize)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "StringsUncompressedSize", "read", n, "start position", pos, "value", lsfh.StringsUncompressedSize) l.Log("member", "StringsUncompressedSize", "read", n, "start position", pos, "value", h.StringsUncompressedSize)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.StringsSizeOnDisk) err = binary.Read(r, binary.LittleEndian, &h.StringsSizeOnDisk)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "StringsSizeOnDisk", "read", n, "start position", pos, "value", lsfh.StringsSizeOnDisk) l.Log("member", "StringsSizeOnDisk", "read", n, "start position", pos, "value", h.StringsSizeOnDisk)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.NodesUncompressedSize) err = binary.Read(r, binary.LittleEndian, &h.NodesUncompressedSize)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "NodesUncompressedSize", "read", n, "start position", pos, "value", lsfh.NodesUncompressedSize) l.Log("member", "NodesUncompressedSize", "read", n, "start position", pos, "value", h.NodesUncompressedSize)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.NodesSizeOnDisk) err = binary.Read(r, binary.LittleEndian, &h.NodesSizeOnDisk)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "NodesSizeOnDisk", "read", n, "start position", pos, "value", lsfh.NodesSizeOnDisk) l.Log("member", "NodesSizeOnDisk", "read", n, "start position", pos, "value", h.NodesSizeOnDisk)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.AttributesUncompressedSize) err = binary.Read(r, binary.LittleEndian, &h.AttributesUncompressedSize)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "AttributesUncompressedSize", "read", n, "start position", pos, "value", lsfh.AttributesUncompressedSize) l.Log("member", "AttributesUncompressedSize", "read", n, "start position", pos, "value", h.AttributesUncompressedSize)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.AttributesSizeOnDisk) err = binary.Read(r, binary.LittleEndian, &h.AttributesSizeOnDisk)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "AttributesSizeOnDisk", "read", n, "start position", pos, "value", lsfh.AttributesSizeOnDisk) l.Log("member", "AttributesSizeOnDisk", "read", n, "start position", pos, "value", h.AttributesSizeOnDisk)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.ValuesUncompressedSize) err = binary.Read(r, binary.LittleEndian, &h.ValuesUncompressedSize)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "ValuesUncompressedSize", "read", n, "start position", pos, "value", lsfh.ValuesUncompressedSize) l.Log("member", "ValuesUncompressedSize", "read", n, "start position", pos, "value", h.ValuesUncompressedSize)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.ValuesSizeOnDisk) err = binary.Read(r, binary.LittleEndian, &h.ValuesSizeOnDisk)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "ValuesSizeOnDisk", "read", n, "start position", pos, "value", lsfh.ValuesSizeOnDisk) l.Log("member", "ValuesSizeOnDisk", "read", n, "start position", pos, "value", h.ValuesSizeOnDisk)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.CompressionFlags) err = binary.Read(r, binary.LittleEndian, &h.CompressionFlags)
n = 1 n = 1
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "CompressionFlags", "read", n, "start position", pos, "value", lsfh.CompressionFlags) l.Log("member", "CompressionFlags", "read", n, "start position", pos, "value", h.CompressionFlags)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.Unknown2) err = binary.Read(r, binary.LittleEndian, &h.Unknown2)
n = 1 n = 1
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Unknown2", "read", n, "start position", pos, "value", lsfh.Unknown2) l.Log("member", "Unknown2", "read", n, "start position", pos, "value", h.Unknown2)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.Unknown3) err = binary.Read(r, binary.LittleEndian, &h.Unknown3)
n = 2 n = 2
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Unknown3", "read", n, "start position", pos, "value", lsfh.Unknown3) l.Log("member", "Unknown3", "read", n, "start position", pos, "value", h.Unknown3)
pos += int64(n) pos += int64(n)
err = binary.Read(r, binary.LittleEndian, &lsfh.Extended) err = binary.Read(r, binary.LittleEndian, &h.Extended)
n = 4 n = 4
if err != nil { if err != nil {
return err return err
} }
l.Log("member", "Extended", "read", n, "start position", pos, "value", lsfh.Extended) l.Log("member", "Extended", "read", n, "start position", pos, "value", h.Extended)
pos += int64(n) pos += int64(n)
if !lsfh.IsCompressed() { if !h.IsCompressed() {
lsfh.NodesSizeOnDisk = lsfh.NodesUncompressedSize h.NodesSizeOnDisk = h.NodesUncompressedSize
lsfh.AttributesSizeOnDisk = lsfh.AttributesUncompressedSize h.AttributesSizeOnDisk = h.AttributesUncompressedSize
lsfh.StringsSizeOnDisk = lsfh.StringsUncompressedSize h.StringsSizeOnDisk = h.StringsUncompressedSize
lsfh.ValuesSizeOnDisk = lsfh.ValuesUncompressedSize h.ValuesSizeOnDisk = h.ValuesUncompressedSize
} }
return nil return nil
} }
func (lsfh LSFHeader) IsCompressed() bool { func (h Header) IsCompressed() bool {
return CompressionFlagsToMethod(lsfh.CompressionFlags) != CMNone && CompressionFlagsToMethod(lsfh.CompressionFlags) != CMInvalid return lsgo.CompressionFlagsToMethod(h.CompressionFlags) != lsgo.CMNone && lsgo.CompressionFlagsToMethod(h.CompressionFlags) != lsgo.CMInvalid
} }
type NodeEntry struct { type NodeEntry struct {
Long bool Long bool
// summary
// (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain) // (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain)
NameHashTableIndex uint32 NameHashTableIndex uint32
// summary
// (-1: node has no attributes) // (-1: node has no attributes)
FirstAttributeIndex int32 FirstAttributeIndex int32
// summary
// (-1: this node is a root region) // (-1: this node is a root region)
ParentIndex int32 ParentIndex int32
// summary
// (-1: this is the last node) // (-1: this is the last node)
NextSiblingIndex int32 NextSiblingIndex int32
} }
@ -274,12 +228,11 @@ func (ne *NodeEntry) readShort(r io.ReadSeeker) error {
err error err error
n int n int
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "short node") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "short node")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &ne.NameHashTableIndex) err = binary.Read(r, binary.LittleEndian, &ne.NameHashTableIndex)
n = 4 n = 4
if err != nil { if err != nil {
// logger.Println(err, "ne.NameHashTableIndex", ne.NameHashTableIndex)
return err return err
} }
l.Log("member", "NameHashTableIndex", "read", n, "start position", pos, "value", strconv.Itoa(ne.NameIndex())+" "+strconv.Itoa(ne.NameOffset())) l.Log("member", "NameHashTableIndex", "read", n, "start position", pos, "value", strconv.Itoa(ne.NameIndex())+" "+strconv.Itoa(ne.NameOffset()))
@ -288,7 +241,6 @@ func (ne *NodeEntry) readShort(r io.ReadSeeker) error {
err = binary.Read(r, binary.LittleEndian, &ne.FirstAttributeIndex) err = binary.Read(r, binary.LittleEndian, &ne.FirstAttributeIndex)
n = 4 n = 4
if err != nil { if err != nil {
// logger.Println(err, "ne.FirstAttributeIndex", ne.FirstAttributeIndex)
return err return err
} }
l.Log("member", "NameHashTableIndex", "read", n, "start position", pos, "value", ne.FirstAttributeIndex) l.Log("member", "NameHashTableIndex", "read", n, "start position", pos, "value", ne.FirstAttributeIndex)
@ -297,7 +249,6 @@ func (ne *NodeEntry) readShort(r io.ReadSeeker) error {
err = binary.Read(r, binary.LittleEndian, &ne.ParentIndex) err = binary.Read(r, binary.LittleEndian, &ne.ParentIndex)
n = 4 n = 4
if err != nil { if err != nil {
// logger.Println(err, "ne.ParentIndex", ne.ParentIndex)
return err return err
} }
l.Log("member", "NameHashTableIndex", "read", n, "start position", pos, "value", ne.ParentIndex) l.Log("member", "NameHashTableIndex", "read", n, "start position", pos, "value", ne.ParentIndex)
@ -312,8 +263,8 @@ func (ne *NodeEntry) readLong(r io.ReadSeeker) error {
err error err error
n int n int
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "long node") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "long node")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &ne.NameHashTableIndex) err = binary.Read(r, binary.LittleEndian, &ne.NameHashTableIndex)
n = 4 n = 4
if err != nil { if err != nil {
@ -359,7 +310,6 @@ func (ne NodeEntry) NameOffset() int {
// Processed node information for a node in the LSF file // Processed node information for a node in the LSF file
type NodeInfo struct { type NodeInfo struct {
// summary
// (-1: this node is a root region) // (-1: this node is a root region)
ParentIndex int ParentIndex int
@ -370,8 +320,6 @@ type NodeInfo struct {
// Offset in hash chain // Offset in hash chain
NameOffset int NameOffset int
// summary
// (-1: node has no attributes) // (-1: node has no attributes)
FirstAttributeIndex int FirstAttributeIndex int
} }
@ -379,23 +327,16 @@ type NodeInfo struct {
// attribute extension in the LSF file // attribute extension in the LSF file
type AttributeEntry struct { type AttributeEntry struct {
Long bool Long bool
// summary
// (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain) // (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain)
NameHashTableIndex uint32 NameHashTableIndex uint32
// summary
// 26-bit MSB: Length of this attribute // 26-bit MSB: Length of this attribute
TypeAndLength uint32 TypeAndLength uint32
// summary
// Note: These indexes are assigned seemingly arbitrarily, and are not necessarily indices into the node list // Note: These indexes are assigned seemingly arbitrarily, and are not necessarily indices into the node list
NodeIndex int32 NodeIndex int32
// summary
// Note: These indexes are assigned seemingly arbitrarily, and are not necessarily indices into the node list // Note: These indexes are assigned seemingly arbitrarily, and are not necessarily indices into the node list
NextAttributeIndex int32 NextAttributeIndex int32
@ -417,8 +358,8 @@ func (ae *AttributeEntry) readShort(r io.ReadSeeker) error {
err error err error
n int n int
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "short attribute") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "short attribute")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex) err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex)
n = 4 n = 4
@ -454,8 +395,8 @@ func (ae *AttributeEntry) readLong(r io.ReadSeeker) error {
err error err error
n int n int
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "long attribute") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "long attribute")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex) err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex)
n = 4 n = 4
@ -503,8 +444,8 @@ func (ae AttributeEntry) NameOffset() int {
} }
// Type of this attribute (see NodeAttribute.DataType) // Type of this attribute (see NodeAttribute.DataType)
func (ae AttributeEntry) TypeID() DataType { func (ae AttributeEntry) TypeID() lsgo.DataType {
return DataType(ae.TypeAndLength & 0x3f) return lsgo.DataType(ae.TypeAndLength & 0x3f)
} }
// Length of this attribute // Length of this attribute
@ -522,14 +463,13 @@ type AttributeInfo struct {
NameOffset int NameOffset int
// Type of this attribute (see NodeAttribute.DataType) // Type of this attribute (see NodeAttribute.DataType)
TypeID DataType TypeID lsgo.DataType
// Length of this attribute // Length of this attribute
Length uint Length uint
// Absolute position of attribute data in the values section // Absolute position of attribute data in the values section
DataOffset uint DataOffset uint
// summary
// (-1: this is the last attribute) // (-1: this is the last attribute)
NextAttributeIndex int NextAttributeIndex int
@ -546,8 +486,8 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) {
pos int64 pos int64
n int n int
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "names") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "names")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &numHashEntries) err = binary.Read(r, binary.LittleEndian, &numHashEntries)
n = 4 n = 4
@ -559,15 +499,17 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) {
names = make([][]string, int(numHashEntries)) names = make([][]string, int(numHashEntries))
for i := range names { for i := range names {
var numStrings uint16 var numStrings uint16
err = binary.Read(r, binary.LittleEndian, &numStrings) err = binary.Read(r, binary.LittleEndian, &numStrings)
n = 4 n = 4
if err != nil {
return nil, err
}
l.Log("member", "numStrings", "read", n, "start position", pos, "value", numStrings) l.Log("member", "numStrings", "read", n, "start position", pos, "value", numStrings)
pos += int64(n) pos += int64(n)
var hash = make([]string, int(numStrings)) hash := make([]string, int(numStrings))
for x := range hash { for x := range hash {
var ( var (
nameLen uint16 nameLen uint16
@ -591,16 +533,13 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) {
pos += int64(n) pos += int64(n)
hash[x] = string(name) hash[x] = string(name)
} }
names[i] = hash names[i] = hash
} }
return names, nil return names, nil
} }
func readNodeInfo(r io.ReadSeeker, longNodes bool) ([]NodeInfo, error) { func readNodeInfo(r io.ReadSeeker, longNodes bool) ([]NodeInfo, error) {
// Console.WriteLine(" ----- DUMP OF NODE TABLE -----");
var ( var (
nodes []NodeInfo nodes []NodeInfo
err error err error
@ -609,7 +548,6 @@ func readNodeInfo(r io.ReadSeeker, longNodes bool) ([]NodeInfo, error) {
for err == nil { for err == nil {
var node NodeInfo var node NodeInfo
// var pos = lsfr.Position;
item := &NodeEntry{Long: longNodes} item := &NodeEntry{Long: longNodes}
err = item.Read(r) err = item.Read(r)
@ -618,11 +556,6 @@ func readNodeInfo(r io.ReadSeeker, longNodes bool) ([]NodeInfo, error) {
node.NameIndex = item.NameIndex() node.NameIndex = item.NameIndex()
node.NameOffset = item.NameOffset() node.NameOffset = item.NameOffset()
node.ParentIndex = int(item.ParentIndex) node.ParentIndex = int(item.ParentIndex)
// Console.WriteLine(String.Format(
// "{0}: {1} @ {2:X} (parent {3}, firstAttribute {4})",
// index, Names[node.NameIndex][node.NameOffset], pos, node.ParentIndex,
// node.FirstAttributeIndex
// ));
nodes = append(nodes, node) nodes = append(nodes, node)
index++ index++
@ -650,7 +583,6 @@ func readAttributeInfo(r io.ReadSeeker, long bool) []AttributeInfo {
break break
} }
// pretty.Log( attribute)
if long { if long {
dataOffset = uint(attribute.Offset) dataOffset = uint(attribute.Offset)
nextAttrIndex = int(attribute.NextAttributeIndex) nextAttrIndex = int(attribute.NextAttributeIndex)
@ -667,7 +599,6 @@ func readAttributeInfo(r io.ReadSeeker, long bool) []AttributeInfo {
if !long { if !long {
// get index of previous attribute for node // get index of previous attribute for node
if indexOfLastAttr, ok := prevAttributeRefs[int(attribute.NodeIndex)]; ok { // previous attribute exists for current node set the next attribute index for the previous node to this attribute if indexOfLastAttr, ok := prevAttributeRefs[int(attribute.NodeIndex)]; ok { // previous attribute exists for current node set the next attribute index for the previous node to this attribute
attributes[indexOfLastAttr].NextAttributeIndex = index attributes[indexOfLastAttr].NextAttributeIndex = index
} }
@ -702,19 +633,9 @@ func readAttributeInfo(r io.ReadSeeker, long bool) []AttributeInfo {
// ); // );
// Console.WriteLine(debug); // Console.WriteLine(debug);
// } // }
} }
type HeaderError struct { func Read(r io.ReadSeeker) (lsgo.Resource, error) {
Expected []byte
Got []byte
}
func (he HeaderError) Error() string {
return fmt.Sprintf("Invalid LSF signature; expected %v, got %v", he.Expected, he.Got)
}
func ReadLSF(r io.ReadSeeker) (Resource, error) {
var ( var (
err error err error
@ -728,38 +649,36 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
attributeInfo []AttributeInfo attributeInfo []AttributeInfo
// Node instances // Node instances
nodeInstances []*Node nodeInstances []*lsgo.Node
) )
var ( var (
l log.Logger l log.Logger
pos, npos int64 pos, npos int64
// n int // n int
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "file") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "file")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
l.Log("member", "header", "start position", pos) l.Log("member", "header", "start position", pos)
hdr := &LSFHeader{} hdr := &Header{}
err = hdr.Read(r) err = hdr.Read(r)
if err != nil || (hdr.Signature != LSFSignature) { if err != nil || (string(hdr.Signature[:]) != Signature) {
return Resource{}, HeaderError{LSFSignature[:], hdr.Signature[:]} return lsgo.Resource{}, lsgo.HeaderError{Expected: Signature, Got: hdr.Signature[:]}
} }
if hdr.Version < VerInitial || hdr.Version > MaxVersion { if hdr.Version < lsgo.VerInitial || hdr.Version > lsgo.MaxVersion {
return Resource{}, fmt.Errorf("LSF version %v is not supported", hdr.Version) return lsgo.Resource{}, fmt.Errorf("LSF version %v is not supported", hdr.Version)
} }
isCompressed := CompressionFlagsToMethod(hdr.CompressionFlags) != CMNone && CompressionFlagsToMethod(hdr.CompressionFlags) != CMInvalid isCompressed := lsgo.CompressionFlagsToMethod(hdr.CompressionFlags) != lsgo.CMNone && lsgo.CompressionFlagsToMethod(hdr.CompressionFlags) != lsgo.CMInvalid
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
l.Log("member", "LSF names", "start position", pos) l.Log("member", "LSF names", "start position", pos)
if hdr.StringsSizeOnDisk > 0 || hdr.StringsUncompressedSize > 0 { if hdr.StringsSizeOnDisk > 0 || hdr.StringsUncompressedSize > 0 {
var ( uncompressed := lsgo.LimitReadSeeker(r, int64(hdr.StringsSizeOnDisk))
uncompressed = LimitReadSeeker(r, int64(hdr.StringsSizeOnDisk))
)
if isCompressed { if isCompressed {
uncompressed = Decompress(uncompressed, int(hdr.StringsUncompressedSize), hdr.CompressionFlags, false) uncompressed = lsgo.Decompress(uncompressed, int(hdr.StringsUncompressedSize), hdr.CompressionFlags, false)
} }
// using (var nodesFile = new FileStream("names.bin", FileMode.Create, FileAccess.Write)) // using (var nodesFile = new FileStream("names.bin", FileMode.Create, FileAccess.Write))
@ -768,13 +687,12 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
// } // }
names, err = ReadNames(uncompressed) names, err = ReadNames(uncompressed)
// pretty.Log(len(names), names)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return Resource{}, err return lsgo.Resource{}, err
} }
} }
npos, err = r.Seek(0, io.SeekCurrent) npos, _ = r.Seek(0, io.SeekCurrent)
l.Log("member", "LSF nodes", "start position", npos) l.Log("member", "LSF nodes", "start position", npos)
if npos != pos+int64(hdr.StringsSizeOnDisk) { if npos != pos+int64(hdr.StringsSizeOnDisk) {
l.Log("member", "LSF nodes", "msg", "seeking to correct offset", "current", npos, "wanted", pos+int64(hdr.StringsSizeOnDisk)) l.Log("member", "LSF nodes", "msg", "seeking to correct offset", "current", npos, "wanted", pos+int64(hdr.StringsSizeOnDisk))
@ -783,11 +701,9 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
pos = npos pos = npos
} }
if hdr.NodesSizeOnDisk > 0 || hdr.NodesUncompressedSize > 0 { if hdr.NodesSizeOnDisk > 0 || hdr.NodesUncompressedSize > 0 {
var ( uncompressed := lsgo.LimitReadSeeker(r, int64(hdr.NodesSizeOnDisk))
uncompressed = LimitReadSeeker(r, int64(hdr.NodesSizeOnDisk))
)
if isCompressed { if isCompressed {
uncompressed = Decompress(uncompressed, int(hdr.NodesUncompressedSize), hdr.CompressionFlags, hdr.Version >= VerChunkedCompress) uncompressed = lsgo.Decompress(uncompressed, int(hdr.NodesUncompressedSize), hdr.CompressionFlags, hdr.Version >= lsgo.VerChunkedCompress)
} }
// using (var nodesFile = new FileStream("nodes.bin", FileMode.Create, FileAccess.Write)) // using (var nodesFile = new FileStream("nodes.bin", FileMode.Create, FileAccess.Write))
@ -795,16 +711,14 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
// nodesFile.Write(uncompressed, 0, uncompressed.Length); // nodesFile.Write(uncompressed, 0, uncompressed.Length);
// } // }
longNodes := hdr.Version >= VerExtendedNodes && hdr.Extended == 1 longNodes := hdr.Version >= lsgo.VerExtendedNodes && hdr.Extended == 1
nodeInfo, err = readNodeInfo(uncompressed, longNodes) nodeInfo, err = readNodeInfo(uncompressed, longNodes)
// pretty.Log(err, nodeInfo)
// logger.Printf("region 1 name: %v", names[nodeInfo[0].NameIndex])
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return Resource{}, err return lsgo.Resource{}, err
} }
} }
npos, err = r.Seek(0, io.SeekCurrent) npos, _ = r.Seek(0, io.SeekCurrent)
l.Log("member", "LSF attributes", "start position", npos) l.Log("member", "LSF attributes", "start position", npos)
if npos != pos+int64(hdr.NodesSizeOnDisk) { if npos != pos+int64(hdr.NodesSizeOnDisk) {
l.Log("msg", "seeking to correct offset", "current", npos, "wanted", pos+int64(hdr.NodesSizeOnDisk)) l.Log("msg", "seeking to correct offset", "current", npos, "wanted", pos+int64(hdr.NodesSizeOnDisk))
@ -813,11 +727,9 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
pos = npos pos = npos
} }
if hdr.AttributesSizeOnDisk > 0 || hdr.AttributesUncompressedSize > 0 { if hdr.AttributesSizeOnDisk > 0 || hdr.AttributesUncompressedSize > 0 {
var ( var uncompressed io.ReadSeeker = lsgo.LimitReadSeeker(r, int64(hdr.AttributesSizeOnDisk))
uncompressed io.ReadSeeker = LimitReadSeeker(r, int64(hdr.AttributesSizeOnDisk))
)
if isCompressed { if isCompressed {
uncompressed = Decompress(uncompressed, int(hdr.AttributesUncompressedSize), hdr.CompressionFlags, hdr.Version >= VerChunkedCompress) uncompressed = lsgo.Decompress(uncompressed, int(hdr.AttributesUncompressedSize), hdr.CompressionFlags, hdr.Version >= lsgo.VerChunkedCompress)
} }
// using (var attributesFile = new FileStream("attributes.bin", FileMode.Create, FileAccess.Write)) // using (var attributesFile = new FileStream("attributes.bin", FileMode.Create, FileAccess.Write))
@ -825,13 +737,11 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
// attributesFile.Write(uncompressed, 0, uncompressed.Length); // attributesFile.Write(uncompressed, 0, uncompressed.Length);
// } // }
longAttributes := hdr.Version >= VerExtendedNodes && hdr.Extended == 1 longAttributes := hdr.Version >= lsgo.VerExtendedNodes && hdr.Extended == 1
attributeInfo = readAttributeInfo(uncompressed, longAttributes) attributeInfo = readAttributeInfo(uncompressed, longAttributes)
// logger.Printf("attribute 1 name: %v", names[attributeInfo[0].NameIndex])
// pretty.Log(attributeInfo)
} }
npos, err = r.Seek(0, io.SeekCurrent) npos, _ = r.Seek(0, io.SeekCurrent)
l.Log("member", "LSF values", "start position", npos) l.Log("member", "LSF values", "start position", npos)
if npos != pos+int64(hdr.AttributesSizeOnDisk) { if npos != pos+int64(hdr.AttributesSizeOnDisk) {
l.Log("msg", "seeking to correct offset", "current", npos, "wanted", pos+int64(hdr.AttributesSizeOnDisk)) l.Log("msg", "seeking to correct offset", "current", npos, "wanted", pos+int64(hdr.AttributesSizeOnDisk))
@ -839,18 +749,16 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
} else { } else {
pos = npos pos = npos
} }
var ( var uncompressed io.ReadSeeker = lsgo.LimitReadSeeker(r, int64(hdr.ValuesSizeOnDisk))
uncompressed io.ReadSeeker = LimitReadSeeker(r, int64(hdr.ValuesSizeOnDisk))
)
if hdr.ValuesSizeOnDisk > 0 || hdr.ValuesUncompressedSize > 0 { if hdr.ValuesSizeOnDisk > 0 || hdr.ValuesUncompressedSize > 0 {
if isCompressed { if isCompressed {
uncompressed = Decompress(r, int(hdr.ValuesUncompressedSize), hdr.CompressionFlags, hdr.Version >= VerChunkedCompress) uncompressed = lsgo.Decompress(r, int(hdr.ValuesUncompressedSize), hdr.CompressionFlags, hdr.Version >= lsgo.VerChunkedCompress)
} }
} }
res := Resource{} res := lsgo.Resource{}
valueStart, _ = uncompressed.Seek(0, io.SeekCurrent) valueStart, _ := uncompressed.Seek(0, io.SeekCurrent)
nodeInstances, err = ReadRegions(uncompressed, names, nodeInfo, attributeInfo, hdr.Version, hdr.EngineVersion) nodeInstances, err = ReadRegions(uncompressed, valueStart, names, nodeInfo, attributeInfo, hdr.Version, hdr.EngineVersion)
if err != nil { if err != nil {
return res, err return res, err
} }
@ -865,20 +773,14 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
res.Metadata.Revision = (hdr.EngineVersion & 0xff0000) >> 16 res.Metadata.Revision = (hdr.EngineVersion & 0xff0000) >> 16
res.Metadata.Build = (hdr.EngineVersion & 0xffff) res.Metadata.Build = (hdr.EngineVersion & 0xffff)
// pretty.Log(res)
return res, nil return res, nil
} }
var valueStart int64 func ReadRegions(r io.ReadSeeker, valueStart int64, names [][]string, nodeInfo []NodeInfo, attributeInfo []AttributeInfo, version lsgo.FileVersion, engineVersion uint32) ([]*lsgo.Node, error) {
NodeInstances := make([]*lsgo.Node, 0, len(nodeInfo))
func ReadRegions(r io.ReadSeeker, names [][]string, nodeInfo []NodeInfo, attributeInfo []AttributeInfo, version FileVersion, engineVersion uint32) ([]*Node, error) {
NodeInstances := make([]*Node, 0, len(nodeInfo))
for _, nodeInfo := range nodeInfo { for _, nodeInfo := range nodeInfo {
if nodeInfo.ParentIndex == -1 { if nodeInfo.ParentIndex == -1 {
region, err := ReadNode(r, nodeInfo, names, attributeInfo, version, engineVersion) region, err := ReadNode(r, valueStart, nodeInfo, names, attributeInfo, version, engineVersion)
// pretty.Log(err, region)
region.RegionName = region.Name region.RegionName = region.Name
NodeInstances = append(NodeInstances, &region) NodeInstances = append(NodeInstances, &region)
@ -887,9 +789,7 @@ func ReadRegions(r io.ReadSeeker, names [][]string, nodeInfo []NodeInfo, attribu
return NodeInstances, err return NodeInstances, err
} }
} else { } else {
node, err := ReadNode(r, nodeInfo, names, attributeInfo, version, engineVersion) node, err := ReadNode(r, valueStart, nodeInfo, names, attributeInfo, version, engineVersion)
// pretty.Log(err, node)
node.Parent = NodeInstances[nodeInfo.ParentIndex] node.Parent = NodeInstances[nodeInfo.ParentIndex]
NodeInstances = append(NodeInstances, &node) NodeInstances = append(NodeInstances, &node)
@ -903,17 +803,17 @@ func ReadRegions(r io.ReadSeeker, names [][]string, nodeInfo []NodeInfo, attribu
return NodeInstances, nil return NodeInstances, nil
} }
func ReadNode(r io.ReadSeeker, ni NodeInfo, names [][]string, attributeInfo []AttributeInfo, version FileVersion, engineVersion uint32) (Node, error) { func ReadNode(r io.ReadSeeker, valueStart int64, ni NodeInfo, names [][]string, attributeInfo []AttributeInfo, version lsgo.FileVersion, engineVersion uint32) (lsgo.Node, error) {
var ( var (
node = Node{} node = lsgo.Node{}
index = ni.FirstAttributeIndex index = ni.FirstAttributeIndex
err error err error
l log.Logger l log.Logger
pos int64 pos int64
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "node") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "node")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
node.Name = names[ni.NameIndex][ni.NameOffset] node.Name = names[ni.NameIndex][ni.NameOffset]
@ -922,7 +822,7 @@ func ReadNode(r io.ReadSeeker, ni NodeInfo, names [][]string, attributeInfo []At
for index != -1 { for index != -1 {
var ( var (
attribute = attributeInfo[index] attribute = attributeInfo[index]
v NodeAttribute v lsgo.NodeAttribute
) )
if valueStart+int64(attribute.DataOffset) != pos { if valueStart+int64(attribute.DataOffset) != pos {
@ -937,18 +837,16 @@ func ReadNode(r io.ReadSeeker, ni NodeInfo, names [][]string, attributeInfo []At
return node, err return node, err
} }
index = attribute.NextAttributeIndex index = attribute.NextAttributeIndex
// Console.WriteLine(String.Format(" {0:X}: {1} ({2})", attribute.DataOffset, names[attribute.NameIndex][attribute.NameOffset], value));
} }
return node, nil return node, nil
} }
func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, version FileVersion, engineVersion uint32) (NodeAttribute, error) { func ReadLSFAttribute(r io.ReadSeeker, name string, dt lsgo.DataType, length uint, version lsgo.FileVersion, engineVersion uint32) (lsgo.NodeAttribute, error) {
// LSF and LSB serialize the buffer types differently, so specialized // LSF and LSB serialize the buffer types differently, so specialized
// code is added to the LSB and LSf serializers, and the common code is // code is added to the LSB and LSf serializers, and the common code is
// available in BinUtils.ReadAttribute() // available in BinUtils.ReadAttribute()
var ( var (
attr = NodeAttribute{ attr = lsgo.NodeAttribute{
Type: dt, Type: dt,
Name: name, Name: name,
} }
@ -957,13 +855,13 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
l log.Logger l log.Logger
pos int64 pos int64
) )
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "attribute") l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "attribute")
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
switch dt { switch dt {
case DTString, DTPath, DTFixedString, DTLSString, DTWString, DTLSWString: case lsgo.DTString, lsgo.DTPath, lsgo.DTFixedString, lsgo.DTLSString, lsgo.DTWString, lsgo.DTLSWString:
var v string var v string
v, err = ReadCString(r, int(length)) v, err = lsgo.ReadCString(r, int(length))
attr.Value = v attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value) l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
@ -971,9 +869,9 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
return attr, err return attr, err
case DTTranslatedString: case lsgo.DTTranslatedString:
var v TranslatedString var v lsgo.TranslatedString
v, err = ReadTranslatedString(r, version, engineVersion) v, err = lsgo.ReadTranslatedString(r, version, engineVersion)
attr.Value = v attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value) l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
@ -981,9 +879,9 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
return attr, err return attr, err
case DTTranslatedFSString: case lsgo.DTTranslatedFSString:
var v TranslatedFSString var v lsgo.TranslatedFSString
v, err = ReadTranslatedFSString(r, version) v, err = lsgo.ReadTranslatedFSString(r, version)
attr.Value = v attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value) l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
@ -991,7 +889,7 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
return attr, err return attr, err
case DTScratchBuffer: case lsgo.DTScratchBuffer:
v := make([]byte, length) v := make([]byte, length)
_, err = r.Read(v) _, err = r.Read(v)
@ -1003,147 +901,10 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
return attr, err return attr, err
default: default:
return ReadAttribute(r, name, dt, length, l) return lsgo.ReadAttribute(r, name, dt, length, l)
} }
} }
func ReadTranslatedString(r io.ReadSeeker, version FileVersion, engineVersion uint32) (TranslatedString, error) { func init() {
var ( lsgo.RegisterFormat("lsf", Signature, Read)
str TranslatedString
err error
)
if version >= VerBG3 || engineVersion == 0x4000001d {
// logger.Println("decoding bg3 data")
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 {
_, err = 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
}
// logger.Printf("handle %s; %v", str.Handle, err)
return str, nil
}
func ReadTranslatedFSString(r io.ReadSeeker, 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
} }

44
lsgo.go Normal file
View File

@ -0,0 +1,44 @@
package lsgo
import (
"strings"
"github.com/go-kit/kit/log"
)
var Logger log.Logger = log.NewNopLogger()
// NewFilter allows filtering of l
func NewFilter(f map[string][]string, l log.Logger) log.Logger {
return filter{
filter: f,
next: l,
}
}
type filter struct {
next log.Logger
filter map[string][]string
}
func (f filter) Log(keyvals ...interface{}) error {
allowed := true // allow everything
for i := 0; i < len(keyvals)-1; i += 2 {
if v, ok := keyvals[i].(string); ok { // key
if fil, ok := f.filter[v]; ok { // key has a filter
if v, ok = keyvals[i+1].(string); ok { // value is a string
allowed = false // this key has a filter deny everything except what the filter allows
for _, fi := range fil {
if strings.Contains(v, fi) {
allowed = true
}
}
}
}
}
}
if allowed {
return f.next.Log(keyvals...)
}
return nil
}

37
lsgo.sublime-project Normal file
View File

@ -0,0 +1,37 @@
{
"folders":
[
{
"path": "."
}
],
"settings":
{
"LSP":
{
"gopls":
{
"settings":
{
"gopls":
{
"usePlaceholders": true,
"buildFlags":
[
"-tags",
"noasm"
],
"directoryFilters":
[
"third_party"
],
"analyses": {
"shadow": true,
"unusedparams": true
}
}
}
}
}
}
}

View File

@ -6,7 +6,7 @@ import (
) )
type LSMetadata struct { type LSMetadata struct {
//public const uint CurrentMajorVersion = 33; // public const uint CurrentMajorVersion = 33;
Timestamp uint64 `xml:"-"` Timestamp uint64 `xml:"-"`
Major uint32 `xml:"major,attr"` Major uint32 `xml:"major,attr"`
@ -15,18 +15,12 @@ type LSMetadata struct {
Build uint32 `xml:"build,attr"` Build uint32 `xml:"build,attr"`
} }
type format struct {
name, magic string
read func(io.Reader) (Resource, error)
}
type Resource struct { type Resource struct {
Metadata LSMetadata `xml:"version"` Metadata LSMetadata `xml:"version"`
Regions []*Node `xml:"region"` Regions []*Node `xml:"region"`
} }
func (r *Resource) Read(io.Reader) { func (r *Resource) Read(io.Reader) {
} }
// public Resource() // public Resource()
@ -43,7 +37,7 @@ type Node struct {
RegionName string `xml:"-"` RegionName string `xml:"-"`
} }
func (n Node) MarshalXML(e *xml.Encoder, start xml.StartElement) error { func (n *Node) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
R := xml.Name{ R := xml.Name{
Local: "region", Local: "region",
} }