Add tell_me_more? mechanism
diff --git a/doc/changelog.md b/doc/changelog.md
index 0ab8793..d4532cf 100644
--- a/doc/changelog.md
+++ b/doc/changelog.md
@@ -3,8 +3,10 @@
 
 ## Work In Progress
 
-- Added `base` library support for `atoi`-like string conversion.
+- Added `WUFFS_BASE__PIXEL_BLEND__SRC_OVER`.
+- Added `WUFFS_BASE__PIXEL_FORMAT__BGR_565`.
 - Added `base` library support for UTF-8.
+- Added `base` library support for `atoi`-like string conversion.
 - Added `endwhile` syntax.
 - Added `example/imageviewer`.
 - Added `example/jsonptr`.
@@ -12,8 +14,7 @@
 - Added `std/gif.config_decoder`.
 - Added `std/json`.
 - Added `std/wbmp`.
-- Added `WUFFS_BASE__PIXEL_BLEND__SRC_OVER`.
-- Added `WUFFS_BASE__PIXEL_FORMAT__BGR_565`.
+- Added `tell_me_more?` mechanism.
 - Added alloc functions.
 - Added colons to const syntax.
 - Added double-curly blocks.
@@ -25,6 +26,7 @@
 - Made `wuffs_base__pixel_format` a struct.
 - Made `wuffs_base__pixel_subsampling` a struct.
 - Made `wuffs_base__status` a struct.
+- Removed `ack_metadata_chunk?`.
 - Removed `wuffs_base__frame_config__blend`.
 - Renamed `decode_io_writer?` methods to `transform_io?`.
 - Renamed warnings to notes.
diff --git a/internal/cgen/base/range-public.h b/internal/cgen/base/range-public.h
index 4fcab38..ac63312 100644
--- a/internal/cgen/base/range-public.h
+++ b/internal/cgen/base/range-public.h
@@ -805,3 +805,140 @@
 }
 
 #endif  // __cplusplus
+
+// ---------------- More Information
+
+// wuffs_base__more_information holds additional fields, typically when a Wuffs
+// method returns a [note status](/doc/note/statuses.md).
+//
+// The flavor field follows the base38 namespace
+// convention](/doc/note/base38-and-fourcc.md). The other fields' semantics
+// depends on the flavor.
+typedef struct {
+  uint32_t flavor;
+  uint32_t w;
+  uint64_t x;
+  uint64_t y;
+  uint64_t z;
+
+#ifdef __cplusplus
+  inline void set(uint32_t flavor_arg,
+                  uint32_t w_arg,
+                  uint64_t x_arg,
+                  uint64_t y_arg,
+                  uint64_t z_arg);
+  inline uint32_t io_redirect__fourcc() const;
+  inline wuffs_base__range_ie_u64 io_redirect__range() const;
+  inline uint64_t io_seek__position() const;
+  inline uint32_t metadata__fourcc() const;
+  inline wuffs_base__range_ie_u64 metadata__range() const;
+#endif  // __cplusplus
+
+} wuffs_base__more_information;
+
+#define WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_REDIRECT 1
+#define WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_SEEK 2
+#define WUFFS_BASE__MORE_INFORMATION__FLAVOR__METADATA 3
+
+static inline wuffs_base__more_information  //
+wuffs_base__empty_more_information() {
+  wuffs_base__more_information ret;
+  ret.flavor = 0;
+  ret.w = 0;
+  ret.x = 0;
+  ret.y = 0;
+  ret.z = 0;
+  return ret;
+}
+
+static inline void  //
+wuffs_base__more_information__set(wuffs_base__more_information* m,
+                                  uint32_t flavor,
+                                  uint32_t w,
+                                  uint64_t x,
+                                  uint64_t y,
+                                  uint64_t z) {
+  if (!m) {
+    return;
+  }
+  m->flavor = flavor;
+  m->w = w;
+  m->x = x;
+  m->y = y;
+  m->z = z;
+}
+
+static inline uint32_t  //
+wuffs_base__more_information__io_redirect__fourcc(
+    const wuffs_base__more_information* m) {
+  return m->w;
+}
+
+static inline wuffs_base__range_ie_u64  //
+wuffs_base__more_information__io_redirect__range(
+    const wuffs_base__more_information* m) {
+  wuffs_base__range_ie_u64 ret;
+  ret.min_incl = m->y;
+  ret.max_excl = m->z;
+  return ret;
+}
+
+static inline uint64_t  //
+wuffs_base__more_information__io_seek__position(
+    const wuffs_base__more_information* m) {
+  return m->x;
+}
+
+static inline uint32_t  //
+wuffs_base__more_information__metadata__fourcc(
+    const wuffs_base__more_information* m) {
+  return m->w;
+}
+
+static inline wuffs_base__range_ie_u64  //
+wuffs_base__more_information__metadata__range(
+    const wuffs_base__more_information* m) {
+  wuffs_base__range_ie_u64 ret;
+  ret.min_incl = m->y;
+  ret.max_excl = m->z;
+  return ret;
+}
+
+#ifdef __cplusplus
+
+inline void  //
+wuffs_base__more_information::set(uint32_t flavor_arg,
+                                  uint32_t w_arg,
+                                  uint64_t x_arg,
+                                  uint64_t y_arg,
+                                  uint64_t z_arg) {
+  wuffs_base__more_information__set(this, flavor_arg, w_arg, x_arg, y_arg,
+                                    z_arg);
+}
+
+inline uint32_t  //
+wuffs_base__more_information::io_redirect__fourcc() const {
+  return wuffs_base__more_information__io_redirect__fourcc(this);
+}
+
+inline wuffs_base__range_ie_u64  //
+wuffs_base__more_information::io_redirect__range() const {
+  return wuffs_base__more_information__io_redirect__range(this);
+}
+
+inline uint64_t  //
+wuffs_base__more_information::io_seek__position() const {
+  return wuffs_base__more_information__io_seek__position(this);
+}
+
+inline uint32_t  //
+wuffs_base__more_information::metadata__fourcc() const {
+  return wuffs_base__more_information__metadata__fourcc(this);
+}
+
+inline wuffs_base__range_ie_u64  //
+wuffs_base__more_information::metadata__range() const {
+  return wuffs_base__more_information__metadata__range(this);
+}
+
+#endif  // __cplusplus
diff --git a/internal/cgen/data.go b/internal/cgen/data.go
index 3d51198..6795243 100644
--- a/internal/cgen/data.go
+++ b/internal/cgen/data.go
@@ -343,7 +343,12 @@
 	"                 uint32_t min_incl_y,\n                             uint32_t max_excl_x,\n                             uint32_t max_excl_y) {\n  wuffs_base__rect_ie_u32 ret;\n  ret.min_incl_x = min_incl_x;\n  ret.min_incl_y = min_incl_y;\n  ret.max_excl_x = max_excl_x;\n  ret.max_excl_y = max_excl_y;\n  return ret;\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__is_empty(const wuffs_base__rect_ie_u32* r) {\n  return (r->min_incl_x >= r->max_excl_x) || (r->min_incl_y >= r->max_excl_y);\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__equals(const wuffs_base__rect_ie_u32* r,\n                                wuffs_base__rect_ie_u32 s) {\n  return (r->min_incl_x == s.min_incl_x && r->min_incl_y == s.min_incl_y &&\n          r->max_excl_x == s.max_excl_x && r->max_excl_y == s.max_excl_y) ||\n         (wuffs_base__rect_ie_u32__is_empty(r) &&\n          wuffs_base__rect_ie_u32__is_empty(&s));\n}\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32__intersect(const wuffs_base__rect_ie_u32* r,\n                    " +
 	"               wuffs_base__rect_ie_u32 s) {\n  wuffs_base__rect_ie_u32 t;\n  t.min_incl_x = wuffs_base__u32__max(r->min_incl_x, s.min_incl_x);\n  t.min_incl_y = wuffs_base__u32__max(r->min_incl_y, s.min_incl_y);\n  t.max_excl_x = wuffs_base__u32__min(r->max_excl_x, s.max_excl_x);\n  t.max_excl_y = wuffs_base__u32__min(r->max_excl_y, s.max_excl_y);\n  return t;\n}\n\nstatic inline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32__unite(const wuffs_base__rect_ie_u32* r,\n                               wuffs_base__rect_ie_u32 s) {\n  if (wuffs_base__rect_ie_u32__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__rect_ie_u32__is_empty(&s)) {\n    return *r;\n  }\n  wuffs_base__rect_ie_u32 t;\n  t.min_incl_x = wuffs_base__u32__min(r->min_incl_x, s.min_incl_x);\n  t.min_incl_y = wuffs_base__u32__min(r->min_incl_y, s.min_incl_y);\n  t.max_excl_x = wuffs_base__u32__max(r->max_excl_x, s.max_excl_x);\n  t.max_excl_y = wuffs_base__u32__max(r->max_excl_y, s.max_excl_y);\n  return t;\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__con" +
 	"tains(const wuffs_base__rect_ie_u32* r,\n                                  uint32_t x,\n                                  uint32_t y) {\n  return (r->min_incl_x <= x) && (x < r->max_excl_x) && (r->min_incl_y <= y) &&\n         (y < r->max_excl_y);\n}\n\nstatic inline bool  //\nwuffs_base__rect_ie_u32__contains_rect(const wuffs_base__rect_ie_u32* r,\n                                       wuffs_base__rect_ie_u32 s) {\n  return wuffs_base__rect_ie_u32__equals(\n      &s, wuffs_base__rect_ie_u32__intersect(r, s));\n}\n\nstatic inline uint32_t  //\nwuffs_base__rect_ie_u32__width(const wuffs_base__rect_ie_u32* r) {\n  return wuffs_base__u32__sat_sub(r->max_excl_x, r->min_incl_x);\n}\n\nstatic inline uint32_t  //\nwuffs_base__rect_ie_u32__height(const wuffs_base__rect_ie_u32* r) {\n  return wuffs_base__u32__sat_sub(r->max_excl_y, r->min_incl_y);\n}\n\n#ifdef __cplusplus\n\ninline bool  //\nwuffs_base__rect_ie_u32::is_empty() const {\n  return wuffs_base__rect_ie_u32__is_empty(this);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::equals(wuffs_bas" +
-	"e__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__equals(this, s);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32::intersect(wuffs_base__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__intersect(this, s);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32::unite(wuffs_base__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::contains(uint32_t x, uint32_t y) const {\n  return wuffs_base__rect_ie_u32__contains(this, x, y);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::contains_rect(wuffs_base__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__contains_rect(this, s);\n}\n\ninline uint32_t  //\nwuffs_base__rect_ie_u32::width() const {\n  return wuffs_base__rect_ie_u32__width(this);\n}\n\ninline uint32_t  //\nwuffs_base__rect_ie_u32::height() const {\n  return wuffs_base__rect_ie_u32__height(this);\n}\n\n#endif  // __cplusplus\n" +
+	"e__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__equals(this, s);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32::intersect(wuffs_base__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__intersect(this, s);\n}\n\ninline wuffs_base__rect_ie_u32  //\nwuffs_base__rect_ie_u32::unite(wuffs_base__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__unite(this, s);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::contains(uint32_t x, uint32_t y) const {\n  return wuffs_base__rect_ie_u32__contains(this, x, y);\n}\n\ninline bool  //\nwuffs_base__rect_ie_u32::contains_rect(wuffs_base__rect_ie_u32 s) const {\n  return wuffs_base__rect_ie_u32__contains_rect(this, s);\n}\n\ninline uint32_t  //\nwuffs_base__rect_ie_u32::width() const {\n  return wuffs_base__rect_ie_u32__width(this);\n}\n\ninline uint32_t  //\nwuffs_base__rect_ie_u32::height() const {\n  return wuffs_base__rect_ie_u32__height(this);\n}\n\n#endif  // __cplusplus\n\n" +
+	"" +
+	"// ---------------- More Information\n\n// wuffs_base__more_information holds additional fields, typically when a Wuffs\n// method returns a [note status](/doc/note/statuses.md).\n//\n// The flavor field follows the base38 namespace\n// convention](/doc/note/base38-and-fourcc.md). The other fields' semantics\n// depends on the flavor.\ntypedef struct {\n  uint32_t flavor;\n  uint32_t w;\n  uint64_t x;\n  uint64_t y;\n  uint64_t z;\n\n#ifdef __cplusplus\n  inline void set(uint32_t flavor_arg,\n                  uint32_t w_arg,\n                  uint64_t x_arg,\n                  uint64_t y_arg,\n                  uint64_t z_arg);\n  inline uint32_t io_redirect__fourcc() const;\n  inline wuffs_base__range_ie_u64 io_redirect__range() const;\n  inline uint64_t io_seek__position() const;\n  inline uint32_t metadata__fourcc() const;\n  inline wuffs_base__range_ie_u64 metadata__range() const;\n#endif  // __cplusplus\n\n} wuffs_base__more_information;\n\n#define WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_REDIRECT 1\n#define WUFFS_BASE__MORE_INFORMA" +
+	"TION__FLAVOR__IO_SEEK 2\n#define WUFFS_BASE__MORE_INFORMATION__FLAVOR__METADATA 3\n\nstatic inline wuffs_base__more_information  //\nwuffs_base__empty_more_information() {\n  wuffs_base__more_information ret;\n  ret.flavor = 0;\n  ret.w = 0;\n  ret.x = 0;\n  ret.y = 0;\n  ret.z = 0;\n  return ret;\n}\n\nstatic inline void  //\nwuffs_base__more_information__set(wuffs_base__more_information* m,\n                                  uint32_t flavor,\n                                  uint32_t w,\n                                  uint64_t x,\n                                  uint64_t y,\n                                  uint64_t z) {\n  if (!m) {\n    return;\n  }\n  m->flavor = flavor;\n  m->w = w;\n  m->x = x;\n  m->y = y;\n  m->z = z;\n}\n\nstatic inline uint32_t  //\nwuffs_base__more_information__io_redirect__fourcc(\n    const wuffs_base__more_information* m) {\n  return m->w;\n}\n\nstatic inline wuffs_base__range_ie_u64  //\nwuffs_base__more_information__io_redirect__range(\n    const wuffs_base__more_information* m) {\n  wuffs_base__range_ie_u64" +
+	" ret;\n  ret.min_incl = m->y;\n  ret.max_excl = m->z;\n  return ret;\n}\n\nstatic inline uint64_t  //\nwuffs_base__more_information__io_seek__position(\n    const wuffs_base__more_information* m) {\n  return m->x;\n}\n\nstatic inline uint32_t  //\nwuffs_base__more_information__metadata__fourcc(\n    const wuffs_base__more_information* m) {\n  return m->w;\n}\n\nstatic inline wuffs_base__range_ie_u64  //\nwuffs_base__more_information__metadata__range(\n    const wuffs_base__more_information* m) {\n  wuffs_base__range_ie_u64 ret;\n  ret.min_incl = m->y;\n  ret.max_excl = m->z;\n  return ret;\n}\n\n#ifdef __cplusplus\n\ninline void  //\nwuffs_base__more_information::set(uint32_t flavor_arg,\n                                  uint32_t w_arg,\n                                  uint64_t x_arg,\n                                  uint64_t y_arg,\n                                  uint64_t z_arg) {\n  wuffs_base__more_information__set(this, flavor_arg, w_arg, x_arg, y_arg,\n                                    z_arg);\n}\n\ninline uint32_t  //\nwuffs_base__m" +
+	"ore_information::io_redirect__fourcc() const {\n  return wuffs_base__more_information__io_redirect__fourcc(this);\n}\n\ninline wuffs_base__range_ie_u64  //\nwuffs_base__more_information::io_redirect__range() const {\n  return wuffs_base__more_information__io_redirect__range(this);\n}\n\ninline uint64_t  //\nwuffs_base__more_information::io_seek__position() const {\n  return wuffs_base__more_information__io_seek__position(this);\n}\n\ninline uint32_t  //\nwuffs_base__more_information::metadata__fourcc() const {\n  return wuffs_base__more_information__metadata__fourcc(this);\n}\n\ninline wuffs_base__range_ie_u64  //\nwuffs_base__more_information::metadata__range() const {\n  return wuffs_base__more_information__metadata__range(this);\n}\n\n#endif  // __cplusplus\n" +
 	""
 
 const baseStrConvPrivateH = "" +
