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
This commit is contained in:
lordwelch 2021-01-06 01:08:20 -08:00
parent e8c7e22293
commit 2f87df40d6
12 changed files with 474 additions and 331 deletions

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

@ -385,9 +385,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

@ -406,3 +406,142 @@ 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 {
// 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
}

View File

@ -1,16 +1,21 @@
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"
"sync"
"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"
@ -75,6 +80,7 @@ func main() {
} }
} }
} }
func openLSF(filename string) error { func openLSF(filename string) error {
var ( var (
l *lsgo.Resource l *lsgo.Resource
@ -119,16 +125,64 @@ 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
file *os.File
fi os.FileInfo
err error err error
) )
f, err = os.Open(filename) switch filepath.Ext(filename) {
case ".lsf", ".lsb":
var b []byte
fi, err = os.Stat(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer f.Close() // 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 +213,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 []byte
Got []byte
}
func (he HeaderError) Error() string {
return fmt.Sprintf("Invalid LSF signature; expected %v, got %v", 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 => ./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,6 +7,8 @@ import (
"io" "io"
"sort" "sort"
"git.narnian.us/lordwelch/lsgo"
"github.com/go-kit/kit/log" "github.com/go-kit/kit/log"
) )
@ -15,7 +17,7 @@ type LSBHeader struct {
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 (lsbh *LSBHeader) Read(r io.ReadSeeker) error {
@ -25,7 +27,7 @@ func (lsbh *LSBHeader) Read(r io.ReadSeeker) error {
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, err = r.Seek(0, io.SeekCurrent)
n, err = r.Read(lsbh.Signature[:]) n, err = r.Read(lsbh.Signature[:])
@ -102,27 +104,27 @@ 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 ReadLSB(r io.ReadSeeker) (lsgo.Resource, error) {
var ( var (
hdr = &LSBHeader{} hdr = &LSBHeader{}
h = [4]byte{0x00, 0x00, 0x00, 0x40} 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, err = 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 !(hdr.Signature == [4]byte{'L', 'S', 'F', 'M'} || hdr.Signature == h) {
return Resource{}, HeaderError{ return lsgo.Resource{}, lsgo.HeaderError{
Expected: []byte("LSFM"), Expected: []byte("LSFM"),
Got: hdr.Signature[:], Got: hdr.Signature[:],
} }
@ -132,13 +134,13 @@ func ReadLSB(r io.ReadSeeker) (Resource, error) {
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, err = 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,7 +155,7 @@ 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, err = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, endianness, &length) err = binary.Read(r, endianness, &length)
@ -179,7 +181,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 +201,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 +214,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, err = 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 +237,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 +255,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 +270,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,7 +283,7 @@ 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, err = r.Seek(0, io.SeekCurrent)
if pos != int64(offset) && offset != 0 { if pos != int64(offset) && offset != 0 {
@ -315,7 +317,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 +326,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 +336,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 +350,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 +374,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, err = 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 +413,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 +425,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", "LSFM", ReadLSB)
lsgo.RegisterFormat("lsb", "\x00\x00\x00\x40", ReadLSB)
}

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 ( var LSFSignature = [4]byte{0x4C, 0x53, 0x4F, 0x46}
LSFSignature = [4]byte{0x4C, 0x53, 0x4F, 0x46}
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 {
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 { 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
@ -103,7 +66,7 @@ func (lsfh *LSFHeader) Read(r io.ReadSeeker) error {
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, err = r.Seek(0, io.SeekCurrent)
n, err = r.Read(lsfh.Signature[:]) n, err = r.Read(lsfh.Signature[:])
if err != nil { if err != nil {
@ -233,7 +196,7 @@ func (lsfh *LSFHeader) Read(r io.ReadSeeker) error {
} }
func (lsfh LSFHeader) IsCompressed() bool { func (lsfh LSFHeader) IsCompressed() bool {
return CompressionFlagsToMethod(lsfh.CompressionFlags) != CMNone && CompressionFlagsToMethod(lsfh.CompressionFlags) != CMInvalid return lsgo.CompressionFlagsToMethod(lsfh.CompressionFlags) != lsgo.CMNone && lsgo.CompressionFlagsToMethod(lsfh.CompressionFlags) != lsgo.CMInvalid
} }
type NodeEntry struct { type NodeEntry struct {
@ -274,7 +237,7 @@ 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, err = 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
@ -312,7 +275,7 @@ 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, err = 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
@ -417,7 +380,7 @@ 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, err = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex) err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex)
@ -454,7 +417,7 @@ 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, err = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex) err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex)
@ -503,8 +466,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,7 +485,7 @@ 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
@ -546,7 +509,7 @@ 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, err = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &numHashEntries) err = binary.Read(r, binary.LittleEndian, &numHashEntries)
@ -567,7 +530,7 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) {
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
@ -702,19 +665,9 @@ func readAttributeInfo(r io.ReadSeeker, long bool) []AttributeInfo {
// ); // );
// Console.WriteLine(debug); // Console.WriteLine(debug);
// } // }
} }
type HeaderError struct { func ReadLSF(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 +681,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, err = r.Seek(0, io.SeekCurrent)
l.Log("member", "header", "start position", pos) l.Log("member", "header", "start position", pos)
hdr := &LSFHeader{} hdr := &LSFHeader{}
err = hdr.Read(r) err = hdr.Read(r)
if err != nil || (hdr.Signature != LSFSignature) { if err != nil || (hdr.Signature != LSFSignature) {
return Resource{}, HeaderError{LSFSignature[:], hdr.Signature[:]} return lsgo.Resource{}, lsgo.HeaderError{Expected: LSFSignature[:], 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, err = 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))
@ -770,7 +721,7 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
names, err = ReadNames(uncompressed) names, err = ReadNames(uncompressed)
// pretty.Log(len(names), names) // pretty.Log(len(names), names)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return Resource{}, err return lsgo.Resource{}, err
} }
} }
@ -783,11 +734,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,12 +744,12 @@ 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) // pretty.Log(err, nodeInfo)
// logger.Printf("region 1 name: %v", names[nodeInfo[0].NameIndex]) // 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
} }
} }
@ -813,11 +762,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,7 +772,7 @@ 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]) // logger.Printf("attribute 1 name: %v", names[attributeInfo[0].NameIndex])
// pretty.Log(attributeInfo) // pretty.Log(attributeInfo)
@ -839,18 +786,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
} }
@ -867,16 +812,13 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
// pretty.Log(res) // 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) // pretty.Log(err, region)
@ -887,7 +829,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) // pretty.Log(err, node)
@ -903,16 +845,16 @@ 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, err = r.Seek(0, io.SeekCurrent)
node.Name = names[ni.NameIndex][ni.NameOffset] node.Name = names[ni.NameIndex][ni.NameOffset]
@ -922,7 +864,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 {
@ -943,12 +885,12 @@ func ReadNode(r io.ReadSeeker, ni NodeInfo, names [][]string, attributeInfo []At
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 +899,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, err = 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 +913,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 +923,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 +933,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 +945,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", "LSOF", ReadLSF)
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
}

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()