11 Commits
v0.0.1 ... cmds

Author SHA1 Message Date
b5d007c865 Add ability to create a git repository from a BG3 install
Add library to parse version info from a windows PE executable
There are issues with go-git, will change to a wrapper library
2021-02-15 21:47:38 -08:00
59f8bdcc54 Change convert to use a struct instead of global variables 2021-02-15 21:35:38 -08:00
97fcfd145c GOG Changelog
Add FindChange to find a changelog matching a version
Refactor
2021-02-15 21:33:51 -08:00
32e0180f91 Add package for retrieving information from GOG (the changelog) 2021-02-15 21:32:00 -08:00
9442d5993d Add checkPaths to find BG3 when registry fails 2021-02-15 21:32:00 -08:00
2e6c882ca9 Add new main 2021-02-15 21:32:00 -08:00
6f9ebf4906 Initial code to create a git repo
separate into separate sub commands
2021-02-15 21:32:00 -08:00
8f099fcd27 Add error handling for decompression 2021-02-15 21:30:46 -08:00
1523c67ee8 Add .gitignore 2021-01-11 19:05:41 -08:00
f56ee42b79 Change signatures to constant strings
Fix issues from golangci-lint

Add sublime project file
2021-01-11 01:24:36 -08:00
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
72 changed files with 2470 additions and 628 deletions

2
.gitignore vendored Normal file
View File

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

View File

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

View File

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

View File

@ -82,46 +82,40 @@ func MakeCompressionFlags(method CompressionMethod, level CompressionLevel) int
return flags | int(level) return flags | int(level)
} }
func Decompress(compressed io.Reader, uncompressedSize int, compressionFlags byte, chunked bool) io.ReadSeeker { func Decompress(compressed io.Reader, uncompressedSize int, compressionFlags byte, chunked bool) (io.ReadSeeker, error) {
switch CompressionMethod(compressionFlags & 0x0f) { switch CompressionMethod(compressionFlags & 0x0f) {
case CMNone: case CMNone:
// logger.Println("No compression")
if v, ok := compressed.(io.ReadSeeker); ok { if v, ok := compressed.(io.ReadSeeker); ok {
return v return v, nil
} }
panic(errors.New("compressed must be an io.ReadSeeker if there is no compression")) return nil, errors.New("compressed must be an io.ReadSeeker if there is no compression")
case CMZlib: case CMZlib:
// logger.Println("zlib compression")
zr, _ := zlib.NewReader(compressed) zr, _ := zlib.NewReader(compressed)
v, _ := ioutil.ReadAll(zr) v, _ := ioutil.ReadAll(zr)
return bytes.NewReader(v) return bytes.NewReader(v), nil
case CMLZ4: case CMLZ4:
if chunked { if chunked {
// logger.Println("lz4 stream compressed")
zr := lz4.NewReader(compressed) zr := lz4.NewReader(compressed)
p := make([]byte, uncompressedSize) p := make([]byte, uncompressedSize)
_, err := zr.Read(p) _, err := zr.Read(p)
if err != nil { if err != nil {
panic(err) return nil, err
} }
return bytes.NewReader(p) return bytes.NewReader(p), nil
} }
// logger.Println("lz4 block compressed")
// panic(errors.New("not implemented"))
src, _ := ioutil.ReadAll(compressed) src, _ := ioutil.ReadAll(compressed)
// logger.Println(len(src))
dst := make([]byte, uncompressedSize*2) dst := make([]byte, uncompressedSize*2)
_, err := lz4.UncompressBlock(src, dst) _, err := lz4.UncompressBlock(src, dst)
if err != nil { if err != nil {
panic(err) return nil, err
} }
return bytes.NewReader(dst) return bytes.NewReader(dst), nil
default: default:
panic(fmt.Errorf("no decompressor found for this format: %v", compressionFlags)) return nil, fmt.Errorf("no decompressor found for this format: %v", compressionFlags)
} }
} }
@ -147,7 +141,7 @@ func ReadAttribute(r io.ReadSeeker, name string, DT DataType, length uint, l log
pos int64 pos int64
n int n int
) )
pos, err = r.Seek(0, io.SeekCurrent) pos, _ = r.Seek(0, io.SeekCurrent)
switch DT { switch DT {
case DTNone: case DTNone:
@ -406,3 +400,140 @@ func (l *LimitedReadSeeker) Seek(offset int64, whence int) (int64, error) {
return -1, io.ErrNoProgress return -1, io.ErrNoProgress
} }
} }
func ReadTranslatedString(r io.ReadSeeker, version FileVersion, engineVersion uint32) (TranslatedString, error) {
var (
str TranslatedString
err error
)
if version >= VerBG3 || engineVersion == 0x4000001d {
var version uint16
err = binary.Read(r, binary.LittleEndian, &version)
if err != nil {
return str, err
}
str.Version = version
err = binary.Read(r, binary.LittleEndian, &version)
if err != nil {
return str, err
}
if version == 0 {
str.Value, err = ReadCString(r, int(str.Version))
if err != nil {
return str, err
}
str.Version = 0
} else {
_, _ = r.Seek(-2, io.SeekCurrent)
}
} else {
str.Version = 0
var (
vlength int32
v []byte
// n int
)
err = binary.Read(r, binary.LittleEndian, &vlength)
if err != nil {
return str, err
}
v = make([]byte, vlength)
_, err = r.Read(v)
if err != nil {
return str, err
}
str.Value = string(v)
}
var handleLength int32
err = binary.Read(r, binary.LittleEndian, &handleLength)
if err != nil {
return str, err
}
str.Handle, err = ReadCString(r, int(handleLength))
if err != nil {
return str, err
}
return str, nil
}
func ReadTranslatedFSString(r io.Reader, version FileVersion) (TranslatedFSString, error) {
var (
str = TranslatedFSString{}
err error
)
if version >= VerBG3 {
var version uint16
err = binary.Read(r, binary.LittleEndian, &version)
if err != nil {
return str, err
}
str.Version = version
} else {
str.Version = 0
var length int32
err = binary.Read(r, binary.LittleEndian, &length)
if err != nil {
return str, err
}
str.Value, err = ReadCString(r, int(length))
if err != nil {
return str, err
}
}
var handleLength int32
err = binary.Read(r, binary.LittleEndian, &handleLength)
if err != nil {
return str, err
}
str.Handle, err = ReadCString(r, int(handleLength))
if err != nil {
return str, err
}
var arguments int32
err = binary.Read(r, binary.LittleEndian, &arguments)
if err != nil {
return str, err
}
str.Arguments = make([]TranslatedFSStringArgument, 0, arguments)
for i := 0; i < int(arguments); i++ {
arg := TranslatedFSStringArgument{}
var argKeyLength int32
err = binary.Read(r, binary.LittleEndian, &argKeyLength)
if err != nil {
return str, err
}
arg.Key, err = ReadCString(r, int(argKeyLength))
if err != nil {
return str, err
}
arg.String, err = ReadTranslatedFSString(r, version)
if err != nil {
return str, err
}
var argValueLength int32
err = binary.Read(r, binary.LittleEndian, &argValueLength)
if err != nil {
return str, err
}
arg.Value, err = ReadCString(r, int(argValueLength))
if err != nil {
return str, err
}
str.Arguments = append(str.Arguments, arg)
}
return str, nil
}

229
cmd/lsconvert/convert.go Normal file
View File

