137 lines
3.5 KiB
Python
Executable file
137 lines
3.5 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""
|
|
Maya License Patcher - Python Implementation
|
|
Properly patches Maya licensing libraries with version-aware binary patches
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import json
|
|
import struct
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
def log(msg):
|
|
print(f"[PATCHER] {msg}")
|
|
|
|
def hex_to_bytes(hex_str):
|
|
"""Convert hex string to bytes"""
|
|
return bytes.fromhex(hex_str.replace(':', '').replace(' ', ''))
|
|
|
|
def find_signature(buffer, signature):
|
|
"""Find signature in buffer, return offset or -1"""
|
|
sig = hex_to_bytes(signature)
|
|
for i in range(len(buffer) - len(sig) + 1):
|
|
if buffer[i:i+len(sig)] == sig:
|
|
return i
|
|
return -1
|
|
|
|
def apply_patch(buffer, patch):
|
|
"""Apply a single patch to buffer, return True if applied"""
|
|
sig = patch.get('signature', '')
|
|
repl = patch.get('replacement', '')
|
|
desc = patch.get('description', 'Unknown')
|
|
|
|
if not sig or not repl:
|
|
log(f" Skipping patch with missing data: {desc}")
|
|
return False
|
|
|
|
sig_bytes = hex_to_bytes(sig)
|
|
repl_bytes = hex_to_bytes(repl)
|
|
|
|
offset = find_signature(buffer, sig)
|
|
if offset == -1:
|
|
log(f" NOT FOUND: {desc}")
|
|
return False
|
|
|
|
# Apply patch
|
|
buffer[offset:offset+len(repl_bytes)] = repl_bytes
|
|
log(f" APPLIED: {desc} at offset 0x{offset:x}")
|
|
return True
|
|
|
|
def patch_library(lib_path, patches):
|
|
"""Patch a library file with given patches"""
|
|
log(f"Patching: {lib_path}")
|
|
|
|
# Read library
|
|
with open(lib_path, 'rb') as f:
|
|
buffer = bytearray(f.read())
|
|
|
|
log(f" File size: {len(buffer):,} bytes")
|
|
|
|
# Create backup
|
|
backup_path = f"{lib_path}.backup"
|
|
shutil.copy2(lib_path, backup_path)
|
|
log(f" Backup created: {backup_path}")
|
|
|
|
# Apply patches
|
|
applied = 0
|
|
not_found = 0
|
|
|
|
for patch in patches:
|
|
if apply_patch(buffer, patch):
|
|
applied += 1
|
|
else:
|
|
not_found += 1
|
|
|
|
# Write patched library
|
|
with open(lib_path, 'wb') as f:
|
|
f.write(buffer)
|
|
|
|
log(f" Patches applied: {applied}, not found: {not_found}")
|
|
return applied, not_found
|
|
|
|
def load_patch_db(db_path):
|
|
"""Load patch database from JSON"""
|
|
with open(db_path, 'r') as f:
|
|
return json.load(f)
|
|
|
|
def main():
|
|
if len(sys.argv) < 3:
|
|
print("Usage: apply_patches.py <version> <patch_db.json>")
|
|
sys.exit(1)
|
|
|
|
version = sys.argv[1]
|
|
patch_db_path = sys.argv[2]
|
|
|
|
log("=== Maya License Patcher ===")
|
|
log(f"Target version: {version}")
|
|
|
|
# Load patch database
|
|
db = load_patch_db(patch_db_path)
|
|
|
|
if version not in db.get('versions', {}):
|
|
log(f"ERROR: Version {version} not found in patch database")
|
|
sys.exit(1)
|
|
|
|
version_data = db['versions'][version]
|
|
lib_path = version_data.get('library')
|
|
patches = version_data.get('patches', [])
|
|
|
|
if not lib_path:
|
|
log("ERROR: No library path specified")
|
|
sys.exit(1)
|
|
|
|
if not os.path.exists(lib_path):
|
|
log(f"ERROR: Library not found: {lib_path}")
|
|
sys.exit(1)
|
|
|
|
log(f"Library: {lib_path}")
|
|
log(f"Patches to apply: {len(patches)}")
|
|
log("")
|
|
|
|
# Apply patches
|
|
applied, not_found = patch_library(lib_path, patches)
|
|
|
|
log("")
|
|
log("=== Patching Complete ===")
|
|
|
|
if applied > 0:
|
|
log(f"Successfully applied {applied} patches")
|
|
sys.exit(0)
|
|
else:
|
|
log("No patches were applied!")
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|