-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathsetup.py
executable file
·462 lines (388 loc) · 14.2 KB
/
setup.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
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
#!/usr/bin/env python3
# SPDX-License-Identifier: AGPL-3.0-only
import os
import sys
import stat
import shutil
import subprocess
from coreutils import (
log,
pip,
exit_with_message,
)
from corenodep import (
load_conf_setting,
save_conf_setting,
check_dependencies,
)
from coreutils import (
show_message,
)
from mainutils import (
download_progress,
is_flatpak,
)
from typing import (
Optional,
Union,
List,
)
if getattr(sys, "frozen", False):
SCRIPT_IMP_FILE = os.path.realpath(sys.executable)
else:
SCRIPT_IMP_FILE = os.path.realpath(__file__)
SCRIPT_PATH = os.path.dirname(SCRIPT_IMP_FILE)
def welcome() -> bool:
import FreeSimpleGUI as sg
import requests
wemod_logo = requests.get(
"https://www.wemod.com/static/images/device-icons/favicon-192-ce0bc030f3.png",
stream=False,
)
sg.theme("systemdefault")
ret = sg.popup_ok_cancel(
"Welcome to WeMod Installer!\nPress OK to start the setup.",
title="WeMod Launcher Setup",
image=wemod_logo.content,
icon=wemod_logo.content,
)
return ret == "OK"
def download_wemod(temp_dir: str) -> str:
import FreeSimpleGUI as sg
sg.theme("systemdefault")
status = [0, 0]
progress = sg.ProgressBar(100, orientation="h", s=(50, 10))
text = sg.Text("0%")
# text = sg.Multiline(str.join("", log), key="-LOG-", autoscroll=True, size=(50,50), disabled=True)
layout = [[progress], [text]]
window = sg.Window("Downloading WeMod", layout, finalize=True)
def update_log(status: list[int], dl: int, total: int) -> None:
status.clear()
status.append(dl)
status.append(total)
setup_file = os.path.join(temp_dir, "wemod_setup.exe")
download_func = lambda: download_progress(
"https://api.wemod.com/client/download",
setup_file,
lambda dl, total: update_log(status, dl, total),
)
# download_func = lambda: download_progress("http://localhost:8000/WeMod-8.3.15.exe", setup_file, lambda dl,total: update_log(status, dl, total))
window.perform_long_operation(download_func, "-DL COMPLETE-")
while True: # Event Loop
event, values = window.read(timeout=1000)
if event == "-DL COMPLETE-":
break
elif event == None:
exit_with_message(
"Window Closed", "The window was closed, exiting", timeout=5
)
else:
if len(status) < 2:
continue
[dl, total] = status
perc = int(100 * (dl / total)) if total > 0 else 0
text.update("{}% ({}/{})".format(perc, dl, total))
progress.update(perc)
window.close()
return setup_file
def unpack_wemod(
setup_file: str, temp_dir: str, install_location: str
) -> bool:
try:
import zipfile
import tempfile
archive = zipfile.ZipFile(setup_file, mode="r")
names = archive.filelist
nupkg = list(
filter(lambda name: str(name.filename).endswith(".nupkg"), names)
)[0]
tmp_nupkgd = tempfile.mktemp(prefix="wemod-nupkg-")
archive.extract(nupkg, tmp_nupkgd)
tmp_nupkg = os.path.join(tmp_nupkgd, nupkg.filename)
archive.close()
archive = zipfile.ZipFile(tmp_nupkg, mode="r")
net = list(
filter(
lambda name: name.filename.startswith("lib/net"),
archive.filelist,
)
)
tmp_net = tempfile.mkdtemp(prefix="wemod-net")
archive.extractall(tmp_net, net)
shutil.move(os.path.join(tmp_net, net[0].filename), install_location)
shutil.rmtree(tmp_net, ignore_errors=True)
shutil.rmtree(tmp_nupkgd, ignore_errors=True)
shutil.rmtree(tmp_nupkg, ignore_errors=True)
shutil.rmtree(temp_dir, ignore_errors=True)
return True
except:
return False
def mk_venv() -> Optional[str]:
venv_path = load_conf_setting("VirtualEnvironment")
if not venv_path:
venv_path = "wemod_venv"
try:
if os.path.isabs(venv_path):
subprocess.run(
[sys.executable, "-m", "venv", venv_path], check=True
)
else:
subprocess.run(
[
sys.executable,
"-m",
"venv",
os.path.abspath(os.path.join(SCRIPT_PATH, venv_path)),
],
check=True,
)
log("Virtual environment created successfully.")
except Exception as e:
log(f"Failed to create virtual environment, with error {e}")
return None
else:
save_conf_setting("VirtualEnvironment", venv_path)
return venv_path
def tk_check() -> None:
try:
if not bool(check_flatpak(None)):
import tkinter
except ImportError:
exit_with_message(
"Tkinter missing",
"Critical error, tkinter is not installed,\nmake sure you have installed the correct tkinter package for your system,\nsearch the internet for 'install tkinter for YOURDISTRO',\nreplace YOURDISTRO with your actual distro",
)
def venv_manager() -> List[Optional[str]]:
requirements_txt = os.path.join(SCRIPT_PATH, "requirements.txt")
tk_check()
if not check_dependencies(requirements_txt):
pip_install = f"install -r '{requirements_txt}'"
return_code = pip(pip_install)
if return_code == 0:
return []
# if dependencies cant just be installed
else:
# go the venv route
venv_path = mk_venv()
if not venv_path:
log(
"Failed to create virtual environment, trying to force install venv"
)
return_code = pip("install --break-system-packages venv")
if return_code == 0:
venv_path = mk_venv()
if not venv_path:
log(
"CRITICAL: Backup failed to create virtual environment, exiting"
)
exit_with_message(
"Error on package venv",
"Failed to create virtual environment. Error.",
ask_for_log=True,
)
else:
log(
"CRITICAL: The python package 'venv' is not installed and could not be downloaded"
)
exit_with_message(
"Missing python-venv",
"The python package 'venv' is not installed and could not be downloaded. Error.",
ask_for_log=True,
)
# At this point we have a venv
if venv_path and not os.path.isabs(venv_path):
venv_path = os.path.join(SCRIPT_PATH, venv_path)
# Determine the path to the Python executable within the virtual environment
venv_python = os.path.join(venv_path, "bin", "python")
# Pre-install dependencies in the virtual environment
return_code = pip(pip_install, venv_path)
if return_code != 0:
log("CRITICAL: Dependencies can't be installed")
exit_with_message(
"Dependencies install error",
"Failed to install dependencies. Error.",
ask_for_log=True,
)
return [venv_python]
def self_update(path: List[Optional[str]]) -> List[Optional[str]]:
upd = os.getenv("SELF_UPDATE")
if not upd:
upd = load_conf_setting("SelfUpdate")
infinite = os.getenv("WeModInfProtect", "1")
if int(infinite) > 3:
return path
elif int(infinite) > 2:
log("Self update skipped, because updates where made already.")
return path
elif getattr(sys, "frozen", False):
log("Self update skipped, because script was compiled")
return path
elif upd and upd.lower() == "false":
log("Self update skipped, because it was requested in the config")
return path
flatpak_cmd = ["flatpak-spawn", "--host"] if is_flatpak() else []
original_cwd = os.getcwd()
try:
os.chdir(SCRIPT_PATH)
# Check if we're in the main branch
curr_branch = subprocess.run(
flatpak_cmd + ["git", "branch", "--show-current"],
stdout=subprocess.PIPE,
text=True,
).stdout.strip()
if curr_branch != "main":
log("Currently not in main branch. Aborting update")
return path
# Fetch latest changes
subprocess.run(flatpak_cmd + ["git", "fetch"], text=True)
# Get local and remote commit hashes
local_hash = subprocess.run(
flatpak_cmd + ["git", "rev-parse", "@"],
stdout=subprocess.PIPE,
text=True,
).stdout.strip()
remote_hash = subprocess.run(
flatpak_cmd + ["git", "rev-parse", "@{u}"],
stdout=subprocess.PIPE,
text=True,
).stdout.strip()
base_hash = subprocess.run(
flatpak_cmd + ["git", "merge-base", "@", "@{u}"],
stdout=subprocess.PIPE,
text=True,
).stdout.strip()
if local_hash == remote_hash:
log("No updates available")
else:
log("Updating...")
# Check if there are local changes that differ from remote
if local_hash != base_hash:
log(
"Warning: Local changes detected, updating from remote anyway."
)
subprocess.run(
flatpak_cmd + ["git", "reset", "--hard", "origin"], text=True
)
subprocess.run(flatpak_cmd + ["git", "pull"], text=True)
# Set executable permissions (replace with specific file names if needed)
subprocess.run(
flatpak_cmd + ["chmod", "-R", "ug+x", "*.py", "wemod{,.bat}"],
text=True,
)
# Optionally update the path to include the executable if not already set
if not path:
path = [sys.executable]
log("Update finished")
except Exception as e:
log(f"Failed to update, the following error appeared:\n\t{e}")
finally:
os.chdir(original_cwd)
return path
def check_flatpak(flatpak_cmd: Optional[List[str]]) -> List[str]:
flatpak_start = []
if is_flatpak() and not os.getenv("FROM_FLATPAK"):
flatpak_start = [
"flatpak-spawn",
"--host",
]
envlist = [
"STEAM_COMPAT_TOOL_PATHS",
"STEAM_COMPAT_DATA_PATH",
"WINE_PREFIX_PATH",
"WINEPREFIX",
"WINE",
"SCANFOLDER",
"TROUBLESHOOT",
"WEMOD_LOG",
"WAIT_ON_GAMECLOSE",
"SELF_UPDATE",
"FORCE_UPDATE_WEMOD",
"REPO_STRING",
]
for env in envlist:
if env in os.environ:
flatpak_start.append(f"--env={env}={os.environ[env]}")
infpr = os.getenv("WeModInfProtect", "1")
infpr = str(int(infpr) + 1)
flatpak_start.append("--env=FROM_FLATPAK=true")
flatpak_start.append(f"--env=WeModInfProtect={infpr}")
flatpak_start.append("--") # Isolate command from command args
if flatpak_cmd: # if venv is set use it
return flatpak_start + flatpak_cmd
elif flatpak_start: # if not use python executable
return flatpak_start + [sys.executable]
else:
return []
def setup_main() -> None:
import tempfile
import FreeSimpleGUI as sg
if not welcome():
print("Installation cancelled by user")
return
install_location = os.path.join(SCRIPT_PATH, "wemod_bin")
winetricks = os.path.join(SCRIPT_PATH, "winetricks")
if os.getenv("FORCE_UPDATE_WEMOD", "0") == "1" or not os.path.isfile(
winetricks
):
if os.path.isfile(winetricks):
shutil.rmtree(winetricks)
log("Winetricks not found...")
log("Downloading latest winetricks...")
download_progress(
"https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks",
winetricks,
None,
)
os.chmod(
winetricks,
stat.S_IXUSR
| stat.S_IXGRP
| stat.S_IXOTH
| stat.S_IRUSR
| stat.S_IRGRP
| stat.S_IROTH,
)
if (
not os.path.isdir(install_location)
or not os.path.isfile(os.path.join(install_location, "WeMod.exe"))
or os.getenv("FORCE_UPDATE_WEMOD", "0") == "1"
):
if os.path.isdir(install_location):
shutil.rmtree(install_location, ignore_errors=True)
temp_dir = tempfile.mkdtemp(prefix="wemod-launcher-")
setup_file = download_wemod(temp_dir)
unpacked = unpack_wemod(setup_file, temp_dir, install_location)
show_message(
'Setup completed successfully.\nMake sure the "LAUNCH OPTIONS" of the game say \''
+ str(os.path.join(SCRIPT_PATH, "wemod"))
+ " %command%'",
title="WeMod Downloaded",
timeout=5,
)
if not unpacked:
log("Failed to unpack WeMod.")
exit_with_message(
"Failed Unpack",
"Failed to unpack WeMod, exiting",
1,
timeout=10,
ask_for_log=True,
)
def run_wemod() -> None:
if getattr(sys, "frozen", False):
exit_with_message(
"Invalid compile",
"The script was compiled with 'setup.py' as the start file.\nThis is incorrect the start-file is 'wemod', please recompile",
)
else:
script_file = os.path.join(SCRIPT_PATH, "wemod")
command = [sys.executable, script_file] + sys.argv[1:]
# Execute the main script so the venv gets created
process = subprocess.run(command, capture_output=True, text=True)
# Send output and error to steam console
# If function log worked it gets logged by the re-run
print(str(process.stdout))
print(str(process.stderr))
sys.exit(process.returncode)
if __name__ == "__main__":
run_wemod()