@ -0,0 +1,229 @@
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"
)
type convertFlags struct {
write bool
printXML bool
printResource bool
recurse bool
logging bool
parts string
}
func convert(arguments ...string) error {
var (
convertFS = flag.NewFlagSet("convert", flag.ExitOnError)
cvFlags = convertFlags{}
)
convertFS.BoolVar(&cvFlags.write, "w", false, "Replace the file with XML data")
convertFS.BoolVar(&cvFlags.printXML, "x", false, "Print XML to stdout")
convertFS.BoolVar(&cvFlags.printResource, "R", false, "Print the resource struct to stderr")
convertFS.BoolVar(&cvFlags.recurse, "r", false, "Recurse into directories")
convertFS.BoolVar(&cvFlags.logging, "l", false, "Enable logging to stderr")
convertFS.StringVar(&cvFlags.parts, "p", "", "Parts to filter logging for, comma separated")
convertFS.Parse(arguments)
if cvFlags.logging {
lsgo.Logger = lsgo.NewFilter(map[string][]string{
"part": strings.Split(cvFlags.parts, ","),
}, log.NewLogfmtLogger(os.Stderr))
}
for _, v := range convertFS.Args() {
fi, err := os.Stat(v)
if err != nil {
return err
}
switch {
case !fi.IsDir():
err = cvFlags.openLSF(v)
if err != nil && !errors.As(err, &lsgo.HeaderError{}) {
return err
}
case cvFlags.recurse:
_ = filepath.Walk(v, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if info.IsDir() {
if info.Name() == ".git" {
return filepath.SkipDir
}
return nil
}
err = cvFlags.openLSF(path)
if err != nil && !errors.Is(err, lsgo.ErrFormat) {
fmt.Fprintln(os.Stderr, err)
}
return nil
})
default:
return fmt.Errorf("lsconvert: %s: Is a directory\n", v)
}
}
return nil
}
func (cf *convertFlags) openLSF(filename string) error {
var (
l *lsgo.Resource
err error
n string
f interface {
io.Writer
io.StringWriter
}
)
l, err = readLSF(filename)
if err != nil {
return fmt.Errorf("reading LSF file %s failed: %w", filename, err)
}
if cf.printResource {
pretty.Log(l)
}
if cf.printXML || cf.write {
n, err = marshalXML(l)
if err != nil {
return fmt.Errorf("creating XML from LSF file %s failed: %w", filename, err)
}
if cf.write {
f, err = os.OpenFile(filename, os.O_TRUNC|os.O_RDWR, 0o666)
if err != nil {
return fmt.Errorf("writing XML from LSF file %s failed: %w", filename, err)
}
} else if cf.printXML {
f = os.Stdout
}
err = writeXML(f, n)
fmt.Fprint(f, "\n")
if err != nil {
return fmt.Errorf("writing XML from LSF file %s failed: %w", filename, err)
}
}
return nil
}
func readLSF(filename string) (*lsgo.Resource, error) {
var (
l lsgo.Resource
r io.ReadSeeker
file *os.File
fi os.FileInfo
err error
)
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()
_, 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, fmt.Errorf("decoding %q failed: %w", filename, err)
}
return &l, nil
}
func marshalXML(l *lsgo.Resource) (string, error) {
var (
v []byte
err error
)
v, err = xml.MarshalIndent(struct {
*lsgo.Resource
XMLName string `xml:"save"`
}{l, ""}, "", "\t")
if err != nil {
return string(v), err
}
n := string(v)
n = strings.ReplaceAll(n, "></version>", " />")
n = strings.ReplaceAll(n, "></attribute>", " />")
n = strings.ReplaceAll(n, "></node>", " />")
n = strings.ReplaceAll(n, "false", "False")
n = strings.ReplaceAll(n, "true", "True")
n = strings.ReplaceAll(n, "&#39;", "'")
n = strings.ReplaceAll(n, "&#34;", "&quot;")
return n, nil
}
func writeXML(f io.StringWriter, n string) error {
var err error
_, err = f.WriteString(strings.ToLower(xml.Header))
if err != nil {
return err
}
_, err = f.WriteString(n)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,19 @@
package main
import (
"github.com/jdferrell3/peinfo-go/peinfo"
)
func getPEFileVersion(filepath string) string {
f, err := peinfo.Initialize(filepath, false, "", false)
if err != nil {
panic(err)
}
defer f.OSFile.Close()
defer f.PEFile.Close()
v, _, err := f.GetVersionInfo()
if err != nil {
panic(err)
}
return v["FileVersion"]
}

308
cmd/lsconvert/init.go Normal file
View File

@ -0,0 +1,308 @@
package main
import (
"bytes"
"errors"
"flag"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/go-git/go-git/v5"
"github.com/hashicorp/go-multierror"
"git.narnian.us/lordwelch/lsgo/gog"
ie "git.narnian.us/lordwelch/lsgo/internal/exec"
"golang.org/x/sys/windows/registry"
)
var (
initFS = flag.NewFlagSet("init", flag.ExitOnError)
initFlags struct {
BG3Path string
divinePath string
Repo string
}
)
const ignore = `# ignores all binary files
*.wem
*.dds
*.png
*.jpg
*.gr2
*.bin
*.patch
*.bshd
*.tga
*.ttf
*.fnt
Assets_pak/Public/Shared/Assets/Characters/_Anims/Humans/_Female/HUM_F_Rig/HUM_F_Rig_DFLT_SPL_Somatic_MimeTalk_01
Assets_pak/Public/Shared/Assets/Characters/_Models/_Creatures/Beholder/BEH_Spectator_A_GM_DDS
Assets_pak/Public/Shared/Assets/Consumables/CONS_GEN_Food_Turkey_ABC/CONS_GEN_Food_Turkey_ABC.ma
Assets_pak/Public/Shared/Assets/Decoration/Harbour/DEC_Harbour_Shell_ABC/DEC_Harbour_Shell_Venus_A
*.bnk
*.pak
*.data
*.osi
*.cur
*.gtp
*.gts
*.ffxanim
*.ffxactor
*.ffxbones
*.bk2
`
type BG3Repo struct {
git *git.Repository
gitPath string
gog gog.GOGalaxy
BG3Path string
divinePath string
Empty bool
}
func openRepository(path string) (BG3Repo, error) {
var (
b BG3Repo
err error
)
b.gitPath = path
b.BG3Path, b.divinePath = preRequisites()
if b.BG3Path == "" {
panic("Could not locate Baldur's Gate 3")
}
b.git, err = git.PlainOpen(b.gitPath)
if err != nil {
b.Empty = true
}
return b, nil
}
func (bgr *BG3Repo) Init() error {
var err error
if bgr.Empty || bgr.git == nil {
bgr.git, err = git.PlainInit(bgr.gitPath, false)
if err == git.ErrRepositoryAlreadyExists {
bgr.git, err = git.PlainOpen(bgr.gitPath)
if err != nil {
return err
}
}
}
if citer, err := bgr.git.CommitObjects(); err == nil {
if _, err := citer.Next(); err == nil {
// Repo contains commits, nothing to do
return nil
}
}
return bgr.AddNewVersion()
}
func (bgr *BG3Repo) RetrieveGOGalaxy() error {
var err error
bgr.gog, err = gog.RetrieveGOGInfo("1456460669")
if err != nil {
return err
}
return nil
}
func (bgr *BG3Repo) AddNewVersion() error {
var (
version string
changelog gog.Change
err error
)
version = getPEFileVersion(filepath.Join(bgr.BG3Path, "bin/bg3.exe"))
err = bgr.RetrieveGOGalaxy()
if err != nil {
return err
}
changelog, err = gog.ParseChangelog(bgr.gog.Changelog, bgr.gog.Title)
if err != nil {
return err
}
versionChanges := changelog.FindChange(version)
if versionChanges == nil {
return fmt.Errorf("no version information found for %q", version)
}
// TODO: Replace with custom errors
t, err := bgr.git.Worktree()
if err != nil {
return err
}
var stat git.Status
stat, err = t.Status()
if err != nil {
return err
}
if !stat.IsClean() {
return errors.New("git working directory must be clean before adding a new version, please save your work:\n" + stat.String())
}
if _, err := t.Filesystem.Stat(".gitignore"); err != nil {
gitignore, err := t.Filesystem.Create(".gitignore")
if err != nil {
return err
}
_, err = gitignore.Write([]byte(ignore))
if err != nil {
return err
}
gitignore.Close()
}
t.Add(".gitignore")
wg := &sync.WaitGroup{}
mut := &sync.Mutex{}
errs := new(multierror.Error)
paths := []string{}
err = filepath.Walk(filepath.Join(bgr.BG3Path, "Data"), func(path string, info os.FileInfo, err error) error {
rpath := strings.TrimPrefix(path, filepath.Join(bgr.BG3Path, "Data"))
fmt.Println("bg3 ", filepath.Join(bgr.BG3Path, "Data"))
fmt.Println("path ", path)
fmt.Println("rpath", rpath)
repoPath := filepath.Join(filepath.Dir(rpath), strings.TrimSuffix(filepath.Base(rpath), filepath.Ext(rpath))+strings.ReplaceAll(filepath.Ext(rpath), ".", "_"))
fmt.Println("repopath", repoPath)
if filepath.Ext(info.Name()) != ".pak" || info.IsDir() || strings.Contains(info.Name(), "Textures") {
return nil
}
err = t.Filesystem.MkdirAll(repoPath, 0o660)
if err != nil {
return err
}
paths = append(paths, repoPath)
divine := exec.Command(bgr.divinePath, "-g", "bg3", "-s", path, "-d", filepath.Join(bgr.gitPath, repoPath), "-a", "extract-package")
var buf bytes.Buffer
divine.Stderr = &buf
divine.Stdout = &buf
err = divine.Run()
if err != nil {
return fmt.Errorf("an error occurred running divine.exe: %s", buf.String())
}
wg.Add(1)
go func() {
if err := convert("-w", "-r", filepath.Join(bgr.gitPath, repoPath)); err != nil {
mut.Lock()
errs = multierror.Append(errs, err)
mut.Unlock()
}
wg.Done()
}()
return nil
})
if err != nil {
return err
}
wg.Wait()
if errs.ErrorOrNil() != nil {
return nil
}
for _, v := range paths {
_, err = t.Add(v)
if err != nil {
return err
}
}
_, err = t.Commit(strings.Replace(versionChanges.String(), versionChanges.Title, versionChanges.Title+"\n", 1), nil)
if err != nil {
return err
}
return nil
}
func preRequisites() (BG3Path, divinePath string) {
BG3Path = locateBG3()
divinePath = locateLSLIB()
return BG3Path, divinePath
}
// locateLSLIB specifically locates divine.exe
func locateLSLIB() string {
var searchPath []string
folderName := "ExportTool-*"
if runtime.GOOS == "windows" {
for _, v := range "cdefgh" {
for _, vv := range []string{`:\Program Files*\*\`, `:\app*\`, `:\`} {
paths, err := filepath.Glob(filepath.Join(string(v)+vv, folderName))
if err != nil {
panic(err)
}
searchPath = append(searchPath, paths...)
}
}
for _, v := range []string{os.ExpandEnv(`${USERPROFILE}\app*\`), os.ExpandEnv(`${USERPROFILE}\Downloads\`), os.ExpandEnv(`${USERPROFILE}\Documents\`)} {
paths, err := filepath.Glob(filepath.Join(v, folderName))
if err != nil {
panic(err)
}
searchPath = append(searchPath, paths...)
}
}
divine, _ := ie.LookPath("divine.exe", strings.Join(searchPath, string(os.PathListSeparator)))
return divine
}
func checkPaths() string {
var searchPath []string
folderName := `Baldurs Gate 3\bin\`
if runtime.GOOS == "windows" {
for _, v := range "cdefgh" {
for _, vv := range []string{`:\Program Files*\*\`, `:\Program Files*\GOG Galaxy\Games\`, `:\GOG Galaxy\Games\`, `:\GOG Games\`, `:\app*\`, `:\`} {
paths, err := filepath.Glob(filepath.Join(string(v)+vv, folderName))
if err != nil {
panic(err)
}
searchPath = append(searchPath, paths...)
}
}
}
bg3, _ := ie.LookPath("bg3.exe", strings.Join(searchPath, string(os.PathListSeparator)))
return strings.TrimSuffix(bg3, `\bin\bg3.exe`)
}
func locateBG3() string {
installPath := initFlags.BG3Path
if installPath == "" {
installPath = checkRegistry()
}
if installPath == "" {
installPath = checkPaths()
}
return installPath
}
func checkRegistry() string {
var (
k registry.Key
err error
installPath string
)
k, err = registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\1456460669_is1`, registry.QUERY_VALUE)
if err != nil {
return ""
}
defer k.Close()
for _, v := range []string{"Inno Setup: App Path", "InstallLocation"} {
var s string
s, _, err = k.GetStringValue(v)
if err == nil {
installPath = s
break
}
}
return installPath
}

View File

@ -1,174 +1,43 @@
package main package main
import ( import (
"encoding/xml"
"errors"
"flag"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath"
"strings"
"git.narnian.us/lordwelch/lsgo"
"github.com/go-kit/kit/log"
"github.com/kr/pretty"
) )
var (
write = flag.Bool("w", false, "replace the file with xml data")
printXML = flag.Bool("x", false, "print xml to stdout")
printResource = flag.Bool("R", false, "print the resource struct to stderr")
recurse = flag.Bool("r", false, "recurse into directories")
logging = flag.Bool("l", false, "enable logging to stderr")
parts = flag.String("p", "", "parts to filter logging for, comma separated")
)
func init() {
flag.Parse()
if *logging {
lsgo.Logger = lsgo.NewFilter(map[string][]string{
"part": strings.Split(*parts, ","),
}, log.NewLogfmtLogger(os.Stderr))
}
}
func main() { func main() {
// flag.Parse()
for _, v := range flag.Args() { arg := ""
fi, err := os.Stat(v) if len(os.Args) > 1 {
arg = os.Args[1]
}
switch arg {
case "convert":
err := convert(os.Args[1:]...)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
} }
switch { case "init":
p := "."
case !fi.IsDir(): if len(os.Args) > 2 {
err = openLSF(v) p = os.Args[2]
if err != nil && !errors.As(err, &lsgo.HeaderError{}) { }
fmt.Fprintln(os.Stderr, err) repo, err := openRepository(p)
os.Exit(1) if err != nil {
} fmt.Fprintln(os.Stderr, err)
os.Exit(1)
case *recurse: }
_ = filepath.Walk(v, func(path string, info os.FileInfo, err error) error { err = repo.Init()
if err != nil { if err != nil {
return nil fmt.Fprintln(os.Stderr, err)
} os.Exit(1)
if info.IsDir() { }
if info.Name() == ".git" { default:
return filepath.SkipDir err := convert(os.Args[1:]...)
} if err != nil {
return nil fmt.Fprintln(os.Stderr, err)
}
err = openLSF(path)
if err != nil && !errors.As(err, &lsgo.HeaderError{}) {
fmt.Fprintln(os.Stderr, err)
}
return nil
})
default:
fmt.Fprintf(os.Stderr, "lsconvert: %s: Is a directory\n", v)
os.Exit(1) os.Exit(1)
} }
} }
} }
func openLSF(filename string) error {
var (
l *lsgo.Resource
err error
n string
f interface {
io.Writer
io.StringWriter
}
)
l, err = readLSF(filename)
if err != nil {
return fmt.Errorf("reading LSF file %s failed: %w", filename, err)
}
if *printResource {
pretty.Log(l)
}
if *printXML || *write {
n, err = marshalXML(l)
if err != nil {
return fmt.Errorf("creating XML from LSF file %s failed: %w", filename, err)
}
if *write {
f, err = os.OpenFile(filename, os.O_TRUNC|os.O_RDWR, 0o666)
if err != nil {
return fmt.Errorf("writing XML from LSF file %s failed: %w", filename, err)
}
} else if *printXML {
f = os.Stdout
}
err = writeXML(f, n)
fmt.Fprint(f, "\n")
if err != nil {
return fmt.Errorf("writing XML from LSF file %s failed: %w", filename, err)
}
}
return nil
}
func readLSF(filename string) (*lsgo.Resource, error) {
var (
l lsgo.Resource
f *os.File
err error
)
f, err = os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
l, err = lsgo.ReadLSF(f)
if err != nil {
return nil, err
}
return &l, nil
}
func marshalXML(l *lsgo.Resource) (string, error) {
var (
v []byte
err error
)
v, err = xml.MarshalIndent(struct {
*lsgo.Resource
XMLName string `xml:"save"`
}{l, ""}, "", "\t")
if err != nil {
return string(v), err
}
n := string(v)
n = strings.ReplaceAll(n, "></version>", " />")
n = strings.ReplaceAll(n, "></attribute>", " />")
n = strings.ReplaceAll(n, "></node>", " />")
n = strings.ReplaceAll(n, "false", "False")
n = strings.ReplaceAll(n, "true", "True")
n = strings.ReplaceAll(n, "&#39;", "'")
n = strings.ReplaceAll(n, "&#34;", "&quot;")
return n, nil
}
func writeXML(f io.StringWriter, n string) error {
var (
err error
)
_, err = f.WriteString(strings.ToLower(xml.Header))
if err != nil {
return err
}
_, err = f.WriteString(n)
if err != nil {
return err
}
return nil
}

View File

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

92
format.go Normal file
View File

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

15
go.mod
View File

@ -2,12 +2,19 @@ module git.narnian.us/lordwelch/lsgo
go 1.15 go 1.15
replace github.com/pierrec/lz4/v4 v4.1.1 => ./lz4 replace github.com/pierrec/lz4/v4 v4.1.3 => ./third_party/lz4
replace github.com/jdferrell3/peinfo-go v0.0.0-20191128021257-cff0d8506fb1 => ./third_party/peinfo-go
require ( require (
github.com/go-git/go-git/v5 v5.2.0
github.com/go-kit/kit v0.10.0 github.com/go-kit/kit v0.10.0
github.com/google/uuid v1.1.2 github.com/google/uuid v1.1.4
github.com/hashicorp/go-multierror v1.1.0
github.com/jdferrell3/peinfo-go v0.0.0-20191128021257-cff0d8506fb1
github.com/kr/pretty v0.2.1 github.com/kr/pretty v0.2.1
github.com/pierrec/lz4/v4 v4.1.1 github.com/pierrec/lz4/v4 v4.1.3
gonum.org/v1/gonum v0.8.1 golang.org/x/net v0.0.0-20200301022130-244492dfa37a
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363
gonum.org/v1/gonum v0.8.2
) )

76
go.sum
View File

@ -7,15 +7,21 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
@ -37,7 +43,9 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@ -45,15 +53,29 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI=
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
@ -82,12 +104,13 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@ -98,11 +121,14 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@ -118,8 +144,13 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -128,6 +159,8 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -136,8 +169,9 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
@ -149,6 +183,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
@ -166,6 +202,8 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@ -189,8 +227,10 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@ -216,6 +256,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@ -232,10 +274,13 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
@ -251,10 +296,15 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -284,6 +334,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -301,13 +353,21 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -329,8 +389,8 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapK
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.1 h1:wGtP3yGpc5mCLOLeTeBdjeui9oZSz5De0eOjMLC/QuQ= gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
gonum.org/v1/gonum v0.8.1/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
@ -355,16 +415,22 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

236
gog/changelog.go Normal file
View File

@ -0,0 +1,236 @@
package gog
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
type Change struct {
Title string
Changes []string
Sub []Change
recurse bool
}
func (c Change) str(indent int) string {
var (
s = new(strings.Builder)
ind = strings.Repeat("\t", indent)
)
fmt.Fprintln(s, ind+c.Title)
for _, v := range c.Changes {
fmt.Fprintln(s, ind+"\t"+v)
}
for _, v := range c.Sub {
s.WriteString(v.str(indent + 1))
}
return s.String()
}
func (c Change) String() string {
return c.str(0)
}
func (c Change) FindChange(version string) *Change {
if strings.Contains(c.Title, version) {
return &c
}
for _, v := range c.Sub {
cc := v.FindChange(version)
if cc != nil {
return cc
}
}
return nil
}
func debug(f ...interface{}) {
if len(os.Args) > 2 && os.Args[2] == "debug" {
fmt.Println(f...)
}
}
// Stringify returns the text from a node and all its children
func stringify(h *html.Node) string {
var (
f func(*html.Node)
str string
def = h
)
f = func(n *html.Node) {
if n.Type == html.TextNode {
str += strings.TrimSpace(n.Data) + " "
}
if n.DataAtom == atom.Br {
str += "\n"
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
f(c)
}
}
f(def)
return str
}
// ParseChange returns the parsed change and the next node to start parsing the next change
func ParseChange(h *html.Node, title string) (Change, *html.Node) {
var c Change
c.Title = title
debug("change", strings.TrimSpace(h.Data))
l:
for h.Data != "hr" {
switch h.Type {
case html.ErrorNode:
panic("An error happened!?!?")
case html.TextNode:
debug("text", strings.TrimSpace(h.Data))
if strings.TrimSpace(h.Data) != "" {
c.Changes = append(c.Changes, strings.TrimSpace(h.Data))
}
case html.ElementNode:
switch h.DataAtom {
case atom.H1, atom.H2, atom.H3, atom.H4, atom.H5, atom.H6, atom.P:
debug(h.DataAtom.String())
if c.Title == "" {
c.Title = strings.TrimSpace(stringify(h))
} else {
tmp := drain(h.NextSibling)
switch tmp.DataAtom {
case atom.H1, atom.H2, atom.H3, atom.H4, atom.H5, atom.H6:
h = tmp
c.Changes = append(c.Changes, strings.TrimSpace(stringify(h)))
break
}
var cc Change
debug("h", strings.TrimSpace(h.Data))
debug("h", strings.TrimSpace(h.NextSibling.Data))
cc, h = ParseChange(h.NextSibling, stringify(h))
debug("h2", h.Data, h.PrevSibling.Data)
// h = h.Parent
c.Sub = append(c.Sub, cc)
continue
}
case atom.Ul:
debug(h.DataAtom.String())
h = h.FirstChild
continue
case atom.Li:
debug("li", h.DataAtom.String())
if h.FirstChild != h.LastChild && h.FirstChild.NextSibling.DataAtom != atom.P && h.FirstChild.NextSibling.DataAtom != atom.A {
var cc Change
cc, h = ParseChange(h.FirstChild.NextSibling, strings.TrimSpace(h.FirstChild.Data))
h = h.Parent
if h.NextSibling != nil {
h = h.NextSibling
}
// pretty.Println(cc)
c.Sub = append(c.Sub, cc)
break l
}
fallthrough
default:
var (
f func(*html.Node)
str string
def = h
)
f = func(n *html.Node) {
if n.Type == html.TextNode {
str += strings.TrimSpace(n.Data) + " "
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
f(c)
}
}
f(def)
c.Changes = append(c.Changes, strings.TrimSpace(str))
}
}
if h.DataAtom == atom.Ul {
h = h.Parent
}
if h.NextSibling != nil { // Move to the next node, h should never be nil
debug("next", h.Type, h.NextSibling.Type)
h = h.NextSibling
} else if h.DataAtom == atom.Li || h.PrevSibling.DataAtom == atom.Li && h.Parent.DataAtom != atom.Body { // If we are currently in a list then go up one unless that would take us to body
debug("ul", strings.TrimSpace(h.Data), strings.TrimSpace(h.Parent.Data))
if h.Parent.NextSibling == nil {
h = h.Parent
} else { // go to parents next sibling so we don't parse the same node again
h = h.Parent.NextSibling
}
debug("break2", strings.TrimSpace(h.Data))
break
} else {
debug("I don't believe this should ever happen")
break
}
}
h = drain(h)
return c, h
}
// drain skips over non-Element Nodes
func drain(h *html.Node) *html.Node {
for h.NextSibling != nil {
if h.Type == html.ElementNode {
break
}
h = h.NextSibling
}
return h
}
func ParseChangelog(ch, title string) (Change, error) {
var (
p *html.Node
v Change
err error
)
v.Title = title
p, err = html.Parse(strings.NewReader(ch))
if err != nil {
return v, err
}
p = p.FirstChild.FirstChild.NextSibling.FirstChild
for p != nil && p.NextSibling != nil {
var tmp Change
tmp, p = ParseChange(p, "")
if p.DataAtom == atom.Hr {
p = p.NextSibling
}
v.Sub = append(v.Sub, tmp)
}
return v, nil
}
func RetrieveGOGInfo(id string) (GOGalaxy, error) {
var (
r *http.Response
err error
b []byte
info GOGalaxy
)
r, err = http.Get("https://api.gog.com/products/" + id + "?expand=downloads,expanded_dlcs,description,screenshots,videos,related_products,changelog")
if err != nil {
return GOGalaxy{}, fmt.Errorf("failed to retrieve GOG info: %w", err)
}
b, err = ioutil.ReadAll(r.Body)
if err != nil {
return GOGalaxy{}, fmt.Errorf("failed to retrieve GOG info: %w", err)
}
r.Body.Close()
err = json.Unmarshal(b, &info)
if err != nil {
return GOGalaxy{}, fmt.Errorf("failed to retrieve GOG info: %w", err)
}
return info, nil
}

201
gog/gog.go Normal file
View File

@ -0,0 +1,201 @@
package gog
import (
"bytes"
"encoding/json"
"reflect"
"time"
)
type GOGalaxy struct {
ID int `json:"id"`
Title string `json:"title"`
PurchaseLink string `json:"purchase_link"`
Slug string `json:"slug"`
ContentSystemCompatibility struct {
Windows bool `json:"windows"`
Osx bool `json:"osx"`
Linux bool `json:"linux"`
} `json:"content_system_compatibility"`
Languages map[string]string `json:"languages"`
Links struct {
PurchaseLink string `json:"purchase_link"`
ProductCard string `json:"product_card"`
Support string `json:"support"`
Forum string `json:"forum"`
} `json:"links"`
InDevelopment struct {
Active bool `json:"active"`
Until interface{} `json:"until"`
} `json:"in_development"`
IsSecret bool `json:"is_secret"`
IsInstallable bool `json:"is_installable"`
GameType string `json:"game_type"`
IsPreOrder bool `json:"is_pre_order"`
ReleaseDate time.Time `json:"release_date"`
Images Images `json:"images"`
Dlcs DLCs `json:"dlcs"`
Downloads struct {
Installers []Download `json:"installers"`
Patches []Download `json:"patches"`
LanguagePacks []Download `json:"language_packs"`
BonusContent []Download `json:"bonus_content"`
} `json:"downloads"`
ExpandedDlcs []Product `json:"expanded_dlcs"`
Description struct {
Lead string `json:"lead"`
Full string `json:"full"`
WhatsCoolAboutIt string `json:"whats_cool_about_it"`
} `json:"description"`
Screenshots []struct {
ImageID string `json:"image_id"`
FormatterTemplateURL string `json:"formatter_template_url"`
FormattedImages []struct {
FormatterName string `json:"formatter_name"`
ImageURL string `json:"image_url"`
} `json:"formatted_images"`
} `json:"screenshots"`
Videos []struct {
VideoURL string `json:"video_url"`
ThumbnailURL string `json:"thumbnail_url"`
Provider string `json:"provider"`
} `json:"videos"`
RelatedProducts []Product `json:"related_products"`
Changelog string `json:"changelog"`
}
func (gog *GOGalaxy) UnmarshalJSON(d []byte) error {
var err error
type T2 GOGalaxy // create new type with same structure as T but without its method set to avoid infinite `UnmarshalJSON` call stack
x := struct {
T2
ReleaseDate string `json:"release_date"`
}{} // don't forget this, if you do and 't' already has some fields set you would lose them; see second example
if err = json.Unmarshal(d, &x); err != nil {
return err
}
*gog = GOGalaxy(x.T2)
if gog.ReleaseDate, err = time.Parse("2006-01-02T15:04:05-0700", x.ReleaseDate); err != nil {
return err
}
return nil
}
type DLCs struct {
Products []struct {
ID int `json:"id"`
Link string `json:"link"`
ExpandedLink string `json:"expanded_link"`
} `json:"products"`
AllProductsURL string `json:"all_products_url"`
ExpandedAllProductsURL string `json:"expanded_all_products_url"`
}
func (DLC *DLCs) UnmarshalJSON(d []byte) error {
type T2 GOGalaxy
var x T2
dec := json.NewDecoder(bytes.NewBuffer(d))
t, err := dec.Token()
if err != nil {
return err
}
if delim, ok := t.(json.Delim); ok {
if delim == '[' {
return nil
} else {
return json.Unmarshal(d, &x)
}
} else {
return &json.UnmarshalTypeError{
Value: reflect.TypeOf(t).String(),
Type: reflect.TypeOf(*DLC),
Offset: 0,
Struct: "DLCs",
Field: ".",
}
}
}
type Languages map[string]string
type Download struct {
ID string `json:"id"`
Name string `json:"name"`
Os string `json:"os"`
Language string `json:"language"`
LanguageFull string `json:"language_full"`
Version string `json:"version"`
TotalSize int64 `json:"total_size"`
Files []File `json:"files"`
}
func (d *Download) UnmarshalJSON(data []byte) error {
var i json.Number
type T2 Download // create new type with same structure as T but without its method set to avoid infinite `UnmarshalJSON` call stack
x := struct {
T2
ID json.RawMessage `json:"id"`
}{} // don't forget this, if you do and 't' already has some fields set you would lose them; see second example
if err := json.Unmarshal(data, &x); err != nil {
return err
}
*d = Download(x.T2)
if err := json.Unmarshal(x.ID, &i); err != nil {
if err := json.Unmarshal(x.ID, &d.ID); err != nil {
return err
}
return nil
}
d.ID = i.String()
return nil
}
type File struct {
ID string `json:"id"`
Size int `json:"size"`
Downlink string `json:"downlink"`
}
type Product struct {
ID int `json:"id"`
Title string `json:"title"`
PurchaseLink string `json:"purchase_link"`
Slug string `json:"slug"`
ContentSystemCompatibility struct {
Windows bool `json:"windows"`
Osx bool `json:"osx"`
Linux bool `json:"linux"`
} `json:"content_system_compatibility"`
Links struct {
PurchaseLink string `json:"purchase_link"`
ProductCard string `json:"product_card"`
Support string `json:"support"`
Forum string `json:"forum"`
} `json:"links"`
InDevelopment struct {
Active bool `json:"active"`
Until time.Time `json:"until"`
} `json:"in_development"`
IsSecret bool `json:"is_secret"`
IsInstallable bool `json:"is_installable"`
GameType string `json:"game_type"`
IsPreOrder bool `json:"is_pre_order"`
ReleaseDate string `json:"release_date"`
Images Images `json:"images"`
Languages Languages `json:"languages,omitempty"`
}
type Images struct {
Background string `json:"background"`
Logo string `json:"logo"`
Logo2X string `json:"logo2x"`
Icon string `json:"icon"`
SidebarIcon string `json:"sidebarIcon"`
SidebarIcon2X string `json:"sidebarIcon2x"`
MenuNotificationAv string `json:"menuNotificationAv"`
MenuNotificationAv2 string `json:"menuNotificationAv2"`
}

53
internal/exec/lp_unix.go Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package exec
import (
"os"
"os/exec"
"path/filepath"
"strings"
)
func findExecutable(file string) error {
d, err := os.Stat(file)
if err != nil {
return err
}
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
return nil
}
return os.ErrPermission
}
// LookPath searches for an executable named file in the
// directories named by the PATH environment variable.
// If file contains a slash, it is tried directly and the PATH is not consulted.
// The result may be an absolute path or a path relative to the current directory.
func LookPath(file, path string) (string, error) {
// NOTE(rsc): I wish we could use the Plan 9 behavior here
// (only bypass the path if file begins with / or ./ or ../)
// but that would not match all the Unix shells.
if strings.Contains(file, "/") {
err := findExecutable(file)
if err == nil {
return file, nil
}
return "", &exec.Error{file, err}
}
for _, dir := range filepath.SplitList(path) {
if dir == "" {
// Unix shell semantics: path element "" means "."
dir = "."
}
path := filepath.Join(dir, file)
if err := findExecutable(path); err == nil {
return path, nil
}
}
return "", &exec.Error{file, exec.ErrNotFound}
}

View File

@ -0,0 +1,89 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package exec
import (
"os"
"os/exec"
"path/filepath"
"strings"
)
func chkStat(file string) error {
d, err := os.Stat(file)
if err != nil {
return err
}
if d.IsDir() {
return os.ErrPermission
}
return nil
}
func hasExt(file string) bool {
i := strings.LastIndex(file, ".")
if i < 0 {
return false
}
return strings.LastIndexAny(file, `:\/`) < i
}
func findExecutable(file string, exts []string) (string, error) {
if len(exts) == 0 {
return file, chkStat(file)
}
if hasExt(file) {
if chkStat(file) == nil {
return file, nil
}
}
for _, e := range exts {
if f := file + e; chkStat(f) == nil {
return f, nil
}
}
return "", os.ErrNotExist
}
// LookPath searches for an executable named file in the
// directories named by the PATH environment variable.
// If file contains a slash, it is tried directly and the PATH is not consulted.
// LookPath also uses PATHEXT environment variable to match
// a suitable candidate.
// The result may be an absolute path or a path relative to the current directory.
func LookPath(file, path string) (string, error) {
var exts []string
x := os.Getenv(`PATHEXT`)
if x != "" {
for _, e := range strings.Split(strings.ToLower(x), `;`) {
if e == "" {
continue
}
if e[0] != '.' {
e = "." + e
}
exts = append(exts, e)
}
} else {
exts = []string{".com", ".exe", ".bat", ".cmd"}
}
if strings.ContainsAny(file, `:\/`) {
if f, err := findExecutable(file, exts); err == nil {
return f, nil
} else {
return "", &exec.Error{file, err}
}
}
if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
return f, nil
}
for _, dir := range filepath.SplitList(path) {
if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil {
return f, nil
}
}
return "", &exec.Error{file, exec.ErrNotFound}
}

