M config.go => config.go +6 -5
@@ 30,6 30,7 @@ type gameConfig struct {
emptyPaths []string
foundPaths map[string]bool
runCfg *runConfig
+ fullyReplaced []*dataDir
}
// Check a single path and return all files within it that were replaced.
@@ 38,7 39,7 @@ func (g *gameConfig) checkSinglePath(path string) []*replacedFile {
var replaced []*replacedFile
for _, rf := range g.replacedFiles {
- if strings.HasPrefix(rf.dir, path) {
+ if strings.HasPrefix(rf.dDir.path, path) {
replaced = append(replaced, rf)
}
}
@@ 49,12 50,12 @@ func (g *gameConfig) checkSinglePath(path string) []*replacedFile {
// Helper method for storing details about when one file replaces another.
func (g *gameConfig) registerReplacement(new *dataFile, old *dataFile) {
g.replacedFiles = append(g.replacedFiles, &replacedFile{
- dir: old.path,
- path: new.localPath,
- replacedByDir: strings.Replace(new.path, new.localPath, "", -1),
+ dDir: old.dDir,
+ localPath: new.localPath,
+ replacedBy: new.dDir,
})
if !g.runCfg.noReplace {
- log.Printf("Replaced: %s", old.path)
+ log.Printf("Replaced: %s", old.dDir.path)
}
}
M files.go => files.go +28 -5
@@ 12,13 12,33 @@ type contentFile struct {
order int
}
+//TODO: plugin file validation is broken, it didn't notice when
+//TODO: Ports Of Vvardenfell V1.6.ESP was configured and didn't exist.
+//TODO: Clean_Ports Of Vvardenfell V1.6.ESP did exist.
+
// Represent a data file and the associated data path, "local path"
// (e.g. meshes/x/foo.nif), and the type of file it is (directory, file).
type dataFile struct {
- path string
+ dDir *dataDir
localPath string
}
+// Helper method to detect if a particular `localPath` has already been stored.
+func (d *dataFile) alreadySaved(files map[string]*dataFile) (*dataFile, bool) {
+ if f, ok := files[d.localPath]; ok {
+ return f, ok
+ }
+ return &dataFile{}, false
+}
+
+// Helper method to detect if a particular data path `path` has already been stored.
+func (d *dataFile) alreadyFound(found map[string]bool) bool {
+ if _, ok := found[d.dDir.path]; ok {
+ return ok // true
+ }
+ return false
+}
+
// Is the given `dataFile` a valid plugin file?
func (d *dataFile) isPlugin() bool {
f := strings.ToLower(d.localPath)
@@ 28,9 48,9 @@ func (d *dataFile) isPlugin() bool {
// Strings representing data paths (e.g. `/meshes/x/foobar.nif`) that have been
// replaced, and the data dirs of the replaced and replacing files.
type replacedFile struct {
- dir string
- path string
- replacedByDir string
+ localPath string
+ dDir *dataDir
+ replacedBy *dataDir
}
func checkContentFile(cf string, order int, gc *gameConfig) bool {
@@ 64,7 84,10 @@ func checkContentFile(cf string, order int, gc *gameConfig) bool {
return false
} else {
gc.contentFiles = append(gc.contentFiles, &contentFile{
- file: &dataFile{},
+ file: &dataFile{
+ dDir: &dataDir{},
+ localPath: "",
+ },
order: 0,
})
}
M paths.go => paths.go +31 -44
@@ 8,21 8,16 @@ import (
"strings"
)
-// Represents attributes related to a configured data path: `files` for strings
-// representing full paths to files within a data path, `plugins` for strings
-// representing found plugin files, `order` an int for the numerical load order
-// of the data path, and `path` which a string that is the path itself.
+// Represents attributes related to a configured data path.
type dataDir struct {
- files []*dataFile
- plugins []*dataFile
- order int
- path string
+ files []*dataFile
+ plugins []*dataFile
+ order int
+ path string
+ replaced int
}
-// Given a string `d` that represents a data path root, a string `p` that represents a
-// top-level data directory, and a struct pointer `dp` that represents tha `dataDir`
-// object: do `filepath.Walk` on `p` and list out all files within it, recursively.
-// func checkDataDirs(d string, p string, dp *dataDir, gc *gameConfig) error {
+// Do `filepath.Walk` on a dataDir's paths and register found files as needed.
func (d *dataDir) checkDataDirs(gc *gameConfig) error {
pathContent, err := ioutil.ReadDir(d.path)
if err != nil {
@@ 45,7 40,7 @@ func (d *dataDir) checkDataDirs(gc *gameConfig) error {
if !nfo.IsDir() && !ignoredFile(filePath) {
df := &dataFile{
- path: filePath,
+ dDir: d,
localPath: localPath,
}
d.files = append(d.files, df)
@@ 53,9 48,10 @@ func (d *dataDir) checkDataDirs(gc *gameConfig) error {
// Does gc.dataFiles already have this localPath stored as a key? If
// so, check for an overwrite and log as needed.
// THANKS: https://stackoverflow.com/a/2050629
- val, ok := alreadySaved(df.localPath, gc.dataFiles)
+ oldf, ok := df.alreadySaved(gc.dataFiles)
if ok {
- gc.registerReplacement(df, val)
+ gc.registerReplacement(df, oldf)
+ d.replaced += 1
}
gc.dataFiles[localPath] = df
@@ 71,17 67,18 @@ func (d *dataDir) checkDataDirs(gc *gameConfig) error {
path := filepath.Join(d.path, pc.Name())
if !ignoredFile(path) {
df := &dataFile{
- path: path,
+ dDir: d,
localPath: pc.Name(),
}
- val, ok := alreadySaved(df.localPath, gc.dataFiles)
+ oldf, ok := df.alreadySaved(gc.dataFiles)
if ok {
- gc.registerReplacement(df, val)
+ gc.registerReplacement(df, oldf)
+ d.replaced += 1
}
if df.isPlugin() {
d.plugins = append(d.plugins, df)
- gc.foundPlugins = append(gc.foundPlugins, df.path)
+ gc.foundPlugins = append(gc.foundPlugins, df.localPath)
}
gc.dataFiles[df.localPath] = df
@@ 91,28 88,17 @@ func (d *dataDir) checkDataDirs(gc *gameConfig) error {
return err
}
-// Helper method to detect if a particular data path `path` has already been stored.
-func alreadyFound(path string, found map[string]bool) bool {
- if _, ok := found[path]; ok {
+func (d *dataDir) alreadyFound(found map[string]bool) bool {
+ if _, ok := found[d.path]; ok {
return ok // true
}
return false
}
-// Helper method to detect if a particular `localPath` has already been stored.
-func alreadySaved(localPath string, files map[string]*dataFile) (*dataFile, bool) {
- if val, ok := files[localPath]; ok {
- return val, ok
- }
- return &dataFile{}, false
-}
-
-// Format a given string `dp` which is a `data=` entry from an openmw.cfg file
-// as just the path with no extra quotes or anything. Check that it's a real
-// thing, then scan it for files.
-func checkDataPath(dp string, dataOrder int, gc *gameConfig) (*dataDir, error) {
+// Verify that a dataDir is real, then scan it for files.
+func (d *dataDir) check(dataOrder int, gc *gameConfig) error {
// Format the path...
- p := strings.Replace(dp, "data=", "", -1)
+ p := strings.Replace(d.path, "data=", "", -1)
p = strings.TrimPrefix(p, "'")
p = strings.TrimPrefix(p, "\"")
p = strings.TrimSuffix(p, "\r")
@@ 123,7 109,7 @@ func checkDataPath(dp string, dataOrder int, gc *gameConfig) (*dataDir, error) {
log.Printf("Checking: %v", p)
}
- found := alreadyFound(p, gc.foundPaths)
+ found := d.alreadyFound(gc.foundPaths)
if found {
log.Printf("Path already configured: %s", p)
gc.duplicatePaths = append(gc.duplicatePaths, p)
@@ 134,16 120,17 @@ func checkDataPath(dp string, dataOrder int, gc *gameConfig) (*dataDir, error) {
_, err := os.Stat(p)
if os.IsNotExist(err) {
gc.badPaths = append(gc.badPaths, p)
- return &dataDir{}, err
+ return err
}
- d := &dataDir{
- files: []*dataFile{},
- plugins: []*dataFile{},
- order: dataOrder,
- path: p,
+ dd := &dataDir{
+ files: []*dataFile{},
+ plugins: []*dataFile{},
+ order: dataOrder,
+ path: p,
+ replaced: 0,
}
- d.checkDataDirs(gc)
+ dd.checkDataDirs(gc)
- return d, err
+ return err
}
M validations.go => validations.go +30 -7
@@ 24,6 24,7 @@ func runValidations(runCfg *runConfig) {
emptyPaths: []string{},
foundPaths: map[string]bool{},
runCfg: runCfg,
+ fullyReplaced: []*dataDir{},
}
if len(d.contentFiles) == 0 || len(d.dataPaths) == 0 {
@@ 37,7 38,15 @@ func runValidations(runCfg *runConfig) {
log.Println()
}
for _, p := range d.dataPaths {
- dp, err := checkDataPath(p, dataDirCount, g)
+ dd := &dataDir{
+ files: []*dataFile{},
+ plugins: []*dataFile{},
+ order: 0,
+ path: p,
+ replaced: 0,
+ }
+ err := dd.check(dd.order, g)
+
if err != nil {
// Most likely this path didn't actually exist..
if !runCfg.quiet {
@@ 46,7 55,7 @@ func runValidations(runCfg *runConfig) {
} else {
dataDirCount++
- g.dataDirs = append(g.dataDirs, dp)
+ g.dataDirs = append(g.dataDirs, dd)
}
}
@@ 86,7 95,18 @@ func runValidations(runCfg *runConfig) {
}
if !runCfg.quiet {
- log.Printf("Data path #%d \"%s\" has %d %s and %d %s", p.order, p.path, fileCount, fileWord, pluginCount, pluginWord)
+
+ fWord := "file"
+ wWord := "was"
+ if p.replaced == 0 || p.replaced > 1 {
+ fWord += "s"
+ wWord = "were"
+ }
+ log.Printf("Data path #%d \"%s\" has %d %s and %d %s. %d %s %s replaced.", p.order, p.path, fileCount, fileWord, pluginCount, pluginWord, p.replaced, fWord, wWord)
+
+ if len(p.files) >= p.replaced {
+ g.fullyReplaced = append(g.fullyReplaced, p)
+ }
}
}
@@ 113,6 133,7 @@ func runValidations(runCfg *runConfig) {
haveEmptyPaths := len(g.emptyPaths) > 0
haveDupedPaths := dupeCount > 0
haveBunkContent := len(g.bunkContent) > 0
+ haveFullyReplaced := len(g.fullyReplaced) > 0
if haveBadPaths {
log.Printf("Bad data paths count: %v", len(g.badPaths))
@@ 154,13 175,13 @@ func runValidations(runCfg *runConfig) {
log.Println()
}
- if !haveBadPaths && !haveEmptyPaths && !haveDupedPaths && !haveBunkContent {
+ //TODO: report on fully replaced
+
+ if !haveBadPaths && !haveEmptyPaths && !haveDupedPaths && !haveBunkContent && !haveFullyReplaced {
log.Println("Great Job! No problems detected.")
log.Println()
}
- //TODO: Report any paths that have been fully replaced
-
if runCfg.checkPath != "" {
var dd *dataDir
for _, d := range g.dataDirs {
@@ 182,7 203,7 @@ func runValidations(runCfg *runConfig) {
log.Printf("The following %d files in this path have been replaced:", len(replaced))
log.Println()
for _, r := range replaced {
- log.Printf("\"%s\" replaced by: \"%s\"", r.path, r.replacedByDir)
+ log.Printf("\"%s\" replaced by: \"%s\"", r.localPath, r.replacedBy.path)
}
log.Println()
}
@@ 201,6 222,8 @@ func runValidations(runCfg *runConfig) {
}
+ //TODO: Check groundcover files with content files
+ //TODO: Report if a data path is totally replaced
//TODO: Report unused content files (ones found in data paths but not configured as content files)
//TODO: Very quiet mode, that just exits 0 or 1
//TODO: Tests for everything