Files
hekate/sd_out.py
niklascfw 6b956c6fe2 Nyx: emuMMC tab realign on reload, sd_out script, bootloader/sys doc
- gui_emummc_tools: re-apply container position after reload so content
  does not jump to top when returning from sub-windows (Change emuMMC, etc.)
- Add sd_out.py: pack build output to output/hekate_X.Y.Z_Nyx_A.B.C.zip
  (payload.bin, bootloader/update.bin, bootloader/sys/*, no ini/patches)
- Add nyx/BOOTLOADER_SYS_ORIGINS.md: where res.pak, thk.bin, emummc.kipm, l4t/ come from

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-20 22:54:16 +01:00

91 lines
3.1 KiB
Python

#!/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()