diff --git a/lang/builtin/builtin.go b/lang/builtin/builtin.go
index 6bce082..cab8fd8 100644
--- a/lang/builtin/builtin.go
+++ b/lang/builtin/builtin.go
@@ -36,6 +36,9 @@
 	`"@metadata reported"`,
 
 	// Suspensions.
+	`"$even more information"`,
+	`"$mispositioned read"`,
+	`"$mispositioned write"`,
 	`"$short read"`,
 	`"$short write"`,
 
@@ -55,6 +58,7 @@
 	`"#initialize falsely claimed already zeroed"`,
 	`"#initialize not called"`,
 	`"#interleaved coroutine calls"`,
+	`"#no more information"`,
 	`"#not enough data"`,
 	`"#out of bounds"`,
 	`"#unsupported method"`,
@@ -84,6 +88,8 @@
 	"rect_ie_u32",
 	"rect_ii_u32",
 
+	"more_information",
+
 	"status",
 
 	"io_reader",
@@ -170,6 +176,10 @@
 	"range_ii_u64.intersect(r: range_ii_u64) range_ii_u64",
 	"range_ii_u64.unite(r: range_ii_u64) range_ii_u64",
 
+	// ---- more_information
+
+	"more_information.set!(flavor: u32, w: u32, x: u64, y: u64, z: u64)",
+
 	// ---- status
 
 	// TODO: should we add is_complete?
@@ -396,21 +406,19 @@
 
 	// ---- image_decoder
 
-	"image_decoder.ack_metadata_chunk?(src: io_reader)",
 	"image_decoder.decode_frame?(" +
 		"dst: ptr pixel_buffer, src: io_reader, blend: pixel_blend," +
 		"workbuf: slice u8, opts: nptr decode_frame_options)",
 	"image_decoder.decode_frame_config?(dst: nptr frame_config, src: io_reader)",
 	"image_decoder.decode_image_config?(dst: nptr image_config, src: io_reader)",
 	"image_decoder.frame_dirty_rect() rect_ie_u32",
-	"image_decoder.metadata_chunk_length() u64",
-	"image_decoder.metadata_fourcc() u32",
 	"image_decoder.num_animation_loops() u32",
 	"image_decoder.num_decoded_frame_configs() u64",
 	"image_decoder.num_decoded_frames() u64",
 	"image_decoder.restart_frame!(index: u64, io_position: u64) status",
 	"image_decoder.set_quirk_enabled!(quirk: u32, enabled: bool)",
 	"image_decoder.set_report_metadata!(fourcc: u32, report: bool)",
+	"image_decoder.tell_me_more?(dst: io_writer, minfo: nptr more_information, src: io_reader)",
 	"image_decoder.workbuf_len() range_ii_u64",
 
 	// ---- io_transformer
diff --git a/lang/check/resolve.go b/lang/check/resolve.go
index 82c25bc..9ac289e 100644
--- a/lang/check/resolve.go
+++ b/lang/check/resolve.go
@@ -54,6 +54,8 @@
 	typeExprRectIEU32  = a.NewTypeExpr(0, t.IDBase, t.IDRectIEU32, nil, nil, nil)
 	typeExprRectIIU32  = a.NewTypeExpr(0, t.IDBase, t.IDRectIIU32, nil, nil, nil)
 
+	typeExprMoreInformation = a.NewTypeExpr(0, t.IDBase, t.IDMoreInformation, nil, nil, nil)
+
 	typeExprStatus = a.NewTypeExpr(0, t.IDBase, t.IDStatus, nil, nil, nil)
 
 	typeExprIOReader    = a.NewTypeExpr(0, t.IDBase, t.IDIOReader, nil, nil, nil)
@@ -100,6 +102,8 @@
 	t.IDRectIEU32:  typeExprRectIEU32,
 	t.IDRectIIU32:  typeExprRectIIU32,
 
+	t.IDMoreInformation: typeExprMoreInformation,
+
 	t.IDStatus: typeExprStatus,
 
 	t.IDIOReader:    typeExprIOReader,
diff --git a/lang/token/list.go b/lang/token/list.go
index c38c963..421452e 100644
--- a/lang/token/list.go
+++ b/lang/token/list.go
@@ -472,17 +472,18 @@
 	IDU32 = ID(0x116)
 	IDU64 = ID(0x117)
 
-	IDBase          = ID(0x120)
-	IDBool          = ID(0x121)
-	IDEmptyIOReader = ID(0x122)
-	IDEmptyIOWriter = ID(0x123)
-	IDEmptyStruct   = ID(0x124)
-	IDIOReader      = ID(0x125)
-	IDIOWriter      = ID(0x126)
-	IDStatus        = ID(0x127)
-	IDTokenReader   = ID(0x128)
-	IDTokenWriter   = ID(0x129)
-	IDUtility       = ID(0x12A)
+	IDBase            = ID(0x120)
+	IDBool            = ID(0x121)
+	IDEmptyIOReader   = ID(0x122)
+	IDEmptyIOWriter   = ID(0x123)
+	IDEmptyStruct     = ID(0x124)
+	IDMoreInformation = ID(0x125)
+	IDIOReader        = ID(0x126)
+	IDIOWriter        = ID(0x127)
+	IDStatus          = ID(0x128)
+	IDTokenReader     = ID(0x129)
+	IDTokenWriter     = ID(0x12A)
+	IDUtility         = ID(0x12B)
 
 	IDRangeIEU32 = ID(0x130)
 	IDRangeIIU32 = ID(0x131)
@@ -832,17 +833,18 @@
 	IDU32: "u32",
 	IDU64: "u64",
 
-	IDBase:          "base",
-	IDBool:          "bool",
-	IDEmptyIOReader: "empty_io_reader",
-	IDEmptyIOWriter: "empty_io_writer",
-	IDEmptyStruct:   "empty_struct",
-	IDIOReader:      "io_reader",
-	IDIOWriter:      "io_writer",
-	IDStatus:        "status",
-	IDTokenReader:   "token_reader",
-	IDTokenWriter:   "token_writer",
-	IDUtility:       "utility",
+	IDBase:            "base",
+	IDBool:            "bool",
+	IDEmptyIOReader:   "empty_io_reader",
+	IDEmptyIOWriter:   "empty_io_writer",
+	IDEmptyStruct:     "empty_struct",
+	IDMoreInformation: "more_information",
+	IDIOReader:        "io_reader",
+	IDIOWriter:        "io_writer",
+	IDStatus:          "status",
+	IDTokenReader:     "token_reader",
+	IDTokenWriter:     "token_writer",
+	IDUtility:         "utility",
 
 	IDRangeIEU32: "range_ie_u32",
 	IDRangeIIU32: "range_ii_u32",
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 3654cc7..cceb627 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -191,6 +191,9 @@
 
 extern const char* wuffs_base__note__end_of_data;
 extern const char* wuffs_base__note__metadata_reported;
+extern const char* wuffs_base__suspension__even_more_information;
+extern const char* wuffs_base__suspension__mispositioned_read;
+extern const char* wuffs_base__suspension__mispositioned_write;
 extern const char* wuffs_base__suspension__short_read;
 extern const char* wuffs_base__suspension__short_write;
 extern const char* wuffs_base__error__bad_i_o_position;
@@ -208,6 +211,7 @@
 extern const char* wuffs_base__error__initialize_falsely_claimed_already_zeroed;
 extern const char* wuffs_base__error__initialize_not_called;
 extern const char* wuffs_base__error__interleaved_coroutine_calls;
+extern const char* wuffs_base__error__no_more_information;
 extern const char* wuffs_base__error__not_enough_data;
 extern const char* wuffs_base__error__out_of_bounds;
 extern const char* wuffs_base__error__unsupported_method;
@@ -1674,6 +1678,143 @@
 
 #endif  // __cplusplus
 
+// ---------------- More Information
+
+// wuffs_base__more_information holds additional fields, typically when a Wuffs
+// method returns a [note status](/doc/note/statuses.md).
+//
+// The flavor field follows the base38 namespace
+// convention](/doc/note/base38-and-fourcc.md). The other fields' semantics
+// depends on the flavor.
+typedef struct {
+  uint32_t flavor;
+  uint32_t w;
+  uint64_t x;
+  uint64_t y;
+  uint64_t z;
+
+#ifdef __cplusplus
+  inline void set(uint32_t flavor_arg,
+                  uint32_t w_arg,
+                  uint64_t x_arg,
+                  uint64_t y_arg,
+                  uint64_t z_arg);
+  inline uint32_t io_redirect__fourcc() const;
+  inline wuffs_base__range_ie_u64 io_redirect__range() const;
+  inline uint64_t io_seek__position() const;
+  inline uint32_t metadata__fourcc() const;
+  inline wuffs_base__range_ie_u64 metadata__range() const;
+#endif  // __cplusplus
+
+} wuffs_base__more_information;
+
+#define WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_REDIRECT 1
+#define WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_SEEK 2
+#define WUFFS_BASE__MORE_INFORMATION__FLAVOR__METADATA 3
+
+static inline wuffs_base__more_information  //
+wuffs_base__empty_more_information() {
+  wuffs_base__more_information ret;
+  ret.flavor = 0;
+  ret.w = 0;
+  ret.x = 0;
+  ret.y = 0;
+  ret.z = 0;
+  return ret;
+}
+
+static inline void  //
+wuffs_base__more_information__set(wuffs_base__more_information* m,
+                                  uint32_t flavor,
+                                  uint32_t w,
+                                  uint64_t x,
+                                  uint64_t y,
+                                  uint64_t z) {
+  if (!m) {
+    return;
+  }
+  m->flavor = flavor;
+  m->w = w;
+  m->x = x;
+  m->y = y;
+  m->z = z;
+}
+
+static inline uint32_t  //
+wuffs_base__more_information__io_redirect__fourcc(
+    const wuffs_base__more_information* m) {
+  return m->w;
+}
+
+static inline wuffs_base__range_ie_u64  //
+wuffs_base__more_information__io_redirect__range(
+    const wuffs_base__more_information* m) {
+  wuffs_base__range_ie_u64 ret;
+  ret.min_incl = m->y;
+  ret.max_excl = m->z;
+  return ret;
+}
+
+static inline uint64_t  //
+wuffs_base__more_information__io_seek__position(
+    const wuffs_base__more_information* m) {
+  return m->x;
+}
+
+static inline uint32_t  //
+wuffs_base__more_information__metadata__fourcc(
+    const wuffs_base__more_information* m) {
+  return m->w;
+}
+
+static inline wuffs_base__range_ie_u64  //
+wuffs_base__more_information__metadata__range(
+    const wuffs_base__more_information* m) {
+  wuffs_base__range_ie_u64 ret;
+  ret.min_incl = m->y;
+  ret.max_excl = m->z;
+  return ret;
+}
+
+#ifdef __cplusplus
+
+inline void  //
+wuffs_base__more_information::set(uint32_t flavor_arg,
+                                  uint32_t w_arg,
+                                  uint64_t x_arg,
+                                  uint64_t y_arg,
+                                  uint64_t z_arg) {
+  wuffs_base__more_information__set(this, flavor_arg, w_arg, x_arg, y_arg,
+                                    z_arg);
+}
+
+inline uint32_t  //
+wuffs_base__more_information::io_redirect__fourcc() const {
+  return wuffs_base__more_information__io_redirect__fourcc(this);
+}
+
+inline wuffs_base__range_ie_u64  //
+wuffs_base__more_information::io_redirect__range() const {
+  return wuffs_base__more_information__io_redirect__range(this);
+}
+
+inline uint64_t  //
+wuffs_base__more_information::io_seek__position() const {
+  return wuffs_base__more_information__io_seek__position(this);
+}
+
+inline uint32_t  //
+wuffs_base__more_information::metadata__fourcc() const {
+  return wuffs_base__more_information__metadata__fourcc(this);
+}
+
+inline wuffs_base__range_ie_u64  //
+wuffs_base__more_information::metadata__range() const {
+  return wuffs_base__more_information__metadata__range(this);
+}
+
+#endif  // __cplusplus
+
 // ---------------- I/O
 //
 // See (/doc/note/io-input-output.md).
