U-Boot Image wrapper: Java (read-write) parsing library

The legacy U-Boot image format (uImage), as created by mkimage: a 64-byte header followed by the image data (e.g. a Linux kernel, a ramdisk or a script). The header describes the data (OS, CPU architecture, image type, compression, load and entry point addresses, name) and protects both the header and the data with CRC-32 checksums.

U-Boot's newer FIT (Flattened Image Tree) format is not covered by this spec.

Application

U-Boot

File extension

["bin", "img", "scr", "trx", "bix", "uimg"]

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.11

References

This page hosts a formal specification of U-Boot Image wrapper using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Java (read-write) source code to parse U-Boot Image wrapper

Uimage.java

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

import io.kaitai.struct.ByteBufferKaitaiStream;
import io.kaitai.struct.KaitaiStruct;
import io.kaitai.struct.KaitaiStream;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import io.kaitai.struct.ConsistencyError;
import java.nio.charset.StandardCharsets;
import java.nio.charset.Charset;


/**
 * The legacy U-Boot image format (uImage), as created by `mkimage`: a 64-byte
 * header followed by the image data (e.g. a Linux kernel, a ramdisk or a
 * script). The header describes the data (OS, CPU architecture, image type,
 * compression, load and entry point addresses, name) and protects both the
 * header and the data with CRC-32 checksums.
 * 
 * U-Boot's newer FIT (Flattened Image Tree) format is not covered by this spec.
 * @see <a href="https://github.com/u-boot/u-boot/blob/ece349ade2973e220f524ce59e59711cc919263f/include/image.h">Git tag "v2026.07"</a>
 */
public class Uimage extends KaitaiStruct.ReadWrite {
    public static Uimage fromFile(String fileName) throws IOException {
        return new Uimage(new ByteBufferKaitaiStream(fileName));
    }

    public enum MagicTypes {
        ALLNET_ALL_SG8316M(5),
        ALLNET_ALL_SG8208M(6),
        ALLNET_ALL_SG8324M(22),
        DATTO_L8(7353344),
        ALLNET_ALL_WAPC0450C(9453568),
        ENGENIUS_EWS2910P_V3(50398464),
        ENGENIUS_EWS1200_28TFP(50402048),
        ENGENIUS_EWS7926EFP(50402050),
        ENGENIUS_EWS7952P(50406401),
        ENGENIUS_EWS2908P(50409728),
        ENGENIUS_EWS7928P_V2(51450112),
        ENGENIUS_EWS1200_28TFP_V2(51450624),
        ENGENIUS_EWS2910P(58730768),
        ENGENIUS_EWS5912FP(58743058),
        ENGENIUS_EWS7928P(58751272),
        ENGENIUS_EWS1200D_10T(59187216),
        ENGENIUS_EWS1200_28T(59187240),
        ENGENIUS_EWS7928FP(59216527),
        ENGENIUS_EWS1200_52T(60235858),
        ENGENIUS_EWS7952FP(60265775),
        YOUKU_YK_L1(304680960),
        REALTEK_SDK(305418240),
        NETGEAR_GS750E(391006017),
        NETGEAR_GS728TP_V2(391006018),
        BOLT_BL100(638656533),
        UIMAGE(654645590),
        NETGEAR_WNCE4004(654645607),
        NETGEAR_WNR612_V2_EARLY(808857906),
        NETGEAR_WNR1000_V2_VC(825241648),
        NETGEAR_WNR1000_V2(825241649),
        NETGEAR_WNR1000_V2_VM(825241650),
        NETGEAR_WNR1000_V4(825241652),
        NETGEAR_WPN824N(825307184),
        ZYXEL_UAG50(827343191),
        NETGEAR_WNR2000(842018865),
        NETGEAR_WNR2000_V3(842018867),
        NETGEAR_WNR2000_V4(842018868),
        NETGEAR_WNR612_V2(842020401),
        NETGEAR_WNR2200(842149936),
        NETGEAR_WNDR3700(859254832),
        NETGEAR_WNDR3700_V2(859254833),
        NETGEAR_WNDR3700_V4(859254835),
        NETGEAR_R6100(909127728),
        NETGEAR_WNR612(909193778),
        MITRASTAR_COMB(1129270594),
        MITRASTAR_COMC(1129270595),
        NETGEAR_GS308T(1313293109),
        NETGEAR_NGE(1313293600),
        NETGEAR_NGF(1313293856),
        NETGEAR_NGG(1313294112),
        NETGEAR_NGH(1313294368),
        NETGEAR_NGI(1313294624),
        NETGEAR_NGP(1313296416),
        OPENWRT_OKLI(1330334793),
        OPENWRT_DLINK_COVR(1752396147),
        OPENWRT_SENAO_OKLI(1936805707),
        ENGENIUS_ECS1112FP(2155872258L),
        ENGENIUS_FITSWITCH(2155872259L),
        ENGENIUS_EWS1200_28TFP_OLD_FW(2197885696L),
        ENGENIUS_EWS7926EFP_OLD_FW(2197885698L),
        ENGENIUS_EWS7952P_OLD_FW(2197890049L),
        REALTEK_RTL838X(2206203904L),
        IODATA_BSH_G08M(2206203905L),
        IODATA_BSH_GP08(2206203906L),
        IODATA_BSH_G16M(2206203907L),
        IODATA_BSH_G24M(2206203908L),
        IODATA_BSH_G08MB(2206203920L),
        IODATA_BSH_GP08MB(2206203921L),
        IODATA_BSH_G16MB(2206203922L),
        IODATA_BSH_G24MB(2206203923L),
        TRENDNET_TPE_3012LS(2206210573L),
        TRENDNET_TPE_3018LS(2206210574L),
        ENGENIUS_EGS2108P_OLD_FW(2206212360L),
        ENGENIUS_EGS2110P_OLD_FW(2206212368L),
        ENGENIUS_EWS2910P_OLD_FW(2206214416L),
        ENGENIUS_EGS5110P_OLD_FW(2206224656L),
        ENGENIUS_EGS5212FP_OLD_FW(2206224914L),
        ENGENIUS_EWS5912FP_OLD_FW(2206226706L),
        ENGENIUS_EWS7928P_OLD_FW(2206234920L),
        ENGENIUS_EWS1200D_10T_OLD_FW(2206670864L),
        ENGENIUS_EWS1200_28T_OLD_FW(2206670888L),
        ENGENIUS_EGS7228FP_OLD_FW(2206671503L),
        ENGENIUS_EWS7928FP_OLD_FW(2206700175L),
        REALTEK_RTL839X(2207252480L),
        REALTEK_RTL8396(2207645696L),
        ENGENIUS_EWS1200_52T_OLD_FW(2207719506L),
        ENGENIUS_EGS7252FP_OLD_FW(2207720751L),
        ENGENIUS_EWS7952FP_OLD_FW(2207749423L),
        REALTEK_RTL930X(2466250752L),
        ZYXEL_XGS1010_12(2466254864L),
        ZYXEL_XGS1210_12(2466255376L),
        ZYXEL_XGS1250_12(2466255440L),
        NICGIGA_S100_0800S_M(2466447360L),
        PLASMACLOUD_PSX28(2467299328L);

