Files
kicad2msla/diagnose/diagnose_pm4n.py

107 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Run this on your machine: python3 diagnose_pm4n.py Dummy.pm4n"""
import struct, sys
path = sys.argv[1] if len(sys.argv) > 1 else 'Dummy.pm4n'
data = open(path, 'rb').read()
fsize = len(data)
print(f"File: {path} ({fsize} bytes)")
print()
# Parse ANYCUBIC header
magic = data[0:8]
version = struct.unpack_from('<I', data, 8)[0]
n_sections = struct.unpack_from('<I', data, 12)[0]
header_size = struct.unpack_from('<I', data, 16)[0]
print(f"Magic: {magic}")
print(f"Bytes 8-11: {data[8:12].hex()} (={version})")
print(f"Section count: {n_sections}")
print(f"Header size: {header_size} (0x{header_size:X})")
print()
# Section table: n_sections entries of (offset:u32, length:u32) starting at byte 20
print(f"Section table ({n_sections} entries from offset 20):")
sections = []
for i in range(n_sections):
base = 20 + i * 8
if base + 8 > fsize:
break
off = struct.unpack_from('<I', data, base)[0]
ln = struct.unpack_from('<I', data, base+4)[0]
sections.append((off, ln))
for idx, (off, ln) in enumerate(sections):
tag = data[off:off+4] if off + 4 <= fsize else b'????'
tag_str = tag.decode('ascii', errors='replace')
print(f" [{idx:2d}] offset=0x{off:06X} ({off:7d}) length={ln:7d} tag@offset={tag_str!r}")
print()
# Also peek at each section start for tag-like content
print("Content at each section offset (first 32 bytes):")
for idx, (off, ln) in enumerate(sections):
if off + 16 <= fsize:
chunk = data[off:off+32]
# Try to find sub-tags
for sub_off in range(0, min(32, len(chunk))-3):
sub_tag = chunk[sub_off:sub_off+4]
if all(32 <= b < 127 for b in sub_tag):
sub_len = struct.unpack_from('<I', chunk, sub_off+4)[0] if sub_off+8 <= len(chunk) else 0
print(f" section[{idx}]+0x{sub_off:02X} tag={sub_tag.decode()!r} next_u32={sub_len}")
break
else:
print(f" section[{idx}] hex: {chunk[:16].hex()}")
print()
# Look for exposure-like floats (1.0 to 600.0) across the whole file
print("Float values in range [1.0 .. 600.0] across whole file:")
for off in range(0, fsize - 3, 4):
v = struct.unpack_from('<f', data, off)[0]
if 1.0 <= v <= 600.0 and v == round(v, 1):
# Show context
section_hint = next((f"sec[{i}]+{off-s:d}" for i,(s,l) in enumerate(sections) if s <= off < s+l), "outside")
print(f" 0x{off:06X} {v:.1f} ({section_hint})")
# ---------------------------------------------------------------------------
# Extra: dump LAYE entries with CORRECT field layout
# (verified from binary probe: HDR=0x20, exposure@+0x00, data_off@+0x14, data_size@+0x18)
# ---------------------------------------------------------------------------
def find_tag2(d, tag):
i = d.find(tag)
return i if i >= 0 else None
LAYE_HDR_SIZE2 = 0x20
ENTRY_STRIDE2 = 0x20
laye2 = find_tag2(data, b'LAYE')
if laye2 is not None:
n_entries2 = struct.unpack_from('<I', data, laye2 + 0x10)[0]
composite_off2 = struct.unpack_from('<I', data, laye2 + 0x14)[0]
block_size2 = struct.unpack_from('<I', data, laye2 + 0x18)[0]
print(f"\nLAYE (correct layout) at 0x{laye2:06X}:")
print(f" n_entries = {n_entries2}")
print(f" composite_off= 0x{composite_off2:06X}")
print(f" block_size = {block_size2}")
expected_off = composite_off2
ok = True
for i in range(n_entries2):
base = laye2 + LAYE_HDR_SIZE2 + i * ENTRY_STRIDE2
if base + ENTRY_STRIDE2 > fsize:
break
exp = struct.unpack_from('<f', data, base + 0x00)[0]
z_pos = struct.unpack_from('<f', data, base + 0x04)[0]
d_off = struct.unpack_from('<I', data, base + 0x14)[0]
d_sz = struct.unpack_from('<I', data, base + 0x18)[0]
expected_off += block_size2
match = "OK" if d_off == expected_off and d_sz == block_size2 else "*** MISMATCH ***"
if match != "OK":
ok = False
print(f" entry[{i:2d}]: exp={exp:.1f}s z={z_pos:.2f}mm "
f"data_off=0x{d_off:06X} (exp 0x{expected_off:06X}) "
f"size={d_sz} {match}")
if ok:
print(" All entries consistent ✓")
# Also confirm global header px dimensions
gw = struct.unpack_from('<I', data, 0x7C)[0]
gh = struct.unpack_from('<I', data, 0x80)[0]
print(f"\nGlobal header px dimensions: {gw}×{gh} "
f"({'✓ correct for Mono 4' if gw==9024 and gh==5120 else '*** WRONG ***'})")