UF2 (USB Flashing Format): Go 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.

Go source code to parse UF2 (USB Flashing Format)

uf2.go

// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.

import (
	"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
	"bytes"
	"io"
)


/**
 * 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:
 * 
 * ```bash
 * $ 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:
 * 
 * * <https://micropython.org/download/RPI_PICO/> - a typical case: all blocks
 *   have the same family ID and `num_blocks`.
 * * <https://micropython.org/download/RPI_PICO2/> - 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).
 * * <https://circuitpython.org/downloads> - 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`.
 * * <https://github.com/raspberrypi/pico-sdk-prebuilts/releases> -
 *   [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
 *   <https://github.com/raspberrypi/pico-examples/blob/c81c855ffdedc825975a40ba357723a71358ddf0/universal/CMakeLists.txt#L159-L169>).
 * * <https://github.com/microsoft/pxt-microsoft-boot-sequence/releases> - some
 *   files contain blocks with the `flags.is_file_container` flag set (for
 *   example
 *   <https://github.com/microsoft/pxt-microsoft-boot-sequence/releases/download/v0.0.4/arcade-p0.uf2>
 *   is a pure file container for
 *   `file_name == "Projects/microsoft-boot-sequence.elf"`). Generated by
 *   MakeCode, the only known producer of file container UF2s.
 * * <https://github.com/umi-eng/uftwo/tree/35bccf75b4f81c43f088696a8c4a9912f1f4104e/uftwo/tests> -
 *   synthetic test files for features that real firmware doesn't seem to use
 *   (e.g. MD5 checksums).
 * @see <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md">Source</a>
 */

type Uf2_ExtensionTagType int
const (
	Uf2_ExtensionTagType__End Uf2_ExtensionTagType = 0
	Uf2_ExtensionTagType__PageSize Uf2_ExtensionTagType = 780791
	Uf2_ExtensionTagType__Description Uf2_ExtensionTagType = 6622621
	Uf2_ExtensionTagType__Rp2IgnoreBlock Uf2_ExtensionTagType = 10049507
	Uf2_ExtensionTagType__Version Uf2_ExtensionTagType = 10471356
	Uf2_ExtensionTagType__Sha2Checksum Uf2_ExtensionTagType = 11824560
	Uf2_ExtensionTagType__DeviceTypeId Uf2_ExtensionTagType = 13149993
)
var values_Uf2_ExtensionTagType = map[Uf2_ExtensionTagType]struct{}{0: {}, 780791: {}, 6622621: {}, 10049507: {}, 10471356: {}, 11824560: {}, 13149993: {}}
func (v Uf2_ExtensionTagType) isDefined() bool {
	_, ok := values_Uf2_ExtensionTagType[v]
	return ok
}

type Uf2_FamilyId int
const (
	Uf2_FamilyId__Stm32l4 Uf2_FamilyId = 16738585
	Uf2_FamilyId__Stm32l5 Uf2_FamilyId = 69471199
	Uf2_FamilyId__Stm32f411xc Uf2_FamilyId = 114362747
	Uf2_FamilyId__M0sense Uf2_FamilyId = 299792458
	Uf2_FamilyId__Atmega32 Uf2_FamilyId = 374814231
	Uf2_FamilyId__Saml21 Uf2_FamilyId = 407992330
	Uf2_FamilyId__Nrf52 Uf2_FamilyId = 458716255
	Uf2_FamilyId__Esp32 Uf2_FamilyId = 475996592
	Uf2_FamilyId__Stm32l1 Uf2_FamilyId = 505365293
	Uf2_FamilyId__Stm32l0 Uf2_FamilyId = 539900561
	Uf2_FamilyId__Stm32wl Uf2_FamilyId = 558239728
	Uf2_FamilyId__Rtl8710b Uf2_FamilyId = 585160444
	Uf2_FamilyId__Lpc55 Uf2_FamilyId = 716994540
	Uf2_FamilyId__Esp32c2 Uf2_FamilyId = 730387100
	Uf2_FamilyId__Stm32f411xe Uf2_FamilyId = 767756741
	Uf2_FamilyId__Stm32g0 Uf2_FamilyId = 806311475
	Uf2_FamilyId__Esp32s31 Uf2_FamilyId = 822212545
	Uf2_FamilyId__Gd32f350 Uf2_FamilyId = 835856582
	Uf2_FamilyId__Esp32h2 Uf2_FamilyId = 858203894
	Uf2_FamilyId__Rtl8720d Uf2_FamilyId = 863621090
	Uf2_FamilyId__Esp32p4 Uf2_FamilyId = 1026592404
	Uf2_FamilyId__MaixplayU4 Uf2_FamilyId = 1265126769
	Uf2_FamilyId__Stm32g4 Uf2_FamilyId = 1282483210
	Uf2_FamilyId__Stm32h5 Uf2_FamilyId = 1318001757
	Uf2_FamilyId__Csk4 Uf2_FamilyId = 1332399698
	Uf2_FamilyId__Mimxrt10xx Uf2_FamilyId = 1337120189
	Uf2_FamilyId__Xr809 Uf2_FamilyId = 1374225320
	Uf2_FamilyId__Stm32f7 Uf2_FamilyId = 1404571392
	Uf2_FamilyId__Esp32c6 Uf2_FamilyId = 1410195298
	Uf2_FamilyId__Samd51 Uf2_FamilyId = 1427194976
	Uf2_FamilyId__Stm32f4 Uf2_FamilyId = 1467308631
	Uf2_FamilyId__Fx2 Uf2_FamilyId = 1511523995
	Uf2_FamilyId__Stm32f2 Uf2_FamilyId = 1561987630
	Uf2_FamilyId__Stm32f1 Uf2_FamilyId = 1591873650
	Uf2_FamilyId__Nrf52833 Uf2_FamilyId = 1646171002
	Uf2_FamilyId__Stm32f0 Uf2_FamilyId = 1685595318
	Uf2_FamilyId__Bk7231u Uf2_FamilyId = 1733968048
	Uf2_FamilyId__Samd21 Uf2_FamilyId = 1760373640
	Uf2_FamilyId__Ch32v Uf2_FamilyId = 1771791084
	Uf2_FamilyId__Bk7251 Uf2_FamilyId = 1786956866
	Uf2_FamilyId__Stm32f3 Uf2_FamilyId = 1803837832
	Uf2_FamilyId__Stm32f407 Uf2_FamilyId = 1829315322
	Uf2_FamilyId__Stm32h7 Uf2_FamilyId = 1840668802
	Uf2_FamilyId__Csk6 Uf2_FamilyId = 1853049000
	Uf2_FamilyId__Nrf52832xxab Uf2_FamilyId = 1869948536
	Uf2_FamilyId__Stm32wb Uf2_FamilyId = 1892771411
	Uf2_FamilyId__Nrf52832xxaa Uf2_FamilyId = 1920081230
	Uf2_FamilyId__Max32690 Uf2_FamilyId = 1947226634
	Uf2_FamilyId__Esp32c61 Uf2_FamilyId = 2010665156
	Uf2_FamilyId__Bk7231n Uf2_FamilyId = 2067722800
	Uf2_FamilyId__Ra4m1 Uf2_FamilyId = 2078840685
	Uf2_FamilyId__Py32f071Uvk5V3 Uf2_FamilyId = 2105173743
	Uf2_FamilyId__Esp8266 Uf2_FamilyId = 2125160941
	Uf2_FamilyId__Kl32l2 Uf2_FamilyId = 2139350931
	Uf2_FamilyId__Nrf52820 Uf2_FamilyId = 2181929567
	Uf2_FamilyId__Stm32f407vg Uf2_FamilyId = 2410701054
	Uf2_FamilyId__Max78002 Uf2_FamilyId = 2446589208
	Uf2_FamilyId__Rza1lu Uf2_FamilyId = 2501329455
	Uf2_FamilyId__Gd32vf103 Uf2_FamilyId = 2599435827
	Uf2_FamilyId__Esp32h4 Uf2_FamilyId = 2651564682
	Uf2_FamilyId__Rtl8710a Uf2_FamilyId = 2684343619
	Uf2_FamilyId__At32f415 Uf2_FamilyId = 2697558926
	Uf2_FamilyId__Nrf52840 Uf2_FamilyId = 2913282112
	Uf2_FamilyId__Esp32h21 Uf2_FamilyId = 3067936943
	Uf2_FamilyId__Esp32s2 Uf2_FamilyId = 3218951918
	Uf2_FamilyId__Esp32s3 Uf2_FamilyId = 3296614247
	Uf2_FamilyId__Esp32c3 Uf2_FamilyId = 3559628908
	Uf2_FamilyId__Max32650 Uf2_FamilyId = 3594487346
	Uf2_FamilyId__Bl602 Uf2_FamilyId = 3725750455
	Uf2_FamilyId__Rtl8720c Uf2_FamilyId = 3767498084
	Uf2_FamilyId__Rp2040 Uf2_FamilyId = 3834380118
	Uf2_FamilyId__Rp2xxxAbsolute Uf2_FamilyId = 3834380119
	Uf2_FamilyId__Rp2xxxData Uf2_FamilyId = 3834380120
	Uf2_FamilyId__Rp2350ArmS Uf2_FamilyId = 3834380121
	Uf2_FamilyId__Rp2350Riscv Uf2_FamilyId = 3834380122
	Uf2_FamilyId__Rp2350ArmNs Uf2_FamilyId = 3834380123
	Uf2_FamilyId__Max32666 Uf2_FamilyId = 4039314801
	Uf2_FamilyId__Esp32c5 Uf2_FamilyId = 4145808195
)
var values_Uf2_FamilyId = map[Uf2_FamilyId]struct{}{16738585: {}, 69471199: {}, 114362747: {}, 299792458: {}, 374814231: {}, 407992330: {}, 458716255: {}, 475996592: {}, 505365293: {}, 539900561: {}, 558239728: {}, 585160444: {}, 716994540: {}, 730387100: {}, 767756741: {}, 806311475: {}, 822212545: {}, 835856582: {}, 858203894: {}, 863621090: {}, 1026592404: {}, 1265126769: {}, 1282483210: {}, 1318001757: {}, 1332399698: {}, 1337120189: {}, 1374225320: {}, 1404571392: {}, 1410195298: {}, 1427194976: {}, 1467308631: {}, 1511523995: {}, 1561987630: {}, 1591873650: {}, 1646171002: {}, 1685595318: {}, 1733968048: {}, 1760373640: {}, 1771791084: {}, 1786956866: {}, 1803837832: {}, 1829315322: {}, 1840668802: {}, 1853049000: {}, 1869948536: {}, 1892771411: {}, 1920081230: {}, 1947226634: {}, 2010665156: {}, 2067722800: {}, 2078840685: {}, 2105173743: {}, 2125160941: {}, 2139350931: {}, 2181929567: {}, 2410701054: {}, 2446589208: {}, 2501329455: {}, 2599435827: {}, 2651564682: {}, 2684343619: {}, 2697558926: {}, 2913282112: {}, 3067936943: {}, 3218951918: {}, 3296614247: {}, 3559628908: {}, 3594487346: {}, 3725750455: {}, 3767498084: {}, 3834380118: {}, 3834380119: {}, 3834380120: {}, 3834380121: {}, 3834380122: {}, 3834380123: {}, 4039314801: {}, 4145808195: {}}
func (v Uf2_FamilyId) isDefined() bool {
	_, ok := values_Uf2_FamilyId[v]
	return ok
}
type Uf2 struct {
	FirstBlock *Uf2_Block
	Blocks []*Uf2_Block
	_io *kaitai.Stream
	_root *Uf2
	_parent kaitai.Struct
}
func NewUf2() *Uf2 {
	return &Uf2{
	}
}

