import pathlib
import sys
BOOTLOADER_OFFSET = 0x0
TABLE_MAGIC = b"\xaa\x50"
TABLE_MAX_LEN = 0xC00
def trim(block: bytes) -> bytes:
return block.rstrip(b"\xff")
def find_table_offset(image: bytes, candidates=(0x8000, 0x9000, 0x10000)) -> int:
for offset in candidates:
if image[offset : offset + 2] == TABLE_MAGIC:
return offset
raise SystemExit(
"no partition table found at any of "
+ ", ".join(hex(candidate) for candidate in candidates)
)
def find_app_offset(image: bytes, table_offset: int) -> int:
position = table_offset
while position + 32 <= len(image):
entry = image[position : position + 32]
if entry[:2] != TABLE_MAGIC:
break
kind = entry[2]
offset = int.from_bytes(entry[4:8], "little")
if kind == 0:
return offset
position += 32
raise SystemExit("the partition table lists no app partition")
def main() -> None:
if len(sys.argv) != 3:
raise SystemExit(f"usage: {sys.argv[0]} MERGED_IMAGE OUTPUT_DIR")
merged = pathlib.Path(sys.argv[1]).read_bytes()
out = pathlib.Path(sys.argv[2])
out.mkdir(parents=True, exist_ok=True)
table_offset = find_table_offset(merged)
app_offset = find_app_offset(merged, table_offset)
if not (BOOTLOADER_OFFSET < table_offset < app_offset):
raise SystemExit(
f"unexpected layout: table {table_offset:#x}, app {app_offset:#x}"
)
if merged[app_offset] != 0xE9:
raise SystemExit(f"no application image magic at {app_offset:#x}")
pieces = [
("bootloader.bin", BOOTLOADER_OFFSET, trim(merged[BOOTLOADER_OFFSET:table_offset])),
(
"partition-table.bin",
table_offset,
trim(merged[table_offset : table_offset + TABLE_MAX_LEN]),
),
("firmware.bin", app_offset, trim(merged[app_offset:])),
]
for name, offset, contents in pieces:
if not contents:
raise SystemExit(f"{name} came out empty at {offset:#x}")
(out / name).write_bytes(contents)
print(f"{name} {offset:#x} {len(contents)} bytes")
print(
"IMAGE_ARGS="
+ " ".join(f"--image {out / name}:{offset:#x}" for name, offset, _ in pieces)
)
if __name__ == "__main__":
main()