#!/usr/bin/env python3 """ Decode the composite block and first layer block from a pm4n file, report pixel counts and first/last pixel values. Run: python3 decode_rle_blocks.py output/pm4n/Flow_Controller_Panel-Front.pm4n """ import struct, sys path = sys.argv[1] data = open(path, 'rb').read() fsize = len(data) print(f"File: {path} ({fsize} bytes)\n") LAYE_HDR = 0x20 STRIDE = 0x20 laye = data.find(b'LAYE') n_entries = struct.unpack_from('> 4) & 0xF colour = 255 if nibble == 0xF else 0 run = ((b0 & 0x0F) << 8) | b1 run += 1 pixels.extend([colour] * run) i += 2 return pixels, i - offset def summarize_block(name, offset, size): print(f"\n{name} at 0x{offset:06X}, {size} bytes:") if offset + size > fsize: print(f" *** PAST EOF (file ends at 0x{fsize:06X}) ***") size = max(0, fsize - offset) # Show first 16 bytes raw raw = data[offset:offset+16] print(f" First 16 bytes: {raw.hex()}") # Decode RLE pixels, bytes_consumed = decode_rle(data, offset, size) print(f" Decoded: {len(pixels)} pixels from {bytes_consumed} bytes") print(f" Expected: {9024*5120} pixels ({9024}×{5120})") if pixels: whites = sum(1 for p in pixels if p >= 128) blacks = len(pixels) - whites print(f" White px: {whites} ({100*whites/len(pixels):.1f}%)") print(f" Black px: {blacks} ({100*blacks/len(pixels):.1f}%)") # Check if it decodes to correct pixel count if len(pixels) == 9024 * 5120: print(f" ✓ Correct pixel count") else: print(f" *** WRONG pixel count (off by {len(pixels) - 9024*5120})") # Show first few runs print(f" First pixels: {pixels[:20]}") # Composite block summarize_block("Composite block (block 0)", composite_off, block_size) # First entry block e0_base = laye + LAYE_HDR e0_data_off = struct.unpack_from('