diff --git a/bemani/format/afp.py b/bemani/format/afp.py index 07b4cf0..15873ef 100644 --- a/bemani/format/afp.py +++ b/bemani/format/afp.py @@ -1,4 +1,5 @@ import io +from hashlib import md5 import struct import sys from PIL import Image # type: ignore @@ -98,6 +99,30 @@ class TextureRegion: } +class Matrix: + def __init__(self, a: float, b: float, c: float, d: float, tx: float, ty: float) -> None: + self.a = a + self.b = b + self.c = c + self.d = d + self.tx = tx + self.ty = ty + + @staticmethod + def identity() -> "Matrix": + return Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) + + +class Color: + def __init__(self, r: float, g: float, b: float, a: float) -> None: + self.r = r + self.g = g + self.b = b + self.a = a + + def __repr__(self) -> str: + return f"{round(self.r, 5)}, {round(self.g, 5)}, {round(self.b, 5)}, {round(self.a, 5)}" + class Tag: END = 0x0 SHOW_FRAME = 0x1 @@ -501,7 +526,7 @@ class SWF: add_coverage(dataoffset, size) shape_reference = f"{self.exported_name}_shape{shape_id}" - vprint(f"{prefix} Tag ID: {shape_id}, Reference: {shape_reference}") + vprint(f"{prefix} Tag ID: {shape_id}, AFP Reference: {shape_reference}, IFS GEO Filename: {md5(shape_reference.encode('utf-8')).hexdigest()}") elif tagid == Tag.AP2_DEFINE_SPRITE: sprite_flags, sprite_id = struct.unpack("BHHHBH", ap2data[dataoffset:(dataoffset + 10)]) vprint(f"{prefix} Flags: {hex(flags)}, Unk1: {hex(unk1)}, Name: {hex(nameoffset)}, Unk2: {hex(unk2)}, Unk3: {hex(unk3)}") - # TODO: Switch on tag types, parse out data. - elif False: - add_coverage(dataoffset, size) + elif tagid == Tag.AP2_PLACE_OBJECT: + # Allow us to keep track of what we've consumed. + datachunk = ap2data[dataoffset:(dataoffset + size)] + flags, depth, object_id = struct.unpack(" 0: + catchup = 4 - misalignment + add_coverage(dataoffset + running_pointer, catchup) + running_pointer += catchup + + # Handle transformation matrix. + transform = Matrix.identity() + + if flags & 0x100: + a_int, d_int = struct.unpack("> 24) & 0xFF) * 0.003921569 + color.g = float((rgba >> 16) & 0xFF) * 0.003921569 + color.b = float((rgba >> 8) & 0xFF) * 0.003921569 + color.a = float(rgba & 0xFF) * 0.003921569 + vprint(f"{prefix} Color: {color}") + + if flags & 0x4000: + rgba = struct.unpack("> 24) & 0xFF) * 0.003921569 + acolor.g = float((rgba >> 16) & 0xFF) * 0.003921569 + acolor.b = float((rgba >> 8) & 0xFF) * 0.003921569 + acolor.a = float(rgba & 0xFF) * 0.003921569 + vprint(f"{prefix} AColor: {color}") + + # Completely unsure what this is + if flags & 0x80: + raise Exception("Unhandled flag!") + + # Completely unsure what this is + if flags & 0x10000: + raise Exception("Unhandled flag!") + + if flags & 0x1000000: + raise Exception("Unhandled flag!") + + if flags & 0x2000000: + raise Exception("Unhandled flag!") + + # This flag states whether we are creating a new object on this depth, or updating one. + if flags & 0x1: + vprint(f"{prefix} Update object request") + else: + vprint(f"{prefix} Create object request") + + if running_pointer < size: + raise Exception(f"Did not consume {size - running_pointer} bytes in object instantiation!") + + elif tagid == Tag.AP2_REMOVE_OBJECT: + if size != 4: + raise Exception(f"Invalid shape size {size}") + + object_id, depth = struct.unpack(" None: # Suppress debug text unless asked @@ -747,6 +925,7 @@ class SWF: add_coverage(32, 2) add_coverage(40, 4) + # TODO: How do these point at created tags in the SWF? vprint(f"Number of Exported Tags: {num_exported_assets}") for assetno in range(num_exported_assets): asset_data_offset, asset_string_offset = struct.unpack("