func (this Uf2) IO_() *kaitai.Stream {
	return this._io
}

func (this *Uf2) Read(io *kaitai.Stream, parent kaitai.Struct, root *Uf2) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp1 := NewUf2_Block()
	err = tmp1.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.FirstBlock = tmp1
	tmp2, err := this.FirstBlock.NumBlocks()
	if err != nil {
		return err
	}
	for i := 0; i < int(tmp2 - 1); i++ {
		_ = i
		tmp3 := NewUf2_Block()
		err = tmp3.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Blocks = append(this.Blocks, tmp3)
	}
	return err
}
type Uf2_Block struct {
	Magic []byte
	SecondMagic []byte
	Flags *Uf2_Flags
	TargetAddress uint32
	LenPayload uint32
	BlockNumber uint32
	NumBlocksRaw uint32
	FileSize uint32
	FamilyId Uf2_FamilyId
	Data *Uf2_BlockData
	FinalMagic []byte
	_io *kaitai.Stream
	_root *Uf2
	_parent *Uf2
	_raw_Data []byte
	_f_isRp2350E10Block bool
	isRp2350E10Block bool
	_f_numBlocks bool
	numBlocks uint32
}
func NewUf2_Block() *Uf2_Block {
	return &Uf2_Block{
	}
}

func (this Uf2_Block) IO_() *kaitai.Stream {
	return this._io
}