        private final long id;
        MagicTypes(long id) { this.id = id; }
        public long id() { return id; }
        private static final Map<Long, MagicTypes> byId = new HashMap<Long, MagicTypes>(94);
        static {
            for (MagicTypes e : MagicTypes.values())
                byId.put(e.id(), e);
        }
        public static MagicTypes byId(long id) { return byId.get(id); }
    }

    public enum UimageArch {
        INVALID(0),
        ALPHA(1),
        ARM(2),
        I386(3),
        IA64(4),
        MIPS(5),
        MIPS64(6),
        PPC(7),
        S390(8),
        SH(9),
        SPARC(10),
        SPARC64(11),
        M68K(12),
        NIOS(13),
        MICROBLAZE(14),
        NIOS2(15),
        BLACKFIN(16),
        AVR32(17),
        ST200(18),
        SANDBOX(19),
        NDS32(20),
        OPENRISC(21),
        ARM64(22),
        ARC(23),
        X86_64(24),
        XTENSA(25),
        RISCV(26);

        private final long id;
        UimageArch(long id) { this.id = id; }
        public long id() { return id; }
        private static final Map<Long, UimageArch> byId = new HashMap<Long, UimageArch>(27);
        static {
            for (UimageArch e : UimageArch.values())
                byId.put(e.id(), e);
        }
        public static UimageArch byId(long id) { return byId.get(id); }
    }

    public enum UimageComp {
        NONE(0),
        GZIP(1),
        BZIP2(2),
        LZMA(3),
        LZO(4),
        LZ4(5),
        ZSTD(6),
        MZ(9),
        XIP(10);

        private final long id;
        UimageComp(long id) { this.id = id; }
        public long id() { return id; }
        private static final Map<Long, UimageComp> byId = new HashMap<Long, UimageComp>(9);
        static {
            for (UimageComp e : UimageComp.values())
                byId.put(e.id(), e);
        }
        public static UimageComp byId(long id) { return byId.get(id); }
    }

    public enum UimageOs {
        INVALID(0),
        OPENBSD(1),
        NETBSD(2),
        FREEBSD(3),
        BSD4_4(4),
        LINUX(5),
        SVR4(6),
        ESIX(7),
        SOLARIS(8),
        IRIX(9),
        SCO(10),
        DELL(11),
        NCR(12),
        LYNXOS(13),
        VXWORKS(14),
        PSOS(15),
        QNX(16),
        U_BOOT(17),
        RTEMS(18),
        ARTOS(19),
        UNITY(20),
        INTEGRITY(21),
        OSE(22),
        PLAN9(23),
        OPENRTOS(24),
        ARM_TRUSTED_FIRMWARE(25),
        TEE(26),
        OPENSBI(27),
        EFI(28),
        ELF(29);

        private final long id;
        UimageOs(long id) { this.id = id; }
        public long id() { return id; }
        private static final Map<Long, UimageOs> byId = new HashMap<Long, UimageOs>(30);
        static {
            for (UimageOs e : UimageOs.values())
                byId.put(e.id(), e);
        }
        public static UimageOs byId(long id) { return byId.get(id); }
    }

