Update mode: - Sysmodules offered by OmniNX Downloader are removed again, since an old build may not run on the new Atmosphere (SysDVR, Fizeau, MissionControl, ldn_mitm), plus sys-botbase and emuiibo. Title IDs taken from the current release zips. Users reinstall them via the Downloader. - Configs of other CFW packs (kefir-updater, blue_pack_updater) and the old Ultrahand name (uberhand) are removed again. - Former OmniNX folders games, NSPs (Tools), scripts and tools are removed again. Patched Apps stays out: the Downloader installs into it. Clean mode: also removes sept/ and fusee-secondary.bin (old Atmosphere boot chain).
81 lines
2.9 KiB
Python
81 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Audit source/deletion_lists_update.h against the OmniNX pack repo.
|
|
|
|
Lists every update-mode deletion entry and whether the pack ships it now,
|
|
shipped it in the past, or never did. Entries under switch/ or config/ that
|
|
the pack never shipped are almost always user installs and should not be in
|
|
the update list (see the header comment in deletion_lists_update.h).
|
|
|
|
usage: python3 tools/audit_update_list.py ../OmniNX
|
|
"""
|
|
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
UPDATE_H = ROOT / "source" / "deletion_lists_update.h"
|
|
PACK_PREFIX = re.compile(r"^(?:variants/[^/]+/(?:extras/hekate-ams-pro/)?|staging/)")
|
|
|
|
# Not shipped as files, but removed on purpose during updates.
|
|
INTENTIONAL = {
|
|
"config/nx-hbmenu", # hbmenu settings (hbmenu.nro is shipped)
|
|
"config/sys-patch", # sys-patch settings (sys-patch is shipped)
|
|
"switch/tinfoil/db", # stale title DB cache
|
|
"config/blue_pack_updater", # other CFW packs
|
|
"config/kefir-updater",
|
|
"config/uberhand", # old Ultrahand name
|
|
}
|
|
|
|
|
|
def pack_paths(repo: Path, all_history: bool) -> list[str]:
|
|
if all_history:
|
|
cmd = ["git", "-C", str(repo), "log", "--all", "--name-only", "--format=", "--", "variants", "staging"]
|
|
else:
|
|
cmd = ["git", "-C", str(repo), "ls-files", "variants", "staging"]
|
|
out = subprocess.run(cmd, check=True, capture_output=True, text=True).stdout
|
|
paths = set()
|
|
for line in out.splitlines():
|
|
line = line.strip()
|
|
if line and PACK_PREFIX.match(line):
|
|
paths.add(PACK_PREFIX.sub("", line).lower())
|
|
return sorted(paths)
|
|
|
|
|
|
def covered(paths: list[str], entry: str) -> bool:
|
|
return any(p == entry or p.startswith(entry + "/") for p in paths)
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) != 2:
|
|
print(__doc__.strip())
|
|
return 2
|
|
repo = Path(sys.argv[1])
|
|
current = pack_paths(repo, all_history=False)
|
|
history = pack_paths(repo, all_history=True)
|
|
|
|
text = UPDATE_H.read_text(encoding="utf-8")
|
|
suspicious = 0
|
|
for m in re.finditer(r"static const char\*\s+(\w+)\[\]\s*=\s*\{(.*?)\};", text, re.S):
|
|
for entry in re.findall(r'"sd:/([^"]+)"', m.group(2)):
|
|
e = entry.lower()
|
|
if covered(current, e):
|
|
state = "shipped"
|
|
elif covered(history, e):
|
|
state = "dropped"
|
|
else:
|
|
state = "never"
|
|
flag = ""
|
|
if state == "never" and e not in INTENTIONAL and (e.startswith("switch/") or e.startswith("config/")):
|
|
flag = " <-- user install? remove from update list"
|
|
suspicious += 1
|
|
print(f"{state:8} {m.group(1):36} {entry}{flag}")
|
|
|
|
print(f"\n{suspicious} suspicious entr{'y' if suspicious == 1 else 'ies'}")
|
|
return 1 if suspicious else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|