gettext binary database: C++98/STL parsing library

GNU gettext is a popular solution in free/open source software world to do i18n/l10n of software, by providing translated strings that will substitute strings in original language (typically, English).

gettext .mo is a binary database format which stores these string translation pairs in an efficient binary format, ready to be used by gettext-enabled software. .mo format is a result of compilation of text-based .po files using msgfmt utility. The reverse conversion (.mo -> .po) is also possible using msgunfmt decompiler utility.

Application

["GNU gettext", "libintl"]

File extension

mo

KS implementation details

License: BSD-2-Clause
Minimal Kaitai Struct required: 0.9

References

This page hosts a formal specification of gettext binary database 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.mo", 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:
    gettext_mo_t data(&ks);
    

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

data.signature() // => get signature

C++98/STL source code to parse gettext binary database

gettext_mo.h

#ifndef GETTEXT_MO_H_
#define GETTEXT_MO_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

/**
 * [GNU gettext](https://www.gnu.org/software/gettext/) is a popular
 * solution in free/open source software world to do i18n/l10n of
 * software, by providing translated strings that will substitute
 * strings in original language (typically, English).
 * 
 * gettext .mo is a binary database format which stores these string
 * translation pairs in an efficient binary format, ready to be used by
 * gettext-enabled software. .mo format is a result of compilation of
 * text-based .po files using
 * [msgfmt](https://www.gnu.org/software/gettext/manual/html_node/msgfmt-Invocation.html#msgfmt-Invocation)
 * utility. The reverse conversion (.mo -> .po) is also possible using
 * [msgunfmt](https://www.gnu.org/software/gettext/manual/html_node/msgunfmt-Invocation.html#msgunfmt-Invocation)
 * decompiler utility.
 * \sa https://gitlab.com/worr/libintl Source
 */

class gettext_mo_t : public kaitai::kstruct {

public:
    class hash_lookup_iteration_t;
    class lookup_iteration_t;
    class hashtable_lookup_t;
    class mo_t;

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

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

public:
    ~gettext_mo_t();

    class hash_lookup_iteration_t : public kaitai::kstruct {

    public:

        hash_lookup_iteration_t(uint32_t p_idx, uint32_t p_collision_step, kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, gettext_mo_t* p__root = 0);

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

    public:
        ~hash_lookup_iteration_t();

    private:
        bool f_original;
        std::string m_original;

    public:
        std::string original();

    private:
        bool f_translation;
        std::string m_translation;

    public:
        std::string translation();

    private:
        bool f_next_idx;
        int32_t m_next_idx;

    public:
        int32_t next_idx();

    private:
        bool f_next;
        hash_lookup_iteration_t* m_next;

    public:
        hash_lookup_iteration_t* next();

    private:
        uint32_t m_idx;
        uint32_t m_collision_step;
        gettext_mo_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        uint32_t idx() const { return m_idx; }
        uint32_t collision_step() const { return m_collision_step; }
        gettext_mo_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    class lookup_iteration_t : public kaitai::kstruct {

    public:

        lookup_iteration_t(hash_lookup_iteration_t* p_current, std::string p_query, kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, gettext_mo_t* p__root = 0);

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

    public:
        ~lookup_iteration_t();

    private:
        bool f_found;
        bool m_found;

    public:
        bool found();

    private:
        bool f_next;
        lookup_iteration_t* m_next;
        bool n_next;

    public:
        bool _is_null_next() { next(); return n_next; };

    private:

    public:
        lookup_iteration_t* next();

    private:
        hash_lookup_iteration_t* m_current;
        std::string m_query;
        gettext_mo_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        hash_lookup_iteration_t* current() const { return m_current; }
        std::string query() const { return m_query; }
        gettext_mo_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    /**
     * def lookup(s:str, t:gettext_mo.GettextMo):
     *   try:
     *     l=gettext_mo.GettextMo.HashtableLookup(s, string_hash(s), t._io, _parent=t, _root=t)
     *     e=l.entry
     *     while(not e.found):
     *       e=e.next
     *     return e.current
     *   except:
     *     raise Exception("Not found "+s+" in the hashtable!")
     * 
     * lookup(t.mo.originals[145].str, t)
     * \sa https://gitlab.com/worr/libintl/raw/master/src/lib/libintl/gettext.c Source
     */

    class hashtable_lookup_t : public kaitai::kstruct {

    public:

        hashtable_lookup_t(std::string p_query, uint32_t p_hash, kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, gettext_mo_t* p__root = 0);

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

    public:
        ~hashtable_lookup_t();

    private:
        bool f_collision_step;
        int32_t m_collision_step;

    public:
        int32_t collision_step();

    private:
        bool f_idx;
        int32_t m_idx;

    public:
        int32_t idx();

    private:
        bool f_hash_lookup_iteration;
        hash_lookup_iteration_t* m_hash_lookup_iteration;

    public:
        hash_lookup_iteration_t* hash_lookup_iteration();

    private:
        bool f_entry;
        lookup_iteration_t* m_entry;

    public:
        lookup_iteration_t* entry();

    private:
        std::string m_query;
        uint32_t m_hash;
        gettext_mo_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        std::string query() const { return m_query; }

        /**
         * def string_hash(s):
         *   s=s.encode("utf-8")
         *   h = 0
         *   for i in range(len(s)):
         *     h = h << 4
         *     h += s[i]
         *     tmp = h & 0xF0000000
         *     if tmp != 0:
         *       h ^= tmp
         *       h ^= tmp >> 24
         *   return h
         * \sa https://gitlab.com/worr/libintl/raw/master/src/lib/libintl/strhash.c Source
         */
        uint32_t hash() const { return m_hash; }
        gettext_mo_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    class mo_t : public kaitai::kstruct {

    public:
        class version_t;
        class hashtable_item_t;
        class descriptor_t;

        mo_t(kaitai::kstream* p__io, gettext_mo_t* p__parent = 0, gettext_mo_t* p__root = 0);

    private:
        int m__is_le;

    public:

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

    public:
        ~mo_t();

        class version_t : public kaitai::kstruct {

        public:

            version_t(kaitai::kstream* p__io, gettext_mo_t::mo_t* p__parent = 0, gettext_mo_t* p__root = 0, int p_is_le = -1);

        private:
            int m__is_le;

        public:

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

        public:
            ~version_t();

        private:
            bool f_major;
            int32_t m_major;

        public:
            int32_t major();

        private:
            bool f_minor;
            int32_t m_minor;

        public:
            int32_t minor();

        private:
            uint32_t m_version_raw;
            gettext_mo_t* m__root;
            gettext_mo_t::mo_t* m__parent;

        public:
            uint32_t version_raw() const { return m_version_raw; }
            gettext_mo_t* _root() const { return m__root; }
            gettext_mo_t::mo_t* _parent() const { return m__parent; }
        };

        class hashtable_item_t : public kaitai::kstruct {

        public:

            hashtable_item_t(kaitai::kstream* p__io, gettext_mo_t::mo_t* p__parent = 0, gettext_mo_t* p__root = 0, int p_is_le = -1);

        private:
            int m__is_le;

        public:

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

        public:
            ~hashtable_item_t();

        private:
            bool f_mask;
            int32_t m_mask;

        public:
            int32_t mask();

        private:
            bool f_val_1;
            int32_t m_val_1;
            bool n_val_1;

        public:
            bool _is_null_val_1() { val_1(); return n_val_1; };

        private:

        public:
            int32_t val_1();

        private:
            bool f_is_system_dependent;
            bool m_is_system_dependent;
            bool n_is_system_dependent;

        public:
            bool _is_null_is_system_dependent() { is_system_dependent(); return n_is_system_dependent; };

        private:

        public:
            bool is_system_dependent();

        private:
            bool f_val;
            int32_t m_val;
            bool n_val;

        public:
            bool _is_null_val() { val(); return n_val; };

        private:

        public:
            int32_t val();

        private:
            uint32_t m_raw_val;
            gettext_mo_t* m__root;
            gettext_mo_t::mo_t* m__parent;

        public:
            uint32_t raw_val() const { return m_raw_val; }
            gettext_mo_t* _root() const { return m__root; }
            gettext_mo_t::mo_t* _parent() const { return m__parent; }
        };

        class descriptor_t : public kaitai::kstruct {

        public:

            descriptor_t(kaitai::kstream* p__io, gettext_mo_t::mo_t* p__parent = 0, gettext_mo_t* p__root = 0, int p_is_le = -1);

        private:
            int m__is_le;

        public:

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

        public:
            ~descriptor_t();

        private:
            bool f_str;
            std::string m_str;

        public:
            std::string str();

        private:
            uint32_t m_len_str;
            uint32_t m_ofs_str;
            gettext_mo_t* m__root;
            gettext_mo_t::mo_t* m__parent;

        public:
            uint32_t len_str() const { return m_len_str; }
            uint32_t ofs_str() const { return m_ofs_str; }
            gettext_mo_t* _root() const { return m__root; }
            gettext_mo_t::mo_t* _parent() const { return m__parent; }
        };

    private:
        bool f_originals;
        std::vector<descriptor_t*>* m_originals;

    public:
        std::vector<descriptor_t*>* originals();

    private:
        bool f_translations;
        std::vector<descriptor_t*>* m_translations;

    public:
        std::vector<descriptor_t*>* translations();

    private:
        bool f_hashtable_items;
        std::vector<hashtable_item_t*>* m_hashtable_items;
        bool n_hashtable_items;

    public:
        bool _is_null_hashtable_items() { hashtable_items(); return n_hashtable_items; };

    private:

    public:
        std::vector<hashtable_item_t*>* hashtable_items();

    private:
        version_t* m_version;
        uint32_t m_num_translations;
        uint32_t m_ofs_originals;
        uint32_t m_ofs_translations;
        uint32_t m_num_hashtable_items;
        uint32_t m_ofs_hashtable_items;
        gettext_mo_t* m__root;
        gettext_mo_t* m__parent;

    public:
        version_t* version() const { return m_version; }
        uint32_t num_translations() const { return m_num_translations; }
        uint32_t ofs_originals() const { return m_ofs_originals; }
        uint32_t ofs_translations() const { return m_ofs_translations; }
        uint32_t num_hashtable_items() const { return m_num_hashtable_items; }
        uint32_t ofs_hashtable_items() const { return m_ofs_hashtable_items; }
        gettext_mo_t* _root() const { return m__root; }
        gettext_mo_t* _parent() const { return m__parent; }
    };

private:
    std::string m_signature;
    mo_t* m_mo;
    gettext_mo_t* m__root;
    kaitai::kstruct* m__parent;

public:
    std::string signature() const { return m_signature; }
    mo_t* mo() const { return m_mo; }
    gettext_mo_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

#endif  // GETTEXT_MO_H_

gettext_mo.cpp

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

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

gettext_mo_t::gettext_mo_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, gettext_mo_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_mo = 0;

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

void gettext_mo_t::_read() {
    m_signature = m__io->read_bytes(4);
    m_mo = new mo_t(m__io, this, m__root);
}

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

void gettext_mo_t::_clean_up() {
    if (m_mo) {
        delete m_mo; m_mo = 0;
    }
}

gettext_mo_t::hash_lookup_iteration_t::hash_lookup_iteration_t(uint32_t p_idx, uint32_t p_collision_step, kaitai::kstream* p__io, kaitai::kstruct* p__parent, gettext_mo_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_idx = p_idx;
    m_collision_step = p_collision_step;
    m_next = 0;
    f_original = false;
    f_translation = false;
    f_next_idx = false;
    f_next = false;

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

void gettext_mo_t::hash_lookup_iteration_t::_read() {
}

gettext_mo_t::hash_lookup_iteration_t::~hash_lookup_iteration_t() {
    _clean_up();
}

void gettext_mo_t::hash_lookup_iteration_t::_clean_up() {
    if (f_next) {
        if (m_next) {
            delete m_next; m_next = 0;
        }
    }
}

std::string gettext_mo_t::hash_lookup_iteration_t::original() {
    if (f_original)
        return m_original;
    m_original = _root()->mo()->originals()->at(idx())->str();
    f_original = true;
    return m_original;
}

std::string gettext_mo_t::hash_lookup_iteration_t::translation() {
    if (f_translation)
        return m_translation;
    m_translation = _root()->mo()->translations()->at(idx())->str();
    f_translation = true;
    return m_translation;
}

int32_t gettext_mo_t::hash_lookup_iteration_t::next_idx() {
    if (f_next_idx)
        return m_next_idx;
    m_next_idx = ((idx() + collision_step()) - ((idx() >= (_root()->mo()->num_hashtable_items() - collision_step())) ? (_root()->mo()->num_hashtable_items()) : (0)));
    f_next_idx = true;
    return m_next_idx;
}

gettext_mo_t::hash_lookup_iteration_t* gettext_mo_t::hash_lookup_iteration_t::next() {
    if (f_next)
        return m_next;
    std::streampos _pos = m__io->pos();
    m__io->seek(0);
    m_next = new hash_lookup_iteration_t(_root()->mo()->hashtable_items()->at(next_idx())->val(), collision_step(), m__io, this, m__root);
    m__io->seek(_pos);
    f_next = true;
    return m_next;
}

gettext_mo_t::lookup_iteration_t::lookup_iteration_t(hash_lookup_iteration_t* p_current, std::string p_query, kaitai::kstream* p__io, kaitai::kstruct* p__parent, gettext_mo_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_current = p_current;
    m_query = p_query;
    m_next = 0;
    f_found = false;
    f_next = false;

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

void gettext_mo_t::lookup_iteration_t::_read() {
}

gettext_mo_t::lookup_iteration_t::~lookup_iteration_t() {
    _clean_up();
}

void gettext_mo_t::lookup_iteration_t::_clean_up() {
    if (f_next && !n_next) {
        if (m_next) {
            delete m_next; m_next = 0;
        }
    }
}

bool gettext_mo_t::lookup_iteration_t::found() {
    if (f_found)
        return m_found;
    m_found = query() == (current()->original());
    f_found = true;
    return m_found;
}

gettext_mo_t::lookup_iteration_t* gettext_mo_t::lookup_iteration_t::next() {
    if (f_next)
        return m_next;
    n_next = true;
    if (!(found())) {
        n_next = false;
        std::streampos _pos = m__io->pos();
        m__io->seek(0);
        m_next = new lookup_iteration_t(current()->next(), query(), m__io, this, m__root);
        m__io->seek(_pos);
        f_next = true;
    }
    return m_next;
}

gettext_mo_t::hashtable_lookup_t::hashtable_lookup_t(std::string p_query, uint32_t p_hash, kaitai::kstream* p__io, kaitai::kstruct* p__parent, gettext_mo_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_query = p_query;
    m_hash = p_hash;
    m_hash_lookup_iteration = 0;
    m_entry = 0;
    f_collision_step = false;
    f_idx = false;
    f_hash_lookup_iteration = false;
    f_entry = false;

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

void gettext_mo_t::hashtable_lookup_t::_read() {
}

gettext_mo_t::hashtable_lookup_t::~hashtable_lookup_t() {
    _clean_up();
}

void gettext_mo_t::hashtable_lookup_t::_clean_up() {
    if (f_hash_lookup_iteration) {
        if (m_hash_lookup_iteration) {
            delete m_hash_lookup_iteration; m_hash_lookup_iteration = 0;
        }
    }
    if (f_entry) {
        if (m_entry) {
            delete m_entry; m_entry = 0;
        }
    }
}

int32_t gettext_mo_t::hashtable_lookup_t::collision_step() {
    if (f_collision_step)
        return m_collision_step;
    m_collision_step = (kaitai::kstream::mod(hash(), (_root()->mo()->num_hashtable_items() - 2)) + 1);
    f_collision_step = true;
    return m_collision_step;
}

int32_t gettext_mo_t::hashtable_lookup_t::idx() {
    if (f_idx)
        return m_idx;
    m_idx = kaitai::kstream::mod(hash(), _root()->mo()->num_hashtable_items());
    f_idx = true;
    return m_idx;
}

gettext_mo_t::hash_lookup_iteration_t* gettext_mo_t::hashtable_lookup_t::hash_lookup_iteration() {
    if (f_hash_lookup_iteration)
        return m_hash_lookup_iteration;
    std::streampos _pos = m__io->pos();
    m__io->seek(0);
    m_hash_lookup_iteration = new hash_lookup_iteration_t(_root()->mo()->hashtable_items()->at(idx())->val(), collision_step(), m__io, this, m__root);
    m__io->seek(_pos);
    f_hash_lookup_iteration = true;
    return m_hash_lookup_iteration;
}

gettext_mo_t::lookup_iteration_t* gettext_mo_t::hashtable_lookup_t::entry() {
    if (f_entry)
        return m_entry;
    std::streampos _pos = m__io->pos();
    m__io->seek(0);
    m_entry = new lookup_iteration_t(hash_lookup_iteration(), query(), m__io, this, m__root);
    m__io->seek(_pos);
    f_entry = true;
    return m_entry;
}

gettext_mo_t::mo_t::mo_t(kaitai::kstream* p__io, gettext_mo_t* p__parent, gettext_mo_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m__is_le = -1;
    m_version = 0;
    m_originals = 0;
    m_translations = 0;
    m_hashtable_items = 0;
    f_originals = false;
    f_translations = false;
    f_hashtable_items = false;

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

void gettext_mo_t::mo_t::_read() {
    {
        std::string on = _root()->signature();
        if (on == std::string("\xDE\x12\x04\x95", 4)) {
            m__is_le = true;
        }
        else if (on == std::string("\x95\x04\x12\xDE", 4)) {
            m__is_le = false;
        }
    }

    if (m__is_le == -1) {
        throw kaitai::undecided_endianness_error("/types/mo");
    } else if (m__is_le == 1) {
        _read_le();
    } else {
        _read_be();
    }
}

void gettext_mo_t::mo_t::_read_le() {
    m_version = new version_t(m__io, this, m__root, m__is_le);
    m_num_translations = m__io->read_u4le();
    m_ofs_originals = m__io->read_u4le();
    m_ofs_translations = m__io->read_u4le();
    m_num_hashtable_items = m__io->read_u4le();
    m_ofs_hashtable_items = m__io->read_u4le();
}

void gettext_mo_t::mo_t::_read_be() {
    m_version = new version_t(m__io, this, m__root, m__is_le);
    m_num_translations = m__io->read_u4be();
    m_ofs_originals = m__io->read_u4be();
    m_ofs_translations = m__io->read_u4be();
    m_num_hashtable_items = m__io->read_u4be();
    m_ofs_hashtable_items = m__io->read_u4be();
}

gettext_mo_t::mo_t::~mo_t() {
    _clean_up();
}

void gettext_mo_t::mo_t::_clean_up() {
    if (m_version) {
        delete m_version; m_version = 0;
    }
    if (f_originals) {
        if (m_originals) {
            for (std::vector<descriptor_t*>::iterator it = m_originals->begin(); it != m_originals->end(); ++it) {
                delete *it;
            }
            delete m_originals; m_originals = 0;
        }
    }
    if (f_translations) {
        if (m_translations) {
            for (std::vector<descriptor_t*>::iterator it = m_translations->begin(); it != m_translations->end(); ++it) {
                delete *it;
            }
            delete m_translations; m_translations = 0;
        }
    }
    if (f_hashtable_items && !n_hashtable_items) {
        if (m_hashtable_items) {
            for (std::vector<hashtable_item_t*>::iterator it = m_hashtable_items->begin(); it != m_hashtable_items->end(); ++it) {
                delete *it;
            }
            delete m_hashtable_items; m_hashtable_items = 0;
        }
    }
}

gettext_mo_t::mo_t::version_t::version_t(kaitai::kstream* p__io, gettext_mo_t::mo_t* p__parent, gettext_mo_t* p__root, int p_is_le) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m__is_le = p_is_le;
    f_major = false;
    f_minor = false;

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

void gettext_mo_t::mo_t::version_t::_read() {

    if (m__is_le == -1) {
        throw kaitai::undecided_endianness_error("/types/mo/types/version");
    } else if (m__is_le == 1) {
        _read_le();
    } else {
        _read_be();
    }
}

void gettext_mo_t::mo_t::version_t::_read_le() {
    m_version_raw = m__io->read_u4le();
}

void gettext_mo_t::mo_t::version_t::_read_be() {
    m_version_raw = m__io->read_u4be();
}

gettext_mo_t::mo_t::version_t::~version_t() {
    _clean_up();
}

void gettext_mo_t::mo_t::version_t::_clean_up() {
}

int32_t gettext_mo_t::mo_t::version_t::major() {
    if (f_major)
        return m_major;
    m_major = (version_raw() >> 16);
    f_major = true;
    return m_major;
}

int32_t gettext_mo_t::mo_t::version_t::minor() {
    if (f_minor)
        return m_minor;
    m_minor = (version_raw() & 65535);
    f_minor = true;
    return m_minor;
}

gettext_mo_t::mo_t::hashtable_item_t::hashtable_item_t(kaitai::kstream* p__io, gettext_mo_t::mo_t* p__parent, gettext_mo_t* p__root, int p_is_le) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m__is_le = p_is_le;
    f_mask = false;
    f_val_1 = false;
    f_is_system_dependent = false;
    f_val = false;

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

void gettext_mo_t::mo_t::hashtable_item_t::_read() {

    if (m__is_le == -1) {
        throw kaitai::undecided_endianness_error("/types/mo/types/hashtable_item");
    } else if (m__is_le == 1) {
        _read_le();
    } else {
        _read_be();
    }
}

void gettext_mo_t::mo_t::hashtable_item_t::_read_le() {
    m_raw_val = m__io->read_u4le();
}

void gettext_mo_t::mo_t::hashtable_item_t::_read_be() {
    m_raw_val = m__io->read_u4be();
}

gettext_mo_t::mo_t::hashtable_item_t::~hashtable_item_t() {
    _clean_up();
}

void gettext_mo_t::mo_t::hashtable_item_t::_clean_up() {
}

int32_t gettext_mo_t::mo_t::hashtable_item_t::mask() {
    if (f_mask)
        return m_mask;
    m_mask = 2147483648UL;
    f_mask = true;
    return m_mask;
}

int32_t gettext_mo_t::mo_t::hashtable_item_t::val_1() {
    if (f_val_1)
        return m_val_1;
    n_val_1 = true;
    if (raw_val() != 0) {
        n_val_1 = false;
        m_val_1 = (raw_val() - 1);
    }
    f_val_1 = true;
    return m_val_1;
}

bool gettext_mo_t::mo_t::hashtable_item_t::is_system_dependent() {
    if (f_is_system_dependent)
        return m_is_system_dependent;
    n_is_system_dependent = true;
    if (raw_val() != 0) {
        n_is_system_dependent = false;
        m_is_system_dependent = (val_1() & mask()) == 1;
    }
    f_is_system_dependent = true;
    return m_is_system_dependent;
}

int32_t gettext_mo_t::mo_t::hashtable_item_t::val() {
    if (f_val)
        return m_val;
    n_val = true;
    if (raw_val() != 0) {
        n_val = false;
        m_val = (val_1() & ~(mask()));
    }
    f_val = true;
    return m_val;
}

gettext_mo_t::mo_t::descriptor_t::descriptor_t(kaitai::kstream* p__io, gettext_mo_t::mo_t* p__parent, gettext_mo_t* p__root, int p_is_le) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m__is_le = p_is_le;
    f_str = false;

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

void gettext_mo_t::mo_t::descriptor_t::_read() {

    if (m__is_le == -1) {
        throw kaitai::undecided_endianness_error("/types/mo/types/descriptor");
    } else if (m__is_le == 1) {
        _read_le();
    } else {
        _read_be();
    }
}

void gettext_mo_t::mo_t::descriptor_t::_read_le() {
    m_len_str = m__io->read_u4le();
    m_ofs_str = m__io->read_u4le();
}

void gettext_mo_t::mo_t::descriptor_t::_read_be() {
    m_len_str = m__io->read_u4be();
    m_ofs_str = m__io->read_u4be();
}

gettext_mo_t::mo_t::descriptor_t::~descriptor_t() {
    _clean_up();
}

void gettext_mo_t::mo_t::descriptor_t::_clean_up() {
    if (f_str) {
    }
}

std::string gettext_mo_t::mo_t::descriptor_t::str() {
    if (f_str)
        return m_str;
    kaitai::kstream *io = _root()->_io();
    std::streampos _pos = io->pos();
    io->seek(ofs_str());
    if (m__is_le == 1) {
        m_str = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(io->read_bytes(len_str()), 0, false), std::string("UTF-8"));
    } else {
        m_str = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(io->read_bytes(len_str()), 0, false), std::string("UTF-8"));
    }
    io->seek(_pos);
    f_str = true;
    return m_str;
}

std::vector<gettext_mo_t::mo_t::descriptor_t*>* gettext_mo_t::mo_t::originals() {
    if (f_originals)
        return m_originals;
    kaitai::kstream *io = _root()->_io();
    std::streampos _pos = io->pos();
    io->seek(ofs_originals());
    if (m__is_le == 1) {
        m_originals = new std::vector<descriptor_t*>();
        const int l_originals = num_translations();
        for (int i = 0; i < l_originals; i++) {
            m_originals->push_back(new descriptor_t(io, this, m__root, m__is_le));
        }
    } else {
        m_originals = new std::vector<descriptor_t*>();
        const int l_originals = num_translations();
        for (int i = 0; i < l_originals; i++) {
            m_originals->push_back(new descriptor_t(io, this, m__root, m__is_le));
        }
    }
    io->seek(_pos);
    f_originals = true;
    return m_originals;
}

std::vector<gettext_mo_t::mo_t::descriptor_t*>* gettext_mo_t::mo_t::translations() {
    if (f_translations)
        return m_translations;
    kaitai::kstream *io = _root()->_io();
    std::streampos _pos = io->pos();
    io->seek(ofs_translations());
    if (m__is_le == 1) {
        m_translations = new std::vector<descriptor_t*>();
        const int l_translations = num_translations();
        for (int i = 0; i < l_translations; i++) {
            m_translations->push_back(new descriptor_t(io, this, m__root, m__is_le));
        }
    } else {
        m_translations = new std::vector<descriptor_t*>();
        const int l_translations = num_translations();
        for (int i = 0; i < l_translations; i++) {
            m_translations->push_back(new descriptor_t(io, this, m__root, m__is_le));
        }
    }
    io->seek(_pos);
    f_translations = true;
    return m_translations;
}

std::vector<gettext_mo_t::mo_t::hashtable_item_t*>* gettext_mo_t::mo_t::hashtable_items() {
    if (f_hashtable_items)
        return m_hashtable_items;
    n_hashtable_items = true;
    if (ofs_hashtable_items() != 0) {
        n_hashtable_items = false;
        kaitai::kstream *io = _root()->_io();
        std::streampos _pos = io->pos();
        io->seek(ofs_hashtable_items());
        if (m__is_le == 1) {
            m_hashtable_items = new std::vector<hashtable_item_t*>();
            const int l_hashtable_items = num_hashtable_items();
            for (int i = 0; i < l_hashtable_items; i++) {
                m_hashtable_items->push_back(new hashtable_item_t(io, this, m__root, m__is_le));
            }
        } else {
            m_hashtable_items = new std::vector<hashtable_item_t*>();
            const int l_hashtable_items = num_hashtable_items();
            for (int i = 0; i < l_hashtable_items; i++) {
                m_hashtable_items->push_back(new hashtable_item_t(io, this, m__root, m__is_le));
            }
        }
        io->seek(_pos);
        f_hashtable_items = true;
    }
    return m_hashtable_items;
}