    public enum UimageType {
        INVALID(0),
        STANDALONE(1),
        KERNEL(2),
        RAMDISK(3),
        MULTI(4),
        FIRMWARE(5),
        SCRIPT(6),
        FILESYSTEM(7),
        FLATDT(8),
        KWBIMAGE(9),
        IMXIMAGE(10),
        UBLIMAGE(11),
        OMAPIMAGE(12),
        AISIMAGE(13),
        KERNEL_NOLOAD(14),
        PBLIMAGE(15),
        MXSIMAGE(16),
        GPIMAGE(17),
        ATMELIMAGE(18),
        SOCFPGAIMAGE(19),
        X86_SETUP(20),
        LPC32XXIMAGE(21),
        LOADABLE(22),
        RKIMAGE(23),
        RKSD(24),
        RKSPI(25),
        ZYNQIMAGE(26),
        ZYNQMPIMAGE(27),
        ZYNQMPBIF(28),
        FPGA(29),
        VYBRIDIMAGE(30),
        TEE(31),
        FIRMWARE_IVT(32),
        PMMC(33),
        STM32IMAGE(34),
        SOCFPGAIMAGE_V1(35),
        MTKIMAGE(36),
        IMX8MIMAGE(37),
        IMX8IMAGE(38),
        COPRO(39),
        SUNXI_EGON(40),
        SUNXI_TOC0(41),
        FDT_LEGACY(42),
        RENESAS_SPKG(43),
        STARFIVE_SPL(44),
        TFA_BL31(45),
        STM32IMAGE_V2(46),
        AMLIMAGE(47);

        private final long id;
        UimageType(long id) { this.id = id; }
        public long id() { return id; }
        private static final Map<Long, UimageType> byId = new HashMap<Long, UimageType>(48);
        static {
            for (UimageType e : UimageType.values())
                byId.put(e.id(), e);
        }
        public static UimageType byId(long id) { return byId.get(id); }
    }
    public Uimage() {
        this(null, null, null);
    }

    public Uimage(KaitaiStream _io) {
        this(_io, null, null);
    }

    public Uimage(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
        this(_io, _parent, null);
    }

    public Uimage(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, Uimage _root) {
        super(_io);
        this._parent = _parent;
        this._root = _root == null ? this : _root;
    }
    public void _read() {
        this.header = new Uheader(this._io, this, _root);
        this.header._read();
        this.data = this._io.readBytes(header().lenImage());
        _dirty = false;
    }

    public void _fetchInstances() {
        this.header._fetchInstances();
    }

    public void _write_Seq() {
        _assertNotDirty();
        this.header._write_Seq(this._io);
        this._io.writeBytes(this.data);
    }

    public void _check() {
        if (!Objects.equals(this.header._root(), _root()))
            throw new ConsistencyError("header", _root(), this.header._root());
        if (!Objects.equals(this.header._parent(), this))
            throw new ConsistencyError("header", this, this.header._parent());
        if (this.data.length != header().lenImage())
            throw new ConsistencyError("data", header().lenImage(), this.data.length);
        _dirty = false;
    }

    /**
     * ASUS firmware images [overlay the 32-byte image
     * name](https://github.com/drag0njoe/RT-AC55U/blob/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src/asustools/mkimage.src/include/image.h#L185-L188)
     * with a [`TAIL`
     * structure](https://github.com/drag0njoe/RT-AC55U/blob/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src/asustools/mkimage.src/include/image.h#L151-L171)
     * that contains version information and the product ID (model name).
     * 
     * The first 16 bytes have the same structure in all three known variants of
     * the `TAIL` structure. The meaning of the last 16 bytes differs across the
     * variants - therefore, for simplicity, this Kaitai Struct implementation
     * treats them as an opaque byte array named `extra_info`. For an overview of
     * the variants, see the documentation for `extra_info`.
     */
    public static class AsusFirmwareInformation extends KaitaiStruct.ReadWrite {
        public static AsusFirmwareInformation fromFile(String fileName) throws IOException {
            return new AsusFirmwareInformation(new ByteBufferKaitaiStream(fileName));
        }
        public AsusFirmwareInformation() {
            this(null, null, null);
        }

        public AsusFirmwareInformation(KaitaiStream _io) {
            this(_io, null, null);
        }

        public AsusFirmwareInformation(KaitaiStream _io, Uimage.NameOrAsusInfo _parent) {
            this(_io, _parent, null);
        }

        public AsusFirmwareInformation(KaitaiStream _io, Uimage.NameOrAsusInfo _parent, Uimage _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.kernelVersion = new Version(this._io, this, _root);
            this.kernelVersion._read();
            this.fsVersion = new Version(this._io, this, _root);
            this.fsVersion._read();
            this.productId = new String(KaitaiStream.bytesTerminate(this._io.readBytes(12), (byte) 0, false), StandardCharsets.UTF_8);
            this.extraInfo = this._io.readBytes(16);
            _dirty = false;
        }

        public void _fetchInstances() {
            this.kernelVersion._fetchInstances();
            this.fsVersion._fetchInstances();
        }

