zisofs is a compression format for files on ISO9660 file system. It has limited support across operating systems, mainly Linux kernel. Typically a directory tree is first preprocessed by mkzftree (from the zisofs-tools package before being turned into an ISO9660 image by mkisofs, genisoimage or similar tool. The data is zlib compressed.
The specification here describes the structure of a file that has been preprocessed by mkzftree, not of a full ISO9660 ziso. Data is not decompressed, as blocks with length 0 have a special meaning. Decompression and deconstruction of this data should be done outside of Kaitai Struct.
This page hosts a formal specification of zisofs using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing 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.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
std::istream
). One can open local file for that, or use existing std::string
or char*
buffer.
#include <fstream>
std::ifstream is("path/to/local/file.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);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
zisofs_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.block_pointers() // => The final pointer (`block_pointers[header.num_blocks]`) indicates the end
of the last block. Typically this is also the end of the file data.
#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
/**
* zisofs is a compression format for files on ISO9660 file system. It has
* limited support across operating systems, mainly Linux kernel. Typically a
* directory tree is first preprocessed by mkzftree (from the zisofs-tools
* package before being turned into an ISO9660 image by mkisofs, genisoimage
* or similar tool. The data is zlib compressed.
*
* The specification here describes the structure of a file that has been
* preprocessed by mkzftree, not of a full ISO9660 ziso. Data is not
* decompressed, as blocks with length 0 have a special meaning. Decompression
* and deconstruction of this data should be done outside of Kaitai Struct.
* \sa https://web.archive.org/web/20200612093441/https://dev.lovelyhq.com/libburnia/web/-/wikis/zisofs Source
*/
class zisofs_t : public kaitai::kstruct {
public:
class header_t;
class block_t;
zisofs_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, zisofs_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~zisofs_t();
class header_t : public kaitai::kstruct {
public:
header_t(kaitai::kstream* p__io, zisofs_t* p__parent = nullptr, zisofs_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~header_t();
private:
bool f_block_size;
int32_t m_block_size;
public:
int32_t block_size();
private:
bool f_num_blocks;
int32_t m_num_blocks;
public:
/**
* ceil(uncompressed_size / block_size)
*/
int32_t num_blocks();
private:
std::string m_magic;
uint32_t m_uncompressed_size;
uint8_t m_len_header;
uint8_t m_block_size_log2;
std::string m_reserved;
zisofs_t* m__root;
zisofs_t* m__parent;
public:
std::string magic() const { return m_magic; }
/**
* Size of the original uncompressed file
*/
uint32_t uncompressed_size() const { return m_uncompressed_size; }
/**
* header_size >> 2 (currently 4)
*/
uint8_t len_header() const { return m_len_header; }
uint8_t block_size_log2() const { return m_block_size_log2; }
std::string reserved() const { return m_reserved; }
zisofs_t* _root() const { return m__root; }
zisofs_t* _parent() const { return m__parent; }
};
class block_t : public kaitai::kstruct {
public:
block_t(uint32_t p_ofs_start, uint32_t p_ofs_end, kaitai::kstream* p__io, zisofs_t* p__parent = nullptr, zisofs_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~block_t();
private:
bool f_len_data;
int32_t m_len_data;
public:
int32_t len_data();
private:
bool f_data;
std::string m_data;
public:
std::string data();
private:
uint32_t m_ofs_start;
uint32_t m_ofs_end;
zisofs_t* m__root;
zisofs_t* m__parent;
public:
uint32_t ofs_start() const { return m_ofs_start; }
uint32_t ofs_end() const { return m_ofs_end; }
zisofs_t* _root() const { return m__root; }
zisofs_t* _parent() const { return m__parent; }
};
private:
bool f_blocks;
std::unique_ptr<std::vector<std::unique_ptr<block_t>>> m_blocks;
public:
std::vector<std::unique_ptr<block_t>>* blocks();
private:
std::unique_ptr<header_t> m_header;
std::unique_ptr<std::vector<uint32_t>> m_block_pointers;
zisofs_t* m__root;
kaitai::kstruct* m__parent;
std::string m__raw_header;
std::unique_ptr<kaitai::kstream> m__io__raw_header;
public:
header_t* header() const { return m_header.get(); }
/**
* The final pointer (`block_pointers[header.num_blocks]`) indicates the end
* of the last block. Typically this is also the end of the file data.
*/
std::vector<uint32_t>* block_pointers() const { return m_block_pointers.get(); }
zisofs_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
std::string _raw_header() const { return m__raw_header; }
kaitai::kstream* _io__raw_header() const { return m__io__raw_header.get(); }
};
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "zisofs.h"
#include "kaitai/exceptions.h"
zisofs_t::zisofs_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, zisofs_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_header = nullptr;
m__io__raw_header = nullptr;
m_block_pointers = nullptr;
m_blocks = nullptr;
f_blocks = false;
_read();
}
void zisofs_t::_read() {
m__raw_header = m__io->read_bytes(16);
m__io__raw_header = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_header));
m_header = std::unique_ptr<header_t>(new header_t(m__io__raw_header.get(), this, m__root));
m_block_pointers = std::unique_ptr<std::vector<uint32_t>>(new std::vector<uint32_t>());
const int l_block_pointers = (header()->num_blocks() + 1);
for (int i = 0; i < l_block_pointers; i++) {
m_block_pointers->push_back(std::move(m__io->read_u4le()));
}
}
zisofs_t::~zisofs_t() {
_clean_up();
}
void zisofs_t::_clean_up() {
if (f_blocks) {
}
}
zisofs_t::header_t::header_t(kaitai::kstream* p__io, zisofs_t* p__parent, zisofs_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
f_block_size = false;
f_num_blocks = false;
_read();
}
void zisofs_t::header_t::_read() {
m_magic = m__io->read_bytes(8);
if (!(magic() == std::string("\x37\xE4\x53\x96\xC9\xDB\xD6\x07", 8))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x37\xE4\x53\x96\xC9\xDB\xD6\x07", 8), magic(), _io(), std::string("/types/header/seq/0"));
}
m_uncompressed_size = m__io->read_u4le();
m_len_header = m__io->read_u1();
if (!(len_header() == 4)) {
throw kaitai::validation_not_equal_error<uint8_t>(4, len_header(), _io(), std::string("/types/header/seq/2"));
}
m_block_size_log2 = m__io->read_u1();
if (!( ((block_size_log2() == 15) || (block_size_log2() == 16) || (block_size_log2() == 17)) )) {
throw kaitai::validation_not_any_of_error<uint8_t>(block_size_log2(), _io(), std::string("/types/header/seq/3"));
}
m_reserved = m__io->read_bytes(2);
if (!(reserved() == std::string("\x00\x00", 2))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x00\x00", 2), reserved(), _io(), std::string("/types/header/seq/4"));
}
}
zisofs_t::header_t::~header_t() {
_clean_up();
}
void zisofs_t::header_t::_clean_up() {
}
int32_t zisofs_t::header_t::block_size() {
if (f_block_size)
return m_block_size;
m_block_size = (1 << block_size_log2());
f_block_size = true;
return m_block_size;
}
int32_t zisofs_t::header_t::num_blocks() {
if (f_num_blocks)
return m_num_blocks;
m_num_blocks = ((uncompressed_size() / block_size()) + ((kaitai::kstream::mod(uncompressed_size(), block_size()) != 0) ? (1) : (0)));
f_num_blocks = true;
return m_num_blocks;
}
zisofs_t::block_t::block_t(uint32_t p_ofs_start, uint32_t p_ofs_end, kaitai::kstream* p__io, zisofs_t* p__parent, zisofs_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_ofs_start = p_ofs_start;
m_ofs_end = p_ofs_end;
f_len_data = false;
f_data = false;
_read();
}
void zisofs_t::block_t::_read() {
}
zisofs_t::block_t::~block_t() {
_clean_up();
}
void zisofs_t::block_t::_clean_up() {
if (f_data) {
}
}
int32_t zisofs_t::block_t::len_data() {
if (f_len_data)
return m_len_data;
m_len_data = (ofs_end() - ofs_start());
f_len_data = true;
return m_len_data;
}
std::string zisofs_t::block_t::data() {
if (f_data)
return m_data;
kaitai::kstream *io = _root()->_io();
std::streampos _pos = io->pos();
io->seek(ofs_start());
m_data = io->read_bytes(len_data());
io->seek(_pos);
f_data = true;
return m_data;
}
std::vector<std::unique_ptr<zisofs_t::block_t>>* zisofs_t::blocks() {
if (f_blocks)
return m_blocks.get();
m_blocks = std::unique_ptr<std::vector<std::unique_ptr<block_t>>>(new std::vector<std::unique_ptr<block_t>>());
const int l_blocks = header()->num_blocks();
for (int i = 0; i < l_blocks; i++) {
m_blocks->push_back(std::move(std::unique_ptr<block_t>(new block_t(block_pointers()->at(i), block_pointers()->at((i + 1)), m__io, this, m__root))));
}
f_blocks = true;
return m_blocks.get();
}