diff options
Diffstat (limited to 'python/examples/kaitai/kaitai_struct_formats/media')
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 + + |