        public void _write_Seq() {
            _assertNotDirty();
            this.kernelVersion._write_Seq(this._io);
            this.fsVersion._write_Seq(this._io);
            this._io.writeBytesLimit((this.productId).getBytes(Charset.forName("UTF-8")), 12, (byte) 0, (byte) 0);
            this._io.writeBytes(this.extraInfo);
        }

        public void _check() {
            if (!Objects.equals(this.kernelVersion._root(), _root()))
                throw new ConsistencyError("kernel_version", _root(), this.kernelVersion._root());
            if (!Objects.equals(this.kernelVersion._parent(), this))
                throw new ConsistencyError("kernel_version", this, this.kernelVersion._parent());
            if (!Objects.equals(this.fsVersion._root(), _root()))
                throw new ConsistencyError("fs_version", _root(), this.fsVersion._root());
            if (!Objects.equals(this.fsVersion._parent(), this))
                throw new ConsistencyError("fs_version", this, this.fsVersion._parent());
            if ((this.productId).getBytes(Charset.forName("UTF-8")).length > 12)
                throw new ConsistencyError("product_id", 12, (this.productId).getBytes(Charset.forName("UTF-8")).length);
            if (KaitaiStream.byteArrayIndexOf((this.productId).getBytes(Charset.forName("UTF-8")), ((byte) 0)) != -1)
                throw new ConsistencyError("product_id", -1, KaitaiStream.byteArrayIndexOf((this.productId).getBytes(Charset.forName("UTF-8")), ((byte) 0)));
            if (this.extraInfo.length != 16)
                throw new ConsistencyError("extra_info", 16, this.extraInfo.length);
            _dirty = false;
        }
        private Version kernelVersion;
        private Version fsVersion;
        private String productId;
        private byte[] extraInfo;
        private Uimage _root;
        private Uimage.NameOrAsusInfo _parent;
        public Version kernelVersion() { return kernelVersion; }
        public void setKernelVersion(Version _v) { _dirty = true; kernelVersion = _v; }
        public Version fsVersion() { return fsVersion; }
        public void setFsVersion(Version _v) { _dirty = true; fsVersion = _v; }

        /**
         * In the Ralink SDK variant (see `extra_info`), the product ID field is
         * actually 23 bytes long, but no known product ID is longer than 12
         * bytes.
         */
        public String productId() { return productId; }
        public void setProductId(String _v) { _dirty = true; productId = _v; }

