APM (Apple Partition Map) partition table: C++11/STL parsing library

This page hosts a formal specification of APM (Apple Partition Map) partition table using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (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.bin", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    apm_partition_table_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.sector_size() // => 0x200 (512) bytes for disks, 0x1000 (4096) bytes is not supported by APM
0x800 (2048) bytes for CDROM

C++11/STL source code to parse APM (Apple Partition Map) partition table

apm_partition_table.h

#pragma once

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

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#include <vector>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif

/**
 * \sa https://en.wikipedia.org/wiki/Apple_Partition_Map Source
 */

class apm_partition_table_t : public kaitai::kstruct {

public:
    class partition_entry_t;

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

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

public:
    ~apm_partition_table_t();

    class partition_entry_t : public kaitai::kstruct {

    public:

        partition_entry_t(kaitai::kstream* p__io, apm_partition_table_t* p__parent = nullptr, apm_partition_table_t* p__root = nullptr);

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

    public:
        ~partition_entry_t();

    private:
        bool f_partition;
        std::string m_partition;
        bool n_partition;

    public:
        bool _is_null_partition() { partition(); return n_partition; };

    private:

    public:
        std::string partition();

    private:
        bool f_data;
        std::string m_data;

    public:
        std::string data();

    private:
        bool f_boot_code;
        std::string m_boot_code;

    public:
        std::string boot_code();

    private:
        std::string m_magic;
        std::string m_reserved_1;
        uint32_t m_number_of_partitions;
        uint32_t m_partition_start;
        uint32_t m_partition_size;
        std::string m_partition_name;
        std::string m_partition_type;
        uint32_t m_data_start;
        uint32_t m_data_size;
        uint32_t m_partition_status;
        uint32_t m_boot_code_start;
        uint32_t m_boot_code_size;
        uint32_t m_boot_loader_address;
        std::string m_reserved_2;
        uint32_t m_boot_code_entry;
        std::string m_reserved_3;
        uint32_t m_boot_code_cksum;
        std::string m_processor_type;
        apm_partition_table_t* m__root;
        apm_partition_table_t* m__parent;

    public:
        std::string magic() const { return m_magic; }
        std::string reserved_1() const { return m_reserved_1; }
        uint32_t number_of_partitions() const { return m_number_of_partitions; }

        /**
         * First sector
         */
        uint32_t partition_start() const { return m_partition_start; }

        /**
         * Number of sectors
         */
        uint32_t partition_size() const { return m_partition_size; }
        std::string partition_name() const { return m_partition_name; }
        std::string partition_type() const { return m_partition_type; }

        /**
         * First sector
         */
        uint32_t data_start() const { return m_data_start; }

        /**
         * Number of sectors
         */
        uint32_t data_size() const { return m_data_size; }
        uint32_t partition_status() const { return m_partition_status; }

        /**
         * First sector
         */
        uint32_t boot_code_start() const { return m_boot_code_start; }

        /**
         * Number of bytes
         */
        uint32_t boot_code_size() const { return m_boot_code_size; }

        /**
         * Address of bootloader code
         */
        uint32_t boot_loader_address() const { return m_boot_loader_address; }
        std::string reserved_2() const { return m_reserved_2; }

        /**
         * Boot code entry point
         */
        uint32_t boot_code_entry() const { return m_boot_code_entry; }
        std::string reserved_3() const { return m_reserved_3; }

        /**
         * Boot code checksum
         */
        uint32_t boot_code_cksum() const { return m_boot_code_cksum; }
        std::string processor_type() const { return m_processor_type; }
        apm_partition_table_t* _root() const { return m__root; }
        apm_partition_table_t* _parent() const { return m__parent; }
    };

private:
    bool f_sector_size;
    int32_t m_sector_size;

public:

    /**
     * 0x200 (512) bytes for disks, 0x1000 (4096) bytes is not supported by APM
     * 0x800 (2048) bytes for CDROM
     */
    int32_t sector_size();

private:
    bool f_partition_lookup;
    std::unique_ptr<partition_entry_t> m_partition_lookup;

public:

    /**
     * Every partition entry contains the number of partition entries.
     * We parse the first entry, to know how many to parse, including the first one.
     * No logic is given what to do if other entries have a different number.
     */
    partition_entry_t* partition_lookup();

private:
    bool f_partition_entries;
    std::unique_ptr<std::vector<std::unique_ptr<partition_entry_t>>> m_partition_entries;

public:
    std::vector<std::unique_ptr<partition_entry_t>>* partition_entries();

private:
    apm_partition_table_t* m__root;
    kaitai::kstruct* m__parent;
    std::string m__raw_partition_lookup;
    std::unique_ptr<kaitai::kstream> m__io__raw_partition_lookup;
    std::unique_ptr<std::vector<std::string>> m__raw_partition_entries;
    std::unique_ptr<std::vector<std::unique_ptr<kaitai::kstream>>> m__io__raw_partition_entries;

public:
    apm_partition_table_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
    std::string _raw_partition_lookup() const { return m__raw_partition_lookup; }
    kaitai::kstream* _io__raw_partition_lookup() const { return m__io__raw_partition_lookup.get(); }
    std::vector<std::string>* _raw_partition_entries() const { return m__raw_partition_entries.get(); }
    std::vector<std::unique_ptr<kaitai::kstream>>* _io__raw_partition_entries() const { return m__io__raw_partition_entries.get(); }
};

apm_partition_table.cpp

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

#include "apm_partition_table.h"
#include "kaitai/exceptions.h"

apm_partition_table_t::apm_partition_table_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, apm_partition_table_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_partition_lookup = nullptr;
    m__io__raw_partition_lookup = nullptr;
    m_partition_entries = nullptr;
    m__raw_partition_entries = nullptr;
    m__io__raw_partition_entries = nullptr;
    f_sector_size = false;
    f_partition_lookup = false;
    f_partition_entries = false;
    _read();
}