func (this *Uf2_Block) Read(io *kaitai.Stream, parent *Uf2, root *Uf2) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp4, err := this._io.ReadBytes(int(4))
	if err != nil {
		return err
	}
	tmp4 = tmp4
	this.Magic = tmp4
	if !(bytes.Equal(this.Magic, []uint8{85, 70, 50, 10})) {
		return kaitai.NewValidationNotEqualError([]uint8{85, 70, 50, 10}, this.Magic, this._io, "/types/block/seq/0")
	}
	tmp5, err := this._io.ReadBytes(int(4))
	if err != nil {
		return err
	}
	tmp5 = tmp5
	this.SecondMagic = tmp5
	if !(bytes.Equal(this.SecondMagic, []uint8{87, 81, 93, 158})) {
		return kaitai.NewValidationNotEqualError([]uint8{87, 81, 93, 158}, this.SecondMagic, this._io, "/types/block/seq/1")
	}
	tmp6 := NewUf2_Flags()
	err = tmp6.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Flags = tmp6
	tmp7, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.TargetAddress = uint32(tmp7)
	{
		_it := this.TargetAddress
		tmp8 := _it % 4
		if tmp8 < 0 {
			tmp8 += 4
		}
		if !(tmp8 == 0) {
			return kaitai.NewValidationExprError(this.TargetAddress, this._io, "/types/block/seq/3")
		}
	}
	tmp9, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.LenPayload = uint32(tmp9)
	{
		_it := this.LenPayload
		tmp10 := _it % 4
		if tmp10 < 0 {
			tmp10 += 4
		}
		if !(tmp10 == 0) {
			return kaitai.NewValidationExprError(this.LenPayload, this._io, "/types/block/seq/4")
		}
	}
	tmp11, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.BlockNumber = uint32(tmp11)
	tmp12, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.NumBlocksRaw = uint32(tmp12)
	if !(this.NumBlocksRaw >= this.BlockNumber + 1) {
		return kaitai.NewValidationLessThanError(this.BlockNumber + 1, this.NumBlocksRaw, this._io, "/types/block/seq/6")
	}
	tmp13, err := this.Flags.HasFamilyId()
	if err != nil {
		return err
	}
	if (!(tmp13)) {
		tmp14, err := this._io.ReadU4le()
		if err != nil {
			return err
		}
		this.FileSize = uint32(tmp14)
	}
	tmp15, err := this.Flags.HasFamilyId()
	if err != nil {
		return err
	}
	if (tmp15) {
		tmp16, err := this._io.ReadU4le()
		if err != nil {
			return err
		}
		this.FamilyId = Uf2_FamilyId(tmp16)
	}
	tmp17, err := this._io.ReadBytes(int(476))
	if err != nil {
		return err
	}
	tmp17 = tmp17
	this._raw_Data = tmp17
	_io__raw_Data := kaitai.NewStream(bytes.NewReader(this._raw_Data))
	tmp18 := NewUf2_BlockData()
	err = tmp18.Read(_io__raw_Data, this, this._root)
	if err != nil {
		return err
	}
	this.Data = tmp18
	tmp19, err := this._io.ReadBytes(int(4))
	if err != nil {
		return err
	}
	tmp19 = tmp19
	this.FinalMagic = tmp19
	if !(bytes.Equal(this.FinalMagic, []uint8{48, 111, 177, 10})) {
		return kaitai.NewValidationNotEqualError([]uint8{48, 111, 177, 10}, this.FinalMagic, this._io, "/types/block/seq/10")
	}
	return err
}