        /**
         * The structure of these bytes varies among the three known variants of
         * the `TAIL` structure, and the header doesn't say which one is used:
         * 
         * * Plain: 8 hardware versions (using the `version` type) - see
         *   <https://github.com/drag0njoe/RT-AC55U/blob/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src/asustools/mkimage.src/include/image.h#L170>
         * 
         *   Sample file: `RT-AC51U_3.0.0.4_380_8591-ga8dd632.trx` from
         *   <https://dlcdnets.asus.com/pub/ASUS/wireless/RT-AC51U/FW_RT_AC51U_30043808591.zip>,
         *   released on 2020-09-22 and listed on
         *   <https://www.asus.com/supportonly/rt-ac51u/helpdesk_bios/>.
         * 
         *   Source code links:
         * 
         *   - [ASUS GPL source, RT-AC55UHP
         *     3.0.0.4.382.51915](https://dlcdnets.asus.com/pub/ASUS/wireless/RT-AC55UHP/GPL_RT_AC55UHP_300438251915.zip)
         *     (`asuswrt/release/src/asustools/mkimage.src/` in
         *     `GPL_RT-AC55UHP_3.0.0.4.382.51915-g4086e57.tgz`)
         *   - [ASUS GPL source, RT-AC55U 3.0.0.4.382.50702 (unofficial
         *     mirror)](https://github.com/drag0njoe/RT-AC55U/tree/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src/asustools/mkimage.src)
         * 
         * * `TRX_NEW` (a macro defined when building `mkimage`): the
         *   build number and extended build number (`u2le` each), two key bytes
         *   and 5 hardware versions - see
         *   <https://github.com/drag0njoe/RT-AC55U/blob/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src-qca/asustools/mkimage.src/include/image.h#L174-L180>
         * 
         *   `mkimage` [overwrites the first 3 hardware
         *   versions](https://github.com/drag0njoe/RT-AC55U/blob/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src-qca/asustools/mkimage.src/mkimage.c#L546-L569)
         *   with values derived from the key bytes.
         * 
         *   Sample file: `RT-AC55UHP_3.0.0.4_382_52236-ga0f880d.trx` (build 382,
         *   extended build 52236) from
         *   <https://dlcdnets.asus.com/pub/ASUS/wireless/RT-AC55UHP/FW_RT_AC55UHP_300438252236.zip>,
         *   released on 2020-06-04 and listed on
         *   <https://www.asus.com/supportonly/rt-ac55uhp/helpdesk_bios/>.
         * 
         *   Source code links:
         * 
         *   - [ASUS GPL source, RT-AC55UHP
         *     3.0.0.4.382.51915](https://dlcdnets.asus.com/pub/ASUS/wireless/RT-AC55UHP/GPL_RT_AC55UHP_300438251915.zip)
         *     (`asuswrt/release/src-qca/asustools/mkimage.src/` in
         *     `GPL_RT-AC55UHP_3.0.0.4.382.51915-g4086e57.tgz`)
         *   - [ASUS GPL source, RT-AC55U 3.0.0.4.382.50702 (unofficial
         *     mirror)](https://github.com/drag0njoe/RT-AC55U/tree/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src-qca/asustools/mkimage.src)
         * 
         * * Ralink SDK: the rest of a 23-byte product ID (see `product_id`),
         *   a version suffix letter and the kernel part size (called `ih_ksz`) -
         *   see <https://github.com/andy-padavan/rt-n56u/blob/32a93db4026cc2cff585d7008373432d888fc1aa/trunk/tools/mkimage/include/image.h#L164-L165>
         * 
         *   Sample file: `RP-N12_1.0.1.1f.trx` from
         *   <https://dlcdnets.asus.com/pub/ASUS/wireless/RP-N12/FW_RP-N12_1.0.1.1f.zip>,
         *   released on 2019-11-12 and listed on
         *   <https://www.asus.com/supportonly/rp-n12/helpdesk_bios/>.
         * 
         *   Source code links:
         * 
         *   - [ASUS GPL source, RP-N12
         *     1.0.1.1f](https://dlcdnets.asus.com/pub/ASUS/wireless/RP-N12/GPL_RP_N12_1011f.zip)
         *     (`GPL_RP-N12_source.1011f/user/mkimage/include/image.h` in
         *     `GPL_RP-N12_source.1011f.tar.bz2`)
         *   - [rt-n56u custom firmware by Andy
         *     Padavan](https://github.com/andy-padavan/rt-n56u/blob/32a93db4026cc2cff585d7008373432d888fc1aa/trunk/tools/mkimage/include/image.h)
         * 
         * In the plain and `TRX_NEW` variants, the last 4 bytes may instead
         * contain the offset of the root file system, which is [written by
         * `mkimage -r`](https://github.com/drag0njoe/RT-AC55U/blob/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src/asustools/mkimage.src/mkimage.c#L527-L542)
         * as the magic byte 0xA9
         * ([`ROOTFS_OFFSET_MAGIC`](https://github.com/drag0njoe/RT-AC55U/blob/cf5874684ab6995ee07e91a52dfdd0562fcc2655/release/src/asustools/mkimage.src/include/image.h#L154-L159))
         * followed by the offset as a 24-bit big-endian integer.
         */
        public byte[] extraInfo() { return extraInfo; }
        public void setExtraInfo(byte[] _v) { _dirty = true; extraInfo = _v; }
        public Uimage _root() { return _root; }
        public void set_root(Uimage _v) { _dirty = true; _root = _v; }
        public Uimage.NameOrAsusInfo _parent() { return _parent; }
        public void set_parent(Uimage.NameOrAsusInfo _v) { _dirty = true; _parent = _v; }
    }
    public static class NameOrAsusInfo extends KaitaiStruct.ReadWrite {
        public static NameOrAsusInfo fromFile(String fileName) throws IOException {
            return new NameOrAsusInfo(new ByteBufferKaitaiStream(fileName));
        }
        public NameOrAsusInfo() {
            this(null, null, null);
        }

        public NameOrAsusInfo(KaitaiStream _io) {
            this(_io, null, null);
        }

        public NameOrAsusInfo(KaitaiStream _io, Uimage.Uheader _parent) {
            this(_io, _parent, null);
        }

        public NameOrAsusInfo(KaitaiStream _io, Uimage.Uheader _parent, Uimage _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            if (!(hasAsusInfo())) {
                this.name = new String(this._io.readBytesTerm((byte) 0, false, true, false), StandardCharsets.UTF_8);
            }
            if (hasAsusInfo()) {
                this.asusInfo = new AsusFirmwareInformation(this._io, this, _root);
                this.asusInfo._read();
            }
            _dirty = false;
        }

        public void _fetchInstances() {
            if (!(hasAsusInfo())) {
            }
            if (hasAsusInfo()) {
                this.asusInfo._fetchInstances();
            }
            byte0();
            if (this.byte0 != null) {
            }
            byte4();
            if (this.byte4 != null) {
            }
        }

        public void _write_Seq() {
            _assertNotDirty();
            _shouldWriteByte0 = _enabledByte0;
            _shouldWriteByte4 = _enabledByte4;
            if (!(hasAsusInfo())) {
                if (KaitaiStream.byteArrayIndexOf((this.name).getBytes(Charset.forName("UTF-8")), ((byte) 0)) != -1)
                    throw new ConsistencyError("name", -1, KaitaiStream.byteArrayIndexOf((this.name).getBytes(Charset.forName("UTF-8")), ((byte) 0)));
                this._io.writeBytes((this.name).getBytes(Charset.forName("UTF-8")));
                if (!(this._io.isEof())) {
                    this._io.writeU1(0);
                }
            }
            if (hasAsusInfo()) {
                if (!Objects.equals(this.asusInfo._root(), _root()))
                    throw new ConsistencyError("asus_info", _root(), this.asusInfo._root());
                if (!Objects.equals(this.asusInfo._parent(), this))
                    throw new ConsistencyError("asus_info", this, this.asusInfo._parent());
                this.asusInfo._write_Seq(this._io);
            }
        }

