UF2 (USB Flashing Format): C# parsing library

UF2 is a file format, developed by Microsoft for PXT (also known as Microsoft MakeCode), that is particularly suitable for flashing microcontrollers over MSC (Mass Storage Class; aka removable flash drive).

The list of family IDs is stored in a separate JSON file:

https://github.com/microsoft/uf2/blob/master/utils/uf2families.json

This JSON file is regularly updated. The family_id enum should be kept in sync with it. For simplicity and consistency, it's strongly recommended to auto-generate this enum from uf2families.json directly. This was last done using the following commands:

$ curl -fsSLO https://github.com/microsoft/uf2/raw/90e9741f217f5a40c98ba74d663e408041037578/utils/uf2families.json
$ jq -r '
  .[]
  | "    \(.id | ascii_downcase):\n"
  + "      id: \(.short_name | ascii_downcase | gsub("-"; "_"))\n"
  + "      doc: \(.description | tojson)"
' uf2families.json

Test files, picked to cover as many different shapes as possible:

File extension

uf2

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of UF2 (USB Flashing Format) using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All C# code generated by Kaitai Struct depends on the Kaitai Struct runtime library for C#. You must add this dependency to your project before you can parse or serialize any data.

The C# runtime library is available in the NuGet Gallery. Installation instructions can also be found there.

Code

Parse a local file and get structure in memory:

var data = Uf2.FromFile("path/to/local/file.uf2");

Or parse structure from a byte array:

byte[] someArray = new byte[] { ... };
var data = new Uf2(new KaitaiStream(someArray));

After that, one can get various attributes from the structure by accessing properties like:

data.FirstBlock // => get first block

C# source code to parse UF2 (USB Flashing Format)

Uf2.cs

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

using System.Collections.Generic;

namespace Kaitai
{

    /// <summary>
    /// UF2 is a file format, developed by Microsoft for PXT (also known as
    /// Microsoft MakeCode), that is particularly suitable for flashing
    /// microcontrollers over MSC (Mass Storage Class; aka removable flash drive).
    /// 
    /// The list of family IDs is stored in a separate JSON file:
    /// 
    /// &lt;https://github.com/microsoft/uf2/blob/master/utils/uf2families.json&gt;
    /// 
    /// This JSON file is regularly updated. The `family_id` enum should be kept in
    /// sync with it. For simplicity and consistency, it's strongly recommended to
    /// auto-generate this enum from `uf2families.json` directly. This was last done
    /// using the following commands:
    /// 
    /// ```bash
    /// $ curl -fsSLO https://github.com/microsoft/uf2/raw/90e9741f217f5a40c98ba74d663e408041037578/utils/uf2families.json
    /// $ jq -r '
    ///   .[]
    ///   | &quot;    \(.id | ascii_downcase):\n&quot;
    ///   + &quot;      id: \(.short_name | ascii_downcase | gsub(&quot;-&quot;; &quot;_&quot;))\n&quot;
    ///   + &quot;      doc: \(.description | tojson)&quot;
    /// ' uf2families.json
    /// ```
    /// 
    /// Test files, picked to cover as many different shapes as possible:
    /// 
    /// * &lt;https://micropython.org/download/RPI_PICO/&gt; - a typical case: all blocks
    ///   have the same family ID and `num_blocks`.
    /// * &lt;https://micropython.org/download/RPI_PICO2/&gt; - these .uf2 files are
    ///   actually two UF2 files concatenated. The first UF2 file is the standalone
    ///   `family_id::rp2xxx_absolute` block that `picotool` prepends (see the
    ///   `is_rp2350_e10_block` value instance in the `block` type).
    /// * &lt;https://circuitpython.org/downloads&gt; - the builds for SAMD boards (for
    ///   example
    ///   [Feather M0 Express](https://circuitpython.org/board/feather_m0_express/))
    ///   set no flags at all, so the field at offset 28 is read as `file_size` rather
    ///   than `family_id`.
    /// * &lt;https://github.com/raspberrypi/pico-sdk-prebuilts/releases&gt; -
    ///   [Universal UF2](https://github.com/raspberrypi/pico-examples/blob/c81c855ffdedc825975a40ba357723a71358ddf0/universal/README.md#universal-binary-vs-universal-uf2)
    ///   files, which are again two UF2 files concatenated. What's interesting about
    ///   these is that the last block of the first file (with the family ID
    ///   `family_id::rp2040`) is moved to the end of the second file, which was
    ///   intended for `family_id::rp2xxx_absolute` (see
    ///   &lt;https://github.com/raspberrypi/pico-examples/blob/c81c855ffdedc825975a40ba357723a71358ddf0/universal/CMakeLists.txt#L159-L169&gt;).
    /// * &lt;https://github.com/microsoft/pxt-microsoft-boot-sequence/releases&gt; - some
    ///   files contain blocks with the `flags.is_file_container` flag set (for
    ///   example
    ///   &lt;https://github.com/microsoft/pxt-microsoft-boot-sequence/releases/download/v0.0.4/arcade-p0.uf2&gt;
    ///   is a pure file container for
    ///   `file_name == &quot;Projects/microsoft-boot-sequence.elf&quot;`). Generated by
    ///   MakeCode, the only known producer of file container UF2s.
    /// * &lt;https://github.com/umi-eng/uftwo/tree/35bccf75b4f81c43f088696a8c4a9912f1f4104e/uftwo/tests&gt; -
    ///   synthetic test files for features that real firmware doesn't seem to use
    ///   (e.g. MD5 checksums).
    /// </summary>
    /// <remarks>
    /// Reference: <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md">Source</a>
    /// </remarks>
    public partial class Uf2 : KaitaiStruct
    {
        public static Uf2 FromFile(string fileName)
        {
            return new Uf2(new KaitaiStream(fileName));
        }