/**
 * 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:
 *    <https://github.com/neednotapply/DC32-cfw/releases/tag/1.69.13.37>
 * 
 * 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
 * <https://github.com/raspberrypi/picotool/commit/78c9bd121b09399823b67ee7ea89003ca0d3315f>
 * 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
 * <https://micropython.org/download/RPI_PICO2/>. 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
 * <https://github.com/kaitai-io/kaitai_struct_formats/pull/542#discussion_r3906386820>).
 * v1.24.x releases predate the `extension_tag_type::rp2_ignore_block`
 * tag, while releases v1.25.0 and later include it.
 * @see <a href="https://github.com/raspberrypi/picotool/blob/6f6458d792b93685a11423b244a585eaa99eafcf/elf2uf2/elf2uf2.cpp#L147">Git tag "2.3.0"</a>
 * @see <a href="https://github.com/raspberrypi/picotool/commit/78c9bd121b09399823b67ee7ea89003ca0d3315f">Source</a>
 */
func (this *Uf2_Block) IsRp2350E10Block() (v bool, err error) {
	if (this._f_isRp2350E10Block) {
		return this.isRp2350E10Block, nil
	}
	this._f_isRp2350E10Block = true
	tmp20 := this.Data.Payload
	tmp21, err := this.Flags.HasExtensionTags()
	if err != nil {
		return false, err
	}
	this.isRp2350E10Block = bool( (( ((this.Flags.Value == 8192) || (this.Flags.Value == 40960)) ) && (this.FamilyId == Uf2_FamilyId__Rp2xxxAbsolute) && (this.NumBlocksRaw == 2) && (this.BlockNumber == 0) && (this.LenPayload == 256) && (this.Data.Payload[0] == 239) && (tmp20[len(tmp20) - 1] == 239) && ( ((!(tmp21)) || (this.Data.ExtensionTags[0].LenTag == 0) || ( ((this.Data.ExtensionTags[0].LenTag == 4) && (this.Data.ExtensionTags[0].TagType == Uf2_ExtensionTagType__Rp2IgnoreBlock)) )) )) )
	return this.isRp2350E10Block, nil
}
func (this *Uf2_Block) NumBlocks() (v uint32, err error) {
	if (this._f_numBlocks) {
		return this.numBlocks, nil
	}
	this._f_numBlocks = true
	var tmp22 int8;
	tmp23, err := this.IsRp2350E10Block()
	if err != nil {
		return 0, err
	}
	if (tmp23) {
		tmp22 = 1
	} else {
		tmp22 = this.NumBlocksRaw
	}
	this.numBlocks = uint32(tmp22)
	return this.numBlocks, nil
}

/**
 * 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:
 * 
 * > In any event, payload size and target address should always be
 * > 4-byte aligned.
 */

/**
 * The [official
 * spec](https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#payload-sizes)
 * says:
 * 
 * > In any event, payload size and target address should always be
 * > 4-byte aligned.
 */

/**
 * 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).
 */

/**
 * 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.
 */
type Uf2_BlockData struct {
	Payload []byte
	FileName string
	ExtensionTags []*Uf2_ExtensionTag
	_io *kaitai.Stream
	_root *Uf2
	_parent *Uf2_Block
	_f_md5Checksum bool
	md5Checksum *Uf2_Md5Checksum
}
func NewUf2_BlockData() *Uf2_BlockData {
	return &Uf2_BlockData{
	}
}

func (this Uf2_BlockData) IO_() *kaitai.Stream {
	return this._io
}

func (this *Uf2_BlockData) Read(io *kaitai.Stream, parent *Uf2_Block, root *Uf2) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp24, err := this._io.ReadBytes(int(this._parent.LenPayload))
	if err != nil {
		return err
	}
	tmp24 = tmp24
	this.Payload = tmp24
	tmp25, err := this._parent.Flags.IsFileContainer()
	if err != nil {
		return err
	}
	if (tmp25) {
		tmp26, err := this._io.ReadBytesTerm(0, false, true, true)
		if err != nil {
			return err
		}
		this.FileName = string(tmp26)
	}
	tmp27, err := this._parent.Flags.HasExtensionTags()
	if err != nil {
		return err
	}
	if (tmp27) {
		for i := 1;; i++ {
			tmp28 := NewUf2_ExtensionTag()
			err = tmp28.Read(this._io, this, this._root)
			if err != nil {
				return err
			}
			_it := tmp28
			this.ExtensionTags = append(this.ExtensionTags, _it)
			if _it.LenTag == 0 {
				break
			}
		}
	}
	return err
}