        public void _check() {
            if (_enabledByte0) {
            }
            if (_enabledByte4) {
            }
            _dirty = false;
        }
        private Integer byte0;
        private boolean _shouldWriteByte0 = false;
        private boolean _enabledByte0 = true;
        public Integer byte0() {
            if (_shouldWriteByte0)
                _writeByte0();
            if (this.byte0 != null)
                return this.byte0;
            if (!_enabledByte0)
                return null;
            long _pos = this._io.pos();
            this._io.seek(0);
            this.byte0 = this._io.readU1();
            this._io.seek(_pos);
            return this.byte0;
        }
        public void setByte0(int _v) { _dirty = true; byte0 = _v; }
        public void setByte0_Enabled(boolean _v) { _dirty = true; _enabledByte0 = _v; }

        private void _writeByte0() {
            _shouldWriteByte0 = false;
            long _pos = this._io.pos();
            this._io.seek(0);
            this._io.writeU1(this.byte0);
            this._io.seek(_pos);
        }
        private Integer byte4;
        private boolean _shouldWriteByte4 = false;
        private boolean _enabledByte4 = true;
        public Integer byte4() {
            if (_shouldWriteByte4)
                _writeByte4();
            if (this.byte4 != null)
                return this.byte4;
            if (!_enabledByte4)
                return null;
            long _pos = this._io.pos();
            this._io.seek(4);
            this.byte4 = this._io.readU1();
            this._io.seek(_pos);
            return this.byte4;
        }
        public void setByte4(int _v) { _dirty = true; byte4 = _v; }
        public void setByte4_Enabled(boolean _v) { _dirty = true; _enabledByte4 = _v; }

        private void _writeByte4() {
            _shouldWriteByte4 = false;
            long _pos = this._io.pos();
            this._io.seek(4);
            this._io.writeU1(this.byte4);
            this._io.seek(_pos);
        }
        private Boolean hasAsusInfo;

        /**
         * Whether this type stores ASUS firmware information (`asus_info`)
         * rather than an image name (`name`).
         * 
         * The header doesn't say which one it is, so we infer it using a
         * heuristic based on the layout of the `asus_firmware_information` type.
         * If ASUS firmware info is present, the first byte (the major kernel
         * version number) is a small integer (i.e. not a printable character),
         * and byte 4 (first byte of the product ID/model name) is a printable
         * character. An empty name is not mistaken for ASUS information, because
         * its byte 4 is a null byte.
         * 
         * The highest known major kernel version number is 9 (e.g. in
         * `DSL-N55U_9.0.0.4_380_3925-gad8f412_Annex_A.trx` from
         * <https://dlcdnets.asus.com/pub/ASUS/wireless/DSL-N55U/FW_DSL_N55U_90043803925.zip>,
         * released on 2016-08-04 and listed on
         * <https://www.asus.com/supportonly/dsl-n55u/helpdesk_bios/>). ASUS
         * seems to use such high major versions (9, sometimes 7) for beta
         * releases, whereas regular releases use 3 (or 1 in older firmware).
         * 
         * It can also be 0, in particular due to a bug in the MediaTek MT798X
         * build of ASUS's `mkimage` (it reads the `-V` values from fixed `argv`
         * positions, even though it parses the options using `getopt()` - see
         * <https://github.com/blocktrron/tuf-ax4200-gpl/blob/a4fe37f4d78473f97c6d47cf93d97c3811af90cc/release/src-mtk-MT798X/Uboot-upstream/tools/mkimage.c#L317-L333>).
         * For example, this is the case in
         * `RT-AX52_3.0.0.4_388_34015-g9488655.trx` from
         * <https://dlcdnets.asus.com/pub/ASUS/wireless/RT-AX52/FW_RT-AX52_300438834015.zip>,
         * released on 2026-03-20 and listed on
         * <https://www.asus.com/supportonly/rt-ax52/helpdesk_bios?model2Name=RT-AX52>.
         */
        public Boolean hasAsusInfo() {
            if (this.hasAsusInfo != null)
                return this.hasAsusInfo;
            this.hasAsusInfo =  ((byte0() < 32) && (byte4() >= 32) && (byte4() < 127)) ;
            return this.hasAsusInfo;
        }
        public void _invalidateHasAsusInfo() { this.hasAsusInfo = null; }
        private String name;
        private AsusFirmwareInformation asusInfo;
        private Uimage _root;
        private Uimage.Uheader _parent;
        public String name() { return name; }
        public void setName(String _v) { _dirty = true; name = _v; }
        public AsusFirmwareInformation asusInfo() { return asusInfo; }
        public void setAsusInfo(AsusFirmwareInformation _v) { _dirty = true; asusInfo = _v; }
        public Uimage _root() { return _root; }
        public void set_root(Uimage _v) { _dirty = true; _root = _v; }
        public Uimage.Uheader _parent() { return _parent; }
        public void set_parent(Uimage.Uheader _v) { _dirty = true; _parent = _v; }
    }
    public static class Uheader extends KaitaiStruct.ReadWrite {
        public static Uheader fromFile(String fileName) throws IOException {
            return new Uheader(new ByteBufferKaitaiStream(fileName));
        }
        public Uheader() {
            this(null, null, null);
        }

