-
Notifications
You must be signed in to change notification settings - Fork 39
/
Copy pathmain.py
407 lines (364 loc) · 18.4 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
import platform, os, shutil, subprocess, json, re, glob, datetime, ctypes
system = platform.system()
def is_admin():
# 你他妈的,要修改文件都是要权限的,不用 sudo 或者 管理员 身份,你修改nm呢?
if system == 'Darwin' or system == 'Linux':
if not os.geteuid() == 0:
log("请以「sudo」运行此脚本")
exit(0)
elif system == 'Windows':
if not ctypes.windll.shell32.IsUserAnAdmin():
log("请以「管理员」身份运行此脚本")
exit(0)
def is_installed():
# macOS下检测是否安装了starUML,Windows下目录不确定,所以没写,拉倒吧
if system == 'Darwin':
if not os.path.exists(os.path.join("/Applications", "StarUML.app")):
log("未检测到 StarUML.app,请先到官网下载安装")
exit(0)
elif system == 'Windows':
if not os.path.exists(os.path.join("C:\\", "Program Files", "StarUML", "StarUML.exe")):
log("未检测到 StarUML.exe 或非官网下载安装,请先到官网下载安装")
exit(0)
# 没安装asar的能不能滚去先看教程怎么装,没有asar跑牛魔呢?
def detect_asar():
if system == "Darwin":
if os.system("command -v asar > /dev/null 2>&1") == 1:
log("未检测到asar,请先安装asar")
exit(0)
elif system == "Windows":
if os.system("where asar >nul 2>nul") != 0:
log("未检测到asar,请先安装asar")
exit(0)
elif system == "Linux":
pass
def extract(base):
asar_file = os.path.join(base, "app.asar")
asar_folder = os.path.join(base, "app")
os.system(f"asar extract \"{asar_file}\" \"{asar_folder}\"")
def pack(base):
asar_file = os.path.join(base, "app.asar")
asar_folder = os.path.join(base, "app")
os.system(f"asar pack \"{asar_folder}\" \"{asar_file}\"")
def backup(base):
if not os.path.exists(os.path.join(base, "app.asar.original")):
log("备份 app.asar -> app.asar.original")
shutil.copyfile(os.path.join(base, "app.asar"), os.path.join(base, "app.asar.original"))
else:
log("备份文件已存在,无需再次备份")
def rollback(base):
if os.path.exists(os.path.join(base, "app.asar.original")):
log("还原 app.asar.original -> app.asar")
shutil.copyfile(os.path.join(base, "app.asar.original"), os.path.join(base, "app.asar"))
else:
log("还原文件不存在,无法还原")
def is_first_install():
if system == "Darwin":
home_dir = os.path.expanduser("~")
user_path = os.path.join(home_dir, "Library", "Application Support", "StarUML")
elif system == "Windows":
home_dir = os.path.expanduser("~")
user_path = os.path.join(home_dir, "AppData", "Roaming", "StarUML")
elif system == "Linux":
home_dir = os.path.expanduser(f"~{os.environ['SUDO_USER']}")
user_path = os.path.join(home_dir, ".config", "StarUML")
else:
log("不支持的操作系统")
exit(0)
# 该文件夹不存在,则表示首次安装
if not os.path.exists(rf"{user_path}"):
log("请先打开一次 StarUML 再执行脚本")
exit(0)
def log(msg):
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"「{now}」 {msg}")
def read_json(file_path):
with open(file_path, 'r', encoding='utf-8') as file:
return json.load(file)
def write_json(file_path, data):
with open(file_path, 'w', encoding='utf-8') as file:
json.dump(data, file, ensure_ascii=False, indent=2)
def get_file_list(path):
if '*' in path:
full_path = path
return glob.glob(full_path)
else:
return [path]
def replace_in_file(file_path, replacements, option):
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
# log(f"正在替换文件: {file_path}")
content = content.replace(r"\u2026", "...").replace(r"\"dev\"", "dev")
for replacement in replacements:
for key, value in replacement.items():
if isinstance(value, list): # 处理嵌套列表
for item in value:
en_text = item['en']
cn_text = item['cn']
# log(f"正在替换 {en_text} -> {cn_text}")
if option == 1 or option == 2: # 汉化
content = re.sub(f'"{key}": "{re.escape(en_text)}"', f'"{key}": "{re.escape(cn_text)}"'.replace('\\', ''), content)
else:
en_text = replacement['en']
cn_text = replacement['cn']
# log(f"正在替换 {en_text} -> {cn_text}")
if option == 1 or option == 2: # 汉化
content = content.replace(en_text, cn_text)
with open(file_path, 'w', encoding='utf-8') as file:
file.write(content)
def is_staruml_running():
# 这里本来要kill掉的,一想到肯定有傻逼会有未保存的图表,kill掉就丢失了,所以仁慈一下
if system == 'Darwin':
if os.system("pgrep -x StarUML > /dev/null 2>&1") == 0:
log("检测到 StarUML 进程正在运行,请先关闭 StarUML 进程")
# os.system("killall -9 StarUML") # macOS
exit(0)
elif system == 'Windows':
if 'StarUML.exe' in subprocess.run(['tasklist', '/FI', 'IMAGENAME eq StarUML.exe'], capture_output=True, text=True).stdout:
log("检测到 StarUML 进程正在运行,请先关闭 StarUML 进程")
# os.system("taskkill /f /t /im StarUML.exe") # Windows
exit(0)
def handler(base, user_choice):
language_file = "StarUML_Language.json"
if user_choice in (0, 1, 2):
backup(base)
if user_choice == 0:
crack(base, user_choice)
if user_choice == 1:
translate(base, user_choice, language_file)
if user_choice == 2:
crack(base, user_choice)
translate(base, user_choice, language_file)
# 还原所有操作 2024.11.04 增加
if user_choice == 3:
rollback(base)
def translate(base, user_choice, language_file):
log("正在进行 StarUML 汉化操作...")
# 1. 仅存在app.asar,只处理app.asar
if os.path.exists(os.path.join(base, "app.asar")) and not os.path.exists(os.path.join(base, "app")):
translate_asar(language_file, base, user_choice)
# 2. app.asar和app文件夹共存,优先处理app.asar
elif os.path.exists(os.path.join(base, "app.asar")) and os.path.exists(os.path.join(base, "app")):
# 如果用户选择破解并汉化,就不需要解包了
if user_choice != 2:
log("检测到 app.asar 和 app 文件夹共存,优先处理 app.asar")
translate_asar(language_file, base, user_choice)
elif user_choice == 2:
translate_app(language_file, base, user_choice)
log("打包 app.asar")
pack(base)
log("删除 app 文件夹")
shutil.rmtree(os.path.join(base, "app"))
# 3. 不存在app.asar,只存在app文件夹,则只处理app文件夹
elif not os.path.exists(os.path.join(base, "app.asar")) and os.path.exists(os.path.join(base, "app")):
log("检测到只存在 app 文件夹,本次操作仅对 app 文件夹进行处理")
translate_app(language_file, base, user_choice)
log("StarUML 汉化操作完成")
def translate_asar(language_file, base, user_choice):
# 这里他妈的app.asar都存在了,还你妈node解包出错的话,你不是傻逼谁是傻逼
log("解包 app.asar")
extract(base)
translate_app(language_file, base, user_choice)
# 汉化完成后,对app.asar进行打包操作,并删除app文件夹
log("打包 app.asar")
pack(base)
log("删除 app 文件夹")
shutil.rmtree(os.path.join(base, "app"))
def translate_app(language_file, base, user_choice):
log("正在汉化文件...")
data = read_json(language_file)
for path, replacements in data.items():
files = get_file_list(os.path.join(base, "app", path))
for file_path in files:
replace_in_file(file_path, replacements, user_choice)
log("文件汉化完成")
def crack(base, user_choice):
log("正在进行 StarUML 破解操作...")
# 先把原来的license.key文件删掉
if system == "Linux":
home_dir = os.path.expanduser(f"~{os.environ['SUDO_USER']}")
else:
home_dir = os.path.expanduser("~")
try:
if system == 'Darwin':
user_path = os.path.join(home_dir, "Library", "Application Support", "StarUML")
if os.path.exists(os.path.join(user_path, "license.key")):
log("移除已存在的 license.key 文件")
os.remove(os.path.join(user_path, "license.key"))
elif system == 'Windows':
user_path = os.path.join(home_dir, "AppData", "Roaming", "StarUML")
if os.path.exists(os.path.join(user_path, "license.key")):
log("移除已存在的 license.key 文件")
os.remove(os.path.join(user_path, "license.key"))
os.remove(os.path.join(base, "app", "license.key"))
elif system == 'Linux':
user_path = os.path.join(home_dir, ".config", "StarUML")
if os.path.exists(os.path.join(user_path, "license.key")):
log("移除已存在的 license.key 文件")
os.remove(os.path.join(user_path, "license.key"))
os.remove(os.path.join(user_path, "app", "license.key"))
except FileNotFoundError:
pass
except KeyboardInterrupt:
pass
log("请输入StarUML关于页面要显示的用户名(回车即使用程序默认): ")
username = input()
if not username: username = "GitHub: X1a0He/StarUML-CrackedAndTranslate"
# 1. 仅存在app.asar,只处理app.asar
# 2. app.asar和app文件夹共存,优先处理app.asar
if os.path.exists(os.path.join(base, "app.asar")) or os.path.exists(os.path.join(base, "app")):
crack_asar(base, username, user_choice)
# 3. 不存在app.asar,只存在app文件夹,则只处理app文件夹
elif not os.path.exists(os.path.join(base, "app.asar")) and os.path.exists(os.path.join(base, "app")):
crack_app(base, username)
log("StarUML 破解处理完毕,请按照下列步骤进行操作")
log("1. 运行StarUML,选择菜单栏的Help - Enter License Key")
log("2. 弹出窗口后,直接点击OK即可")
def crack_asar(base, username, user_choice):
log("解包 app.asar")
extract(base)
crack_app(base, username)
# 如果用户选择破解并汉化的话,就不需要重新打包了,做完再打包
if user_choice != 2:
log("打包 app.asar")
pack(base)
log("删除 app 文件夹")
shutil.rmtree(os.path.join(base, "app"))
def add_member_after_label(obj, target_label, new_member):
if isinstance(obj, list):
for index, item in enumerate(obj):
if isinstance(item, dict) and item.get("label") == target_label:
obj.insert(index + 1, new_member)
return True
result = add_member_after_label(item, target_label, new_member)
if result:
return result
elif isinstance(obj, dict):
for key in obj:
if isinstance(obj[key], (dict, list)):
result = add_member_after_label(obj[key], target_label, new_member)
if result:
return result
return None
def write_author_info(base):
app_folder = os.path.join(base, "app")
src_folder = os.path.join(app_folder, "src")
# 修改关于弹窗部分
static_folder = os.path.join(src_folder, "static")
html_contents_folder = os.path.join(static_folder, "html-contents")
about_dialog = os.path.join(html_contents_folder, "about-dialog.html")
with open(about_dialog, "r", encoding="utf-8") as file:
html_content = file.read()
new_html_content = html_content.replace("<span class=\"license\" style=\"font-weight: 600;\"></span>",
"<a href=\"https://github.com/X1a0He/StarUML-CrackedAndTranslate\"><span class=\"license\" style=\"font-weight: 600;\"></span></a>")
with open(about_dialog, "w", encoding="utf-8") as file:
file.write(new_html_content)
# 修改标题部分
titlebar_view = os.path.join(src_folder, "views", "titlebar-view.js")
with open(titlebar_view, "r", encoding="utf-8") as file:
js_content = file.read()
new_js_content = js_content.replace("""title += "(EVALUATION MODE)";
}""",
"""title += "(EVALUATION MODE)";
} else { title += '【By GitHub: X1a0He/StarUML-CrackedAndTranslate】'}""")
with open(titlebar_view, "w", encoding="utf-8") as file:
file.write(new_js_content)
# 修改菜单栏部分
resources_folder = os.path.join(app_folder, "resources")
darwin_json = os.path.join(resources_folder, "default", "menus", "darwin.json")
win32_json = os.path.join(resources_folder, "default", "menus", "win32.json")
linux_json = os.path.join(resources_folder, "default", "menus", "linux.json")
darwin_data = read_json(darwin_json)
win32_data = read_json(win32_json)
linux_data = read_json(linux_json)
add_member_after_label(darwin_data, "About StarUML", {
"label": "By GitHub: X1a0He/StarUML-CrackedAndTranslate",
"id": "",
"command": "help:cracked"
})
add_member_after_label(win32_data, "About StarUML", {
"label": "By GitHub: X1a0He/StarUML-CrackedAndTranslate",
"id": "",
"command": "help:cracked"
})
add_member_after_label(linux_data, "About StarUML", {
"label": "By GitHub: X1a0He/StarUML-CrackedAndTranslate",
"id": "",
"command": "help:cracked"
})
write_json(darwin_json, darwin_data)
write_json(win32_json, win32_data)
write_json(linux_json, linux_data)
engine_folder = os.path.join(src_folder, "engine")
default_commands = os.path.join(engine_folder, "default-commands.js")
with open(default_commands, "r", encoding="utf-8") as file:
js_content = file.read()
if 'app.commands.register("help:cracked", () => shell.openExternal("https://github.com/X1a0He/StarUML-CrackedAndTranslate"), "Help: Cracked");' not in js_content:
js_content += "\n" + 'app.commands.register("help:cracked", () => shell.openExternal("https://github.com/X1a0He/StarUML-CrackedAndTranslate"), "Help: Cracked");'
with open(default_commands, "w", encoding="utf-8") as file:
file.write(js_content)
def crack_app(base, username):
destination_path = os.path.join(base, "app", "src")
shutil.copy("hook.js", destination_path)
hook_file_path = os.path.join(destination_path, "hook.js")
app_context_file_path = os.path.join(destination_path, "app-context.js")
with open(hook_file_path, "r", encoding="utf-8") as file:
js_content = file.read()
new_js_content = js_content.replace("GitHub: X1a0He/StarUML-CrackedAndTranslate", username)
with open(hook_file_path, "w", encoding="utf-8") as file:
file.write(new_js_content)
with open(app_context_file_path, "r", encoding="utf-8") as file:
js_content = file.read()
if 'require("./hook");' not in js_content:
log("hook写入中...")
new_js_content = js_content.replace('this.appReady();', 'require("./hook");\nthis.appReady();')
with open(app_context_file_path, "w", encoding="utf-8") as file2:
file2.write(new_js_content)
log("hook写入完毕")
else:
log("文本已被修改过,无需再次修改")
write_author_info(base)
def main():
try:
print(" -----------------------------------------------")
print("| |")
print("| ██╗ ██╗ ██╗ █████╗ ██████╗ ██╗ ██╗███████╗ |")
print("| ╚██╗██╔╝███║██╔══██╗██╔═████╗██║ ██║██╔════╝ |")
print("| ╚███╔╝ ╚██║███████║██║██╔██║███████║█████╗ |")
print("| ██╔██╗ ██║██╔══██║████╔╝██║██╔══██║██╔══╝ |")
print("| ██╔╝ ██╗ ██║██║ ██║╚██████╔╝██║ ██║███████╗ |")
print("| ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ |")
print("| StarUML Cracker |")
print(" -----------------------------------------------")
print("StarUML「Mac & Win」一键破解汉化脚本")
print("Github: https://github.com/X1a0He/StarUML-CrackedAndTranslate")
print()
is_admin()
detect_asar()
is_installed()
is_first_install()
is_staruml_running()
log("macOS 15用户请确保在更新完 StarUML 后手动打开一次 StarUML 再执行脚本")
user_choice = int(input("0 -> 仅破解\n1 -> 仅汉化\n2 -> 破解并汉化\n3 -> 还原所有\n-1 -> 退出运行\n请输入您的选择: \n"))
if user_choice == -1:
exit(0)
if user_choice in (0, 1, 2, 3):
if system == 'Darwin':
base = os.path.join("/Applications", "StarUML.app", "Contents", "Resources")
handler(base, user_choice)
log("如遇到打开 StarUML 提示已损坏,请手动在终端执行如下命令后,在 Application 右键打开 StarUML")
log("sudo xattr -cr /Applications/StarUML.app")
log("macOS 15 的用户如果一直遇到提示已损坏,建议先打开一遍 StarUML 后再运行")
# os.system("open -a StarUML")
elif system == 'Windows':
base = os.path.join("C:\\", "Program Files", "StarUML", "resources")
handler(base, user_choice)
# Windows的启动功不知道什么命令,拉倒吧
elif system == 'Linux':
# Linux 系统未经证实,请谨慎运行
base = "/opt/StarUML/resources"
handler(base, user_choice)
except KeyboardInterrupt:
print("\n用户中断了程序执行")
if __name__ == '__main__':
main()