Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b5d007c865 | ||
|
59f8bdcc54 | ||
|
97fcfd145c | ||
|
32e0180f91 | ||
|
9442d5993d | ||
|
2e6c882ca9 | ||
|
6f9ebf4906 | ||
|
8f099fcd27 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
|||||||
lsconvert
|
/lsconvert
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
18
binutils.go
18
binutils.go
@ -82,18 +82,18 @@ func MakeCompressionFlags(method CompressionMethod, level CompressionLevel) int
|
|||||||
return flags | int(level)
|
return flags | int(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Decompress(compressed io.Reader, uncompressedSize int, compressionFlags byte, chunked bool) io.ReadSeeker {
|
func Decompress(compressed io.Reader, uncompressedSize int, compressionFlags byte, chunked bool) (io.ReadSeeker, error) {
|
||||||
switch CompressionMethod(compressionFlags & 0x0f) {
|
switch CompressionMethod(compressionFlags & 0x0f) {
|
||||||
case CMNone:
|
case CMNone:
|
||||||
if v, ok := compressed.(io.ReadSeeker); ok {
|
if v, ok := compressed.(io.ReadSeeker); ok {
|
||||||
return v
|
return v, nil
|
||||||
}
|
}
|
||||||
panic(errors.New("compressed must be an io.ReadSeeker if there is no compression"))
|
return nil, errors.New("compressed must be an io.ReadSeeker if there is no compression")
|
||||||
|
|
||||||
case CMZlib:
|
case CMZlib:
|
||||||
zr, _ := zlib.NewReader(compressed)
|
zr, _ := zlib.NewReader(compressed)
|
||||||
v, _ := ioutil.ReadAll(zr)
|
v, _ := ioutil.ReadAll(zr)
|
||||||
return bytes.NewReader(v)
|
return bytes.NewReader(v), nil
|
||||||
|
|
||||||
case CMLZ4:
|
case CMLZ4:
|
||||||
if chunked {
|
if chunked {
|
||||||
@ -101,21 +101,21 @@ func Decompress(compressed io.Reader, uncompressedSize int, compressionFlags byt
|
|||||||
p := make([]byte, uncompressedSize)
|
p := make([]byte, uncompressedSize)
|
||||||
_, err := zr.Read(p)
|
_, err := zr.Read(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
return bytes.NewReader(p)
|
return bytes.NewReader(p), nil
|
||||||
}
|
}
|
||||||
src, _ := ioutil.ReadAll(compressed)
|
src, _ := ioutil.ReadAll(compressed)
|
||||||
dst := make([]byte, uncompressedSize*2)
|
dst := make([]byte, uncompressedSize*2)
|
||||||
_, err := lz4.UncompressBlock(src, dst)
|
_, err := lz4.UncompressBlock(src, dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes.NewReader(dst)
|
return bytes.NewReader(dst), nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("no decompressor found for this format: %v", compressionFlags))
|
return nil, fmt.Errorf("no decompressor found for this format: %v", compressionFlags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,223 +1,43 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/xml"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
write = flag.Bool("w", false, "replace the file with xml data")
|
|
||||||
printXML = flag.Bool("x", false, "print xml to stdout")
|
|
||||||
printResource = flag.Bool("R", false, "print the resource struct to stderr")
|
|
||||||
recurse = flag.Bool("r", false, "recurse into directories")
|
|
||||||
logging = flag.Bool("l", false, "enable logging to stderr")
|
|
||||||
parts = flag.String("p", "", "parts to filter logging for, comma separated")
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.Parse()
|
|
||||||
if *logging {
|
|
||||||
lsgo.Logger = lsgo.NewFilter(map[string][]string{
|
|
||||||
"part": strings.Split(*parts, ","),
|
|
||||||
}, log.NewLogfmtLogger(os.Stderr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
for _, v := range flag.Args() {
|
// flag.Parse()
|
||||||
fi, err := os.Stat(v)
|
arg := ""
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
arg = os.Args[1]
|
||||||
|
}
|
||||||
|
switch arg {
|
||||||
|
case "convert":
|
||||||
|
err := convert(os.Args[1:]...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
switch {
|
case "init":
|
||||||
case !fi.IsDir():
|
p := "."
|
||||||
err = openLSF(v)
|
if len(os.Args) > 2 {
|
||||||
if err != nil && !errors.As(err, &lsgo.HeaderError{}) {
|
p = os.Args[2]
|
||||||
|
}
|
||||||
|
repo, err := openRepository(p)
|
||||||
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
err = repo.Init()
|
||||||
case *recurse:
|
|
||||||
_ = filepath.Walk(v, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
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)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(os.Stderr, "lsconvert: %s: Is a directory\n", v)
|
err := convert(os.Args[1:]...)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func openLSF(filename string) error {
|
|
||||||
var (
|
|
||||||
l *lsgo.Resource
|
|
||||||
err error
|
|
||||||
n string
|
|
||||||
f interface {
|
|
||||||
io.Writer
|
|
||||||
io.StringWriter
|
|
||||||
}
|
|
||||||
)
|
|
||||||
l, err = readLSF(filename)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("reading LSF file %s failed: %w", filename, err)
|
|
||||||
}
|
|
||||||
if *printResource {
|
|
||||||
pretty.Log(l)
|
|
||||||
}
|
|
||||||
if *printXML || *write {
|
|
||||||
n, err = marshalXML(l)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("creating XML from LSF file %s failed: %w", filename, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *write {
|
|
||||||
f, err = os.OpenFile(filename, os.O_TRUNC|os.O_RDWR, 0o666)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("writing XML from LSF file %s failed: %w", filename, err)
|
|
||||||
}
|
|
||||||
} else if *printXML {
|
|
||||||
f = os.Stdout
|
|
||||||
}
|
|
||||||
|
|
||||||
err = writeXML(f, n)
|
|
||||||
fmt.Fprint(f, "\n")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("writing XML from LSF file %s failed: %w", filename, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readLSF(filename string) (*lsgo.Resource, error) {
|
|
||||||
var (
|
|
||||||
l lsgo.Resource
|
|
||||||
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, 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
|
|
||||||
}
|
|
||||||
|
7
go.mod
7
go.mod
@ -4,10 +4,17 @@ go 1.15
|
|||||||
|
|
||||||
replace github.com/pierrec/lz4/v4 v4.1.3 => ./third_party/lz4
|
replace github.com/pierrec/lz4/v4 v4.1.3 => ./third_party/lz4
|
||||||
|
|
||||||
|
replace github.com/jdferrell3/peinfo-go v0.0.0-20191128021257-cff0d8506fb1 => ./third_party/peinfo-go
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/go-git/go-git/v5 v5.2.0
|
||||||
github.com/go-kit/kit v0.10.0
|
github.com/go-kit/kit v0.10.0
|
||||||
github.com/google/uuid v1.1.4
|
github.com/google/uuid v1.1.4
|
||||||
|
github.com/hashicorp/go-multierror v1.1.0
|
||||||
|
github.com/jdferrell3/peinfo-go v0.0.0-20191128021257-cff0d8506fb1
|
||||||
github.com/kr/pretty v0.2.1
|
github.com/kr/pretty v0.2.1
|
||||||
github.com/pierrec/lz4/v4 v4.1.3
|
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
|
gonum.org/v1/gonum v0.8.2
|
||||||
)
|
)
|
||||||
|
68
go.sum
68
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/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||||
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||||
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
@ -37,7 +43,9 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7
|
|||||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
@ -45,15 +53,29 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
|
|||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||||
|
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||||
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||||
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||||
|
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
|
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||||
|
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||||
|
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
|
||||||
|
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||||
|
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
|
||||||
|
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||||
|
github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI=
|
||||||
|
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
|
github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
|
||||||
@ -82,6 +104,7 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
|||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
@ -98,11 +121,14 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
|||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
@ -118,8 +144,13 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
|
|||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||||
|
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||||
|
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||||
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
@ -128,6 +159,8 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
|||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||||
|
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
|
||||||
|
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
@ -136,8 +169,9 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
|||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||||
@ -149,6 +183,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
|||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
@ -166,6 +202,8 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
|
|||||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
@ -189,8 +227,10 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0
|
|||||||
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
@ -216,6 +256,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
|||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||||
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
@ -232,10 +274,13 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
|
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||||
|
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||||
@ -251,10 +296,15 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@ -284,6 +334,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
|||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -301,13 +353,21 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=
|
||||||
|
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@ -355,16 +415,22 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
|||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
236
gog/changelog.go
Normal file
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}
|
||||||
|
}
|
20
lsf/lsf.go
20
lsf/lsf.go
@ -678,7 +678,10 @@ func Read(r io.ReadSeeker) (lsgo.Resource, error) {
|
|||||||
uncompressed := lsgo.LimitReadSeeker(r, int64(hdr.StringsSizeOnDisk))
|
uncompressed := lsgo.LimitReadSeeker(r, int64(hdr.StringsSizeOnDisk))
|
||||||
|
|
||||||
if isCompressed {
|
if isCompressed {
|
||||||
uncompressed = lsgo.Decompress(uncompressed, int(hdr.StringsUncompressedSize), hdr.CompressionFlags, false)
|
uncompressed, err = lsgo.Decompress(uncompressed, int(hdr.StringsUncompressedSize), hdr.CompressionFlags, false)
|
||||||
|
if err != nil {
|
||||||
|
return lsgo.Resource{}, fmt.Errorf("decompressing LSF name failed: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// using (var nodesFile = new FileStream("names.bin", FileMode.Create, FileAccess.Write))
|
// using (var nodesFile = new FileStream("names.bin", FileMode.Create, FileAccess.Write))
|
||||||
@ -703,7 +706,10 @@ func Read(r io.ReadSeeker) (lsgo.Resource, error) {
|
|||||||
if hdr.NodesSizeOnDisk > 0 || hdr.NodesUncompressedSize > 0 {
|
if hdr.NodesSizeOnDisk > 0 || hdr.NodesUncompressedSize > 0 {
|
||||||
uncompressed := lsgo.LimitReadSeeker(r, int64(hdr.NodesSizeOnDisk))
|
uncompressed := lsgo.LimitReadSeeker(r, int64(hdr.NodesSizeOnDisk))
|
||||||
if isCompressed {
|
if isCompressed {
|
||||||
uncompressed = lsgo.Decompress(uncompressed, int(hdr.NodesUncompressedSize), hdr.CompressionFlags, hdr.Version >= lsgo.VerChunkedCompress)
|
uncompressed, err = lsgo.Decompress(uncompressed, int(hdr.NodesUncompressedSize), hdr.CompressionFlags, hdr.Version >= lsgo.VerChunkedCompress)
|
||||||
|
if err != nil {
|
||||||
|
return lsgo.Resource{}, fmt.Errorf("decompressing LSF nodes failed: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// using (var nodesFile = new FileStream("nodes.bin", FileMode.Create, FileAccess.Write))
|
// using (var nodesFile = new FileStream("nodes.bin", FileMode.Create, FileAccess.Write))
|
||||||
@ -729,7 +735,10 @@ func Read(r io.ReadSeeker) (lsgo.Resource, error) {
|
|||||||
if hdr.AttributesSizeOnDisk > 0 || hdr.AttributesUncompressedSize > 0 {
|
if hdr.AttributesSizeOnDisk > 0 || hdr.AttributesUncompressedSize > 0 {
|
||||||
var uncompressed io.ReadSeeker = lsgo.LimitReadSeeker(r, int64(hdr.AttributesSizeOnDisk))
|
var uncompressed io.ReadSeeker = lsgo.LimitReadSeeker(r, int64(hdr.AttributesSizeOnDisk))
|
||||||
if isCompressed {
|
if isCompressed {
|
||||||
uncompressed = lsgo.Decompress(uncompressed, int(hdr.AttributesUncompressedSize), hdr.CompressionFlags, hdr.Version >= lsgo.VerChunkedCompress)
|
uncompressed, err = lsgo.Decompress(uncompressed, int(hdr.AttributesUncompressedSize), hdr.CompressionFlags, hdr.Version >= lsgo.VerChunkedCompress)
|
||||||
|
if err != nil {
|
||||||
|
return lsgo.Resource{}, fmt.Errorf("decompressing LSF attributes failed: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// using (var attributesFile = new FileStream("attributes.bin", FileMode.Create, FileAccess.Write))
|
// using (var attributesFile = new FileStream("attributes.bin", FileMode.Create, FileAccess.Write))
|
||||||
@ -752,7 +761,10 @@ func Read(r io.ReadSeeker) (lsgo.Resource, error) {
|
|||||||
var uncompressed io.ReadSeeker = lsgo.LimitReadSeeker(r, int64(hdr.ValuesSizeOnDisk))
|
var uncompressed io.ReadSeeker = lsgo.LimitReadSeeker(r, int64(hdr.ValuesSizeOnDisk))
|
||||||
if hdr.ValuesSizeOnDisk > 0 || hdr.ValuesUncompressedSize > 0 {
|
if hdr.ValuesSizeOnDisk > 0 || hdr.ValuesUncompressedSize > 0 {
|
||||||
if isCompressed {
|
if isCompressed {
|
||||||
uncompressed = lsgo.Decompress(r, int(hdr.ValuesUncompressedSize), hdr.CompressionFlags, hdr.Version >= lsgo.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user