#!/usr/bin/env python3 """Dumps a puppet .mdl file's MDLS bone array: parent index, bind-pose matrix determinant (to spot a mirrored/negative-scale bone), and translation - without needing a debug build. Mirrors the parsing in CImage.cpp's parsePuppetBones()/readPuppetMeshData(): MDLS header is 9 bytes, then nextSectionOffset(u32) + boneCount(u32), then per bone: 1 padding byte + type(u32) + parent(i32) + matrixBytes(u32) + [16 floats if matrixBytes==64] + null-terminated name. The 16 floats are the file's row-major matrix - reshaping them row-major (not glm's column-major convention) gives the actual authored matrix back, which is what this script does. Usage: mdl_bones.py Needs: pip install numpy """ import struct import sys import numpy as np def read_cstr(data, offset): end = data.index(b'\x00', offset) return data[offset:end].decode('latin1', errors='replace'), end + 1 def main(path): data = open(path, 'rb').read() marker_size = 9 mdls_offset = data.find(b'MDLS', marker_size) if mdls_offset < 0: raise SystemExit("no MDLS section found - not a puppet mesh, or a layout this script doesn't handle") print('MDLS at', mdls_offset, 'file size', len(data)) offset = mdls_offset + 9 # skip the MDLS0001-style 9-byte magic next_section_offset = struct.unpack_from('