void apm_partition_table_t::_read() {
}

apm_partition_table_t::~apm_partition_table_t() {
    _clean_up();
}

void apm_partition_table_t::_clean_up() {
    if (f_partition_lookup) {
    }
    if (f_partition_entries) {
    }
}

apm_partition_table_t::partition_entry_t::partition_entry_t(kaitai::kstream* p__io, apm_partition_table_t* p__parent, apm_partition_table_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_partition = false;
    f_data = false;
    f_boot_code = false;
    _read();
}

void apm_partition_table_t::partition_entry_t::_read() {
    m_magic = m__io->read_bytes(2);
    if (!(magic() == std::string("\x50\x4D", 2))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x50\x4D", 2), magic(), _io(), std::string("/types/partition_entry/seq/0"));
    }
    m_reserved_1 = m__io->read_bytes(2);
    m_number_of_partitions = m__io->read_u4be();
    m_partition_start = m__io->read_u4be();
    m_partition_size = m__io->read_u4be();
    m_partition_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(32), 0, false), std::string("ascii"));
    m_partition_type = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(32), 0, false), std::string("ascii"));
    m_data_start = m__io->read_u4be();
    m_data_size = m__io->read_u4be();
    m_partition_status = m__io->read_u4be();
    m_boot_code_start = m__io->read_u4be();
    m_boot_code_size = m__io->read_u4be();
    m_boot_loader_address = m__io->read_u4be();
    m_reserved_2 = m__io->read_bytes(4);
    m_boot_code_entry = m__io->read_u4be();
    m_reserved_3 = m__io->read_bytes(4);
    m_boot_code_cksum = m__io->read_u4be();
    m_processor_type = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(16), 0, false), std::string("ascii"));
}

apm_partition_table_t::partition_entry_t::~partition_entry_t() {
    _clean_up();
}