View File

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

View File

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

44
lsgo.go Normal file
View File

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

37
lsgo.sublime-project Normal file
View File

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

View File

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

15
third_party/peinfo-go/.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Mac files
.DS_Store

49
third_party/peinfo-go/LICENSE vendored Normal file
View File

@ -0,0 +1,49 @@
MIT License
Copyright (c) 2019 John Ferrell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
peinfo/revoked.go
Copyright (c) 2014 CloudFlare Inc.
Copyright (c) 2019 John Ferrell
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

62
third_party/peinfo-go/README.md vendored Normal file
View File

@ -0,0 +1,62 @@
# peinfo-go
This is a PE (Portable Executable) parser written in GoLang. I wanted to learn more about the PE format, specifically how the certificates were stored. What better way is there than to write some code?
_This is a work in progress and will continue to change._
This leverages the `debug/pe` package for parsing of the common headers/sections.
Current state:
- Displays some PE details
- Validates certificate, verifies certificate chain, checks against CRL
- Parses Version Info struct
- Displays imports
TODO:
- ~~Actually Parse Version Info struct (currently displayed as raw binary)~~
- Re-write function for finding Version Info (currently written so I could better understand the structure)
- ~~Custom certificate stores~~
## Example
```
[user:~/peinfo-go\ > go run cmd/main.go -certdir ~/RootCerts -versioninfo ~/Downloads/PsExec.exe
type: pe32
TimeDateStamp: 2016-06-28 18:43:09 +0000 UTC
Characteristics: [Executable 32bit]
Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
Cert:
subject: CN=Microsoft Corporation,OU=MOPR,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US
issuer: CN=Microsoft Code Signing PCA,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US
not before: 2015-06-04 17:42:45 +0000 UTC
not after: 2016-09-04 17:42:45 +0000 UTC
CRL: [http://crl.microsoft.com/pki/crl/products/MicCodSigPCA_08-31-2010.crl]
verified: true (chain expired: true)
Version Info:
BuildDate :
BuildVersion :
Comments :
CompanyName : Sysinternals - www.sysinternals.com
Copyright :
FileDescription : Execute processes remotely
FileVersion : 2.2
InternalName : PsExec
LegalCopyright : Copyright (C) 2001-2016 Mark Russinovich
LegalTrademarks :
OriginalFilename : psexec.c
PrivateBuild :
ProductName : Sysinternals PsExec
ProductVersion : 2.2
SpecialBuild :
langCharSet : 040904b0h$
```
## References
- https://golang.org/pkg/debug/pe/
- http://www.pelib.com/resources/luevel.txt
- https://github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/EXE.pm
- https://github.com/deptofdefense/SalSA/blob/master/pe.py
- https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#resource-directory-entries
- https://github.com/quarkslab/dreamboot/blob/31e155b06802dce94367c38ea93316f7cb86cb15/QuarksUBootkit/PeCoffLib.c
- https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format#the-attribute-certificate-table-image-only

7
third_party/peinfo-go/go.mod vendored Normal file
View File

@ -0,0 +1,7 @@
module github.com/jdferrell3/peinfo-go
require (
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
)
go 1.13

10
third_party/peinfo-go/go.sum vendored Normal file
View File

@ -0,0 +1,10 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

17
third_party/peinfo-go/peinfo/consts.go vendored Normal file
View File

@ -0,0 +1,17 @@
package peinfo
var FileOS = map[uint32]string{
0x00001: "Win16",
0x00002: "PM-16",
0x00003: "PM-32",
0x00004: "Win32",
0x10000: "DOS",
0x20000: "OS/2 16-bit",
0x30000: "OS/2 32-bit",
0x40000: "Windows NT",
0x10001: "Windows 16-bit",
0x10004: "Windows 32-bit",
0x20002: "OS/2 16-bit PM-16",
0x30003: "OS/2 32-bit PM-32",
0x40004: "Windows NT 32-bit",
}

View File

@ -0,0 +1,31 @@
package peinfo
import (
"debug/pe"
"os"
"path/filepath"
)
// Initialize returns the config for execution
func Initialize(filePath string, verbose bool, rootCertDir string, extractCert bool) (ConfigT, error) {
fh, err := os.Open(filePath)
if nil != err {
return ConfigT{}, err
}
tempPE, err := pe.NewFile(fh)
if nil != err {
return ConfigT{}, err
}
file := ConfigT{
FileName: filepath.Base(filePath),
OSFile: fh,
PEFile: tempPE,
Verbose: verbose,
ExtractCert: extractCert,
RootCertDir: rootCertDir,
}
return file, nil
}

