forked from CommitteeOfZero/polyversal-coz-linux-patcher
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpolyversal
executable file
·769 lines (655 loc) · 25 KB
/
polyversal
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
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
#!/usr/bin/env bash
### Constants ###
progname="$(basename "$0")"
readonly progname
# Protontricks 1.10.1 or later is needed because anything earlier gives a
# cryptic message about magic numbers.
ptx_flatpak='com.github.Matoking.protontricks'
ptx_minversion='1.10.1'
readonly ptx_flatpak
readonly ptx_minversion
# For log files and the like, all hail ISO 8601.
# People likely won't be running this more than once in a second.
exectime=$(date +%Y%m%dT%H%M%S)
readonly exectime
VERSION=2.0.0
REPO_ID="CommitteeOfZero/multiversal-coz-linux-patcher"
URL_RELEASES="https://github.com/${REPO_ID}/releases"
URL_API_LATEST="https://api.github.com/repos/${REPO_ID}/releases/latest"
readonly VERSION
readonly URL_RELEASES
readonly URL_API_LATEST
### Functions ###
function print_usage() {
cat << EOF >&2
usage: polyversal [ -v | --verbose ] [ -h | --help ] [ --desktop ] [ --log ] ...
Use a GUI for selecting everything graphically.
$ $progname
Patch game with abbreviation GAME_ABBREV and CoZ patch in PATCH_FOLDER.
$ $progname install GAME_ABBREV PATCH_FOLDER
Uninstall patch for game with abbreviation GAME_ABBREV.
$ $progname uninstall GAME_ABBREV
Game abbreviations:
chn
sg
rne
cc
sg0
rnd
options:
-h, --help Display this help message and exit
-v, --verbose Enable debug logging
--desktop Disable terminal output and redirect stdin/stderr to
a new log file in 'logs'. This is the default when
running from the desktop file
--log Copy terminal output to a new file log in
'logs'. Disables terminal colors
EOF
}
# I like colors. Only use them if stderr outputs to a terminal which supports 8
# or more colors.
# Pulled into a function that we can call later since --desktop or similar
# options might change stderr.
txt_normal=''
txt_green=''
txt_yellow=''
txt_red=''
txt_purple=''
txt_blue=''
function set_logcolors() {
if ! test -t 2 || [[ ! $(tput colors) -ge 8 ]]; then return 0; fi
txt_normal="$(tput sgr0)"
txt_green="$(tput setaf 2)"
txt_yellow="$(tput setaf 3)"
txt_red="$(tput setaf 1)"
txt_purple="$(tput setaf 5)"
txt_blue="$(tput setaf 4)"
}
function log_msg() {
local sevpfx
case $(tolower "$1") in
'info' | 'i')
sevpfx="${txt_green}INFO" ;;
'warn' | 'w')
sevpfx="${txt_yellow}WARN" ;;
'error' | 'err' | 'e')
sevpfx="${txt_red}ERR" ;;
'fatal' | 'fat' | 'f')
sevpfx="${txt_purple}FATAL" ;;
'debug' | 'd')
sevpfx="${txt_blue}DEBUG" ;;
*)
# Better than dying for no reason
sevpfx="$1" ;;
esac
printf '%s: %s: %s\n' "$progname" "$sevpfx" "${*:2}${txt_normal}" >&2
}
function log_info() { log_msg info "$*"; }
function log_warn() { log_msg warn "$*"; }
function log_error() { log_msg err "$*"; }
function log_fatal() { log_msg fatal "$*"; }
function log_debug() {
! $mode_debug && return 0
log_msg debug "$*"
}
# Want `./polyversal chn` and `./polyversal CHN` to work the same
function tolower() {
printf '%s' "$*" | tr '[:upper:]' '[:lower:]'
}
# `command -v COMMAND` prints information about COMMAND, but importantly has
# exit status 0 if the command exists and 1 if it does not. This true/false
# value is what we use in this script to determine whether a command is
# installed and available on the system.
function is_cmd() {
command -v "$1" > /dev/null
}
# Shadows any call to zenity so we don't accidentally forget an $is_gui
function zenity() {
! $is_gui && return 0
command zenity "$@"
}
# Happens often enough to warrant a function
function zenity_error() {
zenity --error --title "Polyversal Error" --text "$*"
}
function zenity_warn() {
zenity --warning --title "Polyversal Warning" --text "$*"
}
# Handle non-zero exit statuses from Zenity.
# **Must be called immediately after zenity command.**
# Single optional argument is the message to be displayed in the case that the
# user closes the window.
function handle_zenity() {
local zen_ret=$?
local closedmsg="$*"
[[ ! $closedmsg ]] && closedmsg="You must select an option."
case $zen_ret in
1)
log_fatal "Zenity window was closed while prompting for selection."
zenity_error "$closedmsg"
exit 1 ;;
5)
log_fatal "Zenity dialogue timed out while prompting for selection."
zenity_error "The input dialogue timed out."
exit 1 ;;
-1)
log_fatal "Zenity returned an unexpected error"
zenity_error "An unexpected error occurred using Zenity."
exit 1 ;;
esac
}
# Determines if Protontricks is up to date, i.e. $ptx_minversion or later.
# Single argument is which Protontricks to check:
# 'fp' = Flatpak, 'sys' = system install
# Other arguments don't do anything. Don't use them!
# is_ptxvalid <fp|sys>
function is_ptxvalid() {
local ptx_cmd
case $1 in
fp)
ptx_cmd="flatpak run $ptx_flatpak" ;;
sys)
ptx_cmd="protontricks" ;;
esac
# Decent way to check it's actually functional.
local cur_ver
! cur_ver=$($ptx_cmd --version) && return 1
local older_ver
older_ver=$(printf '%s\n%s\n' "protontricks ($ptx_minversion)" "$cur_ver" | sort -V | head -n 1)
[[ $older_ver == "protontricks ($ptx_minversion)" ]]
}
### Option Parsing ###
if ! parsed_args=$(getopt -n "$progname" -o 'hv' --long 'help,verbose,desktop,log' -- "$@"); then
log_fatal "error parsing command line arguments"
print_usage
exit 1
fi
eval set -- "$parsed_args"
mode_debug=false
mode_desktop=false
mode_filelog=false
while true; do
case "$1" in
-v | --verbose)
mode_debug=true
shift ;;
-h | --help)
print_usage
exit 0 ;;
# Actual mode will be determined by the last one to be called in the command.
--desktop)
mode_desktop=true
mode_filelog=false
shift ;;
--log)
mode_filelog=true
mode_desktop=false
shift ;;
--)
shift
break ;;
*)
log_fatal "Unexpected option '$1', this should never happen"
print_usage
exit 1 ;;
esac
done
# mode_desktop and mode_filelog both log to a file, but desktop disables
# terminal output while filelog does not. Logging is output to a new .log file
# within 'logs/' under the same directory as this script.
logdir="$(dirname "$0")"/logs
logname="${logdir}/polyversal-${exectime}" # minus the .log so we can add -wine
if $mode_desktop; then
mkdir -p "$logdir"
exec > "${logname}.log" 2>&1
fi
if $mode_filelog; then
# https://stackoverflow.com/questions/3173131/redirect-copy-of-stdout-to-log-file-from-within-bash-script-itself
mkdir -p "$logdir"
exec > >(tee -ia "${logname}.log")
exec 2> >(tee -ia "${logname}.log" >&2)
fi
set_logcolors # after all the output redirection
log_info "Starting Polyversal Patcher on $(date) ..."
### set GUI/CLI mode ###
# GUI mode: 0 args
# CLI mode: 2 or 3 args
is_gui=false
if [[ $# -eq 0 ]]; then
if ! is_cmd zenity; then
log_fatal "Zenity is required to run this script in GUI mode. Please make sure you have it installed, or use the CLI."
print_usage
exit 1
fi
is_gui=true
fi
### Protontricks Setup ###
# Detect whether the machine is a Steam Deck.
is_deck=false
if grep -qE '^VERSION_CODENAME=holo' /etc/os-release; then
is_deck=true
log_info "detected Steam Deck environment ..."
fi
# We need Protontricks either through a system install or through Flatpak to
# work the magic. Prefer system install since it plays nice with relative dirs
# and doesn't need permissions to be setup, but if it's unavailable or outdated
# then use Flatpak.
ptx_cmd='protontricks'
is_flatpak=false
if is_cmd protontricks && is_ptxvalid sys; then
log_info "detected valid system install of protontricks ..."
else
if is_cmd protontricks; then
log_warn "system install of protontricks has insufficient version: $(protontricks --version) < $ptx_minversion"
else
log_info "system install of protontricks not found"
fi
log_info "proceeding with flatpak ..."
# Nothing doing if no flatpak :(
if ! is_cmd flatpak; then
log_fatal "either flatpak nor valid system install of protontricks was detected"
zenity_error "Neither Flatpak nor system Protontricks >= $ptx_minversion was found. Please install one of the two and then try again."
exit 1
fi
# Install protontricks if it's not already
if ! flatpak list | grep -q "$ptx_flatpak" -; then
log_warn "protontricks is not installed on flatpak. attempting installation ..."
if ! flatpak install -y "$ptx_flatpak"; then
log_fatal "error occurred while installing flatpak protontricks"
zenity_error "An error occurred while installing Protontricks via Flatpak."
exit 1
fi
log_info "flatpak protontricks installed successfully"
fi
# Has to have version >= $ptx_minversion
if ! is_ptxvalid fp; then
log_warn "flatpak protontricks out-of-date: $(flatpak run $ptx_flatpak --version) < $ptx_minversion. attempting to update ..."
if ! flatpak update -y "$ptx_flatpak"; then
log_fatal "error occurred while updating flatpak protontricks"
zenity_error "An error occurred while updating Flatpak Protontricks."
exit 1
fi
log_info "flatpak protontricks updated successfully"
fi
is_flatpak=true
ptx_cmd="flatpak run $ptx_flatpak"
fi
# Flatpak Protontricks has to be given access to the game's Steam folder to
# make changes. On Deck this is (hopefully) as easy as giving it access to all
# of its mounts and partitions; on PC, this could involve some tricky parsing
# of VDF files to give it access to different library folders.
# TODO: parse VDF files to give it access to different library folders. For
# now, FP Protontricks gives the user a prompt telling it which folder to give
# access to anyway, so it's not too big of an issue as long as the user can
# (a) read, and (b) copy and paste a single command.
compat_mts=
if $is_deck; then
flatpak override --user --filesystem=/run/media/ "$ptx_flatpak"
compat_mts="STEAM_COMPAT_MOUNTS=/run/media/"
fi
$is_flatpak && flatpak override --user --filesystem="$patch_dir" "$ptx_flatpak"
### Functionality Functions ###
# Get the target game and initialize the relevant variables
appid=
patch_exe=
gamename=
gamecode=
function set_game() {
# usage: set_game <shortname?> <zenity msg>
# arg can be omitted if GUI mode is set (no params to script)
local arg_game
if $is_gui; then
arg_game=$(zenity --list --radiolist --title "$2" \
--height 400 --width 600 \
--column "Select" --column "Title" \
TRUE 'CHAOS;HEAD NOAH' \
FALSE 'STEINS;GATE' \
FALSE 'ROBOTICS;NOTES ELITE' \
FALSE 'CHAOS;CHILD' \
FALSE 'STEINS;GATE 0' \
FALSE 'ROBOTICS;NOTES DaSH')
handle_zenity "You must select which game to patch for the script to work."
else
arg_game=$1
fi
# Get the app ID and what the installer exe should be, based on the shortname.
# IDs are available in the README.
# CoZ's naming conventions are beautifully consistent, pls never change them
case $(tolower "$arg_game") in
'chn' | 'ch' | 'chaos'[\;\ ]'head noah')
appid=1961950
patch_exe='CHNSteamPatch-Installer.exe'
gamename="CHAOS;HEAD NOAH"
gamecode=chn
;;
'sg' | 'steins'[\;\ ]'gate')
appid=412830
patch_exe='SGPatch-Installer.exe'
gamename="STEINS;GATE"
gamecode=sg
;;
'rne' | 'rn' | 'robotics'[\;\ ]'notes elite')
appid=1111380
patch_exe='RNEPatch-Installer.exe'
gamename="ROBOTICS;NOTES ELITE"
gamecode=rne
;;
'cc' | 'chaos'[\;\ ]'child')
appid=970570
patch_exe='CCPatch-Installer.exe'
gamename="CHAOS;CHILD"
gamecode=cc
;;
'sg0' | '0' | 'steins'[\;\ ]'gate 0')
appid=825630
patch_exe='SG0Patch-Installer.exe'
gamename="STEINS;GATE 0"
gamecode=sg0
;;
'rnd' | 'dash' | 'robotics'[\;\ ]'notes dash')
appid=1111390
patch_exe='RNDPatch-Installer.exe'
gamename="ROBOTICS;NOTES DaSH"
gamecode=rnd
;;
*)
log_fatal "shortname '$arg_game' is invalid"
print_usage
exit 1
;;
esac
}
# CHN CoZ patch includes custom SteamGrid images, but since the patch is built for
# Windows, the placement of those files ends up happening within the Wine prefix
# instead of the system-level Steam install. The following code will detect the
# STEAMGRID folder within the patch directory, and if it exists, copy any *.png
# files at its root to Steam userdata/<user_id>/config/grid within a default Steam
# path install ($HOME/.local/share/Steam)
#
# TODO: Add support for flatpak Steam installs.
function copy_steamgrid() {
# usage: copy_steamgrid <patch dir>
local patch_dir=$1
local has_users=false
local copies_fine=true
log_info "copying custom SteamGrid images ..."
# Don't iterate over userdata/*/config/grid because it might not exist.
for userdir in "$HOME"/.local/share/Steam/userdata/*; do
has_users=true
local griddir="$userdir/config/grid"
log_debug "installing SteamGrid in $griddir ..."
if ! { mkdir -p "$griddir" && cp "$patch_dir"/STEAMGRID/* "$griddir"; }
then
copies_fine=false
log_error "error occured while installing SteamGrid files to $griddir"
fi
done
if ! $has_users; then
log_error "no users were found in $HOME/.local/share/Steam/userdata"
zenity_error "No users were found in $HOME/.local/share/Steam/userdata, unable to install custom SteamGrid images."
elif $copies_fine; then
log_info "SteamGrid images installed successfully"
fi
}
# S;G and C;C both launch the default launcher (Launcher.exe and launcher.exe,
# respectively) instead of the patched LauncherC0.exe.
# Fix by symlinking default launcher to patched.
function apply_launcherfix() {
# usage: apply_launcherfix
local gamedir
if ! gamedir=$($ptx_cmd -c 'pwd' $appid); then
log_error "protontricks error while getting $gamecode game directory"
zenity_error "Protontricks encountered an error before the launcher issue for $gamename could be fixed. Check the logs for more information."
return 1
fi
log_info "fixing $gamecode launcher issue ..."
if ! pushd "$gamedir"; then
log_error "unable to cd into game directory '$gamedir'"
zenity_error "An error occurred while accessing the game directory '$gamedir', unable to apply launcher fix. See the logs for more info."
return 2
fi
local defaultlauncher
case $gamecode in
sg) defaultlauncher=Launcher.exe ;;
cc) defaultlauncher=launcher.exe ;;
*)
log_error 'u dun mucked up bozo, which launcher were you even trying to fix'
popd
return 3 ;;
esac
if [[ ! ( -f $defaultlauncher && -f LauncherC0.exe ) ]]; then
log_error "the default launcher and CoZ launcher were not both present in gamedir '$gamedir', unable to proceed with fix"
ls -l "$defaultlauncher" LauncherC0.exe >&2
zenity_error "$gamename's game directory did have the default and patched launcher both present, unable to apply launcher fix. Was the patch installed correctly?"
elif [[ $(readlink "$defaultlauncher") == LauncherC0.exe ]]; then
log_warn "$defaultlauncher was already symlinked to LauncherC0, has this script already been run?"
else
if mv "$defaultlauncher" "${defaultlauncher}_bkp" && ln -s LauncherC0.exe "$defaultlauncher"; then
log_info "symlinked $gamecode launcher"
else
log_error "unknown error occurred symlinking $gamecode launcher"
zenity_error "An unknown error occurred while applying launcher fix to $gamename. Execution will continue; consult the logs for details."
fi
fi
popd
}
# Undo the launcher fix for uninstallation
function undo_launcherfix() {
# usage: undo_launcherfix <game dir>
local gamedir=$1
log_info "undoing $gamecode launcher fix ..."
if ! pushd "$gamedir"; then
log_error "unable to cd into game directory '$gamedir'"
zenity_error "An error occurred accessing game directory '$gamedir' while undoing launcher fix, check the logs for details."
return 1
fi
local defaultlauncher
case $gamecode in
sg) defaultlauncher=Launcher.exe ;;
cc) defaultlauncher=launcher.exe ;;
*)
log_error 'uh oh bozo, which launcher were you even trying to fix uhh i mean undo'
popd
return 3 ;;
esac
if [[ ! ( -L $defaultlauncher && -f ${defaultlauncher}_bkp && -f LauncherC0.exe ) ]]; then
log_warn "expected files/symlink in '$gamedir' were not present, unable to unapply launcher fix"
ls -l "$defaultlauncher"{,_bkp} LauncherC0.exe >&2
popd
return 2
else
if unlink "$defaultlauncher" && mv "${defaultlauncher}_bkp" "$defaultlauncher"; then
log_info "$gamecode launcher restored to original"
else
log_error "unknown error occurred undoing $gamecode launcher symlink"
zenity_error "An unexpected error occurred undoing launcher fix for $gamename while uninstalling. Check the logs for details."
fi
fi
popd
}
function install_patch() {
# usage: install_patch <patch dir>
## patch dir validation ##
local arg_patchdir
if $is_gui; then
arg_patchdir=$(zenity --file-selection --title "Choose $gamename CoZ Patch Directory" \
--directory --filename "$HOME/Downloads")
handle_zenity "You must select the directory containing the patch for the script to work."
else
arg_patchdir=$1
fi
# Make sure the patch directory ($arg_patchdir) is valid.
# "Valid" here means:
# (1) it exists, and
# (2) it contains the expected patch EXE file to execute
if [[ ! -d $arg_patchdir ]]; then
log_fatal "directory '$arg_patchdir' does not exist"
zenity_error "Specified directory '$arg_patchdir' does not exist. Please try again."
exit 1
fi
if [[ ! -f "$arg_patchdir/$patch_exe" ]]; then
log_fatal "expected patch EXE '$patch_exe' does not exist within directory '$arg_patchdir'"
zenity_error "Directory '$arg_patchdir' does not contain expected EXE '$patch_exe', please try again. Make sure to select the extracted CoZ patch folder containing this file."
exit 1
fi
local has_steamgrid=false
if [[ -d "$arg_patchdir/STEAMGRID" ]]; then
has_steamgrid=true
log_info "using custom SteamGrid images ..."
fi
# Since we're running `cd` from within protontricks, we need to get the absolute
# path to the patch directory. Relative paths won't work for this since the
# shell invoked by `protontricks -c` sets its CWD to the game's directory.
# Prefer `realpath` to do the job, but if it's not available then get it by
# concatenating the user's CWD and the relative path. Simple testing shows that
# this hack does not work with the double-dot (..) on Flatpak Protontricks.
local patch_dir=$arg_patchdir
# only relative if it doesn't start with '/'
if [[ ! $arg_patchdir =~ ^/ ]]; then
log_warn "got relative path for patch directory"
# the '!' catches if realpath doesn't exist or some other permission error
if ! patch_dir=$(realpath "$arg_patchdir"); then
log_error "error using 'realpath' to set absolute path patch directory"
log_warn "attempting to set absolute path manually, this might cause issues ..."
patch_dir="$(pwd)/$arg_patchdir"
fi
fi
## game patching ##
log_info "patching $gamename ..."
zenity --timeout 10 --info --title 'Info' \
--text "$(printf 'Running patcher ...\n(This will disappear in 10 seconds)')" &
local ptx_winecmd="$ptx_cmd -c 'cd \"$patch_dir\" && $compat_mts wine $patch_exe' $appid"
if $mode_desktop || $mode_filelog; then
ptx_winecmd+=" > ${logname}-wine.log 2>&1"
fi
log_info "running command: \`$ptx_winecmd\`"
# this performs the magic
if ! eval "$ptx_winecmd"; then
log_error "patch installation exited with nonzero status"
zenity_error "Patch installation exited with a nonzero status. Script execution will continue; be wary of errors and check the output for information."
else
log_info "patch installation finished, no errors signaled."
fi
test -t 0 && stty sane # band-aid for newline wonkiness that wine sometimes creates
$has_steamgrid && copy_steamgrid "$patch_dir"
# Currently known launcher issues exist with s;g and c;c
[[ $gamecode == sg || $gamecode == cc ]] && apply_launcherfix
log_info 'Success! Completed without any script-breaking issues. Enjoy the game.'
zenity --info --title 'Polyversal Success!' \
--text "Patch installation for $gamename finished. Please verify that the patch is working in case anything went wrong under the hood. Enjoy the game!"
}
function uninstall_patch() {
# usage: uninstall_patch
zenity --timeout 2 --info --title ':(' --text ':(' &
log_info "uninstalling $gamename patch ..."
zenity --timeout 10 --info --title 'Info' \
--text "$(printf 'Running patch uninstaller ...\n(This will disappear in 10 seconds)')" &
# make sure the uninstaller actually exists
local gamedir
if ! gamedir=$($ptx_cmd -c 'pwd' $appid); then
log_error "protontricks error while getting $gamename game directory"
zenity_error "Protontricks encountered an error while accessing the $gamename's Steam directory. Unable to uninstall patch."
exit 1
fi
local uninstall_exe='nguninstall.exe'
if [[ ! -f $gamedir/$uninstall_exe ]]; then
log_error "$uninstall_exe not present in game directory '$gamedir', can't run anything"
zenity_error "The CoZ uninstaller EXE was not present in the game directory '$gamedir'. Are you sure the patch been installed for this game?"
exit 1
fi
local ptx_winecmd="$ptx_cmd -c '$compat_mts wine $uninstall_exe' $appid"
if $mode_desktop || $mode_filelog; then
ptx_winecmd+=" > ${logname}-wine.log 2>&1"
fi
log_info "running command: \`$ptx_winecmd\`"
[[ $gamecode == sg || $gamecode == cc ]] && undo_launcherfix "$gamedir"
if ! eval "$ptx_winecmd"; then
log_error "patch uninstallation exited with nonzero status"
zenity_error "Patch uninstallation exited with a nonzero status."
else
# Clean up possible leftovers, e.g. FMV project in languagebarrier and
# remaining nguninstall.exe. The idea is to get close to 0 external files
# remaining in the game dir so that e.g. uninstallation doesn't leave a
# dangling folder in steamapps/common.
# (even though dxvk logs cause that anyway. /shrug)
$ptx_cmd -c 'test -d languagebarrier && rm -rf languagebarrier' $appid
$ptx_cmd -c 'test -f nguninstall.exe && rm nguninstall.exe' $appid
log_info "patch uninstallation complete, no errors signaled."
fi
test -t 0 && stty sane
log_info 'Success! Uninstalled without any script-breaking issues.'
zenity --info --title 'Polyversal Success!' \
--text "$gamename CoZ patch uninstalled. Please verify that the game works as you expect in case anything went wrong under the hood. We hope you re-install soon :D"
}
function check_update() {
# usage: check_update
local curl_out
if ! curl_out=$(
curl -SsL -m 10 \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$URL_API_LATEST"
); then
log_error "cURL encountered an error retrieving the latest script version from GitHub"
return 1
fi
local latest_version
if ! latest_version=$(printf %s "$curl_out" | grep '"tag_name"' | grep -oE '[[:digit:]]+(\.[[:digit:]]+){2}'); then
log_error "GitHub version output differed from expected format"
log_debug "cURL output: $curl_out"
return 2
fi
local newer_version
newer_version=$(printf "%s\n%s" "$latest_version" "$VERSION" | sort -V | tail -n 1)
if [[ $newer_version == "$VERSION" ]]; then
log_info "polyversal is up-to-date"
return 0
fi
log_warn "Polyversal Patcher is out of date! using v$VERSION, you can download v$newer_version from $URL_RELEASES"
zenity --info --title 'New Update Available!' \
--text "A new version of the Polyversal Patcher (v$newer_version) is available! You can download it from <a href=\"$URL_RELEASES\">the releases page</a>."
}
### Argument Processing ###
check_update &
# Get the script mode, i.e. install or uninstall
arg_mode=
if $is_gui; then
arg_mode=$(zenity --list --radiolist --title 'What would you like to do?' \
--height 400 --width 600 \
--column 'Select' --column 'Title' \
TRUE 'Install a patch' \
FALSE 'Uninstall a patch' )
handle_zenity 'Please select a script action to continue.'
else
arg_mode=$1
fi
case $(tolower "$arg_mode") in
install | inst | i | 'install a patch')
# polyversal install <game> <patch dir>
if ! $is_gui && [[ $# -ne 3 ]]; then
log_fatal "Invalid syntax"
print_usage
exit 1
fi
set_game "$2" "Choose Which Game to Patch"
log_info "patching $gamename using app ID $appid, expecting patch EXE name '$patch_exe' ..."
install_patch "$3"
;;
uninstall | uninst | u | 'uninstall a patch')
# polyversal uninstall <game>
if ! $is_gui && [[ $# -ne 2 ]]; then
log_fatal "Invalid syntax"
print_usage
exit 1
fi
set_game "$2" "Choose Which Patch to Uninstall"
log_info "uninstalling CoZ patch for $gamename ..."
uninstall_patch
;;
*)
# gui selection will never be wrong, ideally lol
log_fatal "invalid script verb '$arg_mode'"
print_usage
exit 1
;;
esac