summaryrefslogtreecommitdiff
path: root/python/examples/kaitai/kaitai_struct_formats/media
diff options
context:
space:
mode:
authorAndrew Lamoureux <andrew@vector35.com>2019-03-18 17:34:09 -0400
committerRusty Wagner <rusty@vector35.com>2019-03-20 13:00:17 -0400
commit815f5485eb50eff3b483a6bcc0c3d3d11569f8ca (patch)
tree5128136ac86971ed8729e8c19cb00bcf8f220acc /python/examples/kaitai/kaitai_struct_formats/media
parent8e9322ed8b5826dd62bc931d83e0828d42f7969f (diff)
kaitai: all formats compiled and included
Diffstat (limited to 'python/examples/kaitai/kaitai_struct_formats/media')
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/__init__.py0
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/avi.py292
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/blender_blend.py322
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/creative_voice_file.py340
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/genmidi_op2.py151
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/id3v1_1.py218
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/id3v2_3.py310
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/id3v2_4.py411
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/magicavoxel_vox.py385
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/ogg.py114
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/quicktime_mov.py479
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/standard_midi_file.py386
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/stl.py108
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/__init__.py0
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/fasttracker_xm_module.py554
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/s3m.py485
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/vlq_base128_be.py102
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/vp8_ivf.py108
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/media/wav.py492
19 files changed, 5257 insertions, 0 deletions
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/__init__.py b/python/examples/kaitai/kaitai_struct_formats/media/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/__init__.py
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/avi.py b/python/examples/kaitai/kaitai_struct_formats/media/avi.py
new file mode 100644
index 00000000..bf34eb55
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/avi.py
@@ -0,0 +1,292 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+from enum import Enum
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class Avi(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://msdn.microsoft.com/en-us/library/ms779636.aspx
+ """
+
+ class ChunkType(Enum):
+ idx1 = 829973609
+ junk = 1263424842
+ info = 1330007625
+ isft = 1413894985
+ list = 1414744396
+ strf = 1718776947
+ avih = 1751742049
+ strh = 1752331379
+ movi = 1769369453
+ hdrl = 1819436136
+ strl = 1819440243
+
+ class StreamType(Enum):
+ mids = 1935960429
+ vids = 1935960438
+ auds = 1935963489
+ txts = 1937012852
+
+ class HandlerType(Enum):
+ mp3 = 85
+ ac3 = 8192
+ dts = 8193
+ cvid = 1684633187
+ xvid = 1684633208
+ SEQ_FIELDS = ["magic1", "file_size", "magic2", "data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic1']['start'] = self._io.pos()
+ self.magic1 = self._io.ensure_fixed_contents(b"\x52\x49\x46\x46")
+ self._debug['magic1']['end'] = self._io.pos()
+ self._debug['file_size']['start'] = self._io.pos()
+ self.file_size = self._io.read_u4le()
+ self._debug['file_size']['end'] = self._io.pos()
+ self._debug['magic2']['start'] = self._io.pos()
+ self.magic2 = self._io.ensure_fixed_contents(b"\x41\x56\x49\x20")
+ self._debug['magic2']['end'] = self._io.pos()
+ self._debug['data']['start'] = self._io.pos()
+ self._raw_data = self._io.read_bytes((self.file_size - 4))
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.Blocks(io, self, self._root)
+ self.data._read()
+ self._debug['data']['end'] = self._io.pos()
+
+ class ListBody(KaitaiStruct):
+ SEQ_FIELDS = ["list_type", "data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['list_type']['start'] = self._io.pos()
+ self.list_type = KaitaiStream.resolve_enum(self._root.ChunkType, self._io.read_u4le())
+ self._debug['list_type']['end'] = self._io.pos()
+ self._debug['data']['start'] = self._io.pos()
+ self.data = self._root.Blocks(self._io, self, self._root)
+ self.data._read()
+ self._debug['data']['end'] = self._io.pos()
+
+
+ class Rect(KaitaiStruct):
+ SEQ_FIELDS = ["left", "top", "right", "bottom"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['left']['start'] = self._io.pos()
+ self.left = self._io.read_s2le()
+ self._debug['left']['end'] = self._io.pos()
+ self._debug['top']['start'] = self._io.pos()
+ self.top = self._io.read_s2le()
+ self._debug['top']['end'] = self._io.pos()
+ self._debug['right']['start'] = self._io.pos()
+ self.right = self._io.read_s2le()
+ self._debug['right']['end'] = self._io.pos()
+ self._debug['bottom']['start'] = self._io.pos()
+ self.bottom = self._io.read_s2le()
+ self._debug['bottom']['end'] = self._io.pos()
+
+
+ class Blocks(KaitaiStruct):
+ SEQ_FIELDS = ["entries"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['entries']['start'] = self._io.pos()
+ self.entries = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['entries']:
+ self._debug['entries']['arr'] = []
+ self._debug['entries']['arr'].append({'start': self._io.pos()})
+ _t_entries = self._root.Block(self._io, self, self._root)
+ _t_entries._read()
+ self.entries.append(_t_entries)
+ self._debug['entries']['arr'][len(self.entries) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['entries']['end'] = self._io.pos()
+
+
+ class AvihBody(KaitaiStruct):
+ """Main header of an AVI file, defined as AVIMAINHEADER structure.
+
+ .. seealso::
+ Source - https://msdn.microsoft.com/en-us/library/ms779632.aspx
+ """
+ SEQ_FIELDS = ["micro_sec_per_frame", "max_bytes_per_sec", "padding_granularity", "flags", "total_frames", "initial_frames", "streams", "suggested_buffer_size", "width", "height", "reserved"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['micro_sec_per_frame']['start'] = self._io.pos()
+ self.micro_sec_per_frame = self._io.read_u4le()
+ self._debug['micro_sec_per_frame']['end'] = self._io.pos()
+ self._debug['max_bytes_per_sec']['start'] = self._io.pos()
+ self.max_bytes_per_sec = self._io.read_u4le()
+ self._debug['max_bytes_per_sec']['end'] = self._io.pos()
+ self._debug['padding_granularity']['start'] = self._io.pos()
+ self.padding_granularity = self._io.read_u4le()
+ self._debug['padding_granularity']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_u4le()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['total_frames']['start'] = self._io.pos()
+ self.total_frames = self._io.read_u4le()
+ self._debug['total_frames']['end'] = self._io.pos()
+ self._debug['initial_frames']['start'] = self._io.pos()
+ self.initial_frames = self._io.read_u4le()
+ self._debug['initial_frames']['end'] = self._io.pos()
+ self._debug['streams']['start'] = self._io.pos()
+ self.streams = self._io.read_u4le()
+ self._debug['streams']['end'] = self._io.pos()
+ self._debug['suggested_buffer_size']['start'] = self._io.pos()
+ self.suggested_buffer_size = self._io.read_u4le()
+ self._debug['suggested_buffer_size']['end'] = self._io.pos()
+ self._debug['width']['start'] = self._io.pos()
+ self.width = self._io.read_u4le()
+ self._debug['width']['end'] = self._io.pos()
+ self._debug['height']['start'] = self._io.pos()
+ self.height = self._io.read_u4le()
+ self._debug['height']['end'] = self._io.pos()
+ self._debug['reserved']['start'] = self._io.pos()
+ self.reserved = self._io.read_bytes(16)
+ self._debug['reserved']['end'] = self._io.pos()
+
+
+ class Block(KaitaiStruct):
+ SEQ_FIELDS = ["four_cc", "block_size", "data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['four_cc']['start'] = self._io.pos()
+ self.four_cc = KaitaiStream.resolve_enum(self._root.ChunkType, self._io.read_u4le())
+ self._debug['four_cc']['end'] = self._io.pos()
+ self._debug['block_size']['start'] = self._io.pos()
+ self.block_size = self._io.read_u4le()
+ self._debug['block_size']['end'] = self._io.pos()
+ self._debug['data']['start'] = self._io.pos()
+ _on = self.four_cc
+ if _on == self._root.ChunkType.list:
+ self._raw_data = self._io.read_bytes(self.block_size)
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.ListBody(io, self, self._root)
+ self.data._read()
+ elif _on == self._root.ChunkType.avih:
+ self._raw_data = self._io.read_bytes(self.block_size)
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.AvihBody(io, self, self._root)
+ self.data._read()
+ elif _on == self._root.ChunkType.strh:
+ self._raw_data = self._io.read_bytes(self.block_size)
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.StrhBody(io, self, self._root)
+ self.data._read()
+ else:
+ self.data = self._io.read_bytes(self.block_size)
+ self._debug['data']['end'] = self._io.pos()
+
+
+ class StrhBody(KaitaiStruct):
+ """Stream header (one header per stream), defined as AVISTREAMHEADER structure.
+
+ .. seealso::
+ Source - https://msdn.microsoft.com/en-us/library/ms779638.aspx
+ """
+ SEQ_FIELDS = ["fcc_type", "fcc_handler", "flags", "priority", "language", "initial_frames", "scale", "rate", "start", "length", "suggested_buffer_size", "quality", "sample_size", "frame"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['fcc_type']['start'] = self._io.pos()
+ self.fcc_type = KaitaiStream.resolve_enum(self._root.StreamType, self._io.read_u4le())
+ self._debug['fcc_type']['end'] = self._io.pos()
+ self._debug['fcc_handler']['start'] = self._io.pos()
+ self.fcc_handler = KaitaiStream.resolve_enum(self._root.HandlerType, self._io.read_u4le())
+ self._debug['fcc_handler']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_u4le()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['priority']['start'] = self._io.pos()
+ self.priority = self._io.read_u2le()
+ self._debug['priority']['end'] = self._io.pos()
+ self._debug['language']['start'] = self._io.pos()
+ self.language = self._io.read_u2le()
+ self._debug['language']['end'] = self._io.pos()
+ self._debug['initial_frames']['start'] = self._io.pos()
+ self.initial_frames = self._io.read_u4le()
+ self._debug['initial_frames']['end'] = self._io.pos()
+ self._debug['scale']['start'] = self._io.pos()
+ self.scale = self._io.read_u4le()
+ self._debug['scale']['end'] = self._io.pos()
+ self._debug['rate']['start'] = self._io.pos()
+ self.rate = self._io.read_u4le()
+ self._debug['rate']['end'] = self._io.pos()
+ self._debug['start']['start'] = self._io.pos()
+ self.start = self._io.read_u4le()
+ self._debug['start']['end'] = self._io.pos()
+ self._debug['length']['start'] = self._io.pos()
+ self.length = self._io.read_u4le()
+ self._debug['length']['end'] = self._io.pos()
+ self._debug['suggested_buffer_size']['start'] = self._io.pos()
+ self.suggested_buffer_size = self._io.read_u4le()
+ self._debug['suggested_buffer_size']['end'] = self._io.pos()
+ self._debug['quality']['start'] = self._io.pos()
+ self.quality = self._io.read_u4le()
+ self._debug['quality']['end'] = self._io.pos()
+ self._debug['sample_size']['start'] = self._io.pos()
+ self.sample_size = self._io.read_u4le()
+ self._debug['sample_size']['end'] = self._io.pos()
+ self._debug['frame']['start'] = self._io.pos()
+ self.frame = self._root.Rect(self._io, self, self._root)
+ self.frame._read()
+ self._debug['frame']['end'] = self._io.pos()
+
+
+ class StrfBody(KaitaiStruct):
+ """Stream format description."""
+ SEQ_FIELDS = []
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ pass
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/blender_blend.py b/python/examples/kaitai/kaitai_struct_formats/media/blender_blend.py
new file mode 100644
index 00000000..bda24a08
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/blender_blend.py
@@ -0,0 +1,322 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+from enum import Enum
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class BlenderBlend(KaitaiStruct):
+ """Blender is an open source suite for 3D modelling, sculpting,
+ animation, compositing, rendering, preparation of assets for its own
+ game engine and exporting to others, etc. `.blend` is its own binary
+ format that saves whole state of suite: current scene, animations,
+ all software settings, extensions, etc.
+
+ Internally, .blend format is a hybrid semi-self-descriptive
+ format. On top level, it contains a simple header and a sequence of
+ file blocks, which more or less follow typical [TLV
+ pattern](https://en.wikipedia.org/wiki/Type-length-value). Pre-last
+ block would be a structure with code `DNA1`, which is a essentially
+ a machine-readable schema of all other structures used in this file.
+ """
+
+ class PtrSize(Enum):
+ bits_64 = 45
+ bits_32 = 95
+
+ class Endian(Enum):
+ be = 86
+ le = 118
+ SEQ_FIELDS = ["hdr", "blocks"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['hdr']['start'] = self._io.pos()
+ self.hdr = self._root.Header(self._io, self, self._root)
+ self.hdr._read()
+ self._debug['hdr']['end'] = self._io.pos()
+ self._debug['blocks']['start'] = self._io.pos()
+ self.blocks = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['blocks']:
+ self._debug['blocks']['arr'] = []
+ self._debug['blocks']['arr'].append({'start': self._io.pos()})
+ _t_blocks = self._root.FileBlock(self._io, self, self._root)
+ _t_blocks._read()
+ self.blocks.append(_t_blocks)
+ self._debug['blocks']['arr'][len(self.blocks) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['blocks']['end'] = self._io.pos()
+
+ class DnaStruct(KaitaiStruct):
+ """DNA struct contains a `type` (type name), which is specified as
+ an index in types table, and sequence of fields.
+ """
+ SEQ_FIELDS = ["idx_type", "num_fields", "fields"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['idx_type']['start'] = self._io.pos()
+ self.idx_type = self._io.read_u2le()
+ self._debug['idx_type']['end'] = self._io.pos()
+ self._debug['num_fields']['start'] = self._io.pos()
+ self.num_fields = self._io.read_u2le()
+ self._debug['num_fields']['end'] = self._io.pos()
+ self._debug['fields']['start'] = self._io.pos()
+ self.fields = [None] * (self.num_fields)
+ for i in range(self.num_fields):
+ if not 'arr' in self._debug['fields']:
+ self._debug['fields']['arr'] = []
+ self._debug['fields']['arr'].append({'start': self._io.pos()})
+ _t_fields = self._root.DnaField(self._io, self, self._root)
+ _t_fields._read()
+ self.fields[i] = _t_fields
+ self._debug['fields']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['fields']['end'] = self._io.pos()
+
+ @property
+ def type(self):
+ if hasattr(self, '_m_type'):
+ return self._m_type if hasattr(self, '_m_type') else None
+
+ self._m_type = self._parent.types[self.idx_type]
+ return self._m_type if hasattr(self, '_m_type') else None
+
+
+ class FileBlock(KaitaiStruct):
+ SEQ_FIELDS = ["code", "len_body", "mem_addr", "sdna_index", "count", "body"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['code']['start'] = self._io.pos()
+ self.code = (self._io.read_bytes(4)).decode(u"ASCII")
+ self._debug['code']['end'] = self._io.pos()
+ self._debug['len_body']['start'] = self._io.pos()
+ self.len_body = self._io.read_u4le()
+ self._debug['len_body']['end'] = self._io.pos()
+ self._debug['mem_addr']['start'] = self._io.pos()
+ self.mem_addr = self._io.read_bytes(self._root.hdr.psize)
+ self._debug['mem_addr']['end'] = self._io.pos()
+ self._debug['sdna_index']['start'] = self._io.pos()
+ self.sdna_index = self._io.read_u4le()
+ self._debug['sdna_index']['end'] = self._io.pos()
+ self._debug['count']['start'] = self._io.pos()
+ self.count = self._io.read_u4le()
+ self._debug['count']['end'] = self._io.pos()
+ self._debug['body']['start'] = self._io.pos()
+ _on = self.code
+ if _on == u"DNA1":
+ self._raw_body = self._io.read_bytes(self.len_body)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.Dna1Body(io, self, self._root)
+ self.body._read()
+ else:
+ self.body = self._io.read_bytes(self.len_body)
+ self._debug['body']['end'] = self._io.pos()
+
+ @property
+ def sdna_struct(self):
+ if hasattr(self, '_m_sdna_struct'):
+ return self._m_sdna_struct if hasattr(self, '_m_sdna_struct') else None
+
+ if self.sdna_index != 0:
+ self._m_sdna_struct = self._root.sdna_structs[self.sdna_index]
+
+ return self._m_sdna_struct if hasattr(self, '_m_sdna_struct') else None
+
+
+ class Dna1Body(KaitaiStruct):
+ """DNA1, also known as "Structure DNA", is a special block in
+ .blend file, which contains machine-readable specifications of
+ all other structures used in this .blend file.
+
+ Effectively, this block contains:
+
+ * a sequence of "names" (strings which represent field names)
+ * a sequence of "types" (strings which represent type name)
+ * a sequence of "type lengths"
+ * a sequence of "structs" (which describe contents of every
+ structure, referring to types and names by index)
+
+ .. seealso::
+ Source - https://en.blender.org/index.php/Dev:Source/Architecture/File_Format#Structure_DNA
+ """
+ SEQ_FIELDS = ["id", "name_magic", "num_names", "names", "padding_1", "type_magic", "num_types", "types", "padding_2", "tlen_magic", "lengths", "padding_3", "strc_magic", "num_structs", "structs"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['id']['start'] = self._io.pos()
+ self.id = self._io.ensure_fixed_contents(b"\x53\x44\x4E\x41")
+ self._debug['id']['end'] = self._io.pos()
+ self._debug['name_magic']['start'] = self._io.pos()
+ self.name_magic = self._io.ensure_fixed_contents(b"\x4E\x41\x4D\x45")
+ self._debug['name_magic']['end'] = self._io.pos()
+ self._debug['num_names']['start'] = self._io.pos()
+ self.num_names = self._io.read_u4le()
+ self._debug['num_names']['end'] = self._io.pos()
+ self._debug['names']['start'] = self._io.pos()
+ self.names = [None] * (self.num_names)
+ for i in range(self.num_names):
+ if not 'arr' in self._debug['names']:
+ self._debug['names']['arr'] = []
+ self._debug['names']['arr'].append({'start': self._io.pos()})
+ self.names[i] = (self._io.read_bytes_term(0, False, True, True)).decode(u"UTF-8")
+ self._debug['names']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['names']['end'] = self._io.pos()
+ self._debug['padding_1']['start'] = self._io.pos()
+ self.padding_1 = self._io.read_bytes(((4 - self._io.pos()) % 4))
+ self._debug['padding_1']['end'] = self._io.pos()
+ self._debug['type_magic']['start'] = self._io.pos()
+ self.type_magic = self._io.ensure_fixed_contents(b"\x54\x59\x50\x45")
+ self._debug['type_magic']['end'] = self._io.pos()
+ self._debug['num_types']['start'] = self._io.pos()
+ self.num_types = self._io.read_u4le()
+ self._debug['num_types']['end'] = self._io.pos()
+ self._debug['types']['start'] = self._io.pos()
+ self.types = [None] * (self.num_types)
+ for i in range(self.num_types):
+ if not 'arr' in self._debug['types']:
+ self._debug['types']['arr'] = []
+ self._debug['types']['arr'].append({'start': self._io.pos()})
+ self.types[i] = (self._io.read_bytes_term(0, False, True, True)).decode(u"UTF-8")
+ self._debug['types']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['types']['end'] = self._io.pos()
+ self._debug['padding_2']['start'] = self._io.pos()
+ self.padding_2 = self._io.read_bytes(((4 - self._io.pos()) % 4))
+ self._debug['padding_2']['end'] = self._io.pos()
+ self._debug['tlen_magic']['start'] = self._io.pos()
+ self.tlen_magic = self._io.ensure_fixed_contents(b"\x54\x4C\x45\x4E")
+ self._debug['tlen_magic']['end'] = self._io.pos()
+ self._debug['lengths']['start'] = self._io.pos()
+ self.lengths = [None] * (self.num_types)
+ for i in range(self.num_types):
+ if not 'arr' in self._debug['lengths']:
+ self._debug['lengths']['arr'] = []
+ self._debug['lengths']['arr'].append({'start': self._io.pos()})
+ self.lengths[i] = self._io.read_u2le()
+ self._debug['lengths']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['lengths']['end'] = self._io.pos()
+ self._debug['padding_3']['start'] = self._io.pos()
+ self.padding_3 = self._io.read_bytes(((4 - self._io.pos()) % 4))
+ self._debug['padding_3']['end'] = self._io.pos()
+ self._debug['strc_magic']['start'] = self._io.pos()
+ self.strc_magic = self._io.ensure_fixed_contents(b"\x53\x54\x52\x43")
+ self._debug['strc_magic']['end'] = self._io.pos()
+ self._debug['num_structs']['start'] = self._io.pos()
+ self.num_structs = self._io.read_u4le()
+ self._debug['num_structs']['end'] = self._io.pos()
+ self._debug['structs']['start'] = self._io.pos()
+ self.structs = [None] * (self.num_structs)
+ for i in range(self.num_structs):
+ if not 'arr' in self._debug['structs']:
+ self._debug['structs']['arr'] = []
+ self._debug['structs']['arr'].append({'start': self._io.pos()})
+ _t_structs = self._root.DnaStruct(self._io, self, self._root)
+ _t_structs._read()
+ self.structs[i] = _t_structs
+ self._debug['structs']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['structs']['end'] = self._io.pos()
+
+
+ class Header(KaitaiStruct):
+ SEQ_FIELDS = ["magic", "ptr_size_id", "endian", "version"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x42\x4C\x45\x4E\x44\x45\x52")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['ptr_size_id']['start'] = self._io.pos()
+ self.ptr_size_id = KaitaiStream.resolve_enum(self._root.PtrSize, self._io.read_u1())
+ self._debug['ptr_size_id']['end'] = self._io.pos()
+ self._debug['endian']['start'] = self._io.pos()
+ self.endian = KaitaiStream.resolve_enum(self._root.Endian, self._io.read_u1())
+ self._debug['endian']['end'] = self._io.pos()
+ self._debug['version']['start'] = self._io.pos()
+ self.version = (self._io.read_bytes(3)).decode(u"ASCII")
+ self._debug['version']['end'] = self._io.pos()
+
+ @property
+ def psize(self):
+ """Number of bytes that a pointer occupies."""
+ if hasattr(self, '_m_psize'):
+ return self._m_psize if hasattr(self, '_m_psize') else None
+
+ self._m_psize = (8 if self.ptr_size_id == self._root.PtrSize.bits_64 else 4)
+ return self._m_psize if hasattr(self, '_m_psize') else None
+
+
+ class DnaField(KaitaiStruct):
+ SEQ_FIELDS = ["idx_type", "idx_name"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['idx_type']['start'] = self._io.pos()
+ self.idx_type = self._io.read_u2le()
+ self._debug['idx_type']['end'] = self._io.pos()
+ self._debug['idx_name']['start'] = self._io.pos()
+ self.idx_name = self._io.read_u2le()
+ self._debug['idx_name']['end'] = self._io.pos()
+
+ @property
+ def type(self):
+ if hasattr(self, '_m_type'):
+ return self._m_type if hasattr(self, '_m_type') else None
+
+ self._m_type = self._parent._parent.types[self.idx_type]
+ return self._m_type if hasattr(self, '_m_type') else None
+
+ @property
+ def name(self):
+ if hasattr(self, '_m_name'):
+ return self._m_name if hasattr(self, '_m_name') else None
+
+ self._m_name = self._parent._parent.names[self.idx_name]
+ return self._m_name if hasattr(self, '_m_name') else None
+
+
+ @property
+ def sdna_structs(self):
+ if hasattr(self, '_m_sdna_structs'):
+ return self._m_sdna_structs if hasattr(self, '_m_sdna_structs') else None
+
+ self._m_sdna_structs = self.blocks[(len(self.blocks) - 2)].body.structs
+ return self._m_sdna_structs if hasattr(self, '_m_sdna_structs') else None
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/creative_voice_file.py b/python/examples/kaitai/kaitai_struct_formats/media/creative_voice_file.py
new file mode 100644
index 00000000..f49a26ce
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/creative_voice_file.py
@@ -0,0 +1,340 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+from enum import Enum
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class CreativeVoiceFile(KaitaiStruct):
+ """Creative Voice File is a container file format for digital audio
+ wave data. Initial revisions were able to support only unsigned
+ 8-bit PCM and ADPCM data, later versions were revised to add support
+ for 16-bit PCM and a-law / u-law formats.
+
+ This format was actively used in 1990s, around the advent of
+ Creative's sound cards (Sound Blaster family). It was a popular
+ choice for a digital sound container in lots of games and multimedia
+ software due to simplicity and availability of Creative's recording
+ / editing tools.
+
+ .. seealso::
+ Source - https://wiki.multimedia.cx/index.php?title=Creative_Voice
+ """
+
+ class BlockTypes(Enum):
+ terminator = 0
+ sound_data = 1
+ sound_data_cont = 2
+ silence = 3
+ marker = 4
+ text = 5
+ repeat_start = 6
+ repeat_end = 7
+ extra_info = 8
+ sound_data_new = 9
+
+ class Codecs(Enum):
+ pcm_8bit_unsigned = 0
+ adpcm_4bit = 1
+ adpcm_2_6bit = 2
+ adpcm_2_bit = 3
+ pcm_16bit_signed = 4
+ alaw = 6
+ ulaw = 7
+ adpcm_4_to_16bit = 512
+ SEQ_FIELDS = ["magic", "header_size", "version", "checksum", "blocks"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x43\x72\x65\x61\x74\x69\x76\x65\x20\x56\x6F\x69\x63\x65\x20\x46\x69\x6C\x65\x1A")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['header_size']['start'] = self._io.pos()
+ self.header_size = self._io.read_u2le()
+ self._debug['header_size']['end'] = self._io.pos()
+ self._debug['version']['start'] = self._io.pos()
+ self.version = self._io.read_u2le()
+ self._debug['version']['end'] = self._io.pos()
+ self._debug['checksum']['start'] = self._io.pos()
+ self.checksum = self._io.read_u2le()
+ self._debug['checksum']['end'] = self._io.pos()
+ self._debug['blocks']['start'] = self._io.pos()
+ self.blocks = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['blocks']:
+ self._debug['blocks']['arr'] = []
+ self._debug['blocks']['arr'].append({'start': self._io.pos()})
+ _t_blocks = self._root.Block(self._io, self, self._root)
+ _t_blocks._read()
+ self.blocks.append(_t_blocks)
+ self._debug['blocks']['arr'][len(self.blocks) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['blocks']['end'] = self._io.pos()
+
+ class BlockMarker(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://wiki.multimedia.cx/index.php?title=Creative_Voice#Block_type_0x04:_Marker
+ """
+ SEQ_FIELDS = ["marker_id"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['marker_id']['start'] = self._io.pos()
+ self.marker_id = self._io.read_u2le()
+ self._debug['marker_id']['end'] = self._io.pos()
+
+
+ class BlockSilence(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://wiki.multimedia.cx/index.php?title=Creative_Voice#Block_type_0x03:_Silence
+ """
+ SEQ_FIELDS = ["duration_samples", "freq_div"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['duration_samples']['start'] = self._io.pos()
+ self.duration_samples = self._io.read_u2le()
+ self._debug['duration_samples']['end'] = self._io.pos()
+ self._debug['freq_div']['start'] = self._io.pos()
+ self.freq_div = self._io.read_u1()
+ self._debug['freq_div']['end'] = self._io.pos()
+
+ @property
+ def sample_rate(self):
+ if hasattr(self, '_m_sample_rate'):
+ return self._m_sample_rate if hasattr(self, '_m_sample_rate') else None
+
+ self._m_sample_rate = (1000000.0 / (256 - self.freq_div))
+ return self._m_sample_rate if hasattr(self, '_m_sample_rate') else None
+
+ @property
+ def duration_sec(self):
+ """Duration of silence, in seconds."""
+ if hasattr(self, '_m_duration_sec'):
+ return self._m_duration_sec if hasattr(self, '_m_duration_sec') else None
+
+ self._m_duration_sec = (self.duration_samples / self.sample_rate)
+ return self._m_duration_sec if hasattr(self, '_m_duration_sec') else None
+
+
+ class BlockSoundDataNew(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://wiki.multimedia.cx/index.php?title=Creative_Voice#Block_type_0x09:_Sound_data_.28New_format.29
+ """
+ SEQ_FIELDS = ["sample_rate", "bits_per_sample", "num_channels", "codec", "reserved", "wave"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['sample_rate']['start'] = self._io.pos()
+ self.sample_rate = self._io.read_u4le()
+ self._debug['sample_rate']['end'] = self._io.pos()
+ self._debug['bits_per_sample']['start'] = self._io.pos()
+ self.bits_per_sample = self._io.read_u1()
+ self._debug['bits_per_sample']['end'] = self._io.pos()
+ self._debug['num_channels']['start'] = self._io.pos()
+ self.num_channels = self._io.read_u1()
+ self._debug['num_channels']['end'] = self._io.pos()
+ self._debug['codec']['start'] = self._io.pos()
+ self.codec = KaitaiStream.resolve_enum(self._root.Codecs, self._io.read_u2le())
+ self._debug['codec']['end'] = self._io.pos()
+ self._debug['reserved']['start'] = self._io.pos()
+ self.reserved = self._io.read_bytes(4)
+ self._debug['reserved']['end'] = self._io.pos()
+ self._debug['wave']['start'] = self._io.pos()
+ self.wave = self._io.read_bytes_full()
+ self._debug['wave']['end'] = self._io.pos()
+
+
+ class Block(KaitaiStruct):
+ SEQ_FIELDS = ["block_type", "body_size1", "body_size2", "body"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['block_type']['start'] = self._io.pos()
+ self.block_type = KaitaiStream.resolve_enum(self._root.BlockTypes, self._io.read_u1())
+ self._debug['block_type']['end'] = self._io.pos()
+ if self.block_type != self._root.BlockTypes.terminator:
+ self._debug['body_size1']['start'] = self._io.pos()
+ self.body_size1 = self._io.read_u2le()
+ self._debug['body_size1']['end'] = self._io.pos()
+
+ if self.block_type != self._root.BlockTypes.terminator:
+ self._debug['body_size2']['start'] = self._io.pos()
+ self.body_size2 = self._io.read_u1()
+ self._debug['body_size2']['end'] = self._io.pos()
+
+ if self.block_type != self._root.BlockTypes.terminator:
+ self._debug['body']['start'] = self._io.pos()
+ _on = self.block_type
+ if _on == self._root.BlockTypes.sound_data_new:
+ self._raw_body = self._io.read_bytes(self.body_size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.BlockSoundDataNew(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.BlockTypes.repeat_start:
+ self._raw_body = self._io.read_bytes(self.body_size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.BlockRepeatStart(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.BlockTypes.marker:
+ self._raw_body = self._io.read_bytes(self.body_size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.BlockMarker(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.BlockTypes.sound_data:
+ self._raw_body = self._io.read_bytes(self.body_size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.BlockSoundData(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.BlockTypes.extra_info:
+ self._raw_body = self._io.read_bytes(self.body_size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.BlockExtraInfo(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.BlockTypes.silence:
+ self._raw_body = self._io.read_bytes(self.body_size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.BlockSilence(io, self, self._root)
+ self.body._read()
+ else:
+ self.body = self._io.read_bytes(self.body_size)
+ self._debug['body']['end'] = self._io.pos()
+
+
+ @property
+ def body_size(self):
+ """body_size is a 24-bit little-endian integer, so we're
+ emulating that by adding two standard-sized integers
+ (body_size1 and body_size2).
+ """
+ if hasattr(self, '_m_body_size'):
+ return self._m_body_size if hasattr(self, '_m_body_size') else None
+
+ if self.block_type != self._root.BlockTypes.terminator:
+ self._m_body_size = (self.body_size1 + (self.body_size2 << 16))
+
+ return self._m_body_size if hasattr(self, '_m_body_size') else None
+
+
+ class BlockRepeatStart(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://wiki.multimedia.cx/index.php?title=Creative_Voice#Block_type_0x06:_Repeat_start
+ """
+ SEQ_FIELDS = ["repeat_count_1"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['repeat_count_1']['start'] = self._io.pos()
+ self.repeat_count_1 = self._io.read_u2le()
+ self._debug['repeat_count_1']['end'] = self._io.pos()
+
+
+ class BlockSoundData(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://wiki.multimedia.cx/index.php?title=Creative_Voice#Block_type_0x01:_Sound_data
+ """
+ SEQ_FIELDS = ["freq_div", "codec", "wave"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['freq_div']['start'] = self._io.pos()
+ self.freq_div = self._io.read_u1()
+ self._debug['freq_div']['end'] = self._io.pos()
+ self._debug['codec']['start'] = self._io.pos()
+ self.codec = KaitaiStream.resolve_enum(self._root.Codecs, self._io.read_u1())
+ self._debug['codec']['end'] = self._io.pos()
+ self._debug['wave']['start'] = self._io.pos()
+ self.wave = self._io.read_bytes_full()
+ self._debug['wave']['end'] = self._io.pos()
+
+ @property
+ def sample_rate(self):
+ if hasattr(self, '_m_sample_rate'):
+ return self._m_sample_rate if hasattr(self, '_m_sample_rate') else None
+
+ self._m_sample_rate = (1000000.0 / (256 - self.freq_div))
+ return self._m_sample_rate if hasattr(self, '_m_sample_rate') else None
+
+
+ class BlockExtraInfo(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://wiki.multimedia.cx/index.php?title=Creative_Voice#Block_type_0x08:_Extra_info
+ """
+ SEQ_FIELDS = ["freq_div", "codec", "num_channels_1"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['freq_div']['start'] = self._io.pos()
+ self.freq_div = self._io.read_u2le()
+ self._debug['freq_div']['end'] = self._io.pos()
+ self._debug['codec']['start'] = self._io.pos()
+ self.codec = KaitaiStream.resolve_enum(self._root.Codecs, self._io.read_u1())
+ self._debug['codec']['end'] = self._io.pos()
+ self._debug['num_channels_1']['start'] = self._io.pos()
+ self.num_channels_1 = self._io.read_u1()
+ self._debug['num_channels_1']['end'] = self._io.pos()
+
+ @property
+ def num_channels(self):
+ """Number of channels (1 = mono, 2 = stereo)."""
+ if hasattr(self, '_m_num_channels'):
+ return self._m_num_channels if hasattr(self, '_m_num_channels') else None
+
+ self._m_num_channels = (self.num_channels_1 + 1)
+ return self._m_num_channels if hasattr(self, '_m_num_channels') else None
+
+ @property
+ def sample_rate(self):
+ if hasattr(self, '_m_sample_rate'):
+ return self._m_sample_rate if hasattr(self, '_m_sample_rate') else None
+
+ self._m_sample_rate = (256000000.0 / (self.num_channels * (65536 - self.freq_div)))
+ return self._m_sample_rate if hasattr(self, '_m_sample_rate') else None
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/genmidi_op2.py b/python/examples/kaitai/kaitai_struct_formats/media/genmidi_op2.py
new file mode 100644
index 00000000..b73c2f8c
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/genmidi_op2.py
@@ -0,0 +1,151 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class GenmidiOp2(KaitaiStruct):
+ """GENMIDI.OP2 is a sound bank file used by players based on DMX sound
+ library to play MIDI files with General MIDI instruments using OPL2
+ sound chip (which was commonly installed on popular AdLib and Sound
+ Blaster sound cards).
+
+ Major users of DMX sound library include:
+
+ * Original Doom game engine (and games based on it: Heretic, Hexen, Strife, Chex Quest)
+ * Raptor: Call of the Shadows
+
+ .. seealso::
+ http://doom.wikia.com/wiki/GENMIDI - http://www.fit.vutbr.cz/~arnost/muslib/op2_form.zip
+ """
+ SEQ_FIELDS = ["magic", "instruments", "instrument_names"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x23\x4F\x50\x4C\x5F\x49\x49\x23")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['instruments']['start'] = self._io.pos()
+ self.instruments = [None] * (175)
+ for i in range(175):
+ if not 'arr' in self._debug['instruments']:
+ self._debug['instruments']['arr'] = []
+ self._debug['instruments']['arr'].append({'start': self._io.pos()})
+ _t_instruments = self._root.InstrumentEntry(self._io, self, self._root)
+ _t_instruments._read()
+ self.instruments[i] = _t_instruments
+ self._debug['instruments']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['instruments']['end'] = self._io.pos()
+ self._debug['instrument_names']['start'] = self._io.pos()
+ self.instrument_names = [None] * (175)
+ for i in range(175):
+ if not 'arr' in self._debug['instrument_names']:
+ self._debug['instrument_names']['arr'] = []
+ self._debug['instrument_names']['arr'].append({'start': self._io.pos()})
+ self.instrument_names[i] = (KaitaiStream.bytes_terminate(KaitaiStream.bytes_strip_right(self._io.read_bytes(32), 0), 0, False)).decode(u"ASCII")
+ self._debug['instrument_names']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['instrument_names']['end'] = self._io.pos()
+
+ class InstrumentEntry(KaitaiStruct):
+ SEQ_FIELDS = ["flags", "finetune", "note", "instruments"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_u2le()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['finetune']['start'] = self._io.pos()
+ self.finetune = self._io.read_u1()
+ self._debug['finetune']['end'] = self._io.pos()
+ self._debug['note']['start'] = self._io.pos()
+ self.note = self._io.read_u1()
+ self._debug['note']['end'] = self._io.pos()
+ self._debug['instruments']['start'] = self._io.pos()
+ self.instruments = [None] * (2)
+ for i in range(2):
+ if not 'arr' in self._debug['instruments']:
+ self._debug['instruments']['arr'] = []
+ self._debug['instruments']['arr'].append({'start': self._io.pos()})
+ _t_instruments = self._root.Instrument(self._io, self, self._root)
+ _t_instruments._read()
+ self.instruments[i] = _t_instruments
+ self._debug['instruments']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['instruments']['end'] = self._io.pos()
+
+
+ class Instrument(KaitaiStruct):
+ SEQ_FIELDS = ["op1", "feedback", "op2", "unused", "base_note"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['op1']['start'] = self._io.pos()
+ self.op1 = self._root.OpSettings(self._io, self, self._root)
+ self.op1._read()
+ self._debug['op1']['end'] = self._io.pos()
+ self._debug['feedback']['start'] = self._io.pos()
+ self.feedback = self._io.read_u1()
+ self._debug['feedback']['end'] = self._io.pos()
+ self._debug['op2']['start'] = self._io.pos()
+ self.op2 = self._root.OpSettings(self._io, self, self._root)
+ self.op2._read()
+ self._debug['op2']['end'] = self._io.pos()
+ self._debug['unused']['start'] = self._io.pos()
+ self.unused = self._io.read_u1()
+ self._debug['unused']['end'] = self._io.pos()
+ self._debug['base_note']['start'] = self._io.pos()
+ self.base_note = self._io.read_s2le()
+ self._debug['base_note']['end'] = self._io.pos()
+
+
+ class OpSettings(KaitaiStruct):
+ """OPL2 settings for one operator (carrier or modulator)
+ """
+ SEQ_FIELDS = ["trem_vibr", "att_dec", "sust_rel", "wave", "scale", "level"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['trem_vibr']['start'] = self._io.pos()
+ self.trem_vibr = self._io.read_u1()
+ self._debug['trem_vibr']['end'] = self._io.pos()
+ self._debug['att_dec']['start'] = self._io.pos()
+ self.att_dec = self._io.read_u1()
+ self._debug['att_dec']['end'] = self._io.pos()
+ self._debug['sust_rel']['start'] = self._io.pos()
+ self.sust_rel = self._io.read_u1()
+ self._debug['sust_rel']['end'] = self._io.pos()
+ self._debug['wave']['start'] = self._io.pos()
+ self.wave = self._io.read_u1()
+ self._debug['wave']['end'] = self._io.pos()
+ self._debug['scale']['start'] = self._io.pos()
+ self.scale = self._io.read_u1()
+ self._debug['scale']['end'] = self._io.pos()
+ self._debug['level']['start'] = self._io.pos()
+ self.level = self._io.read_u1()
+ self._debug['level']['end'] = self._io.pos()
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/id3v1_1.py b/python/examples/kaitai/kaitai_struct_formats/media/id3v1_1.py
new file mode 100644
index 00000000..37a71459
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/id3v1_1.py
@@ -0,0 +1,218 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+from enum import Enum
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class Id3v11(KaitaiStruct):
+ """ID3v1.1 tag is a method to store simple metadata in .mp3 files. The
+ tag is appended to the end of file and spans exactly 128 bytes.
+
+ This type is supposed to be used on full .mp3 files, seeking to
+ proper position automatically. If you're interesting in parsing only
+ the tag itself, please use `id3v1_1::id3_v1_1_tag` subtype.
+
+ .. seealso::
+ Source - http://id3.org/ID3v1
+ """
+ SEQ_FIELDS = []
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ pass
+
+ class Id3V11Tag(KaitaiStruct):
+ """ID3v1.1 tag itself, a fixed size 128 byte structure. Contains
+ several metadata fields as fixed-size strings.
+
+ Note that string encoding is not specified by standard, so real
+ encoding used would vary a lot from one implementation to
+ another. Most Windows-based applications tend to use "ANSI"
+ (i.e. locale-dependent encoding, usually one byte per
+ character). Some embedded applications allow selection of
+ charset.
+ """
+
+ class GenreEnum(Enum):
+ blues = 0
+ classic_rock = 1
+ country = 2
+ dance = 3
+ disco = 4
+ funk = 5
+ grunge = 6
+ hip_hop = 7
+ jazz = 8
+ metal = 9
+ new_age = 10
+ oldies = 11
+ other = 12
+ pop = 13
+ rnb = 14
+ rap = 15
+ reggae = 16
+ rock = 17
+ techno = 18
+ industrial = 19
+ alternative = 20
+ ska = 21
+ death_metal = 22
+ pranks = 23
+ soundtrack = 24
+ euro_techno = 25
+ ambient = 26
+ trip_hop = 27
+ vocal = 28
+ jazz_funk = 29
+ fusion = 30
+ trance = 31
+ classical = 32
+ instrumental = 33
+ acid = 34
+ house = 35
+ game = 36
+ sound_clip = 37
+ gospel = 38
+ noise = 39
+ alternrock = 40
+ bass = 41
+ soul = 42
+ punk = 43
+ space = 44
+ meditative = 45
+ instrumental_pop = 46
+ instrumental_rock = 47
+ ethnic = 48
+ gothic = 49
+ darkwave = 50
+ techno_industrial = 51
+ electronic = 52
+ pop_folk = 53
+ eurodance = 54
+ dream = 55
+ southern_rock = 56
+ comedy = 57
+ cult = 58
+ gangsta = 59
+ top_40 = 60
+ christian_rap = 61
+ pop_funk = 62
+ jungle = 63
+ native_american = 64
+ cabaret = 65
+ new_wave = 66
+ psychadelic = 67
+ rave = 68
+ showtunes = 69
+ trailer = 70
+ lo_fi = 71
+ tribal = 72
+ acid_punk = 73
+ acid_jazz = 74
+ polka = 75
+ retro = 76
+ musical = 77
+ rock_n_roll = 78
+ hard_rock = 79
+ folk = 80
+ folk_rock = 81
+ national_folk = 82
+ swing = 83
+ fast_fusion = 84
+ bebob = 85
+ latin = 86
+ revival = 87
+ celtic = 88
+ bluegrass = 89
+ avantgarde = 90
+ gothic_rock = 91
+ progressive_rock = 92
+ psychedelic_rock = 93
+ symphonic_rock = 94
+ slow_rock = 95
+ big_band = 96
+ chorus = 97
+ easy_listening = 98
+ acoustic = 99
+ humour = 100
+ speech = 101
+ chanson = 102
+ opera = 103
+ chamber_music = 104
+ sonata = 105
+ symphony = 106
+ booty_bass = 107
+ primus = 108
+ porn_groove = 109
+ satire = 110
+ slow_jam = 111
+ club = 112
+ tango = 113
+ samba = 114
+ folklore = 115
+ ballad = 116
+ power_ballad = 117
+ rhythmic_soul = 118
+ freestyle = 119
+ duet = 120
+ punk_rock = 121
+ drum_solo = 122
+ a_capella = 123
+ euro_house = 124
+ dance_hall = 125
+ SEQ_FIELDS = ["magic", "title", "artist", "album", "year", "comment", "genre"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x54\x41\x47")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['title']['start'] = self._io.pos()
+ self.title = self._io.read_bytes(30)
+ self._debug['title']['end'] = self._io.pos()
+ self._debug['artist']['start'] = self._io.pos()
+ self.artist = self._io.read_bytes(30)
+ self._debug['artist']['end'] = self._io.pos()
+ self._debug['album']['start'] = self._io.pos()
+ self.album = self._io.read_bytes(30)
+ self._debug['album']['end'] = self._io.pos()
+ self._debug['year']['start'] = self._io.pos()
+ self.year = (self._io.read_bytes(4)).decode(u"ASCII")
+ self._debug['year']['end'] = self._io.pos()
+ self._debug['comment']['start'] = self._io.pos()
+ self.comment = self._io.read_bytes(30)
+ self._debug['comment']['end'] = self._io.pos()
+ self._debug['genre']['start'] = self._io.pos()
+ self.genre = KaitaiStream.resolve_enum(self._root.Id3V11Tag.GenreEnum, self._io.read_u1())
+ self._debug['genre']['end'] = self._io.pos()
+
+
+ @property
+ def id3v1_tag(self):
+ if hasattr(self, '_m_id3v1_tag'):
+ return self._m_id3v1_tag if hasattr(self, '_m_id3v1_tag') else None
+
+ _pos = self._io.pos()
+ self._io.seek((self._io.size() - 128))
+ self._debug['_m_id3v1_tag']['start'] = self._io.pos()
+ self._m_id3v1_tag = self._root.Id3V11Tag(self._io, self, self._root)
+ self._m_id3v1_tag._read()
+ self._debug['_m_id3v1_tag']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_id3v1_tag if hasattr(self, '_m_id3v1_tag') else None
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/id3v2_3.py b/python/examples/kaitai/kaitai_struct_formats/media/id3v2_3.py
new file mode 100644
index 00000000..cf6e5e1e
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/id3v2_3.py
@@ -0,0 +1,310 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class Id3v23(KaitaiStruct):
+ SEQ_FIELDS = ["tag"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['tag']['start'] = self._io.pos()
+ self.tag = self._root.Tag(self._io, self, self._root)
+ self.tag._read()
+ self._debug['tag']['end'] = self._io.pos()
+
+ class U1beSynchsafe(KaitaiStruct):
+ SEQ_FIELDS = ["padding", "value"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['padding']['start'] = self._io.pos()
+ self.padding = self._io.read_bits_int(1) != 0
+ self._debug['padding']['end'] = self._io.pos()
+ self._debug['value']['start'] = self._io.pos()
+ self.value = self._io.read_bits_int(7)
+ self._debug['value']['end'] = self._io.pos()
+
+
+ class U2beSynchsafe(KaitaiStruct):
+ SEQ_FIELDS = ["byte0", "byte1"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['byte0']['start'] = self._io.pos()
+ self.byte0 = self._root.U1beSynchsafe(self._io, self, self._root)
+ self.byte0._read()
+ self._debug['byte0']['end'] = self._io.pos()
+ self._debug['byte1']['start'] = self._io.pos()
+ self.byte1 = self._root.U1beSynchsafe(self._io, self, self._root)
+ self.byte1._read()
+ self._debug['byte1']['end'] = self._io.pos()
+
+ @property
+ def value(self):
+ if hasattr(self, '_m_value'):
+ return self._m_value if hasattr(self, '_m_value') else None
+
+ self._m_value = ((self.byte0.value << 7) | self.byte1.value)
+ return self._m_value if hasattr(self, '_m_value') else None
+
+
+ class Tag(KaitaiStruct):
+ SEQ_FIELDS = ["header", "header_ex", "frames", "padding"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['header']['start'] = self._io.pos()
+ self.header = self._root.Header(self._io, self, self._root)
+ self.header._read()
+ self._debug['header']['end'] = self._io.pos()
+ if self.header.flags.flag_headerex:
+ self._debug['header_ex']['start'] = self._io.pos()
+ self.header_ex = self._root.HeaderEx(self._io, self, self._root)
+ self.header_ex._read()
+ self._debug['header_ex']['end'] = self._io.pos()
+
+ self._debug['frames']['start'] = self._io.pos()
+ self.frames = []
+ i = 0
+ while True:
+ if not 'arr' in self._debug['frames']:
+ self._debug['frames']['arr'] = []
+ self._debug['frames']['arr'].append({'start': self._io.pos()})
+ _t_frames = self._root.Frame(self._io, self, self._root)
+ _t_frames._read()
+ _ = _t_frames
+ self.frames.append(_)
+ self._debug['frames']['arr'][len(self.frames) - 1]['end'] = self._io.pos()
+ if (((self._io.pos() + _.size) > self.header.size.value) or (_.is_invalid)) :
+ break
+ i += 1
+ self._debug['frames']['end'] = self._io.pos()
+ if self.header.flags.flag_headerex:
+ self._debug['padding']['start'] = self._io.pos()
+ self.padding = self._io.read_bytes((self.header_ex.padding_size - self._io.pos()))
+ self._debug['padding']['end'] = self._io.pos()
+
+
+
+ class U4beSynchsafe(KaitaiStruct):
+ SEQ_FIELDS = ["short0", "short1"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['short0']['start'] = self._io.pos()
+ self.short0 = self._root.U2beSynchsafe(self._io, self, self._root)
+ self.short0._read()
+ self._debug['short0']['end'] = self._io.pos()
+ self._debug['short1']['start'] = self._io.pos()
+ self.short1 = self._root.U2beSynchsafe(self._io, self, self._root)
+ self.short1._read()
+ self._debug['short1']['end'] = self._io.pos()
+
+ @property
+ def value(self):
+ if hasattr(self, '_m_value'):
+ return self._m_value if hasattr(self, '_m_value') else None
+
+ self._m_value = ((self.short0.value << 14) | self.short1.value)
+ return self._m_value if hasattr(self, '_m_value') else None
+
+
+ class Frame(KaitaiStruct):
+ SEQ_FIELDS = ["id", "size", "flags", "data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['id']['start'] = self._io.pos()
+ self.id = (self._io.read_bytes(4)).decode(u"ASCII")
+ self._debug['id']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._io.read_u4be()
+ self._debug['size']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._root.Frame.Flags(self._io, self, self._root)
+ self.flags._read()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['data']['start'] = self._io.pos()
+ self.data = self._io.read_bytes(self.size)
+ self._debug['data']['end'] = self._io.pos()
+
+ class Flags(KaitaiStruct):
+ SEQ_FIELDS = ["flag_discard_alter_tag", "flag_discard_alter_file", "flag_read_only", "reserved1", "flag_compressed", "flag_encrypted", "flag_grouping", "reserved2"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['flag_discard_alter_tag']['start'] = self._io.pos()
+ self.flag_discard_alter_tag = self._io.read_bits_int(1) != 0
+ self._debug['flag_discard_alter_tag']['end'] = self._io.pos()
+ self._debug['flag_discard_alter_file']['start'] = self._io.pos()
+ self.flag_discard_alter_file = self._io.read_bits_int(1) != 0
+ self._debug['flag_discard_alter_file']['end'] = self._io.pos()
+ self._debug['flag_read_only']['start'] = self._io.pos()
+ self.flag_read_only = self._io.read_bits_int(1) != 0
+ self._debug['flag_read_only']['end'] = self._io.pos()
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_bits_int(5)
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['flag_compressed']['start'] = self._io.pos()
+ self.flag_compressed = self._io.read_bits_int(1) != 0
+ self._debug['flag_compressed']['end'] = self._io.pos()
+ self._debug['flag_encrypted']['start'] = self._io.pos()
+ self.flag_encrypted = self._io.read_bits_int(1) != 0
+ self._debug['flag_encrypted']['end'] = self._io.pos()
+ self._debug['flag_grouping']['start'] = self._io.pos()
+ self.flag_grouping = self._io.read_bits_int(1) != 0
+ self._debug['flag_grouping']['end'] = self._io.pos()
+ self._debug['reserved2']['start'] = self._io.pos()
+ self.reserved2 = self._io.read_bits_int(5)
+ self._debug['reserved2']['end'] = self._io.pos()
+
+
+ @property
+ def is_invalid(self):
+ if hasattr(self, '_m_is_invalid'):
+ return self._m_is_invalid if hasattr(self, '_m_is_invalid') else None
+
+ self._m_is_invalid = self.id == u"\000\000\000\000"
+ return self._m_is_invalid if hasattr(self, '_m_is_invalid') else None
+
+
+ class HeaderEx(KaitaiStruct):
+ """ID3v2 extended header.
+
+ .. seealso::
+ Section 3.2. ID3v2 extended header
+ """
+ SEQ_FIELDS = ["size", "flags_ex", "padding_size", "crc"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._io.read_u4be()
+ self._debug['size']['end'] = self._io.pos()
+ self._debug['flags_ex']['start'] = self._io.pos()
+ self.flags_ex = self._root.HeaderEx.FlagsEx(self._io, self, self._root)
+ self.flags_ex._read()
+ self._debug['flags_ex']['end'] = self._io.pos()
+ self._debug['padding_size']['start'] = self._io.pos()
+ self.padding_size = self._io.read_u4be()
+ self._debug['padding_size']['end'] = self._io.pos()
+ if self.flags_ex.flag_crc:
+ self._debug['crc']['start'] = self._io.pos()
+ self.crc = self._io.read_u4be()
+ self._debug['crc']['end'] = self._io.pos()
+
+
+ class FlagsEx(KaitaiStruct):
+ SEQ_FIELDS = ["flag_crc", "reserved"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['flag_crc']['start'] = self._io.pos()
+ self.flag_crc = self._io.read_bits_int(1) != 0
+ self._debug['flag_crc']['end'] = self._io.pos()
+ self._debug['reserved']['start'] = self._io.pos()
+ self.reserved = self._io.read_bits_int(15)
+ self._debug['reserved']['end'] = self._io.pos()
+
+
+
+ class Header(KaitaiStruct):
+ """ID3v2 fixed header.
+
+ .. seealso::
+ Section 3.1. ID3v2 header
+ """
+ SEQ_FIELDS = ["magic", "version_major", "version_revision", "flags", "size"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x49\x44\x33")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['version_major']['start'] = self._io.pos()
+ self.version_major = self._io.read_u1()
+ self._debug['version_major']['end'] = self._io.pos()
+ self._debug['version_revision']['start'] = self._io.pos()
+ self.version_revision = self._io.read_u1()
+ self._debug['version_revision']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._root.Header.Flags(self._io, self, self._root)
+ self.flags._read()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._root.U4beSynchsafe(self._io, self, self._root)
+ self.size._read()
+ self._debug['size']['end'] = self._io.pos()
+
+ class Flags(KaitaiStruct):
+ SEQ_FIELDS = ["flag_unsynchronization", "flag_headerex", "flag_experimental", "reserved"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['flag_unsynchronization']['start'] = self._io.pos()
+ self.flag_unsynchronization = self._io.read_bits_int(1) != 0
+ self._debug['flag_unsynchronization']['end'] = self._io.pos()
+ self._debug['flag_headerex']['start'] = self._io.pos()
+ self.flag_headerex = self._io.read_bits_int(1) != 0
+ self._debug['flag_headerex']['end'] = self._io.pos()
+ self._debug['flag_experimental']['start'] = self._io.pos()
+ self.flag_experimental = self._io.read_bits_int(1) != 0
+ self._debug['flag_experimental']['end'] = self._io.pos()
+ self._debug['reserved']['start'] = self._io.pos()
+ self.reserved = self._io.read_bits_int(5)
+ self._debug['reserved']['end'] = self._io.pos()
+
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/id3v2_4.py b/python/examples/kaitai/kaitai_struct_formats/media/id3v2_4.py
new file mode 100644
index 00000000..0cdde2eb
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/id3v2_4.py
@@ -0,0 +1,411 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class Id3v24(KaitaiStruct):
+ SEQ_FIELDS = ["tag"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['tag']['start'] = self._io.pos()
+ self.tag = self._root.Tag(self._io, self, self._root)
+ self.tag._read()
+ self._debug['tag']['end'] = self._io.pos()
+
+ class U1beSynchsafe(KaitaiStruct):
+ SEQ_FIELDS = ["padding", "value"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['padding']['start'] = self._io.pos()
+ self.padding = self._io.read_bits_int(1) != 0
+ self._debug['padding']['end'] = self._io.pos()
+ self._debug['value']['start'] = self._io.pos()
+ self.value = self._io.read_bits_int(7)
+ self._debug['value']['end'] = self._io.pos()
+
+
+ class U2beSynchsafe(KaitaiStruct):
+ SEQ_FIELDS = ["byte0", "byte1"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['byte0']['start'] = self._io.pos()
+ self.byte0 = self._root.U1beSynchsafe(self._io, self, self._root)
+ self.byte0._read()
+ self._debug['byte0']['end'] = self._io.pos()
+ self._debug['byte1']['start'] = self._io.pos()
+ self.byte1 = self._root.U1beSynchsafe(self._io, self, self._root)
+ self.byte1._read()
+ self._debug['byte1']['end'] = self._io.pos()
+
+ @property
+ def value(self):
+ if hasattr(self, '_m_value'):
+ return self._m_value if hasattr(self, '_m_value') else None
+
+ self._m_value = ((self.byte0.value << 7) | self.byte1.value)
+ return self._m_value if hasattr(self, '_m_value') else None
+
+
+ class Tag(KaitaiStruct):
+ SEQ_FIELDS = ["header", "header_ex", "frames", "padding", "footer"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['header']['start'] = self._io.pos()
+ self.header = self._root.Header(self._io, self, self._root)
+ self.header._read()
+ self._debug['header']['end'] = self._io.pos()
+ if self.header.flags.flag_headerex:
+ self._debug['header_ex']['start'] = self._io.pos()
+ self.header_ex = self._root.HeaderEx(self._io, self, self._root)
+ self.header_ex._read()
+ self._debug['header_ex']['end'] = self._io.pos()
+
+ self._debug['frames']['start'] = self._io.pos()
+ self.frames = []
+ i = 0
+ while True:
+ if not 'arr' in self._debug['frames']:
+ self._debug['frames']['arr'] = []
+ self._debug['frames']['arr'].append({'start': self._io.pos()})
+ _t_frames = self._root.Frame(self._io, self, self._root)
+ _t_frames._read()
+ _ = _t_frames
+ self.frames.append(_)
+ self._debug['frames']['arr'][len(self.frames) - 1]['end'] = self._io.pos()
+ if (((self._io.pos() + _.size.value) > self.header.size.value) or (_.is_invalid)) :
+ break
+ i += 1
+ self._debug['frames']['end'] = self._io.pos()
+ if not (self.header.flags.flag_footer):
+ self._debug['padding']['start'] = self._io.pos()
+ self.padding = self._root.Padding(self._io, self, self._root)
+ self.padding._read()
+ self._debug['padding']['end'] = self._io.pos()
+
+ if self.header.flags.flag_footer:
+ self._debug['footer']['start'] = self._io.pos()
+ self.footer = self._root.Footer(self._io, self, self._root)
+ self.footer._read()
+ self._debug['footer']['end'] = self._io.pos()
+
+
+
+ class U4beSynchsafe(KaitaiStruct):
+ SEQ_FIELDS = ["short0", "short1"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['short0']['start'] = self._io.pos()
+ self.short0 = self._root.U2beSynchsafe(self._io, self, self._root)
+ self.short0._read()
+ self._debug['short0']['end'] = self._io.pos()
+ self._debug['short1']['start'] = self._io.pos()
+ self.short1 = self._root.U2beSynchsafe(self._io, self, self._root)
+ self.short1._read()
+ self._debug['short1']['end'] = self._io.pos()
+
+ @property
+ def value(self):
+ if hasattr(self, '_m_value'):
+ return self._m_value if hasattr(self, '_m_value') else None
+
+ self._m_value = ((self.short0.value << 14) | self.short1.value)
+ return self._m_value if hasattr(self, '_m_value') else None
+
+
+ class Frame(KaitaiStruct):
+ SEQ_FIELDS = ["id", "size", "flags_status", "flags_format", "data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['id']['start'] = self._io.pos()
+ self.id = (self._io.read_bytes(4)).decode(u"ASCII")
+ self._debug['id']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._root.U4beSynchsafe(self._io, self, self._root)
+ self.size._read()
+ self._debug['size']['end'] = self._io.pos()
+ self._debug['flags_status']['start'] = self._io.pos()
+ self.flags_status = self._root.Frame.FlagsStatus(self._io, self, self._root)
+ self.flags_status._read()
+ self._debug['flags_status']['end'] = self._io.pos()
+ self._debug['flags_format']['start'] = self._io.pos()
+ self.flags_format = self._root.Frame.FlagsFormat(self._io, self, self._root)
+ self.flags_format._read()
+ self._debug['flags_format']['end'] = self._io.pos()
+ self._debug['data']['start'] = self._io.pos()
+ self.data = self._io.read_bytes(self.size.value)
+ self._debug['data']['end'] = self._io.pos()
+
+ class FlagsStatus(KaitaiStruct):
+ SEQ_FIELDS = ["reserved1", "flag_discard_alter_tag", "flag_discard_alter_file", "flag_read_only", "reserved2"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_bits_int(1) != 0
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['flag_discard_alter_tag']['start'] = self._io.pos()
+ self.flag_discard_alter_tag = self._io.read_bits_int(1) != 0
+ self._debug['flag_discard_alter_tag']['end'] = self._io.pos()
+ self._debug['flag_discard_alter_file']['start'] = self._io.pos()
+ self.flag_discard_alter_file = self._io.read_bits_int(1) != 0
+ self._debug['flag_discard_alter_file']['end'] = self._io.pos()
+ self._debug['flag_read_only']['start'] = self._io.pos()
+ self.flag_read_only = self._io.read_bits_int(1) != 0
+ self._debug['flag_read_only']['end'] = self._io.pos()
+ self._debug['reserved2']['start'] = self._io.pos()
+ self.reserved2 = self._io.read_bits_int(4)
+ self._debug['reserved2']['end'] = self._io.pos()
+
+
+ class FlagsFormat(KaitaiStruct):
+ SEQ_FIELDS = ["reserved1", "flag_grouping", "reserved2", "flag_compressed", "flag_encrypted", "flag_unsynchronisated", "flag_indicator"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_bits_int(1) != 0
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['flag_grouping']['start'] = self._io.pos()
+ self.flag_grouping = self._io.read_bits_int(1) != 0
+ self._debug['flag_grouping']['end'] = self._io.pos()
+ self._debug['reserved2']['start'] = self._io.pos()
+ self.reserved2 = self._io.read_bits_int(2)
+ self._debug['reserved2']['end'] = self._io.pos()
+ self._debug['flag_compressed']['start'] = self._io.pos()
+ self.flag_compressed = self._io.read_bits_int(1) != 0
+ self._debug['flag_compressed']['end'] = self._io.pos()
+ self._debug['flag_encrypted']['start'] = self._io.pos()
+ self.flag_encrypted = self._io.read_bits_int(1) != 0
+ self._debug['flag_encrypted']['end'] = self._io.pos()
+ self._debug['flag_unsynchronisated']['start'] = self._io.pos()
+ self.flag_unsynchronisated = self._io.read_bits_int(1) != 0
+ self._debug['flag_unsynchronisated']['end'] = self._io.pos()
+ self._debug['flag_indicator']['start'] = self._io.pos()
+ self.flag_indicator = self._io.read_bits_int(1) != 0
+ self._debug['flag_indicator']['end'] = self._io.pos()
+
+
+ @property
+ def is_invalid(self):
+ if hasattr(self, '_m_is_invalid'):
+ return self._m_is_invalid if hasattr(self, '_m_is_invalid') else None
+
+ self._m_is_invalid = self.id == u"\000\000\000\000"
+ return self._m_is_invalid if hasattr(self, '_m_is_invalid') else None
+
+
+ class HeaderEx(KaitaiStruct):
+ SEQ_FIELDS = ["size", "flags_ex", "data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._root.U4beSynchsafe(self._io, self, self._root)
+ self.size._read()
+ self._debug['size']['end'] = self._io.pos()
+ self._debug['flags_ex']['start'] = self._io.pos()
+ self.flags_ex = self._root.HeaderEx.FlagsEx(self._io, self, self._root)
+ self.flags_ex._read()
+ self._debug['flags_ex']['end'] = self._io.pos()
+ self._debug['data']['start'] = self._io.pos()
+ self.data = self._io.read_bytes((self.size.value - 5))
+ self._debug['data']['end'] = self._io.pos()
+
+ class FlagsEx(KaitaiStruct):
+ SEQ_FIELDS = ["reserved1", "flag_update", "flag_crc", "flag_restrictions", "reserved2"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_bits_int(1) != 0
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['flag_update']['start'] = self._io.pos()
+ self.flag_update = self._io.read_bits_int(1) != 0
+ self._debug['flag_update']['end'] = self._io.pos()
+ self._debug['flag_crc']['start'] = self._io.pos()
+ self.flag_crc = self._io.read_bits_int(1) != 0
+ self._debug['flag_crc']['end'] = self._io.pos()
+ self._debug['flag_restrictions']['start'] = self._io.pos()
+ self.flag_restrictions = self._io.read_bits_int(1) != 0
+ self._debug['flag_restrictions']['end'] = self._io.pos()
+ self._debug['reserved2']['start'] = self._io.pos()
+ self.reserved2 = self._io.read_bits_int(4)
+ self._debug['reserved2']['end'] = self._io.pos()
+
+
+
+ class Header(KaitaiStruct):
+ SEQ_FIELDS = ["magic", "version_major", "version_revision", "flags", "size"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x49\x44\x33")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['version_major']['start'] = self._io.pos()
+ self.version_major = self._io.read_u1()
+ self._debug['version_major']['end'] = self._io.pos()
+ self._debug['version_revision']['start'] = self._io.pos()
+ self.version_revision = self._io.read_u1()
+ self._debug['version_revision']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._root.Header.Flags(self._io, self, self._root)
+ self.flags._read()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._root.U4beSynchsafe(self._io, self, self._root)
+ self.size._read()
+ self._debug['size']['end'] = self._io.pos()
+
+ class Flags(KaitaiStruct):
+ SEQ_FIELDS = ["flag_unsynchronization", "flag_headerex", "flag_experimental", "flag_footer", "reserved"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['flag_unsynchronization']['start'] = self._io.pos()
+ self.flag_unsynchronization = self._io.read_bits_int(1) != 0
+ self._debug['flag_unsynchronization']['end'] = self._io.pos()
+ self._debug['flag_headerex']['start'] = self._io.pos()
+ self.flag_headerex = self._io.read_bits_int(1) != 0
+ self._debug['flag_headerex']['end'] = self._io.pos()
+ self._debug['flag_experimental']['start'] = self._io.pos()
+ self.flag_experimental = self._io.read_bits_int(1) != 0
+ self._debug['flag_experimental']['end'] = self._io.pos()
+ self._debug['flag_footer']['start'] = self._io.pos()
+ self.flag_footer = self._io.read_bits_int(1) != 0
+ self._debug['flag_footer']['end'] = self._io.pos()
+ self._debug['reserved']['start'] = self._io.pos()
+ self.reserved = self._io.read_bits_int(4)
+ self._debug['reserved']['end'] = self._io.pos()
+
+
+
+ class Padding(KaitaiStruct):
+ SEQ_FIELDS = ["padding"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['padding']['start'] = self._io.pos()
+ self.padding = self._io.read_bytes((self._root.tag.header.size.value - self._io.pos()))
+ self._debug['padding']['end'] = self._io.pos()
+
+
+ class Footer(KaitaiStruct):
+ SEQ_FIELDS = ["magic", "version_major", "version_revision", "flags", "size"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x33\x44\x49")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['version_major']['start'] = self._io.pos()
+ self.version_major = self._io.read_u1()
+ self._debug['version_major']['end'] = self._io.pos()
+ self._debug['version_revision']['start'] = self._io.pos()
+ self.version_revision = self._io.read_u1()
+ self._debug['version_revision']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._root.Footer.Flags(self._io, self, self._root)
+ self.flags._read()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._root.U4beSynchsafe(self._io, self, self._root)
+ self.size._read()
+ self._debug['size']['end'] = self._io.pos()
+
+ class Flags(KaitaiStruct):
+ SEQ_FIELDS = ["flag_unsynchronization", "flag_headerex", "flag_experimental", "flag_footer", "reserved"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['flag_unsynchronization']['start'] = self._io.pos()
+ self.flag_unsynchronization = self._io.read_bits_int(1) != 0
+ self._debug['flag_unsynchronization']['end'] = self._io.pos()
+ self._debug['flag_headerex']['start'] = self._io.pos()
+ self.flag_headerex = self._io.read_bits_int(1) != 0
+ self._debug['flag_headerex']['end'] = self._io.pos()
+ self._debug['flag_experimental']['start'] = self._io.pos()
+ self.flag_experimental = self._io.read_bits_int(1) != 0
+ self._debug['flag_experimental']['end'] = self._io.pos()
+ self._debug['flag_footer']['start'] = self._io.pos()
+ self.flag_footer = self._io.read_bits_int(1) != 0
+ self._debug['flag_footer']['end'] = self._io.pos()
+ self._debug['reserved']['start'] = self._io.pos()
+ self.reserved = self._io.read_bits_int(4)
+ self._debug['reserved']['end'] = self._io.pos()
+
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/magicavoxel_vox.py b/python/examples/kaitai/kaitai_struct_formats/media/magicavoxel_vox.py
new file mode 100644
index 00000000..24841007
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/magicavoxel_vox.py
@@ -0,0 +1,385 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+from enum import Enum
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class MagicavoxelVox(KaitaiStruct):
+ """
+ .. seealso::
+ Format Description - https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt
+ """
+
+ class ChunkType(Enum):
+ main = 1296124238
+ matt = 1296127060
+ pack = 1346454347
+ rgba = 1380401729
+ size = 1397316165
+ xyzi = 1482250825
+
+ class MaterialType(Enum):
+ diffuse = 0
+ metal = 1
+ glass = 2
+ emissive = 3
+
+ class PropertyBitsType(Enum):
+ plastic = 1
+ roughness = 2
+ specular = 4
+ ior = 8
+ attenuation = 16
+ power = 32
+ glow = 64
+ is_total_power = 128
+ SEQ_FIELDS = ["magic", "version", "main"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x56\x4F\x58\x20")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['version']['start'] = self._io.pos()
+ self.version = self._io.read_u4le()
+ self._debug['version']['end'] = self._io.pos()
+ self._debug['main']['start'] = self._io.pos()
+ self.main = self._root.Chunk(self._io, self, self._root)
+ self.main._read()
+ self._debug['main']['end'] = self._io.pos()
+
+ class Chunk(KaitaiStruct):
+ SEQ_FIELDS = ["chunk_id", "num_bytes_of_chunk_content", "num_bytes_of_children_chunks", "chunk_content", "children_chunks"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['chunk_id']['start'] = self._io.pos()
+ self.chunk_id = KaitaiStream.resolve_enum(self._root.ChunkType, self._io.read_u4be())
+ self._debug['chunk_id']['end'] = self._io.pos()
+ self._debug['num_bytes_of_chunk_content']['start'] = self._io.pos()
+ self.num_bytes_of_chunk_content = self._io.read_u4le()
+ self._debug['num_bytes_of_chunk_content']['end'] = self._io.pos()
+ self._debug['num_bytes_of_children_chunks']['start'] = self._io.pos()
+ self.num_bytes_of_children_chunks = self._io.read_u4le()
+ self._debug['num_bytes_of_children_chunks']['end'] = self._io.pos()
+ if self.num_bytes_of_chunk_content != 0:
+ self._debug['chunk_content']['start'] = self._io.pos()
+ _on = self.chunk_id
+ if _on == self._root.ChunkType.size:
+ self._raw_chunk_content = self._io.read_bytes(self.num_bytes_of_chunk_content)
+ io = KaitaiStream(BytesIO(self._raw_chunk_content))
+ self.chunk_content = self._root.Size(io, self, self._root)
+ self.chunk_content._read()
+ elif _on == self._root.ChunkType.matt:
+ self._raw_chunk_content = self._io.read_bytes(self.num_bytes_of_chunk_content)
+ io = KaitaiStream(BytesIO(self._raw_chunk_content))
+ self.chunk_content = self._root.Matt(io, self, self._root)
+ self.chunk_content._read()
+ elif _on == self._root.ChunkType.rgba:
+ self._raw_chunk_content = self._io.read_bytes(self.num_bytes_of_chunk_content)
+ io = KaitaiStream(BytesIO(self._raw_chunk_content))
+ self.chunk_content = self._root.Rgba(io, self, self._root)
+ self.chunk_content._read()
+ elif _on == self._root.ChunkType.xyzi:
+ self._raw_chunk_content = self._io.read_bytes(self.num_bytes_of_chunk_content)
+ io = KaitaiStream(BytesIO(self._raw_chunk_content))
+ self.chunk_content = self._root.Xyzi(io, self, self._root)
+ self.chunk_content._read()
+ elif _on == self._root.ChunkType.pack:
+ self._raw_chunk_content = self._io.read_bytes(self.num_bytes_of_chunk_content)
+ io = KaitaiStream(BytesIO(self._raw_chunk_content))
+ self.chunk_content = self._root.Pack(io, self, self._root)
+ self.chunk_content._read()
+ else:
+ self.chunk_content = self._io.read_bytes(self.num_bytes_of_chunk_content)
+ self._debug['chunk_content']['end'] = self._io.pos()
+
+ if self.num_bytes_of_children_chunks != 0:
+ self._debug['children_chunks']['start'] = self._io.pos()
+ self.children_chunks = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['children_chunks']:
+ self._debug['children_chunks']['arr'] = []
+ self._debug['children_chunks']['arr'].append({'start': self._io.pos()})
+ _t_children_chunks = self._root.Chunk(self._io, self, self._root)
+ _t_children_chunks._read()
+ self.children_chunks.append(_t_children_chunks)
+ self._debug['children_chunks']['arr'][len(self.children_chunks) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['children_chunks']['end'] = self._io.pos()
+
+
+
+ class Size(KaitaiStruct):
+ SEQ_FIELDS = ["size_x", "size_y", "size_z"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['size_x']['start'] = self._io.pos()
+ self.size_x = self._io.read_u4le()
+ self._debug['size_x']['end'] = self._io.pos()
+ self._debug['size_y']['start'] = self._io.pos()
+ self.size_y = self._io.read_u4le()
+ self._debug['size_y']['end'] = self._io.pos()
+ self._debug['size_z']['start'] = self._io.pos()
+ self.size_z = self._io.read_u4le()
+ self._debug['size_z']['end'] = self._io.pos()
+
+
+ class Rgba(KaitaiStruct):
+ SEQ_FIELDS = ["colors"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['colors']['start'] = self._io.pos()
+ self.colors = [None] * (256)
+ for i in range(256):
+ if not 'arr' in self._debug['colors']:
+ self._debug['colors']['arr'] = []
+ self._debug['colors']['arr'].append({'start': self._io.pos()})
+ _t_colors = self._root.Color(self._io, self, self._root)
+ _t_colors._read()
+ self.colors[i] = _t_colors
+ self._debug['colors']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['colors']['end'] = self._io.pos()
+
+
+ class Pack(KaitaiStruct):
+ SEQ_FIELDS = ["num_models"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['num_models']['start'] = self._io.pos()
+ self.num_models = self._io.read_u4le()
+ self._debug['num_models']['end'] = self._io.pos()
+
+
+ class Matt(KaitaiStruct):
+ SEQ_FIELDS = ["id", "material_type", "material_weight", "property_bits", "plastic", "roughness", "specular", "ior", "attenuation", "power", "glow", "is_total_power"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['id']['start'] = self._io.pos()
+ self.id = self._io.read_u4le()
+ self._debug['id']['end'] = self._io.pos()
+ self._debug['material_type']['start'] = self._io.pos()
+ self.material_type = KaitaiStream.resolve_enum(self._root.MaterialType, self._io.read_u4le())
+ self._debug['material_type']['end'] = self._io.pos()
+ self._debug['material_weight']['start'] = self._io.pos()
+ self.material_weight = self._io.read_f4le()
+ self._debug['material_weight']['end'] = self._io.pos()
+ self._debug['property_bits']['start'] = self._io.pos()
+ self.property_bits = self._io.read_u4le()
+ self._debug['property_bits']['end'] = self._io.pos()
+ if self.has_plastic:
+ self._debug['plastic']['start'] = self._io.pos()
+ self.plastic = self._io.read_f4le()
+ self._debug['plastic']['end'] = self._io.pos()
+
+ if self.has_roughness:
+ self._debug['roughness']['start'] = self._io.pos()
+ self.roughness = self._io.read_f4le()
+ self._debug['roughness']['end'] = self._io.pos()
+
+ if self.has_specular:
+ self._debug['specular']['start'] = self._io.pos()
+ self.specular = self._io.read_f4le()
+ self._debug['specular']['end'] = self._io.pos()
+
+ if self.has_ior:
+ self._debug['ior']['start'] = self._io.pos()
+ self.ior = self._io.read_f4le()
+ self._debug['ior']['end'] = self._io.pos()
+
+ if self.has_attenuation:
+ self._debug['attenuation']['start'] = self._io.pos()
+ self.attenuation = self._io.read_f4le()
+ self._debug['attenuation']['end'] = self._io.pos()
+
+ if self.has_power:
+ self._debug['power']['start'] = self._io.pos()
+ self.power = self._io.read_f4le()
+ self._debug['power']['end'] = self._io.pos()
+
+ if self.has_glow:
+ self._debug['glow']['start'] = self._io.pos()
+ self.glow = self._io.read_f4le()
+ self._debug['glow']['end'] = self._io.pos()
+
+ if self.has_is_total_power:
+ self._debug['is_total_power']['start'] = self._io.pos()
+ self.is_total_power = self._io.read_f4le()
+ self._debug['is_total_power']['end'] = self._io.pos()
+
+
+ @property
+ def has_is_total_power(self):
+ if hasattr(self, '_m_has_is_total_power'):
+ return self._m_has_is_total_power if hasattr(self, '_m_has_is_total_power') else None
+
+ self._m_has_is_total_power = (self.property_bits & 128) != 0
+ return self._m_has_is_total_power if hasattr(self, '_m_has_is_total_power') else None
+
+ @property
+ def has_plastic(self):
+ if hasattr(self, '_m_has_plastic'):
+ return self._m_has_plastic if hasattr(self, '_m_has_plastic') else None
+
+ self._m_has_plastic = (self.property_bits & 1) != 0
+ return self._m_has_plastic if hasattr(self, '_m_has_plastic') else None
+
+ @property
+ def has_attenuation(self):
+ if hasattr(self, '_m_has_attenuation'):
+ return self._m_has_attenuation if hasattr(self, '_m_has_attenuation') else None
+
+ self._m_has_attenuation = (self.property_bits & 16) != 0
+ return self._m_has_attenuation if hasattr(self, '_m_has_attenuation') else None
+
+ @property
+ def has_power(self):
+ if hasattr(self, '_m_has_power'):
+ return self._m_has_power if hasattr(self, '_m_has_power') else None
+
+ self._m_has_power = (self.property_bits & 32) != 0
+ return self._m_has_power if hasattr(self, '_m_has_power') else None
+
+ @property
+ def has_roughness(self):
+ if hasattr(self, '_m_has_roughness'):
+ return self._m_has_roughness if hasattr(self, '_m_has_roughness') else None
+
+ self._m_has_roughness = (self.property_bits & 2) != 0
+ return self._m_has_roughness if hasattr(self, '_m_has_roughness') else None
+
+ @property
+ def has_specular(self):
+ if hasattr(self, '_m_has_specular'):
+ return self._m_has_specular if hasattr(self, '_m_has_specular') else None
+
+ self._m_has_specular = (self.property_bits & 4) != 0
+ return self._m_has_specular if hasattr(self, '_m_has_specular') else None
+
+ @property
+ def has_ior(self):
+ if hasattr(self, '_m_has_ior'):
+ return self._m_has_ior if hasattr(self, '_m_has_ior') else None
+
+ self._m_has_ior = (self.property_bits & 8) != 0
+ return self._m_has_ior if hasattr(self, '_m_has_ior') else None
+
+ @property
+ def has_glow(self):
+ if hasattr(self, '_m_has_glow'):
+ return self._m_has_glow if hasattr(self, '_m_has_glow') else None
+
+ self._m_has_glow = (self.property_bits & 64) != 0
+ return self._m_has_glow if hasattr(self, '_m_has_glow') else None
+
+
+ class Xyzi(KaitaiStruct):
+ SEQ_FIELDS = ["num_voxels", "voxels"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['num_voxels']['start'] = self._io.pos()
+ self.num_voxels = self._io.read_u4le()
+ self._debug['num_voxels']['end'] = self._io.pos()
+ self._debug['voxels']['start'] = self._io.pos()
+ self.voxels = [None] * (self.num_voxels)
+ for i in range(self.num_voxels):
+ if not 'arr' in self._debug['voxels']:
+ self._debug['voxels']['arr'] = []
+ self._debug['voxels']['arr'].append({'start': self._io.pos()})
+ _t_voxels = self._root.Voxel(self._io, self, self._root)
+ _t_voxels._read()
+ self.voxels[i] = _t_voxels
+ self._debug['voxels']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['voxels']['end'] = self._io.pos()
+
+
+ class Color(KaitaiStruct):
+ SEQ_FIELDS = ["r", "g", "b", "a"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['r']['start'] = self._io.pos()
+ self.r = self._io.read_u1()
+ self._debug['r']['end'] = self._io.pos()
+ self._debug['g']['start'] = self._io.pos()
+ self.g = self._io.read_u1()
+ self._debug['g']['end'] = self._io.pos()
+ self._debug['b']['start'] = self._io.pos()
+ self.b = self._io.read_u1()
+ self._debug['b']['end'] = self._io.pos()
+ self._debug['a']['start'] = self._io.pos()
+ self.a = self._io.read_u1()
+ self._debug['a']['end'] = self._io.pos()
+
+
+ class Voxel(KaitaiStruct):
+ SEQ_FIELDS = ["x", "y", "z", "color_index"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['x']['start'] = self._io.pos()
+ self.x = self._io.read_u1()
+ self._debug['x']['end'] = self._io.pos()
+ self._debug['y']['start'] = self._io.pos()
+ self.y = self._io.read_u1()
+ self._debug['y']['end'] = self._io.pos()
+ self._debug['z']['start'] = self._io.pos()
+ self.z = self._io.read_u1()
+ self._debug['z']['end'] = self._io.pos()
+ self._debug['color_index']['start'] = self._io.pos()
+ self.color_index = self._io.read_u1()
+ self._debug['color_index']['end'] = self._io.pos()
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/ogg.py b/python/examples/kaitai/kaitai_struct_formats/media/ogg.py
new file mode 100644
index 00000000..415d6bdf
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/ogg.py
@@ -0,0 +1,114 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class Ogg(KaitaiStruct):
+ """Ogg is a popular media container format, which provides basic
+ streaming / buffering mechanisms and is content-agnostic. Most
+ popular codecs that are used within Ogg streams are Vorbis (thus
+ making Ogg/Vorbis streams) and Theora (Ogg/Theora).
+
+ Ogg stream is a sequence Ogg pages. They can be read sequentially,
+ or one can jump into arbitrary stream location and scan for "OggS"
+ sync code to find the beginning of a new Ogg page and continue
+ decoding the stream contents from that one.
+ """
+ SEQ_FIELDS = ["pages"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['pages']['start'] = self._io.pos()
+ self.pages = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['pages']:
+ self._debug['pages']['arr'] = []
+ self._debug['pages']['arr'].append({'start': self._io.pos()})
+ _t_pages = self._root.Page(self._io, self, self._root)
+ _t_pages._read()
+ self.pages.append(_t_pages)
+ self._debug['pages']['arr'][len(self.pages) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['pages']['end'] = self._io.pos()
+
+ class Page(KaitaiStruct):
+ """Ogg page is a basic unit of data in an Ogg bitstream, usually
+ it's around 4-8 KB, with a maximum size of 65307 bytes.
+ """
+ SEQ_FIELDS = ["sync_code", "version", "reserved1", "is_end_of_stream", "is_beginning_of_stream", "is_continuation", "granule_pos", "bitstream_serial", "page_seq_num", "crc32", "num_segments", "len_segments", "segments"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['sync_code']['start'] = self._io.pos()
+ self.sync_code = self._io.ensure_fixed_contents(b"\x4F\x67\x67\x53")
+ self._debug['sync_code']['end'] = self._io.pos()
+ self._debug['version']['start'] = self._io.pos()
+ self.version = self._io.ensure_fixed_contents(b"\x00")
+ self._debug['version']['end'] = self._io.pos()
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_bits_int(5)
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['is_end_of_stream']['start'] = self._io.pos()
+ self.is_end_of_stream = self._io.read_bits_int(1) != 0
+ self._debug['is_end_of_stream']['end'] = self._io.pos()
+ self._debug['is_beginning_of_stream']['start'] = self._io.pos()
+ self.is_beginning_of_stream = self._io.read_bits_int(1) != 0
+ self._debug['is_beginning_of_stream']['end'] = self._io.pos()
+ self._debug['is_continuation']['start'] = self._io.pos()
+ self.is_continuation = self._io.read_bits_int(1) != 0
+ self._debug['is_continuation']['end'] = self._io.pos()
+ self._io.align_to_byte()
+ self._debug['granule_pos']['start'] = self._io.pos()
+ self.granule_pos = self._io.read_u8le()
+ self._debug['granule_pos']['end'] = self._io.pos()
+ self._debug['bitstream_serial']['start'] = self._io.pos()
+ self.bitstream_serial = self._io.read_u4le()
+ self._debug['bitstream_serial']['end'] = self._io.pos()
+ self._debug['page_seq_num']['start'] = self._io.pos()
+ self.page_seq_num = self._io.read_u4le()
+ self._debug['page_seq_num']['end'] = self._io.pos()
+ self._debug['crc32']['start'] = self._io.pos()
+ self.crc32 = self._io.read_u4le()
+ self._debug['crc32']['end'] = self._io.pos()
+ self._debug['num_segments']['start'] = self._io.pos()
+ self.num_segments = self._io.read_u1()
+ self._debug['num_segments']['end'] = self._io.pos()
+ self._debug['len_segments']['start'] = self._io.pos()
+ self.len_segments = [None] * (self.num_segments)
+ for i in range(self.num_segments):
+ if not 'arr' in self._debug['len_segments']:
+ self._debug['len_segments']['arr'] = []
+ self._debug['len_segments']['arr'].append({'start': self._io.pos()})
+ self.len_segments[i] = self._io.read_u1()
+ self._debug['len_segments']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['len_segments']['end'] = self._io.pos()
+ self._debug['segments']['start'] = self._io.pos()
+ self.segments = [None] * (self.num_segments)
+ for i in range(self.num_segments):
+ if not 'arr' in self._debug['segments']:
+ self._debug['segments']['arr'] = []
+ self._debug['segments']['arr'].append({'start': self._io.pos()})
+ self.segments[i] = self._io.read_bytes(self.len_segments[i])
+ self._debug['segments']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['segments']['end'] = self._io.pos()
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/quicktime_mov.py b/python/examples/kaitai/kaitai_struct_formats/media/quicktime_mov.py
new file mode 100644
index 00000000..eb2f5c9b
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/quicktime_mov.py
@@ -0,0 +1,479 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+from enum import Enum
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class QuicktimeMov(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html#//apple_ref/doc/uid/TP40000939-CH203-BBCGDDDF
+ """
+
+ class AtomType(Enum):
+ xtra = 1484026465
+ dinf = 1684631142
+ dref = 1685218662
+ edts = 1701082227
+ elst = 1701606260
+ free = 1718773093
+ ftyp = 1718909296
+ hdlr = 1751411826
+ iods = 1768907891
+ mdat = 1835295092
+ mdhd = 1835296868
+ mdia = 1835297121
+ meta = 1835365473
+ minf = 1835626086
+ moof = 1836019558
+ moov = 1836019574
+ mvhd = 1836476516
+ smhd = 1936549988
+ stbl = 1937007212
+ stco = 1937007471
+ stsc = 1937011555
+ stsd = 1937011556
+ stsz = 1937011578
+ stts = 1937011827
+ tkhd = 1953196132
+ traf = 1953653094
+ trak = 1953653099
+ tref = 1953654118
+ udta = 1969517665
+ vmhd = 1986881636
+
+ class Brand(Enum):
+ x_3g2a = 862401121
+ x_3ge6 = 862414134
+ x_3ge9 = 862414137
+ x_3gf9 = 862414393
+ x_3gg6 = 862414646
+ x_3gg9 = 862414649
+ x_3gh9 = 862414905
+ x_3gm9 = 862416185
+ x_3gp4 = 862416948
+ x_3gp5 = 862416949
+ x_3gp6 = 862416950
+ x_3gp7 = 862416951
+ x_3gp8 = 862416952
+ x_3gp9 = 862416953
+ x_3gr6 = 862417462
+ x_3gr9 = 862417465
+ x_3gs6 = 862417718
+ x_3gs9 = 862417721
+ x_3gt9 = 862417977
+ arri = 1095914057
+ caep = 1128351056
+ cdes = 1128555891
+ j2p0 = 1244811312
+ j2p1 = 1244811313
+ lcag = 1279476039
+ m4a = 1295270176
+ m4b = 1295270432
+ m4p = 1295274016
+ m4v = 1295275552
+ mfsm = 1296454477
+ mgsv = 1296520022
+ mppi = 1297109065
+ msnv = 1297305174
+ ross = 1380930387
+ seau = 1397047637
+ sebk = 1397047883
+ xavc = 1480676931
+ avc1 = 1635148593
+ bbxm = 1650620525
+ caqv = 1667330422
+ ccff = 1667458662
+ da0a = 1684090977
+ da0b = 1684090978
+ da1a = 1684091233
+ da1b = 1684091234
+ da2a = 1684091489
+ da2b = 1684091490
+ da3a = 1684091745
+ da3b = 1684091746
+ dash = 1684108136
+ dby1 = 1684175153
+ dmb1 = 1684890161
+ dsms = 1685286259
+ dv1a = 1685467489
+ dv1b = 1685467490
+ dv2a = 1685467745
+ dv2b = 1685467746
+ dv3a = 1685468001
+ dv3b = 1685468002
+ dvr1 = 1685484081
+ dvt1 = 1685484593
+ dxo = 1685614368
+ emsg = 1701671783
+ ifrm = 1768321645
+ isc2 = 1769169714
+ iso2 = 1769172786
+ iso3 = 1769172787
+ iso4 = 1769172788
+ iso5 = 1769172789
+ iso6 = 1769172790
+ isom = 1769172845
+ jp2 = 1785737760
+ jpm = 1785752864
+ jpsi = 1785754473
+ jpx = 1785755680
+ jpxb = 1785755746
+ lmsg = 1819112295
+ mj2s = 1835676275
+ mjp2 = 1835692082
+ mp21 = 1836069425
+ mp41 = 1836069937
+ mp42 = 1836069938
+ mp71 = 1836070705
+ msdh = 1836278888
+ msix = 1836280184
+ niko = 1852402543
+ odcf = 1868850022
+ opf2 = 1869637170
+ opx2 = 1869641778
+ pana = 1885433441
+ piff = 1885955686
+ pnvi = 1886287465
+ qt = 1903435808
+ risx = 1919513464
+ sdv = 1935963680
+ senv = 1936027254
+ sims = 1936289139
+ sisx = 1936290680
+ ssss = 1936946035
+ uvvu = 1970697845
+ SEQ_FIELDS = ["atoms"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['atoms']['start'] = self._io.pos()
+ self.atoms = self._root.AtomList(self._io, self, self._root)
+ self.atoms._read()
+ self._debug['atoms']['end'] = self._io.pos()
+
+ class MvhdBody(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-BBCGFGJG
+ """
+ SEQ_FIELDS = ["version", "flags", "creation_time", "modification_time", "time_scale", "duration", "preferred_rate", "preferred_volume", "reserved1", "matrix", "preview_time", "preview_duration", "poster_time", "selection_time", "selection_duration", "current_time", "next_track_id"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['version']['start'] = self._io.pos()
+ self.version = self._io.read_u1()
+ self._debug['version']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_bytes(3)
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['creation_time']['start'] = self._io.pos()
+ self.creation_time = self._io.read_u4be()
+ self._debug['creation_time']['end'] = self._io.pos()
+ self._debug['modification_time']['start'] = self._io.pos()
+ self.modification_time = self._io.read_u4be()
+ self._debug['modification_time']['end'] = self._io.pos()
+ self._debug['time_scale']['start'] = self._io.pos()
+ self.time_scale = self._io.read_u4be()
+ self._debug['time_scale']['end'] = self._io.pos()
+ self._debug['duration']['start'] = self._io.pos()
+ self.duration = self._io.read_u4be()
+ self._debug['duration']['end'] = self._io.pos()
+ self._debug['preferred_rate']['start'] = self._io.pos()
+ self.preferred_rate = self._root.Fixed32(self._io, self, self._root)
+ self.preferred_rate._read()
+ self._debug['preferred_rate']['end'] = self._io.pos()
+ self._debug['preferred_volume']['start'] = self._io.pos()
+ self.preferred_volume = self._root.Fixed16(self._io, self, self._root)
+ self.preferred_volume._read()
+ self._debug['preferred_volume']['end'] = self._io.pos()
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_bytes(10)
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['matrix']['start'] = self._io.pos()
+ self.matrix = self._io.read_bytes(36)
+ self._debug['matrix']['end'] = self._io.pos()
+ self._debug['preview_time']['start'] = self._io.pos()
+ self.preview_time = self._io.read_u4be()
+ self._debug['preview_time']['end'] = self._io.pos()
+ self._debug['preview_duration']['start'] = self._io.pos()
+ self.preview_duration = self._io.read_u4be()
+ self._debug['preview_duration']['end'] = self._io.pos()
+ self._debug['poster_time']['start'] = self._io.pos()
+ self.poster_time = self._io.read_u4be()
+ self._debug['poster_time']['end'] = self._io.pos()
+ self._debug['selection_time']['start'] = self._io.pos()
+ self.selection_time = self._io.read_u4be()
+ self._debug['selection_time']['end'] = self._io.pos()
+ self._debug['selection_duration']['start'] = self._io.pos()
+ self.selection_duration = self._io.read_u4be()
+ self._debug['selection_duration']['end'] = self._io.pos()
+ self._debug['current_time']['start'] = self._io.pos()
+ self.current_time = self._io.read_u4be()
+ self._debug['current_time']['end'] = self._io.pos()
+ self._debug['next_track_id']['start'] = self._io.pos()
+ self.next_track_id = self._io.read_u4be()
+ self._debug['next_track_id']['end'] = self._io.pos()
+
+
+ class FtypBody(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html#//apple_ref/doc/uid/TP40000939-CH203-CJBCBIFF
+ """
+ SEQ_FIELDS = ["major_brand", "minor_version", "compatible_brands"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['major_brand']['start'] = self._io.pos()
+ self.major_brand = KaitaiStream.resolve_enum(self._root.Brand, self._io.read_u4be())
+ self._debug['major_brand']['end'] = self._io.pos()
+ self._debug['minor_version']['start'] = self._io.pos()
+ self.minor_version = self._io.read_bytes(4)
+ self._debug['minor_version']['end'] = self._io.pos()
+ self._debug['compatible_brands']['start'] = self._io.pos()
+ self.compatible_brands = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['compatible_brands']:
+ self._debug['compatible_brands']['arr'] = []
+ self._debug['compatible_brands']['arr'].append({'start': self._io.pos()})
+ self.compatible_brands.append(KaitaiStream.resolve_enum(self._root.Brand, self._io.read_u4be()))
+ self._debug['compatible_brands']['arr'][len(self.compatible_brands) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['compatible_brands']['end'] = self._io.pos()
+
+
+ class Fixed32(KaitaiStruct):
+ """Fixed-point 32-bit number."""
+ SEQ_FIELDS = ["int_part", "frac_part"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['int_part']['start'] = self._io.pos()
+ self.int_part = self._io.read_s2be()
+ self._debug['int_part']['end'] = self._io.pos()
+ self._debug['frac_part']['start'] = self._io.pos()
+ self.frac_part = self._io.read_u2be()
+ self._debug['frac_part']['end'] = self._io.pos()
+
+
+ class Fixed16(KaitaiStruct):
+ """Fixed-point 16-bit number."""
+ SEQ_FIELDS = ["int_part", "frac_part"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['int_part']['start'] = self._io.pos()
+ self.int_part = self._io.read_s1()
+ self._debug['int_part']['end'] = self._io.pos()
+ self._debug['frac_part']['start'] = self._io.pos()
+ self.frac_part = self._io.read_u1()
+ self._debug['frac_part']['end'] = self._io.pos()
+
+
+ class Atom(KaitaiStruct):
+ SEQ_FIELDS = ["len32", "atom_type", "len64", "body"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['len32']['start'] = self._io.pos()
+ self.len32 = self._io.read_u4be()
+ self._debug['len32']['end'] = self._io.pos()
+ self._debug['atom_type']['start'] = self._io.pos()
+ self.atom_type = KaitaiStream.resolve_enum(self._root.AtomType, self._io.read_u4be())
+ self._debug['atom_type']['end'] = self._io.pos()
+ if self.len32 == 1:
+ self._debug['len64']['start'] = self._io.pos()
+ self.len64 = self._io.read_u8be()
+ self._debug['len64']['end'] = self._io.pos()
+
+ self._debug['body']['start'] = self._io.pos()
+ _on = self.atom_type
+ if _on == self._root.AtomType.moof:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.AtomList(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.AtomType.tkhd:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.TkhdBody(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.AtomType.stbl:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.AtomList(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.AtomType.traf:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.AtomList(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.AtomType.minf:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.AtomList(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.AtomType.trak:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.AtomList(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.AtomType.moov:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.AtomList(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.AtomType.mdia:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.AtomList(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.AtomType.dinf:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.AtomList(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.AtomType.mvhd:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.MvhdBody(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.AtomType.ftyp:
+ self._raw_body = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.FtypBody(io, self, self._root)
+ self.body._read()
+ else:
+ self.body = self._io.read_bytes(self.len)
+ self._debug['body']['end'] = self._io.pos()
+
+ @property
+ def len(self):
+ if hasattr(self, '_m_len'):
+ return self._m_len if hasattr(self, '_m_len') else None
+
+ self._m_len = ((self._io.size() - 8) if self.len32 == 0 else ((self.len64 - 16) if self.len32 == 1 else (self.len32 - 8)))
+ return self._m_len if hasattr(self, '_m_len') else None
+
+
+ class TkhdBody(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25550
+ """
+ SEQ_FIELDS = ["version", "flags", "creation_time", "modification_time", "track_id", "reserved1", "duration", "reserved2", "layer", "alternative_group", "volume", "reserved3", "matrix", "width", "height"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['version']['start'] = self._io.pos()
+ self.version = self._io.read_u1()
+ self._debug['version']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_bytes(3)
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['creation_time']['start'] = self._io.pos()
+ self.creation_time = self._io.read_u4be()
+ self._debug['creation_time']['end'] = self._io.pos()
+ self._debug['modification_time']['start'] = self._io.pos()
+ self.modification_time = self._io.read_u4be()
+ self._debug['modification_time']['end'] = self._io.pos()
+ self._debug['track_id']['start'] = self._io.pos()
+ self.track_id = self._io.read_u4be()
+ self._debug['track_id']['end'] = self._io.pos()
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_bytes(4)
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['duration']['start'] = self._io.pos()
+ self.duration = self._io.read_u4be()
+ self._debug['duration']['end'] = self._io.pos()
+ self._debug['reserved2']['start'] = self._io.pos()
+ self.reserved2 = self._io.read_bytes(8)
+ self._debug['reserved2']['end'] = self._io.pos()
+ self._debug['layer']['start'] = self._io.pos()
+ self.layer = self._io.read_u2be()
+ self._debug['layer']['end'] = self._io.pos()
+ self._debug['alternative_group']['start'] = self._io.pos()
+ self.alternative_group = self._io.read_u2be()
+ self._debug['alternative_group']['end'] = self._io.pos()
+ self._debug['volume']['start'] = self._io.pos()
+ self.volume = self._io.read_u2be()
+ self._debug['volume']['end'] = self._io.pos()
+ self._debug['reserved3']['start'] = self._io.pos()
+ self.reserved3 = self._io.read_u2be()
+ self._debug['reserved3']['end'] = self._io.pos()
+ self._debug['matrix']['start'] = self._io.pos()
+ self.matrix = self._io.read_bytes(36)
+ self._debug['matrix']['end'] = self._io.pos()
+ self._debug['width']['start'] = self._io.pos()
+ self.width = self._root.Fixed32(self._io, self, self._root)
+ self.width._read()
+ self._debug['width']['end'] = self._io.pos()
+ self._debug['height']['start'] = self._io.pos()
+ self.height = self._root.Fixed32(self._io, self, self._root)
+ self.height._read()
+ self._debug['height']['end'] = self._io.pos()
+
+
+ class AtomList(KaitaiStruct):
+ SEQ_FIELDS = ["items"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['items']['start'] = self._io.pos()
+ self.items = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['items']:
+ self._debug['items']['arr'] = []
+ self._debug['items']['arr'].append({'start': self._io.pos()})
+ _t_items = self._root.Atom(self._io, self, self._root)
+ _t_items._read()
+ self.items.append(_t_items)
+ self._debug['items']['arr'][len(self.items) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['items']['end'] = self._io.pos()
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/standard_midi_file.py b/python/examples/kaitai/kaitai_struct_formats/media/standard_midi_file.py
new file mode 100644
index 00000000..6cd7c246
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/standard_midi_file.py
@@ -0,0 +1,386 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+from enum import Enum
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+from vlq_base128_be import VlqBase128Be
+class StandardMidiFile(KaitaiStruct):
+ """Standard MIDI file, typically knows just as "MID", is a standard way
+ to serialize series of MIDI events, which is a protocol used in many
+ music synthesizers to transfer music data: notes being played,
+ effects being applied, etc.
+
+ Internally, file consists of a header and series of tracks, every
+ track listing MIDI events with certain header designating time these
+ events are happening.
+
+ NOTE: Rarely, MIDI files employ certain stateful compression scheme
+ to avoid storing certain elements of further elements, instead
+ reusing them from events which happened earlier in the
+ stream. Kaitai Struct (as of v0.9) is currently unable to parse
+ these, but files employing this mechanism are relatively rare.
+ """
+ SEQ_FIELDS = ["hdr", "tracks"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['hdr']['start'] = self._io.pos()
+ self.hdr = self._root.Header(self._io, self, self._root)
+ self.hdr._read()
+ self._debug['hdr']['end'] = self._io.pos()
+ self._debug['tracks']['start'] = self._io.pos()
+ self.tracks = [None] * (self.hdr.num_tracks)
+ for i in range(self.hdr.num_tracks):
+ if not 'arr' in self._debug['tracks']:
+ self._debug['tracks']['arr'] = []
+ self._debug['tracks']['arr'].append({'start': self._io.pos()})
+ _t_tracks = self._root.Track(self._io, self, self._root)
+ _t_tracks._read()
+ self.tracks[i] = _t_tracks
+ self._debug['tracks']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['tracks']['end'] = self._io.pos()
+
+ class TrackEvents(KaitaiStruct):
+ SEQ_FIELDS = ["event"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['event']['start'] = self._io.pos()
+ self.event = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['event']:
+ self._debug['event']['arr'] = []
+ self._debug['event']['arr'].append({'start': self._io.pos()})
+ _t_event = self._root.TrackEvent(self._io, self, self._root)
+ _t_event._read()
+ self.event.append(_t_event)
+ self._debug['event']['arr'][len(self.event) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['event']['end'] = self._io.pos()
+
+
+ class TrackEvent(KaitaiStruct):
+ SEQ_FIELDS = ["v_time", "event_header", "meta_event_body", "sysex_body", "event_body"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['v_time']['start'] = self._io.pos()
+ self.v_time = VlqBase128Be(self._io)
+ self.v_time._read()
+ self._debug['v_time']['end'] = self._io.pos()
+ self._debug['event_header']['start'] = self._io.pos()
+ self.event_header = self._io.read_u1()
+ self._debug['event_header']['end'] = self._io.pos()
+ if self.event_header == 255:
+ self._debug['meta_event_body']['start'] = self._io.pos()
+ self.meta_event_body = self._root.MetaEventBody(self._io, self, self._root)
+ self.meta_event_body._read()
+ self._debug['meta_event_body']['end'] = self._io.pos()
+
+ if self.event_header == 240:
+ self._debug['sysex_body']['start'] = self._io.pos()
+ self.sysex_body = self._root.SysexEventBody(self._io, self, self._root)
+ self.sysex_body._read()
+ self._debug['sysex_body']['end'] = self._io.pos()
+
+ self._debug['event_body']['start'] = self._io.pos()
+ _on = self.event_type
+ if _on == 224:
+ self.event_body = self._root.PitchBendEvent(self._io, self, self._root)
+ self.event_body._read()
+ elif _on == 144:
+ self.event_body = self._root.NoteOnEvent(self._io, self, self._root)
+ self.event_body._read()
+ elif _on == 208:
+ self.event_body = self._root.ChannelPressureEvent(self._io, self, self._root)
+ self.event_body._read()
+ elif _on == 192:
+ self.event_body = self._root.ProgramChangeEvent(self._io, self, self._root)
+ self.event_body._read()
+ elif _on == 160:
+ self.event_body = self._root.PolyphonicPressureEvent(self._io, self, self._root)
+ self.event_body._read()
+ elif _on == 176:
+ self.event_body = self._root.ControllerEvent(self._io, self, self._root)
+ self.event_body._read()
+ elif _on == 128:
+ self.event_body = self._root.NoteOffEvent(self._io, self, self._root)
+ self.event_body._read()
+ self._debug['event_body']['end'] = self._io.pos()
+
+ @property
+ def event_type(self):
+ if hasattr(self, '_m_event_type'):
+ return self._m_event_type if hasattr(self, '_m_event_type') else None
+
+ self._m_event_type = (self.event_header & 240)
+ return self._m_event_type if hasattr(self, '_m_event_type') else None
+
+ @property
+ def channel(self):
+ if hasattr(self, '_m_channel'):
+ return self._m_channel if hasattr(self, '_m_channel') else None
+
+ if self.event_type != 240:
+ self._m_channel = (self.event_header & 15)
+
+ return self._m_channel if hasattr(self, '_m_channel') else None
+
+
+ class PitchBendEvent(KaitaiStruct):
+ SEQ_FIELDS = ["b1", "b2"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['b1']['start'] = self._io.pos()
+ self.b1 = self._io.read_u1()
+ self._debug['b1']['end'] = self._io.pos()
+ self._debug['b2']['start'] = self._io.pos()
+ self.b2 = self._io.read_u1()
+ self._debug['b2']['end'] = self._io.pos()
+
+ @property
+ def bend_value(self):
+ if hasattr(self, '_m_bend_value'):
+ return self._m_bend_value if hasattr(self, '_m_bend_value') else None
+
+ self._m_bend_value = (((self.b2 << 7) + self.b1) - 16384)
+ return self._m_bend_value if hasattr(self, '_m_bend_value') else None
+
+ @property
+ def adj_bend_value(self):
+ if hasattr(self, '_m_adj_bend_value'):
+ return self._m_adj_bend_value if hasattr(self, '_m_adj_bend_value') else None
+
+ self._m_adj_bend_value = (self.bend_value - 16384)
+ return self._m_adj_bend_value if hasattr(self, '_m_adj_bend_value') else None
+
+
+ class ProgramChangeEvent(KaitaiStruct):
+ SEQ_FIELDS = ["program"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['program']['start'] = self._io.pos()
+ self.program = self._io.read_u1()
+ self._debug['program']['end'] = self._io.pos()
+
+
+ class NoteOnEvent(KaitaiStruct):
+ SEQ_FIELDS = ["note", "velocity"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['note']['start'] = self._io.pos()
+ self.note = self._io.read_u1()
+ self._debug['note']['end'] = self._io.pos()
+ self._debug['velocity']['start'] = self._io.pos()
+ self.velocity = self._io.read_u1()
+ self._debug['velocity']['end'] = self._io.pos()
+
+
+ class PolyphonicPressureEvent(KaitaiStruct):
+ SEQ_FIELDS = ["note", "pressure"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['note']['start'] = self._io.pos()
+ self.note = self._io.read_u1()
+ self._debug['note']['end'] = self._io.pos()
+ self._debug['pressure']['start'] = self._io.pos()
+ self.pressure = self._io.read_u1()
+ self._debug['pressure']['end'] = self._io.pos()
+
+
+ class Track(KaitaiStruct):
+ SEQ_FIELDS = ["magic", "len_events", "events"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x4D\x54\x72\x6B")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['len_events']['start'] = self._io.pos()
+ self.len_events = self._io.read_u4be()
+ self._debug['len_events']['end'] = self._io.pos()
+ self._debug['events']['start'] = self._io.pos()
+ self._raw_events = self._io.read_bytes(self.len_events)
+ io = KaitaiStream(BytesIO(self._raw_events))
+ self.events = self._root.TrackEvents(io, self, self._root)
+ self.events._read()
+ self._debug['events']['end'] = self._io.pos()
+
+
+ class MetaEventBody(KaitaiStruct):
+
+ class MetaTypeEnum(Enum):
+ sequence_number = 0
+ text_event = 1
+ copyright = 2
+ sequence_track_name = 3
+ instrument_name = 4
+ lyric_text = 5
+ marker_text = 6
+ cue_point = 7
+ midi_channel_prefix_assignment = 32
+ end_of_track = 47
+ tempo = 81
+ smpte_offset = 84
+ time_signature = 88
+ key_signature = 89
+ sequencer_specific_event = 127
+ SEQ_FIELDS = ["meta_type", "len", "body"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['meta_type']['start'] = self._io.pos()
+ self.meta_type = KaitaiStream.resolve_enum(self._root.MetaEventBody.MetaTypeEnum, self._io.read_u1())
+ self._debug['meta_type']['end'] = self._io.pos()
+ self._debug['len']['start'] = self._io.pos()
+ self.len = VlqBase128Be(self._io)
+ self.len._read()
+ self._debug['len']['end'] = self._io.pos()
+ self._debug['body']['start'] = self._io.pos()
+ self.body = self._io.read_bytes(self.len.value)
+ self._debug['body']['end'] = self._io.pos()
+
+
+ class ControllerEvent(KaitaiStruct):
+ SEQ_FIELDS = ["controller", "value"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['controller']['start'] = self._io.pos()
+ self.controller = self._io.read_u1()
+ self._debug['controller']['end'] = self._io.pos()
+ self._debug['value']['start'] = self._io.pos()
+ self.value = self._io.read_u1()
+ self._debug['value']['end'] = self._io.pos()
+
+
+ class Header(KaitaiStruct):
+ SEQ_FIELDS = ["magic", "len_header", "format", "num_tracks", "division"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x4D\x54\x68\x64")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['len_header']['start'] = self._io.pos()
+ self.len_header = self._io.read_u4be()
+ self._debug['len_header']['end'] = self._io.pos()
+ self._debug['format']['start'] = self._io.pos()
+ self.format = self._io.read_u2be()
+ self._debug['format']['end'] = self._io.pos()
+ self._debug['num_tracks']['start'] = self._io.pos()
+ self.num_tracks = self._io.read_u2be()
+ self._debug['num_tracks']['end'] = self._io.pos()
+ self._debug['division']['start'] = self._io.pos()
+ self.division = self._io.read_s2be()
+ self._debug['division']['end'] = self._io.pos()
+
+
+ class SysexEventBody(KaitaiStruct):
+ SEQ_FIELDS = ["len", "data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['len']['start'] = self._io.pos()
+ self.len = VlqBase128Be(self._io)
+ self.len._read()
+ self._debug['len']['end'] = self._io.pos()
+ self._debug['data']['start'] = self._io.pos()
+ self.data = self._io.read_bytes(self.len.value)
+ self._debug['data']['end'] = self._io.pos()
+
+
+ class NoteOffEvent(KaitaiStruct):
+ SEQ_FIELDS = ["note", "velocity"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['note']['start'] = self._io.pos()
+ self.note = self._io.read_u1()
+ self._debug['note']['end'] = self._io.pos()
+ self._debug['velocity']['start'] = self._io.pos()
+ self.velocity = self._io.read_u1()
+ self._debug['velocity']['end'] = self._io.pos()
+
+
+ class ChannelPressureEvent(KaitaiStruct):
+ SEQ_FIELDS = ["pressure"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['pressure']['start'] = self._io.pos()
+ self.pressure = self._io.read_u1()
+ self._debug['pressure']['end'] = self._io.pos()
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/stl.py b/python/examples/kaitai/kaitai_struct_formats/media/stl.py
new file mode 100644
index 00000000..381f3319
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/stl.py
@@ -0,0 +1,108 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class Stl(KaitaiStruct):
+ """STL files are used to represent simple 3D models, defined using
+ triangular 3D faces.
+
+ Initially it was introduced as native format for 3D Systems
+ Stereolithography CAD system, but due to its extreme simplicity, it
+ was adopted by a wide range of 3D modelling, CAD, rapid prototyping
+ and 3D printing applications as the simplest 3D model exchange
+ format.
+
+ STL is extremely bare-bones format: there are no complex headers, no
+ texture / color support, no units specifications, no distinct vertex
+ arrays. Whole model is specified as a collection of triangular
+ faces.
+
+ There are two versions of the format (text and binary), this spec
+ describes binary version.
+ """
+ SEQ_FIELDS = ["header", "num_triangles", "triangles"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['header']['start'] = self._io.pos()
+ self.header = self._io.read_bytes(80)
+ self._debug['header']['end'] = self._io.pos()
+ self._debug['num_triangles']['start'] = self._io.pos()
+ self.num_triangles = self._io.read_u4le()
+ self._debug['num_triangles']['end'] = self._io.pos()
+ self._debug['triangles']['start'] = self._io.pos()
+ self.triangles = [None] * (self.num_triangles)
+ for i in range(self.num_triangles):
+ if not 'arr' in self._debug['triangles']:
+ self._debug['triangles']['arr'] = []
+ self._debug['triangles']['arr'].append({'start': self._io.pos()})
+ _t_triangles = self._root.Triangle(self._io, self, self._root)
+ _t_triangles._read()
+ self.triangles[i] = _t_triangles
+ self._debug['triangles']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['triangles']['end'] = self._io.pos()
+
+ class Triangle(KaitaiStruct):
+ """Each STL triangle is defined by its 3 points in 3D space and a
+ normal vector, which is generally used to determine where is
+ "inside" and "outside" of the model.
+ """
+ SEQ_FIELDS = ["normal", "vertices"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['normal']['start'] = self._io.pos()
+ self.normal = self._root.Vec3d(self._io, self, self._root)
+ self.normal._read()
+ self._debug['normal']['end'] = self._io.pos()
+ self._debug['vertices']['start'] = self._io.pos()
+ self.vertices = [None] * (3)
+ for i in range(3):
+ if not 'arr' in self._debug['vertices']:
+ self._debug['vertices']['arr'] = []
+ self._debug['vertices']['arr'].append({'start': self._io.pos()})
+ _t_vertices = self._root.Vec3d(self._io, self, self._root)
+ _t_vertices._read()
+ self.vertices[i] = _t_vertices
+ self._debug['vertices']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['vertices']['end'] = self._io.pos()
+
+
+ class Vec3d(KaitaiStruct):
+ SEQ_FIELDS = ["x", "y", "z"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['x']['start'] = self._io.pos()
+ self.x = self._io.read_f4le()
+ self._debug['x']['end'] = self._io.pos()
+ self._debug['y']['start'] = self._io.pos()
+ self.y = self._io.read_f4le()
+ self._debug['y']['end'] = self._io.pos()
+ self._debug['z']['start'] = self._io.pos()
+ self.z = self._io.read_f4le()
+ self._debug['z']['end'] = self._io.pos()
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/__init__.py b/python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/__init__.py
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/fasttracker_xm_module.py b/python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/fasttracker_xm_module.py
new file mode 100644
index 00000000..fad6c2cc
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/fasttracker_xm_module.py
@@ -0,0 +1,554 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ....kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+from enum import Enum
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class FasttrackerXmModule(KaitaiStruct):
+ """XM (standing for eXtended Module) is a popular module music file
+ format, that was introduced in 1994 in FastTracker2 by Triton demo
+ group. Akin to MOD files, it bundles both digital samples
+ (instruments) and instructions on which note to play at what time
+ (patterns), which provides good audio quality with relatively small
+ file size. Audio is reproducible without relying on the sound of
+ particular hardware samplers or synths.
+
+ .. seealso::
+ Source - http://sid.ethz.ch/debian/milkytracker/milkytracker-0.90.85%2Bdfsg/resources/reference/xm-form.txt
+ ftp://ftp.modland.com/pub/documents/format_documentation/FastTracker%202%20v2.04%20(.xm).html
+ """
+ SEQ_FIELDS = ["preheader", "header", "patterns", "instruments"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['preheader']['start'] = self._io.pos()
+ self.preheader = self._root.Preheader(self._io, self, self._root)
+ self.preheader._read()
+ self._debug['preheader']['end'] = self._io.pos()
+ self._debug['header']['start'] = self._io.pos()
+ self._raw_header = self._io.read_bytes((self.preheader.header_size - 4))
+ io = KaitaiStream(BytesIO(self._raw_header))
+ self.header = self._root.Header(io, self, self._root)
+ self.header._read()
+ self._debug['header']['end'] = self._io.pos()
+ self._debug['patterns']['start'] = self._io.pos()
+ self.patterns = [None] * (self.header.num_patterns)
+ for i in range(self.header.num_patterns):
+ if not 'arr' in self._debug['patterns']:
+ self._debug['patterns']['arr'] = []
+ self._debug['patterns']['arr'].append({'start': self._io.pos()})
+ _t_patterns = self._root.Pattern(self._io, self, self._root)
+ _t_patterns._read()
+ self.patterns[i] = _t_patterns
+ self._debug['patterns']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['patterns']['end'] = self._io.pos()
+ self._debug['instruments']['start'] = self._io.pos()
+ self.instruments = [None] * (self.header.num_instruments)
+ for i in range(self.header.num_instruments):
+ if not 'arr' in self._debug['instruments']:
+ self._debug['instruments']['arr'] = []
+ self._debug['instruments']['arr'].append({'start': self._io.pos()})
+ _t_instruments = self._root.Instrument(self._io, self, self._root)
+ _t_instruments._read()
+ self.instruments[i] = _t_instruments
+ self._debug['instruments']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['instruments']['end'] = self._io.pos()
+
+ class Preheader(KaitaiStruct):
+ SEQ_FIELDS = ["signature0", "module_name", "signature1", "tracker_name", "version_number", "header_size"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['signature0']['start'] = self._io.pos()
+ self.signature0 = self._io.ensure_fixed_contents(b"\x45\x78\x74\x65\x6E\x64\x65\x64\x20\x4D\x6F\x64\x75\x6C\x65\x3A\x20")
+ self._debug['signature0']['end'] = self._io.pos()
+ self._debug['module_name']['start'] = self._io.pos()
+ self.module_name = (KaitaiStream.bytes_terminate(self._io.read_bytes(20), 0, False)).decode(u"utf-8")
+ self._debug['module_name']['end'] = self._io.pos()
+ self._debug['signature1']['start'] = self._io.pos()
+ self.signature1 = self._io.ensure_fixed_contents(b"\x1A")
+ self._debug['signature1']['end'] = self._io.pos()
+ self._debug['tracker_name']['start'] = self._io.pos()
+ self.tracker_name = (KaitaiStream.bytes_terminate(self._io.read_bytes(20), 0, False)).decode(u"utf-8")
+ self._debug['tracker_name']['end'] = self._io.pos()
+ self._debug['version_number']['start'] = self._io.pos()
+ self.version_number = self._root.Preheader.Version(self._io, self, self._root)
+ self.version_number._read()
+ self._debug['version_number']['end'] = self._io.pos()
+ self._debug['header_size']['start'] = self._io.pos()
+ self.header_size = self._io.read_u4le()
+ self._debug['header_size']['end'] = self._io.pos()
+
+ class Version(KaitaiStruct):
+ SEQ_FIELDS = ["minor", "major"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['minor']['start'] = self._io.pos()
+ self.minor = self._io.read_u1()
+ self._debug['minor']['end'] = self._io.pos()
+ self._debug['major']['start'] = self._io.pos()
+ self.major = self._io.read_u1()
+ self._debug['major']['end'] = self._io.pos()
+
+ @property
+ def value(self):
+ if hasattr(self, '_m_value'):
+ return self._m_value if hasattr(self, '_m_value') else None
+
+ self._m_value = ((self.major << 8) | self.minor)
+ return self._m_value if hasattr(self, '_m_value') else None
+
+
+
+ class Pattern(KaitaiStruct):
+ SEQ_FIELDS = ["header", "packed_data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['header']['start'] = self._io.pos()
+ self.header = self._root.Pattern.Header(self._io, self, self._root)
+ self.header._read()
+ self._debug['header']['end'] = self._io.pos()
+ self._debug['packed_data']['start'] = self._io.pos()
+ self.packed_data = self._io.read_bytes(self.header.main.len_packed_pattern)
+ self._debug['packed_data']['end'] = self._io.pos()
+
+ class Header(KaitaiStruct):
+ SEQ_FIELDS = ["header_length", "main"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['header_length']['start'] = self._io.pos()
+ self.header_length = self._io.read_u4le()
+ self._debug['header_length']['end'] = self._io.pos()
+ self._debug['main']['start'] = self._io.pos()
+ self._raw_main = self._io.read_bytes((self.header_length - 4))
+ io = KaitaiStream(BytesIO(self._raw_main))
+ self.main = self._root.Pattern.Header.HeaderMain(io, self, self._root)
+ self.main._read()
+ self._debug['main']['end'] = self._io.pos()
+
+ class HeaderMain(KaitaiStruct):
+ SEQ_FIELDS = ["packing_type", "num_rows_raw", "len_packed_pattern"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['packing_type']['start'] = self._io.pos()
+ self.packing_type = self._io.read_u1()
+ self._debug['packing_type']['end'] = self._io.pos()
+ self._debug['num_rows_raw']['start'] = self._io.pos()
+ _on = self._root.preheader.version_number.value
+ if _on == 258:
+ self.num_rows_raw = self._io.read_u1()
+ else:
+ self.num_rows_raw = self._io.read_u2le()
+ self._debug['num_rows_raw']['end'] = self._io.pos()
+ self._debug['len_packed_pattern']['start'] = self._io.pos()
+ self.len_packed_pattern = self._io.read_u2le()
+ self._debug['len_packed_pattern']['end'] = self._io.pos()
+
+ @property
+ def num_rows(self):
+ if hasattr(self, '_m_num_rows'):
+ return self._m_num_rows if hasattr(self, '_m_num_rows') else None
+
+ self._m_num_rows = (self.num_rows_raw + (1 if self._root.preheader.version_number.value == 258 else 0))
+ return self._m_num_rows if hasattr(self, '_m_num_rows') else None
+
+
+
+
+ class Flags(KaitaiStruct):
+ SEQ_FIELDS = ["reserved", "freq_table_type"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['reserved']['start'] = self._io.pos()
+ self.reserved = self._io.read_bits_int(15)
+ self._debug['reserved']['end'] = self._io.pos()
+ self._debug['freq_table_type']['start'] = self._io.pos()
+ self.freq_table_type = self._io.read_bits_int(1) != 0
+ self._debug['freq_table_type']['end'] = self._io.pos()
+
+
+ class Header(KaitaiStruct):
+ SEQ_FIELDS = ["song_length", "restart_position", "num_channels", "num_patterns", "num_instruments", "flags", "default_tempo", "default_bpm", "pattern_order_table"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['song_length']['start'] = self._io.pos()
+ self.song_length = self._io.read_u2le()
+ self._debug['song_length']['end'] = self._io.pos()
+ self._debug['restart_position']['start'] = self._io.pos()
+ self.restart_position = self._io.read_u2le()
+ self._debug['restart_position']['end'] = self._io.pos()
+ self._debug['num_channels']['start'] = self._io.pos()
+ self.num_channels = self._io.read_u2le()
+ self._debug['num_channels']['end'] = self._io.pos()
+ self._debug['num_patterns']['start'] = self._io.pos()
+ self.num_patterns = self._io.read_u2le()
+ self._debug['num_patterns']['end'] = self._io.pos()
+ self._debug['num_instruments']['start'] = self._io.pos()
+ self.num_instruments = self._io.read_u2le()
+ self._debug['num_instruments']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._root.Flags(self._io, self, self._root)
+ self.flags._read()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['default_tempo']['start'] = self._io.pos()
+ self.default_tempo = self._io.read_u2le()
+ self._debug['default_tempo']['end'] = self._io.pos()
+ self._debug['default_bpm']['start'] = self._io.pos()
+ self.default_bpm = self._io.read_u2le()
+ self._debug['default_bpm']['end'] = self._io.pos()
+ self._debug['pattern_order_table']['start'] = self._io.pos()
+ self.pattern_order_table = [None] * (256)
+ for i in range(256):
+ if not 'arr' in self._debug['pattern_order_table']:
+ self._debug['pattern_order_table']['arr'] = []
+ self._debug['pattern_order_table']['arr'].append({'start': self._io.pos()})
+ self.pattern_order_table[i] = self._io.read_u1()
+ self._debug['pattern_order_table']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['pattern_order_table']['end'] = self._io.pos()
+
+
+ class Instrument(KaitaiStruct):
+ """XM's notion of "instrument" typically constitutes of a
+ instrument metadata and one or several samples. Metadata
+ includes:
+
+ * instrument's name
+ * instruction of which sample to use for which note
+ * volume and panning envelopes and looping instructions
+ * vibrato settings
+ """
+ SEQ_FIELDS = ["header_size", "header", "samples_headers", "samples"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['header_size']['start'] = self._io.pos()
+ self.header_size = self._io.read_u4le()
+ self._debug['header_size']['end'] = self._io.pos()
+ self._debug['header']['start'] = self._io.pos()
+ self._raw_header = self._io.read_bytes((self.header_size - 4))
+ io = KaitaiStream(BytesIO(self._raw_header))
+ self.header = self._root.Instrument.Header(io, self, self._root)
+ self.header._read()
+ self._debug['header']['end'] = self._io.pos()
+ self._debug['samples_headers']['start'] = self._io.pos()
+ self.samples_headers = [None] * (self.header.num_samples)
+ for i in range(self.header.num_samples):
+ if not 'arr' in self._debug['samples_headers']:
+ self._debug['samples_headers']['arr'] = []
+ self._debug['samples_headers']['arr'].append({'start': self._io.pos()})
+ _t_samples_headers = self._root.Instrument.SampleHeader(self._io, self, self._root)
+ _t_samples_headers._read()
+ self.samples_headers[i] = _t_samples_headers
+ self._debug['samples_headers']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['samples_headers']['end'] = self._io.pos()
+ self._debug['samples']['start'] = self._io.pos()
+ self.samples = [None] * (self.header.num_samples)
+ for i in range(self.header.num_samples):
+ if not 'arr' in self._debug['samples']:
+ self._debug['samples']['arr'] = []
+ self._debug['samples']['arr'].append({'start': self._io.pos()})
+ _t_samples = self._root.Instrument.SamplesData(self.samples_headers[i], self._io, self, self._root)
+ _t_samples._read()
+ self.samples[i] = _t_samples
+ self._debug['samples']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['samples']['end'] = self._io.pos()
+
+ class Header(KaitaiStruct):
+ SEQ_FIELDS = ["name", "type", "num_samples", "extra_header"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['name']['start'] = self._io.pos()
+ self.name = (KaitaiStream.bytes_terminate(self._io.read_bytes(22), 0, False)).decode(u"utf-8")
+ self._debug['name']['end'] = self._io.pos()
+ self._debug['type']['start'] = self._io.pos()
+ self.type = self._io.read_u1()
+ self._debug['type']['end'] = self._io.pos()
+ self._debug['num_samples']['start'] = self._io.pos()
+ self.num_samples = self._io.read_u2le()
+ self._debug['num_samples']['end'] = self._io.pos()
+ if self.num_samples > 0:
+ self._debug['extra_header']['start'] = self._io.pos()
+ self.extra_header = self._root.Instrument.ExtraHeader(self._io, self, self._root)
+ self.extra_header._read()
+ self._debug['extra_header']['end'] = self._io.pos()
+
+
+
+ class ExtraHeader(KaitaiStruct):
+
+ class Type(Enum):
+ true = 0
+ sustain = 1
+ loop = 2
+ SEQ_FIELDS = ["len_sample_header", "idx_sample_per_note", "volume_points", "panning_points", "num_volume_points", "num_panning_points", "volume_sustain_point", "volume_loop_start_point", "volume_loop_end_point", "panning_sustain_point", "panning_loop_start_point", "panning_loop_end_point", "volume_type", "panning_type", "vibrato_type", "vibrato_sweep", "vibrato_depth", "vibrato_rate", "volume_fadeout", "reserved"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['len_sample_header']['start'] = self._io.pos()
+ self.len_sample_header = self._io.read_u4le()
+ self._debug['len_sample_header']['end'] = self._io.pos()
+ self._debug['idx_sample_per_note']['start'] = self._io.pos()
+ self.idx_sample_per_note = [None] * (96)
+ for i in range(96):
+ if not 'arr' in self._debug['idx_sample_per_note']:
+ self._debug['idx_sample_per_note']['arr'] = []
+ self._debug['idx_sample_per_note']['arr'].append({'start': self._io.pos()})
+ self.idx_sample_per_note[i] = self._io.read_u1()
+ self._debug['idx_sample_per_note']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['idx_sample_per_note']['end'] = self._io.pos()
+ self._debug['volume_points']['start'] = self._io.pos()
+ self.volume_points = [None] * (12)
+ for i in range(12):
+ if not 'arr' in self._debug['volume_points']:
+ self._debug['volume_points']['arr'] = []
+ self._debug['volume_points']['arr'].append({'start': self._io.pos()})
+ _t_volume_points = self._root.Instrument.ExtraHeader.EnvelopePoint(self._io, self, self._root)
+ _t_volume_points._read()
+ self.volume_points[i] = _t_volume_points
+ self._debug['volume_points']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['volume_points']['end'] = self._io.pos()
+ self._debug['panning_points']['start'] = self._io.pos()
+ self.panning_points = [None] * (12)
+ for i in range(12):
+ if not 'arr' in self._debug['panning_points']:
+ self._debug['panning_points']['arr'] = []
+ self._debug['panning_points']['arr'].append({'start': self._io.pos()})
+ _t_panning_points = self._root.Instrument.ExtraHeader.EnvelopePoint(self._io, self, self._root)
+ _t_panning_points._read()
+ self.panning_points[i] = _t_panning_points
+ self._debug['panning_points']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['panning_points']['end'] = self._io.pos()
+ self._debug['num_volume_points']['start'] = self._io.pos()
+ self.num_volume_points = self._io.read_u1()
+ self._debug['num_volume_points']['end'] = self._io.pos()
+ self._debug['num_panning_points']['start'] = self._io.pos()
+ self.num_panning_points = self._io.read_u1()
+ self._debug['num_panning_points']['end'] = self._io.pos()
+ self._debug['volume_sustain_point']['start'] = self._io.pos()
+ self.volume_sustain_point = self._io.read_u1()
+ self._debug['volume_sustain_point']['end'] = self._io.pos()
+ self._debug['volume_loop_start_point']['start'] = self._io.pos()
+ self.volume_loop_start_point = self._io.read_u1()
+ self._debug['volume_loop_start_point']['end'] = self._io.pos()
+ self._debug['volume_loop_end_point']['start'] = self._io.pos()
+ self.volume_loop_end_point = self._io.read_u1()
+ self._debug['volume_loop_end_point']['end'] = self._io.pos()
+ self._debug['panning_sustain_point']['start'] = self._io.pos()
+ self.panning_sustain_point = self._io.read_u1()
+ self._debug['panning_sustain_point']['end'] = self._io.pos()
+ self._debug['panning_loop_start_point']['start'] = self._io.pos()
+ self.panning_loop_start_point = self._io.read_u1()
+ self._debug['panning_loop_start_point']['end'] = self._io.pos()
+ self._debug['panning_loop_end_point']['start'] = self._io.pos()
+ self.panning_loop_end_point = self._io.read_u1()
+ self._debug['panning_loop_end_point']['end'] = self._io.pos()
+ self._debug['volume_type']['start'] = self._io.pos()
+ self.volume_type = KaitaiStream.resolve_enum(self._root.Instrument.ExtraHeader.Type, self._io.read_u1())
+ self._debug['volume_type']['end'] = self._io.pos()
+ self._debug['panning_type']['start'] = self._io.pos()
+ self.panning_type = KaitaiStream.resolve_enum(self._root.Instrument.ExtraHeader.Type, self._io.read_u1())
+ self._debug['panning_type']['end'] = self._io.pos()
+ self._debug['vibrato_type']['start'] = self._io.pos()
+ self.vibrato_type = self._io.read_u1()
+ self._debug['vibrato_type']['end'] = self._io.pos()
+ self._debug['vibrato_sweep']['start'] = self._io.pos()
+ self.vibrato_sweep = self._io.read_u1()
+ self._debug['vibrato_sweep']['end'] = self._io.pos()
+ self._debug['vibrato_depth']['start'] = self._io.pos()
+ self.vibrato_depth = self._io.read_u1()
+ self._debug['vibrato_depth']['end'] = self._io.pos()
+ self._debug['vibrato_rate']['start'] = self._io.pos()
+ self.vibrato_rate = self._io.read_u1()
+ self._debug['vibrato_rate']['end'] = self._io.pos()
+ self._debug['volume_fadeout']['start'] = self._io.pos()
+ self.volume_fadeout = self._io.read_u2le()
+ self._debug['volume_fadeout']['end'] = self._io.pos()
+ self._debug['reserved']['start'] = self._io.pos()
+ self.reserved = self._io.read_u2le()
+ self._debug['reserved']['end'] = self._io.pos()
+
+ class EnvelopePoint(KaitaiStruct):
+ """Envelope frame-counters work in range 0..FFFFh (0..65535 dec).
+ BUT! FT2 only itself supports only range 0..FFh (0..255 dec).
+ Some other trackers (like SoundTracker for Unix), however, can use the full range 0..FFFF, so it should be supported.
+ !!TIP: This is also a good way to detect if the module has been made with FT2 or not. (In case the tracker name is brain- deadly left unchanged!)
+ Of course it does not help if all instruments have the values inside FT2 supported range.
+ The value-field of the envelope point is ranged between 00..3Fh (0..64 dec).
+ """
+ SEQ_FIELDS = ["x", "y"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['x']['start'] = self._io.pos()
+ self.x = self._io.read_u2le()
+ self._debug['x']['end'] = self._io.pos()
+ self._debug['y']['start'] = self._io.pos()
+ self.y = self._io.read_u2le()
+ self._debug['y']['end'] = self._io.pos()
+
+
+
+ class SamplesData(KaitaiStruct):
+ """The saved data uses simple delta-encoding to achieve better compression ratios (when compressed with pkzip, etc.)
+ Pseudocode for converting the delta-coded data to normal data,
+ old = 0;
+ for i in range(data_len):
+ new = sample[i] + old;
+ sample[i] = new;
+ old = new;
+ """
+ SEQ_FIELDS = ["data"]
+ def __init__(self, header, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.header = header
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['data']['start'] = self._io.pos()
+ self.data = self._io.read_bytes((self.header.sample_length * (2 if self.header.type.is_sample_data_16_bit else 1)))
+ self._debug['data']['end'] = self._io.pos()
+
+
+ class SampleHeader(KaitaiStruct):
+ SEQ_FIELDS = ["sample_length", "sample_loop_start", "sample_loop_length", "volume", "fine_tune", "type", "panning", "relative_note_number", "reserved", "name"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['sample_length']['start'] = self._io.pos()
+ self.sample_length = self._io.read_u4le()
+ self._debug['sample_length']['end'] = self._io.pos()
+ self._debug['sample_loop_start']['start'] = self._io.pos()
+ self.sample_loop_start = self._io.read_u4le()
+ self._debug['sample_loop_start']['end'] = self._io.pos()
+ self._debug['sample_loop_length']['start'] = self._io.pos()
+ self.sample_loop_length = self._io.read_u4le()
+ self._debug['sample_loop_length']['end'] = self._io.pos()
+ self._debug['volume']['start'] = self._io.pos()
+ self.volume = self._io.read_u1()
+ self._debug['volume']['end'] = self._io.pos()
+ self._debug['fine_tune']['start'] = self._io.pos()
+ self.fine_tune = self._io.read_s1()
+ self._debug['fine_tune']['end'] = self._io.pos()
+ self._debug['type']['start'] = self._io.pos()
+ self.type = self._root.Instrument.SampleHeader.LoopType(self._io, self, self._root)
+ self.type._read()
+ self._debug['type']['end'] = self._io.pos()
+ self._debug['panning']['start'] = self._io.pos()
+ self.panning = self._io.read_u1()
+ self._debug['panning']['end'] = self._io.pos()
+ self._debug['relative_note_number']['start'] = self._io.pos()
+ self.relative_note_number = self._io.read_s1()
+ self._debug['relative_note_number']['end'] = self._io.pos()
+ self._debug['reserved']['start'] = self._io.pos()
+ self.reserved = self._io.read_u1()
+ self._debug['reserved']['end'] = self._io.pos()
+ self._debug['name']['start'] = self._io.pos()
+ self.name = (KaitaiStream.bytes_terminate(self._io.read_bytes(22), 0, False)).decode(u"utf-8")
+ self._debug['name']['end'] = self._io.pos()
+
+ class LoopType(KaitaiStruct):
+
+ class LoopType(Enum):
+ none = 0
+ forward = 1
+ ping_pong = 2
+ SEQ_FIELDS = ["reserved0", "is_sample_data_16_bit", "reserved1", "loop_type"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['reserved0']['start'] = self._io.pos()
+ self.reserved0 = self._io.read_bits_int(3)
+ self._debug['reserved0']['end'] = self._io.pos()
+ self._debug['is_sample_data_16_bit']['start'] = self._io.pos()
+ self.is_sample_data_16_bit = self._io.read_bits_int(1) != 0
+ self._debug['is_sample_data_16_bit']['end'] = self._io.pos()
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_bits_int(2)
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['loop_type']['start'] = self._io.pos()
+ self.loop_type = KaitaiStream.resolve_enum(self._root.Instrument.SampleHeader.LoopType.LoopType, self._io.read_bits_int(2))
+ self._debug['loop_type']['end'] = self._io.pos()
+
+
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/s3m.py b/python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/s3m.py
new file mode 100644
index 00000000..472291fc
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/tracker_modules/s3m.py
@@ -0,0 +1,485 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ....kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+from enum import Enum
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class S3m(KaitaiStruct):
+ """Scream Tracker 3 module is a tracker music file format that, as all
+ tracker music, bundles both sound samples and instructions on which
+ notes to play. It originates from a Scream Tracker 3 music editor
+ (1994) by Future Crew, derived from original Scream Tracker 2 (.stm)
+ module format.
+
+ Instrument descriptions in S3M format allow to use either digital
+ samples or setup and control AdLib (OPL2) synth.
+
+ Music is organized in so called `patterns`. "Pattern" is a generally
+ a 64-row long table, which instructs which notes to play on which
+ time measure. "Patterns" are played one-by-one in a sequence
+ determined by `orders`, which is essentially an array of pattern IDs
+ - this way it's possible to reuse certain patterns more than once
+ for repetitive musical phrases.
+
+ .. seealso::
+ Source - http://hackipedia.org/File%20formats/Music/Sample%20based/text/Scream%20Tracker%203.20%20file%20format.cp437.txt.utf-8.txt
+ """
+ SEQ_FIELDS = ["song_name", "magic1", "file_type", "reserved1", "num_orders", "num_instruments", "num_patterns", "flags", "version", "samples_format", "magic2", "global_volume", "initial_speed", "initial_tempo", "is_stereo", "master_volume", "ultra_click_removal", "has_custom_pan", "reserved2", "ofs_special", "channels", "orders", "instruments", "patterns", "channel_pans"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['song_name']['start'] = self._io.pos()
+ self.song_name = KaitaiStream.bytes_terminate(self._io.read_bytes(28), 0, False)
+ self._debug['song_name']['end'] = self._io.pos()
+ self._debug['magic1']['start'] = self._io.pos()
+ self.magic1 = self._io.ensure_fixed_contents(b"\x1A")
+ self._debug['magic1']['end'] = self._io.pos()
+ self._debug['file_type']['start'] = self._io.pos()
+ self.file_type = self._io.read_u1()
+ self._debug['file_type']['end'] = self._io.pos()
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_bytes(2)
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['num_orders']['start'] = self._io.pos()
+ self.num_orders = self._io.read_u2le()
+ self._debug['num_orders']['end'] = self._io.pos()
+ self._debug['num_instruments']['start'] = self._io.pos()
+ self.num_instruments = self._io.read_u2le()
+ self._debug['num_instruments']['end'] = self._io.pos()
+ self._debug['num_patterns']['start'] = self._io.pos()
+ self.num_patterns = self._io.read_u2le()
+ self._debug['num_patterns']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_u2le()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['version']['start'] = self._io.pos()
+ self.version = self._io.read_u2le()
+ self._debug['version']['end'] = self._io.pos()
+ self._debug['samples_format']['start'] = self._io.pos()
+ self.samples_format = self._io.read_u2le()
+ self._debug['samples_format']['end'] = self._io.pos()
+ self._debug['magic2']['start'] = self._io.pos()
+ self.magic2 = self._io.ensure_fixed_contents(b"\x53\x43\x52\x4D")
+ self._debug['magic2']['end'] = self._io.pos()
+ self._debug['global_volume']['start'] = self._io.pos()
+ self.global_volume = self._io.read_u1()
+ self._debug['global_volume']['end'] = self._io.pos()
+ self._debug['initial_speed']['start'] = self._io.pos()
+ self.initial_speed = self._io.read_u1()
+ self._debug['initial_speed']['end'] = self._io.pos()
+ self._debug['initial_tempo']['start'] = self._io.pos()
+ self.initial_tempo = self._io.read_u1()
+ self._debug['initial_tempo']['end'] = self._io.pos()
+ self._debug['is_stereo']['start'] = self._io.pos()
+ self.is_stereo = self._io.read_bits_int(1) != 0
+ self._debug['is_stereo']['end'] = self._io.pos()
+ self._debug['master_volume']['start'] = self._io.pos()
+ self.master_volume = self._io.read_bits_int(7)
+ self._debug['master_volume']['end'] = self._io.pos()
+ self._io.align_to_byte()
+ self._debug['ultra_click_removal']['start'] = self._io.pos()
+ self.ultra_click_removal = self._io.read_u1()
+ self._debug['ultra_click_removal']['end'] = self._io.pos()
+ self._debug['has_custom_pan']['start'] = self._io.pos()
+ self.has_custom_pan = self._io.read_u1()
+ self._debug['has_custom_pan']['end'] = self._io.pos()
+ self._debug['reserved2']['start'] = self._io.pos()
+ self.reserved2 = self._io.read_bytes(8)
+ self._debug['reserved2']['end'] = self._io.pos()
+ self._debug['ofs_special']['start'] = self._io.pos()
+ self.ofs_special = self._io.read_u2le()
+ self._debug['ofs_special']['end'] = self._io.pos()
+ self._debug['channels']['start'] = self._io.pos()
+ self.channels = [None] * (32)
+ for i in range(32):
+ if not 'arr' in self._debug['channels']:
+ self._debug['channels']['arr'] = []
+ self._debug['channels']['arr'].append({'start': self._io.pos()})
+ _t_channels = self._root.Channel(self._io, self, self._root)
+ _t_channels._read()
+ self.channels[i] = _t_channels
+ self._debug['channels']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['channels']['end'] = self._io.pos()
+ self._debug['orders']['start'] = self._io.pos()
+ self.orders = self._io.read_bytes(self.num_orders)
+ self._debug['orders']['end'] = self._io.pos()
+ self._debug['instruments']['start'] = self._io.pos()
+ self.instruments = [None] * (self.num_instruments)
+ for i in range(self.num_instruments):
+ if not 'arr' in self._debug['instruments']:
+ self._debug['instruments']['arr'] = []
+ self._debug['instruments']['arr'].append({'start': self._io.pos()})
+ _t_instruments = self._root.InstrumentPtr(self._io, self, self._root)
+ _t_instruments._read()
+ self.instruments[i] = _t_instruments
+ self._debug['instruments']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['instruments']['end'] = self._io.pos()
+ self._debug['patterns']['start'] = self._io.pos()
+ self.patterns = [None] * (self.num_patterns)
+ for i in range(self.num_patterns):
+ if not 'arr' in self._debug['patterns']:
+ self._debug['patterns']['arr'] = []
+ self._debug['patterns']['arr'].append({'start': self._io.pos()})
+ _t_patterns = self._root.PatternPtr(self._io, self, self._root)
+ _t_patterns._read()
+ self.patterns[i] = _t_patterns
+ self._debug['patterns']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['patterns']['end'] = self._io.pos()
+ if self.has_custom_pan == 252:
+ self._debug['channel_pans']['start'] = self._io.pos()
+ self.channel_pans = [None] * (32)
+ for i in range(32):
+ if not 'arr' in self._debug['channel_pans']:
+ self._debug['channel_pans']['arr'] = []
+ self._debug['channel_pans']['arr'].append({'start': self._io.pos()})
+ _t_channel_pans = self._root.ChannelPan(self._io, self, self._root)
+ _t_channel_pans._read()
+ self.channel_pans[i] = _t_channel_pans
+ self._debug['channel_pans']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['channel_pans']['end'] = self._io.pos()
+
+
+ class ChannelPan(KaitaiStruct):
+ SEQ_FIELDS = ["reserved1", "has_custom_pan", "reserved2", "pan"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_bits_int(2)
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['has_custom_pan']['start'] = self._io.pos()
+ self.has_custom_pan = self._io.read_bits_int(1) != 0
+ self._debug['has_custom_pan']['end'] = self._io.pos()
+ self._debug['reserved2']['start'] = self._io.pos()
+ self.reserved2 = self._io.read_bits_int(1) != 0
+ self._debug['reserved2']['end'] = self._io.pos()
+ self._debug['pan']['start'] = self._io.pos()
+ self.pan = self._io.read_bits_int(4)
+ self._debug['pan']['end'] = self._io.pos()
+
+
+ class PatternCell(KaitaiStruct):
+ SEQ_FIELDS = ["has_fx", "has_volume", "has_note_and_instrument", "channel_num", "note", "instrument", "volume", "fx_type", "fx_value"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['has_fx']['start'] = self._io.pos()
+ self.has_fx = self._io.read_bits_int(1) != 0
+ self._debug['has_fx']['end'] = self._io.pos()
+ self._debug['has_volume']['start'] = self._io.pos()
+ self.has_volume = self._io.read_bits_int(1) != 0
+ self._debug['has_volume']['end'] = self._io.pos()
+ self._debug['has_note_and_instrument']['start'] = self._io.pos()
+ self.has_note_and_instrument = self._io.read_bits_int(1) != 0
+ self._debug['has_note_and_instrument']['end'] = self._io.pos()
+ self._debug['channel_num']['start'] = self._io.pos()
+ self.channel_num = self._io.read_bits_int(5)
+ self._debug['channel_num']['end'] = self._io.pos()
+ self._io.align_to_byte()
+ if self.has_note_and_instrument:
+ self._debug['note']['start'] = self._io.pos()
+ self.note = self._io.read_u1()
+ self._debug['note']['end'] = self._io.pos()
+
+ if self.has_note_and_instrument:
+ self._debug['instrument']['start'] = self._io.pos()
+ self.instrument = self._io.read_u1()
+ self._debug['instrument']['end'] = self._io.pos()
+
+ if self.has_volume:
+ self._debug['volume']['start'] = self._io.pos()
+ self.volume = self._io.read_u1()
+ self._debug['volume']['end'] = self._io.pos()
+
+ if self.has_fx:
+ self._debug['fx_type']['start'] = self._io.pos()
+ self.fx_type = self._io.read_u1()
+ self._debug['fx_type']['end'] = self._io.pos()
+
+ if self.has_fx:
+ self._debug['fx_value']['start'] = self._io.pos()
+ self.fx_value = self._io.read_u1()
+ self._debug['fx_value']['end'] = self._io.pos()
+
+
+
+ class PatternCells(KaitaiStruct):
+ SEQ_FIELDS = ["cells"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['cells']['start'] = self._io.pos()
+ self.cells = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['cells']:
+ self._debug['cells']['arr'] = []
+ self._debug['cells']['arr'].append({'start': self._io.pos()})
+ _t_cells = self._root.PatternCell(self._io, self, self._root)
+ _t_cells._read()
+ self.cells.append(_t_cells)
+ self._debug['cells']['arr'][len(self.cells) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['cells']['end'] = self._io.pos()
+
+
+ class Channel(KaitaiStruct):
+ SEQ_FIELDS = ["is_disabled", "ch_type"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['is_disabled']['start'] = self._io.pos()
+ self.is_disabled = self._io.read_bits_int(1) != 0
+ self._debug['is_disabled']['end'] = self._io.pos()
+ self._debug['ch_type']['start'] = self._io.pos()
+ self.ch_type = self._io.read_bits_int(7)
+ self._debug['ch_type']['end'] = self._io.pos()
+
+
+ class SwappedU3(KaitaiStruct):
+ """Custom 3-byte integer, stored in mixed endian manner."""
+ SEQ_FIELDS = ["hi", "lo"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['hi']['start'] = self._io.pos()
+ self.hi = self._io.read_u1()
+ self._debug['hi']['end'] = self._io.pos()
+ self._debug['lo']['start'] = self._io.pos()
+ self.lo = self._io.read_u2le()
+ self._debug['lo']['end'] = self._io.pos()
+
+ @property
+ def value(self):
+ if hasattr(self, '_m_value'):
+ return self._m_value if hasattr(self, '_m_value') else None
+
+ self._m_value = (self.lo | (self.hi << 16))
+ return self._m_value if hasattr(self, '_m_value') else None
+
+
+ class Pattern(KaitaiStruct):
+ SEQ_FIELDS = ["size", "body"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._io.read_u2le()
+ self._debug['size']['end'] = self._io.pos()
+ self._debug['body']['start'] = self._io.pos()
+ self._raw_body = self._io.read_bytes((self.size - 2))
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.PatternCells(io, self, self._root)
+ self.body._read()
+ self._debug['body']['end'] = self._io.pos()
+
+
+ class PatternPtr(KaitaiStruct):
+ SEQ_FIELDS = ["paraptr"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['paraptr']['start'] = self._io.pos()
+ self.paraptr = self._io.read_u2le()
+ self._debug['paraptr']['end'] = self._io.pos()
+
+ @property
+ def body(self):
+ if hasattr(self, '_m_body'):
+ return self._m_body if hasattr(self, '_m_body') else None
+
+ _pos = self._io.pos()
+ self._io.seek((self.paraptr * 16))
+ self._debug['_m_body']['start'] = self._io.pos()
+ self._m_body = self._root.Pattern(self._io, self, self._root)
+ self._m_body._read()
+ self._debug['_m_body']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_body if hasattr(self, '_m_body') else None
+
+
+ class InstrumentPtr(KaitaiStruct):
+ SEQ_FIELDS = ["paraptr"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['paraptr']['start'] = self._io.pos()
+ self.paraptr = self._io.read_u2le()
+ self._debug['paraptr']['end'] = self._io.pos()
+
+ @property
+ def body(self):
+ if hasattr(self, '_m_body'):
+ return self._m_body if hasattr(self, '_m_body') else None
+
+ _pos = self._io.pos()
+ self._io.seek((self.paraptr * 16))
+ self._debug['_m_body']['start'] = self._io.pos()
+ self._m_body = self._root.Instrument(self._io, self, self._root)
+ self._m_body._read()
+ self._debug['_m_body']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_body if hasattr(self, '_m_body') else None
+
+
+ class Instrument(KaitaiStruct):
+
+ class InstTypes(Enum):
+ sample = 1
+ melodic = 2
+ bass_drum = 3
+ snare_drum = 4
+ tom = 5
+ cymbal = 6
+ hihat = 7
+ SEQ_FIELDS = ["type", "filename", "body", "tuning_hz", "reserved2", "sample_name", "magic"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['type']['start'] = self._io.pos()
+ self.type = KaitaiStream.resolve_enum(self._root.Instrument.InstTypes, self._io.read_u1())
+ self._debug['type']['end'] = self._io.pos()
+ self._debug['filename']['start'] = self._io.pos()
+ self.filename = KaitaiStream.bytes_terminate(self._io.read_bytes(12), 0, False)
+ self._debug['filename']['end'] = self._io.pos()
+ self._debug['body']['start'] = self._io.pos()
+ _on = self.type
+ if _on == self._root.Instrument.InstTypes.sample:
+ self.body = self._root.Instrument.Sampled(self._io, self, self._root)
+ self.body._read()
+ else:
+ self.body = self._root.Instrument.Adlib(self._io, self, self._root)
+ self.body._read()
+ self._debug['body']['end'] = self._io.pos()
+ self._debug['tuning_hz']['start'] = self._io.pos()
+ self.tuning_hz = self._io.read_u4le()
+ self._debug['tuning_hz']['end'] = self._io.pos()
+ self._debug['reserved2']['start'] = self._io.pos()
+ self.reserved2 = self._io.read_bytes(12)
+ self._debug['reserved2']['end'] = self._io.pos()
+ self._debug['sample_name']['start'] = self._io.pos()
+ self.sample_name = KaitaiStream.bytes_terminate(self._io.read_bytes(28), 0, False)
+ self._debug['sample_name']['end'] = self._io.pos()
+ self._debug['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x53\x43\x52\x53")
+ self._debug['magic']['end'] = self._io.pos()
+
+ class Sampled(KaitaiStruct):
+ SEQ_FIELDS = ["paraptr_sample", "len_sample", "loop_begin", "loop_end", "default_volume", "reserved1", "is_packed", "flags"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['paraptr_sample']['start'] = self._io.pos()
+ self.paraptr_sample = self._root.SwappedU3(self._io, self, self._root)
+ self.paraptr_sample._read()
+ self._debug['paraptr_sample']['end'] = self._io.pos()
+ self._debug['len_sample']['start'] = self._io.pos()
+ self.len_sample = self._io.read_u4le()
+ self._debug['len_sample']['end'] = self._io.pos()
+ self._debug['loop_begin']['start'] = self._io.pos()
+ self.loop_begin = self._io.read_u4le()
+ self._debug['loop_begin']['end'] = self._io.pos()
+ self._debug['loop_end']['start'] = self._io.pos()
+ self.loop_end = self._io.read_u4le()
+ self._debug['loop_end']['end'] = self._io.pos()
+ self._debug['default_volume']['start'] = self._io.pos()
+ self.default_volume = self._io.read_u1()
+ self._debug['default_volume']['end'] = self._io.pos()
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.read_u1()
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['is_packed']['start'] = self._io.pos()
+ self.is_packed = self._io.read_u1()
+ self._debug['is_packed']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_u1()
+ self._debug['flags']['end'] = self._io.pos()
+
+ @property
+ def sample(self):
+ if hasattr(self, '_m_sample'):
+ return self._m_sample if hasattr(self, '_m_sample') else None
+
+ _pos = self._io.pos()
+ self._io.seek((self.paraptr_sample.value * 16))
+ self._debug['_m_sample']['start'] = self._io.pos()
+ self._m_sample = self._io.read_bytes(self.len_sample)
+ self._debug['_m_sample']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_sample if hasattr(self, '_m_sample') else None
+
+
+ class Adlib(KaitaiStruct):
+ SEQ_FIELDS = ["reserved1", "_unnamed1"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['reserved1']['start'] = self._io.pos()
+ self.reserved1 = self._io.ensure_fixed_contents(b"\x00\x00\x00")
+ self._debug['reserved1']['end'] = self._io.pos()
+ self._debug['_unnamed1']['start'] = self._io.pos()
+ self._unnamed1 = self._io.read_bytes(16)
+ self._debug['_unnamed1']['end'] = self._io.pos()
+
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/vlq_base128_be.py b/python/examples/kaitai/kaitai_struct_formats/media/vlq_base128_be.py
new file mode 100644
index 00000000..826cdd95
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/vlq_base128_be.py
@@ -0,0 +1,102 @@
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class VlqBase128Be(KaitaiStruct):
+ """A variable-length unsigned integer using base128 encoding. 1-byte groups
+ consist of 1-bit flag of continuation and 7-bit value chunk, and are ordered
+ "most significant group first", i.e. in "big-endian" manner.
+
+ This particular encoding is specified and used in:
+
+ * Standard MIDI file format
+ * ASN.1 BER encoding
+
+ More information on this encoding is available at
+ https://en.wikipedia.org/wiki/Variable-length_quantity
+
+ This particular implementation supports serialized values to up 8 bytes long.
+ """
+ SEQ_FIELDS = ["groups"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['groups']['start'] = self._io.pos()
+ self.groups = []
+ i = 0
+ while True:
+ if not 'arr' in self._debug['groups']:
+ self._debug['groups']['arr'] = []
+ self._debug['groups']['arr'].append({'start': self._io.pos()})
+ _t_groups = self._root.Group(self._io, self, self._root)
+ _t_groups._read()
+ _ = _t_groups
+ self.groups.append(_)
+ self._debug['groups']['arr'][len(self.groups) - 1]['end'] = self._io.pos()
+ if not (_.has_next):
+ break
+ i += 1
+ self._debug['groups']['end'] = self._io.pos()
+
+ class Group(KaitaiStruct):
+ """One byte group, clearly divided into 7-bit "value" chunk and 1-bit "continuation" flag.
+ """
+ SEQ_FIELDS = ["b"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['b']['start'] = self._io.pos()
+ self.b = self._io.read_u1()
+ self._debug['b']['end'] = self._io.pos()
+
+ @property
+ def has_next(self):
+ """If true, then we have more bytes to read."""
+ if hasattr(self, '_m_has_next'):
+ return self._m_has_next if hasattr(self, '_m_has_next') else None
+
+ self._m_has_next = (self.b & 128) != 0
+ return self._m_has_next if hasattr(self, '_m_has_next') else None
+
+ @property
+ def value(self):
+ """The 7-bit (base128) numeric value chunk of this group."""
+ if hasattr(self, '_m_value'):
+ return self._m_value if hasattr(self, '_m_value') else None
+
+ self._m_value = (self.b & 127)
+ return self._m_value if hasattr(self, '_m_value') else None
+
+
+ @property
+ def last(self):
+ if hasattr(self, '_m_last'):
+ return self._m_last if hasattr(self, '_m_last') else None
+
+ self._m_last = (len(self.groups) - 1)
+ return self._m_last if hasattr(self, '_m_last') else None
+
+ @property
+ def value(self):
+ """Resulting value as normal integer."""
+ if hasattr(self, '_m_value'):
+ return self._m_value if hasattr(self, '_m_value') else None
+
+ self._m_value = (((((((self.groups[self.last].value + ((self.groups[(self.last - 1)].value << 7) if self.last >= 1 else 0)) + ((self.groups[(self.last - 2)].value << 14) if self.last >= 2 else 0)) + ((self.groups[(self.last - 3)].value << 21) if self.last >= 3 else 0)) + ((self.groups[(self.last - 4)].value << 28) if self.last >= 4 else 0)) + ((self.groups[(self.last - 5)].value << 35) if self.last >= 5 else 0)) + ((self.groups[(self.last - 6)].value << 42) if self.last >= 6 else 0)) + ((self.groups[(self.last - 7)].value << 49) if self.last >= 7 else 0))
+ return self._m_value if hasattr(self, '_m_value') else None
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/vp8_ivf.py b/python/examples/kaitai/kaitai_struct_formats/media/vp8_ivf.py
new file mode 100644
index 00000000..38ef9ece
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/vp8_ivf.py
@@ -0,0 +1,108 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class Vp8Ivf(KaitaiStruct):
+ """IVF is a simple container format for raw VP8 data, which is an open
+ and royalty-free video compression format, currently developed by
+ Google.
+
+ Test .ivf files are available at https://chromium.googlesource.com/webm/vp8-test-vectors
+
+ .. seealso::
+ Source - https://wiki.multimedia.cx/index.php/IVF
+ """
+ SEQ_FIELDS = ["magic1", "version", "len_header", "codec", "width", "height", "framerate", "timescale", "num_frames", "unused", "image_data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['magic1']['start'] = self._io.pos()
+ self.magic1 = self._io.ensure_fixed_contents(b"\x44\x4B\x49\x46")
+ self._debug['magic1']['end'] = self._io.pos()
+ self._debug['version']['start'] = self._io.pos()
+ self.version = self._io.read_u2le()
+ self._debug['version']['end'] = self._io.pos()
+ self._debug['len_header']['start'] = self._io.pos()
+ self.len_header = self._io.read_u2le()
+ self._debug['len_header']['end'] = self._io.pos()
+ self._debug['codec']['start'] = self._io.pos()
+ self.codec = self._io.ensure_fixed_contents(b"\x56\x50\x38\x30")
+ self._debug['codec']['end'] = self._io.pos()
+ self._debug['width']['start'] = self._io.pos()
+ self.width = self._io.read_u2le()
+ self._debug['width']['end'] = self._io.pos()
+ self._debug['height']['start'] = self._io.pos()
+ self.height = self._io.read_u2le()
+ self._debug['height']['end'] = self._io.pos()
+ self._debug['framerate']['start'] = self._io.pos()
+ self.framerate = self._io.read_u4le()
+ self._debug['framerate']['end'] = self._io.pos()
+ self._debug['timescale']['start'] = self._io.pos()
+ self.timescale = self._io.read_u4le()
+ self._debug['timescale']['end'] = self._io.pos()
+ self._debug['num_frames']['start'] = self._io.pos()
+ self.num_frames = self._io.read_u4le()
+ self._debug['num_frames']['end'] = self._io.pos()
+ self._debug['unused']['start'] = self._io.pos()
+ self.unused = self._io.read_u4le()
+ self._debug['unused']['end'] = self._io.pos()
+ self._debug['image_data']['start'] = self._io.pos()
+ self.image_data = [None] * (self.num_frames)
+ for i in range(self.num_frames):
+ if not 'arr' in self._debug['image_data']:
+ self._debug['image_data']['arr'] = []
+ self._debug['image_data']['arr'].append({'start': self._io.pos()})
+ _t_image_data = self._root.Blocks(self._io, self, self._root)
+ _t_image_data._read()
+ self.image_data[i] = _t_image_data
+ self._debug['image_data']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['image_data']['end'] = self._io.pos()
+
+ class Blocks(KaitaiStruct):
+ SEQ_FIELDS = ["entries"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['entries']['start'] = self._io.pos()
+ self.entries = self._root.Block(self._io, self, self._root)
+ self.entries._read()
+ self._debug['entries']['end'] = self._io.pos()
+
+
+ class Block(KaitaiStruct):
+ SEQ_FIELDS = ["len_frame", "timestamp", "framedata"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['len_frame']['start'] = self._io.pos()
+ self.len_frame = self._io.read_u4le()
+ self._debug['len_frame']['end'] = self._io.pos()
+ self._debug['timestamp']['start'] = self._io.pos()
+ self.timestamp = self._io.read_u8le()
+ self._debug['timestamp']['end'] = self._io.pos()
+ self._debug['framedata']['start'] = self._io.pos()
+ self.framedata = self._io.read_bytes(self.len_frame)
+ self._debug['framedata']['end'] = self._io.pos()
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/media/wav.py b/python/examples/kaitai/kaitai_struct_formats/media/wav.py
new file mode 100644
index 00000000..0a151964
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/media/wav.py
@@ -0,0 +1,492 @@
+from __future__ import absolute_import
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+
+from pkg_resources import parse_version
+from ...kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO
+from enum import Enum
+import collections
+
+
+if parse_version(ks_version) < parse_version('0.7'):
+ raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version))
+
+class Wav(KaitaiStruct):
+ """The WAVE file format is a subset of Microsoft's RIFF specification for the
+ storage of multimedia files. A RIFF file starts out with a file header
+ followed by a sequence of data chunks. A WAVE file is often just a RIFF
+ file with a single "WAVE" chunk which consists of two sub-chunks --
+ a "fmt " chunk specifying the data format and a "data" chunk containing
+ the actual sample data.
+
+ This Kaitai implementation was written by John Byrd of Gigantic Software
+ (jbyrd@giganticsoftware.com), and it is likely to contain bugs.
+
+ .. seealso::
+ Source - https://www.loc.gov/preservation/digital/formats/fdd/fdd000001.shtml
+ """
+
+ class WFormatTagType(Enum):
+ unknown = 0
+ pcm = 1
+ adpcm = 2
+ ieee_float = 3
+ alaw = 6
+ mulaw = 7
+ dvi_adpcm = 17
+ dolby_ac3_spdif = 146
+ extensible = 65534
+ development = 65535
+
+ class ChunkType(Enum):
+ fmt = 544501094
+ bext = 1650817140
+ cue = 1668637984
+ data = 1684108385
+ minf = 1835626086
+ regn = 1919248238
+ umid = 1970104676
+ SEQ_FIELDS = ["riff_id", "file_size", "wave_id", "chunks"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['riff_id']['start'] = self._io.pos()
+ self.riff_id = self._io.ensure_fixed_contents(b"\x52\x49\x46\x46")
+ self._debug['riff_id']['end'] = self._io.pos()
+ self._debug['file_size']['start'] = self._io.pos()
+ self.file_size = self._io.read_u4le()
+ self._debug['file_size']['end'] = self._io.pos()
+ self._debug['wave_id']['start'] = self._io.pos()
+ self.wave_id = self._io.ensure_fixed_contents(b"\x57\x41\x56\x45")
+ self._debug['wave_id']['end'] = self._io.pos()
+ self._debug['chunks']['start'] = self._io.pos()
+ self._raw_chunks = self._io.read_bytes((self.file_size - 5))
+ io = KaitaiStream(BytesIO(self._raw_chunks))
+ self.chunks = self._root.ChunksType(io, self, self._root)
+ self.chunks._read()
+ self._debug['chunks']['end'] = self._io.pos()
+
+ class SampleType(KaitaiStruct):
+ SEQ_FIELDS = ["sample"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['sample']['start'] = self._io.pos()
+ self.sample = self._io.read_u2le()
+ self._debug['sample']['end'] = self._io.pos()
+
+
+ class FormatChunkType(KaitaiStruct):
+ SEQ_FIELDS = ["w_format_tag", "n_channels", "n_samples_per_sec", "n_avg_bytes_per_sec", "n_block_align", "w_bits_per_sample", "cb_size", "w_valid_bits_per_sample", "channel_mask_and_subformat"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['w_format_tag']['start'] = self._io.pos()
+ self.w_format_tag = KaitaiStream.resolve_enum(self._root.WFormatTagType, self._io.read_u2le())
+ self._debug['w_format_tag']['end'] = self._io.pos()
+ self._debug['n_channels']['start'] = self._io.pos()
+ self.n_channels = self._io.read_u2le()
+ self._debug['n_channels']['end'] = self._io.pos()
+ self._debug['n_samples_per_sec']['start'] = self._io.pos()
+ self.n_samples_per_sec = self._io.read_u4le()
+ self._debug['n_samples_per_sec']['end'] = self._io.pos()
+ self._debug['n_avg_bytes_per_sec']['start'] = self._io.pos()
+ self.n_avg_bytes_per_sec = self._io.read_u4le()
+ self._debug['n_avg_bytes_per_sec']['end'] = self._io.pos()
+ self._debug['n_block_align']['start'] = self._io.pos()
+ self.n_block_align = self._io.read_u2le()
+ self._debug['n_block_align']['end'] = self._io.pos()
+ self._debug['w_bits_per_sample']['start'] = self._io.pos()
+ self.w_bits_per_sample = self._io.read_u2le()
+ self._debug['w_bits_per_sample']['end'] = self._io.pos()
+ if not (self.is_basic_pcm):
+ self._debug['cb_size']['start'] = self._io.pos()
+ self.cb_size = self._io.read_u2le()
+ self._debug['cb_size']['end'] = self._io.pos()
+
+ if self.is_cb_size_meaningful:
+ self._debug['w_valid_bits_per_sample']['start'] = self._io.pos()
+ self.w_valid_bits_per_sample = self._io.read_u2le()
+ self._debug['w_valid_bits_per_sample']['end'] = self._io.pos()
+
+ if self.is_extensible:
+ self._debug['channel_mask_and_subformat']['start'] = self._io.pos()
+ self.channel_mask_and_subformat = self._root.ChannelMaskAndSubformatType(self._io, self, self._root)
+ self.channel_mask_and_subformat._read()
+ self._debug['channel_mask_and_subformat']['end'] = self._io.pos()
+
+
+ @property
+ def is_extensible(self):
+ if hasattr(self, '_m_is_extensible'):
+ return self._m_is_extensible if hasattr(self, '_m_is_extensible') else None
+
+ self._m_is_extensible = self.w_format_tag == self._root.WFormatTagType.extensible
+ return self._m_is_extensible if hasattr(self, '_m_is_extensible') else None
+
+ @property
+ def is_basic_pcm(self):
+ if hasattr(self, '_m_is_basic_pcm'):
+ return self._m_is_basic_pcm if hasattr(self, '_m_is_basic_pcm') else None
+
+ self._m_is_basic_pcm = self.w_format_tag == self._root.WFormatTagType.pcm
+ return self._m_is_basic_pcm if hasattr(self, '_m_is_basic_pcm') else None
+
+ @property
+ def is_basic_float(self):
+ if hasattr(self, '_m_is_basic_float'):
+ return self._m_is_basic_float if hasattr(self, '_m_is_basic_float') else None
+
+ self._m_is_basic_float = self.w_format_tag == self._root.WFormatTagType.ieee_float
+ return self._m_is_basic_float if hasattr(self, '_m_is_basic_float') else None
+
+ @property
+ def is_cb_size_meaningful(self):
+ if hasattr(self, '_m_is_cb_size_meaningful'):
+ return self._m_is_cb_size_meaningful if hasattr(self, '_m_is_cb_size_meaningful') else None
+
+ self._m_is_cb_size_meaningful = ((not (self.is_basic_pcm)) and (self.cb_size != 0))
+ return self._m_is_cb_size_meaningful if hasattr(self, '_m_is_cb_size_meaningful') else None
+
+
+ class GuidType(KaitaiStruct):
+ SEQ_FIELDS = ["data1", "data2", "data3", "data4", "data4a"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['data1']['start'] = self._io.pos()
+ self.data1 = self._io.read_u4le()
+ self._debug['data1']['end'] = self._io.pos()
+ self._debug['data2']['start'] = self._io.pos()
+ self.data2 = self._io.read_u2le()
+ self._debug['data2']['end'] = self._io.pos()
+ self._debug['data3']['start'] = self._io.pos()
+ self.data3 = self._io.read_u2le()
+ self._debug['data3']['end'] = self._io.pos()
+ self._debug['data4']['start'] = self._io.pos()
+ self.data4 = self._io.read_u4be()
+ self._debug['data4']['end'] = self._io.pos()
+ self._debug['data4a']['start'] = self._io.pos()
+ self.data4a = self._io.read_u4be()
+ self._debug['data4a']['end'] = self._io.pos()
+
+
+ class CuePointType(KaitaiStruct):
+ SEQ_FIELDS = ["dw_name", "dw_position", "fcc_chunk", "dw_chunk_start", "dw_block_start", "dw_sample_offset"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['dw_name']['start'] = self._io.pos()
+ self.dw_name = self._io.read_u4le()
+ self._debug['dw_name']['end'] = self._io.pos()
+ self._debug['dw_position']['start'] = self._io.pos()
+ self.dw_position = self._io.read_u4le()
+ self._debug['dw_position']['end'] = self._io.pos()
+ self._debug['fcc_chunk']['start'] = self._io.pos()
+ self.fcc_chunk = self._io.read_u4le()
+ self._debug['fcc_chunk']['end'] = self._io.pos()
+ self._debug['dw_chunk_start']['start'] = self._io.pos()
+ self.dw_chunk_start = self._io.read_u4le()
+ self._debug['dw_chunk_start']['end'] = self._io.pos()
+ self._debug['dw_block_start']['start'] = self._io.pos()
+ self.dw_block_start = self._io.read_u4le()
+ self._debug['dw_block_start']['end'] = self._io.pos()
+ self._debug['dw_sample_offset']['start'] = self._io.pos()
+ self.dw_sample_offset = self._io.read_u4le()
+ self._debug['dw_sample_offset']['end'] = self._io.pos()
+
+
+ class DataChunkType(KaitaiStruct):
+ SEQ_FIELDS = ["data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['data']['start'] = self._io.pos()
+ self.data = self._io.read_bytes_full()
+ self._debug['data']['end'] = self._io.pos()
+
+
+ class SamplesType(KaitaiStruct):
+ SEQ_FIELDS = ["samples"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['samples']['start'] = self._io.pos()
+ self.samples = self._io.read_u4le()
+ self._debug['samples']['end'] = self._io.pos()
+
+
+ class ChannelMaskAndSubformatType(KaitaiStruct):
+ SEQ_FIELDS = ["dw_channel_mask", "subformat"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['dw_channel_mask']['start'] = self._io.pos()
+ self.dw_channel_mask = self._root.ChannelMaskType(self._io, self, self._root)
+ self.dw_channel_mask._read()
+ self._debug['dw_channel_mask']['end'] = self._io.pos()
+ self._debug['subformat']['start'] = self._io.pos()
+ self.subformat = self._root.GuidType(self._io, self, self._root)
+ self.subformat._read()
+ self._debug['subformat']['end'] = self._io.pos()
+
+
+ class ChunksType(KaitaiStruct):
+ SEQ_FIELDS = ["chunk"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['chunk']['start'] = self._io.pos()
+ self.chunk = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['chunk']:
+ self._debug['chunk']['arr'] = []
+ self._debug['chunk']['arr'].append({'start': self._io.pos()})
+ _t_chunk = self._root.ChunkType(self._io, self, self._root)
+ _t_chunk._read()
+ self.chunk.append(_t_chunk)
+ self._debug['chunk']['arr'][len(self.chunk) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['chunk']['end'] = self._io.pos()
+
+
+ class CueChunkType(KaitaiStruct):
+ SEQ_FIELDS = ["dw_cue_points", "cue_points"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['dw_cue_points']['start'] = self._io.pos()
+ self.dw_cue_points = self._io.read_u4le()
+ self._debug['dw_cue_points']['end'] = self._io.pos()
+ if self.dw_cue_points != 0:
+ self._debug['cue_points']['start'] = self._io.pos()
+ self.cue_points = [None] * (self.dw_cue_points)
+ for i in range(self.dw_cue_points):
+ if not 'arr' in self._debug['cue_points']:
+ self._debug['cue_points']['arr'] = []
+ self._debug['cue_points']['arr'].append({'start': self._io.pos()})
+ _t_cue_points = self._root.CuePointType(self._io, self, self._root)
+ _t_cue_points._read()
+ self.cue_points[i] = _t_cue_points
+ self._debug['cue_points']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['cue_points']['end'] = self._io.pos()
+
+
+
+ class ChannelMaskType(KaitaiStruct):
+ SEQ_FIELDS = ["front_right_of_center", "front_left_of_center", "back_right", "back_left", "low_frequency", "front_center", "front_right", "front_left", "top_center", "side_right", "side_left", "back_center", "top_back_left", "top_front_right", "top_front_center", "top_front_left", "unused1", "top_back_right", "top_back_center", "unused2"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['front_right_of_center']['start'] = self._io.pos()
+ self.front_right_of_center = self._io.read_bits_int(1) != 0
+ self._debug['front_right_of_center']['end'] = self._io.pos()
+ self._debug['front_left_of_center']['start'] = self._io.pos()
+ self.front_left_of_center = self._io.read_bits_int(1) != 0
+ self._debug['front_left_of_center']['end'] = self._io.pos()
+ self._debug['back_right']['start'] = self._io.pos()
+ self.back_right = self._io.read_bits_int(1) != 0
+ self._debug['back_right']['end'] = self._io.pos()
+ self._debug['back_left']['start'] = self._io.pos()
+ self.back_left = self._io.read_bits_int(1) != 0
+ self._debug['back_left']['end'] = self._io.pos()
+ self._debug['low_frequency']['start'] = self._io.pos()
+ self.low_frequency = self._io.read_bits_int(1) != 0
+ self._debug['low_frequency']['end'] = self._io.pos()
+ self._debug['front_center']['start'] = self._io.pos()
+ self.front_center = self._io.read_bits_int(1) != 0
+ self._debug['front_center']['end'] = self._io.pos()
+ self._debug['front_right']['start'] = self._io.pos()
+ self.front_right = self._io.read_bits_int(1) != 0
+ self._debug['front_right']['end'] = self._io.pos()
+ self._debug['front_left']['start'] = self._io.pos()
+ self.front_left = self._io.read_bits_int(1) != 0
+ self._debug['front_left']['end'] = self._io.pos()
+ self._debug['top_center']['start'] = self._io.pos()
+ self.top_center = self._io.read_bits_int(1) != 0
+ self._debug['top_center']['end'] = self._io.pos()
+ self._debug['side_right']['start'] = self._io.pos()
+ self.side_right = self._io.read_bits_int(1) != 0
+ self._debug['side_right']['end'] = self._io.pos()
+ self._debug['side_left']['start'] = self._io.pos()
+ self.side_left = self._io.read_bits_int(1) != 0
+ self._debug['side_left']['end'] = self._io.pos()
+ self._debug['back_center']['start'] = self._io.pos()
+ self.back_center = self._io.read_bits_int(1) != 0
+ self._debug['back_center']['end'] = self._io.pos()
+ self._debug['top_back_left']['start'] = self._io.pos()
+ self.top_back_left = self._io.read_bits_int(1) != 0
+ self._debug['top_back_left']['end'] = self._io.pos()
+ self._debug['top_front_right']['start'] = self._io.pos()
+ self.top_front_right = self._io.read_bits_int(1) != 0
+ self._debug['top_front_right']['end'] = self._io.pos()
+ self._debug['top_front_center']['start'] = self._io.pos()
+ self.top_front_center = self._io.read_bits_int(1) != 0
+ self._debug['top_front_center']['end'] = self._io.pos()
+ self._debug['top_front_left']['start'] = self._io.pos()
+ self.top_front_left = self._io.read_bits_int(1) != 0
+ self._debug['top_front_left']['end'] = self._io.pos()
+ self._debug['unused1']['start'] = self._io.pos()
+ self.unused1 = self._io.read_bits_int(6)
+ self._debug['unused1']['end'] = self._io.pos()
+ self._debug['top_back_right']['start'] = self._io.pos()
+ self.top_back_right = self._io.read_bits_int(1) != 0
+ self._debug['top_back_right']['end'] = self._io.pos()
+ self._debug['top_back_center']['start'] = self._io.pos()
+ self.top_back_center = self._io.read_bits_int(1) != 0
+ self._debug['top_back_center']['end'] = self._io.pos()
+ self._debug['unused2']['start'] = self._io.pos()
+ self.unused2 = self._io.read_bits_int(8)
+ self._debug['unused2']['end'] = self._io.pos()
+
+
+ class ChunkType(KaitaiStruct):
+ SEQ_FIELDS = ["chunk_id", "len", "data"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['chunk_id']['start'] = self._io.pos()
+ self.chunk_id = self._io.read_u4be()
+ self._debug['chunk_id']['end'] = self._io.pos()
+ self._debug['len']['start'] = self._io.pos()
+ self.len = self._io.read_u4le()
+ self._debug['len']['end'] = self._io.pos()
+ self._debug['data']['start'] = self._io.pos()
+ _on = self.chunk_id
+ if _on == 1684108385:
+ self._raw_data = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.DataChunkType(io, self, self._root)
+ self.data._read()
+ elif _on == 1668637984:
+ self._raw_data = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.CueChunkType(io, self, self._root)
+ self.data._read()
+ elif _on == 1650817140:
+ self._raw_data = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.BextChunkType(io, self, self._root)
+ self.data._read()
+ elif _on == 1718449184:
+ self._raw_data = self._io.read_bytes(self.len)
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.FormatChunkType(io, self, self._root)
+ self.data._read()
+ else:
+ self.data = self._io.read_bytes(self.len)
+ self._debug['data']['end'] = self._io.pos()
+
+
+ class BextChunkType(KaitaiStruct):
+ SEQ_FIELDS = ["description", "originator", "originator_reference", "origination_date", "origination_time", "time_reference_low", "time_reference_high", "version", "umid", "loudness_value", "loudness_range", "max_true_peak_level", "max_momentary_loudness", "max_short_term_loudness"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['description']['start'] = self._io.pos()
+ self.description = (self._io.read_bytes(256)).decode(u"ASCII")
+ self._debug['description']['end'] = self._io.pos()
+ self._debug['originator']['start'] = self._io.pos()
+ self.originator = (self._io.read_bytes(32)).decode(u"ASCII")
+ self._debug['originator']['end'] = self._io.pos()
+ self._debug['originator_reference']['start'] = self._io.pos()
+ self.originator_reference = (self._io.read_bytes(32)).decode(u"ASCII")
+ self._debug['originator_reference']['end'] = self._io.pos()
+ self._debug['origination_date']['start'] = self._io.pos()
+ self.origination_date = (self._io.read_bytes(10)).decode(u"ASCII")
+ self._debug['origination_date']['end'] = self._io.pos()
+ self._debug['origination_time']['start'] = self._io.pos()
+ self.origination_time = (self._io.read_bytes(8)).decode(u"ASCII")
+ self._debug['origination_time']['end'] = self._io.pos()
+ self._debug['time_reference_low']['start'] = self._io.pos()
+ self.time_reference_low = self._io.read_u4le()
+ self._debug['time_reference_low']['end'] = self._io.pos()
+ self._debug['time_reference_high']['start'] = self._io.pos()
+ self.time_reference_high = self._io.read_u4le()
+ self._debug['time_reference_high']['end'] = self._io.pos()
+ self._debug['version']['start'] = self._io.pos()
+ self.version = self._io.read_u2le()
+ self._debug['version']['end'] = self._io.pos()
+ self._debug['umid']['start'] = self._io.pos()
+ self.umid = self._io.read_bytes(64)
+ self._debug['umid']['end'] = self._io.pos()
+ self._debug['loudness_value']['start'] = self._io.pos()
+ self.loudness_value = self._io.read_u2le()
+ self._debug['loudness_value']['end'] = self._io.pos()
+ self._debug['loudness_range']['start'] = self._io.pos()
+ self.loudness_range = self._io.read_u2le()
+ self._debug['loudness_range']['end'] = self._io.pos()
+ self._debug['max_true_peak_level']['start'] = self._io.pos()
+ self.max_true_peak_level = self._io.read_u2le()
+ self._debug['max_true_peak_level']['end'] = self._io.pos()
+ self._debug['max_momentary_loudness']['start'] = self._io.pos()
+ self.max_momentary_loudness = self._io.read_u2le()
+ self._debug['max_momentary_loudness']['end'] = self._io.pos()
+ self._debug['max_short_term_loudness']['start'] = self._io.pos()
+ self.max_short_term_loudness = self._io.read_u2le()
+ self._debug['max_short_term_loudness']['end'] = self._io.pos()
+
+
+ @property
+ def format_chunk(self):
+ if hasattr(self, '_m_format_chunk'):
+ return self._m_format_chunk if hasattr(self, '_m_format_chunk') else None
+
+ self._m_format_chunk = self.chunks.chunk[0].data
+ return self._m_format_chunk if hasattr(self, '_m_format_chunk') else None
+
+