// Package steam is designed to be a rather simplistic library to help pull // information from a local steam library and run basic operations like // archiving, restore and deleting games package steam import ( "bufio" "fmt" "io/ioutil" "os" "path/filepath" "regexp" "strings" ) // Library is used to represent the steam library, the Games map is populated // by NewLibrary when called or when ProcessLibrary is called directly type Library struct { Folder string Games map[string]Game } // Game represents an actual game in the steam Library, allowing you to perform // a delete, package, and such. type Game struct { Name string LibraryPath string Size int64 } var slugregexp = regexp.MustCompile(`[^-0-9A-Za-z_:.]`) // Slug returns a safer version of the name with spaces and other chars // transformed into dashes for use in HTML element ids and such. func (g Game) Slug() string { return slugregexp.ReplaceAllString(g.Name, "-") } // NewLibrary returns a pointer to a processed library and an error // if any func NewLibrary(path string) (*Library, error) { l := &Library{} err := l.ProcessLibrary(path) if err != nil { return nil, err } return l, err } // NewLibraryMust is the same as NewLibrary but calls panic if there // is any error func NewLibraryMust(path string) *Library { l, err := NewLibrary(path) if err != nil { panic(err) } return l } // ProcessLibrary Populates the "Folder" and "Games" fields based on the // provided directory. func (s *Library) ProcessLibrary(r string) error { if !hasCommon(r) { return fmt.Errorf("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() { g := &Game{ Name: f.Name(), LibraryPath: r, } g.setSizeInfo() s.Games[f.Name()] = *g } } return nil } // FindACF will return the filename of the ACF file for a given `game` func FindACF(libraryPath, game string) (string, error) { files, err := filepath.Glob(filepath.Join(libraryPath, "*.acf")) if err != nil { return "", err } for _, fn := range files { info, err := os.Lstat(fn) if err != nil { return "", err } // We don't want it if it's a directory if info.IsDir() { continue } // Open up the file f, err := os.Open(fn) if err != nil { return "", err } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { if strings.Contains(scanner.Text(), game) { return fn, nil } } } return "", fmt.Errorf("Couldn't find ACF file related to Game: %s", game) } func (s *Library) String() (str string) { 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 } func hasCommon(d string) bool { dirs, err := ioutil.ReadDir(d) if err != nil { return false } for _, f := range dirs { if f.Name() == "common" && f.IsDir() { return true } } return false }