ISO9660 CD filesystem: C++98/STL parsing library

ISO9660 is standard filesystem used on read-only optical discs (mostly CD-ROM). The standard was based on earlier High Sierra Format (HSF), proposed for CD-ROMs in 1985, and, after several revisions, it was accepted as ISO9960:1998.

The format emphasizes portability (thus having pretty minimal features and very conservative file names standards) and sequential access (which favors disc devices with relatively slow rotation speed).

File extension

iso

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of ISO9660 CD filesystem 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++98/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.iso", 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:
    iso9660_t data(&ks);
    

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

data.sector_size() // => get sector size

C++98/STL source code to parse ISO9660 CD filesystem

iso9660.h

#ifndef ISO9660_H_
#define ISO9660_H_

// 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 <vector>

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

/**
 * ISO9660 is standard filesystem used on read-only optical discs
 * (mostly CD-ROM). The standard was based on earlier High Sierra
 * Format (HSF), proposed for CD-ROMs in 1985, and, after several
 * revisions, it was accepted as ISO9960:1998.
 * 
 * The format emphasizes portability (thus having pretty minimal
 * features and very conservative file names standards) and sequential
 * access (which favors disc devices with relatively slow rotation
 * speed).
 */

class iso9660_t : public kaitai::kstruct {

public:
    class vol_desc_primary_t;
    class vol_desc_boot_record_t;
    class datetime_t;
    class dir_entry_t;
    class vol_desc_t;
    class path_table_entry_le_t;
    class dir_entries_t;
    class u4bi_t;
    class u2bi_t;
    class path_table_le_t;
    class dec_datetime_t;
    class dir_entry_body_t;

    iso9660_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, iso9660_t* p__root = 0);

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

