aboutsummaryrefslogtreecommitdiff
path: root/steam/package.go
diff options
context:
space:
mode:
Diffstat (limited to 'steam/package.go')
-rw-r--r--steam/package.go181
1 files changed, 58 insertions, 123 deletions
diff --git a/steam/package.go b/steam/package.go
index df6f1e8..5b0c618 100644
--- a/steam/package.go
+++ b/steam/package.go
@@ -2,10 +2,11 @@ package steam
import (
"archive/tar"
+ "errors"
+ "fmt"
"io"
- "os"
"path/filepath"
- "strings"
+ "time"
)
const (
@@ -13,177 +14,111 @@ const (
defaultFileMode = 0644
)
-// Package writes the package to wr, returning errors if any
-func (l *Library) Package(game string, wr io.WriteCloser) error {
- g, ok := l.games[game]
- if !ok {
+func (l *Library) Package(game string, wr io.Writer) error {
+ if _, ok := l.Games()[game]; !ok {
return E_GameDoesNotExist
}
+ g := l.Games()[game]
j := newJob("package", g)
defer j.done()
l.status.addJob(j)
- acf, err := FindACF(l.folder, g.Name)
- if err != nil {
- j.addError(err)
- return err
- }
+ j.setSize(g.GetSizeBytes())
- twriter := tar.NewWriter(wr)
-
- paths := []string{
- filepath.Join(l.folder, "common", g.Name),
- acf,
- }
- for _, pth := range paths {
- err := filepath.Walk(pth, tarWalkfn(twriter, l.folder))
+ // Invert the writer so we can break up the copy and get progress
+ // information in here
+ rdr, pwrtr := io.Pipe()
+ go func() {
+ err := l.packagePrimitive(j, g, pwrtr)
if err != nil {
j.addError(err)
- return err
}
- }
-
- err = twriter.Flush()
- if err != nil {
- j.addError(err)
- return err
- }
-
- err = twriter.Close()
- if err != nil {
- j.addError(err)
- return err
- }
-
- err = wr.Close()
- if err != nil {
- j.addError(err)
- }
- return err
-}
-
-// Extract will read a tarball from the io.Reader and install the game into
-// the current library path. This offers no visibility into the progress,
-// as it does not update the job status on the progress, though it will
-// populate errors.
-//
-// Most callers will want to use ExtractHTTP or ExtractFile instead
-func (l *Library) Extract(r io.Reader) (*Game, error) {
- g := &Game{LibraryPath: l.folder}
- j := newJob("extract", g)
- defer j.done()
-
- l.status.addJob(j)
-
- return l.extractPrimitive(j, g, r)
-}
-
-func (l *Library) extractPrimitive(j *Job, g *Game, r io.Reader) (*Game, error) {
- treader := tar.NewReader(r)
+ }()
+ var total int64
for {
- hdr, err := treader.Next()
+ n, err := io.CopyN(wr, rdr, updateEveryNBytes)
+
if err == io.EOF {
- // We've reached the end! Whoee
break
}
if err != nil {
j.addError(err)
- return nil, err
+ return err
}
- fileName := filepath.ToSlash(hdr.Name)
+ total += n
+ j.setTransferred(total)
- if g.Name == "" {
- s := strings.Split(fileName, "/")
- if len(s) >= 2 {
- g.Name = s[1]
- }
- }
+ elapsedSeconds := float64(time.Since(*j.StartTime()).Seconds())
+
+ rate := float64(total) / elapsedSeconds
- fileName = filepath.Join(l.folder, fileName)
+ fmt.Println("rate in bytes/second: ", formatBytes(int64(rate)))
- info := hdr.FileInfo()
- if info.IsDir() {
- // I don't like hard-coded permissions but it
- // it helps with overall platform compatibility
- err = os.MkdirAll(fileName, defaultDirectoryMode)
- if err != nil {
- j.addError(err)
- return nil, err
- }
+ estSize := j.GetSize()
+ if estSize == nil {
+ j.addError(errors.New("Expected an estimated size, got nil"))
continue
}
- err = os.MkdirAll(filepath.Dir(fileName), defaultDirectoryMode)
- if err != nil {
- j.addError(err)
- return nil, err
- }
+ remainingBytes := float64(*estSize - total)
+ fmt.Println("remaining bytes: ", formatBytes(int64(remainingBytes)))
- // Create a file handle to work with
- f, err := os.OpenFile(fileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY,
- defaultFileMode)
- if err != nil {
- j.addError(err)
- return nil, err
- }
- if _, err := io.Copy(f, treader); err != nil {
- j.addError(err)
- f.Close()
- return nil, err
- }
- f.Close()
+ seconds := (remainingBytes / rate)
- }
+ duration := time.Duration(seconds * 1000 * 1000 * 1000)
+ fmt.Println("Raw duration: ", duration)
- err := g.SetSizeInfo()
- if err != nil {
- j.addError(err)
- return nil, err
+ j.setETA(duration)
}
- l.m.Lock()
- l.games[g.Name] = g
- l.m.Unlock()
-
- return g, nil
+ return nil
}
-// Delete removes all of the game files and the ACF
-func (l *Library) Delete(game string) error {
- g, ok := l.games[game]
- if !ok {
- return E_GameDoesNotExist
+// Package writes the package to wr, returning errors if any
+func (l *Library) packagePrimitive(j *Job, g *Game, wr io.WriteCloser) error {
+
+ acf, err := FindACF(l.folder, g.Name)
+ if err != nil {
+ j.addError(err)
+ return err
}
- j := newJob("delete", g)
- defer j.done()
+ twriter := tar.NewWriter(wr)
- l.status.addJob(j)
+ paths := []string{
+ filepath.Join(l.folder, "common", g.Name),
+ acf,
+ }
+ for _, pth := range paths {
+ err := filepath.Walk(pth, tarWalkfn(twriter, l.folder))
+ if err != nil {
+ j.addError(err)
+ return err
+ }
+ }
- acf, err := FindACF(l.folder, game)
+ err = twriter.Flush()
if err != nil {
j.addError(err)
return err
}
- if err := os.Remove(acf); err != nil {
+
+ err = twriter.Close()
+ if err != nil {
j.addError(err)
return err
}
- err = os.RemoveAll(filepath.Join(l.folder, "common", g.Name))
+ err = wr.Close()
if err != nil {
j.addError(err)
return err
}
- l.m.Lock()
- delete(l.games, game)
- l.m.Unlock()
-
- return nil
+ return err
}