Skip to content

Commit e8c7d64

Browse files
committed
Add ASCII art and make the instructions more user-friendly.
1 parent 623be46 commit e8c7d64

File tree

3 files changed

+191
-69
lines changed

3 files changed

+191
-69
lines changed

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# abletonCracker - What is this?
22

3-
This is an open-source implementation of the R2R Patch and `R2RLIVE.dll` of Ableton Live, written in Python 3.
3+
This is an open-source implementation of the R2R patch and `R2RLIVE.dll` of Ableton Live, written in Python.
44

55
Like `R2RLIVE.dll`, this script uses Team R2R's signing key only.
66

@@ -22,19 +22,24 @@ You can download the Ableton Installers directly from Ableton's servers. I made
2222

2323
# How to use
2424

25-
1. Install Python (3.12 upwards) at [python.org](https://www.python.org/downloads/)
25+
1. Install Python (3.10 upwards) at [python.org](https://www.python.org/downloads/)
2626
2. Open your Terminal and run `python -m pip install cryptography` to install dependencies
2727
3. Find your Ableton HWID, open Ableton, and press "Authorize Ableton offline". You will find your HWID.
2828
4. Open `config.json` and change the variables to fit your Ableton Live installation.
29-
5. Open your Ableton installation folder (for example: `C:\ProgramData\Ableton\Live 11 Suite\Program`)
30-
6. Copy the Ableton executable (.exe file) (for example: `Ableton Live 11 Suite.exe`) to the same folder where you downloaded abletonCracker.
31-
7. Run `patch_ableton.py`, your Ableton should be patched and the `Authorize.auz` file should generate.
32-
8. Copy your Ableton executable back to your installation folder
33-
9. Run Ableton, drag the `Authorize.auz` file into the Activation window
29+
5. Run `patch_ableton.py`, your Ableton should be patched and the `Authorize.auz` file should generate.
30+
6. Run Ableton, drag the `Authorize.auz` file into the Activation window
3431

35-
Hooray, you're done!
32+
#### Hooray, you're done!
3633

37-
If there are any permission erros, its recommended to move the Ableton.exe into the same folder where `patch_ableton.py` is located.
34+
# Troubleshooting
35+
#### I don't have administrator on my PC.
36+
1. Copy your Ableton executable to the same folder where patch_ableton.py is located.
37+
2. In config.json, change your file path from "auto" to the new file path of your Ableton exe.
38+
3. Retry
39+
4. It should work now. Then copy your Ableton exe back to the folder you got it from.
40+
41+
# Support
42+
I do offer support on Discord (@devilAPI) and on Reddit (@devilAPIOnReddit)
3843

3944
# Credits
4045

patch_ableton.py

Lines changed: 57 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,24 @@
1010
from cryptography.hazmat.primitives.asymmetric import dsa
1111
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
1212
from cryptography.hazmat.primitives.hashes import SHA1
13+
# Color support
14+
try:
15+
from colorama import init, Fore, Style
16+
init(autoreset=True)
17+
except ImportError:
18+
class Dummy:
19+
RESET = RED = WHITE = GREEN = LIGHTBLACK_EX = BRIGHT = ''
20+
Fore = Style = Dummy()
1321

1422
def is_admin():
15-
"""Check if the script is running with administrator privileges"""
1623
try:
1724
return ctypes.windll.shell32.IsUserAnAdmin() != 0
1825
except:
1926
return False
2027

2128
def run_as_admin():
22-
"""Relaunch the script with administrator privileges"""
2329
script = os.path.abspath(sys.argv[0])
2430
params = subprocess.list2cmdline(sys.argv[1:])
25-
26-
# Request UAC elevation
2731
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, f'"{script}" {params}', None, 1)
2832
sys.exit(0)
2933

@@ -32,7 +36,6 @@ def load_config(filename: str):
3236
with open(filename, 'r') as f:
3337
data = json.load(f)
3438

35-
# Extract values from JSON
3639
file_path = data.get("file_path")
3740
old_signkey = data.get("old_signkey")
3841
new_signkey = data.get("new_signkey")
@@ -42,7 +45,6 @@ def load_config(filename: str):
4245
authorize_file_output = data.get('authorize_file_output', 'Authorize.auz')
4346
dsa_params = data.get('dsa_parameters')
4447

45-
# Validate essential keys
4648
if not file_path or not old_signkey or not new_signkey:
4749
raise ValueError("JSON file must contain 'file_path', 'old_signkey', and 'new_signkey'.")
4850
if len(hwid) == 24:
@@ -107,7 +109,6 @@ def replace_signkey_in_file(file_path, old_signkey, new_signkey):
107109
with open(file_path, 'wb') as file:
108110
file.write(content)
109111

110-
# Verify replacement
111112
if old_signkey_bytes in content:
112113
print("Error: The old signkey is still present in the file.")
113114
else:
@@ -187,22 +188,20 @@ def generate_all(k: dsa.DSAPrivateKey, edition: str, version: int, hwid: str) ->
187188
for i in range(0x8000, 0x80ff + 1):
188189
yield generate_single(k, i, 0x10, hwid)
189190

190-
# Mapping for the editions
191191
EDITIONS = {
192192
"Lite": 4,
193193
"Intro": 3,
194194
"Standard": 0,
195195
"Suite": 2,
196196
}
197197

198-
# Installation detection functions
199198
def get_user_config_dir():
200199
system = platform.system()
201200
if system == "Windows":
202201
return os.getenv('APPDATA')
203202
elif system == "Darwin":
204203
return os.path.join(os.path.expanduser("~"), "Library", "Application Support")
205-
else: # Linux and others
204+
else:
206205
return os.getenv('XDG_CONFIG_HOME', os.path.join(os.path.expanduser("~"), ".config"))
207206

208207
def find_installations():
@@ -251,113 +250,120 @@ def find_installation_data():
251250
for entry in os.listdir(base_dir):
252251
entry_path = os.path.join(base_dir, entry)
253252
if os.path.isdir(entry_path) and "Live" in entry:
254-
# Check if Unlock directory exists or can be created
255253
data_dirs.append((entry_path, entry))
256254

257255
return data_dirs
258256

259257
def main():
260-
# Request admin on Windows if needed
258+
# Colors
259+
RED = Fore.RED + Style.BRIGHT
260+
WHITE = Fore.WHITE + Style.BRIGHT
261+
GREY = Fore.LIGHTBLACK_EX + Style.NORMAL
262+
LIME = Fore.GREEN + Style.BRIGHT
263+
RESET = Style.RESET_ALL
264+
261265
if platform.system() == "Windows" and not is_admin():
262-
print("\nThis operation requires administrator privileges on Windows.")
263-
print("Relaunching with admin rights...")
266+
print(RED + "\nThis operation requires administrator privileges on Windows." + RESET)
267+
print(GREY + "Relaunching with admin rights..." + RESET)
264268
run_as_admin()
265269
return
270+
# ASCII art in bright red
271+
print(RED + r"""
272+
___. .__ __ _________ __
273+
_____ \_ |__ | | _____/ |_ ____ ____ \_ ___ \____________ ____ | | __ ___________
274+
\__ \ | __ \| | _/ __ \ __\/ _ \ / \/ \ \/_ __ \__ \ _/ ___\| |/ // __ \_ __ \
275+
/ __ \| \_\ \ |_\ ___/| | ( <_> ) | \ \____| | \// __ \\ \___| <\ ___/| | \/
276+
(____ /___ /____/\___ >__| \____/|___| /\______ /|__| (____ /\___ >__|_ \\___ >__|
277+
\/ \/ \/ \/ \/ \/ \/ \/ \/
278+
""" + RESET)
279+
print(LIME + "Made by devilAPI" + RESET)
280+
print(GREY + "GitHub: " + LIME + "https://github.com/devilAPI/abletonCracker/" + RESET + "\n")
266281

267-
print("Ableton Live Patcher - Running with administrative privileges\n")
268-
269-
# Load configuration
270282
config_file = 'config.json'
271283
try:
272284
file_path, old_signkey, new_signkey, hwid, edition, version, authorize_file_output, dsa_params = load_config(config_file)
273285
except Exception as e:
274-
print(f"Error loading configuration: {e}")
275-
input("Press Enter to exit...")
286+
print(RED + f"Error loading configuration: {e}" + RESET)
287+
input(GREY + "Press Enter to exit..." + RESET)
276288
return
277289

278-
# Auto-detect installations if needed
279290
if file_path.lower() == "auto":
280291
installations = find_installations()
281292
if not installations:
282-
print("\nNo Ableton Live installations found. Please specify the path manually.")
283-
input("Press Enter to exit...")
293+
print(RED + "\nNo Ableton Live installations found. Please specify the path manually." + RESET)
294+
input(GREY + "Press Enter to exit..." + RESET)
284295
return
285296

286-
print("\nFound Ableton installations:")
297+
print(LIME + "\nFound Ableton installations:" + RESET)
287298
for i, (path, name) in enumerate(installations):
288-
print(f"{i+1}. {name} at {path}")
299+
print(WHITE + f"{i+1}. " + LIME + f"{name}" + GREY + f" at {path}" + RESET)
289300

290301
try:
291-
selection = int(input("\nSelect installation to patch: ")) - 1
302+
selection = int(input(LIME + "\nSelect installation to patch: " + RESET)) - 1
292303
if selection < 0 or selection >= len(installations):
293-
print("Invalid selection. Using first installation.")
304+
print(RED + "Invalid selection. Using first installation." + RESET)
294305
selection = 0
295306
file_path = installations[selection][0]
296-
print(f"Selected: {file_path}")
307+
print(LIME + f"Selected: {file_path}" + RESET)
297308
except ValueError:
298-
print("Invalid input. Using first installation found.")
309+
print(RED + "Invalid input. Using first installation found." + RESET)
299310
file_path = installations[0][0]
300311

301-
# Auto-detect authorization file location if needed
302312
if authorize_file_output.lower() == "auto":
303313
data_dirs = find_installation_data()
304314
if not data_dirs:
305-
# Create default location if none found
306315
config_dir = get_user_config_dir()
307316
default_dir = os.path.join(config_dir, "Ableton", f"Live {version} {edition}")
308317
unlock_dir = os.path.join(default_dir, "Unlock")
309318
os.makedirs(unlock_dir, exist_ok=True)
310319
authorize_file_output = os.path.join(unlock_dir, "Authorize.auz")
311-
print(f"\nUsing default authorization file location: {authorize_file_output}")
320+
print(LIME + f"\nUsing default authorization file location: " + WHITE + f"{authorize_file_output}" + RESET)
312321
else:
313-
print("\nFound Ableton data directories:")
322+
print(LIME + "\nFound Ableton data directories:" + RESET)
314323
for i, (path, name) in enumerate(data_dirs):
315-
print(f"{i+1}. {name} at {path}")
324+
print(WHITE + f"{i+1}. " + LIME + f"{name}" + GREY + f" at {path}" + RESET)
316325

317326
try:
318-
selection = int(input("\nSelect data directory: ")) - 1
327+
selection = int(input(LIME + "\nSelect data directory: " + RESET)) - 1
319328
if selection < 0 or selection >= len(data_dirs):
320-
print("Invalid selection. Using first directory.")
329+
print(RED + "Invalid selection. Using first directory." + RESET)
321330
selection = 0
322331
unlock_dir = os.path.join(data_dirs[selection][0], "Unlock")
323332
os.makedirs(unlock_dir, exist_ok=True)
324333
authorize_file_output = os.path.join(unlock_dir, "Authorize.auz")
325-
print(f"Selected: {authorize_file_output}")
334+
print(LIME + f"Selected: " + WHITE + f"{authorize_file_output}" + RESET)
326335
except ValueError:
327-
print("Invalid input. Using first data directory found.")
336+
print(RED + "Invalid input. Using first data directory found." + RESET)
328337
unlock_dir = os.path.join(data_dirs[0][0], "Unlock")
329338
os.makedirs(unlock_dir, exist_ok=True)
330339
authorize_file_output = os.path.join(unlock_dir, "Authorize.auz")
331340

332-
# Construct the key from the loaded parameters
333341
try:
334342
team_r2r_key = construct_key(dsa_params)
335343
except Exception as e:
336-
print(f"Error constructing DSA key: {e}")
337-
input("Press Enter to exit...")
344+
print(RED + f"Error constructing DSA key: {e}" + RESET)
345+
input(GREY + "Press Enter to exit..." + RESET)
338346
return
339347

340-
# Generate keys and save the authorize file
341-
print("\nGenerating authorization keys...")
348+
print(LIME + "\nGenerating authorization keys..." + RESET)
342349
try:
343350
lines = list(generate_all(team_r2r_key, edition, version, hwid))
344351
with open(authorize_file_output, "w", newline="\n") as f:
345352
f.write("\n".join(lines))
346-
print(f"Authorization file created: {authorize_file_output}")
353+
print(LIME + "Authorization file created: " + WHITE + f"{authorize_file_output}" + RESET)
347354
except Exception as e:
348-
print(f"Error generating authorization keys: {e}")
349-
input("Press Enter to exit...")
355+
print(RED + f"Error generating authorization keys: {e}" + RESET)
356+
input(GREY + "Press Enter to exit..." + RESET)
350357
return
351358

352-
# Replace the signkey in the binary file
353-
print("\nPatching executable...")
359+
print(LIME + "\nPatching executable..." + RESET)
354360
try:
355361
replace_signkey_in_file(file_path, old_signkey, new_signkey)
356-
print("\nPatch completed successfully!")
357-
input("Press Enter to exit...")
362+
print(LIME + "\nPatch completed successfully!" + RESET)
363+
input(GREY + "Press Enter to exit..." + RESET)
358364
except Exception as e:
359-
print(f"\nPatch failed: {e}")
360-
input("Press Enter to exit...")
365+
print(RED + f"\nPatch failed: {e}" + RESET)
366+
input(GREY + "Press Enter to exit..." + RESET)
361367

362368
if __name__ == "__main__":
363369
main()

0 commit comments

Comments
 (0)