Complete code audit
This commit is contained in:
		
							parent
							
								
									cde4b75c5d
								
							
						
					
					
						commit
						d35eb6016d
					
				
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							@ -4,5 +4,5 @@ go 1.19
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
	sauce.pizzawednes.day/kayomn/ini-gusher v0.0.0-20221220232638-8a897dbd24aa // indirect
 | 
			
		||||
	sauce.pizzawednes.day/kayomn/ini-gusher v0.0.0-20221223145344-d68ddf116418 // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							@ -1,5 +1,5 @@
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
sauce.pizzawednes.day/kayomn/ini-gusher v0.0.0-20221220232638-8a897dbd24aa h1:Co1oVW0IMEIwk3tlQ2dJEFzNSd4r6tkPb2/6mxjnsOo=
 | 
			
		||||
sauce.pizzawednes.day/kayomn/ini-gusher v0.0.0-20221220232638-8a897dbd24aa/go.mod h1:lniG+VCTpfcWAKKudVYLrS5NIpRx90H3mQklQNn+eK0=
 | 
			
		||||
sauce.pizzawednes.day/kayomn/ini-gusher v0.0.0-20221223145344-d68ddf116418 h1:lAFE8yL+87+JGZROd2Wc3PG2BUWyiKe0xOWTNyVEiAU=
 | 
			
		||||
