From 2f87df40d627b15d16c0cff94452bb6bf0e986f8 Mon Sep 17 00:00:00 2001 From: lordwelch <timmy@narnian.us> Date: Wed, 6 Jan 2021 01:08:20 -0800 Subject: [PATCH] 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 --- LICENSE | 2 +- NodeAttribute.go | 4 +- binutils.go | 139 ++++++++++++++++++ cmd/lsconvert/main.go | 76 ++++++++-- const.go | 14 +- format.go | 92 ++++++++++++ go.mod | 8 +- go.sum | 8 +- lsb.go => lsb/lsb.go | 93 +++++++------ lsf.go => lsf/lsf.go | 317 ++++++++---------------------------------- lsgo.go | 44 ++++++ resource.go | 8 +- 12 files changed, 474 insertions(+), 331 deletions(-) create mode 100644 format.go rename lsb.go => lsb/lsb.go (77%) rename lsf.go => lsf/lsf.go (73%) create mode 100644 lsgo.go diff --git a/LICENSE b/LICENSE index cf2e488..6a832db 100644 --- a/LICENSE +++ b/LICENSE @@ -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 diff --git a/NodeAttribute.go b/NodeAttribute.go index 831a045..f982eca 100644 --- a/NodeAttribute.go +++ b/NodeAttribute.go @@ -385,9 +385,7 @@ func (na *NodeAttribute) FromString(str string) error { } } - var ( - err error - ) + var err error switch na.Type { case DTNone: diff --git a/binutils.go b/binutils.go index aa89b22..161a4e3 100644 --- a/binutils.go +++ b/binutils.go @@ -406,3 +406,142 @@ 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 { + // 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 +} diff --git a/cmd/lsconvert/main.go b/cmd/lsconvert/main.go index ae411bb..72d9f84 100644 --- a/cmd/lsconvert/main.go +++ b/cmd/lsconvert/main.go @@ -1,16 +1,21 @@ package main import ( + "bytes" "encoding/xml" "errors" "flag" "fmt" "io" + "io/ioutil" "os" "path/filepath" "strings" + "sync" "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" @@ -75,6 +80,7 @@ func main() { } } } + func openLSF(filename string) error { var ( l *lsgo.Resource @@ -118,17 +124,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) { - l, err = lsgo.ReadLSF(f) + 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() + + _, 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 +213,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 diff --git a/const.go b/const.go index 4ae7d09..6f9eb95 100644 --- a/const.go +++ b/const.go @@ -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 []byte + Got []byte +} + +func (he HeaderError) Error() string { + return fmt.Sprintf("Invalid LSF signature; expected %v, got %v", he.Expected, he.Got) +} diff --git a/format.go b/format.go new file mode 100644 index 0000000..43bbe71 --- /dev/null +++ b/format.go @@ -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 +} diff --git a/go.mod b/go.mod index 22034e2..eb890c7 100644 --- a/go.mod +++ b/go.mod @@ -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 => ./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 ) diff --git a/go.sum b/go.sum index 0fb9d34..041d570 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/lsb.go b/lsb/lsb.go similarity index 77% rename from lsb.go rename to lsb/lsb.go index 5629831..a018cad 100644 --- a/lsb.go +++ b/lsb/lsb.go @@ -1,4 +1,4 @@ -package lsgo +package lsb import ( "encoding/binary" @@ -7,6 +7,8 @@ import ( "io" "sort" + "git.narnian.us/lordwelch/lsgo" + "github.com/go-kit/kit/log" ) @@ -15,7 +17,7 @@ type LSBHeader struct { Size uint32 Endianness uint32 Unknown uint32 - Version LSMetadata + Version lsgo.LSMetadata } func (lsbh *LSBHeader) Read(r io.ReadSeeker) error { @@ -25,7 +27,7 @@ func (lsbh *LSBHeader) Read(r io.ReadSeeker) error { n int 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) n, err = r.Read(lsbh.Signature[:]) @@ -102,27 +104,27 @@ func (lsbh *LSBHeader) Read(r io.ReadSeeker) error { type IdentifierDictionary map[int]string -func ReadLSB(r io.ReadSeeker) (Resource, error) { +func ReadLSB(r io.ReadSeeker) (lsgo.Resource, error) { var ( hdr = &LSBHeader{} h = [4]byte{0x00, 0x00, 0x00, 0x40} 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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "file") pos, err = 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{ + return lsgo.Resource{}, lsgo.HeaderError{ Expected: []byte("LSFM"), Got: hdr.Signature[:], } @@ -132,13 +134,13 @@ func ReadLSB(r io.ReadSeeker) (Resource, error) { 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) 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,7 +155,7 @@ 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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "dictionary") pos, err = r.Seek(0, io.SeekCurrent) 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) 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 +201,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 +214,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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "region") pos, err = r.Seek(0, io.SeekCurrent) err = binary.Read(r, endianness, ®ionCount) 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 +237,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, ®ions[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 +255,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 +270,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,7 +283,7 @@ 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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "node") pos, err = r.Seek(0, io.SeekCurrent) 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) - 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 +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 { node.Children[i], err = readLSBNode(r, d, endianness, version, 0) if err != nil { @@ -334,12 +336,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 +350,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 +374,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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "attribute") pos, err = 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 +413,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 +425,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", "LSFM", ReadLSB) + lsgo.RegisterFormat("lsb", "\x00\x00\x00\x40", ReadLSB) +} diff --git a/lsf.go b/lsf/lsf.go similarity index 73% rename from lsf.go rename to lsf/lsf.go index 6da7039..2d6bb58 100644 --- a/lsf.go +++ b/lsf/lsf.go @@ -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() -) - -// 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 -} +var LSFSignature = [4]byte{0x4C, 0x53, 0x4F, 0x46} type LSFHeader 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 @@ -103,7 +66,7 @@ func (lsfh *LSFHeader) Read(r io.ReadSeeker) error { n int 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) n, err = r.Read(lsfh.Signature[:]) if err != nil { @@ -233,7 +196,7 @@ func (lsfh *LSFHeader) Read(r io.ReadSeeker) error { } 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 { @@ -274,7 +237,7 @@ 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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "short node") pos, err = r.Seek(0, io.SeekCurrent) err = binary.Read(r, binary.LittleEndian, &ne.NameHashTableIndex) n = 4 @@ -312,7 +275,7 @@ 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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "long node") pos, err = r.Seek(0, io.SeekCurrent) err = binary.Read(r, binary.LittleEndian, &ne.NameHashTableIndex) n = 4 @@ -417,7 +380,7 @@ 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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "short attribute") pos, err = r.Seek(0, io.SeekCurrent) err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex) @@ -454,7 +417,7 @@ 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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "long attribute") pos, err = r.Seek(0, io.SeekCurrent) err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex) @@ -503,8 +466,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,7 +485,7 @@ type AttributeInfo struct { NameOffset int // Type of this attribute (see NodeAttribute.DataType) - TypeID DataType + TypeID lsgo.DataType // Length of this attribute Length uint @@ -546,7 +509,7 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) { pos int64 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) 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) pos += int64(n) - var hash = make([]string, int(numStrings)) + hash := make([]string, int(numStrings)) for x := range hash { var ( nameLen uint16 @@ -702,19 +665,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 ReadLSF(r io.ReadSeeker) (lsgo.Resource, error) { var ( err error @@ -728,38 +681,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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "file") pos, err = r.Seek(0, io.SeekCurrent) l.Log("member", "header", "start position", pos) hdr := &LSFHeader{} err = hdr.Read(r) 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 { - 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) 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)) @@ -770,7 +721,7 @@ 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 } } @@ -783,11 +734,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,12 +744,12 @@ 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 } } @@ -813,11 +762,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,7 +772,7 @@ 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) @@ -839,18 +786,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 } @@ -867,16 +812,13 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) { // 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) + region, err := ReadNode(r, valueStart, nodeInfo, names, attributeInfo, version, engineVersion) // pretty.Log(err, region) @@ -887,7 +829,7 @@ func ReadRegions(r io.ReadSeeker, names [][]string, nodeInfo []NodeInfo, attribu return NodeInstances, err } } else { - node, err := ReadNode(r, nodeInfo, names, attributeInfo, version, engineVersion) + node, err := ReadNode(r, valueStart, nodeInfo, names, attributeInfo, version, engineVersion) // pretty.Log(err, node) @@ -903,16 +845,16 @@ 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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "node") pos, err = r.Seek(0, io.SeekCurrent) 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 { var ( attribute = attributeInfo[index] - v NodeAttribute + v lsgo.NodeAttribute ) if valueStart+int64(attribute.DataOffset) != pos { @@ -943,12 +885,12 @@ func ReadNode(r io.ReadSeeker, ni NodeInfo, names [][]string, attributeInfo []At 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 +899,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") + l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "attribute") pos, err = 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 +913,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 +923,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 +933,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 +945,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", "LSOF", ReadLSF) } diff --git a/lsgo.go b/lsgo.go new file mode 100644 index 0000000..5184a74 --- /dev/null +++ b/lsgo.go @@ -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 +} diff --git a/resource.go b/resource.go index 6ee5f1a..7dadba7 100644 --- a/resource.go +++ b/resource.go @@ -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()