diff --git a/scripts/cucholix/format_contents_repo.py b/scripts/cucholix/format_contents_repo.py new file mode 100644 index 0000000..768a3ae --- /dev/null +++ b/scripts/cucholix/format_contents_repo.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +import os +import re +import sys +import shutil +import tempfile +import subprocess +import unicodedata + +def sanitize_name(name): + """Normalize game names: remove symbols, accents, and replace ' - ' with space.""" + n = unicodedata.normalize('NFKD', name).encode('ascii','ignore').decode('ascii') + n = n.replace("'", "").replace("’", "").replace("`", "").replace('"', '') + n = n.replace(" - ", " ") # Replace problematic ' - ' only in the game name + return ' '.join(n.split()).strip() + +def title_case_preserve_numbers(name): + """Capitalize words while preserving full-uppercase acronyms.""" + return ' '.join(w.capitalize() if not w.isupper() else w for w in name.split()) + +def find_title_id(path): + """Look for 16-character Title ID under any 'contents/' directory.""" + for root, dirs, _ in os.walk(path): + if os.path.basename(root).lower() == 'contents': + for d in dirs: + if re.fullmatch(r'[0-9A-Fa-f]{16}', d): + return d + return None + +def extract_with_7z(rar_path, tmpdir): + """Extract only atmosphere/contents/* using 7z.""" + cmd = [ + "7z", "x", "-y", + rar_path, + f"-o{tmpdir}", + "atmosphere/contents/*" + ] + proc = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + return proc.returncode == 0 or find_title_id(tmpdir) is not None + +def process_rar(root_folder, rar_relpath, output_root): + subdir, file = os.path.split(rar_relpath) + version_match = re.match(r"release_(.+)\.rar$", file, re.I) + if not version_match: + print(f"❌ Invalid release name: {file}") + return + version = version_match.group(1) + raw_game_name = os.path.basename(subdir) + + # Clean and normalize game name (remove " - ", keep suffix clean) + cleaned_name = sanitize_name(raw_game_name) + game_name = title_case_preserve_numbers(cleaned_name) + pack_label = f"{game_name} - Graphics Pack" # Final folder + + rar_path = os.path.join(root_folder, rar_relpath) + with tempfile.TemporaryDirectory() as tmp: + ok = extract_with_7z(rar_path, tmp) + if not ok: + print(f"❌ Extraction error (7z) on {rar_relpath}") + return + + title_id = find_title_id(tmp) + if not title_id: + print(f"❌ No Title ID found in {rar_relpath}") + return # Skip making directory if no Title ID found + + version_dir = os.path.join(output_root, pack_label, version) + os.makedirs(version_dir, exist_ok=True) + + src = os.path.join(tmp, "atmosphere", "contents", title_id) + dst = os.path.join(version_dir, title_id) + try: + shutil.copytree(src, dst, dirs_exist_ok=True) + print(f"✅ {rar_relpath} → {os.path.join(pack_label, version, title_id)}") + except Exception as e: + print(f"❌ Copy failed for {rar_relpath}: {e}") + +def main(): + if len(sys.argv) != 2: + print("Usage: python collect_content_mods.py /path/to/root") + sys.exit(1) + + root = sys.argv[1] + output = os.path.join(root, "format2") + os.makedirs(output, exist_ok=True) + + tasks = [] + for dirpath, _, files in os.walk(root): + for fn in files: + if re.match(r"release_.*\.rar$", fn, re.I): + rel = os.path.relpath(os.path.join(dirpath, fn), root) + tasks.append(rel) + + for relpath in tasks: + process_rar(root, relpath, output) + + print("✅ Done.") + +if __name__ == "__main__": + main()