124
third_party/peinfo-go/peinfo/peinfo.go vendored Normal file
View File

@ -0,0 +1,124 @@
package peinfo
import (
"debug/pe"
"encoding/binary"
"fmt"
"os"
"time"
)
// http://www.pelib.com/resources/luevel.txt
// https://github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/EXE.pm
// https://github.com/deptofdefense/SalSA/blob/master/pe.py
// https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#resource-directory-entries
// https://github.com/quarkslab/dreamboot/blob/31e155b06802dce94367c38ea93316f7cb86cb15/QuarksUBootkit/PeCoffLib.c
// https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format#the-attribute-certificate-table-image-only
// https://docs.microsoft.com/en-us/windows/desktop/menurc/vs-versioninfo
var (
sizeofOptionalHeader32 = uint16(binary.Size(pe.OptionalHeader32{}))
sizeofOptionalHeader64 = uint16(binary.Size(pe.OptionalHeader64{}))
)
type ImageDirectoryT struct {
Type int
VirtualAddress uint32
Size uint32
ImageBase uint64
}
func (cfg *ConfigT) HeaderMagic() uint16 {
pe64 := cfg.PEFile.Machine == pe.IMAGE_FILE_MACHINE_AMD64
if pe64 {
return cfg.PEFile.OptionalHeader.(*pe.OptionalHeader64).Magic
}
return cfg.PEFile.OptionalHeader.(*pe.OptionalHeader32).Magic
}
func (cfg *ConfigT) GetPEType() string {
t := "pe32"
if cfg.PEFile.Machine == pe.IMAGE_FILE_MACHINE_AMD64 {
t = "pe32+"
}
return t
}
func (cfg *ConfigT) GetImageSubSystem() string {
pe64 := cfg.PEFile.Machine == pe.IMAGE_FILE_MACHINE_AMD64
subsystem := map[uint16]string{
0: "IMAGE_SUBSYSTEM_UNKNOWN",
1: "IMAGE_SUBSYSTEM_NATIVE",
2: "IMAGE_SUBSYSTEM_WINDOWS_GUI",
3: "IMAGE_SUBSYSTEM_WINDOWS_CUI",
4: "IMAGE_SUBSYSTEM_OS2_CUI",
5: "IMAGE_SUBSYSTEM_POSIX_CUI",
9: "IMAGE_SUBSYSTEM_WINDOWS_CE_GUI",
10: "IMAGE_SUBSYSTEM_EFI_APPLICATION",
11: "IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER",
12: "IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER",
13: "IMAGE_SUBSYSTEM_EFI_ROM",
14: "IMAGE_SUBSYSTEM_XBOX",
15: "IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION",
}
if pe64 {
return subsystem[cfg.PEFile.OptionalHeader.(*pe.OptionalHeader64).Subsystem]
}
return subsystem[cfg.PEFile.OptionalHeader.(*pe.OptionalHeader32).Subsystem]
}
// GetCharacteristics returns a list of PE characteristics
func (cfg *ConfigT) GetCharacteristics() []string {
characteristics := []string{}
if (cfg.PEFile.FileHeader.Characteristics & 0x0002) > 1 {
characteristics = append(characteristics, "Executable")
}
if (cfg.PEFile.FileHeader.Characteristics & 0x0100) > 1 {
characteristics = append(characteristics, "32bit")
}
if (cfg.PEFile.FileHeader.Characteristics & 0x2000) > 1 {
characteristics = append(characteristics, "DLL")
}
return characteristics
}
// GetTimeDateStamp returns the date-time stamp in the PE's header
func (cfg *ConfigT) GetTimeDateStamp() string {
tm := time.Unix(int64(cfg.PEFile.FileHeader.TimeDateStamp), 0)
return fmt.Sprintf("%s", tm.UTC())
}
// FindDataDirectory
func (cfg *ConfigT) FindDataDirectory(imageDirectoryEntryType int) (idd ImageDirectoryT) {
pe64 := cfg.PEFile.Machine == pe.IMAGE_FILE_MACHINE_AMD64
var dd pe.DataDirectory
if pe64 {
dd = cfg.PEFile.OptionalHeader.(*pe.OptionalHeader64).DataDirectory[imageDirectoryEntryType]
idd.ImageBase = cfg.PEFile.OptionalHeader.(*pe.OptionalHeader64).ImageBase
} else {
dd = cfg.PEFile.OptionalHeader.(*pe.OptionalHeader32).DataDirectory[imageDirectoryEntryType]
idd.ImageBase = uint64(cfg.PEFile.OptionalHeader.(*pe.OptionalHeader32).ImageBase)
}
idd.VirtualAddress = dd.VirtualAddress
idd.Size = dd.Size
idd.Type = imageDirectoryEntryType
return idd
}
// Tell is a wrapper for Seek()
func (cfg *ConfigT) Tell() int64 {
pos, _ := cfg.OSFile.Seek(0, os.SEEK_CUR)
return pos
}