@@ -3879,8 +4020,6 @@
 extern const char* wuffs_base__image_decoder__vtable_name;
 
 typedef struct {
-  wuffs_base__status (*ack_metadata_chunk)(void* self,
-                                           wuffs_base__io_buffer* a_src);
   wuffs_base__status (*decode_frame)(void* self,
                                      wuffs_base__pixel_buffer* a_dst,
                                      wuffs_base__io_buffer* a_src,
@@ -3894,8 +4033,6 @@
                                             wuffs_base__image_config* a_dst,
                                             wuffs_base__io_buffer* a_src);
   wuffs_base__rect_ie_u32 (*frame_dirty_rect)(const void* self);
-  uint64_t (*metadata_chunk_length)(const void* self);
-  uint32_t (*metadata_fourcc)(const void* self);
   uint32_t (*num_animation_loops)(const void* self);
   uint64_t (*num_decoded_frame_configs)(const void* self);
   uint64_t (*num_decoded_frames)(const void* self);
@@ -3908,16 +4045,16 @@
   wuffs_base__empty_struct (*set_report_metadata)(void* self,
                                                   uint32_t a_fourcc,
                                                   bool a_report);
+  wuffs_base__status (*tell_me_more)(void* self,
+                                     wuffs_base__io_buffer* a_dst,
+                                     wuffs_base__more_information* a_minfo,
+                                     wuffs_base__io_buffer* a_src);
   wuffs_base__range_ii_u64 (*workbuf_len)(const void* self);
 } wuffs_base__image_decoder__func_ptrs;
 
 typedef struct wuffs_base__image_decoder__struct wuffs_base__image_decoder;
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
-wuffs_base__image_decoder__ack_metadata_chunk(wuffs_base__image_decoder* self,
-                                              wuffs_base__io_buffer* a_src);
-
-WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_base__image_decoder__decode_frame(
     wuffs_base__image_decoder* self,
     wuffs_base__pixel_buffer* a_dst,
@@ -3940,14 +4077,6 @@
 wuffs_base__image_decoder__frame_dirty_rect(
     const wuffs_base__image_decoder* self);
 
-WUFFS_BASE__MAYBE_STATIC uint64_t  //
-wuffs_base__image_decoder__metadata_chunk_length(
-    const wuffs_base__image_decoder* self);
-
-WUFFS_BASE__MAYBE_STATIC uint32_t  //
-wuffs_base__image_decoder__metadata_fourcc(
-    const wuffs_base__image_decoder* self);
-
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
 wuffs_base__image_decoder__num_animation_loops(
     const wuffs_base__image_decoder* self);
@@ -3975,6 +4104,12 @@
                                                uint32_t a_fourcc,
                                                bool a_report);
 
+WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
+wuffs_base__image_decoder__tell_me_more(wuffs_base__image_decoder* self,
+                                        wuffs_base__io_buffer* a_dst,
+                                        wuffs_base__more_information* a_minfo,
+                                        wuffs_base__io_buffer* a_src);
+
 WUFFS_BASE__MAYBE_STATIC wuffs_base__range_ii_u64  //
 wuffs_base__image_decoder__workbuf_len(const wuffs_base__image_decoder* self);
 
@@ -3994,11 +4129,6 @@
 #endif
 
   inline wuffs_base__status  //
-  ack_metadata_chunk(wuffs_base__io_buffer* a_src) {
-    return wuffs_base__image_decoder__ack_metadata_chunk(this, a_src);
-  }
-
-  inline wuffs_base__status  //
   decode_frame(wuffs_base__pixel_buffer* a_dst,
                wuffs_base__io_buffer* a_src,
                wuffs_base__pixel_blend a_blend,
@@ -4025,16 +4155,6 @@
     return wuffs_base__image_decoder__frame_dirty_rect(this);
   }
 
-  inline uint64_t  //
-  metadata_chunk_length() const {
-    return wuffs_base__image_decoder__metadata_chunk_length(this);
-  }
-
-  inline uint32_t  //
-  metadata_fourcc() const {
-    return wuffs_base__image_decoder__metadata_fourcc(this);
-  }
-
   inline uint32_t  //
   num_animation_loops() const {
     return wuffs_base__image_decoder__num_animation_loops(this);
@@ -4068,6 +4188,13 @@
                                                           a_report);
   }
 
+  inline wuffs_base__status  //
+  tell_me_more(wuffs_base__io_buffer* a_dst,
+               wuffs_base__more_information* a_minfo,
+               wuffs_base__io_buffer* a_src) {
+    return wuffs_base__image_decoder__tell_me_more(this, a_dst, a_minfo, a_src);
+  }
+
   inline wuffs_base__range_ii_u64  //
   workbuf_len() const {
     return wuffs_base__image_decoder__workbuf_len(this);
@@ -4481,19 +4608,9 @@
                                  wuffs_base__slice_u8 a_workbuf,
                                  wuffs_base__decode_frame_options* a_opts);
 
-WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
-wuffs_bmp__decoder__ack_metadata_chunk(wuffs_bmp__decoder* self,
-                                       wuffs_base__io_buffer* a_src);
-
 WUFFS_BASE__MAYBE_STATIC wuffs_base__rect_ie_u32  //
 wuffs_bmp__decoder__frame_dirty_rect(const wuffs_bmp__decoder* self);
 
-WUFFS_BASE__MAYBE_STATIC uint64_t  //
-wuffs_bmp__decoder__metadata_chunk_length(const wuffs_bmp__decoder* self);
-
-WUFFS_BASE__MAYBE_STATIC uint32_t  //
-wuffs_bmp__decoder__metadata_fourcc(const wuffs_bmp__decoder* self);
-
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
 wuffs_bmp__decoder__num_animation_loops(const wuffs_bmp__decoder* self);
 
@@ -4513,6 +4630,12 @@
                                         uint32_t a_fourcc,
                                         bool a_report);
 
+WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
+wuffs_bmp__decoder__tell_me_more(wuffs_bmp__decoder* self,
+                                 wuffs_base__io_buffer* a_dst,
+                                 wuffs_base__more_information* a_minfo,
+                                 wuffs_base__io_buffer* a_src);
+
 WUFFS_BASE__MAYBE_STATIC wuffs_base__range_ii_u64  //
 wuffs_bmp__decoder__workbuf_len(const wuffs_bmp__decoder* self);
 
@@ -4670,26 +4793,11 @@
                                             a_workbuf, a_opts);
   }
 
-  inline wuffs_base__status  //
-  ack_metadata_chunk(wuffs_base__io_buffer* a_src) {
-    return wuffs_bmp__decoder__ack_metadata_chunk(this, a_src);
-  }
-
   inline wuffs_base__rect_ie_u32  //
   frame_dirty_rect() const {
     return wuffs_bmp__decoder__frame_dirty_rect(this);
   }
 
-  inline uint64_t  //
-  metadata_chunk_length() const {
-    return wuffs_bmp__decoder__metadata_chunk_length(this);
-  }
-
-  inline uint32_t  //
-  metadata_fourcc() const {
-    return wuffs_bmp__decoder__metadata_fourcc(this);
-  }
-
   inline uint32_t  //
   num_animation_loops() const {
     return wuffs_bmp__decoder__num_animation_loops(this);
@@ -4715,6 +4823,13 @@
     return wuffs_bmp__decoder__set_report_metadata(this, a_fourcc, a_report);
   }
 
+  inline wuffs_base__status  //
+  tell_me_more(wuffs_base__io_buffer* a_dst,
+               wuffs_base__more_information* a_minfo,
+               wuffs_base__io_buffer* a_src) {
+    return wuffs_bmp__decoder__tell_me_more(this, a_dst, a_minfo, a_src);
+  }
+
   inline wuffs_base__range_ii_u64  //
   workbuf_len() const {
     return wuffs_bmp__decoder__workbuf_len(this);
@@ -5505,16 +5620,10 @@
                                                bool a_report);
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
-wuffs_gif__config_decoder__ack_metadata_chunk(wuffs_gif__config_decoder* self,
-                                              wuffs_base__io_buffer* a_src);
-
-WUFFS_BASE__MAYBE_STATIC uint32_t  //
-wuffs_gif__config_decoder__metadata_fourcc(
-    const wuffs_gif__config_decoder* self);
-
-WUFFS_BASE__MAYBE_STATIC uint64_t  //
-wuffs_gif__config_decoder__metadata_chunk_length(
-    const wuffs_gif__config_decoder* self);
+wuffs_gif__config_decoder__tell_me_more(wuffs_gif__config_decoder* self,
+                                        wuffs_base__io_buffer* a_dst,
+                                        wuffs_base__more_information* a_minfo,
+                                        wuffs_base__io_buffer* a_src);
 
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
 wuffs_gif__config_decoder__num_animation_loops(
@@ -5570,14 +5679,10 @@
                                         bool a_report);
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
-wuffs_gif__decoder__ack_metadata_chunk(wuffs_gif__decoder* self,
-                                       wuffs_base__io_buffer* a_src);
-
-WUFFS_BASE__MAYBE_STATIC uint32_t  //
-wuffs_gif__decoder__metadata_fourcc(const wuffs_gif__decoder* self);
-
-WUFFS_BASE__MAYBE_STATIC uint64_t  //
-wuffs_gif__decoder__metadata_chunk_length(const wuffs_gif__decoder* self);
+wuffs_gif__decoder__tell_me_more(wuffs_gif__decoder* self,
+                                 wuffs_base__io_buffer* a_dst,
+                                 wuffs_base__more_information* a_minfo,
+                                 wuffs_base__io_buffer* a_src);
 
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
 wuffs_gif__decoder__num_animation_loops(const wuffs_gif__decoder* self);
@@ -5641,8 +5746,7 @@
     bool f_ignore_metadata;
     bool f_report_metadata_iccp;
     bool f_report_metadata_xmp;
-    uint32_t f_metadata_fourcc_value;
-    uint64_t f_metadata_chunk_length_value;
+    uint32_t f_metadata_fourcc;
     uint64_t f_metadata_io_position;
     bool f_quirks[7];
     bool f_delayed_num_decoded_frames;
@@ -5668,7 +5772,7 @@
     uint32_t f_frame_rect_y1;
 
     uint32_t p_decode_image_config[1];
-    uint32_t p_ack_metadata_chunk[1];
+    uint32_t p_tell_me_more[1];
     uint32_t p_decode_frame_config[1];
     uint32_t p_skip_frame[1];
     uint32_t p_decode_up_to_id_part1[1];
@@ -5801,18 +5905,10 @@
   }
 
   inline wuffs_base__status  //
-  ack_metadata_chunk(wuffs_base__io_buffer* a_src) {
-    return wuffs_gif__config_decoder__ack_metadata_chunk(this, a_src);
-  }
-
-  inline uint32_t  //
-  metadata_fourcc() const {
-    return wuffs_gif__config_decoder__metadata_fourcc(this);
-  }
-
-  inline uint64_t  //
-  metadata_chunk_length() const {
-    return wuffs_gif__config_decoder__metadata_chunk_length(this);
+  tell_me_more(wuffs_base__io_buffer* a_dst,
+               wuffs_base__more_information* a_minfo,
+               wuffs_base__io_buffer* a_src) {
+    return wuffs_gif__config_decoder__tell_me_more(this, a_dst, a_minfo, a_src);
   }
 
   inline uint32_t  //
@@ -5886,8 +5982,7 @@
     bool f_ignore_metadata;
     bool f_report_metadata_iccp;
     bool f_report_metadata_xmp;
-    uint32_t f_metadata_fourcc_value;
-    uint64_t f_metadata_chunk_length_value;
+    uint32_t f_metadata_fourcc;
     uint64_t f_metadata_io_position;
     bool f_quirks[7];
     bool f_delayed_num_decoded_frames;
@@ -5919,7 +6014,7 @@
     wuffs_base__pixel_swizzler f_swizzler;
 
     uint32_t p_decode_image_config[1];
-    uint32_t p_ack_metadata_chunk[1];
+    uint32_t p_tell_me_more[1];
     uint32_t p_decode_frame_config[1];
     uint32_t p_skip_frame[1];
     uint32_t p_decode_frame[1];
@@ -6065,18 +6160,10 @@
   }
 
   inline wuffs_base__status  //
