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)
Copyright (c) 2015 Norbyte
Copyright (c) 2020 lordwelch
Permission is hereby granted, free of charge, to any person obtaining a copy
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 {
case 0:
name.Local = "x"
// start.Name = "float1"
case 1:
name.Local = "y"
start.Name.Local = "float2"
@ -385,9 +384,7 @@ func (na *NodeAttribute) FromString(str string) error {
}
}
var (
err error
)
var err error
switch na.Type {
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 {
switch CompressionMethod(compressionFlags & 0x0f) {
case CMNone:
// logger.Println("No compression")
if v, ok := compressed.(io.ReadSeeker); ok {
return v
}
panic(errors.New("compressed must be an io.ReadSeeker if there is no compression"))
case CMZlib:
// logger.Println("zlib compression")
zr, _ := zlib.NewReader(compressed)
v, _ := ioutil.ReadAll(zr)
return bytes.NewReader(v)
case CMLZ4:
if chunked {
// logger.Println("lz4 stream compressed")
zr := lz4.NewReader(compressed)
p := make([]byte, uncompressedSize)
_, err := zr.Read(p)
@ -108,10 +105,7 @@ func Decompress(compressed io.Reader, uncompressedSize int, compressionFlags byt
}
return bytes.NewReader(p)
}
// logger.Println("lz4 block compressed")
// panic(errors.New("not implemented"))
src, _ := ioutil.ReadAll(compressed)
// logger.Println(len(src))
dst := make([]byte, uncompressedSize*2)
_, err := lz4.UncompressBlock(src, dst)
if err != nil {
@ -147,7 +141,7 @@ func ReadAttribute(r io.ReadSeeker, name string, DT DataType, length uint, l log
pos int64
n int
)
pos, err = r.Seek(0, io.SeekCurrent)
pos, _ = r.Seek(0, io.SeekCurrent)
switch DT {
case DTNone:
@ -406,3 +400,140 @@ func (l *LimitedReadSeeker) Seek(offset int64, whence int) (int64, error) {
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
import (
"bytes"
"encoding/xml"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"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/kr/pretty"
@ -35,7 +39,6 @@ func init() {
}
func main() {
for _, v := range flag.Args() {
fi, err := os.Stat(v)
if err != nil {
@ -43,7 +46,6 @@ func main() {
os.Exit(1)
}
switch {
case !fi.IsDir():
err = openLSF(v)
if err != nil && !errors.As(err, &lsgo.HeaderError{}) {
@ -75,6 +77,7 @@ func main() {
}
}
}
func openLSF(filename string) error {
var (
l *lsgo.Resource
@ -118,17 +121,65 @@ func openLSF(filename string) error {
func readLSF(filename string) (*lsgo.Resource, error) {
var (
l lsgo.Resource
f *os.File
err error
l lsgo.Resource
r io.ReadSeeker
file *os.File
fi os.FileInfo
err error
)
f, err = os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
switch filepath.Ext(filename) {
case ".lsf", ".lsb":
var b []byte
fi, err = os.Stat(filename)
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 {
return nil, err
}
@ -159,9 +210,7 @@ func marshalXML(l *lsgo.Resource) (string, error) {
}
func writeXML(f io.StringWriter, n string) error {
var (
err error
)
var err error
_, err = f.WriteString(strings.ToLower(xml.Header))
if err != nil {
return err

View File

@ -1,6 +1,9 @@
package lsgo
import "errors"
import (
"errors"
"fmt"
)
type FileVersion uint32
@ -43,3 +46,12 @@ var (
ErrInvalidNameKey = errors.New("invalid name key")
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
replace github.com/pierrec/lz4/v4 v4.1.1 => ./lz4
replace github.com/pierrec/lz4/v4 v4.1.3 => ./third_party/lz4
require (
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/pierrec/lz4/v4 v4.1.1
gonum.org/v1/gonum v0.8.1
github.com/pierrec/lz4/v4 v4.1.3
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/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.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0=
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/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
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-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.8.1 h1:wGtP3yGpc5mCLOLeTeBdjeui9oZSz5De0eOjMLC/QuQ=
gonum.org/v1/gonum v0.8.1/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
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/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
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 (
"encoding/binary"
@ -7,94 +7,101 @@ import (
"io"
"sort"
"git.narnian.us/lordwelch/lsgo"
"github.com/go-kit/kit/log"
)
type LSBHeader struct {
const (
Signature = "LSFM"
PreBG3Signature = "\x00\x00\x00\x40"
)
type Header struct {
Signature [4]byte
Size uint32
Endianness 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 (
l log.Logger
pos int64
n int
err error
)
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "header")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "header")
pos, _ = r.Seek(0, io.SeekCurrent)
n, err = r.Read(lsbh.Signature[:])
n, err = r.Read(h.Signature[:])
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsbh.Size)
err = binary.Read(r, binary.LittleEndian, &h.Size)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsbh.Endianness)
err = binary.Read(r, binary.LittleEndian, &h.Endianness)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsbh.Unknown)
err = binary.Read(r, binary.LittleEndian, &h.Unknown)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Timestamp)
err = binary.Read(r, binary.LittleEndian, &h.Version.Timestamp)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Major)
err = binary.Read(r, binary.LittleEndian, &h.Version.Major)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Minor)
err = binary.Read(r, binary.LittleEndian, &h.Version.Minor)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Revision)
err = binary.Read(r, binary.LittleEndian, &h.Version.Revision)
n = 4
if err != nil {
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
if err != nil {
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)
return nil
@ -102,43 +109,42 @@ func (lsbh *LSBHeader) Read(r io.ReadSeeker) error {
type IdentifierDictionary map[int]string
func ReadLSB(r io.ReadSeeker) (Resource, error) {
func Read(r io.ReadSeeker) (lsgo.Resource, error) {
var (
hdr = &LSBHeader{}
h = [4]byte{0x00, 0x00, 0x00, 0x40}
hdr = &Header{}
err error
d IdentifierDictionary
res Resource
res lsgo.Resource
l log.Logger
pos int64
)
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "file")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "file")
pos, _ = r.Seek(0, io.SeekCurrent)
l.Log("member", "header", "start position", pos)
err = hdr.Read(r)
if err != nil {
return Resource{}, err
return lsgo.Resource{}, err
}
if !(hdr.Signature == [4]byte{'L', 'S', 'F', 'M'} || hdr.Signature == h) {
return Resource{}, HeaderError{
Expected: []byte("LSFM"),
if !(string(hdr.Signature[:]) == Signature || string(hdr.Signature[:]) == PreBG3Signature) {
return lsgo.Resource{}, lsgo.HeaderError{
Expected: 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)
d, err = ReadLSBDictionary(r, binary.LittleEndian)
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)
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
return res, err
}
@ -153,8 +159,8 @@ func ReadLSBDictionary(r io.ReadSeeker, endianness binary.ByteOrder) (Identifier
pos int64
n int
)
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "dictionary")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "dictionary")
pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, endianness, &length)
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)
pos += int64(n)
str, err = ReadCString(r, int(stringLength))
str, err = lsgo.ReadCString(r, int(stringLength))
n += int(stringLength)
if err != nil {
return dict, err
@ -199,7 +205,7 @@ func ReadLSBDictionary(r io.ReadSeeker, endianness binary.ByteOrder) (Identifier
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 (
regions []struct {
name string
@ -212,13 +218,13 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
pos int64
n int
)
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "region")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "region")
pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, endianness, &regionCount)
n = 4
if err != nil {
return Resource{}, err
return lsgo.Resource{}, err
}
l.Log("member", "regionCount", "read", n, "start position", pos, "value", regionCount)
pos += int64(n)
@ -235,17 +241,17 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
err = binary.Read(r, endianness, &key)
n = 4
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)
pos += int64(n)
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)
n = 4
if err != nil {
return Resource{}, err
return lsgo.Resource{}, err
}
l.Log("member", "offset", "read", n, "start position", pos, "value", regions[i].offset)
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 {
return regions[i].offset < regions[j].offset
})
res := Resource{
Regions: make([]*Node, 0, regionCount),
res := lsgo.Resource{
Regions: make([]*lsgo.Node, 0, regionCount),
}
for _, re := range regions {
var node *Node
var node *lsgo.Node
node, err = readLSBNode(r, d, endianness, version, re.offset)
if err != nil {
return res, err
@ -268,12 +274,12 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
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 (
key uint32
attrCount uint32
childCount uint32
node = new(Node)
node = new(lsgo.Node)
err error
ok bool
@ -281,8 +287,8 @@ func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.Byte
pos int64
n int
)
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "node")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "node")
pos, _ = r.Seek(0, io.SeekCurrent)
if pos != int64(offset) && offset != 0 {
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)
node.Attributes = make([]NodeAttribute, int(attrCount))
node.Attributes = make([]lsgo.NodeAttribute, int(attrCount))
for i := range node.Attributes {
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 {
node.Children[i], err = readLSBNode(r, d, endianness, version, 0)
if err != nil {
@ -334,12 +340,12 @@ func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.Byte
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 (
key uint32
name string
attrType uint32
attr NodeAttribute
attr lsgo.NodeAttribute
err error
ok bool
)
@ -348,21 +354,21 @@ func readLSBAttribute(r io.ReadSeeker, d IdentifierDictionary, endianness binary
return attr, err
}
if name, ok = d[int(key)]; !ok {
return attr, ErrInvalidNameKey
return attr, lsgo.ErrInvalidNameKey
}
err = binary.Read(r, endianness, &attrType)
if err != nil {
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
// code is added to the LSB and LSf serializers, and the common code is
// available in BinUtils.ReadAttribute()
var (
attr = NodeAttribute{
attr = lsgo.NodeAttribute{
Type: dt,
Name: name,
}
@ -372,38 +378,38 @@ func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.By
l log.Logger
pos int64
)
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "attribute")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "attribute")
pos, _ = r.Seek(0, io.SeekCurrent)
switch dt {
case DTString, DTPath, DTFixedString, DTLSString: //, DTLSWString:
case lsgo.DTString, lsgo.DTPath, lsgo.DTFixedString, lsgo.DTLSString: // DTLSWString:
var v string
err = binary.Read(r, endianness, &length)
if err != nil {
return attr, err
}
v, err = ReadCString(r, int(length))
v, err = lsgo.ReadCString(r, int(length))
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTWString:
case lsgo.DTWString:
panic("Not implemented")
case DTTranslatedString:
var v TranslatedString
v, err = ReadTranslatedString(r, version, 0)
case lsgo.DTTranslatedString:
var v lsgo.TranslatedString
v, err = lsgo.ReadTranslatedString(r, version, 0)
attr.Value = v
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
return attr, err
case DTTranslatedFSString:
case lsgo.DTTranslatedFSString:
panic("Not implemented")
var v TranslatedFSString
var v lsgo.TranslatedFSString
// v, err = ReadTranslatedFSString(r, Version)
attr.Value = v
@ -411,7 +417,7 @@ func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.By
return attr, err
case DTScratchBuffer:
case lsgo.DTScratchBuffer:
panic("Not implemented")
v := make([]byte, length)
@ -423,6 +429,11 @@ func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.By
return attr, err
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 (
"encoding/binary"
"fmt"
"io"
"strconv"
"strings"
"git.narnian.us/lordwelch/lsgo"
"github.com/go-kit/kit/log"
)
var (
LSFSignature = [4]byte{0x4C, 0x53, 0x4F, 0x46}
Logger log.Logger = log.NewNopLogger()
)
const Signature = "LSOF"
// 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 Header struct {
// LSOF file signature
Signature [4]byte
// 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)
EngineVersion uint32
@ -83,7 +46,6 @@ type LSFHeader struct {
// Compressed size of the raw value buffer
ValuesSizeOnDisk uint32
// summary
// Uses the same format as packages (see BinUtils.MakeCompressionFlags)
CompressionFlags byte
@ -96,166 +58,158 @@ type LSFHeader struct {
Extended uint32
}
func (lsfh *LSFHeader) Read(r io.ReadSeeker) error {
func (h *Header) Read(r io.ReadSeeker) error {
var (
l log.Logger
pos int64
n int
err error
)
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "header")
pos, err = r.Seek(0, io.SeekCurrent)
n, err = r.Read(lsfh.Signature[:])
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "header")
pos, _ = r.Seek(0, io.SeekCurrent)
n, err = r.Read(h.Signature[:])
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.Version)
err = binary.Read(r, binary.LittleEndian, &h.Version)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.EngineVersion)
err = binary.Read(r, binary.LittleEndian, &h.EngineVersion)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.StringsUncompressedSize)
err = binary.Read(r, binary.LittleEndian, &h.StringsUncompressedSize)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.StringsSizeOnDisk)
err = binary.Read(r, binary.LittleEndian, &h.StringsSizeOnDisk)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.NodesUncompressedSize)
err = binary.Read(r, binary.LittleEndian, &h.NodesUncompressedSize)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.NodesSizeOnDisk)
err = binary.Read(r, binary.LittleEndian, &h.NodesSizeOnDisk)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.AttributesUncompressedSize)
err = binary.Read(r, binary.LittleEndian, &h.AttributesUncompressedSize)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.AttributesSizeOnDisk)
err = binary.Read(r, binary.LittleEndian, &h.AttributesSizeOnDisk)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.ValuesUncompressedSize)
err = binary.Read(r, binary.LittleEndian, &h.ValuesUncompressedSize)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.ValuesSizeOnDisk)
err = binary.Read(r, binary.LittleEndian, &h.ValuesSizeOnDisk)
n = 4
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.CompressionFlags)
err = binary.Read(r, binary.LittleEndian, &h.CompressionFlags)
n = 1
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.Unknown2)
err = binary.Read(r, binary.LittleEndian, &h.Unknown2)
n = 1
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.Unknown3)
err = binary.Read(r, binary.LittleEndian, &h.Unknown3)
n = 2
if err != nil {
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)
err = binary.Read(r, binary.LittleEndian, &lsfh.Extended)
err = binary.Read(r, binary.LittleEndian, &h.Extended)
n = 4
if err != nil {
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)
if !lsfh.IsCompressed() {
lsfh.NodesSizeOnDisk = lsfh.NodesUncompressedSize
lsfh.AttributesSizeOnDisk = lsfh.AttributesUncompressedSize
lsfh.StringsSizeOnDisk = lsfh.StringsUncompressedSize
lsfh.ValuesSizeOnDisk = lsfh.ValuesUncompressedSize
if !h.IsCompressed() {
h.NodesSizeOnDisk = h.NodesUncompressedSize
h.AttributesSizeOnDisk = h.AttributesUncompressedSize
h.StringsSizeOnDisk = h.StringsUncompressedSize
h.ValuesSizeOnDisk = h.ValuesUncompressedSize
}
return nil
}
func (lsfh LSFHeader) IsCompressed() bool {
return CompressionFlagsToMethod(lsfh.CompressionFlags) != CMNone && CompressionFlagsToMethod(lsfh.CompressionFlags) != CMInvalid
func (h Header) IsCompressed() bool {
return lsgo.CompressionFlagsToMethod(h.CompressionFlags) != lsgo.CMNone && lsgo.CompressionFlagsToMethod(h.CompressionFlags) != lsgo.CMInvalid
}
type NodeEntry struct {
Long bool
// summary
// (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain)
NameHashTableIndex uint32
// summary
// (-1: node has no attributes)
FirstAttributeIndex int32
// summary
// (-1: this node is a root region)
ParentIndex int32
// summary
// (-1: this is the last node)
NextSiblingIndex int32
}
@ -274,12 +228,11 @@ func (ne *NodeEntry) readShort(r io.ReadSeeker) error {
err error
n int
)
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "short node")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "short node")
pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &ne.NameHashTableIndex)
n = 4
if err != nil {
// logger.Println(err, "ne.NameHashTableIndex", ne.NameHashTableIndex)
return err
}
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)
n = 4
if err != nil {
// logger.Println(err, "ne.FirstAttributeIndex", ne.FirstAttributeIndex)
return err
}
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)
n = 4
if err != nil {
// logger.Println(err, "ne.ParentIndex", ne.ParentIndex)
return err
}
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
n int
)
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "long node")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "long node")
pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &ne.NameHashTableIndex)
n = 4
if err != nil {
@ -359,7 +310,6 @@ func (ne NodeEntry) NameOffset() int {
// Processed node information for a node in the LSF file
type NodeInfo struct {
// summary
// (-1: this node is a root region)
ParentIndex int
@ -370,8 +320,6 @@ type NodeInfo struct {
// Offset in hash chain
NameOffset int
// summary
// (-1: node has no attributes)
FirstAttributeIndex int
}
@ -379,23 +327,16 @@ type NodeInfo struct {
// attribute extension in the LSF file
type AttributeEntry struct {
Long bool
// summary
// (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain)
NameHashTableIndex uint32
// summary
// 26-bit MSB: Length of this attribute
TypeAndLength uint32
// summary
// Note: These indexes are assigned seemingly arbitrarily, and are not necessarily indices into the node list
NodeIndex int32
// summary
// Note: These indexes are assigned seemingly arbitrarily, and are not necessarily indices into the node list
NextAttributeIndex int32
@ -417,8 +358,8 @@ func (ae *AttributeEntry) readShort(r io.ReadSeeker) error {
err error
n int
)
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "short attribute")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "short attribute")
pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex)
n = 4
@ -454,8 +395,8 @@ func (ae *AttributeEntry) readLong(r io.ReadSeeker) error {
err error
n int
)
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "long attribute")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "long attribute")
pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex)
n = 4
@ -503,8 +444,8 @@ func (ae AttributeEntry) NameOffset() int {
}
// Type of this attribute (see NodeAttribute.DataType)
func (ae AttributeEntry) TypeID() DataType {
return DataType(ae.TypeAndLength & 0x3f)
func (ae AttributeEntry) TypeID() lsgo.DataType {
return lsgo.DataType(ae.TypeAndLength & 0x3f)
}
// Length of this attribute
@ -522,14 +463,13 @@ type AttributeInfo struct {
NameOffset int
// Type of this attribute (see NodeAttribute.DataType)
TypeID DataType
TypeID lsgo.DataType
// Length of this attribute
Length uint
// Absolute position of attribute data in the values section
DataOffset uint
// summary
// (-1: this is the last attribute)
NextAttributeIndex int
@ -546,8 +486,8 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) {
pos int64
n int
)
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "names")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "names")
pos, _ = r.Seek(0, io.SeekCurrent)
err = binary.Read(r, binary.LittleEndian, &numHashEntries)
n = 4
@ -559,15 +499,17 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) {
names = make([][]string, int(numHashEntries))
for i := range names {
var numStrings uint16
err = binary.Read(r, binary.LittleEndian, &numStrings)
n = 4
if err != nil {
return nil, err
}
l.Log("member", "numStrings", "read", n, "start position", pos, "value", numStrings)
pos += int64(n)
var hash = make([]string, int(numStrings))
hash := make([]string, int(numStrings))
for x := range hash {
var (
nameLen uint16
@ -591,16 +533,13 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) {
pos += int64(n)
hash[x] = string(name)
}
names[i] = hash
}
return names, nil
}
func readNodeInfo(r io.ReadSeeker, longNodes bool) ([]NodeInfo, error) {
// Console.WriteLine(" ----- DUMP OF NODE TABLE -----");
var (
nodes []NodeInfo
err error
@ -609,7 +548,6 @@ func readNodeInfo(r io.ReadSeeker, longNodes bool) ([]NodeInfo, error) {
for err == nil {
var node NodeInfo
// var pos = lsfr.Position;
item := &NodeEntry{Long: longNodes}
err = item.Read(r)
@ -618,11 +556,6 @@ func readNodeInfo(r io.ReadSeeker, longNodes bool) ([]NodeInfo, error) {
node.NameIndex = item.NameIndex()
node.NameOffset = item.NameOffset()
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)
index++
@ -650,7 +583,6 @@ func readAttributeInfo(r io.ReadSeeker, long bool) []AttributeInfo {
break
}
// pretty.Log( attribute)
if long {
dataOffset = uint(attribute.Offset)
nextAttrIndex = int(attribute.NextAttributeIndex)
@ -667,7 +599,6 @@ func readAttributeInfo(r io.ReadSeeker, long bool) []AttributeInfo {
if !long {
// 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
attributes[indexOfLastAttr].NextAttributeIndex = index
}
@ -702,19 +633,9 @@ func readAttributeInfo(r io.ReadSeeker, long bool) []AttributeInfo {
// );
// Console.WriteLine(debug);
// }
}
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)
}
func ReadLSF(r io.ReadSeeker) (Resource, error) {
func Read(r io.ReadSeeker) (lsgo.Resource, error) {
var (
err error
@ -728,38 +649,36 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
attributeInfo []AttributeInfo
// Node instances
nodeInstances []*Node
nodeInstances []*lsgo.Node
)
var (
l log.Logger
pos, npos int64
// n int
)
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "file")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "file")
pos, _ = r.Seek(0, io.SeekCurrent)
l.Log("member", "header", "start position", pos)
hdr := &LSFHeader{}
hdr := &Header{}
err = hdr.Read(r)
if err != nil || (hdr.Signature != LSFSignature) {
return Resource{}, HeaderError{LSFSignature[:], hdr.Signature[:]}
if err != nil || (string(hdr.Signature[:]) != Signature) {
return lsgo.Resource{}, lsgo.HeaderError{Expected: Signature, Got: hdr.Signature[:]}
}
if hdr.Version < VerInitial || hdr.Version > MaxVersion {
return Resource{}, fmt.Errorf("LSF version %v is not supported", hdr.Version)
if hdr.Version < lsgo.VerInitial || hdr.Version > lsgo.MaxVersion {
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)
if hdr.StringsSizeOnDisk > 0 || hdr.StringsUncompressedSize > 0 {
var (
uncompressed = LimitReadSeeker(r, int64(hdr.StringsSizeOnDisk))
)
uncompressed := lsgo.LimitReadSeeker(r, int64(hdr.StringsSizeOnDisk))
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))
@ -768,13 +687,12 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
// }
names, err = ReadNames(uncompressed)
// pretty.Log(len(names), names)
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)
if npos != 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
}
if hdr.NodesSizeOnDisk > 0 || hdr.NodesUncompressedSize > 0 {
var (
uncompressed = LimitReadSeeker(r, int64(hdr.NodesSizeOnDisk))
)
uncompressed := lsgo.LimitReadSeeker(r, int64(hdr.NodesSizeOnDisk))
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))
@ -795,16 +711,14 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
// 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)
// pretty.Log(err, nodeInfo)
// logger.Printf("region 1 name: %v", names[nodeInfo[0].NameIndex])
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)
if npos != 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
}
if hdr.AttributesSizeOnDisk > 0 || hdr.AttributesUncompressedSize > 0 {
var (
uncompressed io.ReadSeeker = LimitReadSeeker(r, int64(hdr.AttributesSizeOnDisk))
)
var uncompressed io.ReadSeeker = lsgo.LimitReadSeeker(r, int64(hdr.AttributesSizeOnDisk))
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))
@ -825,13 +737,11 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
// 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)
// 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)
if npos != 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 {
pos = npos
}
var (
uncompressed io.ReadSeeker = LimitReadSeeker(r, int64(hdr.ValuesSizeOnDisk))
)
var uncompressed io.ReadSeeker = lsgo.LimitReadSeeker(r, int64(hdr.ValuesSizeOnDisk))
if hdr.ValuesSizeOnDisk > 0 || hdr.ValuesUncompressedSize > 0 {
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{}
valueStart, _ = uncompressed.Seek(0, io.SeekCurrent)
nodeInstances, err = ReadRegions(uncompressed, names, nodeInfo, attributeInfo, hdr.Version, hdr.EngineVersion)
res := lsgo.Resource{}
valueStart, _ := uncompressed.Seek(0, io.SeekCurrent)
nodeInstances, err = ReadRegions(uncompressed, valueStart, names, nodeInfo, attributeInfo, hdr.Version, hdr.EngineVersion)
if err != nil {
return res, err
}
@ -865,20 +773,14 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
res.Metadata.Revision = (hdr.EngineVersion & 0xff0000) >> 16
res.Metadata.Build = (hdr.EngineVersion & 0xffff)
// pretty.Log(res)
return res, nil
}
var valueStart int64
func ReadRegions(r io.ReadSeeker, names [][]string, nodeInfo []NodeInfo, attributeInfo []AttributeInfo, version FileVersion, engineVersion uint32) ([]*Node, error) {
NodeInstances := make([]*Node, 0, len(nodeInfo))
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))
for _, nodeInfo := range nodeInfo {
if nodeInfo.ParentIndex == -1 {
region, err := ReadNode(r, nodeInfo, names, attributeInfo, version, engineVersion)
// pretty.Log(err, region)
region, err := ReadNode(r, valueStart, nodeInfo, names, attributeInfo, version, engineVersion)
region.RegionName = region.Name
NodeInstances = append(NodeInstances, &region)
@ -887,9 +789,7 @@ func ReadRegions(r io.ReadSeeker, names [][]string, nodeInfo []NodeInfo, attribu
return NodeInstances, err
}
} else {
node, err := ReadNode(r, nodeInfo, names, attributeInfo, version, engineVersion)
// pretty.Log(err, node)
node, err := ReadNode(r, valueStart, nodeInfo, names, attributeInfo, version, engineVersion)
node.Parent = NodeInstances[nodeInfo.ParentIndex]
NodeInstances = append(NodeInstances, &node)
@ -903,17 +803,17 @@ func ReadRegions(r io.ReadSeeker, names [][]string, nodeInfo []NodeInfo, attribu
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 (
node = Node{}
node = lsgo.Node{}
index = ni.FirstAttributeIndex
err error
l log.Logger
pos int64
)
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "node")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "node")
pos, _ = r.Seek(0, io.SeekCurrent)
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 {
var (
attribute = attributeInfo[index]
v NodeAttribute
v lsgo.NodeAttribute
)
if valueStart+int64(attribute.DataOffset) != pos {
@ -937,18 +837,16 @@ func ReadNode(r io.ReadSeeker, ni NodeInfo, names [][]string, attributeInfo []At
return node, err
}
index = attribute.NextAttributeIndex
// Console.WriteLine(String.Format(" {0:X}: {1} ({2})", attribute.DataOffset, names[attribute.NameIndex][attribute.NameOffset], value));
}
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
// code is added to the LSB and LSf serializers, and the common code is
// available in BinUtils.ReadAttribute()
var (
attr = NodeAttribute{
attr = lsgo.NodeAttribute{
Type: dt,
Name: name,
}
@ -957,13 +855,13 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
l log.Logger
pos int64
)
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "attribute")
pos, err = r.Seek(0, io.SeekCurrent)
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "attribute")
pos, _ = r.Seek(0, io.SeekCurrent)
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
v, err = ReadCString(r, int(length))
v, err = lsgo.ReadCString(r, int(length))
attr.Value = v
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
case DTTranslatedString:
var v TranslatedString
v, err = ReadTranslatedString(r, version, engineVersion)
case lsgo.DTTranslatedString:
var v lsgo.TranslatedString
v, err = lsgo.ReadTranslatedString(r, version, engineVersion)
attr.Value = v
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
case DTTranslatedFSString:
var v TranslatedFSString
v, err = ReadTranslatedFSString(r, version)
case lsgo.DTTranslatedFSString:
var v lsgo.TranslatedFSString
v, err = lsgo.ReadTranslatedFSString(r, version)
attr.Value = v
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
case DTScratchBuffer:
case lsgo.DTScratchBuffer:
v := make([]byte, length)
_, err = r.Read(v)
@ -1003,147 +901,10 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
return attr, err
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) {
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
func init() {
lsgo.RegisterFormat("lsf", Signature, Read)
}

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 {
//public const uint CurrentMajorVersion = 33;
// public const uint CurrentMajorVersion = 33;
Timestamp uint64 `xml:"-"`
Major uint32 `xml:"major,attr"`
@ -15,18 +15,12 @@ type LSMetadata struct {
Build uint32 `xml:"build,attr"`
}
type format struct {
name, magic string
read func(io.Reader) (Resource, error)
}
type Resource struct {
Metadata LSMetadata `xml:"version"`
Regions []*Node `xml:"region"`
}
func (r *Resource) Read(io.Reader) {
}
// public Resource()
@ -43,7 +37,7 @@ type Node struct {
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{
Local: "region",
}