aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitch Riedstra <mitch@riedstra.us>2020-11-20 22:46:45 -0500
committerMitch Riedstra <mitch@riedstra.us>2020-11-20 22:46:45 -0500
commit891447f0e40a6d1a9637b3333cffed8eb2543c51 (patch)
treefb748b0c6af1670a2b73e5d581f206d0fe26e003
parent971cc396e1d01f53f65a86278fd0ac4490565335 (diff)
downloadsteam-export-891447f0e40a6d1a9637b3333cffed8eb2543c51.tar.gz
steam-export-891447f0e40a6d1a9637b3333cffed8eb2543c51.tar.xz
Works after reorganization.
-rw-r--r--archive/archive.go5
-rw-r--r--cmd/cli/main.go133
-rw-r--r--cmd/cli/unix.go (renamed from steam/unix.go)2
-rw-r--r--cmd/cli/windows.go (renamed from steam/windows.go)2
-rw-r--r--steam/archive.go55
-rw-r--r--steam/package.go112
-rw-r--r--steam/steam.go62
7 files changed, 193 insertions, 178 deletions
diff --git a/archive/archive.go b/archive/archive.go
index 639a864..313c062 100644
--- a/archive/archive.go
+++ b/archive/archive.go
@@ -47,7 +47,7 @@ func (a *Archive) Tar(compressionType string) error {
defer twriter.Close()
for _, v := range a.Input {
- if err := filepath.Walk(v, tarWalkfn(twriter)); err != nil {
+ if err := filepath.Walk(v, TarWalkfn(twriter)); err != nil {
return err
}
}
@@ -56,8 +56,7 @@ func (a *Archive) Tar(compressionType string) error {
}
-func tarWalkfn(writer *tar.Writer) filepath.WalkFunc {
- // This is an interesting trick to get around scoping issues
+func TarWalkfn(writer *tar.Writer) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
diff --git a/cmd/cli/main.go b/cmd/cli/main.go
index 3292d30..989592a 100644
--- a/cmd/cli/main.go
+++ b/cmd/cli/main.go
@@ -6,16 +6,9 @@ import (
"fmt"
"os"
- "riedstra.dev/mitch/steam-export/config"
"riedstra.dev/mitch/steam-export/steam"
)
-var (
- errorHandling flag.ErrorHandling = flag.ExitOnError
-
- steamLib *steam.Library = &steam.Library{}
-)
-
func parseArgs(args []string) error {
if len(args) < 2 {
return errors.New("Not enough arguments")
@@ -30,8 +23,6 @@ func parseArgs(args []string) error {
return extractGame(aa)
case "delete":
return deleteGame(aa)
- case "edit-config":
- return editConfig(aa)
default:
printHelp()
}
@@ -49,34 +40,21 @@ Subcommands:
extract
delete
server
- edit-config
Type in a subcommand -h or -help for more information
`)
}
-func editConfig(args []string) error {
- fl := flag.NewFlagSet("edit-config", errorHandling)
- e := fl.String("e", "", "Editor to invoke")
- if err := fl.Parse(args); err != nil {
- return err
- }
- return config.EditDefaultConfig(*e)
-}
-
func listGames(args []string) error {
- fl := flag.NewFlagSet("list", errorHandling)
- lib := fl.String("l", steam.DefaultLib,
+ fl := flag.NewFlagSet("list", flag.ExitOnError)
+ lib := fl.String("l", DefaultLib,
"Path to library in question. All the way to the 'steamapps' folder")
- l := steam.DefaultLib
fl.Parse(args)
- if fl.Parsed() {
- l = string(*lib)
- }
- if err := steamLib.ProcessLibrary(l); err != nil {
+ steamLib := &steam.Library{}
+ if err := steamLib.ProcessLibrary(*lib); err != nil {
return err
}
@@ -86,111 +64,90 @@ func listGames(args []string) error {
}
func packageGame(args []string) error {
- fl := flag.NewFlagSet("package", errorHandling)
- lib := fl.String("l", steam.DefaultLib,
+ fl := flag.NewFlagSet("package", flag.ExitOnError)
+ libPth := fl.String("l", DefaultLib,
"Path to library in question. All the way to the 'steamapps' folder")
- fileName := fl.String("f", "export",
- "Name of the archive file to be created. Please do not include the file extension")
- game := fl.Int("g", -1,
- "Index of the game to be exported. Please see `list` for the index")
- compress := fl.String("z", "gz",
- "Compression type. Default 'gz' '' is no compression")
-
- var g int
- l := steam.DefaultLib
+ fileName := fl.String("f", "", "Name of archive to be created")
+ game := fl.String("g", "", "Name of the game to be exported.")
+
fl.Parse(args)
- if fl.Parsed() {
- l = string(*lib)
- g = int(*game)
+
+ if *fileName == "" {
+ return errors.New("You need to specify a file name")
}
- if err := steamLib.ProcessLibrary(l); err != nil {
+ lib := &steam.Library{}
+ if err := lib.ProcessLibrary(*libPth); err != nil {
return err
}
- if len(steamLib.Games) < g || g == -1 {
- return errors.New("Cannot find game for index provided or no index provided")
+ G, ok := lib.Games[*game]
+ if !ok {
+ return errors.New("Game does not exist")
}
- if *fileName == "export" {
- fileName = &steamLib.Games[g]
+ f, err := os.OpenFile(*fileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0664)
+ if err != nil {
+ return err
}
+ defer f.Close()
- switch *compress {
- case "gz":
- return steamLib.PackageGameToFile(g, *fileName+".tar.gz", *compress)
- default:
- return steamLib.PackageGameToFile(g, *fileName+".tar", *compress)
+ err = G.Package(f)
+ if err != nil {
+ return err
}
+ return nil
}
func extractGame(args []string) error {
- fl := flag.NewFlagSet("extract", errorHandling)
- lib := fl.String("l", steam.DefaultLib,
+ fl := flag.NewFlagSet("extract", flag.ExitOnError)
+ libPath := fl.String("l", DefaultLib,
"Path to library in question. All the way to the 'steamapps' folder")
fileName := fl.String("f", "",
- "Name of the archive file to be extracted. Please include the file extension")
- compress := fl.String("z", "gz",
- "Compression type. Default 'gz' '' is no compression")
+ "Name of the archive file to be extracted. Include the file extension")
- l := steam.DefaultLib
fl.Parse(args)
- if fl.Parsed() {
- l = string(*lib)
- }
- if err := steamLib.ProcessLibrary(l); err != nil {
+ lib := &steam.Library{}
+
+ if err := lib.ProcessLibrary(*libPath); err != nil {
return err
}
- if *fileName == "" {
- return errors.New("No filename provided")
+ fh, err := os.Open(*fileName)
+ if err != nil {
+ return err
}
- return steamLib.ExtractGameFromFile(*fileName, *compress)
-
+ return lib.Extract(fh)
}
func deleteGame(args []string) error {
- fl := flag.NewFlagSet("delete", errorHandling)
- lib := fl.String("l", steam.DefaultLib,
+ fl := flag.NewFlagSet("delete", flag.ExitOnError)
+ libPath := fl.String("l", DefaultLib,
"Path to library in question. All the way to the 'steamapps' folder")
- game := fl.Int("g", -1,
- "Index of the game to be deleted. Please see `list` for the index")
+ game := fl.String("g", "", "Name of the game to be deleted.")
- var g int
- l := steam.DefaultLib
fl.Parse(args)
- if fl.Parsed() {
- l = string(*lib)
- g = int(*game)
- }
- if err := steamLib.ProcessLibrary(l); err != nil {
+ lib := &steam.Library{}
+
+ if err := lib.ProcessLibrary(*libPath); err != nil {
return err
}
- if len(steamLib.Games) < g || g == -1 {
- return errors.New("Cannot find game for index provided or no index provided")
+ G, ok := lib.Games[*game]
+ if !ok {
+ return errors.New("Game does not exist")
}
- return steamLib.DeleteGame(g)
+ return G.Delete()
}
func main() {
- config, err := config.LoadConfig()
- if err != nil {
- fmt.Println(err)
- } else {
- if config.DefaultRepository != "" {
- steam.DefaultLib = config.DefaultRepository
- }
- }
-
if err := parseArgs(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
printHelp()
}
}
-
diff --git a/steam/unix.go b/cmd/cli/unix.go
index caa4b43..6b1b1ae 100644
--- a/steam/unix.go
+++ b/cmd/cli/unix.go
@@ -1,6 +1,6 @@
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
-package steam
+package main
import (
"os"
diff --git a/steam/windows.go b/cmd/cli/windows.go
index 17fe62c..2effa08 100644
--- a/steam/windows.go
+++ b/cmd/cli/windows.go
@@ -1,5 +1,5 @@
// +build windows
-package steam
+package main
var DefaultLib string = `C:\Program Files (x86)\Steam\steamapps`
diff --git a/steam/archive.go b/steam/archive.go
new file mode 100644
index 0000000..d997051
--- /dev/null
+++ b/steam/archive.go
@@ -0,0 +1,55 @@
+package steam
+
+import (
+ "archive/tar"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+func TarWalkfn(writer *tar.Writer) filepath.WalkFunc {
+ return func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
+ if info.IsDir() {
+ return nil
+ }
+
+ f, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ // Convert Windows paths to Unix paths
+ path = strings.Replace(path, "\\", "/", -1)
+
+ // TODO; See if tar.FileInfoheader() could be used instead
+ // without the pathing issues I encountered
+ h := &tar.Header{
+ Name: path,
+ Size: info.Size(),
+ // I don't like it... but it helps with platform compatibility
+ Mode: 0664,
+ ModTime: info.ModTime(),
+ }
+
+ err = writer.WriteHeader(h)
+ if err != nil {
+ return err
+ }
+
+ _, err = io.Copy(writer, f)
+ if err != nil {
+ // TODO: Figure out how to add more useful information to
+ // These errors
+ // fmt.Fprintln(os.Stderr, f.Name())
+ return err
+ }
+
+ return nil
+ }
+}
diff --git a/steam/package.go b/steam/package.go
index a73a6bc..9000cba 100644
--- a/steam/package.go
+++ b/steam/package.go
@@ -1,94 +1,96 @@
package steam
import (
- // "fmt"
- "riedstra.dev/mitch/steam-export/archive"
+ "archive/tar"
+ "io"
"os"
"path/filepath"
+ "strings"
)
-func (l *Library) PackageGameToFile(index int, file, compress string) error {
- g := l.Games[index]
-
- working_dir, err := os.Getwd()
- if err != nil {
+// Package writes the package, returning bytes written and an error if any
+func (g *Game) Package(wr io.Writer) error {
+ if err := os.Chdir(g.LibraryPath); err != nil {
return err
}
- // output := working_dir + "/" + g + ".tar"
- output, err := filepath.Abs(file)
+ acf, err := FindACF(g.LibraryPath, g.Name)
if err != nil {
return err
}
- os.Chdir(l.Folder)
- acf, err := l.FindACF(g)
- if err != nil {
- return err
- }
- input := []string{"common/" + g, acf}
- a := archive.Archive{Output: output, Input: input}
- err = a.Tar(compress)
- if err != nil {
- return err
- }
+ twriter := tar.NewWriter(wr)
- os.Chdir(working_dir)
+ for _, pth := range []string{"common/" + g.Name, acf} {
+ if err := filepath.Walk(pth, TarWalkfn(twriter)); err != nil {
+ return err
+ }
+ }
return nil
}
-func (l *Library) ExtractGameFromFile(f, compress string) error {
- working_dir, err := os.Getwd()
- if err != nil {
+func (l *Library) Extract(r io.Reader) error {
+ if err := os.Chdir(l.Folder); err != nil {
return err
}
- f, err = filepath.Abs(f)
- if err != nil {
- return err
- }
+ treader := tar.NewReader(r)
- if err = os.Chdir(l.Folder); err != nil {
- return err
- }
- u := &archive.Unarchive{
- Input: f,
- }
- if err := u.UnTar(compress); err != nil {
- return err
- }
+ for {
+ hdr, err := treader.Next()
+ if err == io.EOF {
+ // We've reached the end! Whoee
+ break
+ }
+ if err != nil {
+ return err
+ }
- if err = os.Chdir(working_dir); err != nil {
- return err
- }
- return nil
-}
+ // Fix windows slashes...
+ fileName := strings.Replace(hdr.Name, "\\", "/", -1)
-func (l *Library) DeleteGame(i int) error {
- g := l.Games[i]
+ info := hdr.FileInfo()
+ if info.IsDir() {
+ // I don't like hard-coded permissions but it
+ // it helps with overall platform compatibility
+ if err = os.MkdirAll(fileName, 0775); err != nil {
+ return err
+ }
+ continue
+ }
+
+ if err = os.MkdirAll(filepath.Dir(fileName), 0775); err != nil {
+ return err
+ }
+
+ // Create a file handle to work with
+ f, err := os.OpenFile(fileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0664)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ if _, err := io.Copy(f, treader); err != nil {
+ return err
+ }
- working_dir, err := os.Getwd()
- if err != nil {
- return err
}
- if err = os.Chdir(l.Folder); err != nil {
+ return nil
+}
+
+func (g *Game) Delete() error {
+ if err := os.Chdir(g.LibraryPath); err != nil {
return err
}
- acf, err := l.FindACF(g)
+ acf, err := FindACF(g.LibraryPath, g.Name)
if err != nil {
return err
}
- // fmt.Fprintf(os.Stderr, "Removing %q %q\n", acf, "common/"+g)
if err := os.Remove(acf); err != nil {
return err
}
- if err := os.RemoveAll("common/" + g); err != nil {
- return err
- }
-
- if err = os.Chdir(working_dir); err != nil {
+ if err := os.RemoveAll("common/" + g.Name); err != nil {
return err
}
diff --git a/steam/steam.go b/steam/steam.go
index ea27e5a..7071a20 100644
--- a/steam/steam.go
+++ b/steam/steam.go
@@ -5,19 +5,21 @@ package steam
import (
"fmt"
"io/ioutil"
-
"errors"
-
"bufio"
"os"
"path/filepath"
"strings"
- // "log"
)
type Library struct {
Folder string
- Games []string
+ Games map[string]Game
+}
+
+type Game struct {
+ Name string
+ LibraryPath string
}
func ProcessMultipleLibraries(r []string) ([]*Library, error) {
@@ -35,30 +37,33 @@ func ProcessMultipleLibraries(r []string) ([]*Library, error) {
// Populate the "Folder" and "Games" fields based on the provided directory
func (s *Library) ProcessLibrary(r string) error {
- if hasCommon(r) {
- dirs, err := ioutil.ReadDir(r + "/common")
- if err != nil {
- return err
- }
- s.Folder = r
- for _, f := range dirs {
- if f.IsDir() {
- s.Games = append(s.Games, f.Name())
+ if !hasCommon(r) {
+ return errors.New(fmt.Sprintf("No common directory in: %s", r))
+ }
+
+ s.Games = make(map[string]Game)
+
+ dirs, err := ioutil.ReadDir(r + "/common")
+ if err != nil {
+ return err
+ }
+ s.Folder = r
+ for _, f := range dirs {
+ if f.IsDir() {
+ s.Games[f.Name()] = Game{
+ Name: f.Name(),
+ LibraryPath: r,
}
+ // s.Games = append(s.Games, f.Name())
}
- } else {
- return errors.New(fmt.Sprintf("No common directory in: %s", r))
}
+
return nil
}
// Find the ACF files related to this video game
-func (l *Library) FindACF(g string) (string, error) {
- working_dir, err := os.Getwd()
- if err != nil {
- return "", err
- }
- if err = os.Chdir(l.Folder); err != nil {
+func FindACF(libraryPath, game string) (string, error) {
+ if err := os.Chdir(libraryPath); err != nil {
return "", err
}
files, err := filepath.Glob("*.acf")
@@ -84,8 +89,7 @@ func (l *Library) FindACF(g string) (string, error) {
scanner := bufio.NewScanner(f)
for scanner.Scan() {
// Finally check and see if the file has the video game name
- if strings.Contains(scanner.Text(), g) {
- os.Chdir(working_dir)
+ if strings.Contains(scanner.Text(), game) {
return fn, nil
// fmt.Printf("%s/%s:%d: %s\n", root, path, i, scanner.Text())
}
@@ -93,18 +97,16 @@ func (l *Library) FindACF(g string) (string, error) {
}
str := "Couldn't find ACF file related to Game: %s"
- return "", errors.New(fmt.Sprintf(str, g))
+ return "", errors.New(fmt.Sprintf(str, game))
}
// This is automatically called to print out the contents of the struct
// when things like fmt.Println are used
func (s *Library) String() (str string) {
- str = fmt.Sprintln("\n----")
- str = str + fmt.Sprintln(s.Folder)
- str = str + "\n----\n"
- for k, v := range s.Games {
- str = str + fmt.Sprintf("%d: %s\n", k, v)
- //str = str + fmt.Sprintln(v)
+ str = fmt.Sprintf("Library: %s\n", s.Folder)
+ str = str + "----\n"
+ for _, v := range s.Games {
+ str = str + fmt.Sprintf("%s\n", v.Name)
}
return
}