-  ack_metadata_chunk(wuffs_base__io_buffer* a_src) {
-    return wuffs_gif__decoder__ack_metadata_chunk(this, a_src);
-  }
-
-  inline uint32_t  //
-  metadata_fourcc() const {
-    return wuffs_gif__decoder__metadata_fourcc(this);
-  }
-
-  inline uint64_t  //
-  metadata_chunk_length() const {
-    return wuffs_gif__decoder__metadata_chunk_length(this);
+  tell_me_more(wuffs_base__io_buffer* a_dst,
+               wuffs_base__more_information* a_minfo,
+               wuffs_base__io_buffer* a_src) {
+    return wuffs_gif__decoder__tell_me_more(this, a_dst, a_minfo, a_src);
   }
 
   inline uint32_t  //
@@ -6682,19 +6769,9 @@
                                   wuffs_base__slice_u8 a_workbuf,
                                   wuffs_base__decode_frame_options* a_opts);
 
-WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
-wuffs_wbmp__decoder__ack_metadata_chunk(wuffs_wbmp__decoder* self,
-                                        wuffs_base__io_buffer* a_src);
-
 WUFFS_BASE__MAYBE_STATIC wuffs_base__rect_ie_u32  //
 wuffs_wbmp__decoder__frame_dirty_rect(const wuffs_wbmp__decoder* self);
 
-WUFFS_BASE__MAYBE_STATIC uint64_t  //
-wuffs_wbmp__decoder__metadata_chunk_length(const wuffs_wbmp__decoder* self);
-
-WUFFS_BASE__MAYBE_STATIC uint32_t  //
-wuffs_wbmp__decoder__metadata_fourcc(const wuffs_wbmp__decoder* self);
-
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
 wuffs_wbmp__decoder__num_animation_loops(const wuffs_wbmp__decoder* self);
 
@@ -6714,6 +6791,12 @@
                                          uint32_t a_fourcc,
                                          bool a_report);
 
+WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
+wuffs_wbmp__decoder__tell_me_more(wuffs_wbmp__decoder* self,
+                                  wuffs_base__io_buffer* a_dst,
+                                  wuffs_base__more_information* a_minfo,
+                                  wuffs_base__io_buffer* a_src);
+
 WUFFS_BASE__MAYBE_STATIC wuffs_base__range_ii_u64  //
 wuffs_wbmp__decoder__workbuf_len(const wuffs_wbmp__decoder* self);
 
@@ -6854,26 +6937,11 @@
                                              a_workbuf, a_opts);
   }
 
-  inline wuffs_base__status  //
-  ack_metadata_chunk(wuffs_base__io_buffer* a_src) {
-    return wuffs_wbmp__decoder__ack_metadata_chunk(this, a_src);
-  }
-
   inline wuffs_base__rect_ie_u32  //
   frame_dirty_rect() const {
     return wuffs_wbmp__decoder__frame_dirty_rect(this);
   }
 
-  inline uint64_t  //
-  metadata_chunk_length() const {
-    return wuffs_wbmp__decoder__metadata_chunk_length(this);
-  }
-
-  inline uint32_t  //
-  metadata_fourcc() const {
-    return wuffs_wbmp__decoder__metadata_fourcc(this);
-  }
-
   inline uint32_t  //
   num_animation_loops() const {
     return wuffs_wbmp__decoder__num_animation_loops(this);
@@ -6899,6 +6967,13 @@
     return wuffs_wbmp__decoder__set_report_metadata(this, a_fourcc, a_report);
   }
 
+  inline wuffs_base__status  //
+  tell_me_more(wuffs_base__io_buffer* a_dst,
+               wuffs_base__more_information* a_minfo,
+               wuffs_base__io_buffer* a_src) {
+    return wuffs_wbmp__decoder__tell_me_more(this, a_dst, a_minfo, a_src);
+  }
+
   inline wuffs_base__range_ii_u64  //
   workbuf_len() const {
     return wuffs_wbmp__decoder__workbuf_len(this);
@@ -7682,6 +7757,12 @@
 
 const char* wuffs_base__note__end_of_data = "@base: end of data";
 const char* wuffs_base__note__metadata_reported = "@base: metadata reported";
+const char* wuffs_base__suspension__even_more_information =
+    "$base: even more information";
+const char* wuffs_base__suspension__mispositioned_read =
+    "$base: mispositioned read";
+const char* wuffs_base__suspension__mispositioned_write =
+    "$base: mispositioned write";
 const char* wuffs_base__suspension__short_read = "$base: short read";
 const char* wuffs_base__suspension__short_write = "$base: short write";
 const char* wuffs_base__error__bad_i_o_position = "#base: bad I/O position";
@@ -7706,6 +7787,8 @@
     "#base: initialize not called";
 const char* wuffs_base__error__interleaved_coroutine_calls =
     "#base: interleaved coroutine calls";
+const char* wuffs_base__error__no_more_information =
+    "#base: no more information";
 const char* wuffs_base__error__not_enough_data = "#base: not enough data";
 const char* wuffs_base__error__out_of_bounds = "#base: out of bounds";
 const char* wuffs_base__error__unsupported_method = "#base: unsupported method";
@@ -7778,35 +7861,6 @@
     "{vtable}wuffs_base__image_decoder";
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
-wuffs_base__image_decoder__ack_metadata_chunk(wuffs_base__image_decoder* self,
-                                              wuffs_base__io_buffer* a_src) {
-  if (!self) {
-    return wuffs_base__make_status(wuffs_base__error__bad_receiver);
-  }
-  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
-    return wuffs_base__make_status(
-        (self->private_impl.magic == WUFFS_BASE__DISABLED)
-            ? wuffs_base__error__disabled_by_previous_error
-            : wuffs_base__error__initialize_not_called);
-  }
-
-  const wuffs_base__vtable* v = &self->private_impl.first_vtable;
-  int i;
-  for (i = 0; i < 63; i++) {
-    if (v->vtable_name == wuffs_base__image_decoder__vtable_name) {
-      const wuffs_base__image_decoder__func_ptrs* func_ptrs =
-          (const wuffs_base__image_decoder__func_ptrs*)(v->function_pointers);
-      return (*func_ptrs->ack_metadata_chunk)(self, a_src);
-    } else if (v->vtable_name == NULL) {
-      break;
-    }
-    v++;
-  }
-
-  return wuffs_base__make_status(wuffs_base__error__bad_vtable);
-}
-
-WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
 wuffs_base__image_decoder__decode_frame(
     wuffs_base__image_decoder* self,
     wuffs_base__pixel_buffer* a_dst,
@@ -7928,60 +7982,6 @@
   return wuffs_base__utility__empty_rect_ie_u32();
 }
 
-WUFFS_BASE__MAYBE_STATIC uint64_t  //
-wuffs_base__image_decoder__metadata_chunk_length(
-    const wuffs_base__image_decoder* self) {
-  if (!self) {
-    return 0;
-  }
-  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
-      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
-    return 0;
-  }
-
-  const wuffs_base__vtable* v = &self->private_impl.first_vtable;
-  int i;
-  for (i = 0; i < 63; i++) {
-    if (v->vtable_name == wuffs_base__image_decoder__vtable_name) {
-      const wuffs_base__image_decoder__func_ptrs* func_ptrs =
-          (const wuffs_base__image_decoder__func_ptrs*)(v->function_pointers);
-      return (*func_ptrs->metadata_chunk_length)(self);
-    } else if (v->vtable_name == NULL) {
-      break;
-    }
-    v++;
-  }
-
-  return 0;
-}
-
-WUFFS_BASE__MAYBE_STATIC uint32_t  //
-wuffs_base__image_decoder__metadata_fourcc(
-    const wuffs_base__image_decoder* self) {
-  if (!self) {
-    return 0;
-  }
-  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
-      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
-    return 0;
-  }
-
-  const wuffs_base__vtable* v = &self->private_impl.first_vtable;
-  int i;
-  for (i = 0; i < 63; i++) {
-    if (v->vtable_name == wuffs_base__image_decoder__vtable_name) {
-      const wuffs_base__image_decoder__func_ptrs* func_ptrs =
-          (const wuffs_base__image_decoder__func_ptrs*)(v->function_pointers);
-      return (*func_ptrs->metadata_fourcc)(self);
-    } else if (v->vtable_name == NULL) {
-      break;
-    }
-    v++;
-  }
-
-  return 0;
-}
-
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
 wuffs_base__image_decoder__num_animation_loops(
     const wuffs_base__image_decoder* self) {
@@ -8147,6 +8147,37 @@
   return wuffs_base__make_empty_struct();
 }
 
+WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
+wuffs_base__image_decoder__tell_me_more(wuffs_base__image_decoder* self,
+                                        wuffs_base__io_buffer* a_dst,
+                                        wuffs_base__more_information* a_minfo,
+                                        wuffs_base__io_buffer* a_src) {
+  if (!self) {
+    return wuffs_base__make_status(wuffs_base__error__bad_receiver);
+  }
+  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
+    return wuffs_base__make_status(
+        (self->private_impl.magic == WUFFS_BASE__DISABLED)
+            ? wuffs_base__error__disabled_by_previous_error
+            : wuffs_base__error__initialize_not_called);
+  }
+
+  const wuffs_base__vtable* v = &self->private_impl.first_vtable;
+  int i;
+  for (i = 0; i < 63; i++) {
+    if (v->vtable_name == wuffs_base__image_decoder__vtable_name) {
+      const wuffs_base__image_decoder__func_ptrs* func_ptrs =
+          (const wuffs_base__image_decoder__func_ptrs*)(v->function_pointers);
+      return (*func_ptrs->tell_me_more)(self, a_dst, a_minfo, a_src);
+    } else if (v->vtable_name == NULL) {
+      break;
+    }
+    v++;
+  }
+
+  return wuffs_base__make_status(wuffs_base__error__bad_vtable);
+}
+
 WUFFS_BASE__MAYBE_STATIC wuffs_base__range_ii_u64  //
 wuffs_base__image_decoder__workbuf_len(const wuffs_base__image_decoder* self) {
   if (!self) {
@@ -11272,8 +11303,6 @@
 
 const wuffs_base__image_decoder__func_ptrs
     wuffs_bmp__decoder__func_ptrs_for__wuffs_base__image_decoder = {
-        (wuffs_base__status(*)(void*, wuffs_base__io_buffer*))(
-            &wuffs_bmp__decoder__ack_metadata_chunk),
         (wuffs_base__status(*)(void*,
                                wuffs_base__pixel_buffer*,
                                wuffs_base__io_buffer*,
@@ -11291,8 +11320,6 @@
             &wuffs_bmp__decoder__decode_image_config),
         (wuffs_base__rect_ie_u32(*)(const void*))(
             &wuffs_bmp__decoder__frame_dirty_rect),
-        (uint64_t(*)(const void*))(&wuffs_bmp__decoder__metadata_chunk_length),
-        (uint32_t(*)(const void*))(&wuffs_bmp__decoder__metadata_fourcc),
         (uint32_t(*)(const void*))(&wuffs_bmp__decoder__num_animation_loops),
         (uint64_t(*)(const void*))(
             &wuffs_bmp__decoder__num_decoded_frame_configs),
@@ -11303,6 +11330,11 @@
             &wuffs_bmp__decoder__set_quirk_enabled),
         (wuffs_base__empty_struct(*)(void*, uint32_t, bool))(
             &wuffs_bmp__decoder__set_report_metadata),
+        (wuffs_base__status(*)(void*,
+                               wuffs_base__io_buffer*,
+                               wuffs_base__more_information*,
+                               wuffs_base__io_buffer*))(
+            &wuffs_bmp__decoder__tell_me_more),
         (wuffs_base__range_ii_u64(*)(const void*))(
             &wuffs_bmp__decoder__workbuf_len),
 };
@@ -12450,45 +12482,6 @@
   return status;
 }
 
-// -------- func bmp.decoder.ack_metadata_chunk
-
-WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
-wuffs_bmp__decoder__ack_metadata_chunk(wuffs_bmp__decoder* self,
-                                       wuffs_base__io_buffer* a_src) {
-  if (!self) {
-    return wuffs_base__make_status(wuffs_base__error__bad_receiver);
-  }
-  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
-    return wuffs_base__make_status(
-        (self->private_impl.magic == WUFFS_BASE__DISABLED)
-            ? wuffs_base__error__disabled_by_previous_error
-            : wuffs_base__error__initialize_not_called);
-  }
-  if (!a_src) {
-    self->private_impl.magic = WUFFS_BASE__DISABLED;
-    return wuffs_base__make_status(wuffs_base__error__bad_argument);
-  }
-  if ((self->private_impl.active_coroutine != 0) &&
-      (self->private_impl.active_coroutine != 4)) {
-    self->private_impl.magic = WUFFS_BASE__DISABLED;
-    return wuffs_base__make_status(
-        wuffs_base__error__interleaved_coroutine_calls);
-  }
-  self->private_impl.active_coroutine = 0;
-  wuffs_base__status status = wuffs_base__make_status(NULL);
-
-  status = wuffs_base__make_status(NULL);
-  goto ok;
-  goto ok;
-ok:
-  goto exit;
-exit:
-  if (wuffs_base__status__is_error(&status)) {
-    self->private_impl.magic = WUFFS_BASE__DISABLED;
-  }
-  return status;
-}
-
 // -------- func bmp.decoder.frame_dirty_rect
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__rect_ie_u32  //
@@ -12505,36 +12498,6 @@
                                                self->private_impl.f_height);
 }
 
