diff --git a/apps/Lutris/preloader.sh b/apps/Lutris/preloader.sh new file mode 100755 index 0000000..230b596 --- /dev/null +++ b/apps/Lutris/preloader.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Lutris pre-loader script, for allowing loading multiple pre-load scripts. + +# Enabling debug will enable the script output. +DEBUG=1 + +execute_file () { + # Just in case... + chmod +x "$1" + if [[ $DEBUG -eq 0 ]]; then + nohup "$1" >/dev/null 2>&1 & + else + # No this is not as smart as you think... + filename=$(echo "$1" | cut -d "/" -f3) + nohup "$1" > ./logs/preloader_"$filename".log 2>&1 & + fi +} + +echo "Checking directory..." +mkdir -p ./preloader/ ./logs/ +for file in ./preloader/*.*; do + [ -e "$file" ] || continue + echo "Found $file, loading..." + execute_file "$file" +done + diff --git a/games/LoL/linux/README.md b/games/LoL/linux/README.md index bdc8fc4..cf4d641 100644 --- a/games/LoL/linux/README.md +++ b/games/LoL/linux/README.md @@ -1,12 +1,13 @@ # leagueoflinux scripts ## `sulaunchhelper2.sh` -This script is a wrapper for `sulaunchhelper2.py` and `syscall_check.sh`, for use with Lutris pre-game launch script. +This script is a wrapper for `sulaunchhelper2.py` for use with Lutris pre-game launch script. ### Installation -+ To install you must have `sulaunchhelper2.py` and `syscall_check.sh` present, if not you can execute these to quickly download them (to current directory): +> This script no longer wrap `syscall_check.sh`, if you need to execute that script alongside this one, I recommend you to take a look at [preloader.sh](./README.md) + ++ To install you must have `sulaunchhelper2.py` present, if not you can execute these to quickly download them (to current directory): ```sh -curl -OL https://gitlab.com/tretrauit/scripts/-/raw/main/games/LoL/linux/sulaunchhelper2.py -curl -OL https://gitlab.com/tretrauit/scripts/-/raw/main/games/LoL/linux/syscall_check.sh -chmod +x sulaunchhelper2.py syscall_check.sh +curl -OL https://raw.githubusercontent.com/CakeTheLiar/launchhelper/master/sulaunchhelper2.py +chmod +x sulaunchhelper2.py ``` + Then to download `sulaunchhelper2.sh` itself: ```sh @@ -22,12 +23,13 @@ chmod +x sulaunchhelper2.sh ## `garena_wrapper.sh` This script automates the launching of [lol.py](https://github.com/nhubaotruong/league-of-legends-linux-garena-script) (LoL in Garena client) so you don't have to manually do it ;) ### Installation -Because this script is used in Lutris pre-game launch script, it also wrap `syscall_check.sh`. You also need to follow steps in `lol.py` repository to properly config your LoL prefix. -+ To install you must have `lol.py` and `syscall_check.sh` present, if not you can execute these to quickly download them (to current directory): +> This script no longer wrap `syscall_check.sh`, if you need to execute that script alongside this one, I recommend you to take a look at [preloader.sh](./README.md) + +You need to follow steps in `lol.py` repository to properly config your LoL prefix. ++ To install you must have `lol.py` present, if not you can execute these to quickly download them (to current directory): ```sh curl -OL https://raw.githubusercontent.com/nhubaotruong/league-of-legends-linux-garena-script/main/lol.py -curl -OL https://gitlab.com/tretrauit/scripts/-/raw/main/games/LoL/linux/syscall_check.sh -chmod +x sulaunchhelper2.py syscall_check.sh +chmod +x sulaunchhelper2.py ``` + Then to download `garena_wrapper.sh` itself: ```sh diff --git a/games/LoL/linux/garena_wrapper.sh b/games/LoL/linux/garena_wrapper.sh index 1b7dac4..80a71bb 100755 --- a/games/LoL/linux/garena_wrapper.sh +++ b/games/LoL/linux/garena_wrapper.sh @@ -3,21 +3,9 @@ # It automatically execute lol.py to start LoL lutris game from Garena. # You need lol.py and syscall_check.sh present in game prefix root directory. -SCC_SH='syscall_check.sh' LOL_PY='lol.py' -dialog() { - zenity "$@" --icon-name='lutris' --width="400" --title="Garena LoL to LoL lutris wrapper" -} - own_dir="$(realpath .)" -# try to call syscall_check.sh -if ! [ -x "${own_dir}/${SCC_SH}" ]; then - dialog "Please place this script into the same directory as '${SCC_SH}'!" -else - sh "${own_dir}/${SCC_SH}" -fi - echo "Waiting for Garena to start..." until _=$(pidof Garena.exe) do diff --git a/games/LoL/linux/lol.py b/games/LoL/linux/lol.py deleted file mode 100644 index 54f03d4..0000000 --- a/games/LoL/linux/lol.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python3 - -import psutil -import os -import yaml -import sqlite3 -import re -import time - - -def find_process_id_by_name(processName): - """ - Get a list of all the PIDs of a all the running process whose name contains - the given string processName - """ - # Iterate over the all the running process - while True: - time.sleep(1) - for proc in psutil.process_iter(): - try: - pinfo = proc.as_dict(attrs=["cmdline", "name", "pid"]) - # Check if process name contains the given name string. - if processName.lower() in pinfo["name"].lower(): - return pinfo - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - pass - - -def kill_old_league_and_riot_process(): - RIOT_AND_LEAGUE_PROCESS_REGEX = re.compile("(league|riot).*\.exe", flags=re.I) - for proc in psutil.process_iter(): - try: - pinfo = proc.as_dict(attrs=["name", "pid"]) - # Check if process name contains the given name string. - if RIOT_AND_LEAGUE_PROCESS_REGEX.search(pinfo.get("name")): - print( - f"Found old process still running: {pinfo.get('name')}, pid: {pinfo.get('pid')}" - ) - p = psutil.Process(pinfo.get("pid")) - p.kill() - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - pass - - -def main(): - riot_service_process_name = "RiotClientServices.exe" - - print( - f"The script will wait for a {riot_service_process_name} process to show up to get it's token. So go to the Garena client and press Play" - ) - kill_old_league_and_riot_process() - - # Get RiotClientServices.exe process - riot_process = find_process_id_by_name(riot_service_process_name) - - # Programmatically get RiotClientServices.exe arguments - try: - riot_argument = " ".join(riot_process.get("cmdline", [])[1:]).strip() - except Exception: - print("Cannot get Garena Token") - - # Exit if no riot_argument is found - if not riot_argument: - quit() - - print(f"Garena token got from {riot_service_process_name}: {riot_argument}") - - # Kill the current RiotClientServices.exe - p = psutil.Process(riot_process.get("pid")) - p.kill() - - # Start game using lutris - HOME = os.getenv("HOME") - - LUTRIS_DB_PATH = HOME + "/.local/share/lutris" - LUTRIS_DB_NAME = "pga.db" - conn = sqlite3.connect(LUTRIS_DB_PATH + "/" + LUTRIS_DB_NAME) - c = conn.cursor() - c.execute("SELECT id,configpath FROM games WHERE slug='league-of-legends'") - try: - game_id, configpath = c.fetchone() - conn.close() - except Exception: - print("No league of legends install from lutris found") - conn.close() - quit() - - LUTRIS_CONFIG_FOLDER = HOME + "/.config/lutris/games/" - LUTRIS_LOL_CONFIG_FILE = LUTRIS_CONFIG_FOLDER + configpath + ".yml" - - with open(LUTRIS_LOL_CONFIG_FILE, "r") as f: - try: - game_config = yaml.load(f, Loader=yaml.FullLoader) - except yaml.YAMLError as exc: - print(exc) - quit() - - with open(LUTRIS_LOL_CONFIG_FILE, "w") as f: - game_config["game"]["args"] = riot_argument - try: - yaml.dump(game_config, f, default_flow_style=False) - except yaml.YAMLError as exc: - print(exc) - quit() - - print("Starting game with current config:") - print(f"- Wine version: {game_config.get('wine',{}).get('version')}") - print(f"- Executable: {game_config.get('game',{}).get('exe')}") - print(f"- Wineprefix: {game_config.get('game',{}).get('prefix')}") - - print( - "If this is your first time running LOL since reboot, a pop up will appear, chose the first or second option then enter your password\n" - ) - print("It's a workaround by the lutris community\n") - print("Ignore the lines below, they are not actually error\n") - launch_command = f"lutris lutris:rungameid/{game_id}" - os.system(launch_command) - - -if __name__ == "__main__": - main() diff --git a/games/LoL/linux/sulaunchhelper2.py b/games/LoL/linux/sulaunchhelper2.py deleted file mode 100644 index 7ad455d..0000000 --- a/games/LoL/linux/sulaunchhelper2.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env python3 - -import frida -import socket -import ssl -import psutil -import time -import subprocess -import sys - -""" - requires psutil, frida (pip install psutil frida) - - suLaunchhelper2 will try to lauch the League of Legends client by instrumenting frida (https://frida.re/) - This version requires sudo or `sysctl kernel.yama.ptrace_scope=0`. - It will launch frida directly from outside of wine. -""" - -WINEDUMP = 'winedump' # path to winedump, does not necessarily need to be the same version as wine - -SELECT_SIGNATURE = "'long', ['long', 'pointer', 'pointer', 'pointer'], 'stdcall'" # return type, [arg types...], abi - -sleep_time = 1 -timeout = 0 # 0 to disable - -hide_backtrace = True - - -class FridaWrapper(): - def __init__(self, process, module=None, export=None, position=None, signature=None): - self.session = None - self.process = process - self.module = module - self.export = export - self.position = position - self.signature = signature - - def get_location(self): - if self.module and self.export: - return f"Process.getModuleByName('{self.module}').getExportByName('{self.export}')" - if self.position and self.signature: - return f"new NativeFunction(ptr({hex(self.position)}), {self.signature})" - - def attach(self): - if self.session: return - self.session = frida.attach(self.process) - - - script = self.session.create_script(""" - Interceptor.attach( - """ + self.get_location() + """, { - onEnter: function(args) { - args[4].writeInt(0x0); - } - } - ); - """) - script.load() - - def detach(self): - if not self.session: return - script = self.session.create_script("Interceptor.detachAll();") - script.load() - self.session.detach() - self.session = None - - def attached(self): - return self.session is not None - -class TimeoutException(Exception): - pass - -class Process: - def __init__(self, name, internal=None): - self.name = name - self.internal = internal or name - self.process = None - - def find(self): - if p := find_process_by_name(self.internal): - self.process = p - return True - return False - - def wait_for(self, tsleep=1, timeout=0): - start = time.time() - while not self.find(): - time.sleep(sleep_time) - if timeout and time.time() - start > timeout: - raise TimeoutException(f'Timeout while waiting for {self.name}') - - def __repr__(self): - return self.name - -class Symbol: - def __init__(self, offset, position, name): - self.offset = offset if type(offset) == int else int(offset, 16) - self.position = int(position) - self.name = name - - def __repr__(self): - return f'Symbol(offset={hex(self.offset)}, position={self.position}, name=\'{self.name}\')' - -def check_ssl_connect(host, port, verify=True): - ctx = ssl.create_default_context() - if not verify: - ctx.check_hostname = False - ctx.verify_mode = ssl.CERT_NONE - try: - with socket.create_connection((host, port)) as sock: - with ctx.wrap_socket(sock) as ssock: - return True - except: - return False - -def find_process_by_name(name): - for p in psutil.process_iter(attrs=['pid', 'name']): - if p.info['name'] == name: - return p - -def find_section(proc, name): - for m in proc.memory_maps(grouped=False): - if m.path.lower().endswith(name.lower()): - return int(m.addr.split('-')[0], 16), m - -def get_dll_exports(dll): - exports = subprocess.run(['winedump', '-j', 'export', dll], stdout=subprocess.PIPE).stdout - exports = str(exports, 'utf-8') - exports = exports.split('\n') - exports = exports[19:-3] # everything before this is just the pretext - exports = [Symbol(sym[0], sym[1], sym[2]) for sym in [e.strip().split() for e in exports]] - return exports - -def find_dll_export(dll, export): - exports = get_dll_exports(dll) - for e in exports: - if e.name == export: - return e - -if __name__ == '__main__': - # hide backtrace - if hide_backtrace: - sys.tracebacklimit = None - - rclient = Process('RiotClientServices.exe', 'RiotClientServi') - lclient = Process('LeagueClient.exe') - lclientux = Process('LeagueClientUx.exe') - - # Wait for RiotClientServices.exe - print(f'Waiting for {rclient}') - rclient.wait_for(tsleep=sleep_time, timeout=timeout) - print(f'Found {rclient}: pid {rclient.process.pid}') - - base, module = find_section(rclient.process, 'ws2_32.dll') - exp_select = find_dll_export(module.path, 'select') - - # Wait for LeagueClient.exe - print(f'Waiting for {lclient}') - lclient.wait_for(tsleep=sleep_time, timeout=timeout) - print(f'Found {lclient}: pid {lclient.process.pid}') - - f = FridaWrapper(rclient.process.pid, position = base + exp_select.offset, signature = SELECT_SIGNATURE) - - # Wait for LeagueClientUx.exe - print(f'Waiting for {lclientux}') - start = time.time() - while not lclientux.find(): - if not f.attached(): - print('Attaching...') - f.attach() - time.sleep(sleep_time) - if timeout and time.time() - start > timeout: - f.detach() - raise TimeoutException(f'Timeout while waiting for {lclientux}') - print(f'Found {lclientux}: pid {lclientux.process.pid}') - - # Find app-port - port_xarg = next(x for x in lclientux.process.cmdline() if '--app-port=' in x) - port = port_xarg.split('=')[1] - - # Wait for SSL response on app-port - print(f'Waiting for port {port}') - start = time.time() - while not check_ssl_connect('127.0.0.1', port, verify=False): - if not f.attached(): - print('Attaching...') - f.attach() - time.sleep(sleep_time) - if timeout and time.time() - start > timeout: - f.detach() - raise TimeoutException(f'Timeout while waiting for SSL response') - - if f.attached(): - print('Detaching...') - f.detach() - else: - print('Nothing to do') - - print('Done') \ No newline at end of file diff --git a/games/LoL/linux/sulaunchhelper2.sh b/games/LoL/linux/sulaunchhelper2.sh index cada89e..ccf9042 100755 --- a/games/LoL/linux/sulaunchhelper2.sh +++ b/games/LoL/linux/sulaunchhelper2.sh @@ -3,7 +3,6 @@ # Because this is for Lutris pre-launch script, you're required to have both sulaunchhelper2.py and syscall_check.sh # present in the game' wineprefix -SCC_SH='syscall_check.sh' SULH_PY='sulaunchhelper2.py' dialog() { @@ -11,12 +10,6 @@ dialog() { } own_dir="$(realpath .)" -# try to call syscall_check.sh -if ! [ -x "${own_dir}/${SCC_SH}" ]; then - dialog "Please place this script into the same directory as '${SCC_SH}'!" -else - sh "${own_dir}/${SCC_SH}" -fi echo "Trying to check for Frida kernel argument..." if [ "$(cat /proc/sys/kernel/yama/ptrace_scope)" -ne 0 ]; then