#pragma once

// 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 <memory>
#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;

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;

public:

    uf2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, uf2_t* p__root = nullptr);

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 = nullptr, uf2_t* p__root = nullptr);

    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;
        std::unique_ptr<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:
        std::unique_ptr<block_data_t> m_data;
        std::string m_final_magic;
        uf2_t* m__root;
        uf2_t* m__parent;
        std::string m__raw_data;
        std::unique_ptr<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.get(); }

        /**
         * 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.get(); }
        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.get(); }
    };

    class block_data_t : public kaitai::kstruct {

    public:

        block_data_t(kaitai::kstream* p__io, uf2_t::block_t* p__parent = nullptr, uf2_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~block_data_t();

    private:
        bool f_md5_checksum;
        std::unique_ptr<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::unique_ptr<std::vector<std::unique_ptr<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<std::unique_ptr<extension_tag_t>>* extension_tags() const { return m_extension_tags.get(); }
        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 = nullptr, uf2_t* p__root = nullptr);

    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 = nullptr, uf2_t* p__root = nullptr);

    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 = nullptr, uf2_t* p__root = nullptr);

    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:
    std::unique_ptr<block_t> m_first_block;
    std::unique_ptr<std::vector<std::unique_ptr<block_t>>> m_blocks;
    uf2_t* m__root;
    kaitai::kstruct* m__parent;

public:
    block_t* first_block() const { return m_first_block.get(); }
    std::vector<std::unique_ptr<block_t>>* blocks() const { return m_blocks.get(); }
    uf2_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};