-// -------- func bmp.decoder.metadata_chunk_length
-
-WUFFS_BASE__MAYBE_STATIC uint64_t  //
-wuffs_bmp__decoder__metadata_chunk_length(const wuffs_bmp__decoder* self) {
-  if (!self) {
-    return 0;
-  }
-  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
-      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
-    return 0;
-  }
-
-  return 0;
-}
-
-// -------- func bmp.decoder.metadata_fourcc
-
-WUFFS_BASE__MAYBE_STATIC uint32_t  //
-wuffs_bmp__decoder__metadata_fourcc(const wuffs_bmp__decoder* self) {
-  if (!self) {
-    return 0;
-  }
-  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
-      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
-    return 0;
-  }
-
-  return 0;
-}
-
 // -------- func bmp.decoder.num_animation_loops
 
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
@@ -12622,6 +12585,47 @@
   return wuffs_base__make_empty_struct();
 }
 
+// -------- func bmp.decoder.tell_me_more
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
+wuffs_bmp__decoder__tell_me_more(wuffs_bmp__decoder* self,
+                                 wuffs_base__io_buffer* a_dst,
+                                 wuffs_base__more_information* a_minfo,
+                                 wuffs_base__io_buffer* a_src) {
+  if (!self) {
+    return wuffs_base__make_status(wuffs_base__error__bad_receiver);
+  }
+  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
+    return wuffs_base__make_status(
+        (self->private_impl.magic == WUFFS_BASE__DISABLED)
+            ? wuffs_base__error__disabled_by_previous_error
+            : wuffs_base__error__initialize_not_called);
+  }
+  if (!a_dst || !a_src) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__make_status(wuffs_base__error__bad_argument);
+  }
+  if ((self->private_impl.active_coroutine != 0) &&
+      (self->private_impl.active_coroutine != 4)) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__make_status(
+        wuffs_base__error__interleaved_coroutine_calls);
+  }
+  self->private_impl.active_coroutine = 0;
+  wuffs_base__status status = wuffs_base__make_status(NULL);
+
+  status = wuffs_base__make_status(wuffs_base__error__no_more_information);
+  goto exit;
+  goto ok;
+ok:
+  goto exit;
+exit:
+  if (wuffs_base__status__is_error(&status)) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+  }
+  return status;
+}
+
 // -------- func bmp.decoder.workbuf_len
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__range_ii_u64  //
@@ -16423,8 +16427,6 @@
 
 const wuffs_base__image_decoder__func_ptrs
     wuffs_gif__config_decoder__func_ptrs_for__wuffs_base__image_decoder = {
-        (wuffs_base__status(*)(void*, wuffs_base__io_buffer*))(
-            &wuffs_gif__config_decoder__ack_metadata_chunk),
         (wuffs_base__status(*)(void*,
                                wuffs_base__pixel_buffer*,
                                wuffs_base__io_buffer*,
@@ -16442,9 +16444,6 @@
             &wuffs_gif__config_decoder__decode_image_config),
         (wuffs_base__rect_ie_u32(*)(const void*))(
             &wuffs_gif__config_decoder__frame_dirty_rect),
-        (uint64_t(*)(const void*))(
-            &wuffs_gif__config_decoder__metadata_chunk_length),
-        (uint32_t(*)(const void*))(&wuffs_gif__config_decoder__metadata_fourcc),
         (uint32_t(*)(const void*))(
             &wuffs_gif__config_decoder__num_animation_loops),
         (uint64_t(*)(const void*))(
@@ -16457,14 +16456,17 @@
             &wuffs_gif__config_decoder__set_quirk_enabled),
         (wuffs_base__empty_struct(*)(void*, uint32_t, bool))(
             &wuffs_gif__config_decoder__set_report_metadata),
+        (wuffs_base__status(*)(void*,
+                               wuffs_base__io_buffer*,
+                               wuffs_base__more_information*,
+                               wuffs_base__io_buffer*))(
+            &wuffs_gif__config_decoder__tell_me_more),
         (wuffs_base__range_ii_u64(*)(const void*))(
             &wuffs_gif__config_decoder__workbuf_len),
 };
 
 const wuffs_base__image_decoder__func_ptrs
     wuffs_gif__decoder__func_ptrs_for__wuffs_base__image_decoder = {
-        (wuffs_base__status(*)(void*, wuffs_base__io_buffer*))(
-            &wuffs_gif__decoder__ack_metadata_chunk),
         (wuffs_base__status(*)(void*,
                                wuffs_base__pixel_buffer*,
                                wuffs_base__io_buffer*,
@@ -16482,8 +16484,6 @@
             &wuffs_gif__decoder__decode_image_config),
         (wuffs_base__rect_ie_u32(*)(const void*))(
             &wuffs_gif__decoder__frame_dirty_rect),
-        (uint64_t(*)(const void*))(&wuffs_gif__decoder__metadata_chunk_length),
-        (uint32_t(*)(const void*))(&wuffs_gif__decoder__metadata_fourcc),
         (uint32_t(*)(const void*))(&wuffs_gif__decoder__num_animation_loops),
         (uint64_t(*)(const void*))(
             &wuffs_gif__decoder__num_decoded_frame_configs),
@@ -16494,6 +16494,11 @@
             &wuffs_gif__decoder__set_quirk_enabled),
         (wuffs_base__empty_struct(*)(void*, uint32_t, bool))(
             &wuffs_gif__decoder__set_report_metadata),
+        (wuffs_base__status(*)(void*,
+                               wuffs_base__io_buffer*,
+                               wuffs_base__more_information*,
+                               wuffs_base__io_buffer*))(
+            &wuffs_gif__decoder__tell_me_more),
         (wuffs_base__range_ii_u64(*)(const void*))(
             &wuffs_gif__decoder__workbuf_len),
 };
@@ -16790,11 +16795,13 @@
   return wuffs_base__make_empty_struct();
 }
 
-// -------- func gif.config_decoder.ack_metadata_chunk
+// -------- func gif.config_decoder.tell_me_more
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
-wuffs_gif__config_decoder__ack_metadata_chunk(wuffs_gif__config_decoder* self,
-                                              wuffs_base__io_buffer* a_src) {
+wuffs_gif__config_decoder__tell_me_more(wuffs_gif__config_decoder* self,
+                                        wuffs_base__io_buffer* a_dst,
+                                        wuffs_base__more_information* a_minfo,
+                                        wuffs_base__io_buffer* a_src) {
   if (!self) {
     return wuffs_base__make_status(wuffs_base__error__bad_receiver);
   }
@@ -16804,7 +16811,7 @@
             ? wuffs_base__error__disabled_by_previous_error
             : wuffs_base__error__initialize_not_called);
   }
-  if (!a_src) {
+  if (!a_dst || !a_src) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
     return wuffs_base__make_status(wuffs_base__error__bad_argument);
   }
@@ -16817,6 +16824,8 @@
   self->private_impl.active_coroutine = 0;
   wuffs_base__status status = wuffs_base__make_status(NULL);
 
+  uint64_t v_chunk_length = 0;
+
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
@@ -16828,7 +16837,7 @@
     io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
-  uint32_t coro_susp_point = self->private_impl.p_ack_metadata_chunk[0];
+  uint32_t coro_susp_point = self->private_impl.p_tell_me_more[0];
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
@@ -16836,48 +16845,83 @@
       status = wuffs_base__make_status(wuffs_base__error__bad_call_sequence);
       goto exit;
     }
-    if (wuffs_base__u64__sat_add(a_src->meta.pos,
-                                 ((uint64_t)(iop_a_src - io0_a_src))) !=
-        self->private_impl.f_metadata_io_position) {
-      status = wuffs_base__make_status(wuffs_base__error__bad_i_o_position);
+    if (self->private_impl.f_metadata_fourcc == 0) {
+      status = wuffs_base__make_status(wuffs_base__error__no_more_information);
       goto exit;
     }
-    if (self->private_impl.f_metadata_chunk_length_value > 0) {
-      while (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
-        status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(1);
-      }
-      self->private_impl.f_metadata_chunk_length_value =
-          ((uint64_t)(wuffs_base__load_u8be__no_bounds_check(iop_a_src)));
-      if (self->private_impl.f_metadata_chunk_length_value > 0) {
-        if (self->private_impl.f_metadata_fourcc_value == 1481461792) {
-          self->private_impl.f_metadata_chunk_length_value += 1;
-        } else {
-          (iop_a_src += 1, wuffs_base__make_empty_struct());
+    while (true) {
+    label__0__continue:;
+      while (true) {
+        if (wuffs_base__u64__sat_add(a_src->meta.pos,
+                                     ((uint64_t)(iop_a_src - io0_a_src))) !=
+            self->private_impl.f_metadata_io_position) {
+          if (a_minfo != NULL) {
+            wuffs_base__more_information__set(
+                a_minfo, 2, 0, self->private_impl.f_metadata_io_position, 0, 0);
+          }
+          status = wuffs_base__make_status(
+              wuffs_base__suspension__mispositioned_read);
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(1);
+          goto label__0__continue;
         }
-        self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
+        if (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
+          if (a_minfo != NULL) {
+            wuffs_base__more_information__set(a_minfo, 0, 0, 0, 0, 0);
+          }
+          status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(2);
+          goto label__0__continue;
+        }
+        goto label__0__break;
+      }
+    label__0__break:;
+      v_chunk_length =
+          ((uint64_t)(wuffs_base__load_u8be__no_bounds_check(iop_a_src)));
+      if (v_chunk_length <= 0) {
+        (iop_a_src += 1, wuffs_base__make_empty_struct());
+        goto label__1__break;
+      }
+      if (self->private_impl.f_metadata_fourcc == 1481461792) {
+        v_chunk_length += 1;
+      } else {
+        (iop_a_src += 1, wuffs_base__make_empty_struct());
+      }
+      self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
+          wuffs_base__u64__sat_add(a_src->meta.pos,
+                                   ((uint64_t)(iop_a_src - io0_a_src))),
+          v_chunk_length);
+      if (a_minfo != NULL) {
+        wuffs_base__more_information__set(
+            a_minfo, 3, self->private_impl.f_metadata_fourcc, 0,
             wuffs_base__u64__sat_add(a_src->meta.pos,
                                      ((uint64_t)(iop_a_src - io0_a_src))),
-            self->private_impl.f_metadata_chunk_length_value);
-        status = wuffs_base__make_status(wuffs_base__note__metadata_reported);
-        goto ok;
+            self->private_impl.f_metadata_io_position);
       }
-      (iop_a_src += 1, wuffs_base__make_empty_struct());
+      status = wuffs_base__make_status(
+          wuffs_base__suspension__even_more_information);
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(3);
+    }
+  label__1__break:;
+    if (a_minfo != NULL) {
+      wuffs_base__more_information__set(
+          a_minfo, 3, self->private_impl.f_metadata_fourcc, 0,
+          self->private_impl.f_metadata_io_position,
+          self->private_impl.f_metadata_io_position);
     }
     self->private_impl.f_call_sequence = 2;
-    self->private_impl.f_metadata_fourcc_value = 0;
+    self->private_impl.f_metadata_fourcc = 0;
     self->private_impl.f_metadata_io_position = 0;
     status = wuffs_base__make_status(NULL);
     goto ok;
     goto ok;
   ok:
-    self->private_impl.p_ack_metadata_chunk[0] = 0;
+    self->private_impl.p_tell_me_more[0] = 0;
     goto exit;
   }
 
   goto suspend;
 suspend:
-  self->private_impl.p_ack_metadata_chunk[0] =
+  self->private_impl.p_tell_me_more[0] =
       wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
   self->private_impl.active_coroutine =
       wuffs_base__status__is_suspension(&status) ? 2 : 0;
@@ -16894,38 +16938,6 @@
   return status;
 }
 
