NonSteamLaunchers-On-Steam-.../NSLGameScanner.py

1469 lines
62 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
2024-02-12 06:27:02 +01:00
import os, re
import json
import shutil
import binascii
2024-01-25 18:41:04 +01:00
import ctypes
import gzip
import zipfile
import time
import sys
import subprocess
import sqlite3
2024-11-05 14:35:35 +01:00
import csv
from urllib.request import urlopen
from urllib.request import urlretrieve
2024-08-08 12:34:47 +02:00
from urllib.parse import quote
from base64 import b64encode
import xml.etree.ElementTree as ET
2024-02-29 14:25:01 +01:00
2024-08-04 09:31:50 +02:00
# Check the value of the DBUS_SESSION_BUS_ADDRESS environment variable
dbus_address = os.environ.get('DBUS_SESSION_BUS_ADDRESS')
if not dbus_address or not dbus_address.startswith('unix:path='):
# Set the value of the DBUS_SESSION_BUS_ADDRESS environment variable
dbus_address = f'unix:path=/run/user/{os.getuid()}/bus'
os.environ['DBUS_SESSION_BUS_ADDRESS'] = dbus_address
# Path to the env_vars file
env_vars_path = f"{os.environ['HOME']}/.config/systemd/user/env_vars"
# Check if the env_vars file exists
if not os.path.exists(env_vars_path):
print(f"Error: {env_vars_path} does not exist.")
sys.exit(1)
# Read variables from the file
with open(env_vars_path, 'r') as f:
lines = f.readlines()
for line in lines:
if line.startswith('export '):
line = line[7:] # Remove 'export '
2024-02-06 10:20:00 +01:00
name, value = line.strip().split('=', 1)
os.environ[name] = value
# Store the value of separate_appids before deleting it
separate_appids = None
# Delete env_vars entries for Chrome shortcuts so that they're only added once
with open(env_vars_path, 'w') as f:
for line in lines:
if 'export separate_appids=false' in line:
separate_appids = line.split('=')[1].strip()
if line.find('chromelaunchoptions') == -1 and line.find('websites_str') == -1:
f.write(line)
# Variables from NonSteamLaunchers.sh
steamid3 = os.environ['steamid3']
logged_in_home = os.environ['logged_in_home']
compat_tool_name = os.environ['compat_tool_name']
2024-02-06 10:20:00 +01:00
python_version = os.environ['python_version']
#Scanner Variables
epic_games_launcher = os.environ.get('epic_games_launcher', '')
ubisoft_connect_launcher = os.environ.get('ubisoft_connect_launcher', '')
2024-01-23 23:30:55 +01:00
ea_app_launcher = os.environ.get('ea_app_launcher', '')
2024-01-26 10:18:26 +01:00
gog_galaxy_launcher = os.environ.get('gog_galaxy_launcher', '')
2024-02-02 05:51:08 +01:00
bnet_launcher = os.environ.get('bnet_launcher', '')
amazon_launcher = os.environ.get('amazon_launcher', '')
itchio_launcher = os.environ.get('itchio_launcher', '')
legacy_launcher = os.environ.get('legacy_launcher', '')
2024-02-06 10:20:00 +01:00
#Variables of the Launchers
# Define the path of the Launchers
epicshortcutdirectory = os.environ.get('epicshortcutdirectory')
gogshortcutdirectory = os.environ.get('gogshortcutdirectory')
uplayshortcutdirectory = os.environ.get('uplayshortcutdirectory')
battlenetshortcutdirectory = os.environ.get('battlenetshortcutdirectory')
eaappshortcutdirectory = os.environ.get('eaappshortcutdirectory')
amazonshortcutdirectory = os.environ.get('amazonshortcutdirectory')
itchioshortcutdirectory = os.environ.get('itchioshortcutdirectory')
legacyshortcutdirectory = os.environ.get('legacyshortcutdirectory')
humbleshortcutdirectory = os.environ.get('humbleshortcutdirectory')
indieshortcutdirectory = os.environ.get('indieshortcutdirectory')
rockstarshortcutdirectory = os.environ.get('rockstarshortcutdirectory')
glyphshortcutdirectory = os.environ.get('glyphshortcutdirectory')
psplusshortcutdirectory = os.environ.get('psplusshortcutdirectory')
2024-06-21 14:01:55 +02:00
vkplayshortcutdirectory = os.environ.get('vkplayshortcutdirectory')
2024-07-03 14:04:46 +02:00
hoyoplayshortcutdirectory = os.environ.get('hoyoplayshortcutfirectory')
nexonshortcutdirectory = os.environ.get('nexonshortcutdirectory')
repaireaappshortcutdirectory = os.environ.get('repaireaappshortcutdirectory')
2024-02-06 10:20:00 +01:00
#Streaming
chromedirectory = os.environ.get('chromedirectory')
websites_str = os.environ.get('custom_websites_str')
custom_websites = websites_str.split(', ') if websites_str else []
# Define the parent folder
parent_folder = f"{logged_in_home}/.config/systemd/user/Modules"
2024-02-12 06:16:50 +01:00
sys.path.insert(0, os.path.expanduser(f"{logged_in_home}/.config/systemd/user/Modules"))
2024-02-11 02:18:13 +01:00
print(sys.path)
# Now that the requests module has been downloaded, you can import it
sys.path.insert(0, parent_folder)
import requests
2024-02-10 23:28:17 +01:00
import vdf
from steamgrid import SteamGridDB
print(sys.path)
2024-02-11 02:18:13 +01:00
#Set Up nslgamescanner.service
# Define the paths
service_path = f"{logged_in_home}/.config/systemd/user/nslgamescanner.service"
# Define the service file content
service_content = f"""
[Unit]
Description=NSL Game Scanner
[Service]
ExecStart=/usr/bin/python3 '{logged_in_home}/.config/systemd/user/NSLGameScanner.py'
Restart=always
RestartSec=10
StartLimitBurst=40
StartLimitInterval=240
[Install]
WantedBy=default.target
"""
# Check if the service file already exists
if not os.path.exists(service_path):
# Create the service file
with open(service_path, 'w') as f:
f.write(service_content)
print("Service file created.")
# Check if the service is already running
result = subprocess.run(['systemctl', '--user', 'is-active', 'nslgamescanner.service'], stdout=subprocess.PIPE)
if result.stdout.decode('utf-8').strip() != 'active':
# Reload the systemd manager configuration
subprocess.run(['systemctl', '--user', 'daemon-reload'])
# Enable the service to start on boot
subprocess.run(['systemctl', '--user', 'enable', 'nslgamescanner.service'])
# Start the service immediately
subprocess.run(['systemctl', '--user', 'start', 'nslgamescanner.service'])
print("Service started.")
else:
print("Service is already running.")
#Code
def get_steam_shortcut_id(exe_path, display_name):
2024-01-25 18:41:04 +01:00
unique_id = "".join([exe_path, display_name])
id_int = binascii.crc32(str.encode(unique_id)) | 0x80000000
signed = ctypes.c_int(id_int)
# print(f"Signed ID: {signed.value}")
return signed.value
2024-01-25 18:41:04 +01:00
def get_unsigned_shortcut_id(signed_shortcut_id):
unsigned = ctypes.c_uint(signed_shortcut_id)
# print(f"Unsigned ID: {unsigned.value}")
return unsigned.value
# Initialize an empty dictionary to serve as the cache
api_cache = {}
2024-06-23 22:02:18 +02:00
#API KEYS FOR NONSTEAMLAUNCHER USE ONLY
2024-12-11 15:20:31 +01:00
BASE_URL = 'https://nonsteamlaunchers.onrender.com/api'
2024-01-23 23:30:55 +01:00
#GLOBAL VARS
created_shortcuts = []
new_shortcuts_added = False
2024-01-25 18:41:04 +01:00
shortcuts_updated = False
2024-01-23 23:30:55 +01:00
shortcut_id = None # Initialize shortcut_id
decky_shortcuts = {}
gridp64 = ""
grid64 = ""
logo64 = ""
hero64 = ""
2024-01-23 23:30:55 +01:00
2024-06-21 16:57:39 +02:00
2024-06-22 21:45:30 +02:00
def create_empty_shortcuts():
return {'shortcuts': {}}
def write_shortcuts_to_file(shortcuts_file, shortcuts):
with open(shortcuts_file, 'wb') as file:
file.write(vdf.binary_dumps(shortcuts))
os.chmod(shortcuts_file, 0o755)
# Define the path to the shortcuts file
shortcuts_file = f"{logged_in_home}/.steam/root/userdata/{steamid3}/config/shortcuts.vdf"
# Check if the file exists
if os.path.exists(shortcuts_file):
# If the file is not executable, write the shortcuts dictionary and make it executable
if not os.access(shortcuts_file, os.X_OK):
print("The file is not executable. Writing an empty shortcuts dictionary and making it executable.")
shortcuts = create_empty_shortcuts()
write_shortcuts_to_file(shortcuts_file, shortcuts)
else:
# Load the existing shortcuts
with open(shortcuts_file, 'rb') as file:
try:
shortcuts = vdf.binary_loads(file.read())
except vdf.VDFError as e:
2024-06-23 08:44:05 +02:00
print(f"Error reading file: {e}. The file might be corrupted or unreadable.")
print("Exiting the program. Please check the shortcuts.vdf file.")
sys.exit(1)
2024-06-22 21:45:30 +02:00
else:
print("The shortcuts.vdf file does not exist.")
sys.exit(1)
2024-06-21 16:57:39 +02:00
2024-06-23 08:44:05 +02:00
2024-01-23 23:30:55 +01:00
# Open the config.vdf file
with open(f"{logged_in_home}/.steam/root/config/config.vdf", 'r') as file:
config_data = vdf.load(file)
2024-06-21 16:29:48 +02:00
2024-01-23 23:30:55 +01:00
def get_sgdb_art(game_id, app_id):
global grid64
global gridp64
global logo64
global hero64
print(f"Downloading icons artwork...")
2024-08-08 06:29:31 +02:00
download_artwork(game_id, "icons", app_id)
print(f"Downloading logos artwork...")
2024-08-08 06:29:31 +02:00
logo64 = download_artwork(game_id, "logos", app_id)
print(f"Downloading heroes artwork...")
2024-08-08 06:29:31 +02:00
hero64 = download_artwork(game_id, "heroes", app_id)
print("Downloading grids artwork of size 600x900...")
2024-08-08 06:29:31 +02:00
gridp64 = download_artwork(game_id, "grids", app_id, "600x900")
print("Downloading grids artwork of size 920x430...")
2024-08-08 06:29:31 +02:00
grid64 = download_artwork(game_id, "grids", app_id, "920x430")
2024-02-17 04:26:30 +01:00
2024-08-08 06:29:31 +02:00
def download_artwork(game_id, art_type, shortcut_id, dimensions=None):
if game_id is None:
print("Invalid game ID. Skipping download.")
return
2024-02-17 04:26:30 +01:00
cache_key = (game_id, art_type, dimensions)
if dimensions is not None:
filename = get_file_name(art_type, shortcut_id, dimensions)
else:
filename = get_file_name(art_type, shortcut_id)
file_path = f"{logged_in_home}/.steam/root/userdata/{steamid3}/config/grid/{filename}"
2024-02-17 04:26:30 +01:00
2024-02-01 13:49:45 +01:00
directory = os.path.dirname(file_path)
if not os.path.exists(directory):
os.makedirs(directory)
2024-02-17 04:26:30 +01:00
if os.path.exists(file_path):
print(f"Artwork for {game_id} already exists. Skipping download.")
with open(file_path, 'rb') as image_file:
return b64encode(image_file.read()).decode('utf-8')
if cache_key in api_cache:
data = api_cache[cache_key]
else:
try:
2024-08-08 06:29:31 +02:00
print(f"Game ID: {game_id}")
url = f"{BASE_URL}/{art_type}/game/{game_id}"
if dimensions:
url += f"?dimensions={dimensions}"
2024-08-08 06:29:31 +02:00
print(f"Request URL: {url}")
response = requests.get(url)
response.raise_for_status()
data = response.json()
2024-08-08 06:29:31 +02:00
print(f"Response data: {data}")
api_cache[cache_key] = data
2024-08-08 06:29:31 +02:00
except Exception as e:
print(f"Error making API call: {e}")
2024-02-17 04:26:30 +01:00
api_cache[cache_key] = None
return
if data is None:
print(f"No data available for {game_id}. Skipping download.")
return
for artwork in data['data']:
image_url = artwork['thumb']
2024-08-08 06:29:31 +02:00
print(f"Downloading image from: {image_url}")
try:
response = requests.get(image_url, stream=True)
response.raise_for_status()
if response.status_code == 200:
with open(file_path, 'wb') as file:
file.write(response.content)
return b64encode(response.content).decode('utf-8')
except requests.exceptions.RequestException as e:
2024-01-19 03:53:00 +01:00
print(f"Error downloading image: {e}")
if art_type == 'icons':
2024-12-13 19:57:00 +01:00
download_artwork(game_id, 'icons', shortcut_id)
2024-02-17 04:26:30 +01:00
def get_game_id(game_name):
print(f"Searching for game ID for: {game_name}")
try:
2024-08-08 06:29:31 +02:00
encoded_game_name = quote(game_name)
url = f"{BASE_URL}/search/{encoded_game_name}"
print(f"Encoded game name: {encoded_game_name}")
print(f"Request URL: {url}")
response = requests.get(url)
response.raise_for_status()
data = response.json()
print(f"Response data: {data}")
if data['data']:
game_id = data['data'][0]['id']
print(f"Found game ID: {game_id}")
return game_id
print(f"No game ID found for game name: {game_name}")
return None
except Exception as e:
print(f"Error searching for game ID: {e}")
2024-08-08 06:29:31 +02:00
return None
def get_file_name(art_type, shortcut_id, dimensions=None):
singular_art_type = art_type.rstrip('s')
if art_type == 'icons':
if os.path.exists(f"{logged_in_home}/.steam/root/userdata/{steamid3}/config/grid/{shortcut_id}-{singular_art_type}.png"):
return f"{shortcut_id}-{singular_art_type}.png"
else:
return f"{shortcut_id}-{singular_art_type}.ico"
elif art_type == 'grids':
if dimensions == '600x900':
return f"{shortcut_id}p.png"
else:
return f"{shortcut_id}.png"
elif art_type == 'heroes':
return f"{shortcut_id}_hero.png"
elif art_type == 'logos':
return f"{shortcut_id}_logo.png"
else:
return f"{shortcut_id}.png"
def is_match(name1, name2):
if name1 and name2:
return name1.lower() in name2.lower() or name2.lower() in name1.lower()
else:
return False
2024-02-09 01:24:46 +01:00
# Add or update the proton compatibility settings
2024-11-05 14:35:35 +01:00
def add_compat_tool(app_id, launchoptions):
2024-01-25 18:41:04 +01:00
if 'CompatToolMapping' not in config_data['InstallConfigStore']['Software']['Valve']['Steam']:
config_data['InstallConfigStore']['Software']['Valve']['Steam']['CompatToolMapping'] = {}
print(f"CompatToolMapping key not found in config.vdf, creating.")
2024-11-08 10:43:39 +01:00
# Combine checks for 'chrome' and 'PROTONPATH'
if 'chrome' in launchoptions or 'PROTONPATH' in launchoptions:
if 'PROTONPATH' in launchoptions:
print("PROTONPATH found in launch options. Skipping compatibility tool update.")
2024-11-05 14:35:35 +01:00
return False
2024-01-25 18:41:04 +01:00
elif str(app_id) in config_data['InstallConfigStore']['Software']['Valve']['Steam']['CompatToolMapping']:
config_data['InstallConfigStore']['Software']['Valve']['Steam']['CompatToolMapping'][str(app_id)]['name'] = f'{compat_tool_name}'
config_data['InstallConfigStore']['Software']['Valve']['Steam']['CompatToolMapping'][str(app_id)]['config'] = ''
config_data['InstallConfigStore']['Software']['Valve']['Steam']['CompatToolMapping'][str(app_id)]['priority'] = '250'
print(f"Updated CompatToolMapping entry for appid: {app_id}")
return compat_tool_name
2024-01-23 23:30:55 +01:00
else:
2024-11-05 14:35:35 +01:00
# Skip if the shortcut has already been processed by UMU
if app_id in umu_processed_shortcuts:
print(f"CompatTool update skipped for {app_id} because it was already processed by UMU.")
return None
2024-01-25 18:41:04 +01:00
config_data['InstallConfigStore']['Software']['Valve']['Steam']['CompatToolMapping'][str(app_id)] = {'name': f'{compat_tool_name}', 'config': '', 'priority': '250'}
print(f"Created new CompatToolMapping entry for appid: {app_id}")
return compat_tool_name
2024-11-08 10:43:39 +01:00
def check_if_shortcut_exists(shortcut_id, display_name, exe_path, start_dir, launch_options):
# Check if the game already exists in the shortcuts using the id
2024-01-25 18:41:04 +01:00
if any(s.get('appid') == shortcut_id for s in shortcuts['shortcuts'].values()):
2024-02-09 01:24:46 +01:00
print(f"Existing shortcut found based on shortcut ID for game {display_name}. Skipping creation.")
return True
# Check if the game already exists in the shortcuts using the fields (probably unnecessary)
if any(s.get('appname') == display_name and s.get('exe') == exe_path and s.get('StartDir') == start_dir and s.get('LaunchOptions') == launch_options for s in shortcuts['shortcuts'].values()):
2024-02-09 01:24:46 +01:00
print(f"Existing shortcut found based on matching fields for game {display_name}. Skipping creation.")
return True
if any(s.get('AppName') == display_name and s.get('Exe') == exe_path and s.get('StartDir') == start_dir and s.get('LaunchOptions') == launch_options for s in shortcuts['shortcuts'].values()):
print(f"Existing shortcut found based on matching fields for game {display_name}. Skipping creation.")
return True
#End of Code
2024-11-05 14:35:35 +01:00
2024-02-06 10:20:00 +01:00
#Start of Refactoring code from the .sh file
sys.path.insert(0, os.path.expanduser(f"{logged_in_home}/Downloads/NonSteamLaunchersInstallation/lib/python{python_version}/site-packages"))
print(sys.path)
# Create an empty dictionary to store the app IDs
app_ids = {}
2024-02-09 01:24:46 +01:00
2024-07-16 09:57:46 +02:00
def get_next_available_key(shortcuts):
key = 0
while str(key) in shortcuts['shortcuts']:
key += 1
return str(key)
2024-07-15 13:34:14 +02:00
2024-02-06 10:20:00 +01:00
def create_new_entry(shortcutdirectory, appname, launchoptions, startingdir):
2024-02-09 01:24:46 +01:00
global new_shortcuts_added
global shortcuts_updated
global created_shortcuts
global decky_shortcuts
global grid64
global gridp64
global logo64
global hero64
2024-07-15 13:34:14 +02:00
global counter # Add this line to access the counter variable
2024-02-06 10:20:00 +01:00
# Check if the launcher is installed
if not shortcutdirectory or not appname or not launchoptions or not startingdir:
2024-02-06 10:20:00 +01:00
print(f"{appname} is not installed. Skipping.")
return
2024-02-09 01:24:46 +01:00
exe_path = f"{shortcutdirectory}"
2024-02-06 10:20:00 +01:00
signed_shortcut_id = get_steam_shortcut_id(exe_path, appname)
2024-02-09 12:00:01 +01:00
unsigned_shortcut_id = get_unsigned_shortcut_id(signed_shortcut_id)
2024-11-05 14:35:35 +01:00
# **Intercept and modify the shortcut based on UMU data**
exe_path, startingdir, launchoptions = modify_shortcut_for_umu(appname, exe_path, launchoptions, startingdir)
# Check if the shortcut has already been processed by UMU and skip compat tool if so
if unsigned_shortcut_id in umu_processed_shortcuts:
print(f"Skipping compatibility tool for {appname}, as it has already been processed by UMU.")
compatTool = None # Skip adding compatibility tool
else:
compatTool = add_compat_tool(unsigned_shortcut_id, launchoptions)
2024-06-23 06:10:03 +02:00
# Only store the app ID for specific launchers
if appname in ['Epic Games', 'Gog Galaxy', 'Ubisoft Connect', 'Battle.net', 'EA App', 'Amazon Games', 'itch.io', 'Legacy Games', 'Humble Bundle', 'IndieGala Client', 'Rockstar Games Launcher', 'Glyph', 'Playstation Plus', 'VK Play', 'HoYoPlay', 'Nexon Launcher']:
2024-06-23 06:10:03 +02:00
app_ids[appname] = unsigned_shortcut_id
2024-02-09 01:24:46 +01:00
# Check if the game already exists in the shortcuts
2024-02-06 10:20:00 +01:00
if check_if_shortcut_exists(signed_shortcut_id, appname, exe_path, startingdir, launchoptions):
2024-02-09 01:24:46 +01:00
# Check if proton needs applying or updating
if add_compat_tool(unsigned_shortcut_id, launchoptions):
2024-02-06 10:20:00 +01:00
shortcuts_updated = True
return
2024-08-09 06:36:56 +02:00
# Skip artwork download for specific shortcuts
if appname not in ['NonSteamLaunchers', 'Repair EA App']:
# Get artwork
game_id = get_game_id(appname)
if game_id is not None:
get_sgdb_art(game_id, unsigned_shortcut_id)
2024-11-05 14:35:35 +01:00
# Create a new entry for the Steam shortcut, only adding the compat tool if it's not processed by UMU
2024-02-06 10:20:00 +01:00
new_entry = {
'appid': str(signed_shortcut_id),
'appname': appname,
'exe': exe_path,
'StartDir': startingdir,
'icon': f"{logged_in_home}/.steam/root/userdata/{steamid3}/config/grid/{get_file_name('icons', unsigned_shortcut_id)}",
2024-06-23 08:30:45 +02:00
'ShortcutPath': "",
2024-02-06 10:20:00 +01:00
'LaunchOptions': launchoptions,
2024-06-23 08:30:45 +02:00
'IsHidden': 0,
'AllowDesktopConfig': 1,
'AllowOverlay': 1,
'OpenVR': 0,
'Devkit': 0,
'DevkitGameID': "",
'DevkitOverrideAppID': 0,
'LastPlayTime': 0,
'FlatpakAppID': "",
'tags': {
'0': 'NonSteamLaunchers'
}
2024-02-06 10:20:00 +01:00
}
2024-11-05 14:35:35 +01:00
# Create the decky entry, only including the compatTool if not processed by UMU
decky_entry = {
'appname': appname,
'exe': exe_path,
'StartDir': startingdir,
'icon': f"{logged_in_home}/.steam/root/userdata/{steamid3}/config/grid/{get_file_name('icons', unsigned_shortcut_id)}",
'LaunchOptions': launchoptions,
2024-11-05 14:35:35 +01:00
'CompatTool': compatTool, # This will be None if UMU has processed the shortcut
2024-02-28 12:27:24 +01:00
'WideGrid': grid64,
'Grid': gridp64,
'Hero': hero64,
'Logo': logo64,
}
2024-11-05 14:35:35 +01:00
2024-02-09 01:24:46 +01:00
# Add the new entry to the shortcuts dictionary and add proton
2024-07-16 09:57:46 +02:00
key = get_next_available_key(shortcuts)
shortcuts['shortcuts'][key] = new_entry
2024-02-06 10:20:00 +01:00
print(f"Added new entry for {appname} to shortcuts.")
new_shortcuts_added = True
2024-02-09 01:24:46 +01:00
created_shortcuts.append(appname)
2024-11-05 14:35:35 +01:00
# Mark it as processed by UMU (if it wasn't already processed)
if compatTool is not None:
umu_processed_shortcuts[unsigned_shortcut_id] = True
2024-07-16 09:57:46 +02:00
add_compat_tool(unsigned_shortcut_id, launchoptions)
2024-07-15 13:34:14 +02:00
2024-11-05 14:35:35 +01:00
# UMU-related functions
umu_processed_shortcuts = {}
CSV_URL = "https://raw.githubusercontent.com/Open-Wine-Components/umu-database/main/umu-database.csv"
# Global variable to store CSV data
csv_data = []
def fetch_and_parse_csv():
global csv_data
try:
response = requests.get(CSV_URL)
response.raise_for_status() # Raise an HTTPError for bad responses
csv_data = [row for row in csv.DictReader(response.text.splitlines())]
print("Successfully fetched and parsed CSV data.")
except requests.exceptions.RequestException as e:
print(f"Error fetching UMU data: {e}")
return csv_data
def list_all_entries():
global csv_data
if not csv_data:
csv_data = fetch_and_parse_csv()
return csv_data
def extract_umu_id_from_launch_options(launchoptions):
if 'STEAM_COMPAT_DATA_PATH=' not in launchoptions:
return None
# EA
match = re.search(r'offerIds=(\d+)', launchoptions)
if match:
return match.group(1)
# Amazon
match = re.search(r'(amzn1\.adg\.product\.\S+)', launchoptions)
if match:
return match.group(1).rstrip("'")
# Epic
match = re.search(r'com\.epicgames\.launcher://apps/(\w+)[?&]', launchoptions)
if match:
return match.group(1).lower() if not match.group(1).isdigit() else match.group(1)
# Ubisoft
match = re.search(r'uplay://launch/(\d+)/\d+', launchoptions)
if match:
return match.group(1)
# GOG
match = re.search(r'/gameId=(\d+)', launchoptions)
if match:
return match.group(1)
return None
def extract_base_path(launchoptions):
match = re.search(r'STEAM_COMPAT_DATA_PATH="([^"]+)"', launchoptions)
if match:
return match.group(1)
raise ValueError("STEAM_COMPAT_DATA_PATH not found in launch options")
def modify_shortcut_for_umu(appname, exe, launchoptions, startingdir):
# Skip processing if STEAM_COMPAT_DATA_PATH is not present
if 'STEAM_COMPAT_DATA_PATH=' not in launchoptions:
print(f"Launch options for {appname} do not contain STEAM_COMPAT_DATA_PATH. Skipping modification.")
return exe, startingdir, launchoptions
codename = extract_umu_id_from_launch_options(launchoptions)
if not codename:
print(f"No codename found in launch options for {appname}. Trying to match appname.")
entries = list_all_entries()
if not entries:
print(f"No entries found in UMU database. Skipping modification for {appname}.")
return exe, startingdir, launchoptions
if not codename:
for entry in entries:
if entry.get('TITLE') and entry['TITLE'].lower() == appname.lower():
codename = entry['CODENAME']
break
if codename:
for entry in entries:
if entry['CODENAME'] == codename:
umu_id = entry['UMU_ID'].replace("umu-", "") # Remove the "umu-" prefix
base_path = extract_base_path(launchoptions)
new_exe = f'"{logged_in_home}/bin/umu-run" {exe}'
new_start_dir = f'"{logged_in_home}/bin/"'
# Update only the launchoptions part for different game types
updated_launch = launchoptions
if "origin2://game/launch?offerIds=" in launchoptions:
2024-11-08 10:17:37 +01:00
updated_launch = f'"origin2://game/launch?offerIds={codename}"'
2024-11-05 14:35:35 +01:00
elif "amazon-games://play/amzn1.adg.product." in launchoptions:
2024-11-08 10:17:37 +01:00
updated_launch = f"-'amazon-games://play/{codename}'"
2024-11-05 14:35:35 +01:00
elif "com.epicgames.launcher://apps/" in launchoptions:
2024-11-08 10:17:37 +01:00
updated_launch = f"-'com.epicgames.launcher://apps/{codename}?action=launch&silent=true'"
2024-11-05 14:35:35 +01:00
elif "uplay://launch/" in launchoptions:
2024-11-08 10:17:37 +01:00
updated_launch = f'"uplay://launch/{codename}/0"'
2024-11-05 14:35:35 +01:00
elif "/command=runGame /gameId=" in launchoptions:
updated_launch = f'/command=runGame /gameId={codename} /path={launchoptions.split("/path=")[1]}'
new_launch_options = (
f'STEAM_COMPAT_DATA_PATH="{base_path}" '
f'WINEPREFIX="{base_path}pfx" '
f'GAMEID="{umu_id}" '
f'PROTONPATH="{logged_in_home}/.steam/root/compatibilitytools.d/{compat_tool_name}" '
f'%command% {updated_launch}'
)
umu_processed_shortcuts[umu_id] = True
return new_exe, new_start_dir, new_launch_options
print(f"No UMU entry found for {appname}. Skipping modification.")
return exe, startingdir, launchoptions
2024-02-06 10:20:00 +01:00
create_new_entry(os.environ.get('epicshortcutdirectory'), 'Epic Games', os.environ.get('epiclaunchoptions'), os.environ.get('epicstartingdir'))
2024-09-29 07:15:52 +02:00
create_new_entry(os.environ.get('gogshortcutdirectory'), 'GOG Galaxy', os.environ.get('goglaunchoptions'), os.environ.get('gogstartingdir'))
2024-02-06 10:20:00 +01:00
create_new_entry(os.environ.get('uplayshortcutdirectory'), 'Ubisoft Connect', os.environ.get('uplaylaunchoptions'), os.environ.get('uplaystartingdir'))
create_new_entry(os.environ.get('battlenetshortcutdirectory'), 'Battle.net', os.environ.get('battlenetlaunchoptions'), os.environ.get('battlenetstartingdir'))
create_new_entry(os.environ.get('eaappshortcutdirectory'), 'EA App', os.environ.get('eaapplaunchoptions'), os.environ.get('eaappstartingdir'))
create_new_entry(os.environ.get('amazonshortcutdirectory'), 'Amazon Games', os.environ.get('amazonlaunchoptions'), os.environ.get('amazonstartingdir'))
create_new_entry(os.environ.get('itchioshortcutdirectory'), 'itch.io', os.environ.get('itchiolaunchoptions'), os.environ.get('itchiostartingdir'))
create_new_entry(os.environ.get('legacyshortcutdirectory'), 'Legacy Games', os.environ.get('legacylaunchoptions'), os.environ.get('legacystartingdir'))
create_new_entry(os.environ.get('humbleshortcutdirectory'), 'Humble Bundle', os.environ.get('humblelaunchoptions'), os.environ.get('humblestartingdir'))
create_new_entry(os.environ.get('indieshortcutdirectory'), 'IndieGala Client', os.environ.get('indielaunchoptions'), os.environ.get('indiestartingdir'))
create_new_entry(os.environ.get('rockstarshortcutdirectory'), 'Rockstar Games Launcher', os.environ.get('rockstarlaunchoptions'), os.environ.get('rockstarstartingdir'))
create_new_entry(os.environ.get('glyphshortcutdirectory'), 'Glyph', os.environ.get('glyphlaunchoptions'), os.environ.get('glyphstartingdir'))
create_new_entry(os.environ.get('psplusshortcutdirectory'), 'Playstation Plus', os.environ.get('pspluslaunchoptions'), os.environ.get('psplusstartingdir'))
2024-06-21 14:01:55 +02:00
create_new_entry(os.environ.get('vkplayshortcutdirectory'), 'VK Play', os.environ.get('vkplaylaunchoptions'), os.environ.get('vkplaystartingdir'))
2024-07-03 14:04:46 +02:00
create_new_entry(os.environ.get('hoyoplayshortcutdirectory'), 'HoYoPlay', os.environ.get('hoyoplaylaunchoptions'), os.environ.get('hoyoplaystartingdir'))
create_new_entry(os.environ.get('nexonshortcutdirectory'), 'Nexon Launcher', os.environ.get('nexonlaunchoptions'), os.environ.get('nexonstartingdir'))
create_new_entry(os.environ.get('repaireaappshortcutdirectory'), 'Repair EA App', os.environ.get('repaireaapplaunchoptions'), os.environ.get('repaireaappstartingdir'))
2024-02-06 10:20:00 +01:00
create_new_entry(os.environ.get('chromedirectory'), 'Xbox Game Pass', os.environ.get('xboxchromelaunchoptions'), os.environ.get('chrome_startdir'))
create_new_entry(os.environ.get('chromedirectory'), 'GeForce Now', os.environ.get('geforcechromelaunchoptions'), os.environ.get('chrome_startdir'))
2024-03-06 18:02:19 +01:00
create_new_entry(os.environ.get('chromedirectory'), 'Netflix', os.environ.get('netflixchromelaunchoptions'), os.environ.get('chrome_startdir'))
2024-02-06 10:20:00 +01:00
create_new_entry(os.environ.get('chromedirectory'), 'Hulu', os.environ.get('huluchromelaunchoptions'), os.environ.get('chrome_startdir'))
create_new_entry(os.environ.get('chromedirectory'), 'Disney+', os.environ.get('disneychromelaunchoptions'), os.environ.get('chrome_startdir'))
create_new_entry(os.environ.get('chromedirectory'), 'Amazon Prime Video', os.environ.get('amazonchromelaunchoptions'), os.environ.get('chrome_startdir'))
create_new_entry(os.environ.get('chromedirectory'), 'Youtube', os.environ.get('youtubechromelaunchoptions'), os.environ.get('chrome_startdir'))
create_new_entry(os.environ.get('chromedirectory'), 'Amazon Luna', os.environ.get('lunachromelaunchoptions'), os.environ.get('chrome_startdir'))
create_new_entry(os.environ.get('chromedirectory'), 'Twitch', os.environ.get('twitchchromelaunchoptions'), os.environ.get('chrome_startdir'))
2024-03-06 19:40:58 +01:00
create_new_entry(os.environ.get('chromedirectory'), 'Fortnite', os.environ.get('fortnitechromelaunchoptions'), os.environ.get('chrome_startdir'))
2024-10-11 11:10:10 +02:00
create_new_entry(os.environ.get('chromedirectory'), 'WebRcade', os.environ.get('webrcadechromelaunchoptions'), os.environ.get('chrome_startdir'))
create_new_entry(os.environ.get('chromedirectory'), 'WebRcade Editor', os.environ.get('webrcadeeditchromelaunchoptions'), os.environ.get('chrome_startdir'))
create_new_entry(os.environ.get('chromedirectory'), 'Plex', os.environ.get('plexchromelaunchoptions'), os.environ.get('chrome_startdir'))
create_new_entry(os.environ.get('chromedirectory'), 'Apple TV+', os.environ.get('applechromelaunchoptions'), os.environ.get('chrome_startdir'))
create_new_entry(os.environ.get('chromedirectory'), 'Crunchyroll', os.environ.get('crunchychromelaunchoptions'), os.environ.get('chrome_startdir'))
2024-02-06 10:20:00 +01:00
# Iterate over each custom website
for custom_website in custom_websites:
# Check if the custom website is not an empty string
if custom_website:
# Remove any leading or trailing spaces from the custom website URL
custom_website = custom_website.strip()
# Remove the 'http://' or 'https://' prefix and the 'www.' prefix, if present
clean_website = custom_website.replace('http://', '').replace('https://', '').replace('www.', '')
# Define a regular expression pattern to extract the game name from the URL
pattern = r'/games/([\w-]+)'
# Use the regular expression to search for the game name in the custom website URL
match = re.search(pattern, custom_website)
# Check if a match was found
if match:
# Extract the game name from the match object
game_name = match.group(1)
# Replace hyphens with spaces
game_name = game_name.replace('-', ' ')
# Capitalize the first letter of each word in the game name
game_name = game_name.title()
else:
# Use the entire URL as the entry name
game_name = clean_website
# Define the launch options for this website
chromelaunch_options = f'run --branch=stable --arch=x86_64 --command=/app/bin/chrome --file-forwarding com.google.Chrome @@u @@ --window-size=1280,800 --force-device-scale-factor=1.00 --device-scale-factor=1.00 --start-fullscreen https://{clean_website}/ --no-first-run --enable-features=OverlayScrollbar'
2024-02-06 10:20:00 +01:00
# Call the create_new_entry function for this website
create_new_entry(os.environ['chromedirectory'], game_name, chromelaunch_options, os.environ['chrome_startdir'])
#End of Creating Launcher Shortcuts
#Custom Shortcut for NSL
# Define the parameters for the new shortcut
nslshortcutdirectory = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/NonSteamLaunchers/\""
nslappname = "NonSteamLaunchers"
nsllaunchoptions = f"STEAM_COMPAT_DATA_PATH=\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/NonSteamLaunchers/\" %command%"
nslstartingdir = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/NonSteamLaunchers/\""
print(f"nslshortcutdirectory: {nslshortcutdirectory}") # Debug print
print(f"nslappname: {nslappname}") # Debug print
print(f"nsllaunchoptions: {nsllaunchoptions}") # Debug print
# Check if separate_appids is set to 'false'
if separate_appids == 'false':
print("separate_appids is set to 'false'. Creating new shortcut...") # Debug print
# Call the function to create the new shortcut and store the returned appid
appid = create_new_entry(nslshortcutdirectory, nslappname, nsllaunchoptions, nslstartingdir)
app_ids[nslappname] = appid
print(f"appid: {appid}") # Debug print
else:
print("separate_appids is not set to 'false'. Skipping shortcut creation.") # Debug print
2024-02-06 10:20:00 +01:00
# Iterate over each launcher in the app_ids dictionary
for launcher_name, appid in app_ids.items():
print(f"The app ID for {launcher_name} is {appid}")
2024-02-09 13:02:55 +01:00
# Get the app ID for the first launcher that the user chose to install
if app_ids:
appid = app_ids.get(launcher_name)
2024-06-22 13:23:54 +02:00
print(f"App ID for the chosen launcher: {appid}")
2024-02-09 12:27:03 +01:00
2024-06-22 13:23:54 +02:00
# Create User Friendly Symlinks for the launchers
2024-02-06 10:20:00 +01:00
# Define the path to the compatdata directory
compatdata_dir = f'{logged_in_home}/.local/share/Steam/steamapps/compatdata'
2024-06-22 13:23:54 +02:00
print(f"Compatdata directory: {compatdata_dir}")
2024-02-06 10:20:00 +01:00
# Define a dictionary of original folder names
folder_names = {
'Epic Games': 'EpicGamesLauncher',
'Gog Galaxy': 'GogGalaxyLauncher',
'Ubisoft Connect': 'UplayLauncher',
'Battle.net': 'Battle.netLauncher',
'EA App': 'TheEAappLauncher',
'Amazon Games': 'AmazonGamesLauncher',
'itch.io': 'itchioLauncher',
'Legacy Games': 'LegacyGamesLauncher',
'Humble Bundle': 'HumbleGamesLauncher',
'IndieGala Client': 'IndieGalaLauncher',
'Rockstar Games Launcher': 'RockstarGamesLauncher',
2024-06-22 11:29:04 +02:00
'Glyph': 'GlyphLauncher',
2024-02-06 10:20:00 +01:00
'Playstation Plus': 'PlaystationPlusLauncher',
'VK Play': 'VKPlayLauncher',
2024-07-03 14:04:46 +02:00
'HoYoPlay': 'HoYoPlayLauncher',
'Nexon Launcher': 'NexonLauncher',
}
2024-06-29 08:39:11 +02:00
2024-02-06 10:20:00 +01:00
# Iterate over each launcher in the folder_names dictionary
for launcher_name, folder in folder_names.items():
# Define the current path of the folder
current_path = os.path.join(compatdata_dir, folder)
2024-06-22 13:23:54 +02:00
print(f"Current path for {launcher_name}: {current_path}")
2024-02-06 10:20:00 +01:00
# Check if the folder exists
if os.path.exists(current_path):
print(f'{launcher_name}: {folder} exists')
# Get the app ID for this launcher from the app_id_to_name dictionary
appid = app_ids.get(launcher_name)
2024-06-22 13:23:54 +02:00
print(f"App ID for {launcher_name}: {appid}")
2024-06-22 11:24:08 +02:00
# If appid is not None, proceed with renaming and symlink creation
if appid is not None:
# Define the new path of the folder
new_path = os.path.join(compatdata_dir, str(appid))
2024-06-22 13:23:54 +02:00
print(f"New path for {launcher_name}: {new_path}")
2024-06-22 11:24:08 +02:00
# Check if the new path already exists
if os.path.exists(new_path):
2024-06-29 10:19:24 +02:00
print(f'{new_path} already exists. Skipping renaming and symlinking.')
else:
# Rename the folder
os.rename(current_path, new_path)
print(f"Renamed {current_path} to {new_path}")
# Define the path of the symbolic link
symlink_path = os.path.join(compatdata_dir, folder)
print(f"Symlink path for {launcher_name}: {symlink_path}")
# Create a symbolic link to the renamed folder
os.symlink(new_path, symlink_path)
print(f"Created symlink at {symlink_path} to {new_path}")
2024-06-22 10:18:04 +02:00
else:
2024-06-22 11:24:08 +02:00
print(f'App ID for {launcher_name} is not available yet.')
2024-02-06 10:20:00 +01:00
else:
print(f'{launcher_name}: {folder} does not exist')
2024-06-22 13:59:16 +02:00
2024-06-29 10:19:24 +02:00
# Define the appid for the custom shortcut
custom_app_id = 4206469918
print(f"App ID for the custom shortcut: {custom_app_id}")
2024-02-06 10:20:00 +01:00
# Check if the NonSteamLaunchers folder exists
2024-06-22 11:54:04 +02:00
non_steam_launchers_path = os.path.join(compatdata_dir, 'NonSteamLaunchers')
if os.path.exists(non_steam_launchers_path):
2024-06-22 13:23:54 +02:00
print("NonSteamLaunchers already exists at the expected path.")
2024-02-06 10:20:00 +01:00
# Define the current path of the NonSteamLaunchers folder
current_path = os.path.join(compatdata_dir, 'NonSteamLaunchers')
2024-06-22 13:23:54 +02:00
print(f"Current path for NonSteamLaunchers: {current_path}")
2024-02-06 10:20:00 +01:00
# Check if NonSteamLaunchers is already a symbolic link
if os.path.islink(current_path):
print('NonSteamLaunchers is already a symbolic link')
2024-06-22 12:36:57 +02:00
# Check if NonSteamLaunchers is a symlink to an appid folder
if os.readlink(current_path) != os.path.join(compatdata_dir, str(custom_app_id)):
2024-06-22 12:36:57 +02:00
print('NonSteamLaunchers is symlinked to a different folder')
# Remove the existing symbolic link
os.unlink(current_path)
print(f'Removed existing symlink at {current_path}')
# Create a symbolic link to the correct appid folder
os.symlink(os.path.join(compatdata_dir, str(custom_app_id)), current_path)
print(f'Created new symlink at {current_path} to {os.path.join(compatdata_dir, str(custom_app_id))}')
else:
print('NonSteamLaunchers is already correctly symlinked')
2024-02-06 10:20:00 +01:00
else:
2024-06-22 13:23:54 +02:00
print("NonSteamLaunchers is not a symbolic link.")
2024-06-22 12:45:57 +02:00
# Check if the current path exists
if os.path.exists(current_path):
2024-06-22 13:23:54 +02:00
print("NonSteamLaunchers exists at the current path.")
2024-06-22 12:45:57 +02:00
# Define the new path of the NonSteamLaunchers folder
new_path = os.path.join(compatdata_dir, str(custom_app_id))
2024-06-22 13:54:07 +02:00
print(f"New path for NonSteamLaunchers: {new_path}")
2024-02-06 10:20:00 +01:00
2024-06-22 13:36:20 +02:00
# Check if the new path already exists
if os.path.exists(new_path):
print(f'{new_path} already exists. Skipping renaming and symlinking.')
else:
# Move the NonSteamLaunchers folder to the new path
shutil.move(current_path, new_path)
print(f"Moved NonSteamLaunchers folder to {new_path}")
2024-02-06 10:20:00 +01:00
2024-06-22 13:36:20 +02:00
# Define the path of the symbolic link
symlink_path = os.path.join(compatdata_dir, 'NonSteamLaunchers')
2024-06-22 12:45:57 +02:00
2024-06-22 13:36:20 +02:00
# Create a symbolic link to the renamed NonSteamLaunchers folder
os.symlink(new_path, symlink_path)
print(f"Created symlink at {symlink_path} to {new_path}")
2024-06-22 12:45:57 +02:00
else:
print(f"The directory {current_path} does not exist. Skipping.")
2024-06-22 13:36:20 +02:00
2024-06-22 13:54:07 +02:00
2024-06-22 13:23:54 +02:00
#End of old refactored Code
2024-02-06 10:20:00 +01:00
2024-06-29 04:59:35 +02:00
# Print the existing shortcuts
print("Existing Shortcuts:")
for shortcut in shortcuts['shortcuts'].values():
if shortcut.get('appname') is None:
print(f"AppID for {shortcut.get('AppName')}: {shortcut.get('appid')}")
else:
print(f"AppID for {shortcut.get('appname')}: {shortcut.get('appid')}")
2024-02-06 10:20:00 +01:00
#Scanners
# Epic Games Scanner
item_dir = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{epic_games_launcher}/pfx/drive_c/ProgramData/Epic/EpicGamesLauncher/Data/Manifests/"
dat_file_path = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{epic_games_launcher}/pfx/drive_c/ProgramData/Epic/UnrealEngineLauncher/LauncherInstalled.dat"
if os.path.exists(dat_file_path) and os.path.exists(item_dir):
with open(dat_file_path, 'r') as file:
dat_data = json.load(file)
2024-08-19 03:20:59 +02:00
# Epic Game Scanner
for item_file in os.listdir(item_dir):
if item_file.endswith('.item'):
with open(os.path.join(item_dir, item_file), 'r') as file:
item_data = json.load(file)
# Initialize variables
display_name = item_data['DisplayName']
app_name = item_data['AppName']
exe_path = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{epic_games_launcher}/pfx/drive_c/Program Files (x86)/Epic Games/Launcher/Portal/Binaries/Win32/EpicGamesLauncher.exe\""
start_dir = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{epic_games_launcher}/pfx/drive_c/Program Files (x86)/Epic Games/Launcher/Portal/Binaries/Win32/\""
2024-11-02 07:21:36 +01:00
launch_options = f"STEAM_COMPAT_DATA_PATH=\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{epic_games_launcher}/\" %command% -'com.epicgames.launcher://apps/{app_name}?action=launch&silent=true'"
2024-08-19 03:20:59 +02:00
# Check if the game is still installed and if the LaunchExecutable is valid, not content-related, and is a .exe file
if item_data['LaunchExecutable'].endswith('.exe') and "Content" not in item_data['DisplayName'] and "Content" not in item_data['InstallLocation']:
for game in dat_data['InstallationList']:
if game['AppName'] == item_data['AppName']:
create_new_entry(exe_path, display_name, launch_options, start_dir)
else:
print("Epic Games Launcher data not found. Skipping Epic Games Scanner.")
2024-08-19 03:20:59 +02:00
# End of the Epic Games Scanner
# Ubisoft Connect Scanner
def getUplayGameInfo(folderPath, filePath):
# Get the game IDs from the folder
listOfFiles = os.listdir(folderPath)
2024-01-25 18:41:04 +01:00
uplay_ids = [re.findall(r'\d+', str(entry))[0] for entry in listOfFiles if re.findall(r'\d+', str(entry))]
# Parse the registry file
game_dict = {}
with open(filePath, 'r') as file:
2024-01-25 18:41:04 +01:00
uplay_id = None
game_name = None
uplay_install_found = False
for line in file:
2024-06-19 10:26:56 +02:00
line = line.replace("\\x2019", "")
if "Uplay Install" in line:
2024-01-25 18:41:04 +01:00
uplay_id = re.findall(r'Uplay Install (\d+)', line)
if uplay_id:
uplay_id = uplay_id[0]
game_name = None # Reset game_name
uplay_install_found = True
if "DisplayName" in line and uplay_install_found:
game_name = re.findall(r'\"(.+?)\"', line.split("=")[1])
if game_name:
game_name = game_name[0]
uplay_install_found = False
2024-06-19 10:26:56 +02:00
if uplay_id and game_name and uplay_id in uplay_ids:
2024-01-25 18:41:04 +01:00
game_dict[game_name] = uplay_id
uplay_id = None # Reset uplay_id
game_name = None # Reset game_name
return game_dict
# Define your paths
2024-01-19 13:21:02 +01:00
data_folder_path = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{ubisoft_connect_launcher}/pfx/drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/data/"
registry_file_path = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{ubisoft_connect_launcher}/pfx/system.reg"
# Check if the paths exist
2024-01-21 02:37:57 +01:00
if not os.path.exists(data_folder_path) or not os.path.exists(registry_file_path):
print("One or more paths do not exist.")
print("Ubisoft Connect game data not found. Skipping Ubisoft Games Scanner.")
else:
game_dict = getUplayGameInfo(data_folder_path, registry_file_path)
2024-01-25 18:41:04 +01:00
for game, uplay_id in game_dict.items():
if uplay_id:
launch_options = f"STEAM_COMPAT_DATA_PATH=\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{ubisoft_connect_launcher}/\" %command% \"uplay://launch/{uplay_id}/0\""
2024-01-26 22:36:59 +01:00
exe_path = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{ubisoft_connect_launcher}/pfx/drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/upc.exe\""
start_dir = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{ubisoft_connect_launcher}/pfx/drive_c/Program Files (x86)/Ubisoft/Ubisoft Game Launcher/\""
2024-02-09 01:24:46 +01:00
create_new_entry(exe_path, game, launch_options, start_dir)
# End of Ubisoft Game Scanner
2024-01-23 23:30:55 +01:00
# EA App Game Scanner
def get_ea_app_game_info(installed_games, game_directory_path):
2024-01-23 23:30:55 +01:00
game_dict = {}
for game in installed_games:
xml_file = ET.parse(f"{game_directory_path}{game}/__Installer/installerdata.xml")
xml_root = xml_file.getroot()
ea_ids = None
2024-01-23 23:30:55 +01:00
game_name = None
for content_id in xml_root.iter('contentID'):
ea_ids = content_id.text
break # Exit the loop after the first ID is found
for game_title in xml_root.iter('gameTitle'):
if game_name is None:
game_name = game_title.text
continue
for game_title in xml_root.iter('title'):
if game_name is None:
game_name = game_title.text
continue
if game_name is None:
game_name = game
if ea_ids: # Add the game's info to the dictionary if its ID was found in the folder
game_dict[game_name] = ea_ids
2024-01-23 23:30:55 +01:00
return game_dict
game_directory_path = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{ea_app_launcher}/pfx/drive_c/Program Files/EA Games/"
2024-01-23 23:30:55 +01:00
if not os.path.isdir(game_directory_path):
2024-01-24 05:01:36 +01:00
print("EA App game data not found. Skipping EA App Scanner.")
2024-01-23 23:30:55 +01:00
else:
installed_games = os.listdir(game_directory_path) # Get a list of game folders
game_dict = get_ea_app_game_info(installed_games, game_directory_path)
for game, ea_ids in game_dict.items():
launch_options = f"STEAM_COMPAT_DATA_PATH=\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{ea_app_launcher}/\" %command% \"origin2://game/launch?offerIds={ea_ids}\""
exe_path = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{ea_app_launcher}/pfx/drive_c/Program Files/Electronic Arts/EA Desktop/EA Desktop/EALaunchHelper.exe\""
start_dir = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{ea_app_launcher}/pfx/drive_c/Program Files/Electronic Arts/EA Desktop/EA Desktop/\""
2024-02-09 01:24:46 +01:00
create_new_entry(exe_path, game, launch_options, start_dir)
#End of EA App Scanner
2024-01-23 23:30:55 +01:00
2024-01-26 10:18:26 +01:00
# Gog Galaxy Scanner
2024-01-26 10:18:26 +01:00
def getGogGameInfo(filePath):
# Check if the file contains any GOG entries
with open(filePath, 'r') as file:
2024-05-08 08:46:49 +02:00
if "GOG.com" not in file.read():
2024-10-26 00:24:09 +02:00
print("No GOG entries found in the registry file. Skipping GOG Galaxy Games Scanner.")
2024-01-26 10:18:26 +01:00
return {}
# If GOG entries exist, parse the registry file
game_dict = {}
with open(filePath, 'r') as file:
game_id = None
game_name = None
exe_path = None
2024-05-05 19:12:28 +02:00
depends_on = None
launch_command = None
2024-05-08 09:57:05 +02:00
start_menu_link = None
2024-05-08 10:55:55 +02:00
gog_entry = False
2024-01-26 10:18:26 +01:00
for line in file:
split_line = line.split("=")
if len(split_line) > 1:
if "gameid" in line.lower():
game_id = re.findall(r'\"(.+?)\"', split_line[1])
if game_id:
game_id = game_id[0]
if "gamename" in line.lower():
game_name = re.findall(r'\"(.+?)\"', split_line[1])
if game_name:
game_name = bytes(game_name[0], 'utf-8').decode('unicode_escape')
game_name = game_name.replace('!22', '')
if "exe" in line.lower() and not "unins000.exe" in line.lower():
exe_path = re.findall(r'\"(.+?)\"', split_line[1])
if exe_path:
exe_path = exe_path[0].replace('\\\\', '\\')
if "dependson" in line.lower():
depends_on = re.findall(r'\"(.+?)\"', split_line[1])
if depends_on:
depends_on = depends_on[0]
if "launchcommand" in line.lower():
launch_command = re.findall(r'\"(.+?)\"', split_line[1])
if launch_command:
launch_command = launch_command[0]
if game_id and game_name and launch_command:
game_dict[game_name] = {'id': game_id, 'exe': exe_path}
game_id = None
game_name = None
exe_path = None
depends_on = None
launch_command = None
2024-01-26 10:18:26 +01:00
return game_dict
2024-05-08 10:55:55 +02:00
2024-11-04 16:15:18 +01:00
def adjust_dosbox_launch_options(launch_command, game_id):
print(f"Adjusting launch options for command: {launch_command}")
if "dosbox.exe" in launch_command.lower():
try:
# Find the part of the command with DOSBox.exe and its arguments
exe_part, args_part = launch_command.split("DOSBox.exe", 1)
exe_path = exe_part.strip() + "DOSBox.exe"
args = args_part.strip()
# Form the launch options string
launch_options = f'STEAM_COMPAT_DATA_PATH="{logged_in_home}/.local/share/Steam/steamapps/compatdata/{gog_galaxy_launcher}/" %command% /command=runGame /gameId={game_id} /path="{exe_path}" "{args}"'
return launch_options
except ValueError as e:
print(f"Error adjusting launch options: {e}")
return launch_command
else:
2024-11-05 01:35:30 +01:00
# For non-DOSBox games, return the original launch command without trailing spaces
launch_command = launch_command.strip()
2024-11-04 16:15:18 +01:00
return f'STEAM_COMPAT_DATA_PATH="{logged_in_home}/.local/share/Steam/steamapps/compatdata/{gog_galaxy_launcher}/" %command% /command=runGame /gameId={game_id} /path="{launch_command}"'
2024-05-08 10:55:55 +02:00
2024-01-26 10:18:26 +01:00
# Define your paths
gog_games_directory = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{gog_galaxy_launcher}/pfx/drive_c/Program Files (x86)/GOG Galaxy/Games"
registry_file_path = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{gog_galaxy_launcher}/pfx/system.reg"
# Check if the paths exist
if not os.path.exists(gog_games_directory) or not os.path.exists(registry_file_path):
print("One or more paths do not exist.")
print("GOG Galaxy game data not found. Skipping GOG Galaxy Games Scanner.")
else:
game_dict = getGogGameInfo(registry_file_path)
for game, game_info in game_dict.items():
2024-05-08 07:15:15 +02:00
if game_info['id']:
2024-11-04 16:15:18 +01:00
# Adjust the launch options for DOSBox games
launch_options = adjust_dosbox_launch_options(game_info['exe'], game_info['id'])
# Format the paths correctly
2024-05-08 07:15:15 +02:00
exe_path = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{gog_galaxy_launcher}/pfx/drive_c/Program Files (x86)/GOG Galaxy/GalaxyClient.exe\""
start_dir = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{gog_galaxy_launcher}/pfx/drive_c/Program Files (x86)/GOG Galaxy/\""
2024-11-04 16:15:18 +01:00
# Create the new entry
2024-02-09 01:24:46 +01:00
create_new_entry(exe_path, game, launch_options, start_dir)
2024-01-26 10:18:26 +01:00
2024-05-08 07:15:15 +02:00
# End of Gog Galaxy Scanner
2024-05-08 07:05:13 +02:00
2024-05-08 09:23:47 +02:00
2024-05-08 09:57:05 +02:00
2024-05-08 10:55:55 +02:00
2024-11-05 01:35:30 +01:00
2024-02-02 05:51:08 +01:00
#Battle.net Scanner
2024-12-04 11:48:56 +01:00
2024-02-02 05:51:08 +01:00
# Define your mapping
flavor_mapping = {
2024-12-04 11:48:56 +01:00
"RTRO": "Blizzard Arcade Collection",
"D1": "Diablo",
"OSI": "Diablo II Resurrected",
"D3": "Diablo III",
"Fen": "Diablo IV",
"ANBS": "Diablo Immortal (PC)",
"WTCG": "Hearthstone",
"Hero": "Heroes of the Storm",
"Pro": "Overwatch 2",
"S1": "StarCraft",
"S2": "StarCraft 2",
"W1": "Warcraft: Orcs & Humans",
"W2": "Warcraft II: Battle.net Edition",
"W3": "Warcraft III: Reforged",
"WoW": "World of Warcraft",
"WoWC": "World of Warcraft Classic",
"GRY": "Warcraft Arclight Rumble",
"ZEUS": "Call of Duty: Black Ops - Cold War",
"VIPR": "Call of Duty: Black Ops 4",
"ODIN": "Call of Duty: Modern Warfare",
"AUKS": "Call of Duty",
"LAZR": "Call of Duty: MW 2 Campaign Remastered",
"FORE": "Call of Duty: Vanguard",
"SPOT": "Call of Duty: Modern Warfare III",
"WLBY": "Crash Bandicoot 4: It's About Time",
2024-02-02 05:51:08 +01:00
# Add more games here...
}
2024-12-04 11:48:56 +01:00
def parse_battlenet_config(config_file_path):
print(f"Opening Battle.net config file at: {config_file_path}")
with open(config_file_path, 'r') as file:
config_data = json.load(file)
2024-02-02 05:51:08 +01:00
2024-12-04 11:48:56 +01:00
games_info = config_data.get("Games", {})
2024-02-02 05:51:08 +01:00
game_dict = {}
2024-12-04 11:48:56 +01:00
for game_key, game_data in games_info.items():
print(f"Processing game: {game_key}")
if game_key == "battle_net":
print("Skipping 'battle_net' entry")
continue
if "Resumable" not in game_data:
print(f"Skipping {game_key}, no 'Resumable' key found")
continue
if game_data["Resumable"] == "false":
print(f"Game {game_key} is not resumable, adding to game_dict")
game_dict[game_key] = {
"ServerUid": game_data.get("ServerUid", ""),
"LastActioned": game_data.get("LastActioned", "")
}
print(f"Parsed config data: {game_dict}")
2024-02-02 05:51:08 +01:00
return game_dict
game_dict = {}
2024-12-04 11:48:56 +01:00
print("Detected platform: Non-Windows")
config_file_path = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{bnet_launcher}/pfx/drive_c/users/steamuser/AppData/Roaming/Battle.net/Battle.net.config"
print(f"Config file path: {config_file_path}")
if os.path.exists(config_file_path):
print("Battle.net config file found, parsing...")
game_dict = parse_battlenet_config(config_file_path)
2024-02-02 05:51:08 +01:00
else:
2024-12-04 11:48:56 +01:00
print("Battle.net config file not found. Skipping Battle.net Games Scanner.")
if game_dict:
for game_key, game_info in game_dict.items():
print(f"Processing game: {game_key}")
if game_key == "prometheus":
print("Handling 'prometheus' as 'Pro'")
game_key = "Pro"
2024-12-04 14:24:57 +01:00
elif game_key == "fenris":
print("Handling 'fenris' as 'Fen'")
game_key = "Fen"
2024-12-04 11:48:56 +01:00
game_name = flavor_mapping.get(game_key, "unknown")
if game_name == "unknown":
game_name = flavor_mapping.get(game_key.upper(), "unknown")
print(f"Trying uppercase for {game_key}: {game_name}")
if game_name == "unknown":
print(f"Game {game_key} remains unknown, skipping...")
continue
matched_key = next((k for k, v in flavor_mapping.items() if v == game_name), game_key)
print(f"Matched key for {game_key}: {matched_key}")
if game_name == "Overwatch":
game_name = "Overwatch 2"
print(f"Renaming 'Overwatch' to 'Overwatch 2'")
if game_info['ServerUid'] == "unknown":
print(f"Skipping game {game_key} due to unknown ServerUid")
continue
exe_path = f'"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{bnet_launcher}/pfx/drive_c/Program Files (x86)/Battle.net/Battle.net.exe"'
start_dir = f'"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{bnet_launcher}/pfx/drive_c/Program Files (x86)/Battle.net/"'
launch_options = f'STEAM_COMPAT_DATA_PATH="{logged_in_home}/.local/share/Steam/steamapps/compatdata/{bnet_launcher}" %command% --exec="launch {matched_key}" battlenet://{matched_key}'
print(f"Creating new entry for {game_name} with exe_path: {exe_path}")
create_new_entry(exe_path, game_name, launch_options, start_dir)
print("Battle.net Games Scanner completed.")
2024-02-02 05:51:08 +01:00
# End of Battle.net Scanner
# Amazon Games Scanner
def get_sqlite_path():
# Specify the full path to the SQLite file
path = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{amazon_launcher}/pfx/drive_c/users/steamuser/AppData/Local/Amazon Games/Data/Games/Sql/GameInstallInfo.sqlite"
if os.path.exists(path):
return path
else:
print(f"Amazon GameInstallInfo.sqlite not found at {path}")
return None
def get_launcher_path():
# Specify the full path to the Amazon Games launcher executable
path = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{amazon_launcher}/pfx/drive_c/users/steamuser/AppData/Local/Amazon Games/App/Amazon Games.exe"
if os.path.exists(path):
return path
else:
print(f"Could not find Amazon Games.exe at {path}")
return None
def get_amazon_games():
sqllite_path = get_sqlite_path()
launcher_path = get_launcher_path()
if sqllite_path is None or launcher_path is None:
print("Skipping Amazon Games Scanner due to missing paths.")
return []
result = []
connection = sqlite3.connect(sqllite_path)
cursor = connection.cursor()
cursor.execute("SELECT Id, ProductTitle FROM DbSet WHERE Installed = 1")
for row in cursor.fetchall():
id, title = row
result.append({"id": id, "title": title, "launcher_path": launcher_path})
return result
amazon_games = get_amazon_games()
if amazon_games:
for game in amazon_games:
# Initialize variables
display_name = game['title']
exe_path = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{amazon_launcher}/pfx/drive_c/users/steamuser/AppData/Local/Amazon Games/App/Amazon Games.exe\""
start_dir = f"\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{amazon_launcher}/pfx/drive_c/users/steamuser/AppData/Local/Amazon Games/App/\""
2024-11-02 07:21:36 +01:00
launch_options = f"STEAM_COMPAT_DATA_PATH=\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{amazon_launcher}/\" %command% -'amazon-games://play/{game['id']}'"
2024-02-09 01:24:46 +01:00
create_new_entry(exe_path, display_name, launch_options, start_dir)
#End of Amazon Games Scanner
2024-02-09 01:24:46 +01:00
2024-11-05 09:53:10 +01:00
# Itchio Scanner
2024-11-05 08:59:31 +01:00
2024-11-05 09:53:10 +01:00
# Set up the path to the Butler database
itch_db_location = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{itchio_launcher}/pfx/drive_c/users/steamuser/AppData/Roaming/itch/db/butler.db"
2024-11-05 09:53:10 +01:00
# Check if the database path exists
if not os.path.exists(itch_db_location):
2024-11-05 10:20:07 +01:00
print(f"Path not found: {itch_db_location}. Aborting Itch.io scan...")
2024-11-05 09:53:10 +01:00
else:
# Connect to the SQLite database
2024-11-05 08:59:31 +01:00
conn = sqlite3.connect(itch_db_location)
cursor = conn.cursor()
2024-11-05 09:53:10 +01:00
# Fetch data from the 'caves' table
2024-11-05 08:59:31 +01:00
cursor.execute("SELECT * FROM caves;")
caves = cursor.fetchall()
2024-11-05 09:53:10 +01:00
# Fetch data from the 'games' table
2024-11-05 08:59:31 +01:00
cursor.execute("SELECT * FROM games;")
games = cursor.fetchall()
# Create a dictionary to store game information by game_id
games_dict = {game[0]: game for game in games}
2024-11-05 09:53:10 +01:00
# List to store final Itch.io game details
itchgames = []
# Match game_id between 'caves' and 'games' tables and collect relevant game details
2024-11-05 08:59:31 +01:00
for cave in caves:
game_id = cave[1]
if game_id in games_dict:
game_info = games_dict[game_id]
2024-11-05 10:20:07 +01:00
cave_info = json.loads(cave[11])
2024-11-05 09:53:10 +01:00
base_path = cave_info['basePath']
candidates = cave_info['candidates']
2024-11-05 08:59:31 +01:00
executable_path = candidates[0]['path']
2024-11-05 09:53:10 +01:00
# Skip games with an executable that ends with '.html' (browser games)
2024-11-05 08:59:31 +01:00
if executable_path.endswith('.html'):
2024-11-05 10:20:07 +01:00
print(f"Skipping browser game: {game_info[2]}")
2024-11-05 08:59:31 +01:00
continue
2024-11-05 09:53:10 +01:00
# Extract the game title
2024-11-05 08:59:31 +01:00
game_title = game_info[2]
2024-11-05 09:53:10 +01:00
# Append the game info (base path, executable path, game title) to the list
itchgames.append((base_path, executable_path, game_title))
2024-11-05 09:53:10 +01:00
# Process each game for creating new entries
for base_path, executable, game_title in itchgames:
base_path_linux = base_path.replace("C:\\", f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{itchio_launcher}/pfx/drive_c/").replace("\\", "/")
exe_path = "\"" + os.path.join(base_path_linux, executable).replace("\\", "/") + "\""
start_dir = "\"" + base_path_linux + "\""
launchoptions = f"STEAM_COMPAT_DATA_PATH=\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{itchio_launcher}/\" %command%"
2024-11-05 08:59:31 +01:00
2024-11-05 09:53:10 +01:00
# Call the provided function to create a new entry for the game
create_new_entry(exe_path, game_title, launchoptions, start_dir)
2024-11-05 09:53:10 +01:00
# Close the database connection
conn.close()
2024-11-05 08:59:31 +01:00
2024-11-05 09:53:10 +01:00
# End of Itch.io Scanner
2024-08-22 12:00:24 +02:00
#Legacy Games Scanner
legacy_dir = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{legacy_launcher}/pfx/drive_c/Program Files/Legacy Games/"
2024-07-16 10:48:07 +02:00
if not os.path.exists(legacy_dir):
print("Legacy directory not found. Skipping creation.")
else:
2024-07-16 10:48:07 +02:00
user_reg_path = f"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{legacy_launcher}/pfx/user.reg"
with open(user_reg_path, 'r') as file:
user_reg = file.read()
for game_dir in os.listdir(legacy_dir):
if game_dir == "Legacy Games Launcher":
continue
print(f"Processing game directory: {game_dir}")
if game_dir == "100 Doors Escape from School":
app_info_path = f"{legacy_dir}/100 Doors Escape from School/100 Doors Escape From School_Data/app.info"
exe_path = f"{legacy_dir}/100 Doors Escape from School/100 Doors Escape From School.exe"
else:
app_info_path = os.path.join(legacy_dir, game_dir, game_dir.replace(" ", "") + "_Data", "app.info")
exe_path = os.path.join(legacy_dir, game_dir, game_dir.replace(" ", "") + ".exe")
if os.path.exists(app_info_path):
print("app.info file found.")
with open(app_info_path, 'r') as file:
lines = file.read().split('\n')
game_name = lines[1].strip()
print(f"Game Name: {game_name}")
else:
print("No app.info file found.")
if os.path.exists(exe_path):
game_exe_reg = re.search(r'\[Software\\\\Legacy Games\\\\' + re.escape(game_dir) + r'\].*?"GameExe"="([^"]*)"', user_reg, re.DOTALL | re.IGNORECASE)
if game_exe_reg and game_exe_reg.group(1).lower() == os.path.basename(exe_path).lower():
print(f"GameExe found in user.reg: {game_exe_reg.group(1)}")
start_dir = f"{legacy_dir}{game_dir}"
launch_options = f"STEAM_COMPAT_DATA_PATH=\"{logged_in_home}/.local/share/Steam/steamapps/compatdata/{legacy_launcher}\" %command%"
create_new_entry(f'"{exe_path}"', game_name, launch_options, f'"{start_dir}"')
else:
print(f"No matching .exe file found for game: {game_dir}")
else:
print(f"No .exe file found for game: {game_dir}")
#End of the Legacy Games Scanner
2024-02-12 06:44:26 +01:00
# Only write back to the shortcuts.vdf and config.vdf files if new shortcuts were added or compattools changed
if new_shortcuts_added or shortcuts_updated:
print(f"Saving new config and shortcuts files")
conf = vdf.dumps(config_data, pretty=True)
2024-05-02 10:43:22 +02:00
try:
with open(f"{logged_in_home}/.steam/root/config/config.vdf", 'w') as file:
file.write(conf)
except IOError as e:
print(f"Error writing to config.vdf: {e}")
try:
with open(f"{logged_in_home}/.steam/root/userdata/{steamid3}/config/shortcuts.vdf", 'wb') as file:
file.write(vdf.binary_dumps(shortcuts))
except IOError as e:
print(f"Error writing to shortcuts.vdf: {e}")
2024-02-12 06:44:26 +01:00
# Print the created shortcuts
if created_shortcuts:
print("Created Shortcuts:")
for name in created_shortcuts:
print(name)
2024-02-28 05:12:24 +01:00
# Create the path to the output file
output_file_path = f"{logged_in_home}/.config/systemd/user/NSLGameScanner_output.log"
# Open the output file in write mode
2024-05-02 10:43:22 +02:00
try:
with open(output_file_path, 'w') as output_file:
for game in decky_shortcuts.values():
# Skip if 'appname' or 'exe' is None
if game.get('appname') is None or game.get('exe') is None:
continue
# Create a dictionary to hold the shortcut information
shortcut_info = {
'appname': game.get('appname'),
'exe': game.get('exe'),
'StartDir': game.get('StartDir'),
'icon': game.get('icon'),
'LaunchOptions': game.get('LaunchOptions'),
'CompatTool': game.get('CompatTool'),
'WideGrid': game.get('WideGrid'),
'Grid': game.get('Grid'),
'Hero': game.get('Hero'),
'Logo': game.get('Logo'),
}
# Print the shortcut information in JSON format
message = json.dumps(shortcut_info)
print(message, flush=True) # Print to stdout
# Print the shortcut information to the output file
print(message, file=output_file, flush=True)
except IOError as e:
print(f"Error writing to output file: {e}")
2024-02-17 04:40:51 +01:00
2024-02-18 15:23:09 +01:00
print("All finished!")