#!/usr/bin/env python3
"""Prépare le rootfs d'un firmware stock pour l'émulateur (mêmes modifs que l'image 5F138 de devos50).

  mkbase-rootfs ROOTFS_STOCK.img SORTIE.img [--size-mb 700]

- ne garde que les LaunchDaemons nécessaires à SpringBoard (les autres attendent du matériel absent)
- SpringBoard sans accélération MBX 2D (LK_ENABLE_MBX2D=0), lockbot sans redirection vers /dev/null
- une seule partition, montée en écriture (pas de /private/var séparé)
- enregistrements d'activation Lockdown (sinon écran « Connectez à iTunes »)
- verrouillage automatique désactivé (le SoC émulé ne sait pas sortir de veille)
- emuagent (réseau PPP + pilotage, ~/ipod-emu/agent/overlay, à construire avec make) et mDNSResponder (DNS)
"""
import argparse, os, plistlib, shutil, subprocess, sys, tempfile
from importlib.machinery import SourceFileLoader

EMU = os.environ.get("IPOD_EMU_HOME", os.path.expanduser("~/ipod-emu"))
mk = SourceFileLoader("mk", os.path.join(os.path.dirname(os.path.realpath(__file__)), "ipod-mkimage")).load_module()
hfs = mk.hfs

KEEP = {
    "com.apple.ReportCrash.DirectoryService.plist", "com.apple.ReportCrash.Jetsam.plist",
    "com.apple.ReportCrash.SafetyNet.plist", "com.apple.ReportCrash.SimulateCrash.plist",
    "com.apple.ReportCrash.plist", "com.apple.SCHelper-embedded.plist", "com.apple.SpringBoard.plist",
    "com.apple.configd.plist", "com.apple.mobile.lockbot.plist", "com.apple.mobile.lockdown.plist",
    "com.apple.notifyd.plist",
}
LD = "/System/Library/LaunchDaemons"
AGENT = os.path.join(EMU, "agent/overlay")
LOCKDOWN = os.path.join(EMU, "assets/Lockdown")  # enregistrements d'activation (extraits de la NAND 5F138 de devos50)


def edit_plist(img, path, fn, tmp):
    data = subprocess.run([mk.HFSPLUS, img, "cat", path], capture_output=True, check=True).stdout
    p = plistlib.loads(data)
    fn(p)
    out = os.path.join(tmp, os.path.basename(path))
    with open(out, "wb") as f:
        plistlib.dump(p, f, fmt=plistlib.FMT_BINARY)
    hfs(img, "rm", path)
    hfs(img, "add", out, path)


def main():
    a = argparse.ArgumentParser(usage=__doc__)
    a.add_argument("src"); a.add_argument("dst"); a.add_argument("--size-mb", type=int, default=700)
    a = a.parse_args()
    shutil.copyfile(a.src, a.dst)
    img = a.dst
    with tempfile.TemporaryDirectory() as tmp:
        for line in hfs(img, "ls", LD).splitlines():
            name = line.split()[-1] if line.startswith("1") else None
            if name and name.endswith(".plist") and name not in KEEP:
                hfs(img, "rm", f"{LD}/{name}")
        # grow juste après les suppressions : il lui faut un peu de place pour agrandir le bitmap d'allocation (le
        # rootfs stock est plein), et les fichiers ajoutés ensuite sont alloués d'un bloc dans l'espace neuf
        out = hfs(img, "grow", str(a.size_mb * 1024 * 1024))
        if "grew volume" not in out:
            sys.exit(f"grow : {out}")
        edit_plist(img, f"{LD}/com.apple.SpringBoard.plist",
                   lambda p: p.setdefault("EnvironmentVariables", {}).update({"LK_ENABLE_MBX2D": "0"}), tmp)
        edit_plist(img, f"{LD}/com.apple.mobile.lockbot.plist",
                   lambda p: [p.pop(k, None) for k in ("StandardErrorPath", "StandardOutPath")], tmp)

        fstab = os.path.join(tmp, "fstab")
        open(fstab, "w").write("/dev/disk0s1 / hfs rw 0 1\n")
        hfs(img, "rm", "/private/etc/fstab")
        hfs(img, "add", fstab, "/private/etc/fstab")

        mk.add_tree(img, LOCKDOWN, "/private/var/root/Library/Lockdown")

        sb = os.path.join(tmp, "com.apple.springboard.plist")
        with open(sb, "wb") as f:
            # pas de verrouillage auto, pas d'alerte « Edit Home Screen » au premier démarrage
            plistlib.dump({"SBAutoLockTime": -1, "SBAutoDimTime": -1, "SBDidShowReorderText": True}, f, fmt=plistlib.FMT_BINARY)
        prefs = "/private/var/mobile/Library/Preferences"
        mk.mkdirs(img, prefs)
        hfs(img, "add", sb, f"{prefs}/com.apple.springboard.plist")

        # iPod d'un utilisateur français : langue, région, fuseau (localtime est un lien symbolique, que xpwn ne sait
        # pas créer : on y copie le fichier de zone)
        gp = os.path.join(tmp, ".GlobalPreferences.plist")
        g = plistlib.loads(subprocess.run([mk.HFSPLUS, img, "cat", f"{prefs}/.GlobalPreferences.plist"], capture_output=True).stdout)
        langs = [l for l in g.get("AppleLanguages", []) if l != "fr"]
        g["AppleLanguages"] = ["fr"] + langs
        g["AppleLocale"] = "fr_FR"
        with open(gp, "wb") as f:
            plistlib.dump(g, f, fmt=plistlib.FMT_BINARY)
        hfs(img, "rm", f"{prefs}/.GlobalPreferences.plist")
        hfs(img, "add", gp, f"{prefs}/.GlobalPreferences.plist")
        tz = os.path.join(tmp, "localtime")
        with open(tz, "wb") as f:
            f.write(subprocess.run([mk.HFSPLUS, img, "cat", "/usr/share/zoneinfo/Europe/Paris"], capture_output=True, check=True).stdout)
        hfs(img, "rm", "/private/var/db/timezone/localtime")
        hfs(img, "add", tz, "/private/var/db/timezone/localtime")
        if not os.path.isdir(AGENT):
            sys.exit(f"{AGENT} absent : lancer make dans {os.path.dirname(AGENT)}")
        mk.add_tree(img, AGENT, "/")
    print(hfs(img, "ls", LD).splitlines()[-1])


if __name__ == "__main__":
    main()