/**
 * 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 "This is currently only used on ESP32", but no real-world
 * firmware sample has been found (at least none of the .uf2 files from
 * <https://github.com/adafruit/tinyuf2/releases>,
 * [MicroPython](https://micropython.org/download/) or
 * [CircuitPython](https://circuitpython.org/downloads) contain it). It
 * was found only in some synthetic test files, e.g.
 * <https://github.com/umi-eng/uftwo/blob/35bccf75b4f81c43f088696a8c4a9912f1f4104e/uftwo/tests/checksum_256.uf2>.
 */
func (this *Uf2_BlockData) Md5Checksum() (v *Uf2_Md5Checksum, err error) {
	if (this._f_md5Checksum) {
		return this.md5Checksum, nil
	}
	this._f_md5Checksum = true
	tmp29, err := this._parent.Flags.HasMd5Checksum()
	if err != nil {
		return nil, err
	}
	if (tmp29) {
		_pos, err := this._io.Pos()
		if err != nil {
			return nil, err
		}
		tmp30, err := this._io.Size()
		if err != nil {
			return nil, err
		}
		_, err = this._io.Seek(int64(tmp30 - 24), io.SeekStart)
		if err != nil {
			return nil, err
		}
		tmp31 := NewUf2_Md5Checksum()
		err = tmp31.Read(this._io, this, this._root)
		if err != nil {
			return nil, err
		}
		this.md5Checksum = tmp31
		_, err = this._io.Seek(_pos, io.SeekStart)
		if err != nil {
			return nil, err
		}
	}
	return this.md5Checksum, nil
}

/**
 * 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.
 */

/**
 * @see <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#extension-tags">Source</a>
 */
type Uf2_ExtensionTag struct {
	LenTag uint8
	TagType Uf2_ExtensionTagType
	Value []byte
	Padding []byte
	_io *kaitai.Stream
	_root *Uf2
	_parent *Uf2_BlockData
	_f_lenValue bool
	lenValue int
	_f_minLenTag bool
	minLenTag int
}
func NewUf2_ExtensionTag() *Uf2_ExtensionTag {
	return &Uf2_ExtensionTag{
	}
}

func (this Uf2_ExtensionTag) IO_() *kaitai.Stream {
	return this._io
}

func (this *Uf2_ExtensionTag) Read(io *kaitai.Stream, parent *Uf2_BlockData, root *Uf2) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp32, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.LenTag = tmp32
	{
		_it := this.LenTag
		tmp33, err := this.MinLenTag()
		if err != nil {
			return err
		}
		if !( ((_it == 0) || (_it >= tmp33)) ) {
			return kaitai.NewValidationExprError(this.LenTag, this._io, "/types/extension_tag/seq/0")
		}
	}
	tmp34, err := this._io.ReadBitsIntLe(24)
	if err != nil {
		return err
	}
	this.TagType = Uf2_ExtensionTagType(tmp34)
	this._io.AlignToByte()
	if (this.LenTag != 0) {
		tmp35, err := this.LenValue()
		if err != nil {
			return err
		}
		tmp36, err := this._io.ReadBytes(int(tmp35))
		if err != nil {
			return err
		}
		tmp36 = tmp36
		this.Value = tmp36
	}
	tmp37 := -(this.LenTag) % 4
	if tmp37 < 0 {
		tmp37 += 4
	}
	tmp38, err := this._io.ReadBytes(int(tmp37))
	if err != nil {
		return err
	}
	tmp38 = tmp38
	this.Padding = tmp38
	return err
}
func (this *Uf2_ExtensionTag) LenValue() (v int, err error) {
	if (this._f_lenValue) {
		return this.lenValue, nil
	}
	this._f_lenValue = true
	var tmp39 int;
	tmp40, err := this.MinLenTag()
	if err != nil {
		return 0, err
	}
	if (this.LenTag >= tmp40) {
		tmp41, err := this.MinLenTag()
		if err != nil {
			return 0, err
		}
		tmp39 = this.LenTag - tmp41
	} else {
		tmp39 = 0
	}
	this.lenValue = int(tmp39)
	return this.lenValue, nil
}
func (this *Uf2_ExtensionTag) MinLenTag() (v int, err error) {
	if (this._f_minLenTag) {
		return this.minLenTag, nil
	}
	this._f_minLenTag = true
	this.minLenTag = int(1 + 3)
	return this.minLenTag, nil
}

/**
 * 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.
 */

/**
 * Tags are 4-byte aligned, so a tag whose size is not a multiple
 * of 4 is followed by padding.
 */

