import os import json import hashlib import argparse # ============================================================ # CONFIGURATION - edit these values for your game/project # ============================================================ MANIFEST_FILENAME = "manifest.json" # Files the updater must never touch (launcher itself, local settings, etc.) IGNORE_FILES = { MANIFEST_FILENAME, "updater.exe", # Your compiled updater executable "Option.ini", # Example: graphics/sound settings "WindowsInfo.ini", # Example: window layout settings } # Folders the updater must never scan or update IGNORE_FOLDERS = { "screenshots", "logs", "replays", } # File extensions to always skip (lowercase, without the dot doesn't matter, include the dot) IGNORE_EXTENSIONS = { ".log", } # ============================================================ # END CONFIGURATION # ============================================================ def get_md5(filepath): """Computes the MD5 hash of a file.""" hash_md5 = hashlib.md5() try: with open(filepath, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) return hash_md5.hexdigest() except IOError: return None def generate(client_dir): if not os.path.exists(client_dir) or not os.path.isdir(client_dir): print(f"Error: directory '{client_dir}' not found!") return manifest = {} manifest_path = os.path.join(client_dir, MANIFEST_FILENAME) print(f"Scanning client folder: {client_dir}") for root, dirs, files in os.walk(client_dir): # Filter out ignored folders (case-insensitive) dirs[:] = [d for d in dirs if d.lower() not in IGNORE_FOLDERS] for file in files: if file in IGNORE_FILES: continue if any(file.lower().endswith(ext) for ext in IGNORE_EXTENSIONS): continue filepath = os.path.join(root, file) # Path relative to the client folder (this is what the updater expects) rel_path = os.path.relpath(filepath, client_dir) # Convert Windows-style separators to web-style rel_path_web = rel_path.replace("\\", "/") file_hash = get_md5(filepath) if file_hash: manifest[rel_path_web] = file_hash try: with open(manifest_path, "w", encoding="utf-8") as f: json.dump(manifest, f, indent=4) print(f"Done! Manifest saved to: {manifest_path}") print(f"Total files in manifest: {len(manifest)}") except IOError as e: print(f"Error saving manifest: {e}") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Universal manifest generator for game updaters.") parser.add_argument("client_path", help="Path to the client files folder") args = parser.parse_args() generate(args.client_path)