feat(assets): seven asset commands + Run dispatch/helpers

check-dupes, clean-dupes, check-mdl, fix-mdl, convert, upscale, compile
behind a single runner indirection, mirroring internal/depot's Run shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 14:13:54 +02:00
co-authored by Claude Opus 4.8
parent d8f01dc567
commit dd01c96473
16 changed files with 1400 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
package assets
import (
"flag"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
)
var upscaleBackends = []string{"upscayl-bin", "upscayl", "realesrgan-ncnn-vulkan", "waifu2x-ncnn-vulkan"}
// runUpscale upscales textures in place through an installed ncnn-vulkan
// backend. DDS inputs are bridged through PNG so the NWN flip stays correct.
func runUpscale(args []string, stdout, stderr io.Writer) int {
fs := flag.NewFlagSet("upscale", flag.ContinueOnError)
fs.SetOutput(stderr)
scale := fs.Int("scale", 4, "upscale factor")
backend := fs.String("backend", "", "override the upscaler binary")
nonRecursive := fs.Bool("non-recursive", false, "do not descend into subdirectories")
if err := fs.Parse(args); err != nil {
return exitUsage
}
dirs := fs.Args()
if len(dirs) == 0 {
fmt.Fprintln(stderr, "assets upscale: usage: upscale [--scale N] <dir>...")
return exitUsage
}
candidates := upscaleBackends
if *backend != "" {
candidates = []string{*backend}
}
tool := look(candidates...)
if tool == "" {
fmt.Fprintf(stderr, "assets upscale: no upscaler found — install one of: %s\n", strings.Join(upscaleBackends, ", "))
return exitTool
}
files, err := walk(dirs, textureExts, !*nonRecursive)
if err != nil {
fmt.Fprintln(stderr, "assets upscale:", err)
return exitUsage
}
// magick is only needed if a .dds input is present; resolve lazily.
magick := ""
failed := false
for _, src := range files {
var upErr error
if strings.EqualFold(filepath.Ext(src), ".dds") {
if magick == "" {
if magick = look("magick"); magick == "" {
fmt.Fprintln(stderr, "assets upscale: .dds input needs ImageMagick (magick) for the png bridge")
return exitTool
}
}
upErr = upscaleDDS(tool, magick, src, *scale)
} else {
upErr = upscaleImage(tool, src, src, *scale)
}
if upErr != nil {
fmt.Fprintf(stderr, "assets upscale: %s: %v\n", src, upErr)
failed = true
}
}
if failed {
return exitFail
}
return exitOK
}
// upscaleImage runs the ncnn-vulkan backend to upscale src into dst (may be the
// same path).
func upscaleImage(tool, src, dst string, scale int) error {
tmp := dst + ".upscaled.png"
out, err := runner("", nil, tool, "-i", src, "-o", tmp, "-s", strconv.Itoa(scale))
if err != nil {
return fmt.Errorf("%s: %v: %s", filepath.Base(tool), err, strings.TrimSpace(string(out)))
}
return os.Rename(tmp, dst)
}
// upscaleDDS bridges a DDS through PNG: dds->png (flip), upscale, png->dds
// (flip back), yielding a correctly-flipped upscaled DDS.
func upscaleDDS(tool, magick, src string, scale int) error {
tmpPNG := src + ".bridge.png"
if err := ddsToPNG(magick, src, tmpPNG); err != nil {
return err
}
defer os.Remove(tmpPNG)
if err := upscaleImage(tool, tmpPNG, tmpPNG, scale); err != nil {
return err
}
return pngToDDS(magick, tmpPNG, src)
}