        public enum ExtensionTagType
        {
            End = 0,
            PageSize = 780791,
            Description = 6622621,
            Rp2IgnoreBlock = 10049507,
            Version = 10471356,
            Sha2Checksum = 11824560,
            DeviceTypeId = 13149993,
        }

        public enum FamilyId
        {
            Stm32l4 = 16738585,
            Stm32l5 = 69471199,
            Stm32f411xc = 114362747,
            M0sense = 299792458,
            Atmega32 = 374814231,
            Saml21 = 407992330,
            Nrf52 = 458716255,
            Esp32 = 475996592,
            Stm32l1 = 505365293,
            Stm32l0 = 539900561,
            Stm32wl = 558239728,
            Rtl8710b = 585160444,
            Lpc55 = 716994540,
            Esp32c2 = 730387100,
            Stm32f411xe = 767756741,
            Stm32g0 = 806311475,
            Esp32s31 = 822212545,
            Gd32f350 = 835856582,
            Esp32h2 = 858203894,
            Rtl8720d = 863621090,
            Esp32p4 = 1026592404,
            MaixplayU4 = 1265126769,
            Stm32g4 = 1282483210,
            Stm32h5 = 1318001757,
            Csk4 = 1332399698,
            Mimxrt10xx = 1337120189,
            Xr809 = 1374225320,
            Stm32f7 = 1404571392,
            Esp32c6 = 1410195298,
            Samd51 = 1427194976,
            Stm32f4 = 1467308631,
            Fx2 = 1511523995,
            Stm32f2 = 1561987630,
            Stm32f1 = 1591873650,
            Nrf52833 = 1646171002,
            Stm32f0 = 1685595318,
            Bk7231u = 1733968048,
            Samd21 = 1760373640,
            Ch32v = 1771791084,
            Bk7251 = 1786956866,
            Stm32f3 = 1803837832,
            Stm32f407 = 1829315322,
            Stm32h7 = 1840668802,
            Csk6 = 1853049000,
            Nrf52832xxab = 1869948536,
            Stm32wb = 1892771411,
            Nrf52832xxaa = 1920081230,
            Max32690 = 1947226634,
            Esp32c61 = 2010665156,
            Bk7231n = 2067722800,
            Ra4m1 = 2078840685,
            Py32f071Uvk5V3 = 2105173743,
            Esp8266 = 2125160941,
            Kl32l2 = 2139350931,
            Nrf52820 = 2181929567,
            Stm32f407vg = 2410701054,
            Max78002 = 2446589208,
            Rza1lu = 2501329455,
            Gd32vf103 = 2599435827,
            Esp32h4 = 2651564682,
            Rtl8710a = 2684343619,
            At32f415 = 2697558926,
            Nrf52840 = 2913282112,
            Esp32h21 = 3067936943,
            Esp32s2 = 3218951918,
            Esp32s3 = 3296614247,
            Esp32c3 = 3559628908,
            Max32650 = 3594487346,
            Bl602 = 3725750455,
            Rtl8720c = 3767498084,
            Rp2040 = 3834380118,
            Rp2xxxAbsolute = 3834380119,
            Rp2xxxData = 3834380120,
            Rp2350ArmS = 3834380121,
            Rp2350Riscv = 3834380122,
            Rp2350ArmNs = 3834380123,
            Max32666 = 4039314801,
            Esp32c5 = 4145808195,
        }
        public Uf2(KaitaiStream p__io, KaitaiStruct p__parent = null, Uf2 p__root = null) : base(p__io)
        {
            m_parent = p__parent;
            m_root = p__root ?? this;
            _read();
        }
        private void _read()
        {
            _firstBlock = new Block(m_io, this, m_root);
            _blocks = new List<Block>();
            for (var i = 0; i < FirstBlock.NumBlocks - 1; i++)
            {
                _blocks.Add(new Block(m_io, this, m_root));
            }
        }
        public partial class Block : KaitaiStruct
        {
            public static Block FromFile(string fileName)
            {
                return new Block(new KaitaiStream(fileName));
            }