sauce.pizzawednes.day/kayomn/ini-gusher v0.0.0-20221223145344-d68ddf116418/go.mod h1:lniG+VCTpfcWAKKudVYLrS5NIpRx90H3mQklQNn+eK0=
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										143
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								main.go
									
									
									
									
									
								
							@ -32,34 +32,18 @@ var commands = []Command{
 | 
			
		||||
					requiredArguments[0], requiredArguments[1])
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var game, gameOpenError = OpenGame(providedArguments[0])
 | 
			
		||||
 | 
			
		||||
			if gameOpenError != nil {
 | 
			
		||||
				return "", gameOpenError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if closeError := game.Close(); closeError != nil {
 | 
			
		||||
					panic(closeError)
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
 | 
			
		||||
			var archivePaths = providedArguments[1:]
 | 
			
		||||
 | 
			
		||||
			if game, openGameError := LoadGame(providedArguments[0]); openGameError == nil {
 | 
			
		||||
				if installError := game.InstallMods(archivePaths); installError != nil {
 | 
			
		||||
					return "", installError
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if len(archivePaths) > 1 {
 | 
			
		||||
				for _, archivePath := range archivePaths {
 | 
			
		||||
					if installError := game.InstallMod(archivePath); installError != nil {
 | 
			
		||||
						return "", installError
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return "mods installed", nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if installError := game.InstallMod(archivePaths[0]); installError != nil {
 | 
			
		||||
				return "", installError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return "mod installed", nil
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
@ -99,23 +83,23 @@ var commands = []Command{
 | 
			
		||||
					requiredArguments[0], requiredArguments[1])
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var game, gameOpenError = OpenGame(providedArguments[0])
 | 
			
		||||
			var modNames = providedArguments[1:]
 | 
			
		||||
 | 
			
		||||
			if gameOpenError != nil {
 | 
			
		||||
				return "", gameOpenError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if closeError := game.Close(); closeError != nil {
 | 
			
		||||
					panic(closeError)
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
 | 
			
		||||
			if removeModsError := game.RemoveMods(providedArguments[1:]); removeModsError != nil {
 | 
			
		||||
				return "", removeModsError
 | 
			
		||||
			if game, openGameError := LoadGame(providedArguments[0]); openGameError == nil {
 | 
			
		||||
				for _, name := range modNames {
 | 
			
		||||
					if removeError := game.RemoveMod(name); removeError != nil {
 | 
			
		||||
						return "", removeError
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				return "", openGameError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if len(modNames) > 1 {
 | 
			
		||||
				return "removed mods", nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return "removed mod", nil
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@ -131,23 +115,13 @@ var commands = []Command{
 | 
			
		||||
					requiredArguments[0], requiredArguments[1], requiredArguments[2])
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var game, gameOpenError = OpenGame(providedArguments[0])
 | 
			
		||||
 | 
			
		||||
			if gameOpenError != nil {
 | 
			
		||||
				return "", gameOpenError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if closeError := game.Close(); closeError != nil {
 | 
			
		||||
					panic(closeError)
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
 | 
			
		||||
			if renameError := game.RenameMod(
 | 
			
		||||
				providedArguments[1], providedArguments[2]); renameError != nil {
 | 
			
		||||
 | 
			
		||||
			if game, openGameError := LoadGame(providedArguments[0]); openGameError == nil {
 | 
			
		||||
				if renameError := game.RenameMod(providedArguments[1], providedArguments[2]); renameError != nil {
 | 
			
		||||
					return "", renameError
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				return "", openGameError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return "renamed", nil
 | 
			
		||||
		},
 | 
			
		||||
@ -165,30 +139,21 @@ var commands = []Command{
 | 
			
		||||
					requiredArguments[0], requiredArguments[1])
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var game, gameOpenError = OpenGame(providedArguments[0])
 | 
			
		||||
 | 
			
		||||
			if gameOpenError != nil {
 | 
			
		||||
				return "", gameOpenError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if closeError := game.Close(); closeError != nil {
 | 
			
		||||
					panic(closeError)
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
 | 
			
		||||
			var format = providedArguments[1]
 | 
			
		||||
			var formatter, formatterExists = formatters[format]
 | 
			
		||||
 | 
			
		||||
			if !(formatterExists) {
 | 
			
		||||
				return "", fmt.Errorf("unsupported format: `%s`", format)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var manifestBuilder = strings.Builder{}
 | 
			
		||||
 | 
			
		||||
			if game, openGameError := LoadGame(providedArguments[0]); openGameError == nil {
 | 
			
		||||
				var format = providedArguments[1]
 | 
			
		||||
 | 
			
		||||
				if formatter, exists := formatters[format]; exists {
 | 
			
		||||
					if formatError := formatter(&manifestBuilder, game.Mods); formatError != nil {
 | 
			
		||||
						return "", formatError
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					return "", fmt.Errorf("unsupported format: `%s`", format)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				return "", openGameError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return manifestBuilder.String(), nil
 | 
			
		||||
		},
 | 
			
		||||
@ -206,26 +171,12 @@ var commands = []Command{
 | 
			
		||||
					requiredArguments[0], requiredArguments[1])
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var game, gameOpenError = OpenGame(providedArguments[0])
 | 
			
		||||
 | 
			
		||||
			if gameOpenError != nil {
 | 
			
		||||
				return "", gameOpenError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if closeError := game.Close(); closeError != nil {
 | 
			
		||||
					panic(closeError)
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
 | 
			
		||||
			if cleanError := game.CleanDeployedMods(); cleanError != nil {
 | 
			
		||||
				return "", cleanError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, modName := range providedArguments[1:] {
 | 
			
		||||
				if deployError := game.DeployMod(modName); deployError != nil {
 | 
			
		||||
			if game, openGameError := LoadGame(providedArguments[0]); openGameError != nil {
 | 
			
		||||
				if deployError := game.Deploy(providedArguments[1:]); deployError != nil {
 | 
			
		||||
					return "", deployError
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				return "", openGameError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return "deployed", nil
 | 
			
		||||
@ -243,20 +194,12 @@ var commands = []Command{
 | 
			
		||||
				return "", fmt.Errorf("expected %s", requiredArguments[0])
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var game, gameOpenError = OpenGame(arguments[0])
 | 
			
		||||
 | 
			
		||||
			if gameOpenError != nil {
 | 
			
		||||
				return "", gameOpenError
 | 
			
		||||
			if game, openGameError := LoadGame(arguments[0]); openGameError == nil {
 | 
			
		||||
				if cleanError := game.Clean(); cleanError != nil {
 | 
			
		||||
					return "", cleanError
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if closeError := game.Close(); closeError != nil {
 | 
			
		||||
					panic(closeError)
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
 | 
			
		||||
			if cleanDeployedModsError := game.CleanDeployedMods(); cleanDeployedModsError != nil {
 | 
			
		||||
				return "", cleanDeployedModsError
 | 
			
		||||
			} else {
 | 
			
		||||
				return "", openGameError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return "cleaned", nil
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										550
									
								
								manager.go
									
									
									
									
									
								
							
							
						
						
									
										550
									
								
								manager.go
									
									
									
									
									
								
							@ -13,29 +13,23 @@ import (
 | 
			
		||||
	"sauce.pizzawednes.day/kayomn/ini-gusher"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (game *Game) CleanDeployedMods() error {
 | 
			
		||||
func (game *Game) Clean() error {
 | 
			
		||||
	// Clean up currently deployed files first.
 | 
			
		||||
	for _, filePath := range game.DeployedFilePaths {
 | 
			
		||||
		var gameFilePath = filepath.Join(game.Path, filePath)
 | 
			
		||||
		if removeError := os.Remove(filepath.Join(
 | 
			
		||||
			game.Path, filePath)); (removeError != nil) && (!(os.IsNotExist(removeError))) {
 | 
			
		||||
 | 
			
		||||
		if removeError := os.Remove(gameFilePath); removeError != nil {
 | 
			
		||||
			if !(os.IsNotExist(removeError)) {
 | 
			
		||||
			return removeError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	game.DeployedFilePaths = game.DeployedFilePaths[:0]
 | 
			
		||||
 | 
			
		||||
	var overwriteDirPath = filepath.Join(cachePath(), game.Name, "overwritten")
 | 
			
		||||
 | 
			
		||||
	// Then restore all files overwritten by previously deployment.
 | 
			
		||||
	for _, filePath := range game.OverwrittenFilePaths {
 | 
			
		||||
		var backupDirPath, backupDirPathError = game.cachePath("overwritten")
 | 
			
		||||
 | 
			
		||||
		if backupDirPathError != nil {
 | 
			
		||||
			return backupDirPathError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if renameError := os.Rename(filepath.Join(backupDirPath, filePath),
 | 
			
		||||
		if renameError := os.Rename(filepath.Join(overwriteDirPath, filePath),
 | 
			
		||||
			filepath.Join(game.Path, filePath)); renameError != nil {
 | 
			
		||||
 | 
			
		||||
			return renameError
 | 
			
		||||
@ -44,120 +38,32 @@ func (game *Game) CleanDeployedMods() error {
 | 
			
		||||
 | 
			
		||||
	game.OverwrittenFilePaths = game.OverwrittenFilePaths[:0]
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
	return game.saveDeployment()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (game *Game) Close() error {
 | 
			
		||||
	// Write deployed files to disk.
 | 
			
		||||
	var deployedListPath, deployedListPathError = game.cachePath("deployed.txt")
 | 
			
		||||
 | 
			
		||||
	if deployedListPathError != nil {
 | 
			
		||||
		return deployedListPathError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var deployedListFile, deployedListCreateError = os.Create(deployedListPath)
 | 
			
		||||
 | 
			
		||||
	if deployedListCreateError != nil {
 | 
			
		||||
		return deployedListCreateError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if syncError := deployedListFile.Sync(); syncError != nil {
 | 
			
		||||
			panic(syncError)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if closeError := deployedListFile.Close(); closeError != nil {
 | 
			
		||||
			panic(closeError)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	var deployedListWriter = bufio.NewWriter(deployedListFile)
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if flushError := deployedListWriter.Flush(); flushError != nil {
 | 
			
		||||
			panic(flushError)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	for _, filePath := range game.DeployedFilePaths {
 | 
			
		||||
		if _, writeError := deployedListWriter.WriteString(filePath); writeError != nil {
 | 
			
		||||
			return writeError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if writeError := deployedListWriter.WriteByte('\n'); writeError != nil {
 | 
			
		||||
			return writeError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Read mod info from disk.
 | 
			
		||||
	var modInfoPath, modInfoPathError = game.configPath("mods.ini")
 | 
			
		||||
 | 
			
		||||
	if modInfoPathError != nil {
 | 
			
		||||
		return modInfoPathError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var modInfoFile, modInfoCreateError = os.Create(modInfoPath)
 | 
			
		||||
 | 
			
		||||
	if modInfoCreateError != nil {
 | 
			
		||||
		return modInfoCreateError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if closeError := modInfoFile.Close(); closeError != nil {
 | 
			
		||||
			panic(closeError)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	var modInfoEntries = make([]ini.Entry, 0, len(game.Mods)*3)
 | 
			
		||||
 | 
			
		||||
	for name, mod := range game.Mods {
 | 
			
		||||
		modInfoEntries = append(modInfoEntries, ini.Entry{
 | 
			
		||||
			Section: name,
 | 
			
		||||
			Key:     "source",
 | 
			
		||||
			Value:   mod.Source,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		modInfoEntries = append(modInfoEntries, ini.Entry{
 | 
			
		||||
			Section: name,
 | 
			
		||||
			Key:     "format",
 | 
			
		||||
			Value:   mod.Format,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		modInfoEntries = append(modInfoEntries, ini.Entry{
 | 
			
		||||
			Section: name,
 | 
			
		||||
			Key:     "version",
 | 
			
		||||
			Value:   mod.Version,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ini.Write(modInfoFile, modInfoEntries)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (game *Game) DeployMod(name string) error {
 | 
			
		||||
func (game *Game) Deploy(names []string) error {
 | 
			
		||||
	for _, name := range names {
 | 
			
		||||
		var mod, exists = game.Mods[name]
 | 
			
		||||
 | 
			
		||||
		if !(exists) {
 | 
			
		||||
			return fmt.Errorf("mod does not exist: %s", name)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	var archivePath, archivePathError = game.configPath(name)
 | 
			
		||||
 | 
			
		||||
	if archivePathError != nil {
 | 
			
		||||
		return archivePathError
 | 
			
		||||
	}
 | 
			
		||||
		var installPath = fmt.Sprintf("%s.%s",
 | 
			
		||||
			filepath.Join(configPath(), game.Name, name), mod.Format)
 | 
			
		||||
 | 
			
		||||
		switch mod.Format {
 | 
			
		||||
		case "zip":
 | 
			
		||||
		archivePath += ".zip"
 | 
			
		||||
			var zipReadCloser, openError = zip.OpenReader(installPath)
 | 
			
		||||
 | 
			
		||||
		var zipReadCloser, zipReadCloserOpenError = zip.OpenReader(archivePath)
 | 
			
		||||
 | 
			
		||||
		if zipReadCloserOpenError != nil {
 | 
			
		||||
			return zipReadCloserOpenError
 | 
			
		||||
			if openError != nil {
 | 
			
		||||
				return openError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if closeError := zipReadCloser.Close(); closeError != nil {
 | 
			
		||||
					// Zip read closer will not have any pending I/O operations nor is it possible
 | 
			
		||||
					// to have already been closed.
 | 
			
		||||
					panic(closeError)
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
@ -175,58 +81,65 @@ func (game *Game) DeployMod(name string) error {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			var file, openFileError = os.OpenFile(
 | 
			
		||||
				deployPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, fileMode)
 | 
			
		||||
				// Backup up any pre-existing file before it is overwritten by Zip entry
 | 
			
		||||
				// extraction.
 | 
			
		||||
				if fileInfo, statError := os.Stat(deployPath); statError == nil {
 | 
			
		||||
					var backupPath = filepath.Join(
 | 
			
		||||
						cachePath(), game.Name, "overwritten", zipFile.Name)
 | 
			
		||||
 | 
			
		||||
			if openFileError != nil {
 | 
			
		||||
				if !(os.IsExist(openFileError)) {
 | 
			
		||||
					return openFileError
 | 
			
		||||
					if dirError := os.MkdirAll(filepath.Dir(backupPath), fileInfo.Mode()); dirError != nil {
 | 
			
		||||
						if closeError := zipReadCloser.Close(); closeError != nil {
 | 
			
		||||
							return closeError
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
				var backupPath, backupPathError = game.cachePath("overwritten") // deployPath
 | 
			
		||||
 | 
			
		||||
				if backupPathError != nil {
 | 
			
		||||
					return backupPathError
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				backupPath = filepath.Join(backupPath, zipFile.Name)
 | 
			
		||||
 | 
			
		||||
				if dirError := os.MkdirAll(filepath.Dir(backupPath), 0755); dirError != nil {
 | 
			
		||||
						return dirError
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if renameError := os.Rename(deployPath, backupPath); renameError != nil {
 | 
			
		||||
						if closeError := zipReadCloser.Close(); closeError != nil {
 | 
			
		||||
							return closeError
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						return renameError
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					game.OverwrittenFilePaths = append(game.OverwrittenFilePaths, zipFile.Name)
 | 
			
		||||
 | 
			
		||||
				file, openFileError = os.OpenFile(
 | 
			
		||||
					deployPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, fileMode)
 | 
			
		||||
 | 
			
		||||
				if openFileError != nil {
 | 
			
		||||
					return openFileError
 | 
			
		||||
				}
 | 
			
		||||
				} else if !(os.IsNotExist(statError)) {
 | 
			
		||||
					if closeError := zipReadCloser.Close(); closeError != nil {
 | 
			
		||||
						return closeError
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
			var zipReadCloser, zipFileOpenError = zipFile.Open()
 | 
			
		||||
 | 
			
		||||
			if zipFileOpenError != nil {
 | 
			
		||||
				return zipFileOpenError
 | 
			
		||||
					return statError
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if syncError := file.Sync(); syncError != nil {
 | 
			
		||||
					panic(syncError)
 | 
			
		||||
				if file, createError := os.Create(deployPath); createError == nil {
 | 
			
		||||
					var zipFile, ioError = zipFile.Open()
 | 
			
		||||
 | 
			
		||||
					if ioError == nil {
 | 
			
		||||
						_, ioError = io.Copy(file, zipFile)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				if closeError := file.Close(); closeError != nil {
 | 
			
		||||
					panic(closeError)
 | 
			
		||||
					if syncError := file.Sync(); (syncError != nil) && (ioError == nil) {
 | 
			
		||||
						ioError = syncError
 | 
			
		||||
					}
 | 
			
		||||
			}()
 | 
			
		||||
 | 
			
		||||
			if _, copyError := io.Copy(file, zipReadCloser); copyError != nil {
 | 
			
		||||
				return copyError
 | 
			
		||||
					if closeError := file.Close(); (closeError != nil) && (ioError == nil) {
 | 
			
		||||
						ioError = closeError
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if closeError := zipFile.Close(); closeError != nil {
 | 
			
		||||
						ioError = closeError
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if ioError != nil {
 | 
			
		||||
						return ioError
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					if closeError := zipReadCloser.Close(); closeError != nil {
 | 
			
		||||
						return closeError
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					return createError
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				game.DeployedFilePaths = append(game.DeployedFilePaths, zipFile.Name)
 | 
			
		||||
@ -235,8 +148,9 @@ func (game *Game) DeployMod(name string) error {
 | 
			
		||||
		default:
 | 
			
		||||
			return fmt.Errorf("unsupported mod format: %s", mod.Format)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
	return game.saveDeployment()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Game struct {
 | 
			
		||||
@ -247,47 +161,60 @@ type Game struct {
 | 
			
		||||
	Path                 string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (game *Game) InstallMod(archivePath string) error {
 | 
			
		||||
	var archiveBaseName = filepath.Base(archivePath)
 | 
			
		||||
	var archiveExtension = filepath.Ext(archiveBaseName)
 | 
			
		||||
func (game *Game) InstallMods(archivePaths []string) error {
 | 
			
		||||
	var installDirPath = filepath.Join(configPath(), game.Name)
 | 
			
		||||
 | 
			
		||||
	for _, archivePath := range archivePaths {
 | 
			
		||||
		var archiveName = filepath.Base(archivePath)
 | 
			
		||||
		var archiveExtension = filepath.Ext(archiveName)
 | 
			
		||||
 | 
			
		||||
		if len(archiveExtension) == 0 {
 | 
			
		||||
			return fmt.Errorf("unknown archive format: %s", archiveExtension)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	var name = strings.TrimSuffix(archiveBaseName, archiveExtension)
 | 
			
		||||
		var name = strings.TrimSuffix(archiveName, archiveExtension)
 | 
			
		||||
 | 
			
		||||
		if _, exists := game.Mods[name]; exists {
 | 
			
		||||
			return fmt.Errorf("mod with name already exists: `%s`", name)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Copy archive into installation directory.
 | 
			
		||||
	{
 | 
			
		||||
		var installPath, installPathError = game.configPath(archiveBaseName)
 | 
			
		||||
		if archiveFile, openError := os.Open(archivePath); openError == nil {
 | 
			
		||||
			defer func() {
 | 
			
		||||
				if closeError := archiveFile.Close(); closeError != nil {
 | 
			
		||||
					// Archive file will not have any pending I/O operations nor is it possible to have
 | 
			
		||||
					// already been closed.
 | 
			
		||||
					panic(closeError)
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
 | 
			
		||||
		if installPathError != nil {
 | 
			
		||||
			return installPathError
 | 
			
		||||
			var archiveFileInfo, statError = archiveFile.Stat()
 | 
			
		||||
 | 
			
		||||
			if statError != nil {
 | 
			
		||||
				return statError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		var sourceFile, sourceOpenError = os.Open(archivePath)
 | 
			
		||||
 | 
			
		||||
		if sourceOpenError != nil {
 | 
			
		||||
			return sourceOpenError
 | 
			
		||||
			if dirError := os.MkdirAll(installDirPath, archiveFileInfo.Mode()); dirError != nil {
 | 
			
		||||
				return dirError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		defer sourceFile.Close()
 | 
			
		||||
			if installFile, createError := os.Create(filepath.Join(installDirPath, archiveName)); createError == nil {
 | 
			
		||||
				var _, copyError = io.Copy(installFile, archiveFile)
 | 
			
		||||
				var syncError = installFile.Sync()
 | 
			
		||||
				var closeError = installFile.Close()
 | 
			
		||||
 | 
			
		||||
		var targetFile, targetCreateError = os.Create(installPath)
 | 
			
		||||
 | 
			
		||||
		if targetCreateError != nil {
 | 
			
		||||
			return targetCreateError
 | 
			
		||||
				if (copyError != nil) || (syncError != nil) {
 | 
			
		||||
					return fmt.Errorf("failed to install mod")
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
		defer targetFile.Close()
 | 
			
		||||
 | 
			
		||||
		if _, copyError := io.Copy(targetFile, sourceFile); copyError != nil {
 | 
			
		||||
			return copyError
 | 
			
		||||
				if closeError != nil {
 | 
			
		||||
					return closeError
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				return createError
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			return openError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		game.Mods[name] = Mod{
 | 
			
		||||
@ -295,43 +222,32 @@ func (game *Game) InstallMod(archivePath string) error {
 | 
			
		||||
			Source:  archivePath,
 | 
			
		||||
			Version: "",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
	return game.saveMods()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (game *Game) Load() error {
 | 
			
		||||
	// Read deployed files from disk.
 | 
			
		||||
	var deployedListPath, deployedListPathError = game.cachePath("deployed.txt")
 | 
			
		||||
 | 
			
		||||
	if deployedListPathError != nil {
 | 
			
		||||
		return deployedListPathError
 | 
			
		||||
	}
 | 
			
		||||
	var deployedListPath = filepath.Join(cachePath(), game.Name, "deployed.txt")
 | 
			
		||||
 | 
			
		||||
	if file, openError := os.Open(deployedListPath); openError == nil {
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if closeError := file.Close(); closeError != nil {
 | 
			
		||||
				panic(closeError)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		var scanner = bufio.NewScanner(file)
 | 
			
		||||
 | 
			
		||||
		for scanner.Scan() {
 | 
			
		||||
		for scanner := bufio.NewScanner(file); scanner.Scan(); {
 | 
			
		||||
			game.DeployedFilePaths = append(game.DeployedFilePaths, scanner.Text())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if closeError := file.Close(); closeError != nil {
 | 
			
		||||
			return closeError
 | 
			
		||||
		}
 | 
			
		||||
	} else if !(os.IsNotExist(openError)) {
 | 
			
		||||
		return openError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Read overwritten files from disk.
 | 
			
		||||
	var overwriteDirPath, overwriteDirPathError = game.cachePath("overwritten")
 | 
			
		||||
	// Read overwritten game files from disk.
 | 
			
		||||
	var overwrittenFilesDirPath = filepath.Join(cachePath(), game.Name, "overwritten")
 | 
			
		||||
 | 
			
		||||
	if overwriteDirPathError != nil {
 | 
			
		||||
		return overwriteDirPathError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, statError := os.Stat(overwriteDirPath); statError == nil {
 | 
			
		||||
		if walkError := filepath.WalkDir(overwriteDirPath, func(
 | 
			
		||||
	if _, statError := os.Stat(overwrittenFilesDirPath); statError == nil {
 | 
			
		||||
		if walkError := filepath.WalkDir(overwrittenFilesDirPath, func(
 | 
			
		||||
			path string, dirEntry fs.DirEntry, walkError error) error {
 | 
			
		||||
 | 
			
		||||
			if walkError != nil {
 | 
			
		||||
@ -351,15 +267,11 @@ func (game *Game) Load() error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Read mod info from disk.
 | 
			
		||||
	var modInfoPath, modInfoPathError = game.configPath("mods.ini")
 | 
			
		||||
 | 
			
		||||
	if modInfoPathError != nil {
 | 
			
		||||
		return modInfoPathError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if file, openError := os.Open(modInfoPath); openError == nil {
 | 
			
		||||
	if file, openError := os.Open(filepath.Join(configPath(), game.Name, "mods.ini")); openError == nil {
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if closeError := file.Close(); closeError != nil {
 | 
			
		||||
				// File will not have any pending I/O operations nor is it possible to have already
 | 
			
		||||
				// been closed.
 | 
			
		||||
				panic(closeError)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
@ -399,7 +311,7 @@ type Mod struct {
 | 
			
		||||
	Version string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func OpenGame(name string) (Game, error) {
 | 
			
		||||
func LoadGame(name string) (Game, error) {
 | 
			
		||||
	var configPath, configPathError = os.UserConfigDir()
 | 
			
		||||
 | 
			
		||||
	if configPathError != nil {
 | 
			
		||||
@ -425,7 +337,7 @@ func OpenGame(name string) (Game, error) {
 | 
			
		||||
				OverwrittenFilePaths: make([]string, 0, 512),
 | 
			
		||||
				DeployedFilePaths:    make([]string, 0, 512),
 | 
			
		||||
				Mods:                 make(map[string]Mod),
 | 
			
		||||
				Path:                 "/home/kayomn/.steam/steam/steamapps/common/Fallout 4/Data",
 | 
			
		||||
				Path:                 entry.Key,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if loadError := game.Load(); loadError != nil {
 | 
			
		||||
@ -439,84 +351,93 @@ func OpenGame(name string) (Game, error) {
 | 
			
		||||
	return Game{}, fmt.Errorf("game not registered: %s", name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (game *Game) RemoveMods(names []string) error {
 | 
			
		||||
	for _, name := range names {
 | 
			
		||||
func (game *Game) RemoveMod(name string) error {
 | 
			
		||||
	if _, exists := game.Mods[name]; !(exists) {
 | 
			
		||||
		return fmt.Errorf("unknown mod: `%s`", name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		var path, pathError = game.configPath(name)
 | 
			
		||||
	if removeError := os.RemoveAll(filepath.Join(
 | 
			
		||||
		configPath(), game.Name, name)); removeError != nil {
 | 
			
		||||
 | 
			
		||||
		if pathError != nil {
 | 
			
		||||
			return pathError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if removeError := os.RemoveAll(path); removeError != nil {
 | 
			
		||||
		return removeError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(game.Mods, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
	return game.saveMods()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RegisterGame(name string, dataPath string) error {
 | 
			
		||||
	var configPath, configPathError = os.UserConfigDir()
 | 
			
		||||
	var gamesPath = filepath.Join(configPath(), "games.ini")
 | 
			
		||||
	var gameNamePaths = make(map[string]string)
 | 
			
		||||
 | 
			
		||||
	if configPathError != nil {
 | 
			
		||||
		return configPathError
 | 
			
		||||
	if file, openError := os.Open(gamesPath); openError == nil {
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if closeError := file.Close(); closeError != nil {
 | 
			
		||||
				// No way for this to fail.
 | 
			
		||||
				panic(closeError)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		var parser = ini.NewParser(file)
 | 
			
		||||
 | 
			
		||||
		for entry := parser.Parse(); !(parser.IsEnd()); entry = parser.Parse() {
 | 
			
		||||
			if entry.Key == "path" {
 | 
			
		||||
				gameNamePaths[entry.Section] = entry.Value
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	var gamesPath = filepath.Join(configPath, "modman", "games.ini")
 | 
			
		||||
 | 
			
		||||
	var gamesEntries = []ini.Entry{{
 | 
			
		||||
		Section: name,
 | 
			
		||||
		Key:     "path",
 | 
			
		||||
		Value:   dataPath,
 | 
			
		||||
	}}
 | 
			
		||||
 | 
			
		||||
	if gamesFile, gamesFileError := os.Open(gamesPath); gamesFileError == nil {
 | 
			
		||||
		var gamesParser = ini.NewParser(gamesFile)
 | 
			
		||||
 | 
			
		||||
		for entry := gamesParser.Parse(); !(gamesParser.IsEnd()); entry = gamesParser.Parse() {
 | 
			
		||||
			gamesEntries = append(gamesEntries, entry)
 | 
			
		||||
		if parserError := parser.Err(); parserError != nil {
 | 
			
		||||
			return parserError
 | 
			
		||||
		}
 | 
			
		||||
	} else if !(os.IsNotExist(openError)) {
 | 
			
		||||
		return openError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		if err := gamesFile.Close(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
	if file, updateError := os.Create(gamesPath); updateError == nil {
 | 
			
		||||
		var writer = bufio.NewWriter(file)
 | 
			
		||||
		var builder = ini.NewBuilder(writer)
 | 
			
		||||
 | 
			
		||||
		for name, path := range gameNamePaths {
 | 
			
		||||
			if buildError := builder.Section(name); buildError != nil {
 | 
			
		||||
				updateError = buildError
 | 
			
		||||
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		if err := gamesParser.Err(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
			if buildError := builder.KeyValue("path", path); buildError != nil {
 | 
			
		||||
				updateError = buildError
 | 
			
		||||
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
	} else if !(os.IsNotExist(gamesFileError)) {
 | 
			
		||||
		return gamesFileError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	if gamesFile, gamesFileError := os.Create(gamesPath); gamesFileError == nil {
 | 
			
		||||
		var writeError = ini.Write(gamesFile, gamesEntries)
 | 
			
		||||
 | 
			
		||||
		if err := gamesFile.Sync(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		if writeError := writer.Flush(); (writeError != nil) && (updateError == nil) {
 | 
			
		||||
			updateError = writeError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := gamesFile.Close(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		if syncError := file.Sync(); (syncError != nil) && (updateError == nil) {
 | 
			
		||||
			updateError = syncError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if writeError != nil {
 | 
			
		||||
			return writeError
 | 
			
		||||
		if closeError := file.Close(); (closeError != nil) && (updateError == nil) {
 | 
			
		||||
			updateError = closeError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if updateError != nil {
 | 
			
		||||
			return updateError
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		return gamesFileError
 | 
			
		||||
		return updateError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (game *Game) RenameMod(modName string, newName string) error {
 | 
			
		||||
	if _, exists := game.Mods[modName]; !(exists) {
 | 
			
		||||
	var mod, exists = game.Mods[modName]
 | 
			
		||||
 | 
			
		||||
	if !(exists) {
 | 
			
		||||
		return fmt.Errorf("no mod with that name exists")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -524,59 +445,134 @@ func (game *Game) RenameMod(modName string, newName string) error {
 | 
			
		||||
		return fmt.Errorf("a mod with the new name already exists")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var modPath, modPathError = game.configPath(modName)
 | 
			
		||||
	var modsDirPath = filepath.Join(configPath(), game.Name)
 | 
			
		||||
 | 
			
		||||
	if modPathError != nil {
 | 
			
		||||
		return modPathError
 | 
			
		||||
	}
 | 
			
		||||
	if renameError := os.Rename(filepath.Join(modsDirPath, modName),
 | 
			
		||||
		filepath.Join(modsDirPath, newName)); renameError != nil {
 | 
			
		||||
 | 
			
		||||
	var newPath, newPathError = game.configPath(modName)
 | 
			
		||||
 | 
			
		||||
	if newPathError != nil {
 | 
			
		||||
		return newPathError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if renameError := os.Rename(modPath, newPath); renameError != nil {
 | 
			
		||||
		return renameError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var mod = game.Mods[modName]
 | 
			
		||||
 | 
			
		||||
	game.Mods[newName] = mod
 | 
			
		||||
 | 
			
		||||
	delete(game.Mods, modName)
 | 
			
		||||
 | 
			
		||||
	return game.saveMods()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cachePath() string {
 | 
			
		||||
	return fallbackPath(os.UserCacheDir, "cache")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func configPath() string {
 | 
			
		||||
	return fallbackPath(os.UserConfigDir, "config")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fallbackPath(getFalliblePath func() (string, error), fallbackSubdir string) string {
 | 
			
		||||
	var path, pathError = getFalliblePath()
 | 
			
		||||
 | 
			
		||||
	if pathError != nil {
 | 
			
		||||
		// Fallback to homedir.
 | 
			
		||||
		path, pathError = os.UserHomeDir()
 | 
			
		||||
 | 
			
		||||
		if pathError != nil {
 | 
			
		||||
			// User home dir should exist / be accessible.
 | 
			
		||||
			panic(pathError)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return filepath.Join(path, "modman", fallbackSubdir)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return filepath.Join(path, "modman")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (game *Game) saveDeployment() error {
 | 
			
		||||
	var listPath = filepath.Join(cachePath(), "deployed.txt")
 | 
			
		||||
 | 
			
		||||
	if len(game.DeployedFilePaths) == 0 {
 | 
			
		||||
		return os.Truncate(listPath, 0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if file, updateError := os.Create(listPath); updateError == nil {
 | 
			
		||||
		var writer = bufio.NewWriter(file)
 | 
			
		||||
 | 
			
		||||
		for _, filePath := range game.DeployedFilePaths {
 | 
			
		||||
			if _, printError := fmt.Fprintln(writer, filePath); printError != nil {
 | 
			
		||||
				updateError = printError
 | 
			
		||||
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if flushError := writer.Flush(); flushError != nil && updateError == nil {
 | 
			
		||||
			updateError = flushError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if syncError := file.Sync(); syncError != nil && updateError == nil {
 | 
			
		||||
			updateError = syncError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if closeError := file.Close(); closeError != nil && updateError == nil {
 | 
			
		||||
			updateError = closeError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if updateError != nil {
 | 
			
		||||
			return updateError
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		return updateError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (game *Game) cachePath(path string) (string, error) {
 | 
			
		||||
	var dirPath, pathError = os.UserCacheDir()
 | 
			
		||||
func (game *Game) saveMods() error {
 | 
			
		||||
	var file, updateError = os.Open(filepath.Join(configPath(), game.Name, "mods.ini"))
 | 
			
		||||
 | 
			
		||||
	if pathError != nil {
 | 
			
		||||
		return "", pathError
 | 
			
		||||
	if updateError != nil {
 | 
			
		||||
		return updateError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dirPath = filepath.Join(dirPath, "modman", game.Name)
 | 
			
		||||
	var writer = bufio.NewWriter(file)
 | 
			
		||||
	var builder = ini.NewBuilder(writer)
 | 
			
		||||
 | 
			
		||||
	if mkdirError := os.MkdirAll(dirPath, 0755); mkdirError != nil {
 | 
			
		||||
		return "", mkdirError
 | 
			
		||||
	for name, mod := range game.Mods {
 | 
			
		||||
		if buildError := builder.Section(name); buildError != nil {
 | 
			
		||||
			updateError = buildError
 | 
			
		||||
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	return filepath.Join(dirPath, path), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (game *Game) configPath(path string) (string, error) {
 | 
			
		||||
	var dirPath, pathError = os.UserConfigDir()
 | 
			
		||||
 | 
			
		||||
	if pathError != nil {
 | 
			
		||||
		return "", pathError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dirPath = filepath.Join(dirPath, "modman", game.Name)
 | 
			
		||||
 | 
			
		||||
	if mkdirError := os.MkdirAll(dirPath, 0755); mkdirError != nil {
 | 
			
		||||
		return "", mkdirError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return filepath.Join(dirPath, path), nil
 | 
			
		||||
		if buildError := builder.KeyValue("format", mod.Format); buildError != nil {
 | 
			
		||||
			updateError = buildError
 | 
			
		||||
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if buildError := builder.KeyValue("source", mod.Source); buildError != nil {
 | 
			
		||||
			updateError = buildError
 | 
			
		||||
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if buildError := builder.KeyValue("version", mod.Version); buildError != nil {
 | 
			
		||||
			updateError = buildError
 | 
			
		||||
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if writeError := writer.Flush(); (writeError != nil) && (updateError == nil) {
 | 
			
		||||
		updateError = writeError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if syncError := file.Sync(); (syncError != nil) && (updateError == nil) {
 | 
			
		||||
		updateError = syncError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if closeError := file.Close(); (closeError != nil) && (updateError == nil) {
 | 
			
		||||
		updateError = closeError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return updateError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user