public:
    ~iso9660_t();

    /**
     * \sa https://wiki.osdev.org/ISO_9660#The_Primary_Volume_Descriptor Source
     */

    class vol_desc_primary_t : public kaitai::kstruct {

    public:

        vol_desc_primary_t(kaitai::kstream* p__io, iso9660_t::vol_desc_t* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~vol_desc_primary_t();

    private:
        bool f_path_table;
        path_table_le_t* m_path_table;

    public:
        path_table_le_t* path_table();

    private:
        std::string m_unused1;
        std::string m_system_id;
        std::string m_volume_id;
        std::string m_unused2;
        u4bi_t* m_vol_space_size;
        std::string m_unused3;
        u2bi_t* m_vol_set_size;
        u2bi_t* m_vol_seq_num;
        u2bi_t* m_logical_block_size;
        u4bi_t* m_path_table_size;
        uint32_t m_lba_path_table_le;
        uint32_t m_lba_opt_path_table_le;
        uint32_t m_lba_path_table_be;
        uint32_t m_lba_opt_path_table_be;
        dir_entry_t* m_root_dir;
        std::string m_vol_set_id;
        std::string m_publisher_id;
        std::string m_data_preparer_id;
        std::string m_application_id;
        std::string m_copyright_file_id;
        std::string m_abstract_file_id;
        std::string m_bibliographic_file_id;
        dec_datetime_t* m_vol_create_datetime;
        dec_datetime_t* m_vol_mod_datetime;
        dec_datetime_t* m_vol_expire_datetime;
        dec_datetime_t* m_vol_effective_datetime;
        uint8_t m_file_structure_version;
        uint8_t m_unused4;
        std::string m_application_area;
        iso9660_t* m__root;
        iso9660_t::vol_desc_t* m__parent;
        std::string m__raw_root_dir;
        kaitai::kstream* m__io__raw_root_dir;
        std::string m__raw_path_table;
        kaitai::kstream* m__io__raw_path_table;

    public:
        std::string unused1() const { return m_unused1; }
        std::string system_id() const { return m_system_id; }
        std::string volume_id() const { return m_volume_id; }
        std::string unused2() const { return m_unused2; }
        u4bi_t* vol_space_size() const { return m_vol_space_size; }
        std::string unused3() const { return m_unused3; }
        u2bi_t* vol_set_size() const { return m_vol_set_size; }
        u2bi_t* vol_seq_num() const { return m_vol_seq_num; }
        u2bi_t* logical_block_size() const { return m_logical_block_size; }
        u4bi_t* path_table_size() const { return m_path_table_size; }
        uint32_t lba_path_table_le() const { return m_lba_path_table_le; }
        uint32_t lba_opt_path_table_le() const { return m_lba_opt_path_table_le; }
        uint32_t lba_path_table_be() const { return m_lba_path_table_be; }
        uint32_t lba_opt_path_table_be() const { return m_lba_opt_path_table_be; }
        dir_entry_t* root_dir() const { return m_root_dir; }
        std::string vol_set_id() const { return m_vol_set_id; }
        std::string publisher_id() const { return m_publisher_id; }
        std::string data_preparer_id() const { return m_data_preparer_id; }
        std::string application_id() const { return m_application_id; }
        std::string copyright_file_id() const { return m_copyright_file_id; }
        std::string abstract_file_id() const { return m_abstract_file_id; }
        std::string bibliographic_file_id() const { return m_bibliographic_file_id; }
        dec_datetime_t* vol_create_datetime() const { return m_vol_create_datetime; }
        dec_datetime_t* vol_mod_datetime() const { return m_vol_mod_datetime; }
        dec_datetime_t* vol_expire_datetime() const { return m_vol_expire_datetime; }
        dec_datetime_t* vol_effective_datetime() const { return m_vol_effective_datetime; }
        uint8_t file_structure_version() const { return m_file_structure_version; }
        uint8_t unused4() const { return m_unused4; }
        std::string application_area() const { return m_application_area; }
        iso9660_t* _root() const { return m__root; }
        iso9660_t::vol_desc_t* _parent() const { return m__parent; }
        std::string _raw_root_dir() const { return m__raw_root_dir; }
        kaitai::kstream* _io__raw_root_dir() const { return m__io__raw_root_dir; }
        std::string _raw_path_table() const { return m__raw_path_table; }
        kaitai::kstream* _io__raw_path_table() const { return m__io__raw_path_table; }
    };

    class vol_desc_boot_record_t : public kaitai::kstruct {

    public:

        vol_desc_boot_record_t(kaitai::kstream* p__io, iso9660_t::vol_desc_t* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~vol_desc_boot_record_t();

    private:
        std::string m_boot_system_id;
        std::string m_boot_id;
        iso9660_t* m__root;
        iso9660_t::vol_desc_t* m__parent;

    public:
        std::string boot_system_id() const { return m_boot_system_id; }
        std::string boot_id() const { return m_boot_id; }
        iso9660_t* _root() const { return m__root; }
        iso9660_t::vol_desc_t* _parent() const { return m__parent; }
    };

    class datetime_t : public kaitai::kstruct {

    public:

        datetime_t(kaitai::kstream* p__io, iso9660_t::dir_entry_body_t* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~datetime_t();

    private:
        uint8_t m_year;
        uint8_t m_month;
        uint8_t m_day;
        uint8_t m_hour;
        uint8_t m_minute;
        uint8_t m_sec;
        uint8_t m_timezone;
        iso9660_t* m__root;
        iso9660_t::dir_entry_body_t* m__parent;

    public:
        uint8_t year() const { return m_year; }
        uint8_t month() const { return m_month; }
        uint8_t day() const { return m_day; }
        uint8_t hour() const { return m_hour; }
        uint8_t minute() const { return m_minute; }
        uint8_t sec() const { return m_sec; }
        uint8_t timezone() const { return m_timezone; }
        iso9660_t* _root() const { return m__root; }
        iso9660_t::dir_entry_body_t* _parent() const { return m__parent; }
    };

    class dir_entry_t : public kaitai::kstruct {

    public:

        dir_entry_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~dir_entry_t();

    private:
        uint8_t m_len;
        dir_entry_body_t* m_body;
        bool n_body;

    public:
        bool _is_null_body() { body(); return n_body; };

    private:
        iso9660_t* m__root;
        kaitai::kstruct* m__parent;
        std::string m__raw_body;
        bool n__raw_body;

    public:
        bool _is_null__raw_body() { _raw_body(); return n__raw_body; };

    private:
        kaitai::kstream* m__io__raw_body;

    public:
        uint8_t len() const { return m_len; }
        dir_entry_body_t* body() const { return m_body; }
        iso9660_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
        std::string _raw_body() const { return m__raw_body; }
        kaitai::kstream* _io__raw_body() const { return m__io__raw_body; }
    };

    class vol_desc_t : public kaitai::kstruct {

    public:

        vol_desc_t(kaitai::kstream* p__io, iso9660_t* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~vol_desc_t();

    private:
        uint8_t m_type;
        std::string m_magic;
        uint8_t m_version;
        vol_desc_boot_record_t* m_vol_desc_boot_record;
        bool n_vol_desc_boot_record;

    public:
        bool _is_null_vol_desc_boot_record() { vol_desc_boot_record(); return n_vol_desc_boot_record; };

    private:
        vol_desc_primary_t* m_vol_desc_primary;
        bool n_vol_desc_primary;

    public:
        bool _is_null_vol_desc_primary() { vol_desc_primary(); return n_vol_desc_primary; };

    private:
        iso9660_t* m__root;
        iso9660_t* m__parent;

    public:
        uint8_t type() const { return m_type; }
        std::string magic() const { return m_magic; }
        uint8_t version() const { return m_version; }
        vol_desc_boot_record_t* vol_desc_boot_record() const { return m_vol_desc_boot_record; }
        vol_desc_primary_t* vol_desc_primary() const { return m_vol_desc_primary; }
        iso9660_t* _root() const { return m__root; }
        iso9660_t* _parent() const { return m__parent; }
    };

    class path_table_entry_le_t : public kaitai::kstruct {

    public:

        path_table_entry_le_t(kaitai::kstream* p__io, iso9660_t::path_table_le_t* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~path_table_entry_le_t();

    private:
        uint8_t m_len_dir_name;
        uint8_t m_len_ext_attr_rec;
        uint32_t m_lba_extent;
        uint16_t m_parent_dir_idx;
        std::string m_dir_name;
        uint8_t m_padding;
        bool n_padding;

    public:
        bool _is_null_padding() { padding(); return n_padding; };

    private:
        iso9660_t* m__root;
        iso9660_t::path_table_le_t* m__parent;

    public:
        uint8_t len_dir_name() const { return m_len_dir_name; }
        uint8_t len_ext_attr_rec() const { return m_len_ext_attr_rec; }
        uint32_t lba_extent() const { return m_lba_extent; }
        uint16_t parent_dir_idx() const { return m_parent_dir_idx; }
        std::string dir_name() const { return m_dir_name; }
        uint8_t padding() const { return m_padding; }
        iso9660_t* _root() const { return m__root; }
        iso9660_t::path_table_le_t* _parent() const { return m__parent; }
    };

    class dir_entries_t : public kaitai::kstruct {

    public:

        dir_entries_t(kaitai::kstream* p__io, iso9660_t::dir_entry_body_t* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~dir_entries_t();

    private:
        std::vector<dir_entry_t*>* m_entries;
        iso9660_t* m__root;
        iso9660_t::dir_entry_body_t* m__parent;

    public:
        std::vector<dir_entry_t*>* entries() const { return m_entries; }
        iso9660_t* _root() const { return m__root; }
        iso9660_t::dir_entry_body_t* _parent() const { return m__parent; }
    };

    class u4bi_t : public kaitai::kstruct {

    public:

        u4bi_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~u4bi_t();

    private:
        uint32_t m_le;
        uint32_t m_be;
        iso9660_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        uint32_t le() const { return m_le; }
        uint32_t be() const { return m_be; }
        iso9660_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    class u2bi_t : public kaitai::kstruct {

    public:

        u2bi_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~u2bi_t();

    private:
        uint16_t m_le;
        uint16_t m_be;
        iso9660_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        uint16_t le() const { return m_le; }
        uint16_t be() const { return m_be; }
        iso9660_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    /**
     * \sa https://wiki.osdev.org/ISO_9660#The_Path_Table Source
     */

    class path_table_le_t : public kaitai::kstruct {

    public:

        path_table_le_t(kaitai::kstream* p__io, iso9660_t::vol_desc_primary_t* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~path_table_le_t();

    private:
        std::vector<path_table_entry_le_t*>* m_entries;
        iso9660_t* m__root;
        iso9660_t::vol_desc_primary_t* m__parent;

    public:
        std::vector<path_table_entry_le_t*>* entries() const { return m_entries; }
        iso9660_t* _root() const { return m__root; }
        iso9660_t::vol_desc_primary_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://wiki.osdev.org/ISO_9660#Date.2Ftime_format Source
     */

    class dec_datetime_t : public kaitai::kstruct {

    public:

        dec_datetime_t(kaitai::kstream* p__io, iso9660_t::vol_desc_primary_t* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~dec_datetime_t();

    private:
        std::string m_year;
        std::string m_month;
        std::string m_day;
        std::string m_hour;
        std::string m_minute;
        std::string m_sec;
        std::string m_sec_hundreds;
        uint8_t m_timezone;
        iso9660_t* m__root;
        iso9660_t::vol_desc_primary_t* m__parent;

    public:
        std::string year() const { return m_year; }
        std::string month() const { return m_month; }
        std::string day() const { return m_day; }
        std::string hour() const { return m_hour; }
        std::string minute() const { return m_minute; }
        std::string sec() const { return m_sec; }
        std::string sec_hundreds() const { return m_sec_hundreds; }
        uint8_t timezone() const { return m_timezone; }
        iso9660_t* _root() const { return m__root; }
        iso9660_t::vol_desc_primary_t* _parent() const { return m__parent; }
    };

    class dir_entry_body_t : public kaitai::kstruct {

    public:

        dir_entry_body_t(kaitai::kstream* p__io, iso9660_t::dir_entry_t* p__parent = 0, iso9660_t* p__root = 0);

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

    public:
        ~dir_entry_body_t();

    private:
        bool f_extent_as_dir;
        dir_entries_t* m_extent_as_dir;
        bool n_extent_as_dir;

    public:
        bool _is_null_extent_as_dir() { extent_as_dir(); return n_extent_as_dir; };

    private:

    public:
        dir_entries_t* extent_as_dir();

    private:
        bool f_extent_as_file;
        std::string m_extent_as_file;
        bool n_extent_as_file;

    public:
        bool _is_null_extent_as_file() { extent_as_file(); return n_extent_as_file; };

    private:

    public:
        std::string extent_as_file();

    private:
        uint8_t m_len_ext_attr_rec;
        u4bi_t* m_lba_extent;
        u4bi_t* m_size_extent;
        datetime_t* m_datetime;
        uint8_t m_file_flags;
        uint8_t m_file_unit_size;
        uint8_t m_interleave_gap_size;
        u2bi_t* m_vol_seq_num;
        uint8_t m_len_file_name;
        std::string m_file_name;
        uint8_t m_padding;
        bool n_padding;

    public:
        bool _is_null_padding() { padding(); return n_padding; };

    private:
        std::string m_rest;
        iso9660_t* m__root;
        iso9660_t::dir_entry_t* m__parent;
        std::string m__raw_extent_as_dir;
        bool n__raw_extent_as_dir;

    public:
        bool _is_null__raw_extent_as_dir() { _raw_extent_as_dir(); return n__raw_extent_as_dir; };

    private:
        kaitai::kstream* m__io__raw_extent_as_dir;

    public:
        uint8_t len_ext_attr_rec() const { return m_len_ext_attr_rec; }
        u4bi_t* lba_extent() const { return m_lba_extent; }
        u4bi_t* size_extent() const { return m_size_extent; }
        datetime_t* datetime() const { return m_datetime; }
        uint8_t file_flags() const { return m_file_flags; }
        uint8_t file_unit_size() const { return m_file_unit_size; }
        uint8_t interleave_gap_size() const { return m_interleave_gap_size; }
        u2bi_t* vol_seq_num() const { return m_vol_seq_num; }
        uint8_t len_file_name() const { return m_len_file_name; }
        std::string file_name() const { return m_file_name; }
        uint8_t padding() const { return m_padding; }
        std::string rest() const { return m_rest; }
        iso9660_t* _root() const { return m__root; }
        iso9660_t::dir_entry_t* _parent() const { return m__parent; }
        std::string _raw_extent_as_dir() const { return m__raw_extent_as_dir; }
        kaitai::kstream* _io__raw_extent_as_dir() const { return m__io__raw_extent_as_dir; }
    };

private:
    bool f_sector_size;
    int32_t m_sector_size;

public:
    int32_t sector_size();

private:
    bool f_primary_vol_desc;
    vol_desc_t* m_primary_vol_desc;

public:
    vol_desc_t* primary_vol_desc();

private:
    iso9660_t* m__root;
    kaitai::kstruct* m__parent;

public:
    iso9660_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

#endif  // ISO9660_H_

iso9660.cpp

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

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

iso9660_t::iso9660_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_primary_vol_desc = 0;
    f_sector_size = false;
    f_primary_vol_desc = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::_read() {
}

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

void iso9660_t::_clean_up() {
    if (f_primary_vol_desc) {
        if (m_primary_vol_desc) {
            delete m_primary_vol_desc; m_primary_vol_desc = 0;
        }
    }
}

iso9660_t::vol_desc_primary_t::vol_desc_primary_t(kaitai::kstream* p__io, iso9660_t::vol_desc_t* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_vol_space_size = 0;
    m_vol_set_size = 0;
    m_vol_seq_num = 0;
    m_logical_block_size = 0;
    m_path_table_size = 0;
    m_root_dir = 0;
    m__io__raw_root_dir = 0;
    m_vol_create_datetime = 0;
    m_vol_mod_datetime = 0;
    m_vol_expire_datetime = 0;
    m_vol_effective_datetime = 0;
    m_path_table = 0;
    m__io__raw_path_table = 0;
    f_path_table = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::vol_desc_primary_t::_read() {
    m_unused1 = m__io->read_bytes(1);
    if (!(unused1() == std::string("\x00", 1))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x00", 1), unused1(), _io(), std::string("/types/vol_desc_primary/seq/0"));
    }
    m_system_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(32), std::string("UTF-8"));
    m_volume_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(32), std::string("UTF-8"));
    m_unused2 = m__io->read_bytes(8);
    if (!(unused2() == std::string("\x00\x00\x00\x00\x00\x00\x00\x00", 8))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x00\x00\x00\x00\x00\x00\x00\x00", 8), unused2(), _io(), std::string("/types/vol_desc_primary/seq/3"));
    }
    m_vol_space_size = new u4bi_t(m__io, this, m__root);
    m_unused3 = m__io->read_bytes(32);
    if (!(unused3() == std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32), unused3(), _io(), std::string("/types/vol_desc_primary/seq/5"));
    }
    m_vol_set_size = new u2bi_t(m__io, this, m__root);
    m_vol_seq_num = new u2bi_t(m__io, this, m__root);
    m_logical_block_size = new u2bi_t(m__io, this, m__root);
    m_path_table_size = new u4bi_t(m__io, this, m__root);
    m_lba_path_table_le = m__io->read_u4le();
    m_lba_opt_path_table_le = m__io->read_u4le();
    m_lba_path_table_be = m__io->read_u4be();
    m_lba_opt_path_table_be = m__io->read_u4be();
    m__raw_root_dir = m__io->read_bytes(34);
    m__io__raw_root_dir = new kaitai::kstream(m__raw_root_dir);
    m_root_dir = new dir_entry_t(m__io__raw_root_dir, this, m__root);
    m_vol_set_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(128), std::string("UTF-8"));
    m_publisher_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(128), std::string("UTF-8"));
    m_data_preparer_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(128), std::string("UTF-8"));
    m_application_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(128), std::string("UTF-8"));
    m_copyright_file_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(38), std::string("UTF-8"));
    m_abstract_file_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(36), std::string("UTF-8"));
    m_bibliographic_file_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(37), std::string("UTF-8"));
    m_vol_create_datetime = new dec_datetime_t(m__io, this, m__root);
    m_vol_mod_datetime = new dec_datetime_t(m__io, this, m__root);
    m_vol_expire_datetime = new dec_datetime_t(m__io, this, m__root);
    m_vol_effective_datetime = new dec_datetime_t(m__io, this, m__root);
    m_file_structure_version = m__io->read_u1();
    m_unused4 = m__io->read_u1();
    m_application_area = m__io->read_bytes(512);
}

iso9660_t::vol_desc_primary_t::~vol_desc_primary_t() {
    _clean_up();
}

void iso9660_t::vol_desc_primary_t::_clean_up() {
    if (m_vol_space_size) {
        delete m_vol_space_size; m_vol_space_size = 0;
    }
    if (m_vol_set_size) {
        delete m_vol_set_size; m_vol_set_size = 0;
    }
    if (m_vol_seq_num) {
        delete m_vol_seq_num; m_vol_seq_num = 0;
    }
    if (m_logical_block_size) {
        delete m_logical_block_size; m_logical_block_size = 0;
    }
    if (m_path_table_size) {
        delete m_path_table_size; m_path_table_size = 0;
    }
    if (m__io__raw_root_dir) {
        delete m__io__raw_root_dir; m__io__raw_root_dir = 0;
    }
    if (m_root_dir) {
        delete m_root_dir; m_root_dir = 0;
    }
    if (m_vol_create_datetime) {
        delete m_vol_create_datetime; m_vol_create_datetime = 0;
    }
    if (m_vol_mod_datetime) {
        delete m_vol_mod_datetime; m_vol_mod_datetime = 0;
    }
    if (m_vol_expire_datetime) {
        delete m_vol_expire_datetime; m_vol_expire_datetime = 0;
    }
    if (m_vol_effective_datetime) {
        delete m_vol_effective_datetime; m_vol_effective_datetime = 0;
    }
    if (f_path_table) {
        if (m__io__raw_path_table) {
            delete m__io__raw_path_table; m__io__raw_path_table = 0;
        }
        if (m_path_table) {
            delete m_path_table; m_path_table = 0;
        }
    }
}

iso9660_t::path_table_le_t* iso9660_t::vol_desc_primary_t::path_table() {
    if (f_path_table)
        return m_path_table;
    std::streampos _pos = m__io->pos();
    m__io->seek((lba_path_table_le() * _root()->sector_size()));
    m__raw_path_table = m__io->read_bytes(path_table_size()->le());
    m__io__raw_path_table = new kaitai::kstream(m__raw_path_table);
    m_path_table = new path_table_le_t(m__io__raw_path_table, this, m__root);
    m__io->seek(_pos);
    f_path_table = true;
    return m_path_table;
}

iso9660_t::vol_desc_boot_record_t::vol_desc_boot_record_t(kaitai::kstream* p__io, iso9660_t::vol_desc_t* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::vol_desc_boot_record_t::_read() {
    m_boot_system_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(32), std::string("UTF-8"));
    m_boot_id = kaitai::kstream::bytes_to_str(m__io->read_bytes(32), std::string("UTF-8"));
}

iso9660_t::vol_desc_boot_record_t::~vol_desc_boot_record_t() {
    _clean_up();
}

void iso9660_t::vol_desc_boot_record_t::_clean_up() {
}

iso9660_t::datetime_t::datetime_t(kaitai::kstream* p__io, iso9660_t::dir_entry_body_t* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::datetime_t::_read() {
    m_year = m__io->read_u1();
    m_month = m__io->read_u1();
    m_day = m__io->read_u1();
    m_hour = m__io->read_u1();
    m_minute = m__io->read_u1();
    m_sec = m__io->read_u1();
    m_timezone = m__io->read_u1();
}

iso9660_t::datetime_t::~datetime_t() {
    _clean_up();
}

void iso9660_t::datetime_t::_clean_up() {
}

iso9660_t::dir_entry_t::dir_entry_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_body = 0;
    m__io__raw_body = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::dir_entry_t::_read() {
    m_len = m__io->read_u1();
    n_body = true;
    if (len() > 0) {
        n_body = false;
        m__raw_body = m__io->read_bytes((len() - 1));
        m__io__raw_body = new kaitai::kstream(m__raw_body);
        m_body = new dir_entry_body_t(m__io__raw_body, this, m__root);
    }
}

iso9660_t::dir_entry_t::~dir_entry_t() {
    _clean_up();
}

void iso9660_t::dir_entry_t::_clean_up() {
    if (!n_body) {
        if (m__io__raw_body) {
            delete m__io__raw_body; m__io__raw_body = 0;
        }
        if (m_body) {
            delete m_body; m_body = 0;
        }
    }
}

iso9660_t::vol_desc_t::vol_desc_t(kaitai::kstream* p__io, iso9660_t* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_vol_desc_boot_record = 0;
    m_vol_desc_primary = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::vol_desc_t::_read() {
    m_type = m__io->read_u1();
    m_magic = m__io->read_bytes(5);
    if (!(magic() == std::string("\x43\x44\x30\x30\x31", 5))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x43\x44\x30\x30\x31", 5), magic(), _io(), std::string("/types/vol_desc/seq/1"));
    }
    m_version = m__io->read_u1();
    n_vol_desc_boot_record = true;
    if (type() == 0) {
        n_vol_desc_boot_record = false;
        m_vol_desc_boot_record = new vol_desc_boot_record_t(m__io, this, m__root);
    }
    n_vol_desc_primary = true;
    if (type() == 1) {
        n_vol_desc_primary = false;
        m_vol_desc_primary = new vol_desc_primary_t(m__io, this, m__root);
    }
}

iso9660_t::vol_desc_t::~vol_desc_t() {
    _clean_up();
}

void iso9660_t::vol_desc_t::_clean_up() {
    if (!n_vol_desc_boot_record) {
        if (m_vol_desc_boot_record) {
            delete m_vol_desc_boot_record; m_vol_desc_boot_record = 0;
        }
    }
    if (!n_vol_desc_primary) {
        if (m_vol_desc_primary) {
            delete m_vol_desc_primary; m_vol_desc_primary = 0;
        }
    }
}

iso9660_t::path_table_entry_le_t::path_table_entry_le_t(kaitai::kstream* p__io, iso9660_t::path_table_le_t* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::path_table_entry_le_t::_read() {
    m_len_dir_name = m__io->read_u1();
    m_len_ext_attr_rec = m__io->read_u1();
    m_lba_extent = m__io->read_u4le();
    m_parent_dir_idx = m__io->read_u2le();
    m_dir_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(len_dir_name()), std::string("UTF-8"));
    n_padding = true;
    if (kaitai::kstream::mod(len_dir_name(), 2) == 1) {
        n_padding = false;
        m_padding = m__io->read_u1();
    }
}

iso9660_t::path_table_entry_le_t::~path_table_entry_le_t() {
    _clean_up();
}

void iso9660_t::path_table_entry_le_t::_clean_up() {
    if (!n_padding) {
    }
}

iso9660_t::dir_entries_t::dir_entries_t(kaitai::kstream* p__io, iso9660_t::dir_entry_body_t* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_entries = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::dir_entries_t::_read() {
    m_entries = new std::vector<dir_entry_t*>();
    {
        int i = 0;
        dir_entry_t* _;
        do {
            _ = new dir_entry_t(m__io, this, m__root);
            m_entries->push_back(_);
            i++;
        } while (!(_->len() == 0));
    }
}

iso9660_t::dir_entries_t::~dir_entries_t() {
    _clean_up();
}

void iso9660_t::dir_entries_t::_clean_up() {
    if (m_entries) {
        for (std::vector<dir_entry_t*>::iterator it = m_entries->begin(); it != m_entries->end(); ++it) {
            delete *it;
        }
        delete m_entries; m_entries = 0;
    }
}

iso9660_t::u4bi_t::u4bi_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::u4bi_t::_read() {
    m_le = m__io->read_u4le();
    m_be = m__io->read_u4be();
}

iso9660_t::u4bi_t::~u4bi_t() {
    _clean_up();
}

void iso9660_t::u4bi_t::_clean_up() {
}

iso9660_t::u2bi_t::u2bi_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::u2bi_t::_read() {
    m_le = m__io->read_u2le();
    m_be = m__io->read_u2be();
}

iso9660_t::u2bi_t::~u2bi_t() {
    _clean_up();
}

void iso9660_t::u2bi_t::_clean_up() {
}

iso9660_t::path_table_le_t::path_table_le_t(kaitai::kstream* p__io, iso9660_t::vol_desc_primary_t* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_entries = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::path_table_le_t::_read() {
    m_entries = new std::vector<path_table_entry_le_t*>();
    {
        int i = 0;
        while (!m__io->is_eof()) {
            m_entries->push_back(new path_table_entry_le_t(m__io, this, m__root));
            i++;
        }
    }
}

iso9660_t::path_table_le_t::~path_table_le_t() {
    _clean_up();
}

void iso9660_t::path_table_le_t::_clean_up() {
    if (m_entries) {
        for (std::vector<path_table_entry_le_t*>::iterator it = m_entries->begin(); it != m_entries->end(); ++it) {
            delete *it;
        }
        delete m_entries; m_entries = 0;
    }
}

iso9660_t::dec_datetime_t::dec_datetime_t(kaitai::kstream* p__io, iso9660_t::vol_desc_primary_t* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::dec_datetime_t::_read() {
    m_year = kaitai::kstream::bytes_to_str(m__io->read_bytes(4), std::string("ASCII"));
    m_month = kaitai::kstream::bytes_to_str(m__io->read_bytes(2), std::string("ASCII"));
    m_day = kaitai::kstream::bytes_to_str(m__io->read_bytes(2), std::string("ASCII"));
    m_hour = kaitai::kstream::bytes_to_str(m__io->read_bytes(2), std::string("ASCII"));
    m_minute = kaitai::kstream::bytes_to_str(m__io->read_bytes(2), std::string("ASCII"));
    m_sec = kaitai::kstream::bytes_to_str(m__io->read_bytes(2), std::string("ASCII"));
    m_sec_hundreds = kaitai::kstream::bytes_to_str(m__io->read_bytes(2), std::string("ASCII"));
    m_timezone = m__io->read_u1();
}

iso9660_t::dec_datetime_t::~dec_datetime_t() {
    _clean_up();
}

void iso9660_t::dec_datetime_t::_clean_up() {
}

iso9660_t::dir_entry_body_t::dir_entry_body_t(kaitai::kstream* p__io, iso9660_t::dir_entry_t* p__parent, iso9660_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_lba_extent = 0;
    m_size_extent = 0;
    m_datetime = 0;
    m_vol_seq_num = 0;
    m_extent_as_dir = 0;
    m__io__raw_extent_as_dir = 0;
    f_extent_as_dir = false;
    f_extent_as_file = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void iso9660_t::dir_entry_body_t::_read() {
    m_len_ext_attr_rec = m__io->read_u1();
    m_lba_extent = new u4bi_t(m__io, this, m__root);
    m_size_extent = new u4bi_t(m__io, this, m__root);
    m_datetime = new datetime_t(m__io, this, m__root);
    m_file_flags = m__io->read_u1();
    m_file_unit_size = m__io->read_u1();
    m_interleave_gap_size = m__io->read_u1();
    m_vol_seq_num = new u2bi_t(m__io, this, m__root);
    m_len_file_name = m__io->read_u1();
    m_file_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(len_file_name()), std::string("UTF-8"));
    n_padding = true;
    if (kaitai::kstream::mod(len_file_name(), 2) == 0) {
        n_padding = false;
        m_padding = m__io->read_u1();
    }
    m_rest = m__io->read_bytes_full();
}

iso9660_t::dir_entry_body_t::~dir_entry_body_t() {
    _clean_up();
}

void iso9660_t::dir_entry_body_t::_clean_up() {
    if (m_lba_extent) {
        delete m_lba_extent; m_lba_extent = 0;
    }
    if (m_size_extent) {
        delete m_size_extent; m_size_extent = 0;
    }
    if (m_datetime) {
        delete m_datetime; m_datetime = 0;
    }
    if (m_vol_seq_num) {
        delete m_vol_seq_num; m_vol_seq_num = 0;
    }
    if (!n_padding) {
    }
    if (f_extent_as_dir && !n_extent_as_dir) {
        if (m__io__raw_extent_as_dir) {
            delete m__io__raw_extent_as_dir; m__io__raw_extent_as_dir = 0;
        }
        if (m_extent_as_dir) {
            delete m_extent_as_dir; m_extent_as_dir = 0;
        }
    }
    if (f_extent_as_file && !n_extent_as_file) {
    }
}

iso9660_t::dir_entries_t* iso9660_t::dir_entry_body_t::extent_as_dir() {
    if (f_extent_as_dir)
        return m_extent_as_dir;
    n_extent_as_dir = true;
    if ((file_flags() & 2) != 0) {
        n_extent_as_dir = false;
        kaitai::kstream *io = _root()->_io();
        std::streampos _pos = io->pos();
        io->seek((lba_extent()->le() * _root()->sector_size()));
        m__raw_extent_as_dir = io->read_bytes(size_extent()->le());
        m__io__raw_extent_as_dir = new kaitai::kstream(m__raw_extent_as_dir);
        m_extent_as_dir = new dir_entries_t(m__io__raw_extent_as_dir, this, m__root);
        io->seek(_pos);
        f_extent_as_dir = true;
    }
    return m_extent_as_dir;
}

std::string iso9660_t::dir_entry_body_t::extent_as_file() {
    if (f_extent_as_file)
        return m_extent_as_file;
    n_extent_as_file = true;
    if ((file_flags() & 2) == 0) {
        n_extent_as_file = false;
        kaitai::kstream *io = _root()->_io();
        std::streampos _pos = io->pos();
        io->seek((lba_extent()->le() * _root()->sector_size()));
        m_extent_as_file = io->read_bytes(size_extent()->le());
        io->seek(_pos);
        f_extent_as_file = true;
    }
    return m_extent_as_file;
}

int32_t iso9660_t::sector_size() {
    if (f_sector_size)
        return m_sector_size;
    m_sector_size = 2048;
    f_sector_size = true;
    return m_sector_size;
}

iso9660_t::vol_desc_t* iso9660_t::primary_vol_desc() {
    if (f_primary_vol_desc)
        return m_primary_vol_desc;
    std::streampos _pos = m__io->pos();
    m__io->seek((16 * sector_size()));
    m_primary_vol_desc = new vol_desc_t(m__io, this, m__root);
    m__io->seek(_pos);
    f_primary_vol_desc = true;
    return m_primary_vol_desc;
}