/**
 * @see <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/uf2.h#L43-L47">Source</a>
 * @see <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>
 */
type Uf2_Flags struct {
	Value uint32
	_io *kaitai.Stream
	_root *Uf2
	_parent *Uf2_Block
	_f_hasExtensionTags bool
	hasExtensionTags bool
	_f_hasFamilyId bool
	hasFamilyId bool
	_f_hasMd5Checksum bool
	hasMd5Checksum bool
	_f_isFileContainer bool
	isFileContainer bool
	_f_notMainFlash bool
	notMainFlash bool
}
func NewUf2_Flags() *Uf2_Flags {
	return &Uf2_Flags{
	}
}

func (this Uf2_Flags) IO_() *kaitai.Stream {
	return this._io
}

func (this *Uf2_Flags) Read(io *kaitai.Stream, parent *Uf2_Block, root *Uf2) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp42, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.Value = uint32(tmp42)
	{
		_it := this.Value
		tmp43, err := this.IsFileContainer()
		if err != nil {
			return err
		}
		tmp44, err := this.HasExtensionTags()
		if err != nil {
			return err
		}
		if !( ((_it & ^61441 == 0) && (!( ((tmp43) && (tmp44)) ))) ) {
			return kaitai.NewValidationExprError(this.Value, this._io, "/types/flags/seq/0")
		}
	}
	return err
}

/**
 * Indicates whether extension tags are present after the payload.
 */
func (this *Uf2_Flags) HasExtensionTags() (v bool, err error) {
	if (this._f_hasExtensionTags) {
		return this.hasExtensionTags, nil
	}
	this._f_hasExtensionTags = true
	this.hasExtensionTags = bool(this.Value & 32768 != 0)
	return this.hasExtensionTags, nil
}

/**
 * The field at offset 28 in the block is `family_id` instead of
 * `file_size`.
 */
func (this *Uf2_Flags) HasFamilyId() (v bool, err error) {
	if (this._f_hasFamilyId) {
		return this.hasFamilyId, nil
	}
	this._f_hasFamilyId = true
	this.hasFamilyId = bool(this.Value & 8192 != 0)
	return this.hasFamilyId, nil
}

/**
 * Indicates whether `md5_checksum` is present at the end of `data`.
 */
func (this *Uf2_Flags) HasMd5Checksum() (v bool, err error) {
	if (this._f_hasMd5Checksum) {
		return this.hasMd5Checksum, nil
	}
	this._f_hasMd5Checksum = true
	this.hasMd5Checksum = bool(this.Value & 16384 != 0)
	return this.hasMd5Checksum, nil
}

/**
 * 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`.
 */
func (this *Uf2_Flags) IsFileContainer() (v bool, err error) {
	if (this._f_isFileContainer) {
		return this.isFileContainer, nil
	}
	this._f_isFileContainer = true
	this.isFileContainer = bool(this.Value & 4096 != 0)
	return this.isFileContainer, nil
}

/**
 * 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.
 */
func (this *Uf2_Flags) NotMainFlash() (v bool, err error) {
	if (this._f_notMainFlash) {
		return this.notMainFlash, nil
	}
	this._f_notMainFlash = true
	this.notMainFlash = bool(this.Value & 1 != 0)
	return this.notMainFlash, nil
}

/**
 * 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 "Currently, there are five flags defined". 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.
 */

/**
 * @see <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#md5-checksum">Source</a>
 */
type Uf2_Md5Checksum struct {
	StartAddress uint32
	LenRegion uint32
	Md5 []byte
	_io *kaitai.Stream
	_root *Uf2
	_parent *Uf2_BlockData
}
func NewUf2_Md5Checksum() *Uf2_Md5Checksum {
	return &Uf2_Md5Checksum{
	}
}

func (this Uf2_Md5Checksum) IO_() *kaitai.Stream {
	return this._io
}

func (this *Uf2_Md5Checksum) Read(io *kaitai.Stream, parent *Uf2_BlockData, root *Uf2) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp45, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.StartAddress = uint32(tmp45)
	tmp46, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.LenRegion = uint32(tmp46)
	tmp47, err := this._io.ReadBytes(int(16))
	if err != nil {
		return err
	}
	tmp47 = tmp47
	this.Md5 = tmp47
	return err
}