Skip to content

Instantly share code, notes, and snippets.

@error454
Created August 6, 2025 06:06
Show Gist options
  • Save error454/bacf9f5a1f8af700262b349567c32bbb to your computer and use it in GitHub Desktop.
Save error454/bacf9f5a1f8af700262b349567c32bbb to your computer and use it in GitHub Desktop.
Vibe coded logic session parser to print out a list of 3rd party and native plugins in the session
#!/usr/bin/env python3
"""
parseLogicSession.py
------------------
1. Extract Logic Pro version/build from Resources/ProjectInformation.plist
2. Extract 3rd party and native plug-in names from Alternatives/000/ProjectData
Based on a pretty limited understanding and reverse engineering of a single logic session file.
Usage:
python parseLogicSession.py /path/to/LogicPro.app
python parseLogicSession.py /path/to/MySong.logicx
"""
from __future__ import annotations
import argparse
import re
import sys
from pathlib import Path
# ---------- VERSION / BUILD (ProjectInformation.plist) ------------------------
_PLIST_PATTERN = re.compile(rb"Logic Pro ([^)]*\))") # captures "11.1.2 (6162)"
def extract_version_build(root: Path) -> tuple[str | None, str | None]:
"""Return (version, build) or (None, None) if not found."""
plist = root / "Resources" / "ProjectInformation.plist"
if not plist.is_file():
return None, None
data = plist.read_bytes()
m = _PLIST_PATTERN.search(data)
if not m:
return None, None
text = m.group(1).decode("ascii", errors="ignore")
m2 = re.fullmatch(r"([\d.]+)\s*\(([^)]+)\)", text)
return (m2.group(1), m2.group(2)) if m2 else (None, None)
# ---------- PLUG-IN LIST (ProjectData) ----------------------------------------
_MARKER = b"46ia"
_OFFSET = 0x2e # bytes before marker
def extract_3rd_party_plugins(root: Path) -> list[str]:
"""Return sorted, deduplicated list of plug-in names found."""
pdata = root / "Alternatives" / "000" / "ProjectData"
if not pdata.is_file():
return []
blob = pdata.read_bytes()
size = len(blob)
index = 0
names = set()
# 3rd party plugins
while True:
idx = blob.find(_MARKER, index)
if idx == -1:
break
pos = idx - _OFFSET
end = blob.find(0, pos)
if end == -1:
break
raw = blob[pos:end].decode("utf-8", errors="ignore").strip()
if raw:
names.add(raw)
index = idx + 1 # continue searching
return sorted(names, key=str.lower)
_MARKERNATIVE = b"default.pst"
_OFFSETNATIVE = 0x5e # bytes before marker
def extract_native_plugins(root: Path) -> list[str]:
"""Return sorted, deduplicated list of native plug-in names found."""
pdata = root / "Alternatives" / "000" / "ProjectData"
if not pdata.is_file():
return []
blob = pdata.read_bytes()
size = len(blob)
index = 0
names = set()
# 3rd party plugins
while True:
idx = blob.find(_MARKERNATIVE, index)
if idx == -1:
break
pos = idx + len(_MARKERNATIVE) + _OFFSETNATIVE
end = blob.find(0, pos)
if end == -1:
break
raw = blob[pos:end].decode("utf-8", errors="ignore").strip()
if raw:
names.add(raw)
index = idx + 1 # continue searching
return sorted(names, key=str.lower)
# ---------- MAIN ----------------------------------------------------------------
def main() -> None:
parser = argparse.ArgumentParser(
description="Print Logic Pro version/build and plug-in list."
)
parser.add_argument("folder", help="Path to .app bundle or .logicx project")
args = parser.parse_args()
root = Path(args.folder).expanduser()
if not root.exists():
sys.exit(f"Error: {root} does not exist.")
# Version / build
version, build = extract_version_build(root)
if version:
print(f"Logic Pro version : {version}")
print(f"Build number : {build}\n")
else:
print("• ProjectInformation.plist not found (skipping version/build).\n")
# Plug-ins
plugins = extract_3rd_party_plugins(root)
if plugins:
print("3rd Party Plug-ins:")
for name in plugins:
print(f" {name}")
else:
print("• No 3rd party plug-in names found (check ProjectData heuristic or file path).")
plugins = extract_native_plugins(root)
if plugins:
print("Native Plug-ins:")
for name in plugins:
print(f" {name}")
else:
print("• No plug-in names found (check ProjectData heuristic or file path).")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment