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.
All C++98/STL code generated by Kaitai Struct depends on the Kaitai Struct runtime library for C++/STL. You must add this dependency to your project before you can parse or serialize any data.
For C++, the easiest way is to clone the runtime library sources and build them along with your project.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
std::istream). One can open local file for that, or use existing std::string or char* buffer.
#include <fstream>
std::ifstream is("path/to/local/file.uf2", std::ifstream::binary);
#include <sstream>
std::istringstream is(str);
#include <sstream>
const char buf[] = { ... };
std::string str(buf, sizeof buf);
std::istringstream is(str);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
uf2_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.first_block() // => get first block
#ifndef UF2_H_
#define UF2_H_
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
class uf2_t;
#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <set>
#include <vector>
#if KAITAI_STRUCT_VERSION < 11000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required"
#endif
/**
* 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).
* \sa https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md Source
*/
class uf2_t : public kaitai::kstruct {
public:
class block_t;
class block_data_t;
class extension_tag_t;
class flags_t;
class md5_checksum_t;
enum extension_tag_type_t {
EXTENSION_TAG_TYPE_END = 0,
EXTENSION_TAG_TYPE_PAGE_SIZE = 780791,
EXTENSION_TAG_TYPE_DESCRIPTION = 6622621,
EXTENSION_TAG_TYPE_RP2_IGNORE_BLOCK = 10049507,
EXTENSION_TAG_TYPE_VERSION = 10471356,
EXTENSION_TAG_TYPE_SHA2_CHECKSUM = 11824560,
EXTENSION_TAG_TYPE_DEVICE_TYPE_ID = 13149993
};
static bool _is_defined_extension_tag_type_t(extension_tag_type_t v);
private:
static const std::set<extension_tag_type_t> _values_extension_tag_type_t;
static std::set<extension_tag_type_t> _build_values_extension_tag_type_t();
public:
enum family_id_t {
FAMILY_ID_STM32L4 = 16738585,
FAMILY_ID_STM32L5 = 69471199,
FAMILY_ID_STM32F411XC = 114362747,
FAMILY_ID_M0SENSE = 299792458,
FAMILY_ID_ATMEGA32 = 374814231,
FAMILY_ID_SAML21 = 407992330,
FAMILY_ID_NRF52 = 458716255,
FAMILY_ID_ESP32 = 475996592,
FAMILY_ID_STM32L1 = 505365293,
FAMILY_ID_STM32L0 = 539900561,
FAMILY_ID_STM32WL = 558239728,
FAMILY_ID_RTL8710B = 585160444,
FAMILY_ID_LPC55 = 716994540,
FAMILY_ID_ESP32C2 = 730387100,
FAMILY_ID_STM32F411XE = 767756741,
FAMILY_ID_STM32G0 = 806311475,
FAMILY_ID_ESP32S31 = 822212545,
FAMILY_ID_GD32F350 = 835856582,
FAMILY_ID_ESP32H2 = 858203894,
FAMILY_ID_RTL8720D = 863621090,
FAMILY_ID_ESP32P4 = 1026592404,
FAMILY_ID_MAIXPLAY_U4 = 1265126769,
FAMILY_ID_STM32G4 = 1282483210,
FAMILY_ID_STM32H5 = 1318001757,
FAMILY_ID_CSK4 = 1332399698,
FAMILY_ID_MIMXRT10XX = 1337120189,
FAMILY_ID_XR809 = 1374225320,
FAMILY_ID_STM32F7 = 1404571392,
FAMILY_ID_ESP32C6 = 1410195298,
FAMILY_ID_SAMD51 = 1427194976,
FAMILY_ID_STM32F4 = 1467308631,
FAMILY_ID_FX2 = 1511523995,
FAMILY_ID_STM32F2 = 1561987630,
FAMILY_ID_STM32F1 = 1591873650,
FAMILY_ID_NRF52833 = 1646171002,
FAMILY_ID_STM32F0 = 1685595318,
FAMILY_ID_BK7231U = 1733968048,
FAMILY_ID_SAMD21 = 1760373640,
FAMILY_ID_CH32V = 1771791084,
FAMILY_ID_BK7251 = 1786956866,
FAMILY_ID_STM32F3 = 1803837832,
FAMILY_ID_STM32F407 = 1829315322,
FAMILY_ID_STM32H7 = 1840668802,
FAMILY_ID_CSK6 = 1853049000,
FAMILY_ID_NRF52832XXAB = 1869948536,
FAMILY_ID_STM32WB = 1892771411,
FAMILY_ID_NRF52832XXAA = 1920081230,
FAMILY_ID_MAX32690 = 1947226634,
FAMILY_ID_ESP32C61 = 2010665156,
FAMILY_ID_BK7231N = 2067722800,
FAMILY_ID_RA4M1 = 2078840685,
FAMILY_ID_PY32F071_UVK5_V3 = 2105173743,
FAMILY_ID_ESP8266 = 2125160941,
FAMILY_ID_KL32L2 = 2139350931,
FAMILY_ID_NRF52820 = 2181929567UL,
FAMILY_ID_STM32F407VG = 2410701054UL,
FAMILY_ID_MAX78002 = 2446589208UL,
FAMILY_ID_RZA1LU = 2501329455UL,
FAMILY_ID_GD32VF103 = 2599435827UL,
FAMILY_ID_ESP32H4 = 2651564682UL,
FAMILY_ID_RTL8710A = 2684343619UL,
FAMILY_ID_AT32F415 = 2697558926UL,
FAMILY_ID_NRF52840 = 2913282112UL,
FAMILY_ID_ESP32H21 = 3067936943UL,
FAMILY_ID_ESP32S2 = 3218951918UL,
FAMILY_ID_ESP32S3 = 3296614247UL,
FAMILY_ID_ESP32C3 = 3559628908UL,
FAMILY_ID_MAX32650 = 3594487346UL,
FAMILY_ID_BL602 = 3725750455UL,
FAMILY_ID_RTL8720C = 3767498084UL,
FAMILY_ID_RP2040 = 3834380118UL,
FAMILY_ID_RP2XXX_ABSOLUTE = 3834380119UL,
FAMILY_ID_RP2XXX_DATA = 3834380120UL,
FAMILY_ID_RP2350_ARM_S = 3834380121UL,
FAMILY_ID_RP2350_RISCV = 3834380122UL,
FAMILY_ID_RP2350_ARM_NS = 3834380123UL,
FAMILY_ID_MAX32666 = 4039314801UL,
FAMILY_ID_ESP32C5 = 4145808195UL
};
static bool _is_defined_family_id_t(family_id_t v);
private:
static const std::set<family_id_t> _values_family_id_t;
static std::set<family_id_t> _build_values_family_id_t();
public:
uf2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, uf2_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~uf2_t();
class block_t : public kaitai::kstruct {
public:
block_t(kaitai::kstream* p__io, uf2_t* p__parent = 0, uf2_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~block_t();
private:
bool f_is_rp2350_e10_block;
bool m_is_rp2350_e10_block;
public:
/**
* 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.
* \sa https://github.com/raspberrypi/picotool/blob/6f6458d792b93685a11423b244a585eaa99eafcf/elf2uf2/elf2uf2.cpp#L147 Git tag "2.3.0"
* \sa https://github.com/raspberrypi/picotool/commit/78c9bd121b09399823b67ee7ea89003ca0d3315f Source
*/
bool is_rp2350_e10_block();
private:
bool f_num_blocks;
uint32_t m_num_blocks;
public:
uint32_t num_blocks();
private:
std::string m_magic;
std::string m_second_magic;
flags_t* m_flags;
uint32_t m_target_address;
uint32_t m_len_payload;
uint32_t m_block_number;
uint32_t m_num_blocks_raw;
uint32_t m_file_size;
bool n_file_size;
public:
bool _is_null_file_size() { file_size(); return n_file_size; };
private:
family_id_t m_family_id;
bool n_family_id;
public:
bool _is_null_family_id() { family_id(); return n_family_id; };
private:
block_data_t* m_data;
std::string m_final_magic;
uf2_t* m__root;
uf2_t* m__parent;
std::string m__raw_data;
kaitai::kstream* m__io__raw_data;
public:
std::string magic() const { return m_magic; }
std::string second_magic() const { return m_second_magic; }
flags_t* flags() const { return m_flags; }
/**
* 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.
*/
uint32_t target_address() const { return m_target_address; }
/**
* 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.
*/
uint32_t len_payload() const { return m_len_payload; }
uint32_t block_number() const { return m_block_number; }
/**
* 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).
*/
uint32_t num_blocks_raw() const { return m_num_blocks_raw; }
/**
* 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.
*/
uint32_t file_size() const { return m_file_size; }
family_id_t family_id() const { return m_family_id; }
block_data_t* data() const { return m_data; }
std::string final_magic() const { return m_final_magic; }
uf2_t* _root() const { return m__root; }
uf2_t* _parent() const { return m__parent; }
std::string _raw_data() const { return m__raw_data; }
kaitai::kstream* _io__raw_data() const { return m__io__raw_data; }
};
class block_data_t : public kaitai::kstruct {
public:
block_data_t(kaitai::kstream* p__io, uf2_t::block_t* p__parent = 0, uf2_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~block_data_t();
private:
bool f_md5_checksum;
md5_checksum_t* m_md5_checksum;
bool n_md5_checksum;
public:
bool _is_null_md5_checksum() { md5_checksum(); return n_md5_checksum; };
private:
public:
/**
* 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>.
*/
md5_checksum_t* md5_checksum();
private:
std::string m_payload;
std::string m_file_name;
bool n_file_name;
public:
bool _is_null_file_name() { file_name(); return n_file_name; };
private:
std::vector<extension_tag_t*>* m_extension_tags;
bool n_extension_tags;
public:
bool _is_null_extension_tags() { extension_tags(); return n_extension_tags; };
private:
uf2_t* m__root;
uf2_t::block_t* m__parent;
public:
/**
* 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.
*/
std::string payload() const { return m_payload; }
std::string file_name() const { return m_file_name; }
std::vector<extension_tag_t*>* extension_tags() const { return m_extension_tags; }
uf2_t* _root() const { return m__root; }
uf2_t::block_t* _parent() const { return m__parent; }
};
/**
* \sa https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#extension-tags Source
*/
class extension_tag_t : public kaitai::kstruct {
public:
extension_tag_t(kaitai::kstream* p__io, uf2_t::block_data_t* p__parent = 0, uf2_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~extension_tag_t();
private:
bool f_len_value;
int32_t m_len_value;
public:
int32_t len_value();
private:
bool f_min_len_tag;
int32_t m_min_len_tag;
public:
int32_t min_len_tag();
private:
uint8_t m_len_tag;
extension_tag_type_t m_tag_type;
std::string m_value;
bool n_value;
public:
bool _is_null_value() { value(); return n_value; };
private:
std::string m_padding;
uf2_t* m__root;
uf2_t::block_data_t* m__parent;
public:
/**
* 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.
*/
uint8_t len_tag() const { return m_len_tag; }
extension_tag_type_t tag_type() const { return m_tag_type; }
std::string value() const { return m_value; }
/**
* Tags are 4-byte aligned, so a tag whose size is not a multiple
* of 4 is followed by padding.
*/
std::string padding() const { return m_padding; }
uf2_t* _root() const { return m__root; }
uf2_t::block_data_t* _parent() const { return m__parent; }
};
/**
* \sa https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/uf2.h#L43-L47 Source
* \sa https://github.com/raspberrypi/pico-sdk/blob/98a542c1a62fb549ffb5d66a3e5892b06276b670/src/common/boot_uf2_headers/include/boot/uf2.h#L23-L27 Git tag "2.3.0"
*/
class flags_t : public kaitai::kstruct {
public:
flags_t(kaitai::kstream* p__io, uf2_t::block_t* p__parent = 0, uf2_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~flags_t();
private:
bool f_has_extension_tags;
bool m_has_extension_tags;
public:
/**
* Indicates whether extension tags are present after the payload.
*/
bool has_extension_tags();
private:
bool f_has_family_id;
bool m_has_family_id;
public:
/**
* The field at offset 28 in the block is `family_id` instead of
* `file_size`.
*/
bool has_family_id();
private:
bool f_has_md5_checksum;
bool m_has_md5_checksum;
public:
/**
* Indicates whether `md5_checksum` is present at the end of `data`.
*/
bool has_md5_checksum();
private:
bool f_is_file_container;
bool m_is_file_container;
public:
/**
* 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`.
*/
bool is_file_container();
private:
bool f_not_main_flash;
bool m_not_main_flash;
public:
/**
* 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.
*/
bool not_main_flash();
private:
uint32_t m_value;
uf2_t* m__root;
uf2_t::block_t* m__parent;
public:
/**
* 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.
*/
uint32_t value() const { return m_value; }
uf2_t* _root() const { return m__root; }
uf2_t::block_t* _parent() const { return m__parent; }
};
/**
* \sa https://github.com/microsoft/uf2/blob/90e9741f217f5a40c98ba74d663e408041037578/README.md#md5-checksum Source
*/
class md5_checksum_t : public kaitai::kstruct {
public:
md5_checksum_t(kaitai::kstream* p__io, uf2_t::block_data_t* p__parent = 0, uf2_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~md5_checksum_t();
private:
uint32_t m_start_address;
uint32_t m_len_region;
std::string m_md5;
uf2_t* m__root;
uf2_t::block_data_t* m__parent;
public:
uint32_t start_address() const { return m_start_address; }
uint32_t len_region() const { return m_len_region; }
std::string md5() const { return m_md5; }
uf2_t* _root() const { return m__root; }
uf2_t::block_data_t* _parent() const { return m__parent; }
};
private:
block_t* m_first_block;
std::vector<block_t*>* m_blocks;
uf2_t* m__root;
kaitai::kstruct* m__parent;
public:
block_t* first_block() const { return m_first_block; }
std::vector<block_t*>* blocks() const { return m_blocks; }
uf2_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
#endif // UF2_H_
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "uf2.h"
#include "kaitai/exceptions.h"
std::set<uf2_t::extension_tag_type_t> uf2_t::_build_values_extension_tag_type_t() {
std::set<uf2_t::extension_tag_type_t> _t;
_t.insert(uf2_t::EXTENSION_TAG_TYPE_END);
_t.insert(uf2_t::EXTENSION_TAG_TYPE_PAGE_SIZE);
_t.insert(uf2_t::EXTENSION_TAG_TYPE_DESCRIPTION);
_t.insert(uf2_t::EXTENSION_TAG_TYPE_RP2_IGNORE_BLOCK);
_t.insert(uf2_t::EXTENSION_TAG_TYPE_VERSION);
_t.insert(uf2_t::EXTENSION_TAG_TYPE_SHA2_CHECKSUM);
_t.insert(uf2_t::EXTENSION_TAG_TYPE_DEVICE_TYPE_ID);
return _t;
}
const std::set<uf2_t::extension_tag_type_t> uf2_t::_values_extension_tag_type_t = uf2_t::_build_values_extension_tag_type_t();
bool uf2_t::_is_defined_extension_tag_type_t(uf2_t::extension_tag_type_t v) {
return uf2_t::_values_extension_tag_type_t.find(v) != uf2_t::_values_extension_tag_type_t.end();
}
std::set<uf2_t::family_id_t> uf2_t::_build_values_family_id_t() {
std::set<uf2_t::family_id_t> _t;
_t.insert(uf2_t::FAMILY_ID_STM32L4);
_t.insert(uf2_t::FAMILY_ID_STM32L5);
_t.insert(uf2_t::FAMILY_ID_STM32F411XC);
_t.insert(uf2_t::FAMILY_ID_M0SENSE);
_t.insert(uf2_t::FAMILY_ID_ATMEGA32);
_t.insert(uf2_t::FAMILY_ID_SAML21);
_t.insert(uf2_t::FAMILY_ID_NRF52);
_t.insert(uf2_t::FAMILY_ID_ESP32);
_t.insert(uf2_t::FAMILY_ID_STM32L1);
_t.insert(uf2_t::FAMILY_ID_STM32L0);
_t.insert(uf2_t::FAMILY_ID_STM32WL);
_t.insert(uf2_t::FAMILY_ID_RTL8710B);
_t.insert(uf2_t::FAMILY_ID_LPC55);
_t.insert(uf2_t::FAMILY_ID_ESP32C2);
_t.insert(uf2_t::FAMILY_ID_STM32F411XE);
_t.insert(uf2_t::FAMILY_ID_STM32G0);
_t.insert(uf2_t::FAMILY_ID_ESP32S31);
_t.insert(uf2_t::FAMILY_ID_GD32F350);
_t.insert(uf2_t::FAMILY_ID_ESP32H2);
_t.insert(uf2_t::FAMILY_ID_RTL8720D);
_t.insert(uf2_t::FAMILY_ID_ESP32P4);
_t.insert(uf2_t::FAMILY_ID_MAIXPLAY_U4);
_t.insert(uf2_t::FAMILY_ID_STM32G4);
_t.insert(uf2_t::FAMILY_ID_STM32H5);
_t.insert(uf2_t::FAMILY_ID_CSK4);
_t.insert(uf2_t::FAMILY_ID_MIMXRT10XX);
_t.insert(uf2_t::FAMILY_ID_XR809);
_t.insert(uf2_t::FAMILY_ID_STM32F7);
_t.insert(uf2_t::FAMILY_ID_ESP32C6);
_t.insert(uf2_t::FAMILY_ID_SAMD51);
_t.insert(uf2_t::FAMILY_ID_STM32F4);
_t.insert(uf2_t::FAMILY_ID_FX2);
_t.insert(uf2_t::FAMILY_ID_STM32F2);
_t.insert(uf2_t::FAMILY_ID_STM32F1);
_t.insert(uf2_t::FAMILY_ID_NRF52833);
_t.insert(uf2_t::FAMILY_ID_STM32F0);
_t.insert(uf2_t::FAMILY_ID_BK7231U);
_t.insert(uf2_t::FAMILY_ID_SAMD21);
_t.insert(uf2_t::FAMILY_ID_CH32V);
_t.insert(uf2_t::FAMILY_ID_BK7251);
_t.insert(uf2_t::FAMILY_ID_STM32F3);
_t.insert(uf2_t::FAMILY_ID_STM32F407);
_t.insert(uf2_t::FAMILY_ID_STM32H7);
_t.insert(uf2_t::FAMILY_ID_CSK6);
_t.insert(uf2_t::FAMILY_ID_NRF52832XXAB);
_t.insert(uf2_t::FAMILY_ID_STM32WB);
_t.insert(uf2_t::FAMILY_ID_NRF52832XXAA);
_t.insert(uf2_t::FAMILY_ID_MAX32690);
_t.insert(uf2_t::FAMILY_ID_ESP32C61);
_t.insert(uf2_t::FAMILY_ID_BK7231N);
_t.insert(uf2_t::FAMILY_ID_RA4M1);
_t.insert(uf2_t::FAMILY_ID_PY32F071_UVK5_V3);
_t.insert(uf2_t::FAMILY_ID_ESP8266);
_t.insert(uf2_t::FAMILY_ID_KL32L2);
_t.insert(uf2_t::FAMILY_ID_NRF52820);
_t.insert(uf2_t::FAMILY_ID_STM32F407VG);
_t.insert(uf2_t::FAMILY_ID_MAX78002);
_t.insert(uf2_t::FAMILY_ID_RZA1LU);
_t.insert(uf2_t::FAMILY_ID_GD32VF103);
_t.insert(uf2_t::FAMILY_ID_ESP32H4);
_t.insert(uf2_t::FAMILY_ID_RTL8710A);
_t.insert(uf2_t::FAMILY_ID_AT32F415);
_t.insert(uf2_t::FAMILY_ID_NRF52840);
_t.insert(uf2_t::FAMILY_ID_ESP32H21);
_t.insert(uf2_t::FAMILY_ID_ESP32S2);
_t.insert(uf2_t::FAMILY_ID_ESP32S3);
_t.insert(uf2_t::FAMILY_ID_ESP32C3);
_t.insert(uf2_t::FAMILY_ID_MAX32650);
_t.insert(uf2_t::FAMILY_ID_BL602);
_t.insert(uf2_t::FAMILY_ID_RTL8720C);
_t.insert(uf2_t::FAMILY_ID_RP2040);
_t.insert(uf2_t::FAMILY_ID_RP2XXX_ABSOLUTE);
_t.insert(uf2_t::FAMILY_ID_RP2XXX_DATA);
_t.insert(uf2_t::FAMILY_ID_RP2350_ARM_S);
_t.insert(uf2_t::FAMILY_ID_RP2350_RISCV);
_t.insert(uf2_t::FAMILY_ID_RP2350_ARM_NS);
_t.insert(uf2_t::FAMILY_ID_MAX32666);
_t.insert(uf2_t::FAMILY_ID_ESP32C5);
return _t;
}
const std::set<uf2_t::family_id_t> uf2_t::_values_family_id_t = uf2_t::_build_values_family_id_t();
bool uf2_t::_is_defined_family_id_t(uf2_t::family_id_t v) {
return uf2_t::_values_family_id_t.find(v) != uf2_t::_values_family_id_t.end();
}
uf2_t::uf2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, uf2_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root ? p__root : this;
m_first_block = 0;
m_blocks = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void uf2_t::_read() {
m_first_block = new block_t(m__io, this, m__root);
m_blocks = new std::vector<block_t*>();
const int l_blocks = first_block()->num_blocks() - 1;
for (int i = 0; i < l_blocks; i++) {
m_blocks->push_back(new block_t(m__io, this, m__root));
}
}
uf2_t::~uf2_t() {
_clean_up();
}
void uf2_t::_clean_up() {
if (m_first_block) {
delete m_first_block; m_first_block = 0;
}
if (m_blocks) {
for (std::vector<block_t*>::iterator it = m_blocks->begin(); it != m_blocks->end(); ++it) {
delete *it;
}
delete m_blocks; m_blocks = 0;
}
}
uf2_t::block_t::block_t(kaitai::kstream* p__io, uf2_t* p__parent, uf2_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_flags = 0;
m_data = 0;
m__io__raw_data = 0;
f_is_rp2350_e10_block = false;
f_num_blocks = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void uf2_t::block_t::_read() {
m_magic = m__io->read_bytes(4);
if (!(m_magic == std::string("\x55\x46\x32\x0A", 4))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x55\x46\x32\x0A", 4), m_magic, m__io, std::string("/types/block/seq/0"));
}
m_second_magic = m__io->read_bytes(4);
if (!(m_second_magic == std::string("\x57\x51\x5D\x9E", 4))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x57\x51\x5D\x9E", 4), m_second_magic, m__io, std::string("/types/block/seq/1"));
}
m_flags = new flags_t(m__io, this, m__root);
m_target_address = m__io->read_u4le();
{
uint32_t _ = m_target_address;
if (!(kaitai::kstream::mod(_, 4) == 0)) {
throw kaitai::validation_expr_error<uint32_t>(m_target_address, m__io, std::string("/types/block/seq/3"));
}
}
m_len_payload = m__io->read_u4le();
{
uint32_t _ = m_len_payload;
if (!(kaitai::kstream::mod(_, 4) == 0)) {
throw kaitai::validation_expr_error<uint32_t>(m_len_payload, m__io, std::string("/types/block/seq/4"));
}
}
m_block_number = m__io->read_u4le();
m_num_blocks_raw = m__io->read_u4le();
if (!(m_num_blocks_raw >= block_number() + 1)) {
throw kaitai::validation_less_than_error<uint32_t>(block_number() + 1, m_num_blocks_raw, m__io, std::string("/types/block/seq/6"));
}
n_file_size = true;
if (!(flags()->has_family_id())) {
n_file_size = false;
m_file_size = m__io->read_u4le();
}
n_family_id = true;
if (flags()->has_family_id()) {
n_family_id = false;
m_family_id = static_cast<uf2_t::family_id_t>(m__io->read_u4le());
}
m__raw_data = m__io->read_bytes(476);
m__io__raw_data = new kaitai::kstream(m__raw_data);
m_data = new block_data_t(m__io__raw_data, this, m__root);
m_final_magic = m__io->read_bytes(4);
if (!(m_final_magic == std::string("\x30\x6F\xB1\x0A", 4))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x30\x6F\xB1\x0A", 4), m_final_magic, m__io, std::string("/types/block/seq/10"));
}
}
uf2_t::block_t::~block_t() {
_clean_up();
}
void uf2_t::block_t::_clean_up() {
if (m_flags) {
delete m_flags; m_flags = 0;
}
if (!n_file_size) {
}
if (!n_family_id) {
}
if (m__io__raw_data) {
delete m__io__raw_data; m__io__raw_data = 0;
}
if (m_data) {
delete m_data; m_data = 0;
}
}
bool uf2_t::block_t::is_rp2350_e10_block() {
if (f_is_rp2350_e10_block)
return m_is_rp2350_e10_block;
f_is_rp2350_e10_block = true;
m_is_rp2350_e10_block = (( ((flags()->value() == 8192) || (flags()->value() == 40960)) ) && (family_id() == uf2_t::FAMILY_ID_RP2XXX_ABSOLUTE) && (num_blocks_raw() == 2) && (block_number() == 0) && (len_payload() == 256) && (data()->payload().at(0) == 239) && (data()->payload().at(data()->payload().length() - 1) == 239) && ( ((!(flags()->has_extension_tags())) || (data()->extension_tags()->at(0)->len_tag() == 0) || ( ((data()->extension_tags()->at(0)->len_tag() == 4) && (data()->extension_tags()->at(0)->tag_type() == uf2_t::EXTENSION_TAG_TYPE_RP2_IGNORE_BLOCK)) )) )) ;
return m_is_rp2350_e10_block;
}
uint32_t uf2_t::block_t::num_blocks() {
if (f_num_blocks)
return m_num_blocks;
f_num_blocks = true;
m_num_blocks = ((is_rp2350_e10_block()) ? (1) : (num_blocks_raw()));
return m_num_blocks;
}
uf2_t::block_data_t::block_data_t(kaitai::kstream* p__io, uf2_t::block_t* p__parent, uf2_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_extension_tags = 0;
m_md5_checksum = 0;
f_md5_checksum = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void uf2_t::block_data_t::_read() {
m_payload = m__io->read_bytes(_parent()->len_payload());
n_file_name = true;
if (_parent()->flags()->is_file_container()) {
n_file_name = false;
m_file_name = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), "UTF-8");
}
n_extension_tags = true;
if (_parent()->flags()->has_extension_tags()) {
n_extension_tags = false;
m_extension_tags = new std::vector<extension_tag_t*>();
{
int i = 0;
extension_tag_t* _;
do {
_ = new extension_tag_t(m__io, this, m__root);
m_extension_tags->push_back(_);
i++;
} while (!(_->len_tag() == 0));
}
}
}
uf2_t::block_data_t::~block_data_t() {
_clean_up();
}
void uf2_t::block_data_t::_clean_up() {
if (!n_file_name) {
}
if (!n_extension_tags) {
if (m_extension_tags) {
for (std::vector<extension_tag_t*>::iterator it = m_extension_tags->begin(); it != m_extension_tags->end(); ++it) {
delete *it;
}
delete m_extension_tags; m_extension_tags = 0;
}
}
if (f_md5_checksum && !n_md5_checksum) {
if (m_md5_checksum) {
delete m_md5_checksum; m_md5_checksum = 0;
}
}
}
uf2_t::md5_checksum_t* uf2_t::block_data_t::md5_checksum() {
if (f_md5_checksum)
return m_md5_checksum;
f_md5_checksum = true;
n_md5_checksum = true;
if (_parent()->flags()->has_md5_checksum()) {
n_md5_checksum = false;
std::streampos _pos = m__io->pos();
m__io->seek(_io()->size() - 24);
m_md5_checksum = new md5_checksum_t(m__io, this, m__root);
m__io->seek(_pos);
}
return m_md5_checksum;
}
uf2_t::extension_tag_t::extension_tag_t(kaitai::kstream* p__io, uf2_t::block_data_t* p__parent, uf2_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
f_len_value = false;
f_min_len_tag = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void uf2_t::extension_tag_t::_read() {
m_len_tag = m__io->read_u1();
{
uint8_t _ = m_len_tag;
if (!( ((_ == 0) || (_ >= min_len_tag())) )) {
throw kaitai::validation_expr_error<uint8_t>(m_len_tag, m__io, std::string("/types/extension_tag/seq/0"));
}
}
m_tag_type = static_cast<uf2_t::extension_tag_type_t>(m__io->read_bits_int_le(24));
m__io->align_to_byte();
n_value = true;
if (len_tag() != 0) {
n_value = false;
m_value = m__io->read_bytes(len_value());
}
m_padding = m__io->read_bytes(kaitai::kstream::mod(-(len_tag()), 4));
}
uf2_t::extension_tag_t::~extension_tag_t() {
_clean_up();
}
void uf2_t::extension_tag_t::_clean_up() {
if (!n_value) {
}
}
int32_t uf2_t::extension_tag_t::len_value() {
if (f_len_value)
return m_len_value;
f_len_value = true;
m_len_value = ((len_tag() >= min_len_tag()) ? (len_tag() - min_len_tag()) : (0));
return m_len_value;
}
int32_t uf2_t::extension_tag_t::min_len_tag() {
if (f_min_len_tag)
return m_min_len_tag;
f_min_len_tag = true;
m_min_len_tag = 1 + 3;
return m_min_len_tag;
}
uf2_t::flags_t::flags_t(kaitai::kstream* p__io, uf2_t::block_t* p__parent, uf2_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
f_has_extension_tags = false;
f_has_family_id = false;
f_has_md5_checksum = false;
f_is_file_container = false;
f_not_main_flash = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void uf2_t::flags_t::_read() {
m_value = m__io->read_u4le();
{
uint32_t _ = m_value;
if (!( (((_ & ~61441) == 0) && (!( ((is_file_container()) && (has_extension_tags())) ))) )) {
throw kaitai::validation_expr_error<uint32_t>(m_value, m__io, std::string("/types/flags/seq/0"));
}
}
}
uf2_t::flags_t::~flags_t() {
_clean_up();
}
void uf2_t::flags_t::_clean_up() {
}
bool uf2_t::flags_t::has_extension_tags() {
if (f_has_extension_tags)
return m_has_extension_tags;
f_has_extension_tags = true;
m_has_extension_tags = (value() & 32768) != 0;
return m_has_extension_tags;
}
bool uf2_t::flags_t::has_family_id() {
if (f_has_family_id)
return m_has_family_id;
f_has_family_id = true;
m_has_family_id = (value() & 8192) != 0;
return m_has_family_id;
}
bool uf2_t::flags_t::has_md5_checksum() {
if (f_has_md5_checksum)
return m_has_md5_checksum;
f_has_md5_checksum = true;
m_has_md5_checksum = (value() & 16384) != 0;
return m_has_md5_checksum;
}
bool uf2_t::flags_t::is_file_container() {
if (f_is_file_container)
return m_is_file_container;
f_is_file_container = true;
m_is_file_container = (value() & 4096) != 0;
return m_is_file_container;
}
bool uf2_t::flags_t::not_main_flash() {
if (f_not_main_flash)
return m_not_main_flash;
f_not_main_flash = true;
m_not_main_flash = (value() & 1) != 0;
return m_not_main_flash;
}
uf2_t::md5_checksum_t::md5_checksum_t(kaitai::kstream* p__io, uf2_t::block_data_t* p__parent, uf2_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void uf2_t::md5_checksum_t::_read() {
m_start_address = m__io->read_u4le();
m_len_region = m__io->read_u4le();
m_md5 = m__io->read_bytes(16);
}
uf2_t::md5_checksum_t::~md5_checksum_t() {
_clean_up();
}
void uf2_t::md5_checksum_t::_clean_up() {
}