-// -------- func gif.config_decoder.metadata_fourcc
-
-WUFFS_BASE__MAYBE_STATIC uint32_t  //
-wuffs_gif__config_decoder__metadata_fourcc(
-    const wuffs_gif__config_decoder* self) {
-  if (!self) {
-    return 0;
-  }
-  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
-      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
-    return 0;
-  }
-
-  return self->private_impl.f_metadata_fourcc_value;
-}
-
-// -------- func gif.config_decoder.metadata_chunk_length
-
-WUFFS_BASE__MAYBE_STATIC uint64_t  //
-wuffs_gif__config_decoder__metadata_chunk_length(
-    const wuffs_gif__config_decoder* self) {
-  if (!self) {
-    return 0;
-  }
-  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
-      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
-    return 0;
-  }
-
-  return self->private_impl.f_metadata_chunk_length_value;
-}
-
 // -------- func gif.config_decoder.num_animation_loops
 
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
@@ -17960,6 +17972,10 @@
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
     while (true) {
+      if (self->private_impl.f_metadata_fourcc != 0) {
+        status = wuffs_base__make_status(wuffs_base__note__metadata_reported);
+        goto ok;
+      }
       {
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
         if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
@@ -18109,38 +18125,16 @@
         }
       } else if (self->private_impl.f_ignore_metadata) {
       } else if (v_is_iccp && self->private_impl.f_report_metadata_iccp) {
-        while (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
-          status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(10);
-        }
-        self->private_impl.f_metadata_chunk_length_value =
-            ((uint64_t)(wuffs_base__load_u8be__no_bounds_check(iop_a_src)));
-        (iop_a_src += 1, wuffs_base__make_empty_struct());
-        self->private_impl.f_metadata_fourcc_value = 1229144912;
+        self->private_impl.f_metadata_fourcc = 1229144912;
         self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
-            wuffs_base__u64__sat_add(a_src->meta.pos,
-                                     ((uint64_t)(iop_a_src - io0_a_src))),
-            self->private_impl.f_metadata_chunk_length_value);
+            a_src->meta.pos, ((uint64_t)(iop_a_src - io0_a_src)));
         self->private_impl.f_call_sequence = 1;
         status = wuffs_base__make_status(wuffs_base__note__metadata_reported);
         goto ok;
       } else if (v_is_xmp && self->private_impl.f_report_metadata_xmp) {
-        while (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
-          status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(11);
-        }
-        self->private_impl.f_metadata_chunk_length_value =
-            ((uint64_t)(wuffs_base__load_u8be__no_bounds_check(iop_a_src)));
-        if (self->private_impl.f_metadata_chunk_length_value > 0) {
-          self->private_impl.f_metadata_chunk_length_value += 1;
-        } else {
-          (iop_a_src += 1, wuffs_base__make_empty_struct());
-        }
-        self->private_impl.f_metadata_fourcc_value = 1481461792;
+        self->private_impl.f_metadata_fourcc = 1481461792;
         self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
-            wuffs_base__u64__sat_add(a_src->meta.pos,
-                                     ((uint64_t)(iop_a_src - io0_a_src))),
-            self->private_impl.f_metadata_chunk_length_value);
+            a_src->meta.pos, ((uint64_t)(iop_a_src - io0_a_src)));
         self->private_impl.f_call_sequence = 1;
         status = wuffs_base__make_status(wuffs_base__note__metadata_reported);
         goto ok;
@@ -18151,7 +18145,7 @@
     if (a_src) {
       a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
     }
-    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(12);
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(10);
     status = wuffs_gif__config_decoder__skip_blocks(self, a_src);
     if (a_src) {
       iop_a_src = a_src->data.ptr + a_src->meta.ri;
@@ -18629,11 +18623,13 @@
   return wuffs_base__make_empty_struct();
 }
 
-// -------- func gif.decoder.ack_metadata_chunk
+// -------- func gif.decoder.tell_me_more
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
-wuffs_gif__decoder__ack_metadata_chunk(wuffs_gif__decoder* self,
-                                       wuffs_base__io_buffer* a_src) {
+wuffs_gif__decoder__tell_me_more(wuffs_gif__decoder* self,
+                                 wuffs_base__io_buffer* a_dst,
+                                 wuffs_base__more_information* a_minfo,
+                                 wuffs_base__io_buffer* a_src) {
   if (!self) {
     return wuffs_base__make_status(wuffs_base__error__bad_receiver);
   }
@@ -18643,7 +18639,7 @@
             ? wuffs_base__error__disabled_by_previous_error
             : wuffs_base__error__initialize_not_called);
   }
-  if (!a_src) {
+  if (!a_dst || !a_src) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
     return wuffs_base__make_status(wuffs_base__error__bad_argument);
   }
@@ -18656,6 +18652,8 @@
   self->private_impl.active_coroutine = 0;
   wuffs_base__status status = wuffs_base__make_status(NULL);
 
+  uint64_t v_chunk_length = 0;
+
   uint8_t* iop_a_src = NULL;
   uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
   uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
@@ -18667,7 +18665,7 @@
     io2_a_src = io0_a_src + a_src->meta.wi;
   }
 
-  uint32_t coro_susp_point = self->private_impl.p_ack_metadata_chunk[0];
+  uint32_t coro_susp_point = self->private_impl.p_tell_me_more[0];
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
@@ -18675,48 +18673,83 @@
       status = wuffs_base__make_status(wuffs_base__error__bad_call_sequence);
       goto exit;
     }
-    if (wuffs_base__u64__sat_add(a_src->meta.pos,
-                                 ((uint64_t)(iop_a_src - io0_a_src))) !=
-        self->private_impl.f_metadata_io_position) {
-      status = wuffs_base__make_status(wuffs_base__error__bad_i_o_position);
+    if (self->private_impl.f_metadata_fourcc == 0) {
+      status = wuffs_base__make_status(wuffs_base__error__no_more_information);
       goto exit;
     }
-    if (self->private_impl.f_metadata_chunk_length_value > 0) {
-      while (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
-        status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(1);
-      }
-      self->private_impl.f_metadata_chunk_length_value =
-          ((uint64_t)(wuffs_base__load_u8be__no_bounds_check(iop_a_src)));
-      if (self->private_impl.f_metadata_chunk_length_value > 0) {
-        if (self->private_impl.f_metadata_fourcc_value == 1481461792) {
-          self->private_impl.f_metadata_chunk_length_value += 1;
-        } else {
-          (iop_a_src += 1, wuffs_base__make_empty_struct());
+    while (true) {
+    label__0__continue:;
+      while (true) {
+        if (wuffs_base__u64__sat_add(a_src->meta.pos,
+                                     ((uint64_t)(iop_a_src - io0_a_src))) !=
+            self->private_impl.f_metadata_io_position) {
+          if (a_minfo != NULL) {
+            wuffs_base__more_information__set(
+                a_minfo, 2, 0, self->private_impl.f_metadata_io_position, 0, 0);
+          }
+          status = wuffs_base__make_status(
+              wuffs_base__suspension__mispositioned_read);
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(1);
+          goto label__0__continue;
         }
-        self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
+        if (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
+          if (a_minfo != NULL) {
+            wuffs_base__more_information__set(a_minfo, 0, 0, 0, 0, 0);
+          }
+          status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(2);
+          goto label__0__continue;
+        }
+        goto label__0__break;
+      }
+    label__0__break:;
+      v_chunk_length =
+          ((uint64_t)(wuffs_base__load_u8be__no_bounds_check(iop_a_src)));
+      if (v_chunk_length <= 0) {
+        (iop_a_src += 1, wuffs_base__make_empty_struct());
+        goto label__1__break;
+      }
+      if (self->private_impl.f_metadata_fourcc == 1481461792) {
+        v_chunk_length += 1;
+      } else {
+        (iop_a_src += 1, wuffs_base__make_empty_struct());
+      }
+      self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
+          wuffs_base__u64__sat_add(a_src->meta.pos,
+                                   ((uint64_t)(iop_a_src - io0_a_src))),
+          v_chunk_length);
+      if (a_minfo != NULL) {
+        wuffs_base__more_information__set(
+            a_minfo, 3, self->private_impl.f_metadata_fourcc, 0,
             wuffs_base__u64__sat_add(a_src->meta.pos,
                                      ((uint64_t)(iop_a_src - io0_a_src))),
-            self->private_impl.f_metadata_chunk_length_value);
-        status = wuffs_base__make_status(wuffs_base__note__metadata_reported);
-        goto ok;
+            self->private_impl.f_metadata_io_position);
       }
-      (iop_a_src += 1, wuffs_base__make_empty_struct());
+      status = wuffs_base__make_status(
+          wuffs_base__suspension__even_more_information);
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(3);
+    }
+  label__1__break:;
+    if (a_minfo != NULL) {
+      wuffs_base__more_information__set(
+          a_minfo, 3, self->private_impl.f_metadata_fourcc, 0,
+          self->private_impl.f_metadata_io_position,
+          self->private_impl.f_metadata_io_position);
     }
     self->private_impl.f_call_sequence = 2;
-    self->private_impl.f_metadata_fourcc_value = 0;
+    self->private_impl.f_metadata_fourcc = 0;
     self->private_impl.f_metadata_io_position = 0;
     status = wuffs_base__make_status(NULL);
     goto ok;
     goto ok;
   ok:
-    self->private_impl.p_ack_metadata_chunk[0] = 0;
+    self->private_impl.p_tell_me_more[0] = 0;
     goto exit;
   }
 
   goto suspend;
 suspend:
-  self->private_impl.p_ack_metadata_chunk[0] =
+  self->private_impl.p_tell_me_more[0] =
       wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
   self->private_impl.active_coroutine =
       wuffs_base__status__is_suspension(&status) ? 2 : 0;
@@ -18733,36 +18766,6 @@
   return status;
 }
 
-// -------- func gif.decoder.metadata_fourcc
-
-WUFFS_BASE__MAYBE_STATIC uint32_t  //
-wuffs_gif__decoder__metadata_fourcc(const wuffs_gif__decoder* self) {
-  if (!self) {
-    return 0;
-  }
-  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
-      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
-    return 0;
-  }
-
-  return self->private_impl.f_metadata_fourcc_value;
-}
-
-// -------- func gif.decoder.metadata_chunk_length
-
-WUFFS_BASE__MAYBE_STATIC uint64_t  //
-wuffs_gif__decoder__metadata_chunk_length(const wuffs_gif__decoder* self) {
-  if (!self) {
-    return 0;
-  }
-  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
-      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
-    return 0;
-  }
-
-  return self->private_impl.f_metadata_chunk_length_value;
-}
-
 // -------- func gif.decoder.num_animation_loops
 
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
@@ -19842,6 +19845,10 @@
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
     while (true) {
+      if (self->private_impl.f_metadata_fourcc != 0) {
+        status = wuffs_base__make_status(wuffs_base__note__metadata_reported);
+        goto ok;
+      }
       {
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
         if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
@@ -19991,38 +19998,16 @@
         }
       } else if (self->private_impl.f_ignore_metadata) {
       } else if (v_is_iccp && self->private_impl.f_report_metadata_iccp) {
-        while (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
-          status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(10);
-        }
-        self->private_impl.f_metadata_chunk_length_value =
-            ((uint64_t)(wuffs_base__load_u8be__no_bounds_check(iop_a_src)));
-        (iop_a_src += 1, wuffs_base__make_empty_struct());
-        self->private_impl.f_metadata_fourcc_value = 1229144912;
+        self->private_impl.f_metadata_fourcc = 1229144912;
         self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
-            wuffs_base__u64__sat_add(a_src->meta.pos,
-                                     ((uint64_t)(iop_a_src - io0_a_src))),
-            self->private_impl.f_metadata_chunk_length_value);
+            a_src->meta.pos, ((uint64_t)(iop_a_src - io0_a_src)));
         self->private_impl.f_call_sequence = 1;
         status = wuffs_base__make_status(wuffs_base__note__metadata_reported);
         goto ok;
       } else if (v_is_xmp && self->private_impl.f_report_metadata_xmp) {
-        while (((uint64_t)(io2_a_src - iop_a_src)) <= 0) {
-          status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(11);
-        }
-        self->private_impl.f_metadata_chunk_length_value =
-            ((uint64_t)(wuffs_base__load_u8be__no_bounds_check(iop_a_src)));
-        if (self->private_impl.f_metadata_chunk_length_value > 0) {
-          self->private_impl.f_metadata_chunk_length_value += 1;
-        } else {
-          (iop_a_src += 1, wuffs_base__make_empty_struct());
-        }
-        self->private_impl.f_metadata_fourcc_value = 1481461792;
+        self->private_impl.f_metadata_fourcc = 1481461792;
         self->private_impl.f_metadata_io_position = wuffs_base__u64__sat_add(
-            wuffs_base__u64__sat_add(a_src->meta.pos,
-                                     ((uint64_t)(iop_a_src - io0_a_src))),
-            self->private_impl.f_metadata_chunk_length_value);
+            a_src->meta.pos, ((uint64_t)(iop_a_src - io0_a_src)));
         self->private_impl.f_call_sequence = 1;
         status = wuffs_base__make_status(wuffs_base__note__metadata_reported);
         goto ok;
@@ -20033,7 +20018,7 @@
     if (a_src) {
       a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
     }
