Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
b5d007c865 | |||
59f8bdcc54 | |||
97fcfd145c | |||
32e0180f91 | |||
9442d5993d | |||
2e6c882ca9 | |||
6f9ebf4906 | |||
8f099fcd27 | |||
1523c67ee8 | |||
f56ee42b79 | |||
2f87df40d6 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/lsconvert
|
||||
*.sublime-workspace
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Norbyte
|
||||
Copyright (c) 2020 lordwelch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -108,7 +108,6 @@ func (v Vec) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
switch i {
|
||||
case 0:
|
||||
name.Local = "x"
|
||||
// start.Name = "float1"
|
||||
case 1:
|
||||
name.Local = "y"
|
||||
start.Name.Local = "float2"
|
||||
@ -385,9 +384,7 @@ func (na *NodeAttribute) FromString(str string) error {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
)
|
||||
var err error
|
||||
|
||||
switch na.Type {
|
||||
case DTNone:
|
||||
|
163
binutils.go
163
binutils.go
@ -82,46 +82,40 @@ func MakeCompressionFlags(method CompressionMethod, level CompressionLevel) int
|
||||
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) {
|
||||
case CMNone:
|
||||
// logger.Println("No compression")
|
||||
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:
|
||||
// logger.Println("zlib compression")
|
||||
zr, _ := zlib.NewReader(compressed)
|
||||
v, _ := ioutil.ReadAll(zr)
|
||||
return bytes.NewReader(v)
|
||||
return bytes.NewReader(v), nil
|
||||
|
||||
case CMLZ4:
|
||||
if chunked {
|
||||
// logger.Println("lz4 stream compressed")
|
||||
zr := lz4.NewReader(compressed)
|
||||
p := make([]byte, uncompressedSize)
|
||||
_, err := zr.Read(p)
|
||||
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)
|
||||
// logger.Println(len(src))
|
||||
dst := make([]byte, uncompressedSize*2)
|
||||
_, err := lz4.UncompressBlock(src, dst)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytes.NewReader(dst)
|
||||
return bytes.NewReader(dst), nil
|
||||
|
||||
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
|
||||
n int
|
||||
)
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
switch DT {
|
||||
case DTNone:
|
||||
@ -406,3 +400,140 @@ func (l *LimitedReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
||||
return -1, io.ErrNoProgress
|
||||
}
|
||||
}
|
||||
|
||||
func ReadTranslatedString(r io.ReadSeeker, version FileVersion, engineVersion uint32) (TranslatedString, error) {
|
||||
var (
|
||||
str TranslatedString
|
||||
err error
|
||||
)
|
||||
|
||||
if version >= VerBG3 || engineVersion == 0x4000001d {
|
||||
var version uint16
|
||||
err = binary.Read(r, binary.LittleEndian, &version)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Version = version
|
||||
err = binary.Read(r, binary.LittleEndian, &version)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
if version == 0 {
|
||||
str.Value, err = ReadCString(r, int(str.Version))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Version = 0
|
||||
} else {
|
||||
_, _ = r.Seek(-2, io.SeekCurrent)
|
||||
}
|
||||
} else {
|
||||
str.Version = 0
|
||||
|
||||
var (
|
||||
vlength int32
|
||||
v []byte
|
||||
// n int
|
||||
)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &vlength)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
v = make([]byte, vlength)
|
||||
_, err = r.Read(v)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Value = string(v)
|
||||
}
|
||||
|
||||
var handleLength int32
|
||||
err = binary.Read(r, binary.LittleEndian, &handleLength)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Handle, err = ReadCString(r, int(handleLength))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func ReadTranslatedFSString(r io.Reader, version FileVersion) (TranslatedFSString, error) {
|
||||
var (
|
||||
str = TranslatedFSString{}
|
||||
err error
|
||||
)
|
||||
|
||||
if version >= VerBG3 {
|
||||
var version uint16
|
||||
err = binary.Read(r, binary.LittleEndian, &version)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Version = version
|
||||
} else {
|
||||
str.Version = 0
|
||||
|
||||
var length int32
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &length)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Value, err = ReadCString(r, int(length))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
}
|
||||
|
||||
var handleLength int32
|
||||
err = binary.Read(r, binary.LittleEndian, &handleLength)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Handle, err = ReadCString(r, int(handleLength))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
|
||||
var arguments int32
|
||||
err = binary.Read(r, binary.LittleEndian, &arguments)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Arguments = make([]TranslatedFSStringArgument, 0, arguments)
|
||||
for i := 0; i < int(arguments); i++ {
|
||||
arg := TranslatedFSStringArgument{}
|
||||
|
||||
var argKeyLength int32
|
||||
err = binary.Read(r, binary.LittleEndian, &argKeyLength)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
arg.Key, err = ReadCString(r, int(argKeyLength))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
|
||||
arg.String, err = ReadTranslatedFSString(r, version)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
|
||||
var argValueLength int32
|
||||
err = binary.Read(r, binary.LittleEndian, &argValueLength)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
arg.Value, err = ReadCString(r, int(argValueLength))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
|
||||
str.Arguments = append(str.Arguments, arg)
|
||||
}
|
||||
|
||||
return str, nil
|
||||
}
|
||||
|
229
cmd/lsconvert/convert.go
Normal file
229
cmd/lsconvert/convert.go
Normal 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, "'", "'")
|
||||
n = strings.ReplaceAll(n, """, """)
|
||||
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
|
||||
}
|
19
cmd/lsconvert/fileversion.go
Normal file
19
cmd/lsconvert/fileversion.go
Normal 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
308
cmd/lsconvert/init.go
Normal 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
|
||||
}
|
@ -1,174 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"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() {
|
||||
|
||||
for _, v := range flag.Args() {
|
||||
fi, err := os.Stat(v)
|
||||
// flag.Parse()
|
||||
arg := ""
|
||||
if len(os.Args) > 1 {
|
||||
arg = os.Args[1]
|
||||
}
|
||||
switch arg {
|
||||
case "convert":
|
||||
err := convert(os.Args[1:]...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
switch {
|
||||
|
||||
case !fi.IsDir():
|
||||
err = openLSF(v)
|
||||
if err != nil && !errors.As(err, &lsgo.HeaderError{}) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
case *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 = 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)
|
||||
case "init":
|
||||
p := "."
|
||||
if len(os.Args) > 2 {
|
||||
p = os.Args[2]
|
||||
}
|
||||
repo, err := openRepository(p)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = repo.Init()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
err := convert(os.Args[1:]...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
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, "'", "'")
|
||||
n = strings.ReplaceAll(n, """, """)
|
||||
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
|
||||
}
|
||||
|
14
const.go
14
const.go
@ -1,6 +1,9 @@
|
||||
package lsgo
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type FileVersion uint32
|
||||
|
||||
@ -43,3 +46,12 @@ var (
|
||||
ErrInvalidNameKey = errors.New("invalid name key")
|
||||
ErrKeyDoesNotMatch = errors.New("key for this node does not match")
|
||||
)
|
||||
|
||||
type HeaderError struct {
|
||||
Expected 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
92
format.go
Normal 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
15
go.mod
@ -2,12 +2,19 @@ module git.narnian.us/lordwelch/lsgo
|
||||
|
||||
go 1.15
|
||||
|
||||
replace github.com/pierrec/lz4/v4 v4.1.1 => ./lz4
|
||||
replace github.com/pierrec/lz4/v4 v4.1.3 => ./third_party/lz4
|
||||
|
||||
replace github.com/jdferrell3/peinfo-go v0.0.0-20191128021257-cff0d8506fb1 => ./third_party/peinfo-go
|
||||
|
||||
require (
|
||||
github.com/go-git/go-git/v5 v5.2.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/pierrec/lz4/v4 v4.1.1
|
||||
gonum.org/v1/gonum v0.8.1
|
||||
github.com/pierrec/lz4/v4 v4.1.3
|
||||
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
76
go.sum
@ -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/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/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-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-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.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/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-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/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=
|
||||
@ -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/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.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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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/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/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
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.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/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/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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
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.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
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/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.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0=
|
||||
github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
@ -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/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/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/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-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.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-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
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/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/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
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/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/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/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=
|
||||
@ -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/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
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.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-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
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/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.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/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
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.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
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/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=
|
||||
@ -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/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
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/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/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=
|
||||
@ -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/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/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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
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/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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
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/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/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=
|
||||
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=
|
||||
@ -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=
|
||||
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-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-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-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-20180807140117-3d87b88a115f/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-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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
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-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-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-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-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-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-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.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||
gonum.org/v1/gonum v0.8.1 h1:wGtP3yGpc5mCLOLeTeBdjeui9oZSz5De0eOjMLC/QuQ=
|
||||
gonum.org/v1/gonum v0.8.1/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
||||
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
|
||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
||||
@ -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/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-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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
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/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/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
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.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.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-20190102054323-c2f93a96b099/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
236
gog/changelog.go
Normal 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
201
gog/gog.go
Normal 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
53
internal/exec/lp_unix.go
Normal 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}
|
||||
}
|
89
internal/exec/lp_windows.go
Normal file
89
internal/exec/lp_windows.go
Normal 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}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package lsgo
|
||||
package lsb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
@ -7,94 +7,101 @@ import (
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"git.narnian.us/lordwelch/lsgo"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
type LSBHeader struct {
|
||||
const (
|
||||
Signature = "LSFM"
|
||||
PreBG3Signature = "\x00\x00\x00\x40"
|
||||
)
|
||||
|
||||
type Header struct {
|
||||
Signature [4]byte
|
||||
Size uint32
|
||||
Endianness uint32
|
||||
Unknown uint32
|
||||
Version LSMetadata
|
||||
Version lsgo.LSMetadata
|
||||
}
|
||||
|
||||
func (lsbh *LSBHeader) Read(r io.ReadSeeker) error {
|
||||
func (h *Header) Read(r io.ReadSeeker) error {
|
||||
var (
|
||||
l log.Logger
|
||||
pos int64
|
||||
n int
|
||||
err error
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "header")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "header")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
n, err = r.Read(lsbh.Signature[:])
|
||||
n, err = r.Read(h.Signature[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Signature", "read", n, "start position", pos, "value", fmt.Sprintf("%#x", lsbh.Signature[:]))
|
||||
l.Log("member", "Signature", "read", n, "start position", pos, "value", fmt.Sprintf("%#x", h.Signature[:]))
|
||||
pos += int64(n)
|
||||
err = binary.Read(r, binary.LittleEndian, &lsbh.Size)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Size)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Size", "read", n, "start position", pos, "value", lsbh.Size)
|
||||
l.Log("member", "Size", "read", n, "start position", pos, "value", h.Size)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsbh.Endianness)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Endianness)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Endianness", "read", n, "start position", pos, "value", lsbh.Endianness)
|
||||
l.Log("member", "Endianness", "read", n, "start position", pos, "value", h.Endianness)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsbh.Unknown)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Unknown)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Unknown", "read", n, "start position", pos, "value", lsbh.Unknown)
|
||||
l.Log("member", "Unknown", "read", n, "start position", pos, "value", h.Unknown)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Timestamp)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Version.Timestamp)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Version.Timestamp", "read", n, "start position", pos, "value", lsbh.Version.Timestamp)
|
||||
l.Log("member", "Version.Timestamp", "read", n, "start position", pos, "value", h.Version.Timestamp)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Major)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Version.Major)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Version.Major", "read", n, "start position", pos, "value", lsbh.Version.Major)
|
||||
l.Log("member", "Version.Major", "read", n, "start position", pos, "value", h.Version.Major)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Minor)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Version.Minor)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Version.Minor", "read", n, "start position", pos, "value", lsbh.Version.Minor)
|
||||
l.Log("member", "Version.Minor", "read", n, "start position", pos, "value", h.Version.Minor)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Revision)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Version.Revision)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Version.Revision", "read", n, "start position", pos, "value", lsbh.Version.Revision)
|
||||
l.Log("member", "Version.Revision", "read", n, "start position", pos, "value", h.Version.Revision)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsbh.Version.Build)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Version.Build)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Version.Build", "read", n, "start position", pos, "value", lsbh.Version.Build)
|
||||
l.Log("member", "Version.Build", "read", n, "start position", pos, "value", h.Version.Build)
|
||||
pos += int64(n)
|
||||
|
||||
return nil
|
||||
@ -102,43 +109,42 @@ func (lsbh *LSBHeader) Read(r io.ReadSeeker) error {
|
||||
|
||||
type IdentifierDictionary map[int]string
|
||||
|
||||
func ReadLSB(r io.ReadSeeker) (Resource, error) {
|
||||
func Read(r io.ReadSeeker) (lsgo.Resource, error) {
|
||||
var (
|
||||
hdr = &LSBHeader{}
|
||||
h = [4]byte{0x00, 0x00, 0x00, 0x40}
|
||||
hdr = &Header{}
|
||||
err error
|
||||
d IdentifierDictionary
|
||||
res Resource
|
||||
res lsgo.Resource
|
||||
|
||||
l log.Logger
|
||||
pos int64
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "file")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "file")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
l.Log("member", "header", "start position", pos)
|
||||
|
||||
err = hdr.Read(r)
|
||||
if err != nil {
|
||||
return Resource{}, err
|
||||
return lsgo.Resource{}, err
|
||||
}
|
||||
if !(hdr.Signature == [4]byte{'L', 'S', 'F', 'M'} || hdr.Signature == h) {
|
||||
return Resource{}, HeaderError{
|
||||
Expected: []byte("LSFM"),
|
||||
if !(string(hdr.Signature[:]) == Signature || string(hdr.Signature[:]) == PreBG3Signature) {
|
||||
return lsgo.Resource{}, lsgo.HeaderError{
|
||||
Expected: Signature,
|
||||
Got: hdr.Signature[:],
|
||||
}
|
||||
}
|
||||
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
l.Log("member", "string dictionary", "start position", pos)
|
||||
d, err = ReadLSBDictionary(r, binary.LittleEndian)
|
||||
if err != nil {
|
||||
return Resource{}, err
|
||||
return lsgo.Resource{}, err
|
||||
}
|
||||
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
l.Log("member", "Regions", "start position", pos)
|
||||
|
||||
res, err = ReadLSBRegions(r, d, binary.LittleEndian, FileVersion(hdr.Version.Major))
|
||||
res, err = ReadLSBRegions(r, d, binary.LittleEndian, lsgo.FileVersion(hdr.Version.Major))
|
||||
res.Metadata = hdr.Version
|
||||
return res, err
|
||||
}
|
||||
@ -153,8 +159,8 @@ func ReadLSBDictionary(r io.ReadSeeker, endianness binary.ByteOrder) (Identifier
|
||||
pos int64
|
||||
n int
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "dictionary")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "dictionary")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
err = binary.Read(r, endianness, &length)
|
||||
n = 4
|
||||
@ -179,7 +185,7 @@ func ReadLSBDictionary(r io.ReadSeeker, endianness binary.ByteOrder) (Identifier
|
||||
l.Log("member", "stringLength", "read", n, "start position", pos, "value", stringLength)
|
||||
pos += int64(n)
|
||||
|
||||
str, err = ReadCString(r, int(stringLength))
|
||||
str, err = lsgo.ReadCString(r, int(stringLength))
|
||||
n += int(stringLength)
|
||||
if err != nil {
|
||||
return dict, err
|
||||
@ -199,7 +205,7 @@ func ReadLSBDictionary(r io.ReadSeeker, endianness binary.ByteOrder) (Identifier
|
||||
return dict, nil
|
||||
}
|
||||
|
||||
func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version FileVersion) (Resource, error) {
|
||||
func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version lsgo.FileVersion) (lsgo.Resource, error) {
|
||||
var (
|
||||
regions []struct {
|
||||
name string
|
||||
@ -212,13 +218,13 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
|
||||
pos int64
|
||||
n int
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "region")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "region")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
err = binary.Read(r, endianness, ®ionCount)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return Resource{}, err
|
||||
return lsgo.Resource{}, err
|
||||
}
|
||||
l.Log("member", "regionCount", "read", n, "start position", pos, "value", regionCount)
|
||||
pos += int64(n)
|
||||
@ -235,17 +241,17 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
|
||||
err = binary.Read(r, endianness, &key)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return Resource{}, err
|
||||
return lsgo.Resource{}, err
|
||||
}
|
||||
l.Log("member", "key", "read", n, "start position", pos, "value", d[int(key)], "key", key)
|
||||
pos += int64(n)
|
||||
if regions[i].name, ok = d[int(key)]; !ok {
|
||||
return Resource{}, ErrInvalidNameKey
|
||||
return lsgo.Resource{}, lsgo.ErrInvalidNameKey
|
||||
}
|
||||
err = binary.Read(r, endianness, ®ions[i].offset)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return Resource{}, err
|
||||
return lsgo.Resource{}, err
|
||||
}
|
||||
l.Log("member", "offset", "read", n, "start position", pos, "value", regions[i].offset)
|
||||
pos += int64(n)
|
||||
@ -253,11 +259,11 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
|
||||
sort.Slice(regions, func(i, j int) bool {
|
||||
return regions[i].offset < regions[j].offset
|
||||
})
|
||||
res := Resource{
|
||||
Regions: make([]*Node, 0, regionCount),
|
||||
res := lsgo.Resource{
|
||||
Regions: make([]*lsgo.Node, 0, regionCount),
|
||||
}
|
||||
for _, re := range regions {
|
||||
var node *Node
|
||||
var node *lsgo.Node
|
||||
node, err = readLSBNode(r, d, endianness, version, re.offset)
|
||||
if err != nil {
|
||||
return res, err
|
||||
@ -268,12 +274,12 @@ func ReadLSBRegions(r io.ReadSeeker, d IdentifierDictionary, endianness binary.B
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version FileVersion, offset uint32) (*Node, error) {
|
||||
func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version lsgo.FileVersion, offset uint32) (*lsgo.Node, error) {
|
||||
var (
|
||||
key uint32
|
||||
attrCount uint32
|
||||
childCount uint32
|
||||
node = new(Node)
|
||||
node = new(lsgo.Node)
|
||||
err error
|
||||
ok bool
|
||||
|
||||
@ -281,8 +287,8 @@ func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.Byte
|
||||
pos int64
|
||||
n int
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "node")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "node")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
if pos != int64(offset) && offset != 0 {
|
||||
panic("shit")
|
||||
@ -315,7 +321,7 @@ func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.Byte
|
||||
}
|
||||
l.Log("member", "childCount", "read", n, "start position", pos, "value", childCount)
|
||||
|
||||
node.Attributes = make([]NodeAttribute, int(attrCount))
|
||||
node.Attributes = make([]lsgo.NodeAttribute, int(attrCount))
|
||||
|
||||
for i := range node.Attributes {
|
||||
node.Attributes[i], err = readLSBAttribute(r, d, endianness, version)
|
||||
@ -324,7 +330,7 @@ func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.Byte
|
||||
}
|
||||
}
|
||||
|
||||
node.Children = make([]*Node, int(childCount))
|
||||
node.Children = make([]*lsgo.Node, int(childCount))
|
||||
for i := range node.Children {
|
||||
node.Children[i], err = readLSBNode(r, d, endianness, version, 0)
|
||||
if err != nil {
|
||||
@ -334,12 +340,12 @@ func readLSBNode(r io.ReadSeeker, d IdentifierDictionary, endianness binary.Byte
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func readLSBAttribute(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version FileVersion) (NodeAttribute, error) {
|
||||
func readLSBAttribute(r io.ReadSeeker, d IdentifierDictionary, endianness binary.ByteOrder, version lsgo.FileVersion) (lsgo.NodeAttribute, error) {
|
||||
var (
|
||||
key uint32
|
||||
name string
|
||||
attrType uint32
|
||||
attr NodeAttribute
|
||||
attr lsgo.NodeAttribute
|
||||
err error
|
||||
ok bool
|
||||
)
|
||||
@ -348,21 +354,21 @@ func readLSBAttribute(r io.ReadSeeker, d IdentifierDictionary, endianness binary
|
||||
return attr, err
|
||||
}
|
||||
if name, ok = d[int(key)]; !ok {
|
||||
return attr, ErrInvalidNameKey
|
||||
return attr, lsgo.ErrInvalidNameKey
|
||||
}
|
||||
err = binary.Read(r, endianness, &attrType)
|
||||
if err != nil {
|
||||
return attr, err
|
||||
}
|
||||
return ReadLSBAttr(r, name, DataType(attrType), endianness, version)
|
||||
return ReadLSBAttr(r, name, lsgo.DataType(attrType), endianness, version)
|
||||
}
|
||||
|
||||
func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.ByteOrder, version FileVersion) (NodeAttribute, error) {
|
||||
func ReadLSBAttr(r io.ReadSeeker, name string, dt lsgo.DataType, endianness binary.ByteOrder, version lsgo.FileVersion) (lsgo.NodeAttribute, error) {
|
||||
// LSF and LSB serialize the buffer types differently, so specialized
|
||||
// code is added to the LSB and LSf serializers, and the common code is
|
||||
// available in BinUtils.ReadAttribute()
|
||||
var (
|
||||
attr = NodeAttribute{
|
||||
attr = lsgo.NodeAttribute{
|
||||
Type: dt,
|
||||
Name: name,
|
||||
}
|
||||
@ -372,38 +378,38 @@ func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.By
|
||||
l log.Logger
|
||||
pos int64
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsb", "part", "attribute")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsb", "part", "attribute")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
switch dt {
|
||||
case DTString, DTPath, DTFixedString, DTLSString: //, DTLSWString:
|
||||
case lsgo.DTString, lsgo.DTPath, lsgo.DTFixedString, lsgo.DTLSString: // DTLSWString:
|
||||
var v string
|
||||
err = binary.Read(r, endianness, &length)
|
||||
if err != nil {
|
||||
return attr, err
|
||||
}
|
||||
v, err = ReadCString(r, int(length))
|
||||
v, err = lsgo.ReadCString(r, int(length))
|
||||
attr.Value = v
|
||||
|
||||
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
|
||||
|
||||
return attr, err
|
||||
|
||||
case DTWString:
|
||||
case lsgo.DTWString:
|
||||
panic("Not implemented")
|
||||
|
||||
case DTTranslatedString:
|
||||
var v TranslatedString
|
||||
v, err = ReadTranslatedString(r, version, 0)
|
||||
case lsgo.DTTranslatedString:
|
||||
var v lsgo.TranslatedString
|
||||
v, err = lsgo.ReadTranslatedString(r, version, 0)
|
||||
attr.Value = v
|
||||
|
||||
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
|
||||
|
||||
return attr, err
|
||||
|
||||
case DTTranslatedFSString:
|
||||
case lsgo.DTTranslatedFSString:
|
||||
panic("Not implemented")
|
||||
var v TranslatedFSString
|
||||
var v lsgo.TranslatedFSString
|
||||
// v, err = ReadTranslatedFSString(r, Version)
|
||||
attr.Value = v
|
||||
|
||||
@ -411,7 +417,7 @@ func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.By
|
||||
|
||||
return attr, err
|
||||
|
||||
case DTScratchBuffer:
|
||||
case lsgo.DTScratchBuffer:
|
||||
panic("Not implemented")
|
||||
|
||||
v := make([]byte, length)
|
||||
@ -423,6 +429,11 @@ func ReadLSBAttr(r io.ReadSeeker, name string, dt DataType, endianness binary.By
|
||||
return attr, err
|
||||
|
||||
default:
|
||||
return ReadAttribute(r, name, dt, uint(length), l)
|
||||
return lsgo.ReadAttribute(r, name, dt, uint(length), l)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
lsgo.RegisterFormat("lsb", Signature, Read)
|
||||
lsgo.RegisterFormat("lsb", PreBG3Signature, Read)
|
||||
}
|
@ -1,61 +1,24 @@
|
||||
package lsgo
|
||||
package lsf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.narnian.us/lordwelch/lsgo"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
var (
|
||||
LSFSignature = [4]byte{0x4C, 0x53, 0x4F, 0x46}
|
||||
Logger log.Logger = log.NewNopLogger()
|
||||
)
|
||||
const Signature = "LSOF"
|
||||
|
||||
// NewFilter allows filtering of l
|
||||
func NewFilter(f map[string][]string, l log.Logger) log.Logger {
|
||||
return filter{
|
||||
filter: f,
|
||||
next: l,
|
||||
}
|
||||
}
|
||||
|
||||
type filter struct {
|
||||
next log.Logger
|
||||
filter map[string][]string
|
||||
}
|
||||
|
||||
func (f filter) Log(keyvals ...interface{}) error {
|
||||
var allowed = true // allow everything
|
||||
for i := 0; i < len(keyvals)-1; i += 2 {
|
||||
if v, ok := keyvals[i].(string); ok { // key
|
||||
if fil, ok := f.filter[v]; ok { // key has a filter
|
||||
if v, ok = keyvals[i+1].(string); ok { // value is a string
|
||||
allowed = false // this key has a filter deny everything except what the filter allows
|
||||
for _, fi := range fil {
|
||||
if strings.Contains(v, fi) {
|
||||
allowed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if allowed {
|
||||
return f.next.Log(keyvals...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type LSFHeader struct {
|
||||
type Header struct {
|
||||
// LSOF file signature
|
||||
Signature [4]byte
|
||||
|
||||
// Version of the LSOF file D:OS EE is version 1/2, D:OS 2 is version 3
|
||||
Version FileVersion
|
||||
Version lsgo.FileVersion
|
||||
|
||||
// Possibly version number? (major, minor, rev, build)
|
||||
EngineVersion uint32
|
||||
@ -83,7 +46,6 @@ type LSFHeader struct {
|
||||
|
||||
// Compressed size of the raw value buffer
|
||||
ValuesSizeOnDisk uint32
|
||||
// summary
|
||||
|
||||
// Uses the same format as packages (see BinUtils.MakeCompressionFlags)
|
||||
CompressionFlags byte
|
||||
@ -96,166 +58,158 @@ type LSFHeader struct {
|
||||
Extended uint32
|
||||
}
|
||||
|
||||
func (lsfh *LSFHeader) Read(r io.ReadSeeker) error {
|
||||
func (h *Header) Read(r io.ReadSeeker) error {
|
||||
var (
|
||||
l log.Logger
|
||||
pos int64
|
||||
n int
|
||||
err error
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "header")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
n, err = r.Read(lsfh.Signature[:])
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "header")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
n, err = r.Read(h.Signature[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Signature", "read", n, "start position", pos, "value", string(lsfh.Signature[:]))
|
||||
l.Log("member", "Signature", "read", n, "start position", pos, "value", string(h.Signature[:]))
|
||||
pos += int64(n)
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.Version)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Version)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Version", "read", n, "start position", pos, "value", lsfh.Version)
|
||||
l.Log("member", "Version", "read", n, "start position", pos, "value", h.Version)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.EngineVersion)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.EngineVersion)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "EngineVersion", "read", n, "start position", pos, "value", fmt.Sprintf("%d.%d.%d.%d", (lsfh.EngineVersion&0xf0000000)>>28, (lsfh.EngineVersion&0xf000000)>>24, (lsfh.EngineVersion&0xff0000)>>16, (lsfh.EngineVersion&0xffff)))
|
||||
l.Log("member", "EngineVersion", "read", n, "start position", pos, "value", fmt.Sprintf("%d.%d.%d.%d", (h.EngineVersion&0xf0000000)>>28, (h.EngineVersion&0xf000000)>>24, (h.EngineVersion&0xff0000)>>16, (h.EngineVersion&0xffff)))
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.StringsUncompressedSize)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.StringsUncompressedSize)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "StringsUncompressedSize", "read", n, "start position", pos, "value", lsfh.StringsUncompressedSize)
|
||||
l.Log("member", "StringsUncompressedSize", "read", n, "start position", pos, "value", h.StringsUncompressedSize)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.StringsSizeOnDisk)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.StringsSizeOnDisk)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "StringsSizeOnDisk", "read", n, "start position", pos, "value", lsfh.StringsSizeOnDisk)
|
||||
l.Log("member", "StringsSizeOnDisk", "read", n, "start position", pos, "value", h.StringsSizeOnDisk)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.NodesUncompressedSize)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.NodesUncompressedSize)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "NodesUncompressedSize", "read", n, "start position", pos, "value", lsfh.NodesUncompressedSize)
|
||||
l.Log("member", "NodesUncompressedSize", "read", n, "start position", pos, "value", h.NodesUncompressedSize)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.NodesSizeOnDisk)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.NodesSizeOnDisk)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "NodesSizeOnDisk", "read", n, "start position", pos, "value", lsfh.NodesSizeOnDisk)
|
||||
l.Log("member", "NodesSizeOnDisk", "read", n, "start position", pos, "value", h.NodesSizeOnDisk)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.AttributesUncompressedSize)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.AttributesUncompressedSize)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "AttributesUncompressedSize", "read", n, "start position", pos, "value", lsfh.AttributesUncompressedSize)
|
||||
l.Log("member", "AttributesUncompressedSize", "read", n, "start position", pos, "value", h.AttributesUncompressedSize)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.AttributesSizeOnDisk)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.AttributesSizeOnDisk)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "AttributesSizeOnDisk", "read", n, "start position", pos, "value", lsfh.AttributesSizeOnDisk)
|
||||
l.Log("member", "AttributesSizeOnDisk", "read", n, "start position", pos, "value", h.AttributesSizeOnDisk)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.ValuesUncompressedSize)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.ValuesUncompressedSize)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "ValuesUncompressedSize", "read", n, "start position", pos, "value", lsfh.ValuesUncompressedSize)
|
||||
l.Log("member", "ValuesUncompressedSize", "read", n, "start position", pos, "value", h.ValuesUncompressedSize)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.ValuesSizeOnDisk)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.ValuesSizeOnDisk)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "ValuesSizeOnDisk", "read", n, "start position", pos, "value", lsfh.ValuesSizeOnDisk)
|
||||
l.Log("member", "ValuesSizeOnDisk", "read", n, "start position", pos, "value", h.ValuesSizeOnDisk)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.CompressionFlags)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.CompressionFlags)
|
||||
n = 1
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "CompressionFlags", "read", n, "start position", pos, "value", lsfh.CompressionFlags)
|
||||
l.Log("member", "CompressionFlags", "read", n, "start position", pos, "value", h.CompressionFlags)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.Unknown2)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Unknown2)
|
||||
n = 1
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Unknown2", "read", n, "start position", pos, "value", lsfh.Unknown2)
|
||||
l.Log("member", "Unknown2", "read", n, "start position", pos, "value", h.Unknown2)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.Unknown3)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Unknown3)
|
||||
n = 2
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Unknown3", "read", n, "start position", pos, "value", lsfh.Unknown3)
|
||||
l.Log("member", "Unknown3", "read", n, "start position", pos, "value", h.Unknown3)
|
||||
pos += int64(n)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &lsfh.Extended)
|
||||
err = binary.Read(r, binary.LittleEndian, &h.Extended)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Log("member", "Extended", "read", n, "start position", pos, "value", lsfh.Extended)
|
||||
l.Log("member", "Extended", "read", n, "start position", pos, "value", h.Extended)
|
||||
pos += int64(n)
|
||||
|
||||
if !lsfh.IsCompressed() {
|
||||
lsfh.NodesSizeOnDisk = lsfh.NodesUncompressedSize
|
||||
lsfh.AttributesSizeOnDisk = lsfh.AttributesUncompressedSize
|
||||
lsfh.StringsSizeOnDisk = lsfh.StringsUncompressedSize
|
||||
lsfh.ValuesSizeOnDisk = lsfh.ValuesUncompressedSize
|
||||
if !h.IsCompressed() {
|
||||
h.NodesSizeOnDisk = h.NodesUncompressedSize
|
||||
h.AttributesSizeOnDisk = h.AttributesUncompressedSize
|
||||
h.StringsSizeOnDisk = h.StringsUncompressedSize
|
||||
h.ValuesSizeOnDisk = h.ValuesUncompressedSize
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lsfh LSFHeader) IsCompressed() bool {
|
||||
return CompressionFlagsToMethod(lsfh.CompressionFlags) != CMNone && CompressionFlagsToMethod(lsfh.CompressionFlags) != CMInvalid
|
||||
func (h Header) IsCompressed() bool {
|
||||
return lsgo.CompressionFlagsToMethod(h.CompressionFlags) != lsgo.CMNone && lsgo.CompressionFlagsToMethod(h.CompressionFlags) != lsgo.CMInvalid
|
||||
}
|
||||
|
||||
type NodeEntry struct {
|
||||
Long bool
|
||||
|
||||
// summary
|
||||
|
||||
// (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain)
|
||||
NameHashTableIndex uint32
|
||||
|
||||
// summary
|
||||
|
||||
// (-1: node has no attributes)
|
||||
FirstAttributeIndex int32
|
||||
|
||||
// summary
|
||||
|
||||
// (-1: this node is a root region)
|
||||
ParentIndex int32
|
||||
|
||||
// summary
|
||||
|
||||
// (-1: this is the last node)
|
||||
NextSiblingIndex int32
|
||||
}
|
||||
@ -274,12 +228,11 @@ func (ne *NodeEntry) readShort(r io.ReadSeeker) error {
|
||||
err error
|
||||
n int
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "short node")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "short node")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
err = binary.Read(r, binary.LittleEndian, &ne.NameHashTableIndex)
|
||||
n = 4
|
||||
if err != nil {
|
||||
// logger.Println(err, "ne.NameHashTableIndex", ne.NameHashTableIndex)
|
||||
return err
|
||||
}
|
||||
l.Log("member", "NameHashTableIndex", "read", n, "start position", pos, "value", strconv.Itoa(ne.NameIndex())+" "+strconv.Itoa(ne.NameOffset()))
|
||||
@ -288,7 +241,6 @@ func (ne *NodeEntry) readShort(r io.ReadSeeker) error {
|
||||
err = binary.Read(r, binary.LittleEndian, &ne.FirstAttributeIndex)
|
||||
n = 4
|
||||
if err != nil {
|
||||
// logger.Println(err, "ne.FirstAttributeIndex", ne.FirstAttributeIndex)
|
||||
return err
|
||||
}
|
||||
l.Log("member", "NameHashTableIndex", "read", n, "start position", pos, "value", ne.FirstAttributeIndex)
|
||||
@ -297,7 +249,6 @@ func (ne *NodeEntry) readShort(r io.ReadSeeker) error {
|
||||
err = binary.Read(r, binary.LittleEndian, &ne.ParentIndex)
|
||||
n = 4
|
||||
if err != nil {
|
||||
// logger.Println(err, "ne.ParentIndex", ne.ParentIndex)
|
||||
return err
|
||||
}
|
||||
l.Log("member", "NameHashTableIndex", "read", n, "start position", pos, "value", ne.ParentIndex)
|
||||
@ -312,8 +263,8 @@ func (ne *NodeEntry) readLong(r io.ReadSeeker) error {
|
||||
err error
|
||||
n int
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "long node")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "long node")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
err = binary.Read(r, binary.LittleEndian, &ne.NameHashTableIndex)
|
||||
n = 4
|
||||
if err != nil {
|
||||
@ -359,7 +310,6 @@ func (ne NodeEntry) NameOffset() int {
|
||||
|
||||
// Processed node information for a node in the LSF file
|
||||
type NodeInfo struct {
|
||||
// summary
|
||||
|
||||
// (-1: this node is a root region)
|
||||
ParentIndex int
|
||||
@ -370,8 +320,6 @@ type NodeInfo struct {
|
||||
// Offset in hash chain
|
||||
NameOffset int
|
||||
|
||||
// summary
|
||||
|
||||
// (-1: node has no attributes)
|
||||
FirstAttributeIndex int
|
||||
}
|
||||
@ -379,23 +327,16 @@ type NodeInfo struct {
|
||||
// attribute extension in the LSF file
|
||||
type AttributeEntry struct {
|
||||
Long bool
|
||||
// summary
|
||||
|
||||
// (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain)
|
||||
NameHashTableIndex uint32
|
||||
|
||||
// summary
|
||||
|
||||
// 26-bit MSB: Length of this attribute
|
||||
TypeAndLength uint32
|
||||
|
||||
// summary
|
||||
|
||||
// Note: These indexes are assigned seemingly arbitrarily, and are not necessarily indices into the node list
|
||||
NodeIndex int32
|
||||
|
||||
// summary
|
||||
|
||||
// Note: These indexes are assigned seemingly arbitrarily, and are not necessarily indices into the node list
|
||||
NextAttributeIndex int32
|
||||
|
||||
@ -417,8 +358,8 @@ func (ae *AttributeEntry) readShort(r io.ReadSeeker) error {
|
||||
err error
|
||||
n int
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "short attribute")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "short attribute")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex)
|
||||
n = 4
|
||||
@ -454,8 +395,8 @@ func (ae *AttributeEntry) readLong(r io.ReadSeeker) error {
|
||||
err error
|
||||
n int
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "long attribute")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "long attribute")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &ae.NameHashTableIndex)
|
||||
n = 4
|
||||
@ -503,8 +444,8 @@ func (ae AttributeEntry) NameOffset() int {
|
||||
}
|
||||
|
||||
// Type of this attribute (see NodeAttribute.DataType)
|
||||
func (ae AttributeEntry) TypeID() DataType {
|
||||
return DataType(ae.TypeAndLength & 0x3f)
|
||||
func (ae AttributeEntry) TypeID() lsgo.DataType {
|
||||
return lsgo.DataType(ae.TypeAndLength & 0x3f)
|
||||
}
|
||||
|
||||
// Length of this attribute
|
||||
@ -522,14 +463,13 @@ type AttributeInfo struct {
|
||||
NameOffset int
|
||||
|
||||
// Type of this attribute (see NodeAttribute.DataType)
|
||||
TypeID DataType
|
||||
TypeID lsgo.DataType
|
||||
|
||||
// Length of this attribute
|
||||
Length uint
|
||||
|
||||
// Absolute position of attribute data in the values section
|
||||
DataOffset uint
|
||||
// summary
|
||||
|
||||
// (-1: this is the last attribute)
|
||||
NextAttributeIndex int
|
||||
@ -546,8 +486,8 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) {
|
||||
pos int64
|
||||
n int
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "names")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "names")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &numHashEntries)
|
||||
n = 4
|
||||
@ -559,15 +499,17 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) {
|
||||
|
||||
names = make([][]string, int(numHashEntries))
|
||||
for i := range names {
|
||||
|
||||
var numStrings uint16
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &numStrings)
|
||||
n = 4
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.Log("member", "numStrings", "read", n, "start position", pos, "value", numStrings)
|
||||
pos += int64(n)
|
||||
|
||||
var hash = make([]string, int(numStrings))
|
||||
hash := make([]string, int(numStrings))
|
||||
for x := range hash {
|
||||
var (
|
||||
nameLen uint16
|
||||
@ -591,16 +533,13 @@ func ReadNames(r io.ReadSeeker) ([][]string, error) {
|
||||
pos += int64(n)
|
||||
|
||||
hash[x] = string(name)
|
||||
|
||||
}
|
||||
names[i] = hash
|
||||
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func readNodeInfo(r io.ReadSeeker, longNodes bool) ([]NodeInfo, error) {
|
||||
// Console.WriteLine(" ----- DUMP OF NODE TABLE -----");
|
||||
var (
|
||||
nodes []NodeInfo
|
||||
err error
|
||||
@ -609,7 +548,6 @@ func readNodeInfo(r io.ReadSeeker, longNodes bool) ([]NodeInfo, error) {
|
||||
|
||||
for err == nil {
|
||||
var node NodeInfo
|
||||
// var pos = lsfr.Position;
|
||||
|
||||
item := &NodeEntry{Long: longNodes}
|
||||
err = item.Read(r)
|
||||
@ -618,11 +556,6 @@ func readNodeInfo(r io.ReadSeeker, longNodes bool) ([]NodeInfo, error) {
|
||||
node.NameIndex = item.NameIndex()
|
||||
node.NameOffset = item.NameOffset()
|
||||
node.ParentIndex = int(item.ParentIndex)
|
||||
// Console.WriteLine(String.Format(
|
||||
// "{0}: {1} @ {2:X} (parent {3}, firstAttribute {4})",
|
||||
// index, Names[node.NameIndex][node.NameOffset], pos, node.ParentIndex,
|
||||
// node.FirstAttributeIndex
|
||||
// ));
|
||||
|
||||
nodes = append(nodes, node)
|
||||
index++
|
||||
@ -650,7 +583,6 @@ func readAttributeInfo(r io.ReadSeeker, long bool) []AttributeInfo {
|
||||
break
|
||||
}
|
||||
|
||||
// pretty.Log( attribute)
|
||||
if long {
|
||||
dataOffset = uint(attribute.Offset)
|
||||
nextAttrIndex = int(attribute.NextAttributeIndex)
|
||||
@ -667,7 +599,6 @@ func readAttributeInfo(r io.ReadSeeker, long bool) []AttributeInfo {
|
||||
|
||||
if !long {
|
||||
// get index of previous attribute for node
|
||||
|
||||
if indexOfLastAttr, ok := prevAttributeRefs[int(attribute.NodeIndex)]; ok { // previous attribute exists for current node set the next attribute index for the previous node to this attribute
|
||||
attributes[indexOfLastAttr].NextAttributeIndex = index
|
||||
}
|
||||
@ -702,19 +633,9 @@ func readAttributeInfo(r io.ReadSeeker, long bool) []AttributeInfo {
|
||||
// );
|
||||
// Console.WriteLine(debug);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
type HeaderError struct {
|
||||
Expected []byte
|
||||
Got []byte
|
||||
}
|
||||
|
||||
func (he HeaderError) Error() string {
|
||||
return fmt.Sprintf("Invalid LSF signature; expected %v, got %v", he.Expected, he.Got)
|
||||
}
|
||||
|
||||
func ReadLSF(r io.ReadSeeker) (Resource, error) {
|
||||
func Read(r io.ReadSeeker) (lsgo.Resource, error) {
|
||||
var (
|
||||
err error
|
||||
|
||||
@ -728,38 +649,39 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
|
||||
attributeInfo []AttributeInfo
|
||||
|
||||
// Node instances
|
||||
nodeInstances []*Node
|
||||
nodeInstances []*lsgo.Node
|
||||
)
|
||||
var (
|
||||
l log.Logger
|
||||
pos, npos int64
|
||||
// n int
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "file")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "file")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
l.Log("member", "header", "start position", pos)
|
||||
|
||||
hdr := &LSFHeader{}
|
||||
hdr := &Header{}
|
||||
err = hdr.Read(r)
|
||||
if err != nil || (hdr.Signature != LSFSignature) {
|
||||
return Resource{}, HeaderError{LSFSignature[:], hdr.Signature[:]}
|
||||
if err != nil || (string(hdr.Signature[:]) != Signature) {
|
||||
return lsgo.Resource{}, lsgo.HeaderError{Expected: Signature, Got: hdr.Signature[:]}
|
||||
}
|
||||
|
||||
if hdr.Version < VerInitial || hdr.Version > MaxVersion {
|
||||
return Resource{}, fmt.Errorf("LSF version %v is not supported", hdr.Version)
|
||||
if hdr.Version < lsgo.VerInitial || hdr.Version > lsgo.MaxVersion {
|
||||
return lsgo.Resource{}, fmt.Errorf("LSF version %v is not supported", hdr.Version)
|
||||
}
|
||||
|
||||
isCompressed := CompressionFlagsToMethod(hdr.CompressionFlags) != CMNone && CompressionFlagsToMethod(hdr.CompressionFlags) != CMInvalid
|
||||
isCompressed := lsgo.CompressionFlagsToMethod(hdr.CompressionFlags) != lsgo.CMNone && lsgo.CompressionFlagsToMethod(hdr.CompressionFlags) != lsgo.CMInvalid
|
||||
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
l.Log("member", "LSF names", "start position", pos)
|
||||
if hdr.StringsSizeOnDisk > 0 || hdr.StringsUncompressedSize > 0 {
|
||||
var (
|
||||
uncompressed = LimitReadSeeker(r, int64(hdr.StringsSizeOnDisk))
|
||||
)
|
||||
uncompressed := lsgo.LimitReadSeeker(r, int64(hdr.StringsSizeOnDisk))
|
||||
|
||||
if isCompressed {
|
||||
uncompressed = Decompress(uncompressed, int(hdr.StringsUncompressedSize), hdr.CompressionFlags, false)
|
||||
uncompressed, 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))
|
||||
@ -768,13 +690,12 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
|
||||
// }
|
||||
|
||||
names, err = ReadNames(uncompressed)
|
||||
// pretty.Log(len(names), names)
|
||||
if err != nil && err != io.EOF {
|
||||
return Resource{}, err
|
||||
return lsgo.Resource{}, err
|
||||
}
|
||||
}
|
||||
|
||||
npos, err = r.Seek(0, io.SeekCurrent)
|
||||
npos, _ = r.Seek(0, io.SeekCurrent)
|
||||
l.Log("member", "LSF nodes", "start position", npos)
|
||||
if npos != pos+int64(hdr.StringsSizeOnDisk) {
|
||||
l.Log("member", "LSF nodes", "msg", "seeking to correct offset", "current", npos, "wanted", pos+int64(hdr.StringsSizeOnDisk))
|
||||
@ -783,11 +704,12 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
|
||||
pos = npos
|
||||
}
|
||||
if hdr.NodesSizeOnDisk > 0 || hdr.NodesUncompressedSize > 0 {
|
||||
var (
|
||||
uncompressed = LimitReadSeeker(r, int64(hdr.NodesSizeOnDisk))
|
||||
)
|
||||
uncompressed := lsgo.LimitReadSeeker(r, int64(hdr.NodesSizeOnDisk))
|
||||
if isCompressed {
|
||||
uncompressed = Decompress(uncompressed, int(hdr.NodesUncompressedSize), hdr.CompressionFlags, hdr.Version >= VerChunkedCompress)
|
||||
uncompressed, 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))
|
||||
@ -795,16 +717,14 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
|
||||
// nodesFile.Write(uncompressed, 0, uncompressed.Length);
|
||||
// }
|
||||
|
||||
longNodes := hdr.Version >= VerExtendedNodes && hdr.Extended == 1
|
||||
longNodes := hdr.Version >= lsgo.VerExtendedNodes && hdr.Extended == 1
|
||||
nodeInfo, err = readNodeInfo(uncompressed, longNodes)
|
||||
// pretty.Log(err, nodeInfo)
|
||||
// logger.Printf("region 1 name: %v", names[nodeInfo[0].NameIndex])
|
||||
if err != nil && err != io.EOF {
|
||||
return Resource{}, err
|
||||
return lsgo.Resource{}, err
|
||||
}
|
||||
}
|
||||
|
||||
npos, err = r.Seek(0, io.SeekCurrent)
|
||||
npos, _ = r.Seek(0, io.SeekCurrent)
|
||||
l.Log("member", "LSF attributes", "start position", npos)
|
||||
if npos != pos+int64(hdr.NodesSizeOnDisk) {
|
||||
l.Log("msg", "seeking to correct offset", "current", npos, "wanted", pos+int64(hdr.NodesSizeOnDisk))
|
||||
@ -813,11 +733,12 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
|
||||
pos = npos
|
||||
}
|
||||
if hdr.AttributesSizeOnDisk > 0 || hdr.AttributesUncompressedSize > 0 {
|
||||
var (
|
||||
uncompressed io.ReadSeeker = LimitReadSeeker(r, int64(hdr.AttributesSizeOnDisk))
|
||||
)
|
||||
var uncompressed io.ReadSeeker = lsgo.LimitReadSeeker(r, int64(hdr.AttributesSizeOnDisk))
|
||||
if isCompressed {
|
||||
uncompressed = Decompress(uncompressed, int(hdr.AttributesUncompressedSize), hdr.CompressionFlags, hdr.Version >= VerChunkedCompress)
|
||||
uncompressed, 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))
|
||||
@ -825,13 +746,11 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
|
||||
// attributesFile.Write(uncompressed, 0, uncompressed.Length);
|
||||
// }
|
||||
|
||||
longAttributes := hdr.Version >= VerExtendedNodes && hdr.Extended == 1
|
||||
longAttributes := hdr.Version >= lsgo.VerExtendedNodes && hdr.Extended == 1
|
||||
attributeInfo = readAttributeInfo(uncompressed, longAttributes)
|
||||
// logger.Printf("attribute 1 name: %v", names[attributeInfo[0].NameIndex])
|
||||
// pretty.Log(attributeInfo)
|
||||
}
|
||||
|
||||
npos, err = r.Seek(0, io.SeekCurrent)
|
||||
npos, _ = r.Seek(0, io.SeekCurrent)
|
||||
l.Log("member", "LSF values", "start position", npos)
|
||||
if npos != pos+int64(hdr.AttributesSizeOnDisk) {
|
||||
l.Log("msg", "seeking to correct offset", "current", npos, "wanted", pos+int64(hdr.AttributesSizeOnDisk))
|
||||
@ -839,18 +758,19 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
|
||||
} else {
|
||||
pos = npos
|
||||
}
|
||||
var (
|
||||
uncompressed io.ReadSeeker = LimitReadSeeker(r, int64(hdr.ValuesSizeOnDisk))
|
||||
)
|
||||
var uncompressed io.ReadSeeker = lsgo.LimitReadSeeker(r, int64(hdr.ValuesSizeOnDisk))
|
||||
if hdr.ValuesSizeOnDisk > 0 || hdr.ValuesUncompressedSize > 0 {
|
||||
if isCompressed {
|
||||
uncompressed = Decompress(r, int(hdr.ValuesUncompressedSize), hdr.CompressionFlags, hdr.Version >= VerChunkedCompress)
|
||||
uncompressed, 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{}
|
||||
valueStart, _ = uncompressed.Seek(0, io.SeekCurrent)
|
||||
nodeInstances, err = ReadRegions(uncompressed, names, nodeInfo, attributeInfo, hdr.Version, hdr.EngineVersion)
|
||||
res := lsgo.Resource{}
|
||||
valueStart, _ := uncompressed.Seek(0, io.SeekCurrent)
|
||||
nodeInstances, err = ReadRegions(uncompressed, valueStart, names, nodeInfo, attributeInfo, hdr.Version, hdr.EngineVersion)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@ -865,20 +785,14 @@ func ReadLSF(r io.ReadSeeker) (Resource, error) {
|
||||
res.Metadata.Revision = (hdr.EngineVersion & 0xff0000) >> 16
|
||||
res.Metadata.Build = (hdr.EngineVersion & 0xffff)
|
||||
|
||||
// pretty.Log(res)
|
||||
return res, nil
|
||||
|
||||
}
|
||||
|
||||
var valueStart int64
|
||||
|
||||
func ReadRegions(r io.ReadSeeker, names [][]string, nodeInfo []NodeInfo, attributeInfo []AttributeInfo, version FileVersion, engineVersion uint32) ([]*Node, error) {
|
||||
NodeInstances := make([]*Node, 0, len(nodeInfo))
|
||||
func ReadRegions(r io.ReadSeeker, valueStart int64, names [][]string, nodeInfo []NodeInfo, attributeInfo []AttributeInfo, version lsgo.FileVersion, engineVersion uint32) ([]*lsgo.Node, error) {
|
||||
NodeInstances := make([]*lsgo.Node, 0, len(nodeInfo))
|
||||
for _, nodeInfo := range nodeInfo {
|
||||
if nodeInfo.ParentIndex == -1 {
|
||||
region, err := ReadNode(r, nodeInfo, names, attributeInfo, version, engineVersion)
|
||||
|
||||
// pretty.Log(err, region)
|
||||
region, err := ReadNode(r, valueStart, nodeInfo, names, attributeInfo, version, engineVersion)
|
||||
|
||||
region.RegionName = region.Name
|
||||
NodeInstances = append(NodeInstances, ®ion)
|
||||
@ -887,9 +801,7 @@ func ReadRegions(r io.ReadSeeker, names [][]string, nodeInfo []NodeInfo, attribu
|
||||
return NodeInstances, err
|
||||
}
|
||||
} else {
|
||||
node, err := ReadNode(r, nodeInfo, names, attributeInfo, version, engineVersion)
|
||||
|
||||
// pretty.Log(err, node)
|
||||
node, err := ReadNode(r, valueStart, nodeInfo, names, attributeInfo, version, engineVersion)
|
||||
|
||||
node.Parent = NodeInstances[nodeInfo.ParentIndex]
|
||||
NodeInstances = append(NodeInstances, &node)
|
||||
@ -903,17 +815,17 @@ func ReadRegions(r io.ReadSeeker, names [][]string, nodeInfo []NodeInfo, attribu
|
||||
return NodeInstances, nil
|
||||
}
|
||||
|
||||
func ReadNode(r io.ReadSeeker, ni NodeInfo, names [][]string, attributeInfo []AttributeInfo, version FileVersion, engineVersion uint32) (Node, error) {
|
||||
func ReadNode(r io.ReadSeeker, valueStart int64, ni NodeInfo, names [][]string, attributeInfo []AttributeInfo, version lsgo.FileVersion, engineVersion uint32) (lsgo.Node, error) {
|
||||
var (
|
||||
node = Node{}
|
||||
node = lsgo.Node{}
|
||||
index = ni.FirstAttributeIndex
|
||||
err error
|
||||
|
||||
l log.Logger
|
||||
pos int64
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "node")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "node")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
node.Name = names[ni.NameIndex][ni.NameOffset]
|
||||
|
||||
@ -922,7 +834,7 @@ func ReadNode(r io.ReadSeeker, ni NodeInfo, names [][]string, attributeInfo []At
|
||||
for index != -1 {
|
||||
var (
|
||||
attribute = attributeInfo[index]
|
||||
v NodeAttribute
|
||||
v lsgo.NodeAttribute
|
||||
)
|
||||
|
||||
if valueStart+int64(attribute.DataOffset) != pos {
|
||||
@ -937,18 +849,16 @@ func ReadNode(r io.ReadSeeker, ni NodeInfo, names [][]string, attributeInfo []At
|
||||
return node, err
|
||||
}
|
||||
index = attribute.NextAttributeIndex
|
||||
|
||||
// Console.WriteLine(String.Format(" {0:X}: {1} ({2})", attribute.DataOffset, names[attribute.NameIndex][attribute.NameOffset], value));
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, version FileVersion, engineVersion uint32) (NodeAttribute, error) {
|
||||
func ReadLSFAttribute(r io.ReadSeeker, name string, dt lsgo.DataType, length uint, version lsgo.FileVersion, engineVersion uint32) (lsgo.NodeAttribute, error) {
|
||||
// LSF and LSB serialize the buffer types differently, so specialized
|
||||
// code is added to the LSB and LSf serializers, and the common code is
|
||||
// available in BinUtils.ReadAttribute()
|
||||
var (
|
||||
attr = NodeAttribute{
|
||||
attr = lsgo.NodeAttribute{
|
||||
Type: dt,
|
||||
Name: name,
|
||||
}
|
||||
@ -957,13 +867,13 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
|
||||
l log.Logger
|
||||
pos int64
|
||||
)
|
||||
l = log.With(Logger, "component", "LS converter", "file type", "lsf", "part", "attribute")
|
||||
pos, err = r.Seek(0, io.SeekCurrent)
|
||||
l = log.With(lsgo.Logger, "component", "LS converter", "file type", "lsf", "part", "attribute")
|
||||
pos, _ = r.Seek(0, io.SeekCurrent)
|
||||
|
||||
switch dt {
|
||||
case DTString, DTPath, DTFixedString, DTLSString, DTWString, DTLSWString:
|
||||
case lsgo.DTString, lsgo.DTPath, lsgo.DTFixedString, lsgo.DTLSString, lsgo.DTWString, lsgo.DTLSWString:
|
||||
var v string
|
||||
v, err = ReadCString(r, int(length))
|
||||
v, err = lsgo.ReadCString(r, int(length))
|
||||
attr.Value = v
|
||||
|
||||
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
|
||||
@ -971,9 +881,9 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
|
||||
|
||||
return attr, err
|
||||
|
||||
case DTTranslatedString:
|
||||
var v TranslatedString
|
||||
v, err = ReadTranslatedString(r, version, engineVersion)
|
||||
case lsgo.DTTranslatedString:
|
||||
var v lsgo.TranslatedString
|
||||
v, err = lsgo.ReadTranslatedString(r, version, engineVersion)
|
||||
attr.Value = v
|
||||
|
||||
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
|
||||
@ -981,9 +891,9 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
|
||||
|
||||
return attr, err
|
||||
|
||||
case DTTranslatedFSString:
|
||||
var v TranslatedFSString
|
||||
v, err = ReadTranslatedFSString(r, version)
|
||||
case lsgo.DTTranslatedFSString:
|
||||
var v lsgo.TranslatedFSString
|
||||
v, err = lsgo.ReadTranslatedFSString(r, version)
|
||||
attr.Value = v
|
||||
|
||||
l.Log("member", name, "read", length, "start position", pos, "value", attr.Value)
|
||||
@ -991,7 +901,7 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
|
||||
|
||||
return attr, err
|
||||
|
||||
case DTScratchBuffer:
|
||||
case lsgo.DTScratchBuffer:
|
||||
|
||||
v := make([]byte, length)
|
||||
_, err = r.Read(v)
|
||||
@ -1003,147 +913,10 @@ func ReadLSFAttribute(r io.ReadSeeker, name string, dt DataType, length uint, ve
|
||||
return attr, err
|
||||
|
||||
default:
|
||||
return ReadAttribute(r, name, dt, length, l)
|
||||
return lsgo.ReadAttribute(r, name, dt, length, l)
|
||||
}
|
||||
}
|
||||
|
||||
func ReadTranslatedString(r io.ReadSeeker, version FileVersion, engineVersion uint32) (TranslatedString, error) {
|
||||
var (
|
||||
str TranslatedString
|
||||
err error
|
||||
)
|
||||
|
||||
if version >= VerBG3 || engineVersion == 0x4000001d {
|
||||
// logger.Println("decoding bg3 data")
|
||||
var version uint16
|
||||
err = binary.Read(r, binary.LittleEndian, &version)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Version = version
|
||||
err = binary.Read(r, binary.LittleEndian, &version)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
if version == 0 {
|
||||
str.Value, err = ReadCString(r, int(str.Version))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Version = 0
|
||||
} else {
|
||||
_, err = r.Seek(-2, io.SeekCurrent)
|
||||
}
|
||||
} else {
|
||||
str.Version = 0
|
||||
|
||||
var (
|
||||
vlength int32
|
||||
v []byte
|
||||
// n int
|
||||
)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &vlength)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
v = make([]byte, vlength)
|
||||
_, err = r.Read(v)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Value = string(v)
|
||||
}
|
||||
|
||||
var handleLength int32
|
||||
err = binary.Read(r, binary.LittleEndian, &handleLength)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Handle, err = ReadCString(r, int(handleLength))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
// logger.Printf("handle %s; %v", str.Handle, err)
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func ReadTranslatedFSString(r io.ReadSeeker, version FileVersion) (TranslatedFSString, error) {
|
||||
var (
|
||||
str = TranslatedFSString{}
|
||||
err error
|
||||
)
|
||||
|
||||
if version >= VerBG3 {
|
||||
var version uint16
|
||||
err = binary.Read(r, binary.LittleEndian, &version)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Version = version
|
||||
} else {
|
||||
str.Version = 0
|
||||
|
||||
var (
|
||||
length int32
|
||||
)
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &length)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Value, err = ReadCString(r, int(length))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
}
|
||||
|
||||
var handleLength int32
|
||||
err = binary.Read(r, binary.LittleEndian, &handleLength)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Handle, err = ReadCString(r, int(handleLength))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
|
||||
var arguments int32
|
||||
err = binary.Read(r, binary.LittleEndian, &arguments)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
str.Arguments = make([]TranslatedFSStringArgument, 0, arguments)
|
||||
for i := 0; i < int(arguments); i++ {
|
||||
arg := TranslatedFSStringArgument{}
|
||||
|
||||
var argKeyLength int32
|
||||
err = binary.Read(r, binary.LittleEndian, &argKeyLength)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
arg.Key, err = ReadCString(r, int(argKeyLength))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
|
||||
arg.String, err = ReadTranslatedFSString(r, version)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
|
||||
var argValueLength int32
|
||||
err = binary.Read(r, binary.LittleEndian, &argValueLength)
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
arg.Value, err = ReadCString(r, int(argValueLength))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
|
||||
str.Arguments = append(str.Arguments, arg)
|
||||
}
|
||||
|
||||
return str, nil
|
||||
func init() {
|
||||
lsgo.RegisterFormat("lsf", Signature, Read)
|
||||
}
|
44
lsgo.go
Normal file
44
lsgo.go
Normal 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
37
lsgo.sublime-project
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
resource.go
10
resource.go
@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
type LSMetadata struct {
|
||||
//public const uint CurrentMajorVersion = 33;
|
||||
// public const uint CurrentMajorVersion = 33;
|
||||
|
||||
Timestamp uint64 `xml:"-"`
|
||||
Major uint32 `xml:"major,attr"`
|
||||
@ -15,18 +15,12 @@ type LSMetadata struct {
|
||||
Build uint32 `xml:"build,attr"`
|
||||
}
|
||||
|
||||
type format struct {
|
||||
name, magic string
|
||||
read func(io.Reader) (Resource, error)
|
||||
}
|
||||
|
||||
type Resource struct {
|
||||
Metadata LSMetadata `xml:"version"`
|
||||
Regions []*Node `xml:"region"`
|
||||
}
|
||||
|
||||
func (r *Resource) Read(io.Reader) {
|
||||
|
||||
}
|
||||
|
||||
// public Resource()
|
||||
@ -43,7 +37,7 @@ type Node struct {
|
||||
RegionName string `xml:"-"`
|
||||
}
|
||||
|
||||
func (n Node) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
func (n *Node) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
R := xml.Name{
|
||||
Local: "region",
|
||||
}
|
||||
|
0
lz4/LICENSE → third_party/lz4/LICENSE
vendored
0
lz4/LICENSE → third_party/lz4/LICENSE
vendored
0
lz4/go.mod → third_party/lz4/go.mod
vendored
0
lz4/go.mod → third_party/lz4/go.mod
vendored
0
lz4/go.sum → third_party/lz4/go.sum
vendored
0
lz4/go.sum → third_party/lz4/go.sum
vendored
0
lz4/lz4.go → third_party/lz4/lz4.go
vendored
0
lz4/lz4.go → third_party/lz4/lz4.go
vendored
0
lz4/state.go → third_party/lz4/state.go
vendored
0
lz4/state.go → third_party/lz4/state.go
vendored
15
third_party/peinfo-go/.gitignore
vendored
Normal file
15
third_party/peinfo-go/.gitignore
vendored
Normal 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
49
third_party/peinfo-go/LICENSE
vendored
Normal 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
62
third_party/peinfo-go/README.md
vendored
Normal 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
7
third_party/peinfo-go/go.mod
vendored
Normal 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
10
third_party/peinfo-go/go.sum
vendored
Normal 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
17
third_party/peinfo-go/peinfo/consts.go
vendored
Normal 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",
|
||||
}
|
31
third_party/peinfo-go/peinfo/initialize.go
vendored
Normal file
31
third_party/peinfo-go/peinfo/initialize.go
vendored
Normal 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
124
third_party/peinfo-go/peinfo/peinfo.go
vendored
Normal 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
90
third_party/peinfo-go/peinfo/structs.go
vendored
Normal 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
267
third_party/peinfo-go/peinfo/verinfo.go
vendored
Normal 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
|
||||
}
|
Reference in New Issue
Block a user