Last active
August 4, 2026 11:21
-
-
Save denji/a311545c36a32b6cd5c6075f79743c9a to your computer and use it in GitHub Desktop.
vmx_sort.py — Diff-optimized VMware .vmx parser and formatter.
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
| #!/usr/bin/env python3 | |
| """ | |
| vmx_sort.py — Diff-optimized VMware .vmx parser and formatter. | |
| """ | |
| import re | |
| import sys | |
| from collections import OrderedDict | |
| LINE_RE = re.compile(r'^\s*([^\s=]+)\s*=\s*(.*?)\s*$') | |
| PINNED_FIRST = [".encoding", "config.version", "virtualHW.version"] | |
| # Full descriptive group names ordered by volatility (Static -> Volatile) | |
| GROUP_RULES = [ | |
| # 1. Identity & Core Specs (Static) | |
| ("Identity & Guest OS", | |
| r'^(displayName|annotation|guestOS.*|virtualHW\.productCompatibility)$'), | |
| ("CPU Configuration & Nested Virt", | |
| r'^(numvcpus|cpuid\..*|vcpu\..*|vhv\..*|vvtd\..*|vmx\.allowNested|featMask\..*' | |
| r'|paevm|vpmc\.enable|mce\.enable|vhu\.enable|ulm\.disableMitigations)$'), | |
| ("CPU Scheduling & Priority", | |
| r'^(Priority\..*|sched\.cpu\..*)$'), | |
| ("NUMA Topology", | |
| r'^numa\..*$'), | |
| ("Memory Configuration & Scheduling", | |
| r'^(memSize|memory\..*|mem\..*|mainMem\..*|MemTrimRate|MemAllowAutoScaleDown' | |
| r'|sched\.mem\..*|prefvmx\..*|pciHole\..*)$'), | |
| # 2. Controllers & Hardware Topology (Static) | |
| ("Storage Controllers & Drives", | |
| r'^(scsi\d+|sata\d+|nvme\d+|ide\d+|floppy\d+)(:\d+)?\..*$'), | |
| ("Network Adapters", | |
| r'^(ethernet\d+|vmxnet\d*)\..*$'), | |
| ("USB Controllers & Devices", | |
| r'^(usb.*|ehci\..*|xhci\..*)$'), | |
| ("BIOS / EFI / Firmware", | |
| r'^(firmware|bios\..*|bios440\..*|efi\..*|uefi\..*|nvram|acpi\..*)$'), | |
| ("Chipset & PCI", | |
| r'^(chipset\..*|pciBridge\d*\..*|pciPassthru\d*\..*|pci\.allowPassthrough|hpet0\..*)$'), | |
| ("Virtual TPM (vTPM)", | |
| r'^(vtpm\..*|managedVM\.autoAddVTPM)$'), | |
| # 3. Peripherals & Guest Services (Semi-Static) | |
| ("Input Devices & Sensors", | |
| r'^(mouse\..*|vmmouse\..*|keyboard\..*|touchpad\..*|mks\.gamingMouse\..*' | |
| r'|mks\.useDirectInput|mks\.disableTypematic|mks\.disableRemoteClientTypematic' | |
| r'|mks\.keyboardFilter|sensor\..*)$'), | |
| ("Display & Graphics (MKS)", | |
| r'^(svga\..*|mks\..*|gui\..*|pref\..*|gfx\..*)$'), | |
| ("Sound", | |
| r'^sound\..*$'), | |
| ("Serial & Parallel Ports", | |
| r'^(serial|parallel)\d+\..*$'), | |
| ("Shared Folders (HGFS)", | |
| r'^(hgfs\..*|sharedFolder\d*\..*)$'), | |
| ("VMware Tools", | |
| r'^(tools\..*|toolscripts\..*|installerDefaults\..*)$'), | |
| # 4. Security, Stealth & Advanced | |
| ("Anti-Detection / VM Stealth", | |
| r'^(hypervisor\.cpuid\..*|smbios\..*|hw\.model.*|board-id.*|serialNumber.*' | |
| r'|monitor_control\.disable_mmu_largepages|vmGenCounter\.enable)$'), | |
| ("Security & Policy", | |
| r'^(isolation\..*|policy\..*|Guest\.Command\.Enabled)$'), | |
| ("VM Encryption", | |
| r'^(migrate\.encryptionMode|ftcpt\.ftEncryptionMode|encryption\..*|cryptoState)$'), | |
| # 5. Advanced Tuning, Debugging & Logging | |
| ("Storage I/O Tuning", | |
| r'^(aiomgr\..*|cbtmotion\..*)$'), | |
| ("Time Synchronization", | |
| r'^(tools\.syncTime|time\.synchronize\..*|host\.cpukHz|host\.noTSC|ptsc\.noTSC)$'), | |
| ("Power Management", | |
| r'^(powerType\..*|suspend\.disabled)$'), | |
| ("VMCI Interface", | |
| r'^vmci\d*\..*$'), | |
| ("Remote Display (VNC) & Unity", | |
| r'^(RemoteDisplay\..*|unity\..*)$'), | |
| ("Logging & Debugging", | |
| r'^(log\..*|vmx\.buildType|vmx\.scoreboard\..*|vmxstats\..*|debug\..*' | |
| r'|monitor_control\.log.*)$'), | |
| ("Snapshot & File References", | |
| r'^(snapshot\..*|.*\.fileName)$'), | |
| # 6. Hypervisor State (Volatile — Bottom) | |
| ("Runtime & VM State (VMware-managed)", | |
| r'^(cleanShutdown|checkpoint\..*|uuid\..*|vc\.uuid|migration\..*|vmotion\..*|replay\..*' | |
| r'|softPowerOff|monitor\.phys_bits_used|toolsInstallManager\..*' | |
| r'|guestinfo\.detailed\..*|extendedConfigFile|sched\.swap\..*' | |
| r'|vm\.genid.*|vm\.lastPowerRequestTimestamp)$'), | |
| ] | |
| ENCRYPTION_KEYS = {"encryption.data", "encryption.keysafe", "encryption.bundle"} | |
| def is_encrypted_vmx(entries): | |
| parsed_lower_keys = {k.lower() for k in entries.keys()} | |
| return bool(parsed_lower_keys & ENCRYPTION_KEYS) | |
| def natural_key(text): | |
| chunks = re.split(r'(\d+)', text) | |
| key = [] | |
| for c in chunks: | |
| if c == "": | |
| continue | |
| if c.isdigit(): | |
| key.append((1, int(c))) | |
| else: | |
| key.append((0, c.lower())) | |
| return key | |
| def parse_vmx(lines): | |
| entries = OrderedDict() | |
| lower_to_orig = {} | |
| for raw in lines: | |
| stripped = raw.strip() | |
| if not stripped or stripped.startswith("#") or stripped.startswith("!"): | |
| continue | |
| m = LINE_RE.match(raw.rstrip("\n")) | |
| if not m: | |
| continue | |
| key, value = m.group(1), m.group(2) | |
| lower_key = key.lower() | |
| if lower_key in lower_to_orig: | |
| del entries[lower_to_orig[lower_key]] | |
| lower_to_orig[lower_key] = key | |
| entries[key] = value | |
| return entries | |
| def group_for(key): | |
| for name, pattern in GROUP_RULES: | |
| if re.match(pattern, key, re.IGNORECASE): | |
| return name | |
| return "Miscellaneous" | |
| def build_sorted_vmx(entries): | |
| out_lines = [] | |
| # 1. Pinned header lines | |
| lower_entries = {k.lower(): k for k in entries.keys()} | |
| for pinned in PINNED_FIRST: | |
| orig_key = lower_entries.get(pinned.lower()) | |
| if orig_key: | |
| out_lines.append(f'{orig_key} = {entries[orig_key]}') | |
| if out_lines: | |
| out_lines.append("") | |
| # 2. Bucket entries | |
| pinned_lower = {p.lower() for p in PINNED_FIRST} | |
| buckets = OrderedDict((name, []) for name, _ in GROUP_RULES) | |
| buckets["Miscellaneous"] = [] | |
| for key, value in entries.items(): | |
| if key.lower() in pinned_lower: | |
| continue | |
| buckets[group_for(key)].append((key, value)) | |
| # 3. Format output with original style headers: # --- Full Name --------------------------- | |
| for group_name, items in buckets.items(): | |
| if not items: | |
| continue | |
| items.sort(key=lambda kv: natural_key(kv[0])) | |
| header_text = f"# --- {group_name} " | |
| header_line = header_text + "-" * max(0, 47 - len(header_text)) | |
| out_lines.append(header_line) | |
| for key, value in items: | |
| out_lines.append(f"{key} = {value}") | |
| out_lines.append("") | |
| return "\n".join(out_lines).rstrip() + "\n" | |
| def main(): | |
| if len(sys.argv) < 2: | |
| sys.exit("Usage: python3 vmx_sort.py input.vmx [output.vmx]") | |
| in_path = sys.argv[1] | |
| out_path = sys.argv[2] if len(sys.argv) > 2 else None | |
| with open(in_path, "r", encoding="utf-8", errors="replace") as f: | |
| lines = f.readlines() | |
| entries = parse_vmx(lines) | |
| if is_encrypted_vmx(entries): | |
| sys.exit(f"Error: '{in_path}' contains encrypted configuration data. Aborting.") | |
| result = build_sorted_vmx(entries) | |
| if out_path: | |
| with open(out_path, "w", encoding="utf-8") as f: | |
| f.write(result) | |
| else: | |
| sys.stdout.write(result) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment