summaryrefslogtreecommitdiff
path: root/python/examples/kaitai/kaitai_struct_formats/game
diff options
context:
space:
mode:
authorAndrew Lamoureux <andrew@vector35.com>2019-03-18 17:34:09 -0400
committerRusty Wagner <rusty@vector35.com>2019-03-20 13:00:17 -0400
commit815f5485eb50eff3b483a6bcc0c3d3d11569f8ca (patch)
tree5128136ac86971ed8729e8c19cb00bcf8f220acc /python/examples/kaitai/kaitai_struct_formats/game
parent8e9322ed8b5826dd62bc931d83e0828d42f7969f (diff)
kaitai: all formats compiled and included
Diffstat (limited to 'python/examples/kaitai/kaitai_struct_formats/game')
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/__init__.py0
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/allegro_dat.py365
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/doom_wad.py654
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/dune_2_pak.py122
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/fallout2_dat.py161
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/fallout_dat.py156
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/ftl_dat.py90
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/gran_turismo_vol.py136
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/heaps_pak.py173
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/heroes_of_might_and_magic_agg.py116
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/heroes_of_might_and_magic_bmp.py34
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/quake_mdl.py377
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/quake_pak.py110
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/renderware_binary_stream.py774
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/saints_row_2_vpp_pc.py252
-rw-r--r--python/examples/kaitai/kaitai_struct_formats/game/warcraft_2_pud.py421
16 files changed, 3941 insertions, 0 deletions
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/__init__.py b/python/examples/kaitai/kaitai_struct_formats/game/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/__init__.py
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/allegro_dat.py b/python/examples/kaitai/kaitai_struct_formats/game/allegro_dat.py
new file mode 100644
index 00000000..0078bf45
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/allegro_dat.py
@@ -0,0 +1,365 @@
+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 AllegroDat(KaitaiStruct):
+ """Allegro library for C (mostly used for game and multimedia apps
+ programming) used its own container file format.
+
+ In general, it allows storage of arbitrary binary data blocks
+ bundled together with some simple key-value style metadata
+ ("properties") for every block. Allegro also pre-defines some simple
+ formats for bitmaps, fonts, MIDI music, sound samples and
+ palettes. Allegro library v4.0+ also support LZSS compression.
+
+ This spec applies to Allegro data files for library versions 2.2 up
+ to 4.4.
+
+ .. seealso::
+ Source - https://liballeg.org/stabledocs/en/datafile.html
+ """
+
+ class PackEnum(Enum):
+ unpacked = 1936484398
+ SEQ_FIELDS = ["pack_magic", "dat_magic", "num_objects", "objects"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['pack_magic']['start'] = self._io.pos()
+ self.pack_magic = KaitaiStream.resolve_enum(self._root.PackEnum, self._io.read_u4be())
+ self._debug['pack_magic']['end'] = self._io.pos()
+ self._debug['dat_magic']['start'] = self._io.pos()
+ self.dat_magic = self._io.ensure_fixed_contents(b"\x41\x4C\x4C\x2E")
+ self._debug['dat_magic']['end'] = self._io.pos()
+ self._debug['num_objects']['start'] = self._io.pos()
+ self.num_objects = self._io.read_u4be()
+ self._debug['num_objects']['end'] = self._io.pos()
+ self._debug['objects']['start'] = self._io.pos()
+ self.objects = [None] * (self.num_objects)
+ for i in range(self.num_objects):
+ if not 'arr' in self._debug['objects']:
+ self._debug['objects']['arr'] = []
+ self._debug['objects']['arr'].append({'start': self._io.pos()})
+ _t_objects = self._root.DatObject(self._io, self, self._root)
+ _t_objects._read()
+ self.objects[i] = _t_objects
+ self._debug['objects']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['objects']['end'] = self._io.pos()
+
+ class DatFont16(KaitaiStruct):
+ """Simple monochrome monospaced font, 95 characters, 8x16 px
+ characters.
+ """
+ SEQ_FIELDS = ["chars"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['chars']['start'] = self._io.pos()
+ self.chars = [None] * (95)
+ for i in range(95):
+ if not 'arr' in self._debug['chars']:
+ self._debug['chars']['arr'] = []
+ self._debug['chars']['arr'].append({'start': self._io.pos()})
+ self.chars[i] = self._io.read_bytes(16)
+ self._debug['chars']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['chars']['end'] = self._io.pos()
+
+
+ class DatBitmap(KaitaiStruct):
+ SEQ_FIELDS = ["bits_per_pixel", "width", "height", "image"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['bits_per_pixel']['start'] = self._io.pos()
+ self.bits_per_pixel = self._io.read_s2be()
+ self._debug['bits_per_pixel']['end'] = self._io.pos()
+ self._debug['width']['start'] = self._io.pos()
+ self.width = self._io.read_u2be()
+ self._debug['width']['end'] = self._io.pos()
+ self._debug['height']['start'] = self._io.pos()
+ self.height = self._io.read_u2be()
+ self._debug['height']['end'] = self._io.pos()
+ self._debug['image']['start'] = self._io.pos()
+ self.image = self._io.read_bytes_full()
+ self._debug['image']['end'] = self._io.pos()
+
+
+ class DatFont(KaitaiStruct):
+ SEQ_FIELDS = ["font_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['font_size']['start'] = self._io.pos()
+ self.font_size = self._io.read_s2be()
+ self._debug['font_size']['end'] = self._io.pos()
+ self._debug['body']['start'] = self._io.pos()
+ _on = self.font_size
+ if _on == 8:
+ self.body = self._root.DatFont8(self._io, self, self._root)
+ self.body._read()
+ elif _on == 16:
+ self.body = self._root.DatFont16(self._io, self, self._root)
+ self.body._read()
+ elif _on == 0:
+ self.body = self._root.DatFont39(self._io, self, self._root)
+ self.body._read()
+ self._debug['body']['end'] = self._io.pos()
+
+
+ class DatFont8(KaitaiStruct):
+ """Simple monochrome monospaced font, 95 characters, 8x8 px
+ characters.
+ """
+ SEQ_FIELDS = ["chars"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['chars']['start'] = self._io.pos()
+ self.chars = [None] * (95)
+ for i in range(95):
+ if not 'arr' in self._debug['chars']:
+ self._debug['chars']['arr'] = []
+ self._debug['chars']['arr'].append({'start': self._io.pos()})
+ self.chars[i] = self._io.read_bytes(8)
+ self._debug['chars']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['chars']['end'] = self._io.pos()
+
+
+ class DatObject(KaitaiStruct):
+ SEQ_FIELDS = ["properties", "len_compressed", "len_uncompressed", "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['properties']['start'] = self._io.pos()
+ self.properties = []
+ i = 0
+ while True:
+ if not 'arr' in self._debug['properties']:
+ self._debug['properties']['arr'] = []
+ self._debug['properties']['arr'].append({'start': self._io.pos()})
+ _t_properties = self._root.Property(self._io, self, self._root)
+ _t_properties._read()
+ _ = _t_properties
+ self.properties.append(_)
+ self._debug['properties']['arr'][len(self.properties) - 1]['end'] = self._io.pos()
+ if not (_.is_valid):
+ break
+ i += 1
+ self._debug['properties']['end'] = self._io.pos()
+ self._debug['len_compressed']['start'] = self._io.pos()
+ self.len_compressed = self._io.read_s4be()
+ self._debug['len_compressed']['end'] = self._io.pos()
+ self._debug['len_uncompressed']['start'] = self._io.pos()
+ self.len_uncompressed = self._io.read_s4be()
+ self._debug['len_uncompressed']['end'] = self._io.pos()
+ self._debug['body']['start'] = self._io.pos()
+ _on = self.type
+ if _on == u"BMP ":
+ self._raw_body = self._io.read_bytes(self.len_compressed)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.DatBitmap(io, self, self._root)
+ self.body._read()
+ elif _on == u"RLE ":
+ self._raw_body = self._io.read_bytes(self.len_compressed)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.DatRleSprite(io, self, self._root)
+ self.body._read()
+ elif _on == u"FONT":
+ self._raw_body = self._io.read_bytes(self.len_compressed)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.DatFont(io, self, self._root)
+ self.body._read()
+ else:
+ self.body = self._io.read_bytes(self.len_compressed)
+ self._debug['body']['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.properties[-1].magic
+ return self._m_type if hasattr(self, '_m_type') else None
+
+
+ class DatFont39(KaitaiStruct):
+ """New bitmap font format introduced since Allegro 3.9: allows
+ flexible designation of character ranges, 8-bit colored
+ characters, etc.
+ """
+ SEQ_FIELDS = ["num_ranges", "ranges"]
+ def __init__(self, _io, _parent=None, _root=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_ranges']['start'] = self._io.pos()
+ self.num_ranges = self._io.read_s2be()
+ self._debug['num_ranges']['end'] = self._io.pos()
+ self._debug['ranges']['start'] = self._io.pos()
+ self.ranges = [None] * (self.num_ranges)
+ for i in range(self.num_ranges):
+ if not 'arr' in self._debug['ranges']:
+ self._debug['ranges']['arr'] = []
+ self._debug['ranges']['arr'].append({'start': self._io.pos()})
+ _t_ranges = self._root.DatFont39.Range(self._io, self, self._root)
+ _t_ranges._read()
+ self.ranges[i] = _t_ranges
+ self._debug['ranges']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['ranges']['end'] = self._io.pos()
+
+ class Range(KaitaiStruct):
+ SEQ_FIELDS = ["mono", "start_char", "end_char", "chars"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['mono']['start'] = self._io.pos()
+ self.mono = self._io.read_u1()
+ self._debug['mono']['end'] = self._io.pos()
+ self._debug['start_char']['start'] = self._io.pos()
+ self.start_char = self._io.read_u4be()
+ self._debug['start_char']['end'] = self._io.pos()
+ self._debug['end_char']['start'] = self._io.pos()
+ self.end_char = self._io.read_u4be()
+ self._debug['end_char']['end'] = self._io.pos()
+ self._debug['chars']['start'] = self._io.pos()
+ self.chars = [None] * (((self.end_char - self.start_char) + 1))
+ for i in range(((self.end_char - self.start_char) + 1)):
+ if not 'arr' in self._debug['chars']:
+ self._debug['chars']['arr'] = []
+ self._debug['chars']['arr'].append({'start': self._io.pos()})
+ _t_chars = self._root.DatFont39.FontChar(self._io, self, self._root)
+ _t_chars._read()
+ self.chars[i] = _t_chars
+ self._debug['chars']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['chars']['end'] = self._io.pos()
+
+
+ class FontChar(KaitaiStruct):
+ SEQ_FIELDS = ["width", "height", "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['width']['start'] = self._io.pos()
+ self.width = self._io.read_u2be()
+ self._debug['width']['end'] = self._io.pos()
+ self._debug['height']['start'] = self._io.pos()
+ self.height = self._io.read_u2be()
+ self._debug['height']['end'] = self._io.pos()
+ self._debug['body']['start'] = self._io.pos()
+ self.body = self._io.read_bytes((self.width * self.height))
+ self._debug['body']['end'] = self._io.pos()
+
+
+
+ class Property(KaitaiStruct):
+ SEQ_FIELDS = ["magic", "type", "len_body", "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['magic']['start'] = self._io.pos()
+ self.magic = (self._io.read_bytes(4)).decode(u"UTF-8")
+ self._debug['magic']['end'] = self._io.pos()
+ if self.is_valid:
+ self._debug['type']['start'] = self._io.pos()
+ self.type = (self._io.read_bytes(4)).decode(u"UTF-8")
+ self._debug['type']['end'] = self._io.pos()
+
+ if self.is_valid:
+ self._debug['len_body']['start'] = self._io.pos()
+ self.len_body = self._io.read_u4be()
+ self._debug['len_body']['end'] = self._io.pos()
+
+ if self.is_valid:
+ self._debug['body']['start'] = self._io.pos()
+ self.body = (self._io.read_bytes(self.len_body)).decode(u"UTF-8")
+ self._debug['body']['end'] = self._io.pos()
+
+
+ @property
+ def is_valid(self):
+ if hasattr(self, '_m_is_valid'):
+ return self._m_is_valid if hasattr(self, '_m_is_valid') else None
+
+ self._m_is_valid = self.magic == u"prop"
+ return self._m_is_valid if hasattr(self, '_m_is_valid') else None
+
+
+ class DatRleSprite(KaitaiStruct):
+ SEQ_FIELDS = ["bits_per_pixel", "width", "height", "len_image", "image"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['bits_per_pixel']['start'] = self._io.pos()
+ self.bits_per_pixel = self._io.read_s2be()
+ self._debug['bits_per_pixel']['end'] = self._io.pos()
+ self._debug['width']['start'] = self._io.pos()
+ self.width = self._io.read_u2be()
+ self._debug['width']['end'] = self._io.pos()
+ self._debug['height']['start'] = self._io.pos()
+ self.height = self._io.read_u2be()
+ self._debug['height']['end'] = self._io.pos()
+ self._debug['len_image']['start'] = self._io.pos()
+ self.len_image = self._io.read_u4be()
+ self._debug['len_image']['end'] = self._io.pos()
+ self._debug['image']['start'] = self._io.pos()
+ self.image = self._io.read_bytes_full()
+ self._debug['image']['end'] = self._io.pos()
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/doom_wad.py b/python/examples/kaitai/kaitai_struct_formats/game/doom_wad.py
new file mode 100644
index 00000000..b5705ae9
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/doom_wad.py
@@ -0,0 +1,654 @@
+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 DoomWad(KaitaiStruct):
+ SEQ_FIELDS = ["magic", "num_index_entries", "index_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['magic']['start'] = self._io.pos()
+ self.magic = (self._io.read_bytes(4)).decode(u"ASCII")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['num_index_entries']['start'] = self._io.pos()
+ self.num_index_entries = self._io.read_s4le()
+ self._debug['num_index_entries']['end'] = self._io.pos()
+ self._debug['index_offset']['start'] = self._io.pos()
+ self.index_offset = self._io.read_s4le()
+ self._debug['index_offset']['end'] = self._io.pos()
+
+ class Sectors(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.Sector(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 Vertex(KaitaiStruct):
+ 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_s2le()
+ self._debug['x']['end'] = self._io.pos()
+ self._debug['y']['start'] = self._io.pos()
+ self.y = self._io.read_s2le()
+ self._debug['y']['end'] = self._io.pos()
+
+
+ class Texture12(KaitaiStruct):
+ """Used for TEXTURE1 and TEXTURE2 lumps, which designate how to
+ combine wall patches to make wall textures. This essentially
+ provides a very simple form of image compression, allowing
+ certain elements ("patches") to be reused / recombined on
+ different textures for more variety in the game.
+
+ .. seealso::
+ Source - http://doom.wikia.com/wiki/TEXTURE1
+ """
+ SEQ_FIELDS = ["num_textures", "textures"]
+ def __init__(self, _io, _parent=None, _root=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_textures']['start'] = self._io.pos()
+ self.num_textures = self._io.read_s4le()
+ self._debug['num_textures']['end'] = self._io.pos()
+ self._debug['textures']['start'] = self._io.pos()
+ self.textures = [None] * (self.num_textures)
+ for i in range(self.num_textures):
+ if not 'arr' in self._debug['textures']:
+ self._debug['textures']['arr'] = []
+ self._debug['textures']['arr'].append({'start': self._io.pos()})
+ _t_textures = self._root.Texture12.TextureIndex(self._io, self, self._root)
+ _t_textures._read()
+ self.textures[i] = _t_textures
+ self._debug['textures']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['textures']['end'] = self._io.pos()
+
+ class TextureIndex(KaitaiStruct):
+ SEQ_FIELDS = ["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['offset']['start'] = self._io.pos()
+ self.offset = self._io.read_s4le()
+ self._debug['offset']['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.offset)
+ self._debug['_m_body']['start'] = self._io.pos()
+ self._m_body = self._root.Texture12.TextureBody(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 TextureBody(KaitaiStruct):
+ SEQ_FIELDS = ["name", "masked", "width", "height", "column_directory", "num_patches", "patches"]
+ def __init__(self, _io, _parent=None, _root=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_strip_right(self._io.read_bytes(8), 0)).decode(u"ASCII")
+ self._debug['name']['end'] = self._io.pos()
+ self._debug['masked']['start'] = self._io.pos()
+ self.masked = self._io.read_u4le()
+ self._debug['masked']['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['column_directory']['start'] = self._io.pos()
+ self.column_directory = self._io.read_u4le()
+ self._debug['column_directory']['end'] = self._io.pos()
+ self._debug['num_patches']['start'] = self._io.pos()
+ self.num_patches = self._io.read_u2le()
+ self._debug['num_patches']['end'] = self._io.pos()
+ self._debug['patches']['start'] = self._io.pos()
+ self.patches = [None] * (self.num_patches)
+ for i in range(self.num_patches):
+ if not 'arr' in self._debug['patches']:
+ self._debug['patches']['arr'] = []
+ self._debug['patches']['arr'].append({'start': self._io.pos()})
+ _t_patches = self._root.Texture12.Patch(self._io, self, self._root)
+ _t_patches._read()
+ self.patches[i] = _t_patches
+ self._debug['patches']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['patches']['end'] = self._io.pos()
+
+
+ class Patch(KaitaiStruct):
+ SEQ_FIELDS = ["origin_x", "origin_y", "patch_id", "step_dir", "colormap"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['origin_x']['start'] = self._io.pos()
+ self.origin_x = self._io.read_s2le()
+ self._debug['origin_x']['end'] = self._io.pos()
+ self._debug['origin_y']['start'] = self._io.pos()
+ self.origin_y = self._io.read_s2le()
+ self._debug['origin_y']['end'] = self._io.pos()
+ self._debug['patch_id']['start'] = self._io.pos()
+ self.patch_id = self._io.read_u2le()
+ self._debug['patch_id']['end'] = self._io.pos()
+ self._debug['step_dir']['start'] = self._io.pos()
+ self.step_dir = self._io.read_u2le()
+ self._debug['step_dir']['end'] = self._io.pos()
+ self._debug['colormap']['start'] = self._io.pos()
+ self.colormap = self._io.read_u2le()
+ self._debug['colormap']['end'] = self._io.pos()
+
+
+
+ class Linedef(KaitaiStruct):
+ SEQ_FIELDS = ["vertex_start_idx", "vertex_end_idx", "flags", "line_type", "sector_tag", "sidedef_right_idx", "sidedef_left_idx"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['vertex_start_idx']['start'] = self._io.pos()
+ self.vertex_start_idx = self._io.read_u2le()
+ self._debug['vertex_start_idx']['end'] = self._io.pos()
+ self._debug['vertex_end_idx']['start'] = self._io.pos()
+ self.vertex_end_idx = self._io.read_u2le()
+ self._debug['vertex_end_idx']['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['line_type']['start'] = self._io.pos()
+ self.line_type = self._io.read_u2le()
+ self._debug['line_type']['end'] = self._io.pos()
+ self._debug['sector_tag']['start'] = self._io.pos()
+ self.sector_tag = self._io.read_u2le()
+ self._debug['sector_tag']['end'] = self._io.pos()
+ self._debug['sidedef_right_idx']['start'] = self._io.pos()
+ self.sidedef_right_idx = self._io.read_u2le()
+ self._debug['sidedef_right_idx']['end'] = self._io.pos()
+ self._debug['sidedef_left_idx']['start'] = self._io.pos()
+ self.sidedef_left_idx = self._io.read_u2le()
+ self._debug['sidedef_left_idx']['end'] = self._io.pos()
+
+
+ class Pnames(KaitaiStruct):
+ """
+ .. seealso::
+ Source - http://doom.wikia.com/wiki/PNAMES
+ """
+ SEQ_FIELDS = ["num_patches", "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['num_patches']['start'] = self._io.pos()
+ self.num_patches = self._io.read_u4le()
+ self._debug['num_patches']['end'] = self._io.pos()
+ self._debug['names']['start'] = self._io.pos()
+ self.names = [None] * (self.num_patches)
+ for i in range(self.num_patches):
+ if not 'arr' in self._debug['names']:
+ self._debug['names']['arr'] = []
+ self._debug['names']['arr'].append({'start': self._io.pos()})
+ self.names[i] = (KaitaiStream.bytes_strip_right(self._io.read_bytes(8), 0)).decode(u"ASCII")
+ self._debug['names']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['names']['end'] = self._io.pos()
+
+
+ class Thing(KaitaiStruct):
+ SEQ_FIELDS = ["x", "y", "angle", "type", "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['x']['start'] = self._io.pos()
+ self.x = self._io.read_s2le()
+ self._debug['x']['end'] = self._io.pos()
+ self._debug['y']['start'] = self._io.pos()
+ self.y = self._io.read_s2le()
+ self._debug['y']['end'] = self._io.pos()
+ self._debug['angle']['start'] = self._io.pos()
+ self.angle = self._io.read_u2le()
+ self._debug['angle']['end'] = self._io.pos()
+ self._debug['type']['start'] = self._io.pos()
+ self.type = self._io.read_u2le()
+ self._debug['type']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_u2le()
+ self._debug['flags']['end'] = self._io.pos()
+
+
+ class Sector(KaitaiStruct):
+
+ class SpecialSector(Enum):
+ normal = 0
+ d_light_flicker = 1
+ d_light_strobe_fast = 2
+ d_light_strobe_slow = 3
+ d_light_strobe_hurt = 4
+ d_damage_hellslime = 5
+ d_damage_nukage = 7
+ d_light_glow = 8
+ secret = 9
+ d_sector_door_close_in_30 = 10
+ d_damage_end = 11
+ d_light_strobe_slow_sync = 12
+ d_light_strobe_fast_sync = 13
+ d_sector_door_raise_in_5_mins = 14
+ d_friction_low = 15
+ d_damage_super_hellslime = 16
+ d_light_fire_flicker = 17
+ d_damage_lava_wimpy = 18
+ d_damage_lava_hefty = 19
+ d_scroll_east_lava_damage = 20
+ light_phased = 21
+ light_sequence_start = 22
+ light_sequence_special1 = 23
+ light_sequence_special2 = 24
+ SEQ_FIELDS = ["floor_z", "ceil_z", "floor_flat", "ceil_flat", "light", "special_type", "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['floor_z']['start'] = self._io.pos()
+ self.floor_z = self._io.read_s2le()
+ self._debug['floor_z']['end'] = self._io.pos()
+ self._debug['ceil_z']['start'] = self._io.pos()
+ self.ceil_z = self._io.read_s2le()
+ self._debug['ceil_z']['end'] = self._io.pos()
+ self._debug['floor_flat']['start'] = self._io.pos()
+ self.floor_flat = (self._io.read_bytes(8)).decode(u"ASCII")
+ self._debug['floor_flat']['end'] = self._io.pos()
+ self._debug['ceil_flat']['start'] = self._io.pos()
+ self.ceil_flat = (self._io.read_bytes(8)).decode(u"ASCII")
+ self._debug['ceil_flat']['end'] = self._io.pos()
+ self._debug['light']['start'] = self._io.pos()
+ self.light = self._io.read_s2le()
+ self._debug['light']['end'] = self._io.pos()
+ self._debug['special_type']['start'] = self._io.pos()
+ self.special_type = KaitaiStream.resolve_enum(self._root.Sector.SpecialSector, self._io.read_u2le())
+ self._debug['special_type']['end'] = self._io.pos()
+ self._debug['tag']['start'] = self._io.pos()
+ self.tag = self._io.read_u2le()
+ self._debug['tag']['end'] = self._io.pos()
+
+
+ class Vertexes(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.Vertex(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 Sidedef(KaitaiStruct):
+ SEQ_FIELDS = ["offset_x", "offset_y", "upper_texture_name", "lower_texture_name", "normal_texture_name", "sector_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['offset_x']['start'] = self._io.pos()
+ self.offset_x = self._io.read_s2le()
+ self._debug['offset_x']['end'] = self._io.pos()
+ self._debug['offset_y']['start'] = self._io.pos()
+ self.offset_y = self._io.read_s2le()
+ self._debug['offset_y']['end'] = self._io.pos()
+ self._debug['upper_texture_name']['start'] = self._io.pos()
+ self.upper_texture_name = (self._io.read_bytes(8)).decode(u"ASCII")
+ self._debug['upper_texture_name']['end'] = self._io.pos()
+ self._debug['lower_texture_name']['start'] = self._io.pos()
+ self.lower_texture_name = (self._io.read_bytes(8)).decode(u"ASCII")
+ self._debug['lower_texture_name']['end'] = self._io.pos()
+ self._debug['normal_texture_name']['start'] = self._io.pos()
+ self.normal_texture_name = (self._io.read_bytes(8)).decode(u"ASCII")
+ self._debug['normal_texture_name']['end'] = self._io.pos()
+ self._debug['sector_id']['start'] = self._io.pos()
+ self.sector_id = self._io.read_s2le()
+ self._debug['sector_id']['end'] = self._io.pos()
+
+
+ class Things(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.Thing(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 Linedefs(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.Linedef(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 IndexEntry(KaitaiStruct):
+ SEQ_FIELDS = ["offset", "size", "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['offset']['start'] = self._io.pos()
+ self.offset = self._io.read_s4le()
+ self._debug['offset']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._io.read_s4le()
+ self._debug['size']['end'] = self._io.pos()
+ self._debug['name']['start'] = self._io.pos()
+ self.name = (KaitaiStream.bytes_strip_right(self._io.read_bytes(8), 0)).decode(u"ASCII")
+ self._debug['name']['end'] = self._io.pos()
+
+ @property
+ def contents(self):
+ if hasattr(self, '_m_contents'):
+ return self._m_contents if hasattr(self, '_m_contents') else None
+
+ io = self._root._io
+ _pos = io.pos()
+ io.seek(self.offset)
+ self._debug['_m_contents']['start'] = io.pos()
+ _on = self.name
+ if _on == u"SECTORS":
+ self._raw__m_contents = io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw__m_contents))
+ self._m_contents = self._root.Sectors(io, self, self._root)
+ self._m_contents._read()
+ elif _on == u"TEXTURE1":
+ self._raw__m_contents = io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw__m_contents))
+ self._m_contents = self._root.Texture12(io, self, self._root)
+ self._m_contents._read()
+ elif _on == u"VERTEXES":
+ self._raw__m_contents = io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw__m_contents))
+ self._m_contents = self._root.Vertexes(io, self, self._root)
+ self._m_contents._read()
+ elif _on == u"BLOCKMAP":
+ self._raw__m_contents = io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw__m_contents))
+ self._m_contents = self._root.Blockmap(io, self, self._root)
+ self._m_contents._read()
+ elif _on == u"PNAMES":
+ self._raw__m_contents = io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw__m_contents))
+ self._m_contents = self._root.Pnames(io, self, self._root)
+ self._m_contents._read()
+ elif _on == u"TEXTURE2":
+ self._raw__m_contents = io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw__m_contents))
+ self._m_contents = self._root.Texture12(io, self, self._root)
+ self._m_contents._read()
+ elif _on == u"THINGS":
+ self._raw__m_contents = io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw__m_contents))
+ self._m_contents = self._root.Things(io, self, self._root)
+ self._m_contents._read()
+ elif _on == u"LINEDEFS":
+ self._raw__m_contents = io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw__m_contents))
+ self._m_contents = self._root.Linedefs(io, self, self._root)
+ self._m_contents._read()
+ elif _on == u"SIDEDEFS":
+ self._raw__m_contents = io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw__m_contents))
+ self._m_contents = self._root.Sidedefs(io, self, self._root)
+ self._m_contents._read()
+ else:
+ self._m_contents = io.read_bytes(self.size)
+ self._debug['_m_contents']['end'] = io.pos()
+ io.seek(_pos)
+ return self._m_contents if hasattr(self, '_m_contents') else None
+
+
+ class Sidedefs(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.Sidedef(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 Blockmap(KaitaiStruct):
+ SEQ_FIELDS = ["origin_x", "origin_y", "num_cols", "num_rows", "linedefs_in_block"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['origin_x']['start'] = self._io.pos()
+ self.origin_x = self._io.read_s2le()
+ self._debug['origin_x']['end'] = self._io.pos()
+ self._debug['origin_y']['start'] = self._io.pos()
+ self.origin_y = self._io.read_s2le()
+ self._debug['origin_y']['end'] = self._io.pos()
+ self._debug['num_cols']['start'] = self._io.pos()
+ self.num_cols = self._io.read_s2le()
+ self._debug['num_cols']['end'] = self._io.pos()
+ self._debug['num_rows']['start'] = self._io.pos()
+ self.num_rows = self._io.read_s2le()
+ self._debug['num_rows']['end'] = self._io.pos()
+ self._debug['linedefs_in_block']['start'] = self._io.pos()
+ self.linedefs_in_block = [None] * ((self.num_cols * self.num_rows))
+ for i in range((self.num_cols * self.num_rows)):
+ if not 'arr' in self._debug['linedefs_in_block']:
+ self._debug['linedefs_in_block']['arr'] = []
+ self._debug['linedefs_in_block']['arr'].append({'start': self._io.pos()})
+ _t_linedefs_in_block = self._root.Blockmap.Blocklist(self._io, self, self._root)
+ _t_linedefs_in_block._read()
+ self.linedefs_in_block[i] = _t_linedefs_in_block
+ self._debug['linedefs_in_block']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['linedefs_in_block']['end'] = self._io.pos()
+
+ class Blocklist(KaitaiStruct):
+ SEQ_FIELDS = ["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['offset']['start'] = self._io.pos()
+ self.offset = self._io.read_u2le()
+ self._debug['offset']['end'] = self._io.pos()
+
+ @property
+ def linedefs(self):
+ """List of linedefs found in this block."""
+ if hasattr(self, '_m_linedefs'):
+ return self._m_linedefs if hasattr(self, '_m_linedefs') else None
+
+ _pos = self._io.pos()
+ self._io.seek((self.offset * 2))
+ self._debug['_m_linedefs']['start'] = self._io.pos()
+ self._m_linedefs = []
+ i = 0
+ while True:
+ if not 'arr' in self._debug['_m_linedefs']:
+ self._debug['_m_linedefs']['arr'] = []
+ self._debug['_m_linedefs']['arr'].append({'start': self._io.pos()})
+ _ = self._io.read_s2le()
+ self._m_linedefs.append(_)
+ self._debug['_m_linedefs']['arr'][len(self._m_linedefs) - 1]['end'] = self._io.pos()
+ if _ == -1:
+ break
+ i += 1
+ self._debug['_m_linedefs']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_linedefs if hasattr(self, '_m_linedefs') else None
+
+
+
+ @property
+ def index(self):
+ if hasattr(self, '_m_index'):
+ return self._m_index if hasattr(self, '_m_index') else None
+
+ _pos = self._io.pos()
+ self._io.seek(self.index_offset)
+ self._debug['_m_index']['start'] = self._io.pos()
+ self._m_index = [None] * (self.num_index_entries)
+ for i in range(self.num_index_entries):
+ if not 'arr' in self._debug['_m_index']:
+ self._debug['_m_index']['arr'] = []
+ self._debug['_m_index']['arr'].append({'start': self._io.pos()})
+ _t__m_index = self._root.IndexEntry(self._io, self, self._root)
+ _t__m_index._read()
+ self._m_index[i] = _t__m_index
+ self._debug['_m_index']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['_m_index']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_index if hasattr(self, '_m_index') else None
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/dune_2_pak.py b/python/examples/kaitai/kaitai_struct_formats/game/dune_2_pak.py
new file mode 100644
index 00000000..e074b018
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/dune_2_pak.py
@@ -0,0 +1,122 @@
+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 Dune2Pak(KaitaiStruct):
+ SEQ_FIELDS = ["dir"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['dir']['start'] = self._io.pos()
+ self._raw_dir = self._io.read_bytes(self.dir_size)
+ io = KaitaiStream(BytesIO(self._raw_dir))
+ self.dir = self._root.Files(io, self, self._root)
+ self.dir._read()
+ self._debug['dir']['end'] = self._io.pos()
+
+ class Files(KaitaiStruct):
+ SEQ_FIELDS = ["files"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['files']['start'] = self._io.pos()
+ self.files = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['files']:
+ self._debug['files']['arr'] = []
+ self._debug['files']['arr'].append({'start': self._io.pos()})
+ _t_files = self._root.File(i, self._io, self, self._root)
+ _t_files._read()
+ self.files.append(_t_files)
+ self._debug['files']['arr'][len(self.files) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['files']['end'] = self._io.pos()
+
+
+ class File(KaitaiStruct):
+ SEQ_FIELDS = ["ofs", "file_name"]
+ def __init__(self, idx, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.idx = idx
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['ofs']['start'] = self._io.pos()
+ self.ofs = self._io.read_u4le()
+ self._debug['ofs']['end'] = self._io.pos()
+ if self.ofs != 0:
+ self._debug['file_name']['start'] = self._io.pos()
+ self.file_name = (self._io.read_bytes_term(0, False, True, True)).decode(u"ASCII")
+ self._debug['file_name']['end'] = self._io.pos()
+
+
+ @property
+ def next_ofs0(self):
+ if hasattr(self, '_m_next_ofs0'):
+ return self._m_next_ofs0 if hasattr(self, '_m_next_ofs0') else None
+
+ if self.ofs != 0:
+ self._m_next_ofs0 = self._root.dir.files[(self.idx + 1)].ofs
+
+ return self._m_next_ofs0 if hasattr(self, '_m_next_ofs0') else None
+
+ @property
+ def next_ofs(self):
+ if hasattr(self, '_m_next_ofs'):
+ return self._m_next_ofs if hasattr(self, '_m_next_ofs') else None
+
+ if self.ofs != 0:
+ self._m_next_ofs = (self._root._io.size() if self.next_ofs0 == 0 else self.next_ofs0)
+
+ return self._m_next_ofs if hasattr(self, '_m_next_ofs') else None
+
+ @property
+ def body(self):
+ if hasattr(self, '_m_body'):
+ return self._m_body if hasattr(self, '_m_body') else None
+
+ if self.ofs != 0:
+ io = self._root._io
+ _pos = io.pos()
+ io.seek(self.ofs)
+ self._debug['_m_body']['start'] = io.pos()
+ self._m_body = io.read_bytes((self.next_ofs - self.ofs))
+ self._debug['_m_body']['end'] = io.pos()
+ io.seek(_pos)
+
+ return self._m_body if hasattr(self, '_m_body') else None
+
+
+ @property
+ def dir_size(self):
+ if hasattr(self, '_m_dir_size'):
+ return self._m_dir_size if hasattr(self, '_m_dir_size') else None
+
+ _pos = self._io.pos()
+ self._io.seek(0)
+ self._debug['_m_dir_size']['start'] = self._io.pos()
+ self._m_dir_size = self._io.read_u4le()
+ self._debug['_m_dir_size']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_dir_size if hasattr(self, '_m_dir_size') else None
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/fallout2_dat.py b/python/examples/kaitai/kaitai_struct_formats/game/fallout2_dat.py
new file mode 100644
index 00000000..e22fc0d5
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/fallout2_dat.py
@@ -0,0 +1,161 @@
+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
+import zlib
+
+
+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 Fallout2Dat(KaitaiStruct):
+
+ class Compression(Enum):
+ none = 0
+ zlib = 1
+ 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 Pstr(KaitaiStruct):
+ SEQ_FIELDS = ["size", "str"]
+ def __init__(self, _io, _parent=None, _root=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_u4le()
+ self._debug['size']['end'] = self._io.pos()
+ self._debug['str']['start'] = self._io.pos()
+ self.str = (self._io.read_bytes(self.size)).decode(u"ASCII")
+ self._debug['str']['end'] = self._io.pos()
+
+
+ class Footer(KaitaiStruct):
+ SEQ_FIELDS = ["index_size", "file_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['index_size']['start'] = self._io.pos()
+ self.index_size = self._io.read_u4le()
+ self._debug['index_size']['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()
+
+
+ class Index(KaitaiStruct):
+ SEQ_FIELDS = ["file_count", "files"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['file_count']['start'] = self._io.pos()
+ self.file_count = self._io.read_u4le()
+ self._debug['file_count']['end'] = self._io.pos()
+ self._debug['files']['start'] = self._io.pos()
+ self.files = [None] * (self.file_count)
+ for i in range(self.file_count):
+ if not 'arr' in self._debug['files']:
+ self._debug['files']['arr'] = []
+ self._debug['files']['arr'].append({'start': self._io.pos()})
+ _t_files = self._root.File(self._io, self, self._root)
+ _t_files._read()
+ self.files[i] = _t_files
+ self._debug['files']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['files']['end'] = self._io.pos()
+
+
+ class File(KaitaiStruct):
+ SEQ_FIELDS = ["name", "flags", "size_unpacked", "size_packed", "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['name']['start'] = self._io.pos()
+ self.name = self._root.Pstr(self._io, self, self._root)
+ self.name._read()
+ self._debug['name']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = KaitaiStream.resolve_enum(self._root.Compression, self._io.read_u1())
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['size_unpacked']['start'] = self._io.pos()
+ self.size_unpacked = self._io.read_u4le()
+ self._debug['size_unpacked']['end'] = self._io.pos()
+ self._debug['size_packed']['start'] = self._io.pos()
+ self.size_packed = self._io.read_u4le()
+ self._debug['size_packed']['end'] = self._io.pos()
+ self._debug['offset']['start'] = self._io.pos()
+ self.offset = self._io.read_u4le()
+ self._debug['offset']['end'] = self._io.pos()
+
+ @property
+ def contents(self):
+ if hasattr(self, '_m_contents'):
+ return self._m_contents if hasattr(self, '_m_contents') else None
+
+ if self.flags == self._root.Compression.zlib:
+ io = self._root._io
+ _pos = io.pos()
+ io.seek(self.offset)
+ self._debug['_m_contents']['start'] = io.pos()
+ self._raw__m_contents = io.read_bytes(self.size_packed)
+ self._m_contents = zlib.decompress(self._raw__m_contents)
+ self._debug['_m_contents']['end'] = io.pos()
+ io.seek(_pos)
+
+ return self._m_contents if hasattr(self, '_m_contents') else None
+
+
+ @property
+ def footer(self):
+ if hasattr(self, '_m_footer'):
+ return self._m_footer if hasattr(self, '_m_footer') else None
+
+ _pos = self._io.pos()
+ self._io.seek((self._io.size() - 8))
+ self._debug['_m_footer']['start'] = self._io.pos()
+ self._m_footer = self._root.Footer(self._io, self, self._root)
+ self._m_footer._read()
+ self._debug['_m_footer']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_footer if hasattr(self, '_m_footer') else None
+
+ @property
+ def index(self):
+ if hasattr(self, '_m_index'):
+ return self._m_index if hasattr(self, '_m_index') else None
+
+ _pos = self._io.pos()
+ self._io.seek(((self._io.size() - 8) - self.footer.index_size))
+ self._debug['_m_index']['start'] = self._io.pos()
+ self._m_index = self._root.Index(self._io, self, self._root)
+ self._m_index._read()
+ self._debug['_m_index']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_index if hasattr(self, '_m_index') else None
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/fallout_dat.py b/python/examples/kaitai/kaitai_struct_formats/game/fallout_dat.py
new file mode 100644
index 00000000..4ce06191
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/fallout_dat.py
@@ -0,0 +1,156 @@
+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 FalloutDat(KaitaiStruct):
+
+ class Compression(Enum):
+ none = 32
+ lzss = 64
+ SEQ_FIELDS = ["folder_count", "unknown1", "unknown2", "timestamp", "folder_names", "folders"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['folder_count']['start'] = self._io.pos()
+ self.folder_count = self._io.read_u4be()
+ self._debug['folder_count']['end'] = self._io.pos()
+ self._debug['unknown1']['start'] = self._io.pos()
+ self.unknown1 = self._io.read_u4be()
+ self._debug['unknown1']['end'] = self._io.pos()
+ self._debug['unknown2']['start'] = self._io.pos()
+ self.unknown2 = self._io.read_u4be()
+ self._debug['unknown2']['end'] = self._io.pos()
+ self._debug['timestamp']['start'] = self._io.pos()
+ self.timestamp = self._io.read_u4be()
+ self._debug['timestamp']['end'] = self._io.pos()
+ self._debug['folder_names']['start'] = self._io.pos()
+ self.folder_names = [None] * (self.folder_count)
+ for i in range(self.folder_count):
+ if not 'arr' in self._debug['folder_names']:
+ self._debug['folder_names']['arr'] = []
+ self._debug['folder_names']['arr'].append({'start': self._io.pos()})
+ _t_folder_names = self._root.Pstr(self._io, self, self._root)
+ _t_folder_names._read()
+ self.folder_names[i] = _t_folder_names
+ self._debug['folder_names']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['folder_names']['end'] = self._io.pos()
+ self._debug['folders']['start'] = self._io.pos()
+ self.folders = [None] * (self.folder_count)
+ for i in range(self.folder_count):
+ if not 'arr' in self._debug['folders']:
+ self._debug['folders']['arr'] = []
+ self._debug['folders']['arr'].append({'start': self._io.pos()})
+ _t_folders = self._root.Folder(self._io, self, self._root)
+ _t_folders._read()
+ self.folders[i] = _t_folders
+ self._debug['folders']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['folders']['end'] = self._io.pos()
+
+ class Pstr(KaitaiStruct):
+ SEQ_FIELDS = ["size", "str"]
+ def __init__(self, _io, _parent=None, _root=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_u1()
+ self._debug['size']['end'] = self._io.pos()
+ self._debug['str']['start'] = self._io.pos()
+ self.str = (self._io.read_bytes(self.size)).decode(u"ASCII")
+ self._debug['str']['end'] = self._io.pos()
+
+
+ class Folder(KaitaiStruct):
+ SEQ_FIELDS = ["file_count", "unknown", "flags", "timestamp", "files"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['file_count']['start'] = self._io.pos()
+ self.file_count = self._io.read_u4be()
+ self._debug['file_count']['end'] = self._io.pos()
+ self._debug['unknown']['start'] = self._io.pos()
+ self.unknown = self._io.read_u4be()
+ self._debug['unknown']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_u4be()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['timestamp']['start'] = self._io.pos()
+ self.timestamp = self._io.read_u4be()
+ self._debug['timestamp']['end'] = self._io.pos()
+ self._debug['files']['start'] = self._io.pos()
+ self.files = [None] * (self.file_count)
+ for i in range(self.file_count):
+ if not 'arr' in self._debug['files']:
+ self._debug['files']['arr'] = []
+ self._debug['files']['arr'].append({'start': self._io.pos()})
+ _t_files = self._root.File(self._io, self, self._root)
+ _t_files._read()
+ self.files[i] = _t_files
+ self._debug['files']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['files']['end'] = self._io.pos()
+
+
+ class File(KaitaiStruct):
+ SEQ_FIELDS = ["name", "flags", "offset", "size_unpacked", "size_packed"]
+ def __init__(self, _io, _parent=None, _root=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 = self._root.Pstr(self._io, self, self._root)
+ self.name._read()
+ self._debug['name']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = KaitaiStream.resolve_enum(self._root.Compression, self._io.read_u4be())
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['offset']['start'] = self._io.pos()
+ self.offset = self._io.read_u4be()
+ self._debug['offset']['end'] = self._io.pos()
+ self._debug['size_unpacked']['start'] = self._io.pos()
+ self.size_unpacked = self._io.read_u4be()
+ self._debug['size_unpacked']['end'] = self._io.pos()
+ self._debug['size_packed']['start'] = self._io.pos()
+ self.size_packed = self._io.read_u4be()
+ self._debug['size_packed']['end'] = self._io.pos()
+
+ @property
+ def contents(self):
+ if hasattr(self, '_m_contents'):
+ return self._m_contents if hasattr(self, '_m_contents') else None
+
+ io = self._root._io
+ _pos = io.pos()
+ io.seek(self.offset)
+ self._debug['_m_contents']['start'] = io.pos()
+ self._m_contents = io.read_bytes((self.size_unpacked if self.flags == self._root.Compression.none else self.size_packed))
+ self._debug['_m_contents']['end'] = io.pos()
+ io.seek(_pos)
+ return self._m_contents if hasattr(self, '_m_contents') else None
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/ftl_dat.py b/python/examples/kaitai/kaitai_struct_formats/game/ftl_dat.py
new file mode 100644
index 00000000..d56d478a
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/ftl_dat.py
@@ -0,0 +1,90 @@
+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 FtlDat(KaitaiStruct):
+ SEQ_FIELDS = ["num_files", "files"]
+ def __init__(self, _io, _parent=None, _root=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_files']['start'] = self._io.pos()
+ self.num_files = self._io.read_u4le()
+ self._debug['num_files']['end'] = self._io.pos()
+ self._debug['files']['start'] = self._io.pos()
+ self.files = [None] * (self.num_files)
+ for i in range(self.num_files):
+ if not 'arr' in self._debug['files']:
+ self._debug['files']['arr'] = []
+ self._debug['files']['arr'].append({'start': self._io.pos()})
+ _t_files = self._root.File(self._io, self, self._root)
+ _t_files._read()
+ self.files[i] = _t_files
+ self._debug['files']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['files']['end'] = self._io.pos()
+
+ class File(KaitaiStruct):
+ SEQ_FIELDS = ["ofs_meta"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['ofs_meta']['start'] = self._io.pos()
+ self.ofs_meta = self._io.read_u4le()
+ self._debug['ofs_meta']['end'] = self._io.pos()
+
+ @property
+ def meta(self):
+ if hasattr(self, '_m_meta'):
+ return self._m_meta if hasattr(self, '_m_meta') else None
+
+ if self.ofs_meta != 0:
+ _pos = self._io.pos()
+ self._io.seek(self.ofs_meta)
+ self._debug['_m_meta']['start'] = self._io.pos()
+ self._m_meta = self._root.Meta(self._io, self, self._root)
+ self._m_meta._read()
+ self._debug['_m_meta']['end'] = self._io.pos()
+ self._io.seek(_pos)
+
+ return self._m_meta if hasattr(self, '_m_meta') else None
+
+
+ class Meta(KaitaiStruct):
+ SEQ_FIELDS = ["len_file", "len_filename", "filename", "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['len_file']['start'] = self._io.pos()
+ self.len_file = self._io.read_u4le()
+ self._debug['len_file']['end'] = self._io.pos()
+ self._debug['len_filename']['start'] = self._io.pos()
+ self.len_filename = self._io.read_u4le()
+ self._debug['len_filename']['end'] = self._io.pos()
+ self._debug['filename']['start'] = self._io.pos()
+ self.filename = (self._io.read_bytes(self.len_filename)).decode(u"UTF-8")
+ self._debug['filename']['end'] = self._io.pos()
+ self._debug['body']['start'] = self._io.pos()
+ self.body = self._io.read_bytes(self.len_file)
+ self._debug['body']['end'] = self._io.pos()
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/gran_turismo_vol.py b/python/examples/kaitai/kaitai_struct_formats/game/gran_turismo_vol.py
new file mode 100644
index 00000000..1b76c226
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/gran_turismo_vol.py
@@ -0,0 +1,136 @@
+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 GranTurismoVol(KaitaiStruct):
+ SEQ_FIELDS = ["magic", "num_files", "num_entries", "reserved", "offsets"]
+ def __init__(self, _io, _parent=None, _root=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"\x47\x54\x46\x53\x00\x00\x00\x00")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['num_files']['start'] = self._io.pos()
+ self.num_files = self._io.read_u2le()
+ self._debug['num_files']['end'] = self._io.pos()
+ self._debug['num_entries']['start'] = self._io.pos()
+ self.num_entries = self._io.read_u2le()
+ self._debug['num_entries']['end'] = self._io.pos()
+ self._debug['reserved']['start'] = self._io.pos()
+ self.reserved = self._io.ensure_fixed_contents(b"\x00\x00\x00\x00")
+ self._debug['reserved']['end'] = self._io.pos()
+ self._debug['offsets']['start'] = self._io.pos()
+ self.offsets = [None] * (self.num_files)
+ for i in range(self.num_files):
+ if not 'arr' in self._debug['offsets']:
+ self._debug['offsets']['arr'] = []
+ self._debug['offsets']['arr'].append({'start': self._io.pos()})
+ self.offsets[i] = self._io.read_u4le()
+ self._debug['offsets']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['offsets']['end'] = self._io.pos()
+
+ class FileInfo(KaitaiStruct):
+ SEQ_FIELDS = ["timestamp", "offset_idx", "flags", "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['timestamp']['start'] = self._io.pos()
+ self.timestamp = self._io.read_u4le()
+ self._debug['timestamp']['end'] = self._io.pos()
+ self._debug['offset_idx']['start'] = self._io.pos()
+ self.offset_idx = self._io.read_u2le()
+ self._debug['offset_idx']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_u1()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['name']['start'] = self._io.pos()
+ self.name = (KaitaiStream.bytes_terminate(KaitaiStream.bytes_strip_right(self._io.read_bytes(25), 0), 0, False)).decode(u"ASCII")
+ self._debug['name']['end'] = self._io.pos()
+
+ @property
+ def size(self):
+ if hasattr(self, '_m_size'):
+ return self._m_size if hasattr(self, '_m_size') else None
+
+ self._m_size = ((self._root.offsets[(self.offset_idx + 1)] & 4294965248) - self._root.offsets[self.offset_idx])
+ return self._m_size if hasattr(self, '_m_size') else None
+
+ @property
+ def body(self):
+ if hasattr(self, '_m_body'):
+ return self._m_body if hasattr(self, '_m_body') else None
+
+ if not (self.is_dir):
+ _pos = self._io.pos()
+ self._io.seek((self._root.offsets[self.offset_idx] & 4294965248))
+ self._debug['_m_body']['start'] = self._io.pos()
+ self._m_body = self._io.read_bytes(self.size)
+ self._debug['_m_body']['end'] = self._io.pos()
+ self._io.seek(_pos)
+
+ return self._m_body if hasattr(self, '_m_body') else None
+
+ @property
+ def is_dir(self):
+ if hasattr(self, '_m_is_dir'):
+ return self._m_is_dir if hasattr(self, '_m_is_dir') else None
+
+ self._m_is_dir = (self.flags & 1) != 0
+ return self._m_is_dir if hasattr(self, '_m_is_dir') else None
+
+ @property
+ def is_last_entry(self):
+ if hasattr(self, '_m_is_last_entry'):
+ return self._m_is_last_entry if hasattr(self, '_m_is_last_entry') else None
+
+ self._m_is_last_entry = (self.flags & 128) != 0
+ return self._m_is_last_entry if hasattr(self, '_m_is_last_entry') else None
+
+
+ @property
+ def ofs_dir(self):
+ if hasattr(self, '_m_ofs_dir'):
+ return self._m_ofs_dir if hasattr(self, '_m_ofs_dir') else None
+
+ self._m_ofs_dir = self.offsets[1]
+ return self._m_ofs_dir if hasattr(self, '_m_ofs_dir') else None
+
+ @property
+ def files(self):
+ if hasattr(self, '_m_files'):
+ return self._m_files if hasattr(self, '_m_files') else None
+
+ _pos = self._io.pos()
+ self._io.seek((self.ofs_dir & 4294965248))
+ self._debug['_m_files']['start'] = self._io.pos()
+ self._m_files = [None] * (self._root.num_entries)
+ for i in range(self._root.num_entries):
+ if not 'arr' in self._debug['_m_files']:
+ self._debug['_m_files']['arr'] = []
+ self._debug['_m_files']['arr'].append({'start': self._io.pos()})
+ _t__m_files = self._root.FileInfo(self._io, self, self._root)
+ _t__m_files._read()
+ self._m_files[i] = _t__m_files
+ self._debug['_m_files']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['_m_files']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_files if hasattr(self, '_m_files') else None
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/heaps_pak.py b/python/examples/kaitai/kaitai_struct_formats/game/heaps_pak.py
new file mode 100644
index 00000000..63937a47
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/heaps_pak.py
@@ -0,0 +1,173 @@
+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 HeapsPak(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://github.com/HeapsIO/heaps/blob/2bbc2b386952dfd8856c04a854bb706a52cb4b58/hxd/fmt/pak/Reader.hx
+ """
+ SEQ_FIELDS = ["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['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()
+
+ class Header(KaitaiStruct):
+ SEQ_FIELDS = ["magic1", "version", "len_header", "len_data", "root_entry", "magic2"]
+ def __init__(self, _io, _parent=None, _root=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"\x50\x41\x4B")
+ self._debug['magic1']['end'] = self._io.pos()
+ self._debug['version']['start'] = self._io.pos()
+ self.version = self._io.read_u1()
+ self._debug['version']['end'] = self._io.pos()
+ self._debug['len_header']['start'] = self._io.pos()
+ self.len_header = self._io.read_u4le()
+ self._debug['len_header']['end'] = self._io.pos()
+ self._debug['len_data']['start'] = self._io.pos()
+ self.len_data = self._io.read_u4le()
+ self._debug['len_data']['end'] = self._io.pos()
+ self._debug['root_entry']['start'] = self._io.pos()
+ self._raw_root_entry = self._io.read_bytes((self.len_header - 16))
+ io = KaitaiStream(BytesIO(self._raw_root_entry))
+ self.root_entry = self._root.Header.Entry(io, self, self._root)
+ self.root_entry._read()
+ self._debug['root_entry']['end'] = self._io.pos()
+ self._debug['magic2']['start'] = self._io.pos()
+ self.magic2 = self._io.ensure_fixed_contents(b"\x44\x41\x54\x41")
+ self._debug['magic2']['end'] = self._io.pos()
+
+ class Entry(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://github.com/HeapsIO/heaps/blob/2bbc2b386952dfd8856c04a854bb706a52cb4b58/hxd/fmt/pak/Data.hx
+ """
+ SEQ_FIELDS = ["len_name", "name", "flags", "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['len_name']['start'] = self._io.pos()
+ self.len_name = self._io.read_u1()
+ self._debug['len_name']['end'] = self._io.pos()
+ self._debug['name']['start'] = self._io.pos()
+ self.name = (self._io.read_bytes(self.len_name)).decode(u"UTF-8")
+ self._debug['name']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._root.Header.Entry.Flags(self._io, self, self._root)
+ self.flags._read()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['body']['start'] = self._io.pos()
+ _on = self.flags.is_dir
+ if _on == True:
+ self.body = self._root.Header.Dir(self._io, self, self._root)
+ self.body._read()
+ elif _on == False:
+ self.body = self._root.Header.File(self._io, self, self._root)
+ self.body._read()
+ self._debug['body']['end'] = self._io.pos()
+
+ class Flags(KaitaiStruct):
+ SEQ_FIELDS = ["unused", "is_dir"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['unused']['start'] = self._io.pos()
+ self.unused = self._io.read_bits_int(7)
+ self._debug['unused']['end'] = self._io.pos()
+ self._debug['is_dir']['start'] = self._io.pos()
+ self.is_dir = self._io.read_bits_int(1) != 0
+ self._debug['is_dir']['end'] = self._io.pos()
+
+
+
+ class File(KaitaiStruct):
+ SEQ_FIELDS = ["ofs_data", "len_data", "checksum"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['ofs_data']['start'] = self._io.pos()
+ self.ofs_data = self._io.read_u4le()
+ self._debug['ofs_data']['end'] = self._io.pos()
+ self._debug['len_data']['start'] = self._io.pos()
+ self.len_data = self._io.read_u4le()
+ self._debug['len_data']['end'] = self._io.pos()
+ self._debug['checksum']['start'] = self._io.pos()
+ self.checksum = self._io.read_bytes(4)
+ self._debug['checksum']['end'] = self._io.pos()
+
+ @property
+ def data(self):
+ if hasattr(self, '_m_data'):
+ return self._m_data if hasattr(self, '_m_data') else None
+
+ io = self._root._io
+ _pos = io.pos()
+ io.seek((self._root.header.len_header + self.ofs_data))
+ self._debug['_m_data']['start'] = io.pos()
+ self._m_data = io.read_bytes(self.len_data)
+ self._debug['_m_data']['end'] = io.pos()
+ io.seek(_pos)
+ return self._m_data if hasattr(self, '_m_data') else None
+
+
+ class Dir(KaitaiStruct):
+ SEQ_FIELDS = ["num_entries", "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['num_entries']['start'] = self._io.pos()
+ self.num_entries = self._io.read_u4le()
+ self._debug['num_entries']['end'] = self._io.pos()
+ self._debug['entries']['start'] = self._io.pos()
+ self.entries = [None] * (self.num_entries)
+ for i in range(self.num_entries):
+ if not 'arr' in self._debug['entries']:
+ self._debug['entries']['arr'] = []
+ self._debug['entries']['arr'].append({'start': self._io.pos()})
+ _t_entries = self._root.Header.Entry(self._io, self, self._root)
+ _t_entries._read()
+ self.entries[i] = _t_entries
+ self._debug['entries']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['entries']['end'] = self._io.pos()
+
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/heroes_of_might_and_magic_agg.py b/python/examples/kaitai/kaitai_struct_formats/game/heroes_of_might_and_magic_agg.py
new file mode 100644
index 00000000..caff6a27
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/heroes_of_might_and_magic_agg.py
@@ -0,0 +1,116 @@
+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 HeroesOfMightAndMagicAgg(KaitaiStruct):
+ """
+ .. seealso::
+ Source - http://rewiki.regengedanken.de/wiki/.AGG_(Heroes_of_Might_and_Magic)
+ """
+ SEQ_FIELDS = ["num_files", "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['num_files']['start'] = self._io.pos()
+ self.num_files = self._io.read_u2le()
+ self._debug['num_files']['end'] = self._io.pos()
+ self._debug['entries']['start'] = self._io.pos()
+ self.entries = [None] * (self.num_files)
+ for i in range(self.num_files):
+ if not 'arr' in self._debug['entries']:
+ self._debug['entries']['arr'] = []
+ self._debug['entries']['arr'].append({'start': self._io.pos()})
+ _t_entries = self._root.Entry(self._io, self, self._root)
+ _t_entries._read()
+ self.entries[i] = _t_entries
+ self._debug['entries']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['entries']['end'] = self._io.pos()
+
+ class Entry(KaitaiStruct):
+ SEQ_FIELDS = ["hash", "offset", "size", "size2"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['hash']['start'] = self._io.pos()
+ self.hash = self._io.read_u2le()
+ self._debug['hash']['end'] = self._io.pos()
+ self._debug['offset']['start'] = self._io.pos()
+ self.offset = self._io.read_u4le()
+ self._debug['offset']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._io.read_u4le()
+ self._debug['size']['end'] = self._io.pos()
+ self._debug['size2']['start'] = self._io.pos()
+ self.size2 = self._io.read_u4le()
+ self._debug['size2']['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.offset)
+ self._debug['_m_body']['start'] = self._io.pos()
+ self._m_body = self._io.read_bytes(self.size)
+ self._debug['_m_body']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_body if hasattr(self, '_m_body') else None
+
+
+ class Filename(KaitaiStruct):
+ SEQ_FIELDS = ["str"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['str']['start'] = self._io.pos()
+ self.str = (self._io.read_bytes_term(0, False, True, True)).decode(u"ASCII")
+ self._debug['str']['end'] = self._io.pos()
+
+
+ @property
+ def filenames(self):
+ if hasattr(self, '_m_filenames'):
+ return self._m_filenames if hasattr(self, '_m_filenames') else None
+
+ _pos = self._io.pos()
+ self._io.seek((self.entries[-1].offset + self.entries[-1].size))
+ self._debug['_m_filenames']['start'] = self._io.pos()
+ self._raw__m_filenames = [None] * (self.num_files)
+ self._m_filenames = [None] * (self.num_files)
+ for i in range(self.num_files):
+ if not 'arr' in self._debug['_m_filenames']:
+ self._debug['_m_filenames']['arr'] = []
+ self._debug['_m_filenames']['arr'].append({'start': self._io.pos()})
+ self._raw__m_filenames[i] = self._io.read_bytes(15)
+ io = KaitaiStream(BytesIO(self._raw__m_filenames[i]))
+ _t__m_filenames = self._root.Filename(io, self, self._root)
+ _t__m_filenames._read()
+ self._m_filenames[i] = _t__m_filenames
+ self._debug['_m_filenames']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['_m_filenames']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_filenames if hasattr(self, '_m_filenames') else None
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/heroes_of_might_and_magic_bmp.py b/python/examples/kaitai/kaitai_struct_formats/game/heroes_of_might_and_magic_bmp.py
new file mode 100644
index 00000000..cc02696a
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/heroes_of_might_and_magic_bmp.py
@@ -0,0 +1,34 @@
+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 HeroesOfMightAndMagicBmp(KaitaiStruct):
+ SEQ_FIELDS = ["magic", "width", "height", "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['magic']['start'] = self._io.pos()
+ self.magic = self._io.read_u2le()
+ self._debug['magic']['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['data']['start'] = self._io.pos()
+ self.data = self._io.read_bytes((self.width * self.height))
+ self._debug['data']['end'] = self._io.pos()
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/quake_mdl.py b/python/examples/kaitai/kaitai_struct_formats/game/quake_mdl.py
new file mode 100644
index 00000000..7077e8f8
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/quake_mdl.py
@@ -0,0 +1,377 @@
+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 QuakeMdl(KaitaiStruct):
+ SEQ_FIELDS = ["header", "skins", "texture_coordinates", "triangles", "frames"]
+ def __init__(self, _io, _parent=None, _root=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.MdlHeader(self._io, self, self._root)
+ self.header._read()
+ self._debug['header']['end'] = self._io.pos()
+ self._debug['skins']['start'] = self._io.pos()
+ self.skins = [None] * (self.header.num_skins)
+ for i in range(self.header.num_skins):
+ if not 'arr' in self._debug['skins']:
+ self._debug['skins']['arr'] = []
+ self._debug['skins']['arr'].append({'start': self._io.pos()})
+ _t_skins = self._root.MdlSkin(self._io, self, self._root)
+ _t_skins._read()
+ self.skins[i] = _t_skins
+ self._debug['skins']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['skins']['end'] = self._io.pos()
+ self._debug['texture_coordinates']['start'] = self._io.pos()
+ self.texture_coordinates = [None] * (self.header.num_verts)
+ for i in range(self.header.num_verts):
+ if not 'arr' in self._debug['texture_coordinates']:
+ self._debug['texture_coordinates']['arr'] = []
+ self._debug['texture_coordinates']['arr'].append({'start': self._io.pos()})
+ _t_texture_coordinates = self._root.MdlTexcoord(self._io, self, self._root)
+ _t_texture_coordinates._read()
+ self.texture_coordinates[i] = _t_texture_coordinates
+ self._debug['texture_coordinates']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['texture_coordinates']['end'] = self._io.pos()
+ self._debug['triangles']['start'] = self._io.pos()
+ self.triangles = [None] * (self.header.num_tris)
+ for i in range(self.header.num_tris):
+ if not 'arr' in self._debug['triangles']:
+ self._debug['triangles']['arr'] = []
+ self._debug['triangles']['arr'].append({'start': self._io.pos()})
+ _t_triangles = self._root.MdlTriangle(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()
+ self._debug['frames']['start'] = self._io.pos()
+ self.frames = [None] * (self.header.num_frames)
+ for i in range(self.header.num_frames):
+ if not 'arr' in self._debug['frames']:
+ self._debug['frames']['arr'] = []
+ self._debug['frames']['arr'].append({'start': self._io.pos()})
+ _t_frames = self._root.MdlFrame(self._io, self, self._root)
+ _t_frames._read()
+ self.frames[i] = _t_frames
+ self._debug['frames']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['frames']['end'] = self._io.pos()
+
+ class MdlVertex(KaitaiStruct):
+ SEQ_FIELDS = ["values", "normal_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['values']['start'] = self._io.pos()
+ self.values = [None] * (3)
+ for i in range(3):
+ if not 'arr' in self._debug['values']:
+ self._debug['values']['arr'] = []
+ self._debug['values']['arr'].append({'start': self._io.pos()})
+ self.values[i] = self._io.read_u1()
+ self._debug['values']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['values']['end'] = self._io.pos()
+ self._debug['normal_index']['start'] = self._io.pos()
+ self.normal_index = self._io.read_u1()
+ self._debug['normal_index']['end'] = self._io.pos()
+
+
+ class MdlTexcoord(KaitaiStruct):
+ SEQ_FIELDS = ["on_seam", "s", "t"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['on_seam']['start'] = self._io.pos()
+ self.on_seam = self._io.read_s4le()
+ self._debug['on_seam']['end'] = self._io.pos()
+ self._debug['s']['start'] = self._io.pos()
+ self.s = self._io.read_s4le()
+ self._debug['s']['end'] = self._io.pos()
+ self._debug['t']['start'] = self._io.pos()
+ self.t = self._io.read_s4le()
+ self._debug['t']['end'] = self._io.pos()
+
+
+ class MdlHeader(KaitaiStruct):
+ SEQ_FIELDS = ["ident", "version_must_be_6", "scale", "origin", "radius", "eye_position", "num_skins", "skin_width", "skin_height", "num_verts", "num_tris", "num_frames", "synctype", "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['ident']['start'] = self._io.pos()
+ self.ident = self._io.ensure_fixed_contents(b"\x49\x44\x50\x4F")
+ self._debug['ident']['end'] = self._io.pos()
+ self._debug['version_must_be_6']['start'] = self._io.pos()
+ self.version_must_be_6 = self._io.ensure_fixed_contents(b"\x06\x00\x00\x00")
+ self._debug['version_must_be_6']['end'] = self._io.pos()
+ self._debug['scale']['start'] = self._io.pos()
+ self.scale = self._root.Vec3(self._io, self, self._root)
+ self.scale._read()
+ self._debug['scale']['end'] = self._io.pos()
+ self._debug['origin']['start'] = self._io.pos()
+ self.origin = self._root.Vec3(self._io, self, self._root)
+ self.origin._read()
+ self._debug['origin']['end'] = self._io.pos()
+ self._debug['radius']['start'] = self._io.pos()
+ self.radius = self._io.read_f4le()
+ self._debug['radius']['end'] = self._io.pos()
+ self._debug['eye_position']['start'] = self._io.pos()
+ self.eye_position = self._root.Vec3(self._io, self, self._root)
+ self.eye_position._read()
+ self._debug['eye_position']['end'] = self._io.pos()
+ self._debug['num_skins']['start'] = self._io.pos()
+ self.num_skins = self._io.read_s4le()
+ self._debug['num_skins']['end'] = self._io.pos()
+ self._debug['skin_width']['start'] = self._io.pos()
+ self.skin_width = self._io.read_s4le()
+ self._debug['skin_width']['end'] = self._io.pos()
+ self._debug['skin_height']['start'] = self._io.pos()
+ self.skin_height = self._io.read_s4le()
+ self._debug['skin_height']['end'] = self._io.pos()
+ self._debug['num_verts']['start'] = self._io.pos()
+ self.num_verts = self._io.read_s4le()
+ self._debug['num_verts']['end'] = self._io.pos()
+ self._debug['num_tris']['start'] = self._io.pos()
+ self.num_tris = self._io.read_s4le()
+ self._debug['num_tris']['end'] = self._io.pos()
+ self._debug['num_frames']['start'] = self._io.pos()
+ self.num_frames = self._io.read_s4le()
+ self._debug['num_frames']['end'] = self._io.pos()
+ self._debug['synctype']['start'] = self._io.pos()
+ self.synctype = self._io.read_s4le()
+ self._debug['synctype']['end'] = self._io.pos()
+ self._debug['flags']['start'] = self._io.pos()
+ self.flags = self._io.read_s4le()
+ self._debug['flags']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._io.read_f4le()
+ self._debug['size']['end'] = self._io.pos()
+
+ @property
+ def version(self):
+ if hasattr(self, '_m_version'):
+ return self._m_version if hasattr(self, '_m_version') else None
+
+ self._m_version = 6
+ return self._m_version if hasattr(self, '_m_version') else None
+
+ @property
+ def skin_size(self):
+ if hasattr(self, '_m_skin_size'):
+ return self._m_skin_size if hasattr(self, '_m_skin_size') else None
+
+ self._m_skin_size = (self.skin_width * self.skin_height)
+ return self._m_skin_size if hasattr(self, '_m_skin_size') else None
+
+
+ class MdlSkin(KaitaiStruct):
+ SEQ_FIELDS = ["group", "single_texture_data", "num_frames", "frame_times", "group_texture_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['group']['start'] = self._io.pos()
+ self.group = self._io.read_s4le()
+ self._debug['group']['end'] = self._io.pos()
+ if self.group == 0:
+ self._debug['single_texture_data']['start'] = self._io.pos()
+ self.single_texture_data = self._io.read_bytes(self._root.header.skin_size)
+ self._debug['single_texture_data']['end'] = self._io.pos()
+
+ if self.group != 0:
+ self._debug['num_frames']['start'] = self._io.pos()
+ self.num_frames = self._io.read_u4le()
+ self._debug['num_frames']['end'] = self._io.pos()
+
+ if self.group != 0:
+ self._debug['frame_times']['start'] = self._io.pos()
+ self.frame_times = [None] * (self.num_frames)
+ for i in range(self.num_frames):
+ if not 'arr' in self._debug['frame_times']:
+ self._debug['frame_times']['arr'] = []
+ self._debug['frame_times']['arr'].append({'start': self._io.pos()})
+ self.frame_times[i] = self._io.read_f4le()
+ self._debug['frame_times']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['frame_times']['end'] = self._io.pos()
+
+ if self.group != 0:
+ self._debug['group_texture_data']['start'] = self._io.pos()
+ self.group_texture_data = [None] * (self.num_frames)
+ for i in range(self.num_frames):
+ if not 'arr' in self._debug['group_texture_data']:
+ self._debug['group_texture_data']['arr'] = []
+ self._debug['group_texture_data']['arr'].append({'start': self._io.pos()})
+ self.group_texture_data[i] = self._io.read_bytes(self._root.header.skin_size)
+ self._debug['group_texture_data']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['group_texture_data']['end'] = self._io.pos()
+
+
+
+ class MdlFrame(KaitaiStruct):
+ SEQ_FIELDS = ["type", "min", "max", "time", "frames"]
+ def __init__(self, _io, _parent=None, _root=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 = self._io.read_s4le()
+ self._debug['type']['end'] = self._io.pos()
+ if self.type != 0:
+ self._debug['min']['start'] = self._io.pos()
+ self.min = self._root.MdlVertex(self._io, self, self._root)
+ self.min._read()
+ self._debug['min']['end'] = self._io.pos()
+
+ if self.type != 0:
+ self._debug['max']['start'] = self._io.pos()
+ self.max = self._root.MdlVertex(self._io, self, self._root)
+ self.max._read()
+ self._debug['max']['end'] = self._io.pos()
+
+ if self.type != 0:
+ self._debug['time']['start'] = self._io.pos()
+ self.time = [None] * (self.type)
+ for i in range(self.type):
+ if not 'arr' in self._debug['time']:
+ self._debug['time']['arr'] = []
+ self._debug['time']['arr'].append({'start': self._io.pos()})
+ self.time[i] = self._io.read_f4le()
+ self._debug['time']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['time']['end'] = self._io.pos()
+
+ self._debug['frames']['start'] = self._io.pos()
+ self.frames = [None] * (self.num_simple_frames)
+ for i in range(self.num_simple_frames):
+ if not 'arr' in self._debug['frames']:
+ self._debug['frames']['arr'] = []
+ self._debug['frames']['arr'].append({'start': self._io.pos()})
+ _t_frames = self._root.MdlSimpleFrame(self._io, self, self._root)
+ _t_frames._read()
+ self.frames[i] = _t_frames
+ self._debug['frames']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['frames']['end'] = self._io.pos()
+
+ @property
+ def num_simple_frames(self):
+ if hasattr(self, '_m_num_simple_frames'):
+ return self._m_num_simple_frames if hasattr(self, '_m_num_simple_frames') else None
+
+ self._m_num_simple_frames = (1 if self.type == 0 else self.type)
+ return self._m_num_simple_frames if hasattr(self, '_m_num_simple_frames') else None
+
+
+ class MdlSimpleFrame(KaitaiStruct):
+ SEQ_FIELDS = ["bbox_min", "bbox_max", "name", "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['bbox_min']['start'] = self._io.pos()
+ self.bbox_min = self._root.MdlVertex(self._io, self, self._root)
+ self.bbox_min._read()
+ self._debug['bbox_min']['end'] = self._io.pos()
+ self._debug['bbox_max']['start'] = self._io.pos()
+ self.bbox_max = self._root.MdlVertex(self._io, self, self._root)
+ self.bbox_max._read()
+ self._debug['bbox_max']['end'] = self._io.pos()
+ self._debug['name']['start'] = self._io.pos()
+ self.name = (KaitaiStream.bytes_terminate(KaitaiStream.bytes_strip_right(self._io.read_bytes(16), 0), 0, False)).decode(u"ASCII")
+ self._debug['name']['end'] = self._io.pos()
+ self._debug['vertices']['start'] = self._io.pos()
+ self.vertices = [None] * (self._root.header.num_verts)
+ for i in range(self._root.header.num_verts):
+ if not 'arr' in self._debug['vertices']:
+ self._debug['vertices']['arr'] = []
+ self._debug['vertices']['arr'].append({'start': self._io.pos()})
+ _t_vertices = self._root.MdlVertex(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 MdlTriangle(KaitaiStruct):
+ SEQ_FIELDS = ["faces_front", "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['faces_front']['start'] = self._io.pos()
+ self.faces_front = self._io.read_s4le()
+ self._debug['faces_front']['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()})
+ self.vertices[i] = self._io.read_s4le()
+ self._debug['vertices']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['vertices']['end'] = self._io.pos()
+
+
+ class Vec3(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/game/quake_pak.py b/python/examples/kaitai/kaitai_struct_formats/game/quake_pak.py
new file mode 100644
index 00000000..3b4c0358
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/quake_pak.py
@@ -0,0 +1,110 @@
+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 QuakePak(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://quakewiki.org/wiki/.pak#Format_specification
+ """
+ SEQ_FIELDS = ["magic", "ofs_index", "len_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['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x50\x41\x43\x4B")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['ofs_index']['start'] = self._io.pos()
+ self.ofs_index = self._io.read_u4le()
+ self._debug['ofs_index']['end'] = self._io.pos()
+ self._debug['len_index']['start'] = self._io.pos()
+ self.len_index = self._io.read_u4le()
+ self._debug['len_index']['end'] = self._io.pos()
+
+ class IndexStruct(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.IndexEntry(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 IndexEntry(KaitaiStruct):
+ SEQ_FIELDS = ["name", "ofs", "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['name']['start'] = self._io.pos()
+ self.name = (KaitaiStream.bytes_terminate(KaitaiStream.bytes_strip_right(self._io.read_bytes(56), 0), 0, False)).decode(u"UTF-8")
+ self._debug['name']['end'] = self._io.pos()
+ self._debug['ofs']['start'] = self._io.pos()
+ self.ofs = self._io.read_u4le()
+ self._debug['ofs']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._io.read_u4le()
+ self._debug['size']['end'] = self._io.pos()
+
+ @property
+ def body(self):
+ if hasattr(self, '_m_body'):
+ return self._m_body if hasattr(self, '_m_body') else None
+
+ io = self._root._io
+ _pos = io.pos()
+ io.seek(self.ofs)
+ self._debug['_m_body']['start'] = io.pos()
+ self._m_body = io.read_bytes(self.size)
+ self._debug['_m_body']['end'] = io.pos()
+ io.seek(_pos)
+ return self._m_body if hasattr(self, '_m_body') else None
+
+
+ @property
+ def index(self):
+ if hasattr(self, '_m_index'):
+ return self._m_index if hasattr(self, '_m_index') else None
+
+ _pos = self._io.pos()
+ self._io.seek(self.ofs_index)
+ self._debug['_m_index']['start'] = self._io.pos()
+ self._raw__m_index = self._io.read_bytes(self.len_index)
+ io = KaitaiStream(BytesIO(self._raw__m_index))
+ self._m_index = self._root.IndexStruct(io, self, self._root)
+ self._m_index._read()
+ self._debug['_m_index']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_index if hasattr(self, '_m_index') else None
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/renderware_binary_stream.py b/python/examples/kaitai/kaitai_struct_formats/game/renderware_binary_stream.py
new file mode 100644
index 00000000..5e27c895
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/renderware_binary_stream.py
@@ -0,0 +1,774 @@
+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 RenderwareBinaryStream(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://www.gtamodding.com/wiki/RenderWare_binary_stream_file
+ """
+
+ class Sections(Enum):
+ struct = 1
+ string = 2
+ extension = 3
+ camera = 5
+ texture = 6
+ material = 7
+ material_list = 8
+ atomic_section = 9
+ plane_section = 10
+ world = 11
+ spline = 12
+ matrix = 13
+ frame_list = 14
+ geometry = 15
+ clump = 16
+ light = 18
+ unicode_string = 19
+ atomic = 20
+ texture_native = 21
+ texture_dictionary = 22
+ animation_database = 23
+ image = 24
+ skin_animation = 25
+ geometry_list = 26
+ anim_animation = 27
+ team = 28
+ crowd = 29
+ delta_morph_animation = 30
+ right_to_render = 31
+ multitexture_effect_native = 32
+ multitexture_effect_dictionary = 33
+ team_dictionary = 34
+ platform_independent_texture_dictionary = 35
+ table_of_contents = 36
+ particle_standard_global_data = 37
+ altpipe = 38
+ platform_independent_peds = 39
+ patch_mesh = 40
+ chunk_group_start = 41
+ chunk_group_end = 42
+ uv_animation_dictionary = 43
+ coll_tree = 44
+ metrics_plg = 257
+ spline_plg = 258
+ stereo_plg = 259
+ vrml_plg = 260
+ morph_plg = 261
+ pvs_plg = 262
+ memory_leak_plg = 263
+ animation_plg = 264
+ gloss_plg = 265
+ logo_plg = 266
+ memory_info_plg = 267
+ random_plg = 268
+ png_image_plg = 269
+ bone_plg = 270
+ vrml_anim_plg = 271
+ sky_mipmap_val = 272
+ mrm_plg = 273
+ lod_atomic_plg = 274
+ me_plg = 275
+ lightmap_plg = 276
+ refine_plg = 277
+ skin_plg = 278
+ label_plg = 279
+ particles_plg = 280
+ geomtx_plg = 281
+ synth_core_plg = 282
+ stqpp_plg = 283
+ part_pp_plg = 284
+ collision_plg = 285
+ hanim_plg = 286
+ user_data_plg = 287
+ material_effects_plg = 288
+ particle_system_plg = 289
+ delta_morph_plg = 290
+ patch_plg = 291
+ team_plg = 292
+ crowd_pp_plg = 293
+ mip_split_plg = 294
+ anisotropy_plg = 295
+ gcn_material_plg = 297
+ geometric_pvs_plg = 298
+ xbox_material_plg = 299
+ multi_texture_plg = 300
+ chain_plg = 301
+ toon_plg = 302
+ ptank_plg = 303
+ particle_standard_plg = 304
+ pds_plg = 305
+ prtadv_plg = 306
+ normal_map_plg = 307
+ adc_plg = 308
+ uv_animation_plg = 309
+ character_set_plg = 384
+ nohs_world_plg = 385
+ import_util_plg = 386
+ slerp_plg = 387
+ optim_plg = 388
+ tl_world_plg = 389
+ database_plg = 390
+ raytrace_plg = 391
+ ray_plg = 392
+ library_plg = 393
+ plg_2d = 400
+ tile_render_plg = 401
+ jpeg_image_plg = 402
+ tga_image_plg = 403
+ gif_image_plg = 404
+ quat_plg = 405
+ spline_pvs_plg = 406
+ mipmap_plg = 407
+ mipmapk_plg = 408
+ font_2d = 409
+ intersection_plg = 410
+ tiff_image_plg = 411
+ pick_plg = 412
+ bmp_image_plg = 413
+ ras_image_plg = 414
+ skin_fx_plg = 415
+ vcat_plg = 416
+ path_2d = 417
+ brush_2d = 418
+ object_2d = 419
+ shape_2d = 420
+ scene_2d = 421
+ pick_region_2d = 422
+ object_string_2d = 423
+ animation_plg_2d = 424
+ animation_2d = 425
+ keyframe_2d = 432
+ maestro_2d = 433
+ barycentric = 434
+ platform_independent_texture_dictionary_tk = 435
+ toc_tk = 436
+ tpl_tk = 437
+ altpipe_tk = 438
+ animation_tk = 439
+ skin_split_tookit = 440
+ compressed_key_tk = 441
+ geometry_conditioning_plg = 442
+ wing_plg = 443
+ generic_pipeline_tk = 444
+ lightmap_conversion_tk = 445
+ filesystem_plg = 446
+ dictionary_tk = 447
+ uv_animation_linear = 448
+ uv_animation_parameter = 449
+ bin_mesh_plg = 1294
+ native_data_plg = 1296
+ zmodeler_lock = 61982
+ atomic_visibility_distance = 39055872
+ clump_visibility_distance = 39055873
+ frame_visibility_distance = 39055874
+ pipeline_set = 39056115
+ unused_5 = 39056116
+ texdictionary_link = 39056117
+ specular_material = 39056118
+ unused_8 = 39056119
+ effect_2d = 39056120
+ extra_vert_colour = 39056121
+ collision_model = 39056122
+ gta_hanim = 39056123
+ reflection_material = 39056124
+ breakable = 39056125
+ frame = 39056126
+ unused_16 = 39056127
+ SEQ_FIELDS = ["code", "size", "version", "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 = KaitaiStream.resolve_enum(self._root.Sections, self._io.read_u4le())
+ self._debug['code']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._io.read_u4le()
+ self._debug['size']['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['body']['start'] = self._io.pos()
+ _on = self.code
+ if _on == self._root.Sections.geometry:
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.ListWithHeader(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.Sections.texture_dictionary:
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.ListWithHeader(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.Sections.geometry_list:
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.ListWithHeader(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.Sections.texture_native:
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.ListWithHeader(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.Sections.clump:
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.ListWithHeader(io, self, self._root)
+ self.body._read()
+ elif _on == self._root.Sections.frame_list:
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.ListWithHeader(io, self, self._root)
+ self.body._read()
+ else:
+ self.body = self._io.read_bytes(self.size)
+ self._debug['body']['end'] = self._io.pos()
+
+ class StructClump(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://www.gtamodding.com/wiki/RpClump
+ """
+ SEQ_FIELDS = ["num_atomics", "num_lights", "num_cameras"]
+ def __init__(self, _io, _parent=None, _root=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_atomics']['start'] = self._io.pos()
+ self.num_atomics = self._io.read_u4le()
+ self._debug['num_atomics']['end'] = self._io.pos()
+ if self._parent.version >= 208896:
+ self._debug['num_lights']['start'] = self._io.pos()
+ self.num_lights = self._io.read_u4le()
+ self._debug['num_lights']['end'] = self._io.pos()
+
+ if self._parent.version >= 208896:
+ self._debug['num_cameras']['start'] = self._io.pos()
+ self.num_cameras = self._io.read_u4le()
+ self._debug['num_cameras']['end'] = self._io.pos()
+
+
+
+ class StructGeometry(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://www.gtamodding.com/wiki/RpGeometry
+ """
+ SEQ_FIELDS = ["format", "num_triangles", "num_vertices", "num_morph_targets", "surf_prop", "geometry", "morph_targets"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['format']['start'] = self._io.pos()
+ self.format = self._io.read_u4le()
+ self._debug['format']['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['num_vertices']['start'] = self._io.pos()
+ self.num_vertices = self._io.read_u4le()
+ self._debug['num_vertices']['end'] = self._io.pos()
+ self._debug['num_morph_targets']['start'] = self._io.pos()
+ self.num_morph_targets = self._io.read_u4le()
+ self._debug['num_morph_targets']['end'] = self._io.pos()
+ if self._parent.version < 212992:
+ self._debug['surf_prop']['start'] = self._io.pos()
+ self.surf_prop = self._root.SurfaceProperties(self._io, self, self._root)
+ self.surf_prop._read()
+ self._debug['surf_prop']['end'] = self._io.pos()
+
+ if not (self.is_native):
+ self._debug['geometry']['start'] = self._io.pos()
+ self.geometry = self._root.GeometryNonNative(self._io, self, self._root)
+ self.geometry._read()
+ self._debug['geometry']['end'] = self._io.pos()
+
+ self._debug['morph_targets']['start'] = self._io.pos()
+ self.morph_targets = [None] * (self.num_morph_targets)
+ for i in range(self.num_morph_targets):
+ if not 'arr' in self._debug['morph_targets']:
+ self._debug['morph_targets']['arr'] = []
+ self._debug['morph_targets']['arr'].append({'start': self._io.pos()})
+ _t_morph_targets = self._root.MorphTarget(self._io, self, self._root)
+ _t_morph_targets._read()
+ self.morph_targets[i] = _t_morph_targets
+ self._debug['morph_targets']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['morph_targets']['end'] = self._io.pos()
+
+ @property
+ def is_textured(self):
+ if hasattr(self, '_m_is_textured'):
+ return self._m_is_textured if hasattr(self, '_m_is_textured') else None
+
+ self._m_is_textured = (self.format & 4) != 0
+ return self._m_is_textured if hasattr(self, '_m_is_textured') else None
+
+ @property
+ def is_prelit(self):
+ if hasattr(self, '_m_is_prelit'):
+ return self._m_is_prelit if hasattr(self, '_m_is_prelit') else None
+
+ self._m_is_prelit = (self.format & 8) != 0
+ return self._m_is_prelit if hasattr(self, '_m_is_prelit') else None
+
+ @property
+ def is_textured2(self):
+ if hasattr(self, '_m_is_textured2'):
+ return self._m_is_textured2 if hasattr(self, '_m_is_textured2') else None
+
+ self._m_is_textured2 = (self.format & 128) != 0
+ return self._m_is_textured2 if hasattr(self, '_m_is_textured2') else None
+
+ @property
+ def is_native(self):
+ if hasattr(self, '_m_is_native'):
+ return self._m_is_native if hasattr(self, '_m_is_native') else None
+
+ self._m_is_native = (self.format & 16777216) != 0
+ return self._m_is_native if hasattr(self, '_m_is_native') else None
+
+
+ class GeometryNonNative(KaitaiStruct):
+ SEQ_FIELDS = ["prelit_colors", "tex_coords", "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):
+ if self._parent.is_prelit:
+ self._debug['prelit_colors']['start'] = self._io.pos()
+ self.prelit_colors = [None] * (self._parent.num_vertices)
+ for i in range(self._parent.num_vertices):
+ if not 'arr' in self._debug['prelit_colors']:
+ self._debug['prelit_colors']['arr'] = []
+ self._debug['prelit_colors']['arr'].append({'start': self._io.pos()})
+ _t_prelit_colors = self._root.Rgba(self._io, self, self._root)
+ _t_prelit_colors._read()
+ self.prelit_colors[i] = _t_prelit_colors
+ self._debug['prelit_colors']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['prelit_colors']['end'] = self._io.pos()
+
+ if ((self._parent.is_textured) or (self._parent.is_textured2)) :
+ self._debug['tex_coords']['start'] = self._io.pos()
+ self.tex_coords = [None] * (self._parent.num_vertices)
+ for i in range(self._parent.num_vertices):
+ if not 'arr' in self._debug['tex_coords']:
+ self._debug['tex_coords']['arr'] = []
+ self._debug['tex_coords']['arr'].append({'start': self._io.pos()})
+ _t_tex_coords = self._root.TexCoord(self._io, self, self._root)
+ _t_tex_coords._read()
+ self.tex_coords[i] = _t_tex_coords
+ self._debug['tex_coords']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['tex_coords']['end'] = self._io.pos()
+
+ self._debug['triangles']['start'] = self._io.pos()
+ self.triangles = [None] * (self._parent.num_triangles)
+ for i in range(self._parent.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 StructGeometryList(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://www.gtamodding.com/wiki/Geometry_List_(RW_Section)#Structure
+ """
+ SEQ_FIELDS = ["num_geometries"]
+ def __init__(self, _io, _parent=None, _root=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_geometries']['start'] = self._io.pos()
+ self.num_geometries = self._io.read_u4le()
+ self._debug['num_geometries']['end'] = self._io.pos()
+
+
+ class Rgba(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 Sphere(KaitaiStruct):
+ SEQ_FIELDS = ["x", "y", "z", "radius"]
+ def __init__(self, _io, _parent=None, _root=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()
+ self._debug['radius']['start'] = self._io.pos()
+ self.radius = self._io.read_f4le()
+ self._debug['radius']['end'] = self._io.pos()
+
+
+ class MorphTarget(KaitaiStruct):
+ SEQ_FIELDS = ["bounding_sphere", "has_vertices", "has_normals", "vertices", "normals"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['bounding_sphere']['start'] = self._io.pos()
+ self.bounding_sphere = self._root.Sphere(self._io, self, self._root)
+ self.bounding_sphere._read()
+ self._debug['bounding_sphere']['end'] = self._io.pos()
+ self._debug['has_vertices']['start'] = self._io.pos()
+ self.has_vertices = self._io.read_u4le()
+ self._debug['has_vertices']['end'] = self._io.pos()
+ self._debug['has_normals']['start'] = self._io.pos()
+ self.has_normals = self._io.read_u4le()
+ self._debug['has_normals']['end'] = self._io.pos()
+ if self.has_vertices != 0:
+ self._debug['vertices']['start'] = self._io.pos()
+ self.vertices = [None] * (self._parent.num_vertices)
+ for i in range(self._parent.num_vertices):
+ if not 'arr' in self._debug['vertices']:
+ self._debug['vertices']['arr'] = []
+ self._debug['vertices']['arr'].append({'start': self._io.pos()})
+ _t_vertices = self._root.Vector3d(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()
+
+ if self.has_normals != 0:
+ self._debug['normals']['start'] = self._io.pos()
+ self.normals = [None] * (self._parent.num_vertices)
+ for i in range(self._parent.num_vertices):
+ if not 'arr' in self._debug['normals']:
+ self._debug['normals']['arr'] = []
+ self._debug['normals']['arr'].append({'start': self._io.pos()})
+ _t_normals = self._root.Vector3d(self._io, self, self._root)
+ _t_normals._read()
+ self.normals[i] = _t_normals
+ self._debug['normals']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['normals']['end'] = self._io.pos()
+
+
+
+ class SurfaceProperties(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://www.gtamodding.com/wiki/RpGeometry
+ """
+ SEQ_FIELDS = ["ambient", "specular", "diffuse"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['ambient']['start'] = self._io.pos()
+ self.ambient = self._io.read_f4le()
+ self._debug['ambient']['end'] = self._io.pos()
+ self._debug['specular']['start'] = self._io.pos()
+ self.specular = self._io.read_f4le()
+ self._debug['specular']['end'] = self._io.pos()
+ self._debug['diffuse']['start'] = self._io.pos()
+ self.diffuse = self._io.read_f4le()
+ self._debug['diffuse']['end'] = self._io.pos()
+
+
+ class StructFrameList(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://www.gtamodding.com/wiki/Frame_List_(RW_Section)#Structure
+ """
+ SEQ_FIELDS = ["num_frames", "frames"]
+ def __init__(self, _io, _parent=None, _root=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_frames']['start'] = self._io.pos()
+ self.num_frames = self._io.read_u4le()
+ self._debug['num_frames']['end'] = self._io.pos()
+ self._debug['frames']['start'] = self._io.pos()
+ self.frames = [None] * (self.num_frames)
+ for i in range(self.num_frames):
+ 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()
+ self.frames[i] = _t_frames
+ self._debug['frames']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['frames']['end'] = self._io.pos()
+
+
+ class Matrix(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://www.gtamodding.com/wiki/Frame_List_(RW_Section)#Structure
+ """
+ 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 = [None] * (3)
+ for i in range(3):
+ if not 'arr' in self._debug['entries']:
+ self._debug['entries']['arr'] = []
+ self._debug['entries']['arr'].append({'start': self._io.pos()})
+ _t_entries = self._root.Vector3d(self._io, self, self._root)
+ _t_entries._read()
+ self.entries[i] = _t_entries
+ self._debug['entries']['arr'][i]['end'] = self._io.pos()
+
+ self._debug['entries']['end'] = self._io.pos()
+
+
+ class Vector3d(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://www.gtamodding.com/wiki/Frame_List_(RW_Section)#Structure
+ """
+ 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()
+
+
+ class ListWithHeader(KaitaiStruct):
+ """Typical structure used by many data types in RenderWare binary
+ stream. Substream contains a list of binary stream entries,
+ first entry always has type "struct" and carries some specific
+ binary data it in, determined by the type of parent. All other
+ entries, beside the first one, are normal, self-describing
+ records.
+ """
+ SEQ_FIELDS = ["code", "header_size", "version", "header", "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['code']['start'] = self._io.pos()
+ self.code = self._io.ensure_fixed_contents(b"\x01\x00\x00\x00")
+ self._debug['code']['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()
+ self._debug['version']['start'] = self._io.pos()
+ self.version = self._io.read_u4le()
+ self._debug['version']['end'] = self._io.pos()
+ self._debug['header']['start'] = self._io.pos()
+ _on = self._parent.code
+ if _on == self._root.Sections.geometry:
+ self._raw_header = self._io.read_bytes(self.header_size)
+ io = KaitaiStream(BytesIO(self._raw_header))
+ self.header = self._root.StructGeometry(io, self, self._root)
+ self.header._read()
+ elif _on == self._root.Sections.texture_dictionary:
+ self._raw_header = self._io.read_bytes(self.header_size)
+ io = KaitaiStream(BytesIO(self._raw_header))
+ self.header = self._root.StructTextureDictionary(io, self, self._root)
+ self.header._read()
+ elif _on == self._root.Sections.geometry_list:
+ self._raw_header = self._io.read_bytes(self.header_size)
+ io = KaitaiStream(BytesIO(self._raw_header))
+ self.header = self._root.StructGeometryList(io, self, self._root)
+ self.header._read()
+ elif _on == self._root.Sections.clump:
+ self._raw_header = self._io.read_bytes(self.header_size)
+ io = KaitaiStream(BytesIO(self._raw_header))
+ self.header = self._root.StructClump(io, self, self._root)
+ self.header._read()
+ elif _on == self._root.Sections.frame_list:
+ self._raw_header = self._io.read_bytes(self.header_size)
+ io = KaitaiStream(BytesIO(self._raw_header))
+ self.header = self._root.StructFrameList(io, self, self._root)
+ self.header._read()
+ else:
+ self.header = self._io.read_bytes(self.header_size)
+ self._debug['header']['end'] = self._io.pos()
+ 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 = RenderwareBinaryStream(self._io)
+ _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 Triangle(KaitaiStruct):
+ SEQ_FIELDS = ["vertex2", "vertex1", "material_id", "vertex3"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['vertex2']['start'] = self._io.pos()
+ self.vertex2 = self._io.read_u2le()
+ self._debug['vertex2']['end'] = self._io.pos()
+ self._debug['vertex1']['start'] = self._io.pos()
+ self.vertex1 = self._io.read_u2le()
+ self._debug['vertex1']['end'] = self._io.pos()
+ self._debug['material_id']['start'] = self._io.pos()
+ self.material_id = self._io.read_u2le()
+ self._debug['material_id']['end'] = self._io.pos()
+ self._debug['vertex3']['start'] = self._io.pos()
+ self.vertex3 = self._io.read_u2le()
+ self._debug['vertex3']['end'] = self._io.pos()
+
+
+ class Frame(KaitaiStruct):
+ """
+ .. seealso::
+ Source - https://www.gtamodding.com/wiki/Frame_List_(RW_Section)#Structure
+ """
+ SEQ_FIELDS = ["rotation_matrix", "position", "cur_frame_idx", "matrix_creation_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['rotation_matrix']['start'] = self._io.pos()
+ self.rotation_matrix = self._root.Matrix(self._io, self, self._root)
+ self.rotation_matrix._read()
+ self._debug['rotation_matrix']['end'] = self._io.pos()
+ self._debug['position']['start'] = self._io.pos()
+ self.position = self._root.Vector3d(self._io, self, self._root)
+ self.position._read()
+ self._debug['position']['end'] = self._io.pos()
+ self._debug['cur_frame_idx']['start'] = self._io.pos()
+ self.cur_frame_idx = self._io.read_s4le()
+ self._debug['cur_frame_idx']['end'] = self._io.pos()
+ self._debug['matrix_creation_flags']['start'] = self._io.pos()
+ self.matrix_creation_flags = self._io.read_u4le()
+ self._debug['matrix_creation_flags']['end'] = self._io.pos()
+
+
+ class TexCoord(KaitaiStruct):
+ SEQ_FIELDS = ["u", "v"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['u']['start'] = self._io.pos()
+ self.u = self._io.read_f4le()
+ self._debug['u']['end'] = self._io.pos()
+ self._debug['v']['start'] = self._io.pos()
+ self.v = self._io.read_f4le()
+ self._debug['v']['end'] = self._io.pos()
+
+
+ class StructTextureDictionary(KaitaiStruct):
+ SEQ_FIELDS = ["num_textures"]
+ def __init__(self, _io, _parent=None, _root=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_textures']['start'] = self._io.pos()
+ self.num_textures = self._io.read_u4le()
+ self._debug['num_textures']['end'] = self._io.pos()
+
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/saints_row_2_vpp_pc.py b/python/examples/kaitai/kaitai_struct_formats/game/saints_row_2_vpp_pc.py
new file mode 100644
index 00000000..91be64ea
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/saints_row_2_vpp_pc.py
@@ -0,0 +1,252 @@
+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 SaintsRow2VppPc(KaitaiStruct):
+ SEQ_FIELDS = ["magic", "pad1", "num_files", "container_size", "len_offsets", "len_filenames", "len_extensions", "smth5", "smth6", "smth7", "smth8", "smth9"]
+ def __init__(self, _io, _parent=None, _root=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"\xCE\x0A\x89\x51\x04")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['pad1']['start'] = self._io.pos()
+ self.pad1 = self._io.read_bytes(335)
+ self._debug['pad1']['end'] = self._io.pos()
+ self._debug['num_files']['start'] = self._io.pos()
+ self.num_files = self._io.read_s4le()
+ self._debug['num_files']['end'] = self._io.pos()
+ self._debug['container_size']['start'] = self._io.pos()
+ self.container_size = self._io.read_s4le()
+ self._debug['container_size']['end'] = self._io.pos()
+ self._debug['len_offsets']['start'] = self._io.pos()
+ self.len_offsets = self._io.read_s4le()
+ self._debug['len_offsets']['end'] = self._io.pos()
+ self._debug['len_filenames']['start'] = self._io.pos()
+ self.len_filenames = self._io.read_s4le()
+ self._debug['len_filenames']['end'] = self._io.pos()
+ self._debug['len_extensions']['start'] = self._io.pos()
+ self.len_extensions = self._io.read_s4le()
+ self._debug['len_extensions']['end'] = self._io.pos()
+ self._debug['smth5']['start'] = self._io.pos()
+ self.smth5 = self._io.read_s4le()
+ self._debug['smth5']['end'] = self._io.pos()
+ self._debug['smth6']['start'] = self._io.pos()
+ self.smth6 = self._io.read_s4le()
+ self._debug['smth6']['end'] = self._io.pos()
+ self._debug['smth7']['start'] = self._io.pos()
+ self.smth7 = self._io.read_s4le()
+ self._debug['smth7']['end'] = self._io.pos()
+ self._debug['smth8']['start'] = self._io.pos()
+ self.smth8 = self._io.read_s4le()
+ self._debug['smth8']['end'] = self._io.pos()
+ self._debug['smth9']['start'] = self._io.pos()
+ self.smth9 = self._io.read_s4le()
+ self._debug['smth9']['end'] = self._io.pos()
+
+ class Offsets(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.Offsets.Offset(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 Offset(KaitaiStruct):
+ SEQ_FIELDS = ["name_ofs", "ext_ofs", "smth2", "ofs_body", "len_body", "always_minus_1", "always_zero"]
+ def __init__(self, _io, _parent=None, _root=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_ofs']['start'] = self._io.pos()
+ self.name_ofs = self._io.read_u4le()
+ self._debug['name_ofs']['end'] = self._io.pos()
+ self._debug['ext_ofs']['start'] = self._io.pos()
+ self.ext_ofs = self._io.read_u4le()
+ self._debug['ext_ofs']['end'] = self._io.pos()
+ self._debug['smth2']['start'] = self._io.pos()
+ self.smth2 = self._io.read_s4le()
+ self._debug['smth2']['end'] = self._io.pos()
+ self._debug['ofs_body']['start'] = self._io.pos()
+ self.ofs_body = self._io.read_s4le()
+ self._debug['ofs_body']['end'] = self._io.pos()
+ self._debug['len_body']['start'] = self._io.pos()
+ self.len_body = self._io.read_s4le()
+ self._debug['len_body']['end'] = self._io.pos()
+ self._debug['always_minus_1']['start'] = self._io.pos()
+ self.always_minus_1 = self._io.read_s4le()
+ self._debug['always_minus_1']['end'] = self._io.pos()
+ self._debug['always_zero']['start'] = self._io.pos()
+ self.always_zero = self._io.read_s4le()
+ self._debug['always_zero']['end'] = self._io.pos()
+
+ @property
+ def filename(self):
+ if hasattr(self, '_m_filename'):
+ return self._m_filename if hasattr(self, '_m_filename') else None
+
+ io = self._root.filenames._io
+ _pos = io.pos()
+ io.seek(self.name_ofs)
+ self._debug['_m_filename']['start'] = io.pos()
+ self._m_filename = (io.read_bytes_term(0, False, True, True)).decode(u"UTF-8")
+ self._debug['_m_filename']['end'] = io.pos()
+ io.seek(_pos)
+ return self._m_filename if hasattr(self, '_m_filename') else None
+
+ @property
+ def ext(self):
+ if hasattr(self, '_m_ext'):
+ return self._m_ext if hasattr(self, '_m_ext') else None
+
+ io = self._root.extensions._io
+ _pos = io.pos()
+ io.seek(self.ext_ofs)
+ self._debug['_m_ext']['start'] = io.pos()
+ self._m_ext = (io.read_bytes_term(0, False, True, True)).decode(u"UTF-8")
+ self._debug['_m_ext']['end'] = io.pos()
+ io.seek(_pos)
+ return self._m_ext if hasattr(self, '_m_ext') else None
+
+ @property
+ def body(self):
+ if hasattr(self, '_m_body'):
+ return self._m_body if hasattr(self, '_m_body') else None
+
+ io = self._root._io
+ _pos = io.pos()
+ io.seek((self._root.data_start + self.ofs_body))
+ self._debug['_m_body']['start'] = io.pos()
+ self._m_body = io.read_bytes(self.len_body)
+ self._debug['_m_body']['end'] = io.pos()
+ io.seek(_pos)
+ return self._m_body if hasattr(self, '_m_body') else None
+
+
+
+ class Strings(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()})
+ self.entries.append((self._io.read_bytes_term(0, False, True, True)).decode(u"UTF-8"))
+ self._debug['entries']['arr'][len(self.entries) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['entries']['end'] = self._io.pos()
+
+
+ @property
+ def filenames(self):
+ if hasattr(self, '_m_filenames'):
+ return self._m_filenames if hasattr(self, '_m_filenames') else None
+
+ _pos = self._io.pos()
+ self._io.seek(self.ofs_filenames)
+ self._debug['_m_filenames']['start'] = self._io.pos()
+ self._raw__m_filenames = self._io.read_bytes(self.len_filenames)
+ io = KaitaiStream(BytesIO(self._raw__m_filenames))
+ self._m_filenames = self._root.Strings(io, self, self._root)
+ self._m_filenames._read()
+ self._debug['_m_filenames']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_filenames if hasattr(self, '_m_filenames') else None
+
+ @property
+ def ofs_extensions(self):
+ if hasattr(self, '_m_ofs_extensions'):
+ return self._m_ofs_extensions if hasattr(self, '_m_ofs_extensions') else None
+
+ self._m_ofs_extensions = (((self.ofs_filenames + self.len_filenames) & 4294965248) + 2048)
+ return self._m_ofs_extensions if hasattr(self, '_m_ofs_extensions') else None
+
+ @property
+ def files(self):
+ if hasattr(self, '_m_files'):
+ return self._m_files if hasattr(self, '_m_files') else None
+
+ _pos = self._io.pos()
+ self._io.seek(2048)
+ self._debug['_m_files']['start'] = self._io.pos()
+ self._raw__m_files = self._io.read_bytes(self.len_offsets)
+ io = KaitaiStream(BytesIO(self._raw__m_files))
+ self._m_files = self._root.Offsets(io, self, self._root)
+ self._m_files._read()
+ self._debug['_m_files']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_files if hasattr(self, '_m_files') else None
+
+ @property
+ def data_start(self):
+ if hasattr(self, '_m_data_start'):
+ return self._m_data_start if hasattr(self, '_m_data_start') else None
+
+ self._m_data_start = (((self.ofs_extensions + self.len_extensions) & 4294965248) + 2048)
+ return self._m_data_start if hasattr(self, '_m_data_start') else None
+
+ @property
+ def extensions(self):
+ if hasattr(self, '_m_extensions'):
+ return self._m_extensions if hasattr(self, '_m_extensions') else None
+
+ _pos = self._io.pos()
+ self._io.seek(self.ofs_extensions)
+ self._debug['_m_extensions']['start'] = self._io.pos()
+ self._raw__m_extensions = self._io.read_bytes(self.len_extensions)
+ io = KaitaiStream(BytesIO(self._raw__m_extensions))
+ self._m_extensions = self._root.Strings(io, self, self._root)
+ self._m_extensions._read()
+ self._debug['_m_extensions']['end'] = self._io.pos()
+ self._io.seek(_pos)
+ return self._m_extensions if hasattr(self, '_m_extensions') else None
+
+ @property
+ def ofs_filenames(self):
+ if hasattr(self, '_m_ofs_filenames'):
+ return self._m_ofs_filenames if hasattr(self, '_m_ofs_filenames') else None
+
+ self._m_ofs_filenames = (((2048 + self.len_offsets) & 4294965248) + 2048)
+ return self._m_ofs_filenames if hasattr(self, '_m_ofs_filenames') else None
+
+
diff --git a/python/examples/kaitai/kaitai_struct_formats/game/warcraft_2_pud.py b/python/examples/kaitai/kaitai_struct_formats/game/warcraft_2_pud.py
new file mode 100644
index 00000000..a76f83d9
--- /dev/null
+++ b/python/examples/kaitai/kaitai_struct_formats/game/warcraft_2_pud.py
@@ -0,0 +1,421 @@
+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 Warcraft2Pud(KaitaiStruct):
+ """Warcraft II game engine uses this format for map files. External
+ maps can be edited by official Warcraft II map editor and saved in
+ .pud files. Maps supplied with the game (i.e. single player
+ campaign) follow the same format, but are instead embedded inside
+ the game container files.
+
+ There are two major versions: 0x11 (original one) and 0x13 (roughly
+ corresponds to v1.33 of the game engine, although some of the
+ features got limited support in v1.3).
+
+ File consists of a sequence of typed sections.
+
+ .. seealso::
+ Source - http://cade.datamax.bg/war2x/pudspec.html
+ """
+
+ class Controller(Enum):
+ computer = 1
+ passive_computer = 2
+ nobody = 3
+ computer = 4
+ human = 5
+ rescue_passive = 6
+ rescue_active = 7
+
+ class TerrainType(Enum):
+ forest = 0
+ winter = 1
+ wasteland = 2
+ swamp = 3
+
+ class UnitType(Enum):
+ infantry = 0
+ grunt = 1
+ peasant = 2
+ peon = 3
+ ballista = 4
+ catapult = 5
+ knight = 6
+ ogre = 7
+ archer = 8
+ axethrower = 9
+ mage = 10
+ death_knight = 11
+ paladin = 12
+ ogre_mage = 13
+ dwarves = 14
+ goblin_sapper = 15
+ attack_peasant = 16
+ attack_peon = 17
+ ranger = 18
+ berserker = 19
+ alleria = 20
+ teron_gorefiend = 21
+ kurdan_and_sky_ree = 22
+ dentarg = 23
+ khadgar = 24
+ grom_hellscream = 25
+ human_tanker = 26
+ orc_tanker = 27
+ human_transport = 28
+ orc_transport = 29
+ elven_destroyer = 30
+ troll_destroyer = 31
+ battleship = 32
+ juggernaught = 33
+ deathwing = 35
+ gnomish_submarine = 38
+ giant_turtle = 39
+ gnomish_flying_machine = 40
+ goblin_zepplin = 41
+ gryphon_rider = 42
+ dragon = 43
+ turalyon = 44
+ eye_of_kilrogg = 45
+ danath = 46
+ khorgath_bladefist = 47
+ cho_gall = 49
+ lothar = 50
+ gul_dan = 51
+ uther_lightbringer = 52
+ zuljin = 53
+ skeleton = 55
+ daemon = 56
+ critter = 57
+ farm = 58
+ pig_farm = 59
+ human_barracks = 60
+ orc_barracks = 61
+ church = 62
+ altar_of_storms = 63
+ human_scout_tower = 64
+ orc_scout_tower = 65
+ stables = 66
+ ogre_mound = 67
+ gnomish_inventor = 68
+ goblin_alchemist = 69
+ gryphon_aviary = 70
+ dragon_roost = 71
+ human_shipyard = 72
+ orc_shipyard = 73
+ town_hall = 74
+ great_hall = 75
+ elven_lumber_mill = 76
+ troll_lumber_mill = 77
+ human_foundry = 78
+ orc_foundry = 79
+ mage_tower = 80
+ temple_of_the_damned = 81
+ human_blacksmith = 82
+ orc_blacksmith = 83
+ human_refinery = 84
+ orc_refinery = 85
+ human_oil_well = 86
+ orc_oil_well = 87
+ keep = 88
+ stronghold = 89
+ castle = 90
+ fortress = 91
+ gold_mine = 92
+ oil_patch = 93
+ human_start = 94
+ orc_start = 95
+ human_guard_tower = 96
+ orc_guard_tower = 97
+ human_cannon_tower = 98
+ orc_cannon_tower = 99
+ circle_of_power = 100
+ dark_portal = 101
+ runestone = 102
+ human_wall = 103
+ orc_wall = 104
+ SEQ_FIELDS = ["sections"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['sections']['start'] = self._io.pos()
+ self.sections = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['sections']:
+ self._debug['sections']['arr'] = []
+ self._debug['sections']['arr'].append({'start': self._io.pos()})
+ _t_sections = self._root.Section(self._io, self, self._root)
+ _t_sections._read()
+ self.sections.append(_t_sections)
+ self._debug['sections']['arr'][len(self.sections) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['sections']['end'] = self._io.pos()
+
+ class SectionStartingResource(KaitaiStruct):
+ SEQ_FIELDS = ["resources_by_player"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['resources_by_player']['start'] = self._io.pos()
+ self.resources_by_player = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['resources_by_player']:
+ self._debug['resources_by_player']['arr'] = []
+ self._debug['resources_by_player']['arr'].append({'start': self._io.pos()})
+ self.resources_by_player.append(self._io.read_u2le())
+ self._debug['resources_by_player']['arr'][len(self.resources_by_player) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['resources_by_player']['end'] = self._io.pos()
+
+
+ class SectionEra(KaitaiStruct):
+ """Section that specifies terrain type for this map."""
+ SEQ_FIELDS = ["terrain"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['terrain']['start'] = self._io.pos()
+ self.terrain = KaitaiStream.resolve_enum(self._root.TerrainType, self._io.read_u2le())
+ self._debug['terrain']['end'] = self._io.pos()
+
+
+ class SectionVer(KaitaiStruct):
+ """Section that specifies format version."""
+ SEQ_FIELDS = ["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['version']['start'] = self._io.pos()
+ self.version = self._io.read_u2le()
+ self._debug['version']['end'] = self._io.pos()
+
+
+ class SectionDim(KaitaiStruct):
+ 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 SectionType(KaitaiStruct):
+ """Section that confirms that this file is a "map file" by certain
+ magic string and supplies a tag that could be used in
+ multiplayer to check that all player use the same version of the
+ map.
+ """
+ SEQ_FIELDS = ["magic", "unused", "id_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['magic']['start'] = self._io.pos()
+ self.magic = self._io.ensure_fixed_contents(b"\x57\x41\x52\x32\x20\x4D\x41\x50\x00\x00")
+ self._debug['magic']['end'] = self._io.pos()
+ self._debug['unused']['start'] = self._io.pos()
+ self.unused = self._io.read_bytes(2)
+ self._debug['unused']['end'] = self._io.pos()
+ self._debug['id_tag']['start'] = self._io.pos()
+ self.id_tag = self._io.read_u4le()
+ self._debug['id_tag']['end'] = self._io.pos()
+
+
+ class SectionUnit(KaitaiStruct):
+ SEQ_FIELDS = ["units"]
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self._debug = collections.defaultdict(dict)
+
+ def _read(self):
+ self._debug['units']['start'] = self._io.pos()
+ self.units = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['units']:
+ self._debug['units']['arr'] = []
+ self._debug['units']['arr'].append({'start': self._io.pos()})
+ _t_units = self._root.Unit(self._io, self, self._root)
+ _t_units._read()
+ self.units.append(_t_units)
+ self._debug['units']['arr'][len(self.units) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['units']['end'] = self._io.pos()
+
+
+ class Section(KaitaiStruct):
+ SEQ_FIELDS = ["name", "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['name']['start'] = self._io.pos()
+ self.name = (self._io.read_bytes(4)).decode(u"ASCII")
+ self._debug['name']['end'] = self._io.pos()
+ self._debug['size']['start'] = self._io.pos()
+ self.size = self._io.read_u4le()
+ self._debug['size']['end'] = self._io.pos()
+ self._debug['body']['start'] = self._io.pos()
+ _on = self.name
+ if _on == u"SLBR":
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.SectionStartingResource(io, self, self._root)
+ self.body._read()
+ elif _on == u"ERAX":
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.SectionEra(io, self, self._root)
+ self.body._read()
+ elif _on == u"OWNR":
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.SectionOwnr(io, self, self._root)
+ self.body._read()
+ elif _on == u"ERA ":
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.SectionEra(io, self, self._root)
+ self.body._read()
+ elif _on == u"SGLD":
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.SectionStartingResource(io, self, self._root)
+ self.body._read()
+ elif _on == u"VER ":
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.SectionVer(io, self, self._root)
+ self.body._read()
+ elif _on == u"SOIL":
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.SectionStartingResource(io, self, self._root)
+ self.body._read()
+ elif _on == u"UNIT":
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.SectionUnit(io, self, self._root)
+ self.body._read()
+ elif _on == u"DIM ":
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.SectionDim(io, self, self._root)
+ self.body._read()
+ elif _on == u"TYPE":
+ self._raw_body = self._io.read_bytes(self.size)
+ io = KaitaiStream(BytesIO(self._raw_body))
+ self.body = self._root.SectionType(io, self, self._root)
+ self.body._read()
+ else:
+ self.body = self._io.read_bytes(self.size)
+ self._debug['body']['end'] = self._io.pos()
+
+
+ class SectionOwnr(KaitaiStruct):
+ """Section that specifies who controls each player."""
+ SEQ_FIELDS = ["controller_by_player"]
+ def __init__(self, _io, _parent=None, _root=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_by_player']['start'] = self._io.pos()
+ self.controller_by_player = []
+ i = 0
+ while not self._io.is_eof():
+ if not 'arr' in self._debug['controller_by_player']:
+ self._debug['controller_by_player']['arr'] = []
+ self._debug['controller_by_player']['arr'].append({'start': self._io.pos()})
+ self.controller_by_player.append(KaitaiStream.resolve_enum(self._root.Controller, self._io.read_u1()))
+ self._debug['controller_by_player']['arr'][len(self.controller_by_player) - 1]['end'] = self._io.pos()
+ i += 1
+
+ self._debug['controller_by_player']['end'] = self._io.pos()
+
+
+ class Unit(KaitaiStruct):
+ SEQ_FIELDS = ["x", "y", "u_type", "owner", "options"]
+ def __init__(self, _io, _parent=None, _root=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()
+ self._debug['u_type']['start'] = self._io.pos()
+ self.u_type = KaitaiStream.resolve_enum(self._root.UnitType, self._io.read_u1())
+ self._debug['u_type']['end'] = self._io.pos()
+ self._debug['owner']['start'] = self._io.pos()
+ self.owner = self._io.read_u1()
+ self._debug['owner']['end'] = self._io.pos()
+ self._debug['options']['start'] = self._io.pos()
+ self.options = self._io.read_u2le()
+ self._debug['options']['end'] = self._io.pos()
+
+ @property
+ def resource(self):
+ if hasattr(self, '_m_resource'):
+ return self._m_resource if hasattr(self, '_m_resource') else None
+
+ if ((self.u_type == self._root.UnitType.gold_mine) or (self.u_type == self._root.UnitType.human_oil_well) or (self.u_type == self._root.UnitType.orc_oil_well) or (self.u_type == self._root.UnitType.oil_patch)) :
+ self._m_resource = (self.options * 2500)
+
+ return self._m_resource if hasattr(self, '_m_resource') else None
+
+
+