-    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(12);
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(10);
     status = wuffs_gif__decoder__skip_blocks(self, a_src);
     if (a_src) {
       iop_a_src = a_src->data.ptr + a_src->meta.ri;
@@ -23828,8 +23813,6 @@
 
 const wuffs_base__image_decoder__func_ptrs
     wuffs_wbmp__decoder__func_ptrs_for__wuffs_base__image_decoder = {
-        (wuffs_base__status(*)(void*, wuffs_base__io_buffer*))(
-            &wuffs_wbmp__decoder__ack_metadata_chunk),
         (wuffs_base__status(*)(void*,
                                wuffs_base__pixel_buffer*,
                                wuffs_base__io_buffer*,
@@ -23847,8 +23830,6 @@
             &wuffs_wbmp__decoder__decode_image_config),
         (wuffs_base__rect_ie_u32(*)(const void*))(
             &wuffs_wbmp__decoder__frame_dirty_rect),
-        (uint64_t(*)(const void*))(&wuffs_wbmp__decoder__metadata_chunk_length),
-        (uint32_t(*)(const void*))(&wuffs_wbmp__decoder__metadata_fourcc),
         (uint32_t(*)(const void*))(&wuffs_wbmp__decoder__num_animation_loops),
         (uint64_t(*)(const void*))(
             &wuffs_wbmp__decoder__num_decoded_frame_configs),
@@ -23859,6 +23840,11 @@
             &wuffs_wbmp__decoder__set_quirk_enabled),
         (wuffs_base__empty_struct(*)(void*, uint32_t, bool))(
             &wuffs_wbmp__decoder__set_report_metadata),
+        (wuffs_base__status(*)(void*,
+                               wuffs_base__io_buffer*,
+                               wuffs_base__more_information*,
+                               wuffs_base__io_buffer*))(
+            &wuffs_wbmp__decoder__tell_me_more),
         (wuffs_base__range_ii_u64(*)(const void*))(
             &wuffs_wbmp__decoder__workbuf_len),
 };
@@ -24427,45 +24413,6 @@
   return status;
 }
 
-// -------- func wbmp.decoder.ack_metadata_chunk
-
-WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
-wuffs_wbmp__decoder__ack_metadata_chunk(wuffs_wbmp__decoder* self,
-                                        wuffs_base__io_buffer* a_src) {
-  if (!self) {
-    return wuffs_base__make_status(wuffs_base__error__bad_receiver);
-  }
-  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
-    return wuffs_base__make_status(
-        (self->private_impl.magic == WUFFS_BASE__DISABLED)
-            ? wuffs_base__error__disabled_by_previous_error
-            : wuffs_base__error__initialize_not_called);
-  }
-  if (!a_src) {
-    self->private_impl.magic = WUFFS_BASE__DISABLED;
-    return wuffs_base__make_status(wuffs_base__error__bad_argument);
-  }
-  if ((self->private_impl.active_coroutine != 0) &&
-      (self->private_impl.active_coroutine != 4)) {
-    self->private_impl.magic = WUFFS_BASE__DISABLED;
-    return wuffs_base__make_status(
-        wuffs_base__error__interleaved_coroutine_calls);
-  }
-  self->private_impl.active_coroutine = 0;
-  wuffs_base__status status = wuffs_base__make_status(NULL);
-
-  status = wuffs_base__make_status(NULL);
-  goto ok;
-  goto ok;
-ok:
-  goto exit;
-exit:
-  if (wuffs_base__status__is_error(&status)) {
-    self->private_impl.magic = WUFFS_BASE__DISABLED;
-  }
-  return status;
-}
-
 // -------- func wbmp.decoder.frame_dirty_rect
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__rect_ie_u32  //
@@ -24482,36 +24429,6 @@
                                                self->private_impl.f_height);
 }
 
-// -------- func wbmp.decoder.metadata_chunk_length
-
-WUFFS_BASE__MAYBE_STATIC uint64_t  //
-wuffs_wbmp__decoder__metadata_chunk_length(const wuffs_wbmp__decoder* self) {
-  if (!self) {
-    return 0;
-  }
-  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
-      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
-    return 0;
-  }
-
-  return 0;
-}
-
-// -------- func wbmp.decoder.metadata_fourcc
-
-WUFFS_BASE__MAYBE_STATIC uint32_t  //
-wuffs_wbmp__decoder__metadata_fourcc(const wuffs_wbmp__decoder* self) {
-  if (!self) {
-    return 0;
-  }
-  if ((self->private_impl.magic != WUFFS_BASE__MAGIC) &&
-      (self->private_impl.magic != WUFFS_BASE__DISABLED)) {
-    return 0;
-  }
-
-  return 0;
-}
-
 // -------- func wbmp.decoder.num_animation_loops
 
 WUFFS_BASE__MAYBE_STATIC uint32_t  //
@@ -24600,6 +24517,47 @@
   return wuffs_base__make_empty_struct();
 }
 
+// -------- func wbmp.decoder.tell_me_more
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__status  //
+wuffs_wbmp__decoder__tell_me_more(wuffs_wbmp__decoder* self,
+                                  wuffs_base__io_buffer* a_dst,
+                                  wuffs_base__more_information* a_minfo,
+                                  wuffs_base__io_buffer* a_src) {
+  if (!self) {
+    return wuffs_base__make_status(wuffs_base__error__bad_receiver);
+  }
+  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
+    return wuffs_base__make_status(
+        (self->private_impl.magic == WUFFS_BASE__DISABLED)
+            ? wuffs_base__error__disabled_by_previous_error
+            : wuffs_base__error__initialize_not_called);
+  }
+  if (!a_dst || !a_src) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__make_status(wuffs_base__error__bad_argument);
+  }
+  if ((self->private_impl.active_coroutine != 0) &&
+      (self->private_impl.active_coroutine != 4)) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+    return wuffs_base__make_status(
+        wuffs_base__error__interleaved_coroutine_calls);
+  }
+  self->private_impl.active_coroutine = 0;
+  wuffs_base__status status = wuffs_base__make_status(NULL);
+
+  status = wuffs_base__make_status(wuffs_base__error__no_more_information);
+  goto exit;
+  goto ok;
+ok:
+  goto exit;
+exit:
+  if (wuffs_base__status__is_error(&status)) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+  }
+  return status;
+}
+
 // -------- func wbmp.decoder.workbuf_len
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__range_ii_u64  //
diff --git a/std/bmp/decode_bmp.wuffs b/std/bmp/decode_bmp.wuffs
index dd88a59..021ba8c 100644
--- a/std/bmp/decode_bmp.wuffs
+++ b/std/bmp/decode_bmp.wuffs
@@ -411,12 +411,6 @@
 	this.call_sequence = 3
 }
 
-pub func decoder.ack_metadata_chunk?(src: base.io_reader) {
-	// TODO: this final line shouldn't be necessary, here and in some other
-	// std/*/*.wuffs functions.
-	return ok
-}
-
 pub func decoder.frame_dirty_rect() base.rect_ie_u32 {
 	return this.util.make_rect_ie_u32(
 		min_incl_x: 0,
@@ -425,14 +419,6 @@
 		max_excl_y: this.height)
 }
 
-pub func decoder.metadata_chunk_length() base.u64 {
-	return 0
-}
-
-pub func decoder.metadata_fourcc() base.u32 {
-	return 0
-}
-
 pub func decoder.num_animation_loops() base.u32 {
 	return 0
 }
@@ -467,6 +453,10 @@
 	// No-op. BMP doesn't support metadata.
 }
 
+pub func decoder.tell_me_more?(dst: base.io_writer, minfo: nptr base.more_information, src: base.io_reader) {
+	return base."#no more information"
+}
+
 pub func decoder.workbuf_len() base.range_ii_u64 {
 	return this.util.make_range_ii_u64(min_incl: 0, max_incl: 0)
 }
diff --git a/std/gif/decode_config.wuffs b/std/gif/decode_config.wuffs
index 8b8dc70..6fa32a9 100644
--- a/std/gif/decode_config.wuffs
+++ b/std/gif/decode_config.wuffs
@@ -61,12 +61,11 @@
 	//  - IC  is decode_image_config, implicit means nullptr args.dst
 	call_sequence : base.u8,
 
-	ignore_metadata             : base.bool,
-	report_metadata_iccp        : base.bool,
-	report_metadata_xmp         : base.bool,
-	metadata_fourcc_value       : base.u32,
-	metadata_chunk_length_value : base.u64,
-	metadata_io_position        : base.u64,
+	ignore_metadata      : base.bool,
+	report_metadata_iccp : base.bool,
+	report_metadata_xmp  : base.bool,
+	metadata_fourcc      : base.u32,
+	metadata_io_position : base.u64,
 
 	quirks : array[QUIRKS_COUNT] base.bool,
 
@@ -170,54 +169,92 @@
 	}
 }
 
