#!/usr/bin/env python3 """ Pack hekate build output into a ZIP with SD card layout: ZIP ├── payload.bin (hekate payload for injection / root of SD) └── bootloader/ ├── update.bin (hekate payload; for modchips / auto-update) ├── ini/ (empty; for More configs) ├── res/ (empty; for user icons/background) ├── payloads/ (empty; for Payloads menu) └── sys/ ├── nyx.bin ├── libsys_lp0.bso └── libsys_minerva.bso Run from repo root after `make`. No hekate_ipl.ini or patches.ini. Optional: res.pak, thk.bin, emummc.kipm must be added separately. """ import os import re import sys import zipfile from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent OUTPUT = REPO_ROOT / "output" def parse_versions(): """Read Versions.inc and return (bl_ver, nyx_ver) e.g. ('6.5.1', '1.9.1').""" inc = REPO_ROOT / "Versions.inc" if not inc.exists(): return ("0.0.0", "0.0.0") vals = {} for line in inc.read_text().splitlines(): m = re.match(r"(\w+)\s*:?=\s*(\d+)", line.strip()) if m: vals[m.group(1)] = m.group(2) bl_ver = f"{vals.get('BLVERSION_MAJOR', 0)}.{vals.get('BLVERSION_MINOR', 0)}.{vals.get('BLVERSION_HOTFX', 0)}" nyx_ver = f"{vals.get('NYXVERSION_MAJOR', 0)}.{vals.get('NYXVERSION_MINOR', 0)}.{vals.get('NYXVERSION_HOTFX', 0)}" return (bl_ver, nyx_ver) def main(): bl_ver, nyx_ver = parse_versions() zip_name = f"hekate_{bl_ver}_Nyx_{nyx_ver}.zip" zip_path = OUTPUT / zip_name # Required build artifacts payload_src = OUTPUT / "hekate.bin" sys_files = [ ("bootloader/sys/nyx.bin", OUTPUT / "nyx.bin"), ("bootloader/sys/libsys_lp0.bso", OUTPUT / "libsys_lp0.bso"), ("bootloader/sys/libsys_minerva.bso", OUTPUT / "libsys_minerva.bso"), ] missing = [] if not payload_src.exists(): missing.append(str(payload_src)) for _, src in sys_files: if not src.exists(): missing.append(str(src)) if missing: print("Missing build outputs (run 'make' first):", file=sys.stderr) for m in missing: print(" ", m, file=sys.stderr) sys.exit(1) zip_path.parent.mkdir(parents=True, exist_ok=True) with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: # Payload at root (for injection / some setups) zf.write(payload_src, "payload.bin") # bootloader/update.bin (same payload; for modchips / auto-update) zf.write(payload_src, "bootloader/update.bin") # bootloader/sys/* for arc, src in sys_files: zf.write(src, arc) # Empty dirs (ZIP has no true directories; .keep so extractors create them) for d in ("bootloader/ini/", "bootloader/res/", "bootloader/payloads/", "bootloader/screenshots/"): zf.writestr(d + ".keep", "") print(f"Wrote {zip_path}") print("Note: res.pak, thk.bin, emummc.kipm are not in this repo; add to bootloader/sys/ if needed.") if __name__ == "__main__": main()