        public Uheader(KaitaiStream _io) {
            this(_io, null, null);
        }

        public Uheader(KaitaiStream _io, Uimage _parent) {
            this(_io, _parent, null);
        }

        public Uheader(KaitaiStream _io, Uimage _parent, Uimage _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.magic = Uimage.MagicTypes.byId(this._io.readU4be());
            if (this.magic == null) {
                throw new KaitaiStream.ValidationNotInEnumError(this.magic, this._io, "/types/uheader/seq/0");
            }
            this.headerCrc = this._io.readU4be();
            this.timestamp = this._io.readU4be();
            this.lenImage = this._io.readU4be();
            this.loadAddress = this._io.readU4be();
            this.entryAddress = this._io.readU4be();
            this.dataCrc = this._io.readU4be();
            this.osType = Uimage.UimageOs.byId(this._io.readU1());
            if (this.osType == null) {
                throw new KaitaiStream.ValidationNotInEnumError(this.osType, this._io, "/types/uheader/seq/7");
            }
            this.architecture = Uimage.UimageArch.byId(this._io.readU1());
            if (this.architecture == null) {
                throw new KaitaiStream.ValidationNotInEnumError(this.architecture, this._io, "/types/uheader/seq/8");
            }
            this.imageType = Uimage.UimageType.byId(this._io.readU1());
            if (this.imageType == null) {
                throw new KaitaiStream.ValidationNotInEnumError(this.imageType, this._io, "/types/uheader/seq/9");
            }
            this.compressionType = Uimage.UimageComp.byId(this._io.readU1());
            if (this.compressionType == null) {
                throw new KaitaiStream.ValidationNotInEnumError(this.compressionType, this._io, "/types/uheader/seq/10");
            }
            this._raw_nameOrAsusInfo = this._io.readBytes(32);
            KaitaiStream _io__raw_nameOrAsusInfo = new ByteBufferKaitaiStream(this._raw_nameOrAsusInfo);
            this.nameOrAsusInfo = new NameOrAsusInfo(_io__raw_nameOrAsusInfo, this, _root);
            this.nameOrAsusInfo._read();
            _dirty = false;
        }

        public void _fetchInstances() {
            this.nameOrAsusInfo._fetchInstances();
        }

        public void _write_Seq() {
            _assertNotDirty();
            this._io.writeU4be(((Number) (this.magic.id())).longValue());
            this._io.writeU4be(this.headerCrc);
            this._io.writeU4be(this.timestamp);
            this._io.writeU4be(this.lenImage);
            this._io.writeU4be(this.loadAddress);
            this._io.writeU4be(this.entryAddress);
            this._io.writeU4be(this.dataCrc);
            this._io.writeU1(((Number) (this.osType.id())).intValue());
            this._io.writeU1(((Number) (this.architecture.id())).intValue());
            this._io.writeU1(((Number) (this.imageType.id())).intValue());
            this._io.writeU1(((Number) (this.compressionType.id())).intValue());
            final KaitaiStream _io__raw_nameOrAsusInfo = new ByteBufferKaitaiStream(32);
            this._io.addChildStream(_io__raw_nameOrAsusInfo);
            {
                long _pos2 = this._io.pos();
                this._io.seek(this._io.pos() + (32));
                final Uheader _this = this;
                _io__raw_nameOrAsusInfo.setWriteBackHandler(new KaitaiStream.WriteBackHandler(_pos2) {
                    @Override
                    protected void write(KaitaiStream parent) {
                        _this._raw_nameOrAsusInfo = _io__raw_nameOrAsusInfo.toByteArray();
                        if (_this._raw_nameOrAsusInfo.length != 32)
                            throw new ConsistencyError("raw(name_or_asus_info)", 32, _this._raw_nameOrAsusInfo.length);
                        parent.writeBytes(_this._raw_nameOrAsusInfo);
                    }
                });
            }
            this.nameOrAsusInfo._write_Seq(_io__raw_nameOrAsusInfo);
        }

        public void _check() {
            if (this.magic == null) {
                throw new KaitaiStream.ValidationNotInEnumError(this.magic, null, "/types/uheader/seq/0");
            }
            if (this.osType == null) {
                throw new KaitaiStream.ValidationNotInEnumError(this.osType, null, "/types/uheader/seq/7");
            }
            if (this.architecture == null) {
                throw new KaitaiStream.ValidationNotInEnumError(this.architecture, null, "/types/uheader/seq/8");
            }
            if (this.imageType == null) {
                throw new KaitaiStream.ValidationNotInEnumError(this.imageType, null, "/types/uheader/seq/9");
            }
            if (this.compressionType == null) {
                throw new KaitaiStream.ValidationNotInEnumError(this.compressionType, null, "/types/uheader/seq/10");
            }
            if (!Objects.equals(this.nameOrAsusInfo._root(), _root()))
                throw new ConsistencyError("name_or_asus_info", _root(), this.nameOrAsusInfo._root());
            if (!Objects.equals(this.nameOrAsusInfo._parent(), this))
                throw new ConsistencyError("name_or_asus_info", this, this.nameOrAsusInfo._parent());
            _dirty = false;
        }
        private MagicTypes magic;
        private long headerCrc;
        private long timestamp;
        private long lenImage;
        private long loadAddress;
        private long entryAddress;
        private long dataCrc;
        private UimageOs osType;
        private UimageArch architecture;
        private UimageType imageType;
        private UimageComp compressionType;
        private NameOrAsusInfo nameOrAsusInfo;
        private Uimage _root;
        private Uimage _parent;
        private byte[] _raw_nameOrAsusInfo;

        /**
         * Besides the standard U-Boot magic, some vendors' bootloaders expect
         * their own value (often a different one for each device model), with
         * the rest of the header unchanged. `magic_types` lists the values
         * known so far; images with any other magic are rejected.
         */
        public MagicTypes magic() { return magic; }
        public void setMagic(MagicTypes _v) { _dirty = true; magic = _v; }
        public long headerCrc() { return headerCrc; }
        public void setHeaderCrc(long _v) { _dirty = true; headerCrc = _v; }
        public long timestamp() { return timestamp; }
        public void setTimestamp(long _v) { _dirty = true; timestamp = _v; }
        public long lenImage() { return lenImage; }
        public void setLenImage(long _v) { _dirty = true; lenImage = _v; }
        public long loadAddress() { return loadAddress; }
        public void setLoadAddress(long _v) { _dirty = true; loadAddress = _v; }
        public long entryAddress() { return entryAddress; }
        public void setEntryAddress(long _v) { _dirty = true; entryAddress = _v; }
        public long dataCrc() { return dataCrc; }
        public void setDataCrc(long _v) { _dirty = true; dataCrc = _v; }
        public UimageOs osType() { return osType; }
        public void setOsType(UimageOs _v) { _dirty = true; osType = _v; }
        public UimageArch architecture() { return architecture; }
        public void setArchitecture(UimageArch _v) { _dirty = true; architecture = _v; }
        public UimageType imageType() { return imageType; }
        public void setImageType(UimageType _v) { _dirty = true; imageType = _v; }
        public UimageComp compressionType() { return compressionType; }
        public void setCompressionType(UimageComp _v) { _dirty = true; compressionType = _v; }
        public NameOrAsusInfo nameOrAsusInfo() { return nameOrAsusInfo; }
        public void setNameOrAsusInfo(NameOrAsusInfo _v) { _dirty = true; nameOrAsusInfo = _v; }
        public Uimage _root() { return _root; }
        public void set_root(Uimage _v) { _dirty = true; _root = _v; }
        public Uimage _parent() { return _parent; }
        public void set_parent(Uimage _v) { _dirty = true; _parent = _v; }
        public byte[] _raw_nameOrAsusInfo() { return _raw_nameOrAsusInfo; }
        public void set_raw_NameOrAsusInfo(byte[] _v) { _dirty = true; _raw_nameOrAsusInfo = _v; }
    }
    public static class Version extends KaitaiStruct.ReadWrite {
        public static Version fromFile(String fileName) throws IOException {
            return new Version(new ByteBufferKaitaiStream(fileName));
        }
        public Version() {
            this(null, null, null);
        }

        public Version(KaitaiStream _io) {
            this(_io, null, null);
        }

        public Version(KaitaiStream _io, Uimage.AsusFirmwareInformation _parent) {
            this(_io, _parent, null);
        }

        public Version(KaitaiStream _io, Uimage.AsusFirmwareInformation _parent, Uimage _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.major = this._io.readU1();
            this.minor = this._io.readU1();
            _dirty = false;
        }

        public void _fetchInstances() {
        }

        public void _write_Seq() {
            _assertNotDirty();
            this._io.writeU1(this.major);
            this._io.writeU1(this.minor);
        }

        public void _check() {
            _dirty = false;
        }
        private int major;
        private int minor;
        private Uimage _root;
        private Uimage.AsusFirmwareInformation _parent;
        public int major() { return major; }
        public void setMajor(int _v) { _dirty = true; major = _v; }
        public int minor() { return minor; }
        public void setMinor(int _v) { _dirty = true; minor = _v; }
        public Uimage _root() { return _root; }
        public void set_root(Uimage _v) { _dirty = true; _root = _v; }
        public Uimage.AsusFirmwareInformation _parent() { return _parent; }
        public void set_parent(Uimage.AsusFirmwareInformation _v) { _dirty = true; _parent = _v; }
    }
    private Uheader header;
    private byte[] data;
    private Uimage _root;
    private KaitaiStruct.ReadWrite _parent;
    public Uheader header() { return header; }
    public void setHeader(Uheader _v) { _dirty = true; header = _v; }
    public byte[] data() { return data; }
    public void setData(byte[] _v) { _dirty = true; data = _v; }
    public Uimage _root() { return _root; }
    public void set_root(Uimage _v) { _dirty = true; _root = _v; }
    public KaitaiStruct.ReadWrite _parent() { return _parent; }
    public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
}