diff --git a/library/context.py b/library/context.py index 0451afb..1264f71 100644 --- a/library/context.py +++ b/library/context.py @@ -43,3 +43,45 @@ def __init__(self, result: bytes = b'', name: str = '', data=None, block=None, p super().__init__(name=name, data=data, block=block, parent=parent) self.result = result self.write_start_offset = len(result) + + +class DocumentationCtxData: + def __init__(self, label): + self.label = label + + def __str__(self): + return self.label + + def __add__(self, other): + a = str(self) + b = str(other) + return DocumentationCtxData(f'{a}+{b}') + + def __radd__(self, other): + a = str(other) + b = str(self) + return DocumentationCtxData(f'{a}+{b}') + + def __mul__(self, other): + a = str(self) + b = str(other) + if '+' in a or '-' in a: + a = f'({a})' + if '+' in b or '-' in b: + b = f'({b})' + return DocumentationCtxData(f'{a}*{b}') + + def __rmul__(self, other): + a = str(other) + b = str(self) + if '+' in a or '-' in a: + a = f'({a})' + if '+' in b or '-' in b: + b = f'({b})' + return DocumentationCtxData(f'{a}*{b}') + + +class DocumentationContext(BaseContext): + + def data(self, local_path: str): + return DocumentationCtxData(local_path.replace('../', '^')) diff --git a/library/read_blocks/array.py b/library/read_blocks/array.py index 3e9e1af..51e3bf2 100644 --- a/library/read_blocks/array.py +++ b/library/read_blocks/array.py @@ -3,7 +3,7 @@ from math import ceil from typing import Dict, Tuple, Any -from library.context import ReadContext, WriteContext +from library.context import ReadContext, WriteContext, DocumentationContext from library.exceptions import EndOfBufferException from library.read_blocks.basic import DataBlock, DataBlockWithChildren from library.read_blocks.numbers import IntegerBlock @@ -31,7 +31,10 @@ def length_doc_str(self): (_, doc_str) = self._length return doc_str if callable(self._length): - return "custom_func" + try: + return str(self._length(DocumentationContext())) + except: + return 'custom_func' return str(self._length) # For auto-generated documentation only @@ -159,7 +162,10 @@ def length_doc_str(self): (_, doc_str) = self._length return doc_str if callable(self._length): - return "custom_func" + try: + return str(self._length(DocumentationContext())) + except: + return 'custom_func' return str(self._length) # For auto-generated documentation only diff --git a/library/read_blocks/basic.py b/library/read_blocks/basic.py index 4fafbaf..14f3ee2 100644 --- a/library/read_blocks/basic.py +++ b/library/read_blocks/basic.py @@ -2,7 +2,7 @@ from io import BufferedReader, BytesIO, SEEK_CUR from typing import Dict, Any, Tuple, Literal -from library.context import ReadContext, WriteContext +from library.context import ReadContext, WriteContext, DocumentationContext from library.exceptions import DataIntegrityException, BlockDefinitionException, EndOfBufferException from library.utils import represent_value_as_str @@ -97,7 +97,10 @@ def size_doc_str(self): (_, doc_str) = self._length return doc_str if callable(self._length): - return "custom_func" + try: + return str(self._length(DocumentationContext())) + except: + return 'custom_func' else: return str(self._length) diff --git a/library/read_blocks/smart_fields.py b/library/read_blocks/smart_fields.py index 996b127..7b113d4 100644 --- a/library/read_blocks/smart_fields.py +++ b/library/read_blocks/smart_fields.py @@ -1,7 +1,7 @@ from io import BufferedReader, BytesIO from typing import List, Dict, Tuple, Any -from library.context import ReadContext, WriteContext +from library.context import ReadContext, WriteContext, DocumentationContext from library.exceptions import DataIntegrityException from library.read_blocks.basic import DataBlock, SkipBlock, BytesBlock from library.utils.id import join_id @@ -20,7 +20,10 @@ def schema(self) -> Dict: if isinstance(choice_index_doc, tuple): (_, choice_index_doc) = choice_index_doc if callable(choice_index_doc): - choice_index_doc = "custom_func" + try: + choice_index_doc = str(choice_index_doc(DocumentationContext())) + except: + choice_index_doc = 'custom_func' return { **super().schema, 'possible_resource_schemas': [block.schema for block in self.possible_blocks], diff --git a/library/read_blocks/strings.py b/library/read_blocks/strings.py index 8954b7d..cabe9af 100644 --- a/library/read_blocks/strings.py +++ b/library/read_blocks/strings.py @@ -1,7 +1,7 @@ from io import BufferedReader, BytesIO from typing import Dict -from library.context import ReadContext, WriteContext +from library.context import ReadContext, WriteContext, DocumentationContext from library.exceptions import EndOfBufferException from library.read_blocks.basic import DataBlock @@ -30,7 +30,10 @@ def size_doc_str(self): (_, doc_str) = self._length return doc_str if callable(self._length): - return "custom_func" + try: + return str(self._length(DocumentationContext())) + except: + return 'custom_func' return str(self._length) def resolve_length(self, ctx): diff --git a/resources/NFS2.md b/resources/NFS2.md index a6b7f91..af6c0dc 100644 --- a/resources/NFS2.md +++ b/resources/NFS2.md @@ -126,7 +126,7 @@ Did not find what you need or some given data is wrong? Please submit an | 8 | **unk** | 4 | Bytes | Unknown purpose | | 12 | **x** | 2 | 2-bytes unsigned integer (little endian) | X coordinate of bitmap position on screen. Used for menu/dash sprites | | 14 | **y** | 2 | 2-bytes unsigned integer (little endian) | Y coordinate of bitmap position on screen. Used for menu/dash sprites | -| 16 | **bitmap** | height\*ceil((width)\*4/8) | Array of `height` items
Item size: ceil((width)\*4/8) bytes
Item type: Array of `width` sub-byte numbers. Each number consists of 4 bits | Font atlas bitmap data, array of bitmap rows | +| 16 | **bitmap** | height\*ceil((^width)\*4/8) | Array of `height` items
Item size: ceil((^width)\*4/8) bytes
Item type: Array of `^width` sub-byte numbers. Each number consists of 4 bits | Font atlas bitmap data, array of bitmap rows | ### **Bitmap8Bit** ### #### **Size**: 16..? bytes #### #### **Description**: 8bit bitmap can be serialized to image only with palette. Basically, for every pixel it uses 8-bit index of color in assigned palette. The tricky part is to determine how the game understands which palette to use. In most cases, if bitmap has embedded palette, it should be used, EXCEPT Autumn Valley fence texture: there embedded palette should be ignored. In all other cases it is tricky even more: it uses !pal or !PAL palette from own SHPI archive, if it is WWWW archive, palette can be in a different SHPI before this one. In CONTROL directory most of QFS files use !pal even from different QFS file! It is a mystery how to reliably pick palette #### diff --git a/resources/NFS2_SE.md b/resources/NFS2_SE.md index 348da46..494093f 100644 --- a/resources/NFS2_SE.md +++ b/resources/NFS2_SE.md @@ -122,7 +122,7 @@ Did not find what you need or some given data is wrong? Please submit an | 8 | **unk** | 4 | Bytes | Unknown purpose | | 12 | **x** | 2 | 2-bytes unsigned integer (little endian) | X coordinate of bitmap position on screen. Used for menu/dash sprites | | 14 | **y** | 2 | 2-bytes unsigned integer (little endian) | Y coordinate of bitmap position on screen. Used for menu/dash sprites | -| 16 | **bitmap** | height\*ceil((width)\*4/8) | Array of `height` items
Item size: ceil((width)\*4/8) bytes
Item type: Array of `width` sub-byte numbers. Each number consists of 4 bits | Font atlas bitmap data, array of bitmap rows | +| 16 | **bitmap** | height\*ceil((^width)\*4/8) | Array of `height` items
Item size: ceil((^width)\*4/8) bytes
Item type: Array of `^width` sub-byte numbers. Each number consists of 4 bits | Font atlas bitmap data, array of bitmap rows | ### **Bitmap8Bit** ### #### **Size**: 16..? bytes #### #### **Description**: 8bit bitmap can be serialized to image only with palette. Basically, for every pixel it uses 8-bit index of color in assigned palette. The tricky part is to determine how the game understands which palette to use. In most cases, if bitmap has embedded palette, it should be used, EXCEPT Autumn Valley fence texture: there embedded palette should be ignored. In all other cases it is tricky even more: it uses !pal or !PAL palette from own SHPI archive, if it is WWWW archive, palette can be in a different SHPI before this one. In CONTROL directory most of QFS files use !pal even from different QFS file! It is a mystery how to reliably pick palette #### diff --git a/resources/NFS3.md b/resources/NFS3.md index e408371..cc4d1f7 100644 --- a/resources/NFS3.md +++ b/resources/NFS3.md @@ -54,7 +54,7 @@ Did not find what you need or some given data is wrong? Please submit an | 8 | **unk** | 4 | Bytes | Unknown purpose | | 12 | **x** | 2 | 2-bytes unsigned integer (little endian) | X coordinate of bitmap position on screen. Used for menu/dash sprites | | 14 | **y** | 2 | 2-bytes unsigned integer (little endian) | Y coordinate of bitmap position on screen. Used for menu/dash sprites | -| 16 | **bitmap** | height\*ceil((width)\*4/8) | Array of `height` items
Item size: ceil((width)\*4/8) bytes
Item type: Array of `width` sub-byte numbers. Each number consists of 4 bits | Font atlas bitmap data, array of bitmap rows | +| 16 | **bitmap** | height\*ceil((^width)\*4/8) | Array of `height` items
Item size: ceil((^width)\*4/8) bytes
Item type: Array of `^width` sub-byte numbers. Each number consists of 4 bits | Font atlas bitmap data, array of bitmap rows | ### **Bitmap8Bit** ### #### **Size**: 16..? bytes #### #### **Description**: 8bit bitmap can be serialized to image only with palette. Basically, for every pixel it uses 8-bit index of color in assigned palette. The tricky part is to determine how the game understands which palette to use. In most cases, if bitmap has embedded palette, it should be used, EXCEPT Autumn Valley fence texture: there embedded palette should be ignored. In all other cases it is tricky even more: it uses !pal or !PAL palette from own SHPI archive, if it is WWWW archive, palette can be in a different SHPI before this one. In CONTROL directory most of QFS files use !pal even from different QFS file! It is a mystery how to reliably pick palette #### diff --git a/resources/NFS4.md b/resources/NFS4.md index 5556de1..2b4bba7 100644 --- a/resources/NFS4.md +++ b/resources/NFS4.md @@ -54,7 +54,7 @@ Did not find what you need or some given data is wrong? Please submit an | 8 | **unk** | 4 | Bytes | Unknown purpose | | 12 | **x** | 2 | 2-bytes unsigned integer (little endian) | X coordinate of bitmap position on screen. Used for menu/dash sprites | | 14 | **y** | 2 | 2-bytes unsigned integer (little endian) | Y coordinate of bitmap position on screen. Used for menu/dash sprites | -| 16 | **bitmap** | height\*ceil((width)\*4/8) | Array of `height` items
Item size: ceil((width)\*4/8) bytes
Item type: Array of `width` sub-byte numbers. Each number consists of 4 bits | Font atlas bitmap data, array of bitmap rows | +| 16 | **bitmap** | height\*ceil((^width)\*4/8) | Array of `height` items
Item size: ceil((^width)\*4/8) bytes
Item type: Array of `^width` sub-byte numbers. Each number consists of 4 bits | Font atlas bitmap data, array of bitmap rows | ### **Bitmap8Bit** ### #### **Size**: 16..? bytes #### #### **Description**: 8bit bitmap can be serialized to image only with palette. Basically, for every pixel it uses 8-bit index of color in assigned palette. The tricky part is to determine how the game understands which palette to use. In most cases, if bitmap has embedded palette, it should be used, EXCEPT Autumn Valley fence texture: there embedded palette should be ignored. In all other cases it is tricky even more: it uses !pal or !PAL palette from own SHPI archive, if it is WWWW archive, palette can be in a different SHPI before this one. In CONTROL directory most of QFS files use !pal even from different QFS file! It is a mystery how to reliably pick palette #### diff --git a/resources/TNFS_SE.md b/resources/TNFS_SE.md index a00790f..c9bcef2 100644 --- a/resources/TNFS_SE.md +++ b/resources/TNFS_SE.md @@ -367,7 +367,7 @@ Did not find what you need or some given data is wrong? Please submit an | 8 | **unk** | 4 | Bytes | Unknown purpose | | 12 | **x** | 2 | 2-bytes unsigned integer (little endian) | X coordinate of bitmap position on screen. Used for menu/dash sprites | | 14 | **y** | 2 | 2-bytes unsigned integer (little endian) | Y coordinate of bitmap position on screen. Used for menu/dash sprites | -| 16 | **bitmap** | height\*ceil((width)\*4/8) | Array of `height` items
Item size: ceil((width)\*4/8) bytes
Item type: Array of `width` sub-byte numbers. Each number consists of 4 bits | Font atlas bitmap data, array of bitmap rows | +| 16 | **bitmap** | height\*ceil((^width)\*4/8) | Array of `height` items
Item size: ceil((^width)\*4/8) bytes
Item type: Array of `^width` sub-byte numbers. Each number consists of 4 bits | Font atlas bitmap data, array of bitmap rows | ### **Bitmap8Bit** ### #### **Size**: 16..? bytes #### #### **Description**: 8bit bitmap can be serialized to image only with palette. Basically, for every pixel it uses 8-bit index of color in assigned palette. The tricky part is to determine how the game understands which palette to use. In most cases, if bitmap has embedded palette, it should be used, EXCEPT Autumn Valley fence texture: there embedded palette should be ignored. In all other cases it is tricky even more: it uses !pal or !PAL palette from own SHPI archive, if it is WWWW archive, palette can be in a different SHPI before this one. In CONTROL directory most of QFS files use !pal even from different QFS file! It is a mystery how to reliably pick palette #### diff --git a/resources/eac/archives.py b/resources/eac/archives.py index e4e2b4d..d72aff0 100644 --- a/resources/eac/archives.py +++ b/resources/eac/archives.py @@ -196,7 +196,7 @@ class Fields(DeclarativeCompoundBlock.Fields): inline_description='8-bytes record, first 4 bytes is a UTF-8 ' 'string, last 4 bytes is an unsigned integer ' '(little-endian)'), - length=(lambda ctx: ctx.data('num_items'), 'num_items')), + length=lambda ctx: ctx.data('num_items')), {'description': 'An array of items, each of them represents name of SHPI item (image or palette)' ' and offset to item data in file, relatively to SHPI block start (where ' 'resource id string is presented). Names are not always unique'}) @@ -286,7 +286,7 @@ class Fields(DeclarativeCompoundBlock.Fields): {'description': 'An amount of items', 'programmatic_value': lambda ctx: len(ctx.data('items_descr'))}) items_descr = (ArrayBlock(child=IntegerBlock(length=4), - length=(lambda ctx: ctx.data('num_items'), 'num_items')), + length=lambda ctx: ctx.data('num_items')), {'description': 'An array of offsets to items data in file, relatively to wwww block start ' '(where resource id string is presented)'}) children = (ArrayBlock(length=(0, 'num_items'), child=None), @@ -355,7 +355,7 @@ class Fields(DeclarativeCompoundBlock.Fields): 'programmatic_value': lambda ctx: len(ctx.data('items_descr'))}) unk0 = (IntegerBlock(length=4), {'is_unknown': True}) - items_descr = ArrayBlock(length=(lambda ctx: ctx.data('num_items'), 'num_items'), + items_descr = ArrayBlock(length=lambda ctx: ctx.data('num_items'), child=CompoundBlock(fields=[('offset', IntegerBlock(length=4, byte_order='big'), {}), ('length', IntegerBlock(length=4, byte_order='big'), {}), ('name', NullTerminatedUTF8Block(length=8), {})])) diff --git a/resources/eac/bitmaps.py b/resources/eac/bitmaps.py index 3d59bed..a61397b 100644 --- a/resources/eac/bitmaps.py +++ b/resources/eac/bitmaps.py @@ -35,7 +35,7 @@ class Fields(DeclarativeCompoundBlock.Fields): y = (IntegerBlock(length=2), {'description': 'Y coordinate of bitmap position on screen. Used for menu/dash sprites'}) bitmap = (ArrayBlock(child=Color16Bit0565Block(simplified=True), - length=(lambda ctx: ctx.data('width') * ctx.data('height'), 'width*height')), + length=lambda ctx: ctx.data('width') * ctx.data('height')), {'description': 'Colors of bitmap pixels'}) @@ -65,9 +65,9 @@ class Fields(DeclarativeCompoundBlock.Fields): {'description': 'X coordinate of bitmap position on screen. Used for menu/dash sprites'}) y = (IntegerBlock(length=2), {'description': 'Y coordinate of bitmap position on screen. Used for menu/dash sprites'}) - bitmap = (ArrayBlock(length=(lambda ctx: ctx.data('height'), 'height'), + bitmap = (ArrayBlock(length=lambda ctx: ctx.data('height'), child=SubByteArrayBlock(bits_per_value=4, - length=(lambda ctx: ctx.data('../width'), 'width'), + length=lambda ctx: ctx.data('../width'), value_deserialize_func=lambda x: 0xFFFFFF00 | transform_bitness(x, 4), value_serialize_func=lambda x: (x & 0xFF) >> 4)), @@ -102,15 +102,15 @@ class Fields(DeclarativeCompoundBlock.Fields): unk = (IntegerBlock(length=2), {'is_unknown': True}) pivot_y = (IntegerBlock(length=2), - {'description': 'For "horz" bitmap in TNFS FAM files: Y coordinate of the horizon line on ' - 'the image. Higher value = image as horizon will be put higher on the screen. ' - 'Seems to affect only open tracks'}) + {'description': 'For "horz" bitmap in TNFS FAM files: Y coordinate of the horizon line on ' + 'the image. Higher value = image as horizon will be put higher on the screen. ' + 'Seems to affect only open tracks'}) x = (IntegerBlock(length=2), {'description': 'X coordinate of bitmap position on screen. Used for menu/dash sprites'}) y = (IntegerBlock(length=2), {'description': 'Y coordinate of bitmap position on screen. Used for menu/dash sprites'}) bitmap = (ArrayBlock(child=IntegerBlock(length=1), - length=(lambda ctx: ctx.data('width') * ctx.data('height'), 'width*height')), + length=lambda ctx: ctx.data('width') * ctx.data('height')), {'description': 'Color indexes of bitmap pixels. The actual colors are ' 'in assigned to this bitmap palette'}) @@ -132,7 +132,7 @@ class Fields(DeclarativeCompoundBlock.Fields): y = (IntegerBlock(length=2), {'description': 'Y coordinate of bitmap position on screen. Used for menu/dash sprites'}) bitmap = (ArrayBlock(child=Color32BitBlock(), - length=(lambda ctx: ctx.data('width') * ctx.data('height'), 'width*height')), + length=lambda ctx: ctx.data('width') * ctx.data('height')), {'description': 'Colors of bitmap pixels'}) @@ -153,7 +153,7 @@ class Fields(DeclarativeCompoundBlock.Fields): y = (IntegerBlock(length=2), {'description': 'Y coordinate of bitmap position on screen. Used for menu/dash sprites'}) bitmap = (ArrayBlock(child=Color16Bit1555Block(), - length=(lambda ctx: ctx.data('width') * ctx.data('height'), 'width*height')), + length=lambda ctx: ctx.data('width') * ctx.data('height')), {'description': 'Colors of bitmap pixels'}) @@ -174,5 +174,5 @@ class Fields(DeclarativeCompoundBlock.Fields): y = (IntegerBlock(length=2), {'description': 'Y coordinate of bitmap position on screen. Used for menu/dash sprites'}) bitmap = (ArrayBlock(child=Color24BitLittleEndianField(), - length=(lambda ctx: ctx.data('width') * ctx.data('height'), 'width*height')), + length=lambda ctx: ctx.data('width') * ctx.data('height')), {'description': 'Colors of bitmap pixels'}) diff --git a/resources/eac/fonts.py b/resources/eac/fonts.py index d017ce3..e9acbf5 100644 --- a/resources/eac/fonts.py +++ b/resources/eac/fonts.py @@ -64,7 +64,7 @@ class Fields(DeclarativeCompoundBlock.Fields): {'is_unknown': True}) unk6 = (IntegerBlock(length=1, required_value=0), {'is_unknown': True}) - definitions = (ArrayBlock(child=GlyphDefinition(), length=(lambda ctx: ctx.data('num_glyphs'), 'num_glyphs')), + definitions = (ArrayBlock(child=GlyphDefinition(), length=lambda ctx: ctx.data('num_glyphs')), {'description': 'Definitions of chars in this bitmap font'}) skip_bytes = (BytesBlock(length=(lambda ctx: ctx.data('bdata_ptr') - ctx.buffer.tell(), 'up to offset bdata_ptr')), diff --git a/resources/eac/geometries.py b/resources/eac/geometries.py index 94baf94..0029d56 100644 --- a/resources/eac/geometries.py +++ b/resources/eac/geometries.py @@ -213,15 +213,15 @@ class Fields(DeclarativeCompoundBlock.Fields): unknowns1 = (BytesBlock(length=12), {'is_unknown': True}) polygons = (ArrayBlock(child=OripPolygon(), - length=(lambda ctx: ctx.data('num_polygons'), 'num_polygons')), + length=lambda ctx: ctx.data('num_polygons')), {'description': 'A block with polygons of the geometry. Probably should be a start point when ' 'building model from this file'}) vertex_uvs = (ArrayBlock(child=OripVertexUV(), - length=(lambda ctx: ctx.data('num_uvs'), 'num_uvs')), + length=lambda ctx: ctx.data('num_uvs')), {'description': 'A table of texture coordinates. Items are retrieved by index, located in vmap', 'custom_offset': 'uvs_ptr'}) tex_ids = (ArrayBlock(child=OripTextureName(), - length=(lambda ctx: ctx.data('num_tex_ids'), 'num_tex_ids')), + length=lambda ctx: ctx.data('num_tex_ids')), {'description': 'A table of texture references. Items are retrieved by index, located in ' 'polygon item', 'custom_offset': 'tex_ids_ptr'}) @@ -233,20 +233,20 @@ class Fields(DeclarativeCompoundBlock.Fields): ' case of DIABLO.CFM it\'s length is equal to -3, meaning that last 3 bytes from ' 'texture names block are reused by next block'}) tex_nmb = (ArrayBlock(child=ArrayBlock(child=IntegerBlock(length=1), length=20), - length=(lambda ctx: ctx.data('num_tex_nmb'), 'num_tex_nmb')), + length=lambda ctx: ctx.data('num_tex_nmb')), {'is_unknown': True, 'custom_offset': 'tex_nmb_ptr'}) render_order = (ArrayBlock(child=RenderOrderBlock(), - length=(lambda ctx: ctx.data('num_ren_ord'), 'num_ren_ord')), + length=lambda ctx: ctx.data('num_ren_ord')), {'description': 'Render order. The exact mechanism how it works is unknown', 'custom_offset': 'ren_ord_ptr'}) fx_polys = (ArrayBlock(child=NamedIndex(), - length=(lambda ctx: ctx.data('num_fxp'), 'num_fxp')), + length=lambda ctx: ctx.data('num_fxp')), {'description': 'Indexes of polygons which participate in visual effects such as engine smoke, ' 'dust particles, tyre trails? Presented in car CFM-s. ', 'custom_offset': 'fxp_ptr'}) labels = (ArrayBlock(child=NamedIndex(), - length=(lambda ctx: ctx.data('num_lbl'), 'num_lbl')), + length=lambda ctx: ctx.data('num_lbl')), {'description': 'Marks special polygons for the game, where it should change texture on runtime such ' 'as tyres, tail lights', 'custom_offset': 'lbl_ptr'}) @@ -254,7 +254,7 @@ class Fields(DeclarativeCompoundBlock.Fields): choice_index=lambda ctx, **_: ( 0 if ctx.buffer.name.endswith('.CFM') else 1)), - length=(lambda ctx: ctx.data('num_vrtx'), 'num_vrtx')), + length=lambda ctx: ctx.data('num_vrtx')), {'description': 'A table of mesh vertices 3D coordinates. For cars uses 32:7 points, else 32:4', 'custom_offset': 'vrtx_ptr'}) vmap = (ArrayBlock(child=IntegerBlock(length=4), @@ -316,12 +316,12 @@ class Fields(DeclarativeCompoundBlock.Fields): {'is_unknown': True}) unk4 = (IntegerBlock(length=8, required_value=1), {'is_unknown': True}) - vertices = (ArrayBlock(length=(lambda ctx: ctx.data('num_vrtx'), 'num_vrtx'), + vertices = (ArrayBlock(length=lambda ctx: ctx.data('num_vrtx'), child=Point3D_16()), {'description': 'Vertex coordinates'}) offset = (BytesBlock(length=(lambda ctx: 0 if ctx.data('num_vrtx') % 2 == 0 else 6, '(num_vrtx % 2) ? 6 : 0')), {'description': 'Data offset, happens when `num_vrtx` is odd'}) - polygons = (ArrayBlock(length=(lambda ctx: ctx.data('num_plgn'), 'num_plgn'), + polygons = (ArrayBlock(length=lambda ctx: ctx.data('num_plgn'), child=GeoPolygon()), {'description': 'Array of mesh polygons', 'custom_offset': '52 + ceil(num_vrtx/2)*12'}) diff --git a/resources/eac/maps.py b/resources/eac/maps.py index d6d764c..410240c 100644 --- a/resources/eac/maps.py +++ b/resources/eac/maps.py @@ -341,11 +341,11 @@ class Fields(DeclarativeCompoundBlock.Fields): unk3 = (IntegerBlock(length=4, required_value=0), {'is_unknown': True}) prop_descr = ArrayBlock(child=PropDescr(), - length=(lambda ctx: ctx.data('num_prop_descr'), 'num_prop_descr')) + length=lambda ctx: ctx.data('num_prop_descr')) props = ArrayBlock(child=MapProp(), - length=(lambda ctx: ctx.data('num_props'), 'num_props')) + length=lambda ctx: ctx.data('num_props')) terrain = ArrayBlock(child=TerrainEntry(), - length=(lambda ctx: ctx.data('num_chunks'), 'num_chunks')) + length=lambda ctx: ctx.data('num_chunks')) def action_reverse_track(self, read_data): # FIXME lanes are a bit off: CY1.TRI now has both lanes oncoming, and racers drive on right verge. Traffic never appears diff --git a/resources/eac/misc.py b/resources/eac/misc.py index 4ab1fe8..a8b8560 100644 --- a/resources/eac/misc.py +++ b/resources/eac/misc.py @@ -22,7 +22,7 @@ class Fields(DeclarativeCompoundBlock.Fields): {'is_unknown': True}) length = (IntegerBlock(length=4), {'description': 'Text length'}) - text = (UTF8Block(length=(lambda ctx: ctx.data('length'), 'length')), + text = (UTF8Block(length=lambda ctx: ctx.data('length')), {'description': 'Text itself'}) diff --git a/resources/eac/palettes.py b/resources/eac/palettes.py index 4414f71..aa23718 100644 --- a/resources/eac/palettes.py +++ b/resources/eac/palettes.py @@ -58,7 +58,7 @@ class Fields(DeclarativeCompoundBlock.Fields): {'is_unknown': True}) unk1_length = (IntegerBlock(length=4), {'is_unknown': True}) - unk1 = (ArrayBlock(length=(lambda ctx: 2 * ctx.data('unk1_length'), '2*unk1_length'), + unk1 = (ArrayBlock(length=lambda ctx: 2 * ctx.data('unk1_length'), child=IntegerBlock(length=4)), {'is_unknown': True}) @@ -87,7 +87,7 @@ class Fields(DeclarativeCompoundBlock.Fields): 'programmatic_value': lambda ctx: len(ctx.data('colors'))}) unk2 = (BytesBlock(length=6), {'is_unknown': True}) - colors = (ArrayBlock(length=(lambda ctx: ctx.data('num_colors'), 'num_colors'), + colors = (ArrayBlock(length=lambda ctx: ctx.data('num_colors'), child=Color24BitDosBlock()), {'description': 'Colors LUT'}) @@ -108,7 +108,7 @@ class Fields(DeclarativeCompoundBlock.Fields): 'programmatic_value': lambda ctx: len(ctx.data('colors'))}) unk2 = (BytesBlock(length=6), {'is_unknown': True}) - colors = (ArrayBlock(length=(lambda ctx: ctx.data('num_colors'), 'num_colors'), + colors = (ArrayBlock(length=lambda ctx: ctx.data('num_colors'), child=Color24BitBigEndianField()), {'description': 'Colors LUT'}) @@ -129,7 +129,7 @@ class Fields(DeclarativeCompoundBlock.Fields): 'programmatic_value': lambda ctx: len(ctx.data('colors'))}) unk2 = (BytesBlock(length=6), {'is_unknown': True}) - colors = (ArrayBlock(length=(lambda ctx: ctx.data('num_colors'), 'num_colors'), + colors = (ArrayBlock(length=lambda ctx: ctx.data('num_colors'), child=Color16BitDosBlock()), {'description': 'Colors LUT'}) @@ -150,7 +150,7 @@ class Fields(DeclarativeCompoundBlock.Fields): 'programmatic_value': lambda ctx: len(ctx.data('colors'))}) unk2 = (BytesBlock(length=6), {'is_unknown': True}) - colors = (ArrayBlock(length=(lambda ctx: ctx.data('num_colors'), 'num_colors'), + colors = (ArrayBlock(length=lambda ctx: ctx.data('num_colors'), child=Color32BitBlock()), {'description': 'Colors LUT'}) @@ -173,6 +173,6 @@ class Fields(DeclarativeCompoundBlock.Fields): 'programmatic_value': lambda ctx: len(ctx.data('colors'))}) unk2 = (BytesBlock(length=6), {'is_unknown': True}) - colors = (ArrayBlock(length=(lambda ctx: ctx.data('num_colors'), 'num_colors'), + colors = (ArrayBlock(length=lambda ctx: ctx.data('num_colors'), child=Color16Bit0565Block()), {'description': 'Colors LUT'}) diff --git a/resources/test_resource.py b/resources/test_resource.py index 452dccc..f9f39b1 100644 --- a/resources/test_resource.py +++ b/resources/test_resource.py @@ -27,7 +27,7 @@ class Fields(DeclarativeCompoundBlock.Fields): {'description': 'Variable string length', 'programmatic_value': lambda ctx: len(ctx.data('var_str'))}) # TODO Should automatically determine min_length, max_length - var_str = (UTF8Block(length=(lambda ctx: ctx.data('var_str_length'), 'var_str_length')), + var_str = (UTF8Block(length=lambda ctx: ctx.data('var_str_length')), {'description': 'Test text field with variable length'}) # TODO nested compound block diff --git a/test/library/read_blocks/test_compound.py b/test/library/read_blocks/test_compound.py index 9553504..3b14e91 100644 --- a/test/library/read_blocks/test_compound.py +++ b/test/library/read_blocks/test_compound.py @@ -68,7 +68,7 @@ class Fields(DeclarativeCompoundBlock.Fields): len = (IntegerBlock(length=1), {'description': "A length of `val` array", 'programmatic_value': lambda ctx: len(ctx.data('val'))}) - val = ArrayBlock(child=IntegerBlock(length=1), length=(lambda ctx: ctx.data('len'), "len")) + val = ArrayBlock(child=IntegerBlock(length=1), length=lambda ctx: ctx.data('len')) class TestDeclarativeCompound(unittest.TestCase):