            public Block(KaitaiStream p__io, Uf2 p__parent = null, Uf2 p__root = null) : base(p__io)
            {
                m_parent = p__parent;
                m_root = p__root;
                f_isRp2350E10Block = false;
                f_numBlocks = false;
                _read();
            }
            private void _read()
            {
                _magic = m_io.ReadBytes(4);
                if (!((KaitaiStream.ByteArrayCompare(_magic, new byte[] { 85, 70, 50, 10 }) == 0)))
                {
                    throw new ValidationNotEqualError(new byte[] { 85, 70, 50, 10 }, _magic, m_io, "/types/block/seq/0");
                }
                _secondMagic = m_io.ReadBytes(4);
                if (!((KaitaiStream.ByteArrayCompare(_secondMagic, new byte[] { 87, 81, 93, 158 }) == 0)))
                {
                    throw new ValidationNotEqualError(new byte[] { 87, 81, 93, 158 }, _secondMagic, m_io, "/types/block/seq/1");
                }
                _flags = new Flags(m_io, this, m_root);
                _targetAddress = m_io.ReadU4le();
                {
                    uint M_ = _targetAddress;
                    if (!(KaitaiStream.Mod(M_, 4) == 0))
                    {
                        throw new ValidationExprError(_targetAddress, m_io, "/types/block/seq/3");
                    }
                }
                _lenPayload = m_io.ReadU4le();
                {
                    uint M_ = _lenPayload;
                    if (!(KaitaiStream.Mod(M_, 4) == 0))
                    {
                        throw new ValidationExprError(_lenPayload, m_io, "/types/block/seq/4");
                    }
                }
                _blockNumber = m_io.ReadU4le();
                _numBlocksRaw = m_io.ReadU4le();
                if (!(_numBlocksRaw >= BlockNumber + 1))
                {
                    throw new ValidationLessThanError(BlockNumber + 1, _numBlocksRaw, m_io, "/types/block/seq/6");
                }
                if (!(Flags.HasFamilyId)) {
                    _fileSize = m_io.ReadU4le();
                }
                if (Flags.HasFamilyId) {
                    _familyId = ((Uf2.FamilyId) m_io.ReadU4le());
                }
                __raw_data = m_io.ReadBytes(476);
                var io___raw_data = new KaitaiStream(__raw_data);
                _data = new BlockData(io___raw_data, this, m_root);
                _finalMagic = m_io.ReadBytes(4);
                if (!((KaitaiStream.ByteArrayCompare(_finalMagic, new byte[] { 48, 111, 177, 10 }) == 0)))
                {
                    throw new ValidationNotEqualError(new byte[] { 48, 111, 177, 10 }, _finalMagic, m_io, "/types/block/seq/10");
                }
            }
            private bool f_isRp2350E10Block;
            private bool _isRp2350E10Block;

            /// <summary>
            /// Determines whether this is a block that `picotool` prepends to RP2350
            /// flash images as a workaround for erratum RP2350-E10 (i.e. a hardware
            /// bug in the A2 version of the RP2350 boot ROM).
            /// 
            /// Such a block is always written on its own, but its `num_blocks_raw` is
            /// set to 2. If we trusted this value, we would attempt to read one block
            /// too many (which would most likely fail). Therefore, we must correct it
            /// to 1 before using it.
            /// 
            /// The conditions for detecting this block come from the
            /// [`check_abs_block()`](https://github.com/raspberrypi/picotool/blob/6f6458d792b93685a11423b244a585eaa99eafcf/elf2uf2/elf2uf2.cpp#L147)
            /// function in `picotool`. However, there are some differences:
            /// 
            /// 1. In Kaitai Struct, we cannot easily check whether all 256 payload
            ///    bytes are set to `0xef`, so we only check the first and last bytes.
            /// 2. There are .uf2 files in the wild where `flags.has_extension_tags`
            ///    is true, but there are actually no extension tags (the first and
            ///    only tag has a size of 0, which is just a terminator), so our
            ///    condition allows for this case. You can download an example of such
            ///    a .uf2 file here:
            ///    &lt;https://github.com/neednotapply/DC32-cfw/releases/tag/1.69.13.37&gt;
            /// 
            /// It's worth noting that we cannot require the presence of the
            /// `extension_tag_type::rp2_ignore_block`
            /// (`UF2_EXTENSION_RP2_IGNORE_BLOCK`) tag because the UF2 files generated
            /// by `picotool` prior to
            /// &lt;https://github.com/raspberrypi/picotool/commit/78c9bd121b09399823b67ee7ea89003ca0d3315f&gt;
            /// don't have it. Therefore, we check whether this tag is present only if
            /// `flags.has_extension_tags` is set.
            /// 
            /// Test .uf2 files with this special block can be downloaded from
            /// &lt;https://micropython.org/download/RPI_PICO2/&gt;. Note that all the .uf2
            /// files there are actually two UF2 (sub)files concatenated, and this
            /// Kaitai Struct implementation parses only one at a time (so in order to
            /// parse both, you need something like the helper spec `uf2_files.ksy`
            /// from
            /// &lt;https://github.com/kaitai-io/kaitai_struct_formats/pull/542#discussion_r3906386820&gt;).
            /// v1.24.x releases predate the `extension_tag_type::rp2_ignore_block`
            /// tag, while releases v1.25.0 and later include it.
            /// </summary>
            /// <remarks>
            /// Reference: <a href="https://github.com/raspberrypi/picotool/blob/6f6458d792b93685a11423b244a585eaa99eafcf/elf2uf2/elf2uf2.cpp#L147">Git tag "2.3.0"</a>
            /// </remarks>
            /// <remarks>
            /// Reference: <a href="https://github.com/raspberrypi/picotool/commit/78c9bd121b09399823b67ee7ea89003ca0d3315f">Source</a>
            /// </remarks>
            public bool IsRp2350E10Block
            {
                get
                {
                    if (f_isRp2350E10Block)
                        return _isRp2350E10Block;
                    f_isRp2350E10Block = true;
                    _isRp2350E10Block = (bool) ( (( ((Flags.Value == 8192) || (Flags.Value == 40960)) ) && (FamilyId == Uf2.FamilyId.Rp2xxxAbsolute) && (NumBlocksRaw == 2) && (BlockNumber == 0) && (LenPayload == 256) && (Data.Payload[0] == 239) && (Data.Payload[Data.Payload.Length - 1] == 239) && ( ((!(Flags.HasExtensionTags)) || (Data.ExtensionTags[0].LenTag == 0) || ( ((Data.ExtensionTags[0].LenTag == 4) && (Data.ExtensionTags[0].TagType == Uf2.ExtensionTagType.Rp2IgnoreBlock)) )) )) );
                    return _isRp2350E10Block;
                }
            }
            private bool f_numBlocks;
            private uint _numBlocks;
            public uint NumBlocks
            {
                get
                {
                    if (f_numBlocks)
                        return _numBlocks;
                    f_numBlocks = true;
                    _numBlocks = (uint) ((IsRp2350E10Block ? 1 : NumBlocksRaw));
                    return _numBlocks;
                }
            }
            private byte[] _magic;
            private byte[] _secondMagic;
            private Flags _flags;
            private uint _targetAddress;
            private uint _lenPayload;
            private uint _blockNumber;
            private uint _numBlocksRaw;
            private uint? _fileSize;
            private FamilyId _familyId;
            private BlockData _data;
            private byte[] _finalMagic;
            private Uf2 m_root;
            private Uf2 m_parent;
            private byte[] __raw_data;
            public byte[] Magic { get { return _magic; } }
            public byte[] SecondMagic { get { return _secondMagic; } }
            public Flags Flags { get { return _flags; } }

