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:
num_blocks.family_id::rp2xxx_absolute block that picotool prepends (see the
is_rp2350_e10_block value instance in the block type).file_size rather
than 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).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.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.
// 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.ArrayList;
import java.util.Objects;
import io.kaitai.struct.ConsistencyError;
import java.util.Arrays;
import java.nio.charset.StandardCharsets;
import java.nio.charset.Charset;
import java.util.List;
/**
* 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>
*/
public class Uf2 extends KaitaiStruct.ReadWrite {
public static Uf2 fromFile(String fileName) throws IOException {
return new Uf2(new ByteBufferKaitaiStream(fileName));
}
public enum ExtensionTagType {
END(0),
PAGE_SIZE(780791),
DESCRIPTION(6622621),
RP2_IGNORE_BLOCK(10049507),
VERSION(10471356),
SHA2_CHECKSUM(11824560),
DEVICE_TYPE_ID(13149993);
private final long id;
ExtensionTagType(long id) { this.id = id; }
public long id() { return id; }
private static final Map<Long, ExtensionTagType> byId = new HashMap<Long, ExtensionTagType>(7);
static {
for (ExtensionTagType e : ExtensionTagType.values())
byId.put(e.id(), e);
}
public static ExtensionTagType byId(long id) { return byId.get(id); }
}
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),
MAIXPLAY_U4(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),
PY32F071_UVK5_V3(2105173743),
ESP8266(2125160941),
KL32L2(2139350931),
NRF52820(2181929567L),
STM32F407VG(2410701054L),
MAX78002(2446589208L),
RZA1LU(2501329455L),
GD32VF103(2599435827L),
ESP32H4(2651564682L),
RTL8710A(2684343619L),
AT32F415(2697558926L),
NRF52840(2913282112L),
ESP32H21(3067936943L),
ESP32S2(3218951918L),
ESP32S3(3296614247L),
ESP32C3(3559628908L),
MAX32650(3594487346L),
BL602(3725750455L),
RTL8720C(3767498084L),
RP2040(3834380118L),
RP2XXX_ABSOLUTE(3834380119L),
RP2XXX_DATA(3834380120L),
RP2350_ARM_S(3834380121L),
RP2350_RISCV(3834380122L),
RP2350_ARM_NS(3834380123L),
MAX32666(4039314801L),
ESP32C5(4145808195L);
private final long id;
FamilyId(long id) { this.id = id; }
public long id() { return id; }
private static final Map<Long, FamilyId> byId = new HashMap<Long, FamilyId>(78);
static {
for (FamilyId e : FamilyId.values())
byId.put(e.id(), e);
}
public static FamilyId byId(long id) { return byId.get(id); }
}
public Uf2() {
this(null, null, null);
}
public Uf2(KaitaiStream _io) {
this(_io, null, null);
}
public Uf2(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
this(_io, _parent, null);
}
public Uf2(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, Uf2 _root) {
super(_io);
this._parent = _parent;
this._root = _root == null ? this : _root;
}
public void _read() {
this.firstBlock = new Block(this._io, this, _root);
this.firstBlock._read();
this.blocks = new ArrayList<Block>();
for (int i = 0; i < firstBlock().numBlocks() - 1; i++) {
Block _t_blocks = new Block(this._io, this, _root);
try {
_t_blocks._read();
} finally {
this.blocks.add(_t_blocks);
}
}
_dirty = false;
}
public void _fetchInstances() {
this.firstBlock._fetchInstances();
for (int i = 0; i < this.blocks.size(); i++) {
this.blocks.get(((Number) (i)).intValue())._fetchInstances();
}
}
public void _write_Seq() {
_assertNotDirty();
this.firstBlock._write_Seq(this._io);
for (int i = 0; i < this.blocks.size(); i++) {
this.blocks.get(((Number) (i)).intValue())._write_Seq(this._io);
}
}
public void _check() {
if (!Objects.equals(this.firstBlock._root(), _root()))
throw new ConsistencyError("first_block", _root(), this.firstBlock._root());
if (!Objects.equals(this.firstBlock._parent(), this))
throw new ConsistencyError("first_block", this, this.firstBlock._parent());
if (this.blocks.size() != firstBlock().numBlocks() - 1)
throw new ConsistencyError("blocks", firstBlock().numBlocks() - 1, this.blocks.size());
for (int i = 0; i < this.blocks.size(); i++) {
if (!Objects.equals(this.blocks.get(((Number) (i)).intValue())._root(), _root()))
throw new ConsistencyError("blocks", _root(), this.blocks.get(((Number) (i)).intValue())._root());
if (!Objects.equals(this.blocks.get(((Number) (i)).intValue())._parent(), this))
throw new ConsistencyError("blocks", this, this.blocks.get(((Number) (i)).intValue())._parent());
}
_dirty = false;
}
public static class Block extends KaitaiStruct.ReadWrite {
public static Block fromFile(String fileName) throws IOException {
return new Block(new ByteBufferKaitaiStream(fileName));
}
public Block() {
this(null, null, null);
}
public Block(KaitaiStream _io) {
this(_io, null, null);
}
public Block(KaitaiStream _io, Uf2 _parent) {
this(_io, _parent, null);
}
public Block(KaitaiStream _io, Uf2 _parent, Uf2 _root) {
super(_io);
this._parent = _parent;
this._root = _root;
}
public void _read() {
this.magic = this._io.readBytes(4);
if (!(Arrays.equals(this.magic, new byte[] { 85, 70, 50, 10 }))) {
throw new KaitaiStream.ValidationNotEqualError(new byte[] { 85, 70, 50, 10 }, this.magic, this._io, "/types/block/seq/0");
}
this.secondMagic = this._io.readBytes(4);
if (!(Arrays.equals(this.secondMagic, new byte[] { 87, 81, 93, -98 }))) {
throw new KaitaiStream.ValidationNotEqualError(new byte[] { 87, 81, 93, -98 }, this.secondMagic, this._io, "/types/block/seq/1");
}
this.flags = new Flags(this._io, this, _root);
this.flags._read();
this.targetAddress = this._io.readU4le();
{
long _it = this.targetAddress;
if (!(KaitaiStream.mod(_it, 4) == 0)) {
throw new KaitaiStream.ValidationExprError(this.targetAddress, this._io, "/types/block/seq/3");
}
}
this.lenPayload = this._io.readU4le();
{
long _it = this.lenPayload;
if (!(KaitaiStream.mod(_it, 4) == 0)) {
throw new KaitaiStream.ValidationExprError(this.lenPayload, this._io, "/types/block/seq/4");
}
}
this.blockNumber = this._io.readU4le();
this.numBlocksRaw = this._io.readU4le();
if (!(this.numBlocksRaw >= blockNumber() + 1)) {
throw new KaitaiStream.ValidationLessThanError(blockNumber() + 1, this.numBlocksRaw, this._io, "/types/block/seq/6");
}
if (!(flags().hasFamilyId())) {
this.fileSize = this._io.readU4le();
}
if (flags().hasFamilyId()) {
this.familyId = Uf2.FamilyId.byId(this._io.readU4le());
}
this._raw_data = this._io.readBytes(476);
KaitaiStream _io__raw_data = new ByteBufferKaitaiStream(this._raw_data);
this.data = new BlockData(_io__raw_data, this, _root);
this.data._read();
this.finalMagic = this._io.readBytes(4);
if (!(Arrays.equals(this.finalMagic, new byte[] { 48, 111, -79, 10 }))) {
throw new KaitaiStream.ValidationNotEqualError(new byte[] { 48, 111, -79, 10 }, this.finalMagic, this._io, "/types/block/seq/10");
}
_dirty = false;
}
public void _fetchInstances() {
this.flags._fetchInstances();
if (!(flags().hasFamilyId())) {
}
if (flags().hasFamilyId()) {
}
this.data._fetchInstances();
}
public void _write_Seq() {
_assertNotDirty();
this._io.writeBytes(this.magic);
this._io.writeBytes(this.secondMagic);
this.flags._write_Seq(this._io);
this._io.writeU4le(this.targetAddress);
this._io.writeU4le(this.lenPayload);
this._io.writeU4le(this.blockNumber);
this._io.writeU4le(this.numBlocksRaw);
if (!(flags().hasFamilyId())) {
this._io.writeU4le(this.fileSize);
}
if (flags().hasFamilyId()) {
this._io.writeU4le(((Number) (this.familyId.id())).longValue());
}
final KaitaiStream _io__raw_data = new ByteBufferKaitaiStream(476);
this._io.addChildStream(_io__raw_data);
{
long _pos2 = this._io.pos();
this._io.seek(this._io.pos() + (476));
final Block _this = this;
_io__raw_data.setWriteBackHandler(new KaitaiStream.WriteBackHandler(_pos2) {
@Override
protected void write(KaitaiStream parent) {
_this._raw_data = _io__raw_data.toByteArray();
if (_this._raw_data.length != 476)
throw new ConsistencyError("raw(data)", 476, _this._raw_data.length);
parent.writeBytes(_this._raw_data);
}
});
}
this.data._write_Seq(_io__raw_data);
this._io.writeBytes(this.finalMagic);
}
public void _check() {
if (this.magic.length != 4)
throw new ConsistencyError("magic", 4, this.magic.length);
if (!(Arrays.equals(this.magic, new byte[] { 85, 70, 50, 10 }))) {
throw new KaitaiStream.ValidationNotEqualError(new byte[] { 85, 70, 50, 10 }, this.magic, null, "/types/block/seq/0");
}
if (this.secondMagic.length != 4)
throw new ConsistencyError("second_magic", 4, this.secondMagic.length);
if (!(Arrays.equals(this.secondMagic, new byte[] { 87, 81, 93, -98 }))) {
throw new KaitaiStream.ValidationNotEqualError(new byte[] { 87, 81, 93, -98 }, this.secondMagic, null, "/types/block/seq/1");
}
if (!Objects.equals(this.flags._root(), _root()))
throw new ConsistencyError("flags", _root(), this.flags._root());
if (!Objects.equals(this.flags._parent(), this))
throw new ConsistencyError("flags", this, this.flags._parent());
{
long _it = this.targetAddress;
if (!(KaitaiStream.mod(_it, 4) == 0)) {
throw new KaitaiStream.ValidationExprError(this.targetAddress, null, "/types/block/seq/3");
}
}
{
long _it = this.lenPayload;
if (!(KaitaiStream.mod(_it, 4) == 0)) {
throw new KaitaiStream.ValidationExprError(this.lenPayload, null, "/types/block/seq/4");
}
}
if (!(this.numBlocksRaw >= blockNumber() + 1)) {
throw new KaitaiStream.ValidationLessThanError(blockNumber() + 1, this.numBlocksRaw, null, "/types/block/seq/6");
}
if (!(flags().hasFamilyId())) {
}
if (flags().hasFamilyId()) {
}
if (!Objects.equals(this.data._root(), _root()))
throw new ConsistencyError("data", _root(), this.data._root());
if (!Objects.equals(this.data._parent(), this))
throw new ConsistencyError("data", this, this.data._parent());
if (this.finalMagic.length != 4)
throw new ConsistencyError("final_magic", 4, this.finalMagic.length);
if (!(Arrays.equals(this.finalMagic, new byte[] { 48, 111, -79, 10 }))) {
throw new KaitaiStream.ValidationNotEqualError(new byte[] { 48, 111, -79, 10 }, this.finalMagic, null, "/types/block/seq/10");
}
_dirty = false;
}
private Boolean isRp2350E10Block;
/**
* 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>
*/
public Boolean isRp2350E10Block() {
if (this.isRp2350E10Block != null)
return this.isRp2350E10Block;
this.isRp2350E10Block = (( ((flags().value() == 8192) || (flags().value() == 40960)) ) && (familyId() == Uf2.FamilyId.RP2XXX_ABSOLUTE) && (numBlocksRaw() == 2) && (blockNumber() == 0) && (lenPayload() == 256) && ((data().payload()[((int) 0)] & 0xff) == 239) && ((data().payload()[((Number) (data().payload().length - 1)).intValue()] & 0xff) == 239) && ( ((!(flags().hasExtensionTags())) || (data().extensionTags().get(((int) 0)).lenTag() == 0) || ( ((data().extensionTags().get(((int) 0)).lenTag() == 4) && (data().extensionTags().get(((int) 0)).tagType() == Uf2.ExtensionTagType.RP2_IGNORE_BLOCK)) )) )) ;
return this.isRp2350E10Block;
}
public void _invalidateIsRp2350E10Block() { this.isRp2350E10Block = null; }
private Long numBlocks;
public Long numBlocks() {
if (this.numBlocks != null)
return this.numBlocks;
this.numBlocks = ((Number) ((isRp2350E10Block() ? 1 : numBlocksRaw()))).longValue();
return this.numBlocks;
}
public void _invalidateNumBlocks() { this.numBlocks = null; }
private byte[] magic;
private byte[] secondMagic;
private Flags flags;
private long targetAddress;
private long lenPayload;
private long blockNumber;
private long numBlocksRaw;
private Long fileSize;
private FamilyId familyId;
private BlockData data;
private byte[] finalMagic;
private Uf2 _root;
private Uf2 _parent;
private byte[] _raw_data;
public byte[] magic() { return magic; }
public void setMagic(byte[] _v) { _dirty = true; magic = _v; }
public byte[] secondMagic() { return secondMagic; }
public void setSecondMagic(byte[] _v) { _dirty = true; secondMagic = _v; }
public Flags flags() { return flags; }
public void setFlags(Flags _v) { _dirty = true; flags = _v; }
/**
* 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.
*/
public long targetAddress() { return targetAddress; }
public void setTargetAddress(long _v) { _dirty = true; targetAddress = _v; }
/**
* 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.
*/
public long lenPayload() { return lenPayload; }
public void setLenPayload(long _v) { _dirty = true; lenPayload = _v; }
public long blockNumber() { return blockNumber; }
public void setBlockNumber(long _v) { _dirty = true; blockNumber = _v; }
/**
* 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).
*/
public long numBlocksRaw() { return numBlocksRaw; }
public void setNumBlocksRaw(long _v) { _dirty = true; numBlocksRaw = _v; }
/**
* 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.
*/
public Long fileSize() { return fileSize; }
public void setFileSize(Long _v) { _dirty = true; fileSize = _v; }
public FamilyId familyId() { return familyId; }
public void setFamilyId(FamilyId _v) { _dirty = true; familyId = _v; }
public BlockData data() { return data; }
public void setData(BlockData _v) { _dirty = true; data = _v; }
public byte[] finalMagic() { return finalMagic; }
public void setFinalMagic(byte[] _v) { _dirty = true; finalMagic = _v; }
public Uf2 _root() { return _root; }
public void set_root(Uf2 _v) { _dirty = true; _root = _v; }
public Uf2 _parent() { return _parent; }
public void set_parent(Uf2 _v) { _dirty = true; _parent = _v; }
public byte[] _raw_data() { return _raw_data; }
public void set_raw_Data(byte[] _v) { _dirty = true; _raw_data = _v; }
}
public static class BlockData extends KaitaiStruct.ReadWrite {
public static BlockData fromFile(String fileName) throws IOException {
return new BlockData(new ByteBufferKaitaiStream(fileName));
}
public BlockData() {
this(null, null, null);
}
public BlockData(KaitaiStream _io) {
this(_io, null, null);
}
public BlockData(KaitaiStream _io, Uf2.Block _parent) {
this(_io, _parent, null);
}
public BlockData(KaitaiStream _io, Uf2.Block _parent, Uf2 _root) {
super(_io);
this._parent = _parent;
this._root = _root;
}
public void _read() {
this.payload = this._io.readBytes(_parent().lenPayload());
if (_parent().flags().isFileContainer()) {
this.fileName = new String(this._io.readBytesTerm((byte) 0, false, true, true), StandardCharsets.UTF_8);
}
if (_parent().flags().hasExtensionTags()) {
this.extensionTags = new ArrayList<ExtensionTag>();
{
ExtensionTag _it;
int i = 0;
do {
ExtensionTag _t_extensionTags = new ExtensionTag(this._io, this, _root);
try {
_t_extensionTags._read();
} finally {
_it = _t_extensionTags;
this.extensionTags.add(_it);
}
i++;
} while (!(_it.lenTag() == 0));
}
}
_dirty = false;
}
public void _fetchInstances() {
if (_parent().flags().isFileContainer()) {
}
if (_parent().flags().hasExtensionTags()) {
for (int i = 0; i < this.extensionTags.size(); i++) {
this.extensionTags.get(((Number) (i)).intValue())._fetchInstances();
}
}
md5Checksum();
if (this.md5Checksum != null) {
this.md5Checksum._fetchInstances();
}
}
public void _write_Seq() {
_assertNotDirty();
_shouldWriteMd5Checksum = _enabledMd5Checksum;
this._io.writeBytes(this.payload);
if (_parent().flags().isFileContainer()) {
this._io.writeBytes((this.fileName).getBytes(Charset.forName("UTF-8")));
this._io.writeU1(0);
}
if (_parent().flags().hasExtensionTags()) {
for (int i = 0; i < this.extensionTags.size(); i++) {
this.extensionTags.get(((Number) (i)).intValue())._write_Seq(this._io);
}
}
}
public void _check() {
if (this.payload.length != _parent().lenPayload())
throw new ConsistencyError("payload", _parent().lenPayload(), this.payload.length);
if (_parent().flags().isFileContainer()) {
if (KaitaiStream.byteArrayIndexOf((this.fileName).getBytes(Charset.forName("UTF-8")), ((byte) 0)) != -1)
throw new ConsistencyError("file_name", -1, KaitaiStream.byteArrayIndexOf((this.fileName).getBytes(Charset.forName("UTF-8")), ((byte) 0)));
}
if (_parent().flags().hasExtensionTags()) {
if (this.extensionTags.size() == 0)
throw new ConsistencyError("extension_tags", 0, this.extensionTags.size());
for (int i = 0; i < this.extensionTags.size(); i++) {
if (!Objects.equals(this.extensionTags.get(((Number) (i)).intValue())._root(), _root()))
throw new ConsistencyError("extension_tags", _root(), this.extensionTags.get(((Number) (i)).intValue())._root());
if (!Objects.equals(this.extensionTags.get(((Number) (i)).intValue())._parent(), this))
throw new ConsistencyError("extension_tags", this, this.extensionTags.get(((Number) (i)).intValue())._parent());
{
ExtensionTag _it = this.extensionTags.get(((Number) (i)).intValue());
if ((_it.lenTag() == 0) != (i == this.extensionTags.size() - 1))
throw new ConsistencyError("extension_tags", i == this.extensionTags.size() - 1, _it.lenTag() == 0);
}
}
}
if (_enabledMd5Checksum) {
if (_parent().flags().hasMd5Checksum()) {
if (!Objects.equals(this.md5Checksum._root(), _root()))
throw new ConsistencyError("md5_checksum", _root(), this.md5Checksum._root());
if (!Objects.equals(this.md5Checksum._parent(), this))
throw new ConsistencyError("md5_checksum", this, this.md5Checksum._parent());
}
}
_dirty = false;
}
private Md5Checksum md5Checksum;
private boolean _shouldWriteMd5Checksum = false;
private boolean _enabledMd5Checksum = true;
/**
* 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>.
*/
public Md5Checksum md5Checksum() {
if (_shouldWriteMd5Checksum)
_writeMd5Checksum();
if (this.md5Checksum != null)
return this.md5Checksum;
if (!_enabledMd5Checksum)
return null;
if (_parent().flags().hasMd5Checksum()) {
long _pos = this._io.pos();
this._io.seek(_io().size() - 24);
this.md5Checksum = new Md5Checksum(this._io, this, _root);
this.md5Checksum._read();
this._io.seek(_pos);
}
return this.md5Checksum;
}
public void setMd5Checksum(Md5Checksum _v) { _dirty = true; md5Checksum = _v; }
public void setMd5Checksum_Enabled(boolean _v) { _dirty = true; _enabledMd5Checksum = _v; }
private void _writeMd5Checksum() {
_shouldWriteMd5Checksum = false;
if (_parent().flags().hasMd5Checksum()) {
long _pos = this._io.pos();
this._io.seek(_io().size() - 24);
this.md5Checksum._write_Seq(this._io);
this._io.seek(_pos);
}
}
private byte[] payload;
private String fileName;
private List<ExtensionTag> extensionTags;
private Uf2 _root;
private Uf2.Block _parent;
/**
* 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.
*/
public byte[] payload() { return payload; }
public void setPayload(byte[] _v) { _dirty = true; payload = _v; }
public String fileName() { return fileName; }
public void setFileName(String _v) { _dirty = true; fileName = _v; }
public List<ExtensionTag> extensionTags() { return extensionTags; }
public void setExtensionTags(List<ExtensionTag> _v) { _dirty = true; extensionTags = _v; }
public Uf2 _root() { return _root; }
public void set_root(Uf2 _v) { _dirty = true; _root = _v; }
public Uf2.Block _parent() { return _parent; }
public void set_parent(Uf2.Block _v) { _dirty = true; _parent = _v; }
}
/**
* @see <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#extension-tags">Source</a>
*/
public static class ExtensionTag extends KaitaiStruct.ReadWrite {
public static ExtensionTag fromFile(String fileName) throws IOException {
return new ExtensionTag(new ByteBufferKaitaiStream(fileName));
}
public ExtensionTag() {
this(null, null, null);
}
public ExtensionTag(KaitaiStream _io) {
this(_io, null, null);
}
public ExtensionTag(KaitaiStream _io, Uf2.BlockData _parent) {
this(_io, _parent, null);
}
public ExtensionTag(KaitaiStream _io, Uf2.BlockData _parent, Uf2 _root) {
super(_io);
this._parent = _parent;
this._root = _root;
}
public void _read() {
this.lenTag = this._io.readU1();
{
int _it = this.lenTag;
if (!( ((_it == 0) || (_it >= minLenTag())) )) {
throw new KaitaiStream.ValidationExprError(this.lenTag, this._io, "/types/extension_tag/seq/0");
}
}
this.tagType = Uf2.ExtensionTagType.byId(this._io.readBitsIntLe(24));
if (lenTag() != 0) {
this.value = this._io.readBytes(lenValue());
}
this.padding = this._io.readBytes(KaitaiStream.mod(-(lenTag()), 4));
_dirty = false;
}
public void _fetchInstances() {
if (lenTag() != 0) {
}
}
public void _write_Seq() {
_assertNotDirty();
this._io.writeU1(this.lenTag);
this._io.writeBitsIntLe(24, ((Number) (this.tagType.id())).longValue());
if (lenTag() != 0) {
this._io.writeBytes(this.value);
}
this._io.writeBytes(this.padding);
}
public void _check() {
{
int _it = this.lenTag;
if (!( ((_it == 0) || (_it >= minLenTag())) )) {
throw new KaitaiStream.ValidationExprError(this.lenTag, null, "/types/extension_tag/seq/0");
}
}
if (lenTag() != 0) {
if (this.value.length != lenValue())
throw new ConsistencyError("value", lenValue(), this.value.length);
}
if (this.padding.length != KaitaiStream.mod(-(lenTag()), 4))
throw new ConsistencyError("padding", KaitaiStream.mod(-(lenTag()), 4), this.padding.length);
_dirty = false;
}
private Integer lenValue;
public Integer lenValue() {
if (this.lenValue != null)
return this.lenValue;
this.lenValue = ((Number) ((lenTag() >= minLenTag() ? lenTag() - minLenTag() : 0))).intValue();
return this.lenValue;
}
public void _invalidateLenValue() { this.lenValue = null; }
private Integer minLenTag;
public Integer minLenTag() {
if (this.minLenTag != null)
return this.minLenTag;
this.minLenTag = ((Number) (1 + 3)).intValue();
return this.minLenTag;
}
public void _invalidateMinLenTag() { this.minLenTag = null; }
private int lenTag;
private ExtensionTagType tagType;
private byte[] value;
private byte[] padding;
private Uf2 _root;
private Uf2.BlockData _parent;
/**
* 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.
*/
public int lenTag() { return lenTag; }
public void setLenTag(int _v) { _dirty = true; lenTag = _v; }
public ExtensionTagType tagType() { return tagType; }
public void setTagType(ExtensionTagType _v) { _dirty = true; tagType = _v; }
public byte[] value() { return value; }
public void setValue(byte[] _v) { _dirty = true; value = _v; }
/**
* Tags are 4-byte aligned, so a tag whose size is not a multiple
* of 4 is followed by padding.
*/
public byte[] padding() { return padding; }
public void setPadding(byte[] _v) { _dirty = true; padding = _v; }
public Uf2 _root() { return _root; }
public void set_root(Uf2 _v) { _dirty = true; _root = _v; }
public Uf2.BlockData _parent() { return _parent; }
public void set_parent(Uf2.BlockData _v) { _dirty = true; _parent = _v; }
}
/**
* @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>
*/
public static class Flags extends KaitaiStruct.ReadWrite {
public static Flags fromFile(String fileName) throws IOException {
return new Flags(new ByteBufferKaitaiStream(fileName));
}
public Flags() {
this(null, null, null);
}
public Flags(KaitaiStream _io) {
this(_io, null, null);
}
public Flags(KaitaiStream _io, Uf2.Block _parent) {
this(_io, _parent, null);
}
public Flags(KaitaiStream _io, Uf2.Block _parent, Uf2 _root) {
super(_io);
this._parent = _parent;
this._root = _root;
}
public void _read() {
this.value = this._io.readU4le();
{
long _it = this.value;
if (!( (((_it & ~61441) == 0) && (!( ((isFileContainer()) && (hasExtensionTags())) ))) )) {
throw new KaitaiStream.ValidationExprError(this.value, this._io, "/types/flags/seq/0");
}
}
_dirty = false;
}
public void _fetchInstances() {
}
public void _write_Seq() {
_assertNotDirty();
this._io.writeU4le(this.value);
}
public void _check() {
{
long _it = this.value;
if (!( (((_it & ~61441) == 0) && (!( ((isFileContainer()) && (hasExtensionTags())) ))) )) {
throw new KaitaiStream.ValidationExprError(this.value, null, "/types/flags/seq/0");
}
}
_dirty = false;
}
private Boolean hasExtensionTags;
/**
* Indicates whether extension tags are present after the payload.
*/
public Boolean hasExtensionTags() {
if (this.hasExtensionTags != null)
return this.hasExtensionTags;
this.hasExtensionTags = (value() & 32768) != 0;
return this.hasExtensionTags;
}
public void _invalidateHasExtensionTags() { this.hasExtensionTags = null; }
private Boolean hasFamilyId;
/**
* The field at offset 28 in the block is `family_id` instead of
* `file_size`.
*/
public Boolean hasFamilyId() {
if (this.hasFamilyId != null)
return this.hasFamilyId;
this.hasFamilyId = (value() & 8192) != 0;
return this.hasFamilyId;
}
public void _invalidateHasFamilyId() { this.hasFamilyId = null; }
private Boolean hasMd5Checksum;
/**
* Indicates whether `md5_checksum` is present at the end of `data`.
*/
public Boolean hasMd5Checksum() {
if (this.hasMd5Checksum != null)
return this.hasMd5Checksum;
this.hasMd5Checksum = (value() & 16384) != 0;
return this.hasMd5Checksum;
}
public void _invalidateHasMd5Checksum() { this.hasMd5Checksum = null; }
private Boolean isFileContainer;
/**
* 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`.
*/
public Boolean isFileContainer() {
if (this.isFileContainer != null)
return this.isFileContainer;
this.isFileContainer = (value() & 4096) != 0;
return this.isFileContainer;
}
public void _invalidateIsFileContainer() { this.isFileContainer = null; }
private Boolean notMainFlash;
/**
* 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.
*/
public Boolean notMainFlash() {
if (this.notMainFlash != null)
return this.notMainFlash;
this.notMainFlash = (value() & 1) != 0;
return this.notMainFlash;
}
public void _invalidateNotMainFlash() { this.notMainFlash = null; }
private long value;
private Uf2 _root;
private Uf2.Block _parent;
/**
* 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.
*/
public long value() { return value; }
public void setValue(long _v) { _dirty = true; value = _v; }
public Uf2 _root() { return _root; }
public void set_root(Uf2 _v) { _dirty = true; _root = _v; }
public Uf2.Block _parent() { return _parent; }
public void set_parent(Uf2.Block _v) { _dirty = true; _parent = _v; }
}
/**
* @see <a href="https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#md5-checksum">Source</a>
*/
public static class Md5Checksum extends KaitaiStruct.ReadWrite {
public static Md5Checksum fromFile(String fileName) throws IOException {
return new Md5Checksum(new ByteBufferKaitaiStream(fileName));
}
public Md5Checksum() {
this(null, null, null);
}
public Md5Checksum(KaitaiStream _io) {
this(_io, null, null);
}
public Md5Checksum(KaitaiStream _io, Uf2.BlockData _parent) {
this(_io, _parent, null);
}
public Md5Checksum(KaitaiStream _io, Uf2.BlockData _parent, Uf2 _root) {
super(_io);
this._parent = _parent;
this._root = _root;
}
public void _read() {
this.startAddress = this._io.readU4le();
this.lenRegion = this._io.readU4le();
this.md5 = this._io.readBytes(16);
_dirty = false;
}
public void _fetchInstances() {
}
public void _write_Seq() {
_assertNotDirty();
this._io.writeU4le(this.startAddress);
this._io.writeU4le(this.lenRegion);
this._io.writeBytes(this.md5);
}
public void _check() {
if (this.md5.length != 16)
throw new ConsistencyError("md5", 16, this.md5.length);
_dirty = false;
}
private long startAddress;
private long lenRegion;
private byte[] md5;
private Uf2 _root;
private Uf2.BlockData _parent;
public long startAddress() { return startAddress; }
public void setStartAddress(long _v) { _dirty = true; startAddress = _v; }
public long lenRegion() { return lenRegion; }
public void setLenRegion(long _v) { _dirty = true; lenRegion = _v; }
public byte[] md5() { return md5; }
public void setMd5(byte[] _v) { _dirty = true; md5 = _v; }
public Uf2 _root() { return _root; }
public void set_root(Uf2 _v) { _dirty = true; _root = _v; }
public Uf2.BlockData _parent() { return _parent; }
public void set_parent(Uf2.BlockData _v) { _dirty = true; _parent = _v; }
}
private Block firstBlock;
private List<Block> blocks;
private Uf2 _root;
private KaitaiStruct.ReadWrite _parent;
public Block firstBlock() { return firstBlock; }
public void setFirstBlock(Block _v) { _dirty = true; firstBlock = _v; }
public List<Block> blocks() { return blocks; }
public void setBlocks(List<Block> _v) { _dirty = true; blocks = _v; }
public Uf2 _root() { return _root; }
public void set_root(Uf2 _v) { _dirty = true; _root = _v; }
public KaitaiStruct.ReadWrite _parent() { return _parent; }
public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
}