Just put this python file in a dedicated folder and just run it. It'll save the file location off DDM to a file called ddm.txt so you won't have to specify it the next time.
Last active
July 5, 2026 12:04
-
-
Save jericjan/840f66c05b7d0c03a91679cc797f1017 to your computer and use it in GitHub Desktop.
manilua-to-ddm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import re | |
| import subprocess | |
| import tempfile | |
| from pathlib import Path | |
| from zipfile import ZipFile | |
| class KeyExtractor: | |
| """handles parsing of decryptions keys from lua files""" | |
| def __init__(self, pattern: str): | |
| self.pattern = re.compile(pattern, re.MULTILINE) | |
| def extract_keys(self, content: str) -> list[tuple[str, str]]: | |
| return self.pattern.findall(content) | |
| def write_keys_file(self, keys: list[tuple[str, str]], output_path: Path) -> None: | |
| lines = [f"{depot};{key}\n" for depot, key in keys] | |
| output_path.write_text("".join(lines), encoding="utf-8") | |
| class DepotDownloaderMod: | |
| def __init__(self, exe_path: Path, keys_path: Path): | |
| self.exe_path = exe_path | |
| self.keys_path = keys_path | |
| def run(self, manifest_path: Path, depot_id: str, app_id: str, target_dir: Path) -> None: | |
| cmd = [ | |
| str(self.exe_path), | |
| "-app", | |
| app_id, | |
| "-depot", | |
| depot_id, | |
| "-depotkeys", | |
| str(self.keys_path), | |
| "-manifestfile", | |
| str(manifest_path), | |
| "-dir", | |
| str(target_dir) | |
| ] | |
| subprocess.run(cmd, check=True) | |
| def process_zip(zip_path: Path, exe_path: Path) -> None: | |
| lua_pattern = ( | |
| r"^\s*addappid\s*\(\s*(\d+)\s*,\s*\d\s*,\s*(?:\"|')\s*(\S+)\s*(?:\"|')" | |
| ) | |
| extractor = KeyExtractor(lua_pattern) | |
| with tempfile.TemporaryDirectory() as temp_dir_str: | |
| temp_dir = Path(temp_dir_str) | |
| manifest_files: list[Path] = [] | |
| lua_file = None | |
| with ZipFile(zip_path, "r") as archive: | |
| for file_info in archive.infolist(): | |
| suffix = Path(file_info.filename).suffix.lower() | |
| if suffix in (".manifest", ".lua"): | |
| extracted_path = Path(archive.extract(file_info, temp_dir)) | |
| if suffix == ".manifest": | |
| manifest_files.append(extracted_path) | |
| elif suffix == ".lua": | |
| lua_file = extracted_path | |
| if not lua_file: | |
| raise FileNotFoundError("no .lua file found inside the zip archive") | |
| keys_path = temp_dir / "depot.keys" | |
| keys = extractor.extract_keys(lua_file.read_text(encoding="utf-8")) | |
| extractor.write_keys_file(keys, keys_path) | |
| execution_queue: list[tuple[Path, str, str]] = [] | |
| for manifest in manifest_files: | |
| match = re.search(r"\d+", manifest.name) | |
| if not match: | |
| raise ValueError(f"invalid manifest filename: {manifest.name}") | |
| depot_id = match.group(0) | |
| guessed_id = f"{depot_id[:-1]}0" | |
| print(f"\n[Manifest File]: {manifest.name}") | |
| print(f"Predicted App ID: {guessed_id}") | |
| user_val = input("Press ENTER to confirm, or type the correct App ID: ").strip() | |
| app_id = user_val if user_val else guessed_id | |
| execution_queue.append((manifest, depot_id, app_id)) | |
| downloader = DepotDownloaderMod(exe_path, keys_path) | |
| for manifest, depot_id, app_id in execution_queue: | |
| downloader.run(manifest, depot_id, app_id, Path(zip_path.stem)) | |
| def main() -> None: | |
| ddm_path_file = Path('ddm.txt') | |
| ddm_path_file.touch(exist_ok=True) | |
| with ddm_path_file.open(encoding="utf-8") as f: | |
| ddm_path = f.read() | |
| if ddm_path: | |
| exe_input = ddm_path | |
| else: | |
| exe_input = input("Enter absolute path to DepotDownloaderMod.exe: ").strip("'\"") | |
| exe_path = Path(exe_input).resolve() | |
| if not exe_path.is_file(): | |
| print("Error: Executable file not found.") | |
| return | |
| if not ddm_path: | |
| with ddm_path_file.open("w", encoding="utf-8") as f: | |
| f.write(str(exe_path)) | |
| zip_input = input("Enter absolute path to the ZIP file: ").strip("'\"") | |
| zip_path = Path(zip_input).resolve() | |
| if not zip_path.is_file(): | |
| print("Error: ZIP file not found.") | |
| return | |
| try: | |
| process_zip(zip_path, exe_path) | |
| print("\nProcessing completed successfully.") | |
| except Exception as e: | |
| print(f"An error occurred: {e}") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment