diff --git a/bemani/utils/afputils.py b/bemani/utils/afputils.py
index 351a9bb..d31efcf 100644
--- a/bemani/utils/afputils.py
+++ b/bemani/utils/afputils.py
@@ -38,10 +38,7 @@ class Texture:
header_flags1: int,
header_flags2: int,
header_flags3: int,
- unk_flags1: int,
- unk_flags2: int,
- unk_flags3: int,
- unk_flags4: int,
+ fmtflags: int,
rawdata: bytes,
imgdata: Any,
) -> None:
@@ -52,10 +49,7 @@ class Texture:
self.header_flags1 = header_flags1
self.header_flags2 = header_flags2
self.header_flags3 = header_flags3
- self.unk_flags1 = unk_flags1
- self.unk_flags2 = unk_flags2
- self.unk_flags3 = unk_flags3
- self.unk_flags4 = unk_flags4
+ self.fmtflags = fmtflags
self.raw = rawdata
self.img = imgdata
@@ -448,15 +442,15 @@ class AFPFile:
fmt = fmtflags & 0xFF
# Extract flags that the game cares about.
- flags1 = (fmtflags >> 24) & 0xFF
- flags2 = (fmtflags >> 16) & 0xFF
+ # flags1 = (fmtflags >> 24) & 0xFF
+ # flags2 = (fmtflags >> 16) & 0xFF
# These flags may have some significance, such as
# the unk3/unk4 possibly indicating texture doubling?
- unk1 = 3 if (flags1 & 0xF == 1) else 1
- unk2 = 3 if ((flags1 >> 4) & 0xF == 1) else 1
- unk3 = 1 if (flags2 & 0xF == 1) else 2
- unk4 = 1 if ((flags2 >> 4) & 0xF == 1) else 2
+ # unk1 = 3 if (flags1 & 0xF == 1) else 1
+ # unk2 = 3 if ((flags1 >> 4) & 0xF == 1) else 1
+ # unk3 = 1 if (flags2 & 0xF == 1) else 2
+ # unk4 = 1 if ((flags2 >> 4) & 0xF == 1) else 2
if self.endian == "<" and magic != b"TDXT":
raise Exception("Unexpected texture format!")
@@ -554,11 +548,8 @@ class AFPFile:
header_flags1,
header_flags2,
header_flags3,
- unk1,
- unk2,
- unk3,
- unk4,
- raw_data,
+ fmtflags & 0xFFFFFF00,
+ raw_data[64:],
img,
)
)
@@ -602,8 +593,6 @@ class AFPFile:
header_offset += 8
if offset != 0 and length > 0:
- self.texture_to_region = [TextureRegion(0, 0, 0, 0, 0)] * length
-
for i in range(length):
descriptor_offset = offset + (10 * i)
texture_no, left, top, right, bottom = struct.unpack(
@@ -618,7 +607,7 @@ class AFPFile:
# TODO: The offsets here seem to be off by a power of 2, there
# might be more flags in the above texture format that specify
# device scaling and such?
- self.texture_to_region[i] = TextureRegion(texture_no, left, top, right, bottom)
+ self.texture_to_region.append(TextureRegion(texture_no, left, top, right, bottom))
vprint(f"Bit 0x000008 - regions; count: {length}, offset: {hex(offset)}")
else:
@@ -1111,8 +1100,92 @@ class AFPFile:
# First, lay down pointers and length, regardless of number of entries.
bitchunks[0] = struct.pack(f"{self.endian}II", len(self.textures), offset)
+ # Now, calculate where we can put texturedata.
+ tex_offset = AFPFile.align(len(body) + (len(self.textures) * 12))
+ textures: bytes = b""
+ name_to_offset: Dict[str, Tuple[int, int]] = {}
+
+ # Now, possibly compress and lay down textures.
for texture in self.textures:
- raise Exception("TODO!")
+ # Construct the TXDT texture format from our parsed results.
+ if self.endian == "<":
+ magic = b"TDXT"
+ elif self.endian == ">":
+ magic != b"TXDT"
+ else:
+ raise Exception("Unexpected texture format!")
+
+ fmtflags = (texture.fmtflags & 0xFFFFFF00) | (texture.fmt & 0xFF)
+
+ raw_texture = struct.pack(
+ f"{self.endian}4sIIIHHIII",
+ magic,
+ texture.header_flags1,
+ texture.header_flags2,
+ 64 + len(texture.raw),
+ texture.width,
+ texture.height,
+ fmtflags,
+ 0,
+ 0,
+ ) + (b'\0' * 12) + struct.pack(
+ f"{self.endian}I", texture.header_flags3,
+ ) + (b'\0' * 16) + texture.raw
+
+ if self.legacy_lz:
+ raise Exception("We don't support legacy lz mode!")
+ elif self.modern_lz:
+ # We need to compress the raw texture.
+ lz77 = Lz77()
+ compressed_texture = lz77.compress(raw_texture)
+
+ # Make room for the texture, remember where we put it.
+ textures = AFPFile.pad(textures, AFPFile.align(len(textures)))
+ name_to_offset[texture.name] = (len(textures), len(compressed_texture) + 8)
+
+ # Place down the mini-header and the texture itself.
+ textures += struct.pack(
+ ">II",
+ len(raw_texture),
+ len(compressed_texture),
+ ) + compressed_texture
+ else:
+ # We just need to place the raw texture down.
+ textures = AFPFile.pad(textures, AFPFile.align(len(textures)))
+ name_to_offset[texture.name] = (len(textures), len(raw_texture) + 8)
+
+ textures += struct.pack(
+ ">II",
+ len(raw_texture),
+ len(raw_texture),
+ ) + raw_texture
+
+ # Now, make sure the texture block is padded to 4 bytes, so we can figure out
+ # where strings go.
+ textures = AFPFile.pad(textures, AFPFile.align(len(textures)))
+ string_offset = AFPFile.align(len(body) + (len(self.textures) * 12) + len(textures))
+
+ # Now, write out textures and strings.
+ for texture in self.textures:
+ if texture.name not in string_offsets:
+ # We haven't written this string out yet, so put it on our pending list.
+ pending_strings[texture.name] = string_offset
+ string_offsets[texture.name] = string_offset
+
+ # Room for the null byte!
+ string_offset += len(texture.name) + 1
+
+ # Write out the chunk itself.
+ body += struct.pack(
+ f"{self.endian}III",
+ string_offsets[texture.name],
+ name_to_offset[texture.name][1], # Structure length
+ tex_offset + name_to_offset[texture.name][0], # Structure offset
+ )
+
+ # Now, put down the texture chunk itself and then strings that were new in this chunk.
+ body = self.write_strings(body + textures, pending_strings)
+ pending_strings = {}
if self.features & 0x08:
# Mapping between individual graphics and their respective textures.
@@ -1122,8 +1195,15 @@ class AFPFile:
# First, lay down pointers and length, regardless of number of entries.
bitchunks[3] = struct.pack(f"{self.endian}II", len(self.texture_to_region), offset)
- for tex_to_region in self.texture_to_region:
- raise Exception("TODO!")
+ for bounds in self.texture_to_region:
+ body += struct.pack(
+ f"{self.endian}HHHHH",
+ bounds.textureno,
+ bounds.left,
+ bounds.top,
+ bounds.right,
+ bounds.bottom,
+ )
if self.features & 0x40:
# Unknown file chunk.
@@ -1461,20 +1541,19 @@ def main() -> int:
with open(f"{filename}.raw", "wb") as bfp:
bfp.write(texture.raw)
- if args.write_mappings:
- if args.pretend:
- print(f"Would write {filename}.xml texture info...")
- else:
- print(f"Writing {filename}.xml texture info...")
- with open(f"{filename}.xml", "w") as sfp:
- sfp.write(textwrap.dedent(f"""
-
- {texture.width}
- {texture.height}
- {hex(texture.fmt)}
- {filename}.raw
-
- """).strip())
+ if args.pretend:
+ print(f"Would write {filename}.xml texture info...")
+ else:
+ print(f"Writing {filename}.xml texture info...")
+ with open(f"{filename}.xml", "w") as sfp:
+ sfp.write(textwrap.dedent(f"""
+
+ {texture.width}
+ {texture.height}
+ {hex(texture.fmt)}
+ {filename}.raw
+
+ """).strip())
if args.write_mappings:
for i, name in enumerate(afpfile.regionmap.entries):