-pub func config_decoder.ack_metadata_chunk?(src: base.io_reader) {
+pub func config_decoder.tell_me_more?(dst: base.io_writer, minfo: nptr base.more_information, src: base.io_reader) {
+	var chunk_length : base.u64
+
 	if this.call_sequence <> 1 {
 		return base."#bad call sequence"
 	}
-	if args.src.position() <> this.metadata_io_position {
-		return base."#bad I/O position"
+	if this.metadata_fourcc == 0 {
+		return base."#no more information"
 	}
 
-	if this.metadata_chunk_length_value > 0 {
-		while args.src.available() <= 0,
+	while true {
+		while true,
 			post args.src.available() > 0,
 		{
-			yield? base."$short read"
+			if args.src.position() <> this.metadata_io_position {
+				if args.minfo <> nullptr {
+					args.minfo.set!(
+						flavor: 2,  // WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_SEEK
+						w: 0,
+						x: this.metadata_io_position,
+						y: 0,
+						z: 0)
+				}
+				yield? base."$mispositioned read"
+				continue
+			}
+
+			if args.src.available() <= 0 {
+				if args.minfo <> nullptr {
+					args.minfo.set!(
+						flavor: 0,
+						w: 0,
+						x: 0,
+						y: 0,
+						z: 0)
+				}
+				yield? base."$short read"
+				continue
+			}
+
+			break
 		} endwhile
 
-		this.metadata_chunk_length_value = args.src.peek_u8_as_u64()
-		if this.metadata_chunk_length_value > 0 {
-			if this.metadata_fourcc_value == 'XMP 'be {
-				// The +1 is because XMP metadata's encoding includes each
-				// block's leading byte (the block size) as part of the
-				// metadata passed to the caller.
-				this.metadata_chunk_length_value += 1
-			} else {
-				args.src.skip32_fast!(actual: 1, worst_case: 1)
-			}
-			this.metadata_io_position =
-				args.src.position() ~sat+ this.metadata_chunk_length_value
-			return base."@metadata reported"
+		chunk_length = args.src.peek_u8_as_u64()
+		if chunk_length <= 0 {
+			// Consume the '\x00' that means a zero-length block.
+			args.src.skip32_fast!(actual: 1, worst_case: 1)
+			break
 		}
 
-		// Consume the '\x00' that means a zero-length block.
-		args.src.skip32_fast!(actual: 1, worst_case: 1)
-	}
+		if this.metadata_fourcc == 'XMP 'be {
+			// The +1 is because XMP metadata's encoding includes each
+			// block's leading byte (the block size) as part of the
+			// metadata passed to the caller.
+			chunk_length += 1
+		} else {
+			args.src.skip32_fast!(actual: 1, worst_case: 1)
+		}
+		this.metadata_io_position = args.src.position() ~sat+ chunk_length
 
+		if args.minfo <> nullptr {
+			args.minfo.set!(
+				flavor: 3,  // WUFFS_BASE__MORE_INFORMATION__FLAVOR__METADATA
+				w: this.metadata_fourcc,
+				x: 0,
+				y: args.src.position(),
+				z: this.metadata_io_position)
+		}
+
+		yield? base."$even more information"
+	} endwhile
+
+	if args.minfo <> nullptr {
+		args.minfo.set!(
+			flavor: 3,  // WUFFS_BASE__MORE_INFORMATION__FLAVOR__METADATA
+			w: this.metadata_fourcc,
+			x: 0,
+			y: this.metadata_io_position,
+			z: this.metadata_io_position)
+	}
 	this.call_sequence = 2
-	this.metadata_fourcc_value = 0
+	this.metadata_fourcc = 0
 	this.metadata_io_position = 0
 	return ok
 }
 
-pub func config_decoder.metadata_fourcc() base.u32 {
-	return this.metadata_fourcc_value
-}
-
-pub func config_decoder.metadata_chunk_length() base.u64 {
-	return this.metadata_chunk_length_value
-}
-
 pub func config_decoder.num_animation_loops() base.u32 {
 	if this.seen_num_loops {
 		return this.num_loops
@@ -528,6 +565,10 @@
 	var is_xmp      : base.bool
 
 	while.goto_done true {{
+	if this.metadata_fourcc <> 0 {
+		return base."@metadata reported"
+	}
+
 	block_size = args.src.read_u8?()
 	if block_size == 0 {
 		return ok
@@ -591,35 +632,14 @@
 		// No-op.
 
 	} else if is_iccp and this.report_metadata_iccp {
-		while args.src.available() <= 0,
-			post args.src.available() > 0,
-		{
-			yield? base."$short read"
-		} endwhile
-		this.metadata_chunk_length_value = args.src.peek_u8_as_u64()
-		args.src.skip32_fast!(actual: 1, worst_case: 1)
-		this.metadata_fourcc_value = 'ICCP'be
-		this.metadata_io_position = args.src.position() ~sat+ this.metadata_chunk_length_value
+		this.metadata_fourcc = 'ICCP'be
+		this.metadata_io_position = args.src.position()
 		this.call_sequence = 1
 		return base."@metadata reported"
 
 	} else if is_xmp and this.report_metadata_xmp {
-		while args.src.available() <= 0,
-			post args.src.available() > 0,
-		{
-			yield? base."$short read"
-		} endwhile
-		this.metadata_chunk_length_value = args.src.peek_u8_as_u64()
-		if this.metadata_chunk_length_value > 0 {
-			// The +1 is because XMP metadata's encoding includes each
-			// block's leading byte (the block size) as part of the
-			// metadata passed to the caller.
-			this.metadata_chunk_length_value += 1
-		} else {
-			args.src.skip32_fast!(actual: 1, worst_case: 1)
-		}
-		this.metadata_fourcc_value = 'XMP 'be
-		this.metadata_io_position = args.src.position() ~sat+ this.metadata_chunk_length_value
+		this.metadata_fourcc = 'XMP 'be
+		this.metadata_io_position = args.src.position()
 		this.call_sequence = 1
 		return base."@metadata reported"
 	}
diff --git a/std/gif/decode_gif.wuffs b/std/gif/decode_gif.wuffs
index 6d38d48..375bde4 100644
--- a/std/gif/decode_gif.wuffs
+++ b/std/gif/decode_gif.wuffs
@@ -76,12 +76,11 @@
 	//  - IC  is decode_image_config, implicit means nullptr args.dst
 	call_sequence : base.u8,
 
-	ignore_metadata             : base.bool,
-	report_metadata_iccp        : base.bool,
-	report_metadata_xmp         : base.bool,
-	metadata_fourcc_value       : base.u32,
-	metadata_chunk_length_value : base.u64,
-	metadata_io_position        : base.u64,
+	ignore_metadata      : base.bool,
+	report_metadata_iccp : base.bool,
+	report_metadata_xmp  : base.bool,
+	metadata_fourcc      : base.u32,
+	metadata_io_position : base.u64,
 
 	quirks : array[QUIRKS_COUNT] base.bool,
 
@@ -209,54 +208,92 @@
 	}
 }
 
-pub func decoder.ack_metadata_chunk?(src: base.io_reader) {
+pub func decoder.tell_me_more?(dst: base.io_writer, minfo: nptr base.more_information, src: base.io_reader) {
+	var chunk_length : base.u64
+
 	if this.call_sequence <> 1 {
 		return base."#bad call sequence"
 	}
-	if args.src.position() <> this.metadata_io_position {
-		return base."#bad I/O position"
+	if this.metadata_fourcc == 0 {
+		return base."#no more information"
 	}
 
-	if this.metadata_chunk_length_value > 0 {
-		while args.src.available() <= 0,
+	while true {
+		while true,
 			post args.src.available() > 0,
 		{
-			yield? base."$short read"
+			if args.src.position() <> this.metadata_io_position {
+				if args.minfo <> nullptr {
+					args.minfo.set!(
+						flavor: 2,  // WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_SEEK
+						w: 0,
+						x: this.metadata_io_position,
+						y: 0,
+						z: 0)
+				}
+				yield? base."$mispositioned read"
+				continue
+			}
+
+			if args.src.available() <= 0 {
+				if args.minfo <> nullptr {
+					args.minfo.set!(
+						flavor: 0,
+						w: 0,
+						x: 0,
+						y: 0,
+						z: 0)
+				}
+				yield? base."$short read"
+				continue
+			}
+
+			break
 		} endwhile
 
-		this.metadata_chunk_length_value = args.src.peek_u8_as_u64()
-		if this.metadata_chunk_length_value > 0 {
-			if this.metadata_fourcc_value == 'XMP 'be {
-				// The +1 is because XMP metadata's encoding includes each
-				// block's leading byte (the block size) as part of the
-				// metadata passed to the caller.
-				this.metadata_chunk_length_value += 1
-			} else {
-				args.src.skip32_fast!(actual: 1, worst_case: 1)
-			}
-			this.metadata_io_position =
-				args.src.position() ~sat+ this.metadata_chunk_length_value
-			return base."@metadata reported"
+		chunk_length = args.src.peek_u8_as_u64()
+		if chunk_length <= 0 {
+			// Consume the '\x00' that means a zero-length block.
+			args.src.skip32_fast!(actual: 1, worst_case: 1)
+			break
 		}
 
-		// Consume the '\x00' that means a zero-length block.
-		args.src.skip32_fast!(actual: 1, worst_case: 1)
-	}
+		if this.metadata_fourcc == 'XMP 'be {
+			// The +1 is because XMP metadata's encoding includes each
+			// block's leading byte (the block size) as part of the
+			// metadata passed to the caller.
+			chunk_length += 1
+		} else {
+			args.src.skip32_fast!(actual: 1, worst_case: 1)
+		}
+		this.metadata_io_position = args.src.position() ~sat+ chunk_length
 
+		if args.minfo <> nullptr {
+			args.minfo.set!(
+				flavor: 3,  // WUFFS_BASE__MORE_INFORMATION__FLAVOR__METADATA
+				w: this.metadata_fourcc,
+				x: 0,
+				y: args.src.position(),
+				z: this.metadata_io_position)
+		}
+
+		yield? base."$even more information"
+	} endwhile
+
+	if args.minfo <> nullptr {
+		args.minfo.set!(
+			flavor: 3,  // WUFFS_BASE__MORE_INFORMATION__FLAVOR__METADATA
+			w: this.metadata_fourcc,
+			x: 0,
+			y: this.metadata_io_position,
+			z: this.metadata_io_position)
+	}
 	this.call_sequence = 2
-	this.metadata_fourcc_value = 0
+	this.metadata_fourcc = 0
 	this.metadata_io_position = 0
 	return ok
 }
 
-pub func decoder.metadata_fourcc() base.u32 {
-	return this.metadata_fourcc_value
-}
-
-pub func decoder.metadata_chunk_length() base.u64 {
-	return this.metadata_chunk_length_value
-}
-
 pub func decoder.num_animation_loops() base.u32 {
 	if this.seen_num_loops {
 		return this.num_loops
@@ -603,6 +640,10 @@
 	var is_xmp      : base.bool
 
 	while.goto_done true {{
+	if this.metadata_fourcc <> 0 {
+		return base."@metadata reported"
+	}
+
 	block_size = args.src.read_u8?()
 	if block_size == 0 {
 		return ok
@@ -666,35 +707,14 @@
 		// No-op.
 
 	} else if is_iccp and this.report_metadata_iccp {
-		while args.src.available() <= 0,
-			post args.src.available() > 0,
-		{
-			yield? base."$short read"
-		} endwhile
-		this.metadata_chunk_length_value = args.src.peek_u8_as_u64()
-		args.src.skip32_fast!(actual: 1, worst_case: 1)
-		this.metadata_fourcc_value = 'ICCP'be
-		this.metadata_io_position = args.src.position() ~sat+ this.metadata_chunk_length_value
+		this.metadata_fourcc = 'ICCP'be
+		this.metadata_io_position = args.src.position()
 		this.call_sequence = 1
 		return base."@metadata reported"
 
 	} else if is_xmp and this.report_metadata_xmp {
-		while args.src.available() <= 0,
-			post args.src.available() > 0,
-		{
-			yield? base."$short read"
-		} endwhile
-		this.metadata_chunk_length_value = args.src.peek_u8_as_u64()
-		if this.metadata_chunk_length_value > 0 {
-			// The +1 is because XMP metadata's encoding includes each
-			// block's leading byte (the block size) as part of the
-			// metadata passed to the caller.
-			this.metadata_chunk_length_value += 1
-		} else {
-			args.src.skip32_fast!(actual: 1, worst_case: 1)
-		}
-		this.metadata_fourcc_value = 'XMP 'be
-		this.metadata_io_position = args.src.position() ~sat+ this.metadata_chunk_length_value
+		this.metadata_fourcc = 'XMP 'be
+		this.metadata_io_position = args.src.position()
 		this.call_sequence = 1
 		return base."@metadata reported"
 	}
diff --git a/std/wbmp/decode_wbmp.wuffs b/std/wbmp/decode_wbmp.wuffs
index 6159dcf..35c371e 100644
--- a/std/wbmp/decode_wbmp.wuffs
+++ b/std/wbmp/decode_wbmp.wuffs
@@ -213,12 +213,6 @@
 	this.call_sequence = 3
 }
 
-pub func decoder.ack_metadata_chunk?(src: base.io_reader) {
-	// TODO: this final line shouldn't be necessary, here and in some other
-	// std/*/*.wuffs functions.
-	return ok
-}
-
 pub func decoder.frame_dirty_rect() base.rect_ie_u32 {
 	return this.util.make_rect_ie_u32(
 		min_incl_x: 0,
@@ -227,14 +221,6 @@
 		max_excl_y: this.height)
 }
 
-pub func decoder.metadata_chunk_length() base.u64 {
-	return 0
-}
-
-pub func decoder.metadata_fourcc() base.u32 {
-	return 0
-}
-
 pub func decoder.num_animation_loops() base.u32 {
 	return 0
 }
@@ -269,6 +255,10 @@
 	// No-op. WBMP doesn't support metadata.
 }
 
+pub func decoder.tell_me_more?(dst: base.io_writer, minfo: nptr base.more_information, src: base.io_reader) {
+	return base."#no more information"
+}
+
 pub func decoder.workbuf_len() base.range_ii_u64 {
 	return this.util.make_range_ii_u64(min_incl: 0, max_incl: 0)
 }
diff --git a/test/c/std/gif.c b/test/c/std/gif.c
index 65911c4..095b5ff 100644
--- a/test/c/std/gif.c
+++ b/test/c/std/gif.c
@@ -1347,35 +1347,54 @@
         const char* want = "";
         char have_buffer[100];
         int have_length = 0;
-        uint32_t have_fourcc = wuffs_gif__decoder__metadata_fourcc(&dec);
-
-        switch (wuffs_gif__decoder__metadata_fourcc(&dec)) {
-          case WUFFS_BASE__FOURCC__ICCP:
-            want = full ? "\x16\x26\x36\x46\x56\x76\x86\x96" : "";
-            seen_iccp = true;
-            break;
-          case WUFFS_BASE__FOURCC__XMP:
-            want = full ? "\x05\x17\x27\x37\x47\x57\x03\x77\x87\x97" : "";
-            seen_xmp = true;
-            break;
-          default:
-            RETURN_FAIL(
-                "metadata_fourcc (iccp=%d, xmp=%d): unexpected FourCC "
-                "0x%08" PRIX32,
-                iccp, xmp, have_fourcc);
-        }
+        uint32_t have_fourcc = 0;
 
         while (true) {
-          uint64_t n = wuffs_gif__decoder__metadata_chunk_length(&dec);
+          wuffs_base__io_buffer empty = wuffs_base__empty_io_buffer();
+          wuffs_base__more_information minfo =
+              wuffs_base__empty_more_information();
+          wuffs_base__status status =
+              wuffs_gif__decoder__tell_me_more(&dec, &empty, &minfo, &src);
+          if (wuffs_base__status__is_error(&status)) {
+            RETURN_FAIL("tell_me_more (iccp=%d, xmp=%d): \"%s\"", iccp, xmp,
+                        status.repr);
+          } else if (minfo.flavor !=
+                     WUFFS_BASE__MORE_INFORMATION__FLAVOR__METADATA) {
+            RETURN_FAIL("tell_me_more (iccp=%d, xmp=%d): flavor: have %" PRIu32
+                        ", want %" PRIu32,
+                        iccp, xmp, minfo.flavor,
+                        WUFFS_BASE__MORE_INFORMATION__FLAVOR__METADATA);
+          }
+
+          have_fourcc = wuffs_base__more_information__metadata__fourcc(&minfo);
+          switch (have_fourcc) {
+            case WUFFS_BASE__FOURCC__ICCP:
+              want = full ? "\x16\x26\x36\x46\x56\x76\x86\x96" : "";
+              seen_iccp = true;
+              break;
+            case WUFFS_BASE__FOURCC__XMP:
+              want = full ? "\x05\x17\x27\x37\x47\x57\x03\x77\x87\x97" : "";
+              seen_xmp = true;
+              break;
+            default:
+              RETURN_FAIL(
+                  "metadata_fourcc (iccp=%d, xmp=%d): unexpected FourCC "
+                  "0x%08" PRIX32,
+                  iccp, xmp, have_fourcc);
+          }
+
+          wuffs_base__range_ie_u64 r =
+              wuffs_base__more_information__metadata__range(&minfo);
+          uint64_t n = wuffs_base__range_ie_u64__length(&r);
           if ((n > 100) || (n + have_length > 100)) {
             RETURN_FAIL(
-                "metadata_chunk_length (iccp=%d, xmp=%d): too much "
+                "metadata chunk length (iccp=%d, xmp=%d): too much "
                 "metadata (vs buffer size)",
                 iccp, xmp);
           }
           if (n > wuffs_base__io_buffer__reader_available(&src)) {
             RETURN_FAIL(
-                "metadata_chunk_length (iccp=%d, xmp=%d): too much "
+                "metadata chunk length (iccp=%d, xmp=%d): too much "
                 "metadata (vs available)",
                 iccp, xmp);
           }
@@ -1383,15 +1402,14 @@
           have_length += n;
           src.meta.ri += n;
 
-          wuffs_base__status status =
-              wuffs_gif__decoder__ack_metadata_chunk(&dec, &src);
           if (wuffs_base__status__is_ok(&status)) {
             break;
-          } else if (status.repr != wuffs_base__note__metadata_reported) {
+          } else if (status.repr !=
+                     wuffs_base__suspension__even_more_information) {
             RETURN_FAIL(
-                "ack_metadata_chunk (iccp=%d, xmp=%d): have \"%s\", want "
-                "\"%s\"",
-                iccp, xmp, status.repr, wuffs_base__note__metadata_reported);
+                "tell_me_more (iccp=%d, xmp=%d): have \"%s\", want \"%s\"",
+                iccp, xmp, status.repr,
+                wuffs_base__suspension__even_more_information);
           }
         }