package main import ( "encoding/csv" "fmt" "os" "path/filepath" "strings" ) type App struct { ConfigDirPath string } type Extractor func(string, string) error type Game struct { ID string Mods map[string]Mod HasUpdated bool } func (game *Game) InstallMod(extractor Extractor, archivePath string) error { var gamePath, pathError = game.Path() if pathError != nil { return pathError } var baseName = filepath.Base(archivePath) var modName = strings.TrimSuffix(baseName, filepath.Ext(baseName)) if extractError := extractor(archivePath, filepath.Join( gamePath, "mods", modName)); extractError != nil { return extractError } game.Mods[modName] = Mod{ IsEnabled: false, } game.HasUpdated = true return nil } type Mod struct { IsEnabled bool } func WithGame(gameName string, action func(*Game) error) error { var supportedGames = []string{"fallout4", "falloutnv", "skyrim"} for i := range supportedGames { var supportedGame = supportedGames[i] if gameName == supportedGame { var game = Game{ ID: supportedGame, Mods: make(map[string]Mod), HasUpdated: false, } var gamePath, pathError = game.Path() if pathError != nil { return pathError } var manifestPath = filepath.Join(gamePath, "mods.csv") // Load manifest from disk. { var manifestFile, openError = os.Open(manifestPath) if openError == nil { defer manifestFile.Close() var manifestReader = csv.NewReader(manifestFile) var recordValues, recordError = manifestReader.Read() for recordValues != nil { if recordError != nil { return recordError } if len(recordValues) < 2 { return fmt.Errorf("could not read mod manifest data - may be corrupt") } var status = recordValues[0] var name = recordValues[1] game.Mods[name] = Mod{ IsEnabled: status == "*", } recordValues, recordError = manifestReader.Read() } } } if actionError := action(&game); actionError != nil { return actionError } // Save manifest back to disk. if game.HasUpdated { var manifestFile, openError = os.OpenFile(manifestPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if openError != nil { return openError } defer manifestFile.Close() var manifestWriter = csv.NewWriter(manifestFile) for name, mod := range game.Mods { var status = "" if mod.IsEnabled { status = "*" } manifestWriter.Write([]string{status, name}) } manifestWriter.Flush() } return nil } } return fmt.Errorf("%s: game not supported", gameName) } func (game Game) Path() (string, error) { var configDirPath, configError = os.UserConfigDir() if configError != nil { return "", configError } return filepath.Join(configDirPath, "modman", game.ID), nil } func (game *Game) RemoveMods(modNames []string) ([]string, error) { var gamePath, pathError = game.Path() if pathError != nil { return nil, pathError } var processed = 0 for i := range modNames { var modName = modNames[i] if removeError := os.RemoveAll(filepath.Join(gamePath, modName)); removeError != nil { return nil, removeError } delete(game.Mods, modName) game.HasUpdated = true processed += 1 } return modNames[0:processed], nil } func (game *Game) RenameMod(modName string, newName string) error { var gamePath, pathError = game.Path() if pathError != nil { return pathError } if _, exists := game.Mods[modName]; !(exists) { return fmt.Errorf("no mod with that name exists") } if _, exists := game.Mods[newName]; exists { return fmt.Errorf("a mod with the new name already exists") } if renameError := os.Rename(filepath.Join(gamePath, modName), filepath.Join(gamePath, newName)); renameError != nil { return renameError } game.Mods[newName] = game.Mods[modName] delete(game.Mods, modName) game.HasUpdated = true return nil }