package assets import ( "flag" "fmt" "io" "os" "path/filepath" "strings" "git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/assets/mdl" ) // runFixMDL lowercases .mdl filenames and rewrites ASCII model identity to // match each file's stem. Binary models are skipped (the engine owns those). func runFixMDL(args []string, stdout, stderr io.Writer) int { fs := flag.NewFlagSet("fix-mdl", flag.ContinueOnError) fs.SetOutput(stderr) dryRun := fs.Bool("dry-run", false, "report changes without touching disk") nonRecursive := fs.Bool("non-recursive", false, "do not descend into subdirectories") if err := fs.Parse(args); err != nil { return exitUsage } paths := fs.Args() if len(paths) == 0 { fmt.Fprintln(stderr, "assets fix-mdl: usage: fix-mdl [--dry-run] ...") return exitUsage } files, err := walk(paths, mdlExt, !*nonRecursive) if err != nil { fmt.Fprintln(stderr, "assets fix-mdl:", err) return exitUsage } changed := 0 failed := false for _, f := range files { // 1. Lowercase the basename if needed. // // ponytail: 35k files are each read once here (CheckNames + the rewrite // on candidates). The shell reference batched an awk pass to avoid // per-file subprocess spawns; in Go a plain read is cheap, so the batch // is not worth porting. If profiling ever shows this hot, parallelize // the loop. lower := f if base := filepath.Base(f); base != strings.ToLower(base) { lower = filepath.Join(filepath.Dir(f), strings.ToLower(base)) if _, err := os.Stat(lower); err == nil { fmt.Fprintf(stderr, "assets fix-mdl: lowercase collision: %s -> %s\n", f, lower) failed = true continue } if *dryRun { fmt.Fprintf(stdout, "would lowercase: %s -> %s\n", f, lower) } else { if err := os.Rename(f, lower); err != nil { fmt.Fprintf(stderr, "assets fix-mdl: failed to lowercase %s: %v\n", f, err) failed = true continue } fmt.Fprintf(stdout, "lowercased: %s -> %s\n", f, lower) } changed++ } // 2. Rewrite model identity if the (lowercased) file has a mismatch. // In dry-run the on-disk file is still the original path. readPath := lower if *dryRun && lower != f { readPath = f } data, err := os.ReadFile(readPath) if err != nil { fmt.Fprintln(stderr, "assets fix-mdl:", err) failed = true continue } out, wasChanged := mdl.FixNames(data, mdl.ExpectedName(lower)) if !wasChanged { continue } if *dryRun { fmt.Fprintf(stdout, "would fix: %s\n", lower) } else { if err := os.WriteFile(lower, out, 0o644); err != nil { fmt.Fprintln(stderr, "assets fix-mdl:", err) failed = true continue } fmt.Fprintf(stdout, "fixed: %s\n", lower) } changed++ } if changed == 0 { fmt.Fprintln(stdout, "no broken mdl model names found") } if failed { return exitFail } return exitOK }