c74/M4L/.amxd/How To/Decompile .amxd into json
I ended up having ChatGPT develop a Python/Script to do this (note, I had it use its code interpreter to run it; I haven't tried it locally yet)
- python
import json import re import sys def extract_json_from_amxd(amxd_file_path, output_json_path): """ Extracts the JSON content from a Max for Live (.amxd) file by removing its binary header. Args: amxd_file_path (str): Path to the .amxd file. output_json_path (str): Path to save the extracted JSON file. Returns: bool: True if extraction and saving were successful, False otherwise. """ try: # Read the .amxd file as binary with open(amxd_file_path, "rb") as f: data = f.read().decode("utf-8", errors="ignore") # Decode, ignoring errors # Find the start of JSON (first '{') json_start = data.find("{") if json_start == -1: print("Error: No JSON-like structure found in the .amxd file.") return False # Extract only the printable part after the first '{' json_data_clean = re.sub(r'[^\x20-\x7E\n\t]', '', data[json_start:]) # Remove non-printable characters # Attempt to parse JSON try: parsed_json = json.loads(json_data_clean) except json.JSONDecodeError: print("Error: Extracted content is not valid JSON.") return False # Save extracted JSON with open(output_json_path, "w", encoding="utf-8") as out_f: json.dump(parsed_json, out_f, indent=4) print(f"Successfully extracted JSON saved to: {output_json_path}") return True except Exception as e: print(f"Unexpected error: {e}") return False if __name__ == "__main__": if len(sys.argv) < 3: print("Usage: python extract_amxd_json.py <input.amxd> <output.json>") sys.exit(1) input_amxd = sys.argv[1] output_json = sys.argv[2] success = extract_json_from_amxd(input_amxd, output_json) if not success: sys.exit(1) # Exit with error code if extraction fails
And just for good measure, a Typescript version as well - it says one can run it with
ts-node extractAmxdJson.ts your_file.amxd output.json. This uses ts-node- typescript
import * as fs from 'fs'; function extractJsonFromAmxd(amxdFilePath: string, outputJsonPath: string): boolean { try { // Read the .amxd file as a buffer const data = fs.readFileSync(amxdFilePath); let textData = data.toString('utf-8'); // Find the start of JSON (first '{') const jsonStart = textData.indexOf('{'); if (jsonStart === -1) { console.error("Error: No JSON-like structure found in the .amxd file."); return false; } // Extract only the printable part after the first '{' const jsonDataClean = textData.substring(jsonStart).replace(/[^\x20-\x7E\n\t]/g, ''); // Attempt to parse JSON let parsedJson; try { parsedJson = JSON.parse(jsonDataClean); } catch (error) { console.error("Error: Extracted content is not valid JSON."); return false; } // Save extracted JSON fs.writeFileSync(outputJsonPath, JSON.stringify(parsedJson, null, 4), 'utf-8'); console.log(`Successfully extracted JSON saved to: ${outputJsonPath}`); return true; } catch (error) { console.error(`Unexpected error: ${error}`); return false; } } if (process.argv.length < 4) { console.log("Usage: ts-node extractAmxdJson.ts <input.amxd> <output.json>"); process.exit(1); } const inputAmxd = process.argv[2]; const outputJson = process.argv[3]; const success = extractJsonFromAmxd(inputAmxd, outputJson); if (!success) { process.exit(1); }
Here's a sample json of the maxmsp-test.amxd patcher as json:
- json
{ "patcher": { "fileversion": 1, "appversion": { "major": 8, "minor": 5, "revision": 8, "architecture": "x64", "modernui": 1 }, "classnamespace": "box", "rect": [ 65.0, 399.0, 385.0, 229.0 ], "openrect": [ 0.0, 0.0, 0.0, 169.0 ], "bglocked": 0, "openinpresentation": 0, "default_fontsize": 10.0, "default_fontface": 0, "default_fontname": "Arial Bold", "gridonopen": 1, "gridsize": [ 8.0, 8.0 ], "gridsnaponopen": 1, "objectsnaponopen": 1, "statusbarvisible": 2, "toolbarvisible": 1, "lefttoolbarpinned": 0, "toptoolbarpinned": 0, "righttoolbarpinned": 0, "bottomtoolbarpinned": 0, "toolbars_unpinned_last_save": 0, "tallnewobj": 0, "boxanimatetime": 500, "enablehscroll": 1, "enablevscroll": 1, "devicewidth": 0.0, "description": "", "digest": "", "tags": "", "style": "", "subpatcher_template": "", "assistshowspatchername": 0, "title": "Max MIDI Effect", "boxes": [ { "box": { "id": "obj-5", "maxclass": "live.comment", "numinlets": 1, "numoutlets": 0, "patching_rect": [ 42.0, 136.0, 58.0, 18.0 ], "text": "MIDI to Live", "textjustification": 0 } }, { "box": { "id": "obj-4", "maxclass": "live.comment", "numinlets": 1, "numoutlets": 0, "patching_rect": [ 33.0, 16.0, 69.0, 18.0 ], "text": "MIDI from Live", "textjustification": 0 } }, { "box": { "id": "obj-3", "maxclass": "live.comment", "numinlets": 1, "numoutlets": 0, "patching_rect": [ 63.0, 75.0, 122.0, 18.0 ], "text": "Build your MIDI effect here", "textjustification": 0 } }, { "box": { "fontname": "Ableton Sans Medium Regular", "fontsize": 11.0, "hidden": 1, "id": "obj-9", "maxclass": "comment", "numinlets": 1, "numoutlets": 0, "patching_rect": [ 0.0, 170.0, 133.0, 20.0 ], "text": "Device vertical limit" } }, { "box": { "fontname": "Arial Bold", "fontsize": 10.0, "id": "obj-2", "maxclass": "newobj", "numinlets": 1, "numoutlets": 0, "patching_rect": [ 48.0, 114.0, 47.0, 20.0 ], "text": "midiout" } }, { "box": { "fontname": "Arial Bold", "fontsize": 10.0, "id": "obj-1", "maxclass": "newobj", "numinlets": 1, "numoutlets": 1, "outlettype": [ "int" ], "patching_rect": [ 48.0, 34.0, 40.0, 20.0 ], "text": "midiin" } } ], "lines": [ { "patchline": { "destination": [ "obj-2", 0 ], "source": [ "obj-1", 0 ] } } ], "dependency_cache": [], "latency": 0, "is_mpe": 0, "minimum_live_version": "", "minimum_max_version": "", "platform_compatibility": 0, "project": { "version": 1, "creationdate": 3590052786, "modificationdate": 3590052786, "viewrect": [ 0.0, 0.0, 300.0, 500.0 ], "autoorganize": 1, "hideprojectwindow": 1, "showdependencies": 1, "autolocalize": 0, "contents": { "patchers": {} }, "layout": {}, "searchpath": {}, "detailsvisible": 0, "amxdtype": 1835887981, "readonly": 0, "devpathtype": 0, "devpath": ".", "sortmode": 0, "viewmode": 0, "includepackages": 0 }, "autosave": 0, "saved_attribute_attributes": { "default_plcolor": { "expression": "" } } } }
- This is reminiscent of both GitHub/aptrn/maxmsp-ts-library-template/apps/maxmsp-test/maxmsp-test.maxproj and GitHub/aptrn/maxmsp-ts-library-template/apps/maxmsp-test/Patchers/Test.maxpat, but not identical to it. In particular, there is no section "code" that contains dependencies.