            /// <summary>
            /// Address in flash where `data.payload` should be written, or an offset
            /// in the file specified by `data.file_name` if `flags.is_file_container`
            /// is set.
            /// 
            /// The [official
            /// spec](https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#payload-sizes)
            /// says:
            /// 
            /// &gt; In any event, payload size and target address should always be
            /// &gt; 4-byte aligned.
            /// </summary>
            public uint TargetAddress { get { return _targetAddress; } }

            /// <summary>
            /// The [official
            /// spec](https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#payload-sizes)
            /// says:
            /// 
            /// &gt; In any event, payload size and target address should always be
            /// &gt; 4-byte aligned.
            /// </summary>
            public uint LenPayload { get { return _lenPayload; } }
            public uint BlockNumber { get { return _blockNumber; } }

            /// <summary>
            /// Number of blocks that make up the UF2 file to which this block
            /// belongs. Every block of a file has the same value. It is at least 1
            /// and every `block_number` in the file must be less than this value
            /// (which is validated by this Kaitai Struct implementation).
            /// </summary>
            public uint NumBlocksRaw { get { return _numBlocksRaw; } }

            /// <summary>
            /// Size of the file this block belongs to, but only if
            /// `flags.is_file_container` is true. Otherwise, the official spec allows
            /// this field to be set to anything - though in practice, it's always
            /// zero.
            /// </summary>
            public uint? FileSize { get { return _fileSize; } }
            public FamilyId FamilyId { get { return _familyId; } }
            public BlockData Data { get { return _data; } }
            public byte[] FinalMagic { get { return _finalMagic; } }
            public Uf2 M_Root { get { return m_root; } }
            public Uf2 M_Parent { get { return m_parent; } }
            public byte[] M_RawData { get { return __raw_data; } }
        }
        public partial class BlockData : KaitaiStruct
        {
            public static BlockData FromFile(string fileName)
            {
                return new BlockData(new KaitaiStream(fileName));
            }

            public BlockData(KaitaiStream p__io, Uf2.Block p__parent = null, Uf2 p__root = null) : base(p__io)
            {
                m_parent = p__parent;
                m_root = p__root;
                f_md5Checksum = false;
                _read();
            }
            private void _read()
            {
                _payload = m_io.ReadBytes(M_Parent.LenPayload);
                if (M_Parent.Flags.IsFileContainer) {
                    _fileName = System.Text.Encoding.GetEncoding("UTF-8").GetString(m_io.ReadBytesTerm(0, false, true, true));
                }
                if (M_Parent.Flags.HasExtensionTags) {
                    _extensionTags = new List<ExtensionTag>();
                    {
                        var i = 0;
                        ExtensionTag M_;
                        do {
                            M_ = new ExtensionTag(m_io, this, m_root);
                            _extensionTags.Add(M_);
                            i++;
                        } while (!(M_.LenTag == 0));
                    }
                }
            }
            private bool f_md5Checksum;
            private Md5Checksum _md5Checksum;

            /// <summary>
            /// Describes a region that doesn't need to be flashed again if the
            /// checksum matches.
            /// 
            /// The [official
            /// spec](https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#md5-checksum)
            /// says that &quot;This is currently only used on ESP32&quot;, but no real-world
            /// firmware sample has been found (at least none of the .uf2 files from
            /// &lt;https://github.com/adafruit/tinyuf2/releases&gt;,
            /// [MicroPython](https://micropython.org/download/) or
            /// [CircuitPython](https://circuitpython.org/downloads) contain it). It
            /// was found only in some synthetic test files, e.g.
            /// &lt;https://github.com/umi-eng/uftwo/blob/35bccf75b4f81c43f088696a8c4a9912f1f4104e/uftwo/tests/checksum_256.uf2&gt;.
            /// </summary>
            public Md5Checksum Md5Checksum
            {
                get
                {
                    if (f_md5Checksum)
                        return _md5Checksum;
                    f_md5Checksum = true;
                    if (M_Parent.Flags.HasMd5Checksum) {
                        long _pos = m_io.Pos;
                        m_io.Seek(M_Io.Size - 24);
                        _md5Checksum = new Md5Checksum(m_io, this, m_root);
                        m_io.Seek(_pos);
                    }
                    return _md5Checksum;
                }
            }
            private byte[] _payload;
            private string _fileName;
            private List<ExtensionTag> _extensionTags;
            private Uf2 m_root;
            private Uf2.Block m_parent;

            /// <summary>
            /// The bytes to be written to `_parent.target_address`, which is either
            /// an address in flash, or an offset in the file specified by `file_name`
            /// if `_parent.flags.is_file_container` is set.
            /// </summary>
            public byte[] Payload { get { return _payload; } }
            public string FileName { get { return _fileName; } }
            public List<ExtensionTag> ExtensionTags { get { return _extensionTags; } }
            public Uf2 M_Root { get { return m_root; } }
            public Uf2.Block M_Parent { get { return m_parent; } }
        }

        /// <remarks>
        /// Reference: <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#extension-tags">Source</a>
        /// </remarks>
        public partial class ExtensionTag : KaitaiStruct
        {
            public static ExtensionTag FromFile(string fileName)
            {
                return new ExtensionTag(new KaitaiStream(fileName));
            }

            public ExtensionTag(KaitaiStream p__io, Uf2.BlockData p__parent = null, Uf2 p__root = null) : base(p__io)
            {
                m_parent = p__parent;
                m_root = p__root;
                f_lenValue = false;
                f_minLenTag = false;
                _read();
            }
            private void _read()
            {
                _lenTag = m_io.ReadU1();
                {
                    byte M_ = _lenTag;
                    if (!( ((M_ == 0) || (M_ >= MinLenTag)) ))
                    {
                        throw new ValidationExprError(_lenTag, m_io, "/types/extension_tag/seq/0");
                    }
                }
                _tagType = ((Uf2.ExtensionTagType) m_io.ReadBitsIntLe(24));
                m_io.AlignToByte();
                if (LenTag != 0) {
                    _value = m_io.ReadBytes(LenValue);
                }
                _padding = m_io.ReadBytes(KaitaiStream.Mod(-(LenTag), 4));
            }
            private bool f_lenValue;
            private int _lenValue;
            public int LenValue
            {
                get
                {
                    if (f_lenValue)
                        return _lenValue;
                    f_lenValue = true;
                    _lenValue = (int) ((LenTag >= MinLenTag ? LenTag - MinLenTag : 0));
                    return _lenValue;
                }
            }
            private bool f_minLenTag;
            private int _minLenTag;
            public int MinLenTag
            {
                get
                {
                    if (f_minLenTag)
                        return _minLenTag;
                    f_minLenTag = true;
                    _minLenTag = (int) (1 + 3);
                    return _minLenTag;
                }
            }
            private byte _lenTag;
            private ExtensionTagType _tagType;
            private byte[] _value;
            private byte[] _padding;
            private Uf2 m_root;
            private Uf2.BlockData m_parent;

            /// <summary>
            /// Total size of the tag in bytes, including this byte and `tag_type`, so
            /// at least 4. The exception is the last tag which terminates the list -
            /// it specifies a total size of 0.
            /// </summary>
            public byte LenTag { get { return _lenTag; } }
            public ExtensionTagType TagType { get { return _tagType; } }
            public byte[] Value { get { return _value; } }

            /// <summary>
            /// Tags are 4-byte aligned, so a tag whose size is not a multiple
            /// of 4 is followed by padding.
            /// </summary>
            public byte[] Padding { get { return _padding; } }
            public Uf2 M_Root { get { return m_root; } }
            public Uf2.BlockData M_Parent { get { return m_parent; } }
        }

        /// <remarks>
        /// Reference: <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/uf2.h#L43-L47">Source</a>
        /// </remarks>
        /// <remarks>
        /// Reference: <a href="https://github.com/raspberrypi/pico-sdk/blob/98a542c1a62fb549ffb5d66a3e5892b06276b670/src/common/boot_uf2_headers/include/boot/uf2.h#L23-L27">Git tag "2.3.0"</a>
        /// </remarks>
        public partial class Flags : KaitaiStruct
        {
            public static Flags FromFile(string fileName)
            {
                return new Flags(new KaitaiStream(fileName));
            }

            public Flags(KaitaiStream p__io, Uf2.Block p__parent = null, Uf2 p__root = null) : base(p__io)
            {
                m_parent = p__parent;
                m_root = p__root;
                f_hasExtensionTags = false;
                f_hasFamilyId = false;
                f_hasMd5Checksum = false;
                f_isFileContainer = false;
                f_notMainFlash = false;
                _read();
            }
            private void _read()
            {
                _value = m_io.ReadU4le();
                {
                    uint M_ = _value;
                    if (!( (((M_ & ~61441) == 0) && (!( ((IsFileContainer) && (HasExtensionTags)) ))) ))
                    {
                        throw new ValidationExprError(_value, m_io, "/types/flags/seq/0");
                    }
                }
            }
            private bool f_hasExtensionTags;
            private bool _hasExtensionTags;

            /// <summary>
            /// Indicates whether extension tags are present after the payload.
            /// </summary>
            public bool HasExtensionTags
            {
                get
                {
                    if (f_hasExtensionTags)
                        return _hasExtensionTags;
                    f_hasExtensionTags = true;
                    _hasExtensionTags = (bool) ((Value & 32768) != 0);
                    return _hasExtensionTags;
                }
            }
            private bool f_hasFamilyId;
            private bool _hasFamilyId;

            /// <summary>
            /// The field at offset 28 in the block is `family_id` instead of
            /// `file_size`.
            /// </summary>
            public bool HasFamilyId
            {
                get
                {
                    if (f_hasFamilyId)
                        return _hasFamilyId;
                    f_hasFamilyId = true;
                    _hasFamilyId = (bool) ((Value & 8192) != 0);
                    return _hasFamilyId;
                }
            }
            private bool f_hasMd5Checksum;
            private bool _hasMd5Checksum;

