import json import os import sys def streamline_materials(broken_path, working_path, output_path): print(f"[*] Loading working templates from: {os.path.basename(working_path)}") try: with open(working_path, 'r', encoding='utf-8') as f: working_data = json.load(f) except FileNotFoundError: print(f"[-] ERROR: Could not find '{os.path.basename(working_path)}'. Please check the filename.") return print(f"[*] Applying patches to: {os.path.basename(broken_path)}") try: with open(broken_path, 'r', encoding='utf-8') as f: broken_data = json.load(f) except FileNotFoundError: print(f"[-] ERROR: Could not find '{os.path.basename(broken_path)}'. Please check the filename.") return # 1. Build a template library from the working demo file working_templates = {} for mat in working_data: mat_name = mat.get("material_name", "") shader_name = mat.get("shader_name", "") # Isolate the shadow shader as a special case, otherwise use the shader_name key = "shadow" if "shadow" in mat_name.lower() else shader_name if key not in working_templates: working_templates[key] = { "hash": mat.get("shader_switches_hash_referenceonly"), "shaders": mat.get("shaders", []), "material_switches": mat.get("material_switches", []) } patch_count = 0 # 2. Iterate through the broken file and apply exact rules for mat in broken_data: mat_name = mat.get("material_name", "") shader_name = mat.get("shader_name", "") key = "shadow" if "shadow" in mat_name.lower() else shader_name template = working_templates.get(key) if not template: print(f"[-] Warning: No working template found for '{mat_name}' (Shader: {key}), skipping...") continue # Overwrite the rendering arrays to match the compiled demo exactly mat["shader_switches_hash_referenceonly"] = template["hash"] mat["shaders"] = template["shaders"] mat["material_switches"] = template["material_switches"] # 3. Apply strict Texture Slot rules new_textures = [] for tex in mat.get("textures", []): slot = tex.get("texture_slot") # RULE: Never change names for slots 0, 3, and 7 if slot in [0, 3, 7]: new_textures.append(tex) # RULE: Update toon ramps to demo equivalents (Slot 9) elif slot == 9: if key in ["chr_skin", "chr_eye"]: tex["texture_image_name"] = "toon_chr02" else: tex["texture_image_name"] = "toon_chr01" new_textures.append(tex) # Any other slot (like slot 8 for legacy alpha masks) is automatically purged mat["textures"] = new_textures print(f"[+] Successfully patched material: '{mat_name}' (ID: {mat.get('id_referenceonly')})") patch_count += 1 # 4. Save the flawless file if patch_count > 0: with open(output_path, 'w', encoding='utf-8') as f: json.dump(broken_data, f, indent=4) print(f"\n[SUCCESS] Saved perfectly mapped JSON to: {os.path.basename(output_path)}") else: print("\n[!] No materials were patched. Check your templates.") def main(): # Reliably get the directory where this script is located in Windows current_dir = os.path.dirname(os.path.abspath(__file__)) print(f"--- Mod Material Auto-Patcher ---") print(f"Working Directory: {current_dir}\n") # --- DEFINE YOUR INPUT FILES HERE --- WORKING_FILE = "working_material_info.json" BROKEN_FILE = "broken_material_info.json" OUTPUT_FILE = "material_info.json" working_path = os.path.join(current_dir, WORKING_FILE) broken_path = os.path.join(current_dir, BROKEN_FILE) output_path = os.path.join(current_dir, OUTPUT_FILE) streamline_materials(broken_path, working_path, output_path) # Pause execution so the user can read the console output input("\nPress Enter to exit...") if __name__ == "__main__": main()