90
third_party/peinfo-go/peinfo/structs.go vendored Normal file
View File

@ -0,0 +1,90 @@
package peinfo
import (
"debug/pe"
"os"
)
type ConfigT struct {
FileName string
OSFile *os.File
PEFile *pe.File
ExtractCert bool
Verbose bool
RootCertDir string
}
type ResourceDirectoryD struct {
Characteristics uint32
TimeDateStamp uint32
MajorVersion uint16
MinorVersion uint16
NumberOfNamedEntries uint16
NumberOfIdEntries uint16
}
type CertDetails struct {
Length uint32
Revision uint16
CertificateType uint16
DER []byte
}
// typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
// union {
// struct {
// DWORD NameOffset:31;
// DWORD NameIsString:1;
// };
// DWORD Name;
// WORD Id;
// };
// union {
// DWORD OffsetToData;
// struct {
// DWORD OffsetToDirectory:31;
// DWORD DataIsDirectory:1;
// };
// };
// } IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
type ResourceDirectoryEntry struct {
Name uint32
OffsetToData uint32
}
type ResourceDirectoryEntryNamed struct {
Name uint32
OffsetToData uint32
}
/* Resource Directory Entry */
// type ResourceDirectoryEntryT struct {
// ResourceDirectoryEntry ResourceDirectoryEntry
// FileOffset uint32
// Size uint32
// DataIsDirectory bool
// }
type _IMAGE_RESOURCE_DATA_ENTRY struct {
OffsetToData uint32
Size uint32
CodePage uint32
Reserved uint32
}
type VS_FIXEDFILEINFO struct {
DwSignature uint32
DwStrucVersion uint32
DwFileVersionMS uint32
DwFileVersionLS uint32
DwProductVersionMS uint32
DwProductVersionLS uint32
DwFileFlagsMask uint32
DwFileFlags uint32
DwFileOS uint32
DwFileType uint32
DwFileSubtype uint32
DwFileDateMS uint32
DwFileDateLS uint32
}

267
third_party/peinfo-go/peinfo/verinfo.go vendored Normal file
View File