            /// <summary>
            /// Indicates whether `md5_checksum` is present at the end of `data`.
            /// </summary>
            public bool HasMd5Checksum
            {
                get
                {
                    if (f_hasMd5Checksum)
                        return _hasMd5Checksum;
                    f_hasMd5Checksum = true;
                    _hasMd5Checksum = (bool) ((Value & 16384) != 0);
                    return _hasMd5Checksum;
                }
            }
            private bool f_isFileContainer;
            private bool _isFileContainer;

            /// <summary>
            /// When set, the UF2 format is used as a container for regular files
            /// (akin to a TAR file, or ZIP archive without compression).
            /// 
            /// `target_address` is the offset in the file where the payload is to be
            /// written, and `file_size` is the size of that file. The name of the
            /// destination file is stored in `data.file_name`.
            /// </summary>
            public bool IsFileContainer
            {
                get
                {
                    if (f_isFileContainer)
                        return _isFileContainer;
                    f_isFileContainer = true;
                    _isFileContainer = (bool) ((Value & 4096) != 0);
                    return _isFileContainer;
                }
            }
            private bool f_notMainFlash;
            private bool _notMainFlash;

            /// <summary>
            /// Indicates that this block should be skipped when writing the device
            /// flash. It can be used to store data that does not fit on the device,
            /// typically embedded source code or debug info.
            /// </summary>
            public bool NotMainFlash
            {
                get
                {
                    if (f_notMainFlash)
                        return _notMainFlash;
                    f_notMainFlash = true;
                    _notMainFlash = (bool) ((Value & 1) != 0);
                    return _notMainFlash;
                }
            }
            private uint _value;
            private Uf2 m_root;
            private Uf2.Block m_parent;

            /// <summary>
            /// Only the five bits that we cover in value instances below are defined,
            /// and no other bit may be set. The [official
            /// spec](https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#flags)
            /// says &quot;Currently, there are five flags defined&quot;. If any other flags are
            /// added in the future, this .ksy spec will need to be updated.
            /// 
            /// The `is_file_container` and `has_extension_tags` flags disagree about
            /// what follows the payload in `data` (a file name or a list of extension
            /// tags), so this Kaitai Struct implementation treats them as mutually
            /// exclusive and will reject a block that sets both. The official spec
            /// does not specify how this should be handled, but logically there's a
            /// conflict, so we've decided to strictly treat it as an error.
            /// </summary>
            public uint Value { get { return _value; } }
            public Uf2 M_Root { get { return m_root; } }
            public Uf2.Block M_Parent { get { return m_parent; } }
        }

        /// <remarks>
        /// Reference: <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#md5-checksum">Source</a>
        /// </remarks>
        public partial class Md5Checksum : KaitaiStruct
        {
            public static Md5Checksum FromFile(string fileName)
            {
                return new Md5Checksum(new KaitaiStream(fileName));
            }

            public Md5Checksum(KaitaiStream p__io, Uf2.BlockData p__parent = null, Uf2 p__root = null) : base(p__io)
            {
                m_parent = p__parent;
                m_root = p__root;
                _read();
            }
            private void _read()
            {
                _startAddress = m_io.ReadU4le();
                _lenRegion = m_io.ReadU4le();
                _md5 = m_io.ReadBytes(16);
            }
            private uint _startAddress;
            private uint _lenRegion;
            private byte[] _md5;
            private Uf2 m_root;
            private Uf2.BlockData m_parent;
            public uint StartAddress { get { return _startAddress; } }
            public uint LenRegion { get { return _lenRegion; } }
            public byte[] Md5 { get { return _md5; } }
            public Uf2 M_Root { get { return m_root; } }
            public Uf2.BlockData M_Parent { get { return m_parent; } }
        }
        private Block _firstBlock;
        private List<Block> _blocks;
        private Uf2 m_root;
        private KaitaiStruct m_parent;
        public Block FirstBlock { get { return _firstBlock; } }
        public List<Block> Blocks { get { return _blocks; } }
        public Uf2 M_Root { get { return m_root; } }
        public KaitaiStruct M_Parent { get { return m_parent; } }
    }
}