void apm_partition_table_t::partition_entry_t::_clean_up() {
    if (f_partition && !n_partition) {
    }
    if (f_data) {
    }
    if (f_boot_code) {
    }
}

std::string apm_partition_table_t::partition_entry_t::partition() {
    if (f_partition)
        return m_partition;
    n_partition = true;
    if ((partition_status() & 1) != 0) {
        n_partition = false;
        kaitai::kstream *io = _root()->_io();
        std::streampos _pos = io->pos();
        io->seek((partition_start() * _root()->sector_size()));
        m_partition = io->read_bytes((partition_size() * _root()->sector_size()));
        io->seek(_pos);
        f_partition = true;
    }
    return m_partition;
}

std::string apm_partition_table_t::partition_entry_t::data() {
    if (f_data)
        return m_data;
    kaitai::kstream *io = _root()->_io();
    std::streampos _pos = io->pos();
    io->seek((data_start() * _root()->sector_size()));
    m_data = io->read_bytes((data_size() * _root()->sector_size()));
    io->seek(_pos);
    f_data = true;
    return m_data;
}

std::string apm_partition_table_t::partition_entry_t::boot_code() {
    if (f_boot_code)
        return m_boot_code;
    kaitai::kstream *io = _root()->_io();
    std::streampos _pos = io->pos();
    io->seek((boot_code_start() * _root()->sector_size()));
    m_boot_code = io->read_bytes(boot_code_size());
    io->seek(_pos);
    f_boot_code = true;
    return m_boot_code;
}

int32_t apm_partition_table_t::sector_size() {
    if (f_sector_size)
        return m_sector_size;
    m_sector_size = 512;
    f_sector_size = true;
    return m_sector_size;
}

apm_partition_table_t::partition_entry_t* apm_partition_table_t::partition_lookup() {
    if (f_partition_lookup)
        return m_partition_lookup.get();
    kaitai::kstream *io = _root()->_io();
    std::streampos _pos = io->pos();
    io->seek(_root()->sector_size());
    m__raw_partition_lookup = io->read_bytes(sector_size());
    m__io__raw_partition_lookup = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_partition_lookup));
    m_partition_lookup = std::unique_ptr<partition_entry_t>(new partition_entry_t(m__io__raw_partition_lookup.get(), this, m__root));
    io->seek(_pos);
    f_partition_lookup = true;
    return m_partition_lookup.get();
}

std::vector<std::unique_ptr<apm_partition_table_t::partition_entry_t>>* apm_partition_table_t::partition_entries() {
    if (f_partition_entries)
        return m_partition_entries.get();
    kaitai::kstream *io = _root()->_io();
    std::streampos _pos = io->pos();
    io->seek(_root()->sector_size());
    m__raw_partition_entries = std::unique_ptr<std::vector<std::string>>(new std::vector<std::string>());
    m__io__raw_partition_entries = std::unique_ptr<std::vector<std::unique_ptr<kaitai::kstream>>>(new std::vector<std::unique_ptr<kaitai::kstream>>());
    m_partition_entries = std::unique_ptr<std::vector<std::unique_ptr<partition_entry_t>>>(new std::vector<std::unique_ptr<partition_entry_t>>());
    const int l_partition_entries = _root()->partition_lookup()->number_of_partitions();
    for (int i = 0; i < l_partition_entries; i++) {
        m__raw_partition_entries->push_back(std::move(io->read_bytes(sector_size())));
        kaitai::kstream* io__raw_partition_entries = new kaitai::kstream(m__raw_partition_entries->at(m__raw_partition_entries->size() - 1));
        m__io__raw_partition_entries->emplace_back(io__raw_partition_entries);
        m_partition_entries->push_back(std::move(std::unique_ptr<partition_entry_t>(new partition_entry_t(io__raw_partition_entries, this, m__root))));
    }
    io->seek(_pos);
    f_partition_entries = true;
    return m_partition_entries.get();
}