@ -0,0 +1,267 @@
package peinfo
import (
"bytes"
"debug/pe"
"encoding/binary"
"encoding/hex"
"fmt"
"os"
)
const (
RT_VERSION = 16
)
func (cfg *ConfigT) FindVerInfoOffset(fileOffset int64, sectionOffset uint32, sectionVirtualAddress uint32) (verInfoOffset int64, len uint32, err error) {
pos, _ := cfg.OSFile.Seek(fileOffset, os.SEEK_SET)
if pos != fileOffset {
return 0, 0, fmt.Errorf("did not seek to offset")
}
type VerInfoDetailsT struct {
Off uint32
Len uint32
D1 uint32
D2 uint32
}
var peoff VerInfoDetailsT
err = binary.Read(cfg.OSFile, binary.LittleEndian, &peoff)
if nil != err {
return verInfoOffset, len, err
}
// $filePos = $off + $$section{Base} - $$section{VirtualAddress};
verInfoOffset = int64(peoff.Off + sectionOffset - sectionVirtualAddress)
return verInfoOffset, peoff.Len, nil
}
func (cfg *ConfigT) GetVersionInfo() (vi map[string]string, keys []string, err error) {
vi = map[string]string{
"BuildDate": "",
"BuildVersion": "",
"Comments": "",
"CompanyName": "",
"Copyright": "",
"FileDescription": "",
"FileVersion": "",
"InternalName": "",
"LegalCopyright": "",
"LegalTrademarks": "",
"OriginalFilename": "",
"PrivateBuild": "",
"ProductName": "",
"ProductVersion": "",
"SpecialBuild": "",
"langCharSet": "",
// "varFileInfo": "",
}
keys = []string{
"BuildDate",
"BuildVersion",
"Comments",
"CompanyName",
"Copyright",
"FileDescription",
"FileVersion",
"InternalName",
"LegalCopyright",
"LegalTrademarks",
"OriginalFilename",
"PrivateBuild",
"ProductName",
"ProductVersion",
"SpecialBuild",
"langCharSet",
// "varFileInfo"
}
section := cfg.PEFile.Section(".rsrc")
if section == nil {
return vi, keys, fmt.Errorf("resource section not found")
}
// fmt.Printf("%+v\n", section)
// Resource
_, err = cfg.OSFile.Seek(int64(0), os.SEEK_SET)
if nil != err {
return vi, keys, err
}
idd := cfg.FindDataDirectory(pe.IMAGE_DIRECTORY_ENTRY_RESOURCE)
idd.VirtualAddress -= (section.VirtualAddress - section.Offset)
if cfg.Verbose {
fmt.Printf("IMAGE_DIRECTORY_ENTRY_RESOURCE virtual address: %d\n", idd.VirtualAddress)
fmt.Printf("IMAGE_DIRECTORY_ENTRY_RESOURCE size: %d\n", idd.Size)
fmt.Printf("IMAGE_DIRECTORY_ENTRY_RESOURCE image base: %d\n", idd.ImageBase)
}
pos, err := cfg.OSFile.Seek(int64(idd.VirtualAddress), os.SEEK_SET)
if nil != err {
return vi, keys, err
}
if pos != int64(idd.VirtualAddress) {
fmt.Errorf("did not seek to VirtualAddress")
}
var table ResourceDirectoryD
err = binary.Read(cfg.OSFile, binary.LittleEndian, &table)
if nil != err {
return vi, keys, err
}
// fmt.Printf("table %+v\n", table)
x := 0
for x < int(table.NumberOfNamedEntries+table.NumberOfIdEntries) {
var entry ResourceDirectoryEntry
err = binary.Read(cfg.OSFile, binary.LittleEndian, &entry)
if nil != err {
return vi, keys, err
}
if entry.Name == RT_VERSION {
// Directory
if (entry.OffsetToData&0x80000000)>>31 == 1 {
new := entry.OffsetToData&0x7fffffff + idd.VirtualAddress
cfg.OSFile.Seek(int64(new), os.SEEK_SET)
var innerDir ResourceDirectoryD
err = binary.Read(cfg.OSFile, binary.LittleEndian, &innerDir)
if nil != err {
return vi, keys, err
}
// pos := f.Tell()
// fmt.Printf("level 1 innerDir %+v (file offset=%d)\n", innerDir, pos)
y := 0
for y < int(innerDir.NumberOfNamedEntries+innerDir.NumberOfIdEntries) {
var entry ResourceDirectoryEntry
err = binary.Read(cfg.OSFile, binary.LittleEndian, &entry)
if nil != err {
return vi, keys, err
}
// pos := f.Tell()
// fmt.Printf("item %d - level 2 buff %s (file offset=%d)\n", y, entry, pos)
if (entry.OffsetToData&0x80000000)>>31 == 1 {
new := entry.OffsetToData&0x7fffffff + idd.VirtualAddress
// fmt.Printf("level 2 DirStart 0x%x (%d)\n", new, new)
cfg.OSFile.Seek(int64(new), os.SEEK_SET)
}
var innerDir ResourceDirectoryD
err = binary.Read(cfg.OSFile, binary.LittleEndian, &innerDir)
if nil != err {
return vi, keys, err
}
// pos = f.Tell()
// fmt.Printf("level 3 innerDir %+v (file offset=%d)\n", innerDir, pos)
z := 0
for z < int(innerDir.NumberOfNamedEntries+innerDir.NumberOfIdEntries) {
var entry ResourceDirectoryEntry
err = binary.Read(cfg.OSFile, binary.LittleEndian, &entry)
if nil != err {
return vi, keys, err
}
// pos := f.Tell()
// fmt.Printf("item %d - level 3 buff %s (file offset=%d)\n", y, entry, pos)
// fmt.Printf("ver: 0x%x\n", entry.OffsetToData+idd.VirtualAddress)
// find offset of VS_VERSION_INFO
off := int64(entry.OffsetToData + idd.VirtualAddress)
viPos, viLen, err := cfg.FindVerInfoOffset(off, section.SectionHeader.Offset, section.SectionHeader.VirtualAddress)
if nil != err {
return vi, keys, err
}
// fmt.Printf("VerInfo Struct filePos: 0x%x (%d)\n", viPos, viPos)
cfg.OSFile.Seek(viPos, os.SEEK_SET)
b := make([]byte, viLen)
err = binary.Read(cfg.OSFile, binary.LittleEndian, &b)
if nil != err {
return vi, keys, err
}
// fmt.Printf("%s\n", b)
if cfg.Verbose {
fmt.Printf(" %s\n", hex.Dump(b))
}
vi, err = parseVersionInfo(b, vi)
if nil != err {
return vi, keys, err
}
return vi, keys, nil
}
y++
}
}
}
x++
}
return vi, keys, fmt.Errorf("no version info found")
}
func parseVersionInfo(vi []byte, versionInfo map[string]string) (map[string]string, error) {
// Grab everything after "StringFileInfo"
stringFileInfoTemp := bytes.Split(vi, []byte{0x53, 0x0, 0x74, 0x0, 0x72, 0x0, 0x69, 0x0, 0x6e, 0x0, 0x67, 0x0, 0x46, 0x0, 0x69, 0x0, 0x6c, 0x0, 0x65, 0x0, 0x49, 0x0, 0x6e, 0x0, 0x66, 0x0, 0x6f})
if len(stringFileInfoTemp) < 2 {
err := fmt.Errorf("can't find StringFileInfo bytes")
return versionInfo, err
}
stringFileInfo := stringFileInfoTemp[1]
divide := bytes.Split(stringFileInfo, []byte{0x0, 0x1, 0x0})
langCharSet := trimSlice(divide[1])
versionInfo["langCharSet"] = string(langCharSet)
// check for slice out of bounds
if len(divide) < 3 {
err := fmt.Errorf("VersionInfo slice too small")
return versionInfo, err
}
end := len(divide) - 1
if end < 2 {
err := fmt.Errorf("slice end less than start")
return versionInfo, err
}
values := divide[2:end]
// TODO: handle varFileInfo, currently contains binary information which chrome does not display
// varFileInfo := divide[len(divide)-1]
// versionInfo["varFileInfo"] = string(trimSlice(varFileInfo))
for _, element := range values {
temp := bytes.Split(element, []byte{0x0, 0x0, 0x0})
valueInfo := temp[:len(temp)-1]
if len(valueInfo) > 1 {
name := string(trimSlice(valueInfo[0]))
value := string(trimSlice(valueInfo[1]))
versionInfo[name] = value
}
}
return versionInfo, nil
}
func trimSlice(nonTrimmed []byte) (trimmed []byte) {
for bytes.HasPrefix(nonTrimmed, []byte{0x0}) {
nonTrimmed = nonTrimmed[1:]
}
for i, val := range nonTrimmed {
if i%2 == 0 && val != 0x0 {
trimmed = append(trimmed, val)
}
}
return trimmed
}