script: add print-riff-chunks.go
diff --git a/script/print-riff-chunks.go b/script/print-riff-chunks.go
new file mode 100644
index 0000000..bf1bc3e
--- /dev/null
+++ b/script/print-riff-chunks.go
@@ -0,0 +1,105 @@
+// Copyright 2026 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//go:build ignore
+// +build ignore
+
+package main
+
+// print-riff-chunks.go prints a RIFF's chunks' position and type.
+//
+// ANI, AVI, WAV and WEBP files are all RIFF files.
+//
+// Usage: go run print-riff-chunks.go foo.webp
+
+import (
+	"flag"
+	"fmt"
+	"os"
+)
+
+func main() {
+	if err := main1(); err != nil {
+		os.Stderr.WriteString(err.Error() + "\n")
+		os.Exit(1)
+	}
+}
+
+func main1() error {
+	flag.Parse()
+	args := flag.Args()
+	if len(args) != 1 {
+		return fmt.Errorf("usage: progname filename.riff")
+	}
+	src, err := os.ReadFile(args[0])
+	if err != nil {
+		return err
+	}
+
+	pos := 0
+	if len(src) < 12 {
+		return posError(pos, len(src))
+	} else if len(src) > 0x7FFF_FFFF {
+		return fmt.Errorf("RIFF file is too large")
+	}
+	chunkLen := int64(u32le(src[pos+4 : pos+8]))
+	if int64(len(src)-8) < chunkLen {
+		return posError(0, len(src))
+	}
+	fmt.Printf("pos = 0x%08X = %10d (+8)    len = 0x%08X = %10d    RIFF/%s\n",
+		0, 0, chunkLen, chunkLen, clean(src[8:12]))
+	pos = 12
+
+	for pos < len(src) {
+		if pos > (len(src) - 8) {
+			return posError(pos, len(src))
+		}
+
+		n := int64(u32le(src[pos+4 : pos+8]))
+		fmt.Printf("pos = 0x%08X = %10d (+8)    len = 0x%08X = %10d    %s\n",
+			pos, pos, n, n, clean(src[pos:pos+4]))
+		pos += 8
+		if (n & 1) != 0 {
+			n++
+		}
+
+		if int64(len(src)-pos) < n {
+			return posError(pos, len(src))
+		}
+		pos += int(n)
+	}
+
+	fmt.Printf("pos = 0x%08X = %10d                                          ~~~~\n",
+		pos, pos)
+	return nil
+}
+
+func posError(pos int, lenSrc int) error {
+	return fmt.Errorf("bad RIFF, pos = 0x%08X = %10d, len(src) = 0x%08X = %10d",
+		pos, pos, lenSrc, lenSrc)
+}
+
+func clean(b []byte) string {
+	a := [4]byte{}
+	for i, c := range b[:4] {
+		if (c < '!') || ('~' < c) {
+			c = '.'
+		}
+		a[i] = c
+	}
+	return string(a[:4])
+}
+
+func u32le(b []byte) uint32 {
+	return (uint32(b[0]) << 0) |
+		(uint32(b[1]) << 8) |
+		(uint32(b[2]) << 16) |
+		(uint32(b[3]) << 24)
+}