mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-01-17 11:37:01 +01:00
1711 lines
68 KiB
Bash
1711 lines
68 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
###################################################
|
|
# #
|
|
# A tool for running Wabbajack modlists on Linux #
|
|
# #
|
|
# Beta v0.69 - Omni 03/18/2025 #
|
|
# #
|
|
###################################################
|
|
|
|
# Full Changelog can be found here: https://github.com/Omni-guides/Wabbajack-Modlist-Linux/blob/main/binaries/omni-guides-sh.changelog.txt
|
|
|
|
|
|
# Current Script Version (beta)
|
|
script_ver=0.69
|
|
|
|
# Define modlist-specific configurations
|
|
declare -A modlist_configs=(
|
|
["wildlander"]="dotnet472"
|
|
["librum|apostasy"]="dotnet40 dotnet8"
|
|
["nordicsouls"]="dotnet40"
|
|
["livingskyrim|lsiv|ls4"]="dotnet40"
|
|
["lostlegacy"]="dotnet48"
|
|
)
|
|
|
|
# Set up and blank logs (simplified)
|
|
LOGFILE=$HOME/omni-guides-sh.log
|
|
echo "" >$HOME/omni-guides-sh.log
|
|
|
|
# Add our new logging function
|
|
log_status() {
|
|
local level="$1"
|
|
local message="$2"
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
|
|
# Always write to log file with timestamp but without color codes
|
|
echo "[$timestamp] [$level] $(echo "$message" | sed 's/\x1b\[[0-9;]*m//g')" >> "$LOGFILE"
|
|
|
|
# Only display non-DEBUG messages to the user, preserving color codes
|
|
if [ "$level" != "DEBUG" ]; then
|
|
echo -e "$message"
|
|
fi
|
|
}
|
|
|
|
#set -x
|
|
#Protontricks Bug
|
|
#export PROTON_VERSION="Proton Experimental"
|
|
|
|
# Display banner
|
|
echo "╔══════════════════════════════════════════════════════════════════╗"
|
|
echo "║ Omni-Guides (beta) ║"
|
|
echo "║ ║"
|
|
echo "║ A tool for running Wabbajack modlists on Linux ║"
|
|
echo "╚══════════════════════════════════════════════════════════════════╝"
|
|
|
|
#########
|
|
# Intro #
|
|
#########
|
|
echo ""
|
|
log_status "INFO" "Omni-Guides Wabbajack Post-Install Script v$script_ver"
|
|
echo "───────────────────────────────────────────────────────────────────"
|
|
log_status "INFO" "This script automates the post-install steps for Wabbajack modlists on Linux/Steam Deck."
|
|
log_status "INFO" "It will configure your modlist location, install required components, and apply necessary fixes."
|
|
echo ""
|
|
log_status "WARN" "⚠ IMPORTANT: Use this script at your own risk."
|
|
log_status "INFO" "Please report any issues via GitHub (Omni-guides/Wabbajack-Modlist-Linux)."
|
|
echo "───────────────────────────────────────────────────────────────────"
|
|
echo -e "\e[33mPress any key to continue...\e[0m"
|
|
read -n 1 -s -r -p ""
|
|
|
|
#############
|
|
# Functions #
|
|
#############
|
|
|
|
##########################
|
|
# Cleanup Wine Processes #
|
|
##########################
|
|
|
|
cleanup_wine_procs() {
|
|
|
|
# Find and kill processes containing various process names
|
|
processes=$(pgrep -f "win7|win10|ShowDotFiles|protontricks")
|
|
if [[ -n "$processes" ]]; then
|
|
echo "$processes" | xargs kill -9
|
|
echo "Processes killed successfully." >>$LOGFILE 2>&1
|
|
else
|
|
echo "No matching processes found." >>$LOGFILE 2>&1
|
|
fi
|
|
|
|
pkill -9 winetricks
|
|
|
|
}
|
|
|
|
#############
|
|
# Set APPID #
|
|
#############
|
|
|
|
set_appid() {
|
|
|
|
echo "DEBUG: Extracting APPID from choice: '$choice'" >>$LOGFILE 2>&1
|
|
APPID=$(echo "$choice" | awk -F'[()]' '{print $2}')
|
|
echo "DEBUG: Extracted APPID: '$APPID'" >>$LOGFILE 2>&1
|
|
|
|
#APPID=$(echo $choice | awk {'print $NF'} | sed 's:^.\(.*\).$:\1:')
|
|
echo "APPID=$APPID" >>$LOGFILE 2>&1
|
|
|
|
if [ -z "$APPID" ]; then
|
|
echo "Error: APPID cannot be empty, exiting... Please tell Omni :("
|
|
cleaner_exit
|
|
fi
|
|
|
|
}
|
|
|
|
#############################
|
|
# Detect if running on deck #
|
|
#############################
|
|
|
|
detect_steamdeck() {
|
|
# Steamdeck or nah?
|
|
|
|
if [ -f "/etc/os-release" ] && grep -q "steamdeck" "/etc/os-release"; then
|
|
steamdeck=1
|
|
echo "Running on Steam Deck" >>$LOGFILE 2>&1
|
|
else
|
|
steamdeck=0
|
|
echo "NOT A steamdeck" >>$LOGFILE 2>&1
|
|
fi
|
|
|
|
}
|
|
|
|
###########################################
|
|
# Detect Protontricks (flatpak or native) #
|
|
###########################################
|
|
|
|
detect_protontricks() {
|
|
echo -ne "\nDetecting if protontricks is installed..." >>$LOGFILE 2>&1
|
|
|
|
# Check if native protontricks exists
|
|
if command -v protontricks >/dev/null 2>&1; then
|
|
protontricks_path=$(command -v protontricks)
|
|
# Check if the detected binary is actually a Flatpak wrapper
|
|
if [[ -f "$protontricks_path" ]] && grep -q "flatpak run" "$protontricks_path"; then
|
|
echo -e "Detected Protontricks is actually a Flatpak wrapper at $protontricks_path." >>$LOGFILE 2>&1
|
|
which_protontricks=flatpak
|
|
else
|
|
echo -e "Native Protontricks found at $protontricks_path." | tee -a $LOGFILE
|
|
which_protontricks=native
|
|
return 0 # Exit function since we confirmed native protontricks
|
|
fi
|
|
fi
|
|
|
|
# If not found, check for Flatpak protontricks
|
|
if flatpak list | grep -iq protontricks; then
|
|
echo -e "Flatpak Protontricks is already installed." >>$LOGFILE 2>&1
|
|
which_protontricks=flatpak
|
|
return 0
|
|
fi
|
|
|
|
# If neither found, offer to install Flatpak
|
|
echo -e "\e[31m\n** Protontricks not found. Do you wish to install it? (y/n): **\e[0m"
|
|
read -p " " answer
|
|
if [[ $answer =~ ^[Yy]$ ]]; then
|
|
if [[ $steamdeck -eq 1 ]]; then
|
|
if flatpak install -u -y --noninteractive flathub com.github.Matoking.protontricks; then
|
|
which_protontricks=flatpak
|
|
return 0
|
|
else
|
|
echo -e "\n\e[31mFailed to install Protontricks via Flatpak. Please install it manually and rerun this script.\e[0m" | tee -a $LOGFILE
|
|
exit 1
|
|
fi
|
|
else
|
|
read -p "Choose installation method: 1) Flatpak (preferred) 2) Native: " choice
|
|
if [[ $choice =~ 1 ]]; then
|
|
if flatpak install -u -y --noninteractive flathub com.github.Matoking.protontricks; then
|
|
which_protontricks=flatpak
|
|
return 0
|
|
else
|
|
echo -e "\n\e[31mFailed to install Protontricks via Flatpak. Please install it manually and rerun this script.\e[0m" | tee -a $LOGFILE
|
|
exit 1
|
|
fi
|
|
else
|
|
echo -e "\nSorry, there are too many distros to automate this!" | tee -a $LOGFILE
|
|
echo -e "Please check how to install Protontricks using your OS package manager (yum, dnf, apt, pacman, etc.)" | tee -a $LOGFILE
|
|
echo -e "\e[31mProtontricks is required for this script to function. Exiting.\e[0m" | tee -a $LOGFILE
|
|
exit 1
|
|
fi
|
|
fi
|
|
else
|
|
echo -e "\e[31mProtontricks is required for this script to function. Exiting.\e[0m" | tee -a $LOGFILE
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
#############################
|
|
# Run protontricks commands #
|
|
#############################
|
|
|
|
run_protontricks() {
|
|
# Determine the protontricks binary path and create command array
|
|
if [ "$which_protontricks" = "flatpak" ]; then
|
|
local cmd=(flatpak run com.github.Matoking.protontricks)
|
|
else
|
|
local cmd=(protontricks)
|
|
fi
|
|
|
|
# Execute the command with all arguments
|
|
"${cmd[@]}" "$@"
|
|
}
|
|
|
|
###############################
|
|
# Detect Protontricks Version #
|
|
###############################
|
|
|
|
protontricks_version() {
|
|
# Get the current version of protontricks
|
|
protontricks_version=$(run_protontricks -V | cut -d ' ' -f 2 | sed 's/[()]//g')
|
|
|
|
# Remove any non-numeric characters from the version number
|
|
protontricks_version_cleaned=$(echo "$protontricks_version" | sed 's/[^0-9.]//g')
|
|
|
|
echo "Protontricks Version Cleaned = $protontricks_version_cleaned" >> "$LOGFILE" 2>&1
|
|
|
|
# Split the version into digits
|
|
IFS='.' read -r first_digit second_digit third_digit <<< "$protontricks_version_cleaned"
|
|
|
|
# Check if the second digit is defined and greater than or equal to 12
|
|
if [[ -n "$second_digit" && "$second_digit" -lt 12 ]]; then
|
|
echo "Your protontricks version is too old! Update to version 1.12 or newer and rerun this script. If 'flatpak run com.github.Matoking.protontricks -V' returns 'unknown', then please update via flatpak." | tee -a "$LOGFILE"
|
|
cleaner_exit
|
|
fi
|
|
}
|
|
|
|
#######################################
|
|
# Detect Skyrim or Fallout 4 Function #
|
|
#######################################
|
|
|
|
detect_game() {
|
|
# Define lookup table for games
|
|
declare -A game_lookup=(
|
|
["Skyrim"]="Skyrim Special Edition"
|
|
["Fallout 4"]="Fallout 4"
|
|
["Fallout New Vegas"]="Fallout New Vegas"
|
|
["FNV"]="Fallout New Vegas"
|
|
["Oblivion"]="Oblivion"
|
|
)
|
|
|
|
# Try direct match first
|
|
for pattern in "${!game_lookup[@]}"; do
|
|
if [[ $choice == *"$pattern"* ]]; then
|
|
gamevar="${game_lookup[$pattern]}"
|
|
which_game="${gamevar%% *}"
|
|
echo "Game variable set to $which_game." >>"$LOGFILE" 2>&1
|
|
echo "Game variable: $gamevar" >>"$LOGFILE" 2>&1
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
# Handle generic "Fallout" case
|
|
if [[ $choice == *"Fallout"* ]]; then
|
|
PS3="Please select a Fallout game (enter the number): "
|
|
select fallout_opt in "Fallout 4" "Fallout New Vegas"; do
|
|
if [[ -n $fallout_opt ]]; then
|
|
gamevar="$fallout_opt"
|
|
which_game="${gamevar%% *}"
|
|
echo "Game variable set to $which_game." >>"$LOGFILE" 2>&1
|
|
echo "Game variable: $gamevar" >>"$LOGFILE" 2>&1
|
|
return 0
|
|
else
|
|
echo "Invalid option"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# If no match found, show selection menu
|
|
PS3="Please select a game (enter the number): "
|
|
select opt in "Skyrim" "Fallout 4" "Fallout New Vegas" "Oblivion"; do
|
|
if [[ -n $opt ]]; then
|
|
gamevar="${game_lookup[$opt]}"
|
|
which_game="${gamevar%% *}"
|
|
echo "Game variable set to $which_game." >>"$LOGFILE" 2>&1
|
|
echo "Game variable: $gamevar" >>"$LOGFILE" 2>&1
|
|
return 0
|
|
else
|
|
echo "Invalid option"
|
|
fi
|
|
done
|
|
}
|
|
|
|
###################################
|
|
# Try to detect the Steam Library #
|
|
###################################
|
|
|
|
detect_steam_library() {
|
|
|
|
local libraryfolders_vdf="$HOME/.steam/steam/config/libraryfolders.vdf"
|
|
|
|
if [[ ! -f "$libraryfolders_vdf" ]]; then
|
|
echo "libraryfolders.vdf not found in ~/.steam/steam/config/. Please ensure Steam is installed." | tee -a "$LOGFILE"
|
|
return 1
|
|
fi
|
|
|
|
local library_paths=()
|
|
while IFS='' read -r line; do
|
|
if [[ "$line" =~ \"path\" ]]; then
|
|
local path=$(echo "$line" | sed 's/.*"path"\s*"\(.*\)"/\1/')
|
|
if [[ -n "$path" ]]; then
|
|
library_paths+=("$path/steamapps/common")
|
|
fi
|
|
fi
|
|
done <"$libraryfolders_vdf"
|
|
|
|
local found=0
|
|
for library_path in "${library_paths[@]}"; do
|
|
if [[ -d "$library_path/$gamevar" ]]; then
|
|
steam_library="$library_path"
|
|
found=1
|
|
echo "Found '$gamevar' in $steam_library." >>$LOGFILE 2>&1
|
|
break
|
|
else
|
|
echo "Checking $library_path: '$gamevar' not found." >>$LOGFILE 2>&1
|
|
fi
|
|
done
|
|
|
|
if [[ "$found" -eq 0 ]]; then
|
|
echo "Vanilla game not found in Steam library locations." | tee -a "$LOGFILE"
|
|
|
|
while true; do
|
|
echo -e "\n** Enter the path to your Vanilla $gamevar directory manually (e.g. /data/SteamLibrary/steamapps/common/$gamevar): **"
|
|
read -e -r gamevar_input
|
|
|
|
steam_library_input="${gamevar_input%/*}/"
|
|
|
|
if [[ -d "$steam_library_input/$gamevar" ]]; then
|
|
steam_library="$steam_library_input"
|
|
echo "Found $gamevar in $steam_library_input." | tee -a "$LOGFILE"
|
|
echo "Steam Library set to: $steam_library" >>$LOGFILE 2>&1
|
|
break
|
|
else
|
|
echo "Game not found in $steam_library_input. Please enter a valid path to Vanilla $gamevar." | tee -a "$LOGFILE"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
echo "Steam Library Location: $steam_library" >>$LOGFILE 2>&1
|
|
|
|
if [[ "$steamdeck" -eq 1 && "$steam_library" == "/run/media"* ]]; then
|
|
basegame_sdcard=1
|
|
fi
|
|
|
|
}
|
|
|
|
#################################
|
|
# Detect Modlist Directory Path #
|
|
#################################
|
|
|
|
detect_modlist_dir_path() {
|
|
log_status "DEBUG" "Detecting $MODLIST Install Directory..."
|
|
local modlist_paths=()
|
|
local choice modlist_ini_temp
|
|
local pattern=$(echo "$MODLIST" | sed 's/ /.*\|/g')
|
|
|
|
# Search for ModOrganizer.exe entries matching the modlist pattern
|
|
while IFS= read -r entry; do
|
|
modlist_paths+=("$(dirname "${entry//[\"\']/}")")
|
|
done < <(strings ~/.steam/steam/userdata/*/config/shortcuts.vdf | grep -iE "ModOrganizer.exe" | grep -iE "$pattern")
|
|
|
|
# If no exact matches, get all ModOrganizer.exe instances
|
|
if [[ ${#modlist_paths[@]} -eq 0 ]]; then
|
|
echo "No exact matches found. Searching for all ModOrganizer.exe instances..."
|
|
while IFS= read -r entry; do
|
|
modlist_paths+=("$(dirname "${entry//[\"\']/}")")
|
|
done < <(strings ~/.steam/steam/userdata/*/config/shortcuts.vdf | grep -iE "ModOrganizer.exe")
|
|
fi
|
|
|
|
# Handle different cases based on number of paths found
|
|
if [[ ${#modlist_paths[@]} -eq 0 ]]; then
|
|
# No paths found - must enter manually
|
|
echo -e "\e[34mNo ModOrganizer.exe entries found. Please enter the directory manually:\e[0m"
|
|
read -r -e modlist_dir
|
|
elif [[ ${#modlist_paths[@]} -eq 1 ]]; then
|
|
# Single path found - use it directly without output
|
|
modlist_dir="${modlist_paths[0]}"
|
|
else
|
|
# Multiple paths found - show selection menu
|
|
echo "Select the ModOrganizer directory:"
|
|
for i in "${!modlist_paths[@]}"; do
|
|
echo -e "\e[33m$((i + 1))) ${modlist_paths[i]}\e[0m"
|
|
done
|
|
echo -e "\e[34m$(( ${#modlist_paths[@]} + 1 ))) Enter path manually\e[0m"
|
|
|
|
while true; do
|
|
read -p "Enter your choice (1-$((${#modlist_paths[@]} + 1))): " choice
|
|
if [[ "$choice" =~ ^[0-9]+$ && "$choice" -ge 1 && "$choice" -le $(( ${#modlist_paths[@]} + 1 )) ]]; then
|
|
if [[ "$choice" -eq $(( ${#modlist_paths[@]} + 1 )) ]]; then
|
|
echo -ne "\e[34mEnter the ModOrganizer directory path: \e[0m"
|
|
read -r -e modlist_dir
|
|
else
|
|
modlist_dir="${modlist_paths[choice - 1]}"
|
|
fi
|
|
break
|
|
else
|
|
echo "Invalid selection. Please try again."
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Validate selection
|
|
modlist_ini_temp="$modlist_dir/ModOrganizer.ini"
|
|
while [[ ! -f "$modlist_ini_temp" ]]; do
|
|
echo "ModOrganizer.ini not found in $modlist_dir. Please enter a valid path."
|
|
echo -ne "\e[34mEnter the ModOrganizer directory path: \e[0m"
|
|
read -r -e modlist_dir
|
|
modlist_ini_temp="$modlist_dir/ModOrganizer.ini"
|
|
done
|
|
|
|
# Save and log results
|
|
modlist_ini="$modlist_ini_temp"
|
|
echo "Modlist directory: $modlist_dir" >> "$LOGFILE"
|
|
echo "Modlist INI location: $modlist_ini" >> "$LOGFILE"
|
|
}
|
|
|
|
#####################################################
|
|
# Set protontricks permissions on Modlist Directory #
|
|
#####################################################
|
|
|
|
set_protontricks_perms() {
|
|
if [ "$which_protontricks" = "flatpak" ]; then
|
|
log_status "INFO" "\nSetting Protontricks permissions..."
|
|
flatpak override --user com.github.Matoking.protontricks --filesystem="$modlist_dir"
|
|
log_status "SUCCESS" "Done!"
|
|
|
|
if [[ $steamdeck = 1 ]]; then
|
|
log_status "WARN" "\nChecking for SDCard and setting permissions appropriately.."
|
|
sdcard_path=$(df -h | grep "/run/media" | awk {'print $NF'})
|
|
echo "$sdcard_path" >>$LOGFILE 2>&1
|
|
flatpak override --user --filesystem=$sdcard_path com.github.Matoking.protontricks
|
|
flatpak override --user --filesystem=/run/media/mmcblk0p1 com.github.Matoking.protontricks
|
|
log_status "SUCCESS" "Done."
|
|
fi
|
|
else
|
|
log_status "DEBUG" "Using Native protontricks, skip setting permissions"
|
|
fi
|
|
}
|
|
|
|
#####################################
|
|
# Enable Visibility of (.)dot files #
|
|
#####################################
|
|
|
|
enable_dotfiles() {
|
|
log_status "DEBUG" "APPID=$APPID"
|
|
log_status "INFO" "\nEnabling visibility of (.)dot files..."
|
|
|
|
# Completely redirect all output to avoid any wine debug messages
|
|
dotfiles_check=$(WINEDEBUG=-all run_protontricks -c 'wine reg query "HKEY_CURRENT_USER\Software\Wine" /v ShowDotFiles' $APPID > /dev/null 2>&1;
|
|
WINEDEBUG=-all run_protontricks -c 'wine reg query "HKEY_CURRENT_USER\Software\Wine" /v ShowDotFiles' $APPID 2>/dev/null | grep ShowDotFiles | awk '{gsub(/\r/,""); print $NF}')
|
|
|
|
log_status "DEBUG" "Current dotfiles setting: $dotfiles_check"
|
|
|
|
if [[ "$dotfiles_check" = "Y" ]]; then
|
|
log_status "INFO" "DotFiles already enabled via registry... skipping"
|
|
else
|
|
# Method 2: Set registry key (standard approach)
|
|
log_status "DEBUG" "Setting ShowDotFiles registry key..."
|
|
WINEDEBUG=-all run_protontricks -c 'wine reg add "HKEY_CURRENT_USER\Software\Wine" /v ShowDotFiles /d Y /f' $APPID > /dev/null 2>&1
|
|
|
|
# Method 3: Also try direct winecfg approach as backup
|
|
log_status "DEBUG" "Also setting via winecfg command..."
|
|
WINEDEBUG=-all run_protontricks -c 'winecfg /v wine' $APPID > /dev/null 2>&1
|
|
|
|
# Method 4: Create user.reg entry if it doesn't exist
|
|
log_status "DEBUG" "Ensuring user.reg has correct entry..."
|
|
prefix_path=$(WINEDEBUG=-all run_protontricks -c 'echo $WINEPREFIX' $APPID 2>/dev/null)
|
|
if [[ -n "$prefix_path" && -d "$prefix_path" ]]; then
|
|
if [[ -f "$prefix_path/user.reg" ]]; then
|
|
if ! grep -q "ShowDotFiles" "$prefix_path/user.reg" 2>/dev/null; then
|
|
echo '[Software\\Wine] 1603891765' >> "$prefix_path/user.reg" 2>/dev/null
|
|
echo '"ShowDotFiles"="Y"' >> "$prefix_path/user.reg" 2>/dev/null
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Verify the setting took effect
|
|
dotfiles_verify=$(WINEDEBUG=-all run_protontricks -c 'wine reg query "HKEY_CURRENT_USER\Software\Wine" /v ShowDotFiles' $APPID > /dev/null 2>&1;
|
|
WINEDEBUG=-all run_protontricks -c 'wine reg query "HKEY_CURRENT_USER\Software\Wine" /v ShowDotFiles' $APPID 2>/dev/null | grep ShowDotFiles | awk '{gsub(/\r/,""); print $NF}')
|
|
log_status "DEBUG" "Verification check: $dotfiles_verify"
|
|
|
|
log_status "SUCCESS" "Done!"
|
|
fi
|
|
}
|
|
|
|
###############################################
|
|
# Set Windows 10 version in the proton prefix #
|
|
###############################################
|
|
|
|
set_win10_prefix() {
|
|
WINEDEBUG=-all run_protontricks --no-bwrap $APPID win10 >/dev/null 2>&1
|
|
}
|
|
|
|
######################################
|
|
# Install Wine Components & VCRedist #
|
|
######################################
|
|
|
|
install_wine_components() {
|
|
log_status "INFO" "Installing Wine Components... This can take some time, be patient!"
|
|
|
|
# Define game-specific component sets
|
|
local protontricks_appid="$APPID"
|
|
local protontricks_components=()
|
|
|
|
# Common components for all games
|
|
local common_components=("fontsmooth=rgb" "xact" "xact_x64" "vcrun2022")
|
|
|
|
# Game-specific configuration
|
|
case "$gamevar" in
|
|
"Skyrim Special Edition"|"Fallout 4")
|
|
protontricks_components=("${common_components[@]}" "d3dcompiler_47" "d3dx11_43" "d3dcompiler_43" "dotnet6" "dotnet7")
|
|
;;
|
|
"Fallout New Vegas")
|
|
protontricks_components=("${common_components[@]}" "d3dx9_43" "d3dx9")
|
|
protontricks_appid="22380" # Force appid for FNV
|
|
;;
|
|
"Oblivion")
|
|
protontricks_components=("${common_components[@]}" "d3dx9_43" "d3dx9")
|
|
;;
|
|
*)
|
|
echo "Unsupported game: $gamevar" | tee -a "$LOGFILE"
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
# Log the command we're about to run
|
|
echo "Installing components: ${protontricks_components[*]}" >>$LOGFILE 2>&1
|
|
|
|
# Run the installation with progress indicator
|
|
printf "Protontricks running... "
|
|
|
|
# Try up to 3 times to install components
|
|
local max_attempts=3
|
|
local attempt=1
|
|
local success=false
|
|
|
|
while [[ $attempt -le $max_attempts && $success == false ]]; do
|
|
if [[ $attempt -gt 1 ]]; then
|
|
echo "Retry attempt $attempt/$max_attempts..." | tee -a "$LOGFILE"
|
|
sleep 2
|
|
fi
|
|
|
|
if WINEDEBUG=-all run_protontricks --no-bwrap "$protontricks_appid" -q "${protontricks_components[@]}" >/dev/null 2>&1; then
|
|
success=true
|
|
else
|
|
echo "Attempt $attempt failed, cleaning up wine processes before retry..." >>$LOGFILE 2>&1
|
|
cleanup_wine_procs
|
|
attempt=$((attempt+1))
|
|
fi
|
|
done
|
|
|
|
if [[ $success == true ]]; then
|
|
printf "Done.\n"
|
|
log_status "SUCCESS" "Wine Component installation completed."
|
|
else
|
|
printf "Failed.\n"
|
|
log_status "ERROR" "Component install failed after $max_attempts attempts."
|
|
return 1
|
|
fi
|
|
|
|
# Verify installation
|
|
log_status "DEBUG" "Verifying installed components..."
|
|
local output
|
|
output=$(run_protontricks --no-bwrap "$protontricks_appid" list-installed 2>/dev/null)
|
|
|
|
# Clean up and deduplicate the component list
|
|
local cleaned_output
|
|
cleaned_output=$(echo "$output" | grep -v "Using winetricks" | sort -u | grep -v '^$')
|
|
log_status "DEBUG" "Installed components (unique):"
|
|
echo "$cleaned_output" >> "$LOGFILE"
|
|
|
|
# Check for critical components only to avoid false negatives
|
|
local critical_components=("vcrun2022" "xact")
|
|
local missing_components=()
|
|
|
|
for component in "${critical_components[@]}"; do
|
|
if ! grep -q "$component" <<<"$output"; then
|
|
missing_components+=("$component")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#missing_components[@]} -gt 0 ]]; then
|
|
echo -e "\nWarning: Some critical components may be missing: ${missing_components[*]}" | tee -a "$LOGFILE"
|
|
echo "Installation will continue, but you may encounter issues." | tee -a "$LOGFILE"
|
|
else
|
|
echo "Critical components verified successfully." >>$LOGFILE 2>&1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
############################################
|
|
# Detect default compatdata Directory Path #
|
|
############################################
|
|
default_steam_compatdata_dir() {
|
|
# Prefer ~/.local/share/Steam if it exists
|
|
if [[ -d "$HOME/.local/share/Steam/steamapps/compatdata" ]]; then
|
|
echo "$HOME/.local/share/Steam/steamapps/compatdata"
|
|
elif [[ -d "$HOME/.steam/steam/steamapps/compatdata" ]]; then
|
|
echo "$HOME/.steam/steam/steamapps/compatdata"
|
|
else
|
|
# Do not create the directory; just return empty string
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
# Helper to get all Steam library folders from libraryfolders.vdf
|
|
get_all_steam_libraries() {
|
|
local vdf_file="$HOME/.steam/steam/config/libraryfolders.vdf"
|
|
local libraries=("$HOME/.local/share/Steam" "$HOME/.steam/steam")
|
|
if [[ -f "$vdf_file" ]]; then
|
|
while IFS='' read -r line; do
|
|
if [[ "$line" =~ "path" ]]; then
|
|
local path=$(echo "$line" | sed 's/.*"path"\s*"\(.*\)"/\1/')
|
|
if [[ -n "$path" ]]; then
|
|
libraries+=("$path")
|
|
fi
|
|
fi
|
|
done <"$vdf_file"
|
|
fi
|
|
echo "${libraries[@]}"
|
|
}
|
|
|
|
####################################
|
|
# Detect compatdata Directory Path #
|
|
####################################
|
|
|
|
detect_compatdata_path() {
|
|
local appid_to_check="$APPID"
|
|
if [[ "$gamevar" == "Fallout New Vegas" ]]; then
|
|
appid_to_check="22380"
|
|
local vdf_file="$HOME/.local/share/Steam/config/libraryfolders.vdf"
|
|
local libraries=("$HOME/.local/share/Steam")
|
|
# Parse all additional libraries from the VDF
|
|
if [[ -f "$vdf_file" ]]; then
|
|
while IFS= read -r line; do
|
|
if [[ "$line" =~ "path" ]]; then
|
|
# Extract the path value using sed
|
|
local path=$(echo "$line" | sed -E 's/.*"path"[ \t]*"([^"]+)".*/\1/')
|
|
if [[ "$path" == /* ]]; then
|
|
libraries+=("$path")
|
|
else
|
|
libraries+=("$HOME/$path")
|
|
fi
|
|
fi
|
|
done < "$vdf_file"
|
|
fi
|
|
compat_data_path=""
|
|
for lib in "${libraries[@]}"; do
|
|
local compat_path="$lib/steamapps/compatdata/$appid_to_check"
|
|
log_status "DEBUG" "Checking for compatdata at: $compat_path"
|
|
if [[ -d "$compat_path" ]]; then
|
|
compat_data_path="$compat_path"
|
|
log_status "DEBUG" "Found FNV compatdata: $compat_data_path"
|
|
break
|
|
fi
|
|
done
|
|
if [[ -z "$compat_data_path" ]]; then
|
|
log_status "ERROR" "Could not find compatdata directory for Fallout New Vegas (22380) in any Steam library."
|
|
log_status "ERROR" "Please ensure you have launched the vanilla Fallout New Vegas game at least once via Steam."
|
|
return 1
|
|
fi
|
|
return 0
|
|
fi
|
|
# ... (existing logic for other games)
|
|
# Check common Steam library locations first
|
|
for path in "$HOME/.local/share/Steam/steamapps/compatdata" "$HOME/.steam/steam/steamapps/compatdata"; do
|
|
if [[ -d "$path/$appid_to_check" ]]; then
|
|
compat_data_path="$path/$appid_to_check"
|
|
log_status "DEBUG" "compatdata Path detected: $compat_data_path"
|
|
break
|
|
fi
|
|
done
|
|
|
|
# If not found in common locations, use find command
|
|
if [[ -z "$compat_data_path" ]]; then
|
|
find / -type d -name "compatdata" 2>/dev/null | while read -r compatdata_dir; do
|
|
if [[ -d "$compatdata_dir/$appid_to_check" ]]; then
|
|
compat_data_path="$compatdata_dir/$appid_to_check"
|
|
log_status "DEBUG" "compatdata Path detected: $compat_data_path"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ -z "$compat_data_path" ]]; then
|
|
log_status "ERROR" "Directory named '$appid_to_check' not found in any compatdata directories."
|
|
log_status "ERROR" "Please ensure you have started the Steam entry for the modlist at least once, even if it fails.."
|
|
else
|
|
log_status "DEBUG" "Found compatdata directory with '$appid_to_check': $compat_data_path"
|
|
fi
|
|
}
|
|
|
|
#########################
|
|
# Detect Proton Version #
|
|
#########################
|
|
|
|
detect_proton_version() {
|
|
log_status "DEBUG" "Detecting Proton version..."
|
|
|
|
# Validate the compatdata path exists
|
|
if [[ ! -d "$compat_data_path" ]]; then
|
|
log_status "WARN" "Compatdata directory not found at '$compat_data_path'"
|
|
proton_ver="Unknown"
|
|
return 1
|
|
fi
|
|
|
|
# First try to get Proton version from the registry
|
|
if [[ -f "$compat_data_path/pfx/system.reg" ]]; then
|
|
local reg_output
|
|
reg_output=$(grep -A 3 "\"SteamClientProtonVersion\"" "$compat_data_path/pfx/system.reg" | grep "=" | cut -d= -f2 | tr -d '"' | tr -d ' ')
|
|
|
|
if [[ -n "$reg_output" ]]; then
|
|
# Keep GE versions as is, otherwise prefix with "Proton"
|
|
if [[ "$reg_output" == *"GE"* ]]; then
|
|
proton_ver="$reg_output" # Keep GE versions as is
|
|
else
|
|
proton_ver="Proton $reg_output"
|
|
fi
|
|
log_status "DEBUG" "Detected Proton version from registry: $proton_ver"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Fallback to config_info if registry method fails
|
|
if [[ -f "$compat_data_path/config_info" ]]; then
|
|
local config_ver
|
|
config_ver=$(head -n 1 "$compat_data_path/config_info")
|
|
if [[ -n "$config_ver" ]]; then
|
|
# Keep GE versions as is, otherwise prefix with "Proton"
|
|
if [[ "$config_ver" == *"GE"* ]]; then
|
|
proton_ver="$config_ver" # Keep GE versions as is
|
|
else
|
|
proton_ver="Proton $config_ver"
|
|
fi
|
|
log_status "DEBUG" "Detected Proton version from config_info: $proton_ver"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
proton_ver="Unknown"
|
|
log_status "WARN" "Could not detect Proton version"
|
|
return 1
|
|
}
|
|
|
|
###############################
|
|
# Confirmation before running #
|
|
###############################
|
|
|
|
confirmation_before_running() {
|
|
|
|
echo "" | tee -a $LOGFILE
|
|
echo -e "Detail Checklist:" | tee -a $LOGFILE
|
|
echo -e "=================" | tee -a $LOGFILE
|
|
echo -e "Modlist: $MODLIST .....\e[32m OK.\e[0m" | tee -a $LOGFILE
|
|
echo -e "Directory: $modlist_dir .....\e[32m OK.\e[0m" | tee -a $LOGFILE
|
|
echo -e "Proton Version: $proton_ver .....\e[32m OK.\e[0m" | tee -a $LOGFILE
|
|
echo -e "App ID: $APPID" | tee -a $LOGFILE
|
|
|
|
}
|
|
|
|
#################################
|
|
# chown/chmod modlist directory #
|
|
#################################
|
|
|
|
chown_chmod_modlist_dir() {
|
|
log_status "WARN" "Changing Ownership and Permissions of modlist directory (may require sudo password)"
|
|
|
|
user=$(whoami)
|
|
group=$(id -gn)
|
|
log_status "DEBUG" "User is $user and Group is $group"
|
|
|
|
sudo chown -R "$user:$group" "$modlist_dir"
|
|
sudo chmod -R 755 "$modlist_dir"
|
|
}
|
|
|
|
|
|
###############################################
|
|
# Backup ModOrganizer.ini and backup gamePath #
|
|
###############################################
|
|
|
|
backup_modorganizer() {
|
|
log_status "DEBUG" "Backing up ModOrganizer.ini: $modlist_ini"
|
|
cp "$modlist_ini" "$modlist_ini.$(date +"%Y%m%d_%H%M%S").bak"
|
|
grep gamePath "$modlist_ini" | sed '/^backupPath/! s/gamePath/backupPath/' >> "$modlist_ini"
|
|
}
|
|
|
|
########################################
|
|
# Blank or set MO2 Downloads Directory #
|
|
########################################
|
|
|
|
blank_downloads_dir() {
|
|
log_status "INFO" "\nEditing download_directory..."
|
|
sed -i "/download_directory/c\download_directory =" "$modlist_ini"
|
|
log_status "SUCCESS" "Done."
|
|
}
|
|
|
|
############################################
|
|
# Replace the gamePath in ModOrganizer.ini #
|
|
############################################
|
|
|
|
replace_gamepath() {
|
|
log_status "INFO" "Setting game path in ModOrganizer.ini..."
|
|
|
|
log_status "DEBUG" "Using Steam Library Path: $steam_library"
|
|
log_status "DEBUG" "Use SDCard?: $basegame_sdcard"
|
|
|
|
# Check if Modlist uses Game Root, Stock Game, etc.
|
|
game_path_line=$(grep '^gamePath' "$modlist_ini")
|
|
log_status "DEBUG" "Game Path Line: $game_path_line"
|
|
|
|
if [[ "$game_path_line" == *Stock\ Game* || "$game_path_line" == *STOCK\ GAME* || "$game_path_line" == *Stock\ Game\ Folder* || "$game_path_line" == *Stock\ Folder* || "$game_path_line" == *Skyrim\ Stock* || "$game_path_line" == *Game\ Root* || $game_path_line == *root\\\\Skyrim\ Special\ Edition* ]]; then
|
|
# Stock Game, Game Root or equivalent directory found
|
|
log_status "INFO" "Found Game Root/Stock Game or equivalent directory, editing Game Path..."
|
|
|
|
# Get the end of our path
|
|
if [[ $game_path_line =~ Stock\ Game\ Folder ]]; then
|
|
modlist_gamedir="$modlist_dir/Stock Game Folder"
|
|
log_status "DEBUG" "Modlist Gamedir: $modlist_gamedir"
|
|
elif [[ $game_path_line =~ Stock\ Folder ]]; then
|
|
modlist_gamedir="$modlist_dir/Stock Folder"
|
|
elif [[ $game_path_line =~ Skyrim\ Stock ]]; then
|
|
modlist_gamedir="$modlist_dir/Skyrim Stock"
|
|
log_status "DEBUG" "Modlist Gamedir: $modlist_gamedir"
|
|
elif [[ $game_path_line =~ Game\ Root ]]; then
|
|
modlist_gamedir="$modlist_dir/Game Root"
|
|
log_status "DEBUG" "Modlist Gamedir: $modlist_gamedir"
|
|
elif [[ $game_path_line =~ STOCK\ GAME ]]; then
|
|
modlist_gamedir="$modlist_dir/STOCK GAME"
|
|
log_status "DEBUG" "Modlist Gamedir: $modlist_gamedir"
|
|
elif [[ $game_path_line =~ Stock\ Game ]]; then
|
|
modlist_gamedir="$modlist_dir/Stock Game"
|
|
log_status "DEBUG" "Modlist Gamedir: $modlist_gamedir"
|
|
elif [[ $game_path_line =~ root\\\\Skyrim\ Special\ Edition ]]; then
|
|
modlist_gamedir="$modlist_dir/root/Skyrim Special Edition"
|
|
log_status "DEBUG" "Modlist Gamedir: $modlist_gamedir"
|
|
fi
|
|
|
|
if [[ "$modlist_sdcard" -eq "1" && "$steamdeck" -eq "1" ]]; then
|
|
log_status "DEBUG" "Using SDCard on Steam Deck"
|
|
modlist_gamedir_sdcard="${modlist_gamedir#*mmcblk0p1}"
|
|
sdcard_new_path="$modlist_gamedir_sdcard"
|
|
|
|
# Strip /run/media/deck/UUID if present
|
|
if [[ "$sdcard_new_path" == /run/media/deck/* ]]; then
|
|
sdcard_new_path="/${sdcard_new_path#*/run/media/deck/*/*}"
|
|
log_status "DEBUG" "SD Card Path after stripping: $sdcard_new_path"
|
|
fi
|
|
|
|
new_string="@ByteArray(D:${sdcard_new_path//\//\\\\})"
|
|
log_status "DEBUG" "New String: $new_string"
|
|
else
|
|
new_string="@ByteArray(Z:${modlist_gamedir//\//\\\\})"
|
|
log_status "DEBUG" "New String: $new_string"
|
|
fi
|
|
|
|
elif [[ "$game_path_line" == *steamapps* ]]; then
|
|
log_status "INFO" "Vanilla Game Directory required, editing Game Path..."
|
|
modlist_gamedir="$steam_library/$gamevar"
|
|
log_status "DEBUG" "Modlist Gamedir: $modlist_gamedir"
|
|
|
|
if [[ "$basegame_sdcard" -eq "1" && "$steamdeck" -eq "1" ]]; then
|
|
log_status "DEBUG" "Using SDCard on Steam Deck"
|
|
modlist_gamedir_sdcard="${modlist_gamedir#*mmcblk0p1}"
|
|
sdcard_new_path="$modlist_gamedir_sdcard/$gamevar"
|
|
new_string="@ByteArray(D:${sdcard_new_path//\//\\\\})"
|
|
log_status "DEBUG" "New String: $new_string"
|
|
else
|
|
new_string="@ByteArray(Z:${modlist_gamedir//\//\\\\})"
|
|
log_status "DEBUG" "New String: $new_string"
|
|
fi
|
|
else
|
|
log_status "WARN" "Neither Game Root, Stock Game or Vanilla Game directory found, Please launch MO and set path manually..."
|
|
return 1
|
|
fi
|
|
|
|
# Replace the string in the file
|
|
file_to_modify="$modlist_dir/ModOrganizer.ini"
|
|
escaped_new_string=$(printf '%s\n' "$new_string" | sed -e 's/[\/&]/\\&/g')
|
|
sed -i "/^gamePath/c\gamePath=$escaped_new_string" "$file_to_modify"
|
|
|
|
log_status "SUCCESS" "Game path set successfully"
|
|
}
|
|
|
|
##########################################
|
|
# Update Executables in ModOrganizer.ini #
|
|
##########################################
|
|
|
|
update_executables() {
|
|
|
|
# Take the line passed to the function
|
|
echo "Original Line: $orig_line_path" >>$LOGFILE 2>&1
|
|
|
|
skse_loc=$(echo "$orig_line_path" | cut -d '=' -f 2-)
|
|
echo "SKSE Loc: $skse_loc" >>$LOGFILE 2>&1
|
|
|
|
# Drive letter
|
|
if [[ "$modlist_sdcard" -eq 1 && "$steamdeck" -eq 1 ]]; then
|
|
echo "Using SDCard on Steam Deck" >>$LOGFILE 2>&1
|
|
drive_letter=" = D:"
|
|
else
|
|
drive_letter=" = Z:"
|
|
fi
|
|
|
|
# Find the workingDirectory number
|
|
|
|
binary_num=$(echo "$orig_line_path" | cut -d '=' -f -1)
|
|
echo "Binary Num: $binary_num" >>$LOGFILE 2>&1
|
|
|
|
# Find the equvalent workingDirectory
|
|
justnum=$(echo "$binary_num" | cut -d '\' -f 1)
|
|
bin_path_start=$(echo "$binary_num" | tr -d ' ' | sed 's/\\/\\\\/g')
|
|
path_start=$(echo "$justnum\\workingDirectory" | sed 's/\\/\\\\/g')
|
|
echo "Path Start: $path_start" >>$LOGFILE 2>&1
|
|
# Decide on steam apps or Stock Game etc
|
|
|
|
if [[ "$orig_line_path" == *"mods"* ]]; then
|
|
# mods path type found
|
|
echo -e "mods path Found" >>$LOGFILE 2>&1
|
|
|
|
# Path Middle / modlist_dr
|
|
if [[ "$modlist_sdcard" -eq 1 && "$steamdeck" -eq 1 ]]; then
|
|
echo "Using SDCard on Steam Deck" >>$LOGFILE 2>&1
|
|
drive_letter=" = D:"
|
|
echo "$modlist_dir" >>$LOGFILE 2>&1
|
|
path_middle="${modlist_dir#*mmcblk0p1}"
|
|
# Strip /run/media/deck/UUID
|
|
if [[ "$path_middle" == /run/media/*/* ]]; then
|
|
path_middle="/${path_middle#*/run/media/*/*/*}"
|
|
echo "Path Middle after stripping: $path_middle" >>$LOGFILE 2>&1
|
|
fi
|
|
else
|
|
path_middle="$modlist_dir"
|
|
fi
|
|
|
|
echo "Path Middle: $path_middle" >>$LOGFILE 2>&1
|
|
|
|
path_end=$(echo "${skse_loc%/*}" | sed 's/.*\/mods/\/mods/')
|
|
echo "Path End: $path_end" >>$LOGFILE 2>&1
|
|
bin_path_end=$(echo "$skse_loc" | sed 's/.*\/mods/\/mods/')
|
|
echo "Bin Path End: $bin_path_end" >>$LOGFILE 2>&1
|
|
elif grep -q -E "(Stock Game|Game Root|STOCK GAME|Stock Game Folder|Stock Folder|Skyrim Stock|root/Skyrim Special Edition)" <<<"$orig_line_path"; then
|
|
# STOCK GAME ROOT FOUND
|
|
echo -e "Stock/Game Root Found" >>$LOGFILE 2>&1
|
|
|
|
# Path Middle / modlist_dr
|
|
if [[ "$modlist_sdcard" -eq 1 && "$steamdeck" -eq 1 ]]; then
|
|
echo "Using SDCard on Steam Deck" >>$LOGFILE 2>&1
|
|
drive_letter=" = D:"
|
|
echo "Modlist Dir: $modlist_dir" >>$LOGFILE 2>&1
|
|
path_middle="${modlist_dir#*mmcblk0p1}"
|
|
# Strip /run/media/deck/UUID
|
|
if [[ "$path_middle" == /run/media/*/* ]]; then
|
|
path_middle="/${path_middle#*/run/media/*/*/*}"
|
|
echo "Path Middle after stripping: $path_middle" >>$LOGFILE 2>&1
|
|
fi
|
|
else
|
|
path_middle="$modlist_dir"
|
|
fi
|
|
echo "Path Middle: $path_middle" >>$LOGFILE 2>&1
|
|
|
|
# Get the end of our path
|
|
if [[ $orig_line_path =~ Stock\ Game ]]; then
|
|
dir_type="stockgame"
|
|
path_end=$(echo "${skse_loc%/*}" | sed 's/.*\/Stock Game/\/Stock Game/')
|
|
echo "Path End: $path_end" >>$LOGFILE 2>&1
|
|
bin_path_end=$(echo "$skse_loc" | sed 's/.*\/Stock Game/\/Stock Game/')
|
|
echo "Bin Path End: $bin_path_end" >>$LOGFILE 2>&1
|
|
elif [[ $orig_line_path =~ Game\ Root ]]; then
|
|
dir_type="gameroot"
|
|
path_end=$(echo "${skse_loc%/*}" | sed 's/.*\/Game Root/\/Game Root/')
|
|
echo "Path End: $path_end" >>$LOGFILE 2>&1
|
|
bin_path_end=$(echo "$skse_loc" | sed 's/.*\/Game Root/\/Game Root/')
|
|
echo "Bin Path End: $bin_path_end" >>$LOGFILE 2>&1
|
|
elif [[ $orig_line_path =~ STOCK\ GAME ]]; then
|
|
dir_type="STOCKGAME"
|
|
path_end=$(echo "${skse_loc%/*}" | sed 's/.*\/STOCK GAME/\/STOCK GAME/')
|
|
echo "Path End: $path_end" >>$LOGFILE 2>&1
|
|
bin_path_end=$(echo "$skse_loc" | sed 's/.*\/STOCK GAME/\/STOCK GAME/')
|
|
echo "Bin Path End: $bin_path_end" >>$LOGFILE 2>&1
|
|
elif [[ $orig_line_path =~ Stock\ Folder ]]; then
|
|
dir_type="stockfolder"
|
|
path_end=$(echo "${skse_loc%/*}" | sed 's/.*\/Stock Folder/\/Stock Folder/')
|
|
echo "Path End: $path_end" >>$LOGFILE 2>&1
|
|
bin_path_end=$(echo "$skse_loc" | sed 's/.*\/Stock Folder/\/Stock Folder/')
|
|
echo "Bin Path End: $bin_path_end" >>$LOGFILE 2>&1
|
|
elif [[ $orig_line_path =~ Skyrim\ Stock ]]; then
|
|
dir_type="skyrimstock"
|
|
path_end=$(echo "${skse_loc%/*}" | sed 's/.*\/Skyrim Stock/\/Skyrim Stock/')
|
|
echo "Path End: $path_end" >>$LOGFILE 2>&1
|
|
bin_path_end=$(echo "$skse_loc" | sed 's/.*\/Skyrim Stock/\/Skyrim Stock/')
|
|
echo "Bin Path End: $bin_path_end" >>$LOGFILE 2>&1
|
|
elif [[ $orig_line_path =~ Stock\ Game\ Folder ]]; then
|
|
dir_type="stockgamefolder"
|
|
path_end=$(echo "$skse_loc" | sed 's/.*\/Stock Game Folder/\/Stock Game Folder/')
|
|
echo "Path End: $path_end" >>$LOGFILE 2>&1
|
|
elif [[ $orig_line_path =~ root\/Skyrim\ Special\ Edition ]]; then
|
|
dir_type="rootskyrimse"
|
|
path_end="/${skse_loc# }"
|
|
echo "Path End: $path_end" >>$LOGFILE 2>&1
|
|
bin_path_end="/${skse_loc# }"
|
|
echo "Bin Path End: $bin_path_end" >>$LOGFILE 2>&1
|
|
fi
|
|
elif [[ "$orig_line_path" == *"steamapps"* ]]; then
|
|
# STEAMAPPS FOUND
|
|
echo -e "steamapps Found" >>$LOGFILE 2>&1
|
|
|
|
# Path Middle / modlist_dr
|
|
if [[ "$basegame_sdcard" -eq "1" && "$steamdeck" -eq "1" ]]; then
|
|
echo "Using SDCard on Steam Deck" >>$LOGFILE 2>&1
|
|
path_middle="${steam_library#*mmcblk0p1}"
|
|
drive_letter=" = D:"
|
|
else
|
|
echo "Steamapps Steam Library Path: $steam_library"
|
|
path_middle=${steam_library%%steamapps*}
|
|
fi
|
|
echo "Path Middle: $path_middle" >>$LOGFILE 2>&1
|
|
path_end=$(echo "${skse_loc%/*}" | sed 's/.*\/steamapps/\/steamapps/')
|
|
echo "Path End: $path_end" >>$LOGFILE 2>&1
|
|
bin_path_end=$(echo "$skse_loc" | sed 's/.*\/steamapps/\/steamapps/')
|
|
echo "Bin Path End: $bin_path_end" >>$LOGFILE 2>&1
|
|
|
|
else
|
|
echo "No matching pattern found in the path: $orig_line_path" >>$LOGFILE 2>&1
|
|
bail_out=1
|
|
echo $bail_out >>$LOGFILE 2>&1
|
|
|
|
fi
|
|
|
|
echo "Bail Out: $bail_out" >>$LOGFILE 2>&1
|
|
|
|
if [[ $bail_out -eq 1 ]]; then
|
|
echo "Exiting function due to bail_out" >>$LOGFILE 2>&1
|
|
return
|
|
else
|
|
# Combine them all together
|
|
full_bin_path="$bin_path_start$drive_letter$path_middle$bin_path_end"
|
|
echo "Full Bin Path: $full_bin_path" >>$LOGFILE 2>&1
|
|
full_path="$path_start$drive_letter$path_middle$path_end"
|
|
echo "Full Path: $full_path" >>$LOGFILE 2>&1
|
|
|
|
# Replace forwardslashes with double backslashes
|
|
new_path=${full_path//\//\\\\\\\\}
|
|
echo "New Path: $new_path" >>$LOGFILE 2>&1
|
|
|
|
# Convert the lines in ModOrganizer.ini, if it isn't already
|
|
|
|
sed -i "\|^${bin_path_start}|s|^.*$|${full_bin_path}|" "$modlist_ini"
|
|
# Convert workingDirectory entries
|
|
sed -i "\|^${path_start}|s|^.*$|${new_path}|" "$modlist_ini"
|
|
fi
|
|
|
|
}
|
|
|
|
#################################################
|
|
# Edit Custom binary and workingDirectory paths #
|
|
#################################################
|
|
|
|
edit_binary_working_paths() {
|
|
|
|
grep -E -e "skse64_loader\.exe" -e "f4se_loader\.exe" "$modlist_ini" | while IFS= read -r orig_line_path; do
|
|
update_executables
|
|
done
|
|
|
|
}
|
|
|
|
################################
|
|
# Set or Select the Resolution #
|
|
################################
|
|
|
|
select_resolution() {
|
|
if [ "$steamdeck" -eq 1 ]; then
|
|
set_res="1280x800"
|
|
else
|
|
while true; do
|
|
echo -e "\e[31m ** Enter your desired resolution in the format 1920x1200: ** \e[0m"
|
|
read -p " " user_res
|
|
|
|
# Validate the input format
|
|
if [[ "$user_res" =~ ^[0-9]+x[0-9]+$ ]]; then
|
|
# Ask for confirmation
|
|
echo -e "\e[31m \n** Is $user_res your desired resolution? (y/N): ** \e[0m"
|
|
read -p " " confirm
|
|
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
|
set_res="$user_res"
|
|
break
|
|
else
|
|
echo "Please enter the resolution again." | tee -a $LOGFILE
|
|
fi
|
|
else
|
|
echo "Invalid input format. Please enter the resolution in the format 1920x1200." | tee -a $LOGFILE
|
|
fi
|
|
done
|
|
fi
|
|
|
|
echo "Resolution set to: $set_res" | tee -a $LOGFILE
|
|
}
|
|
|
|
######################################
|
|
# Update the resolution in INI files #
|
|
######################################
|
|
|
|
update_ini_resolution() {
|
|
|
|
echo -ne "\nEditing Resolution in prefs files... " | tee -a "$LOGFILE"
|
|
|
|
# Find all SSEDisplayTweaks.ini files in the specified directory and its subdirectories
|
|
ini_files=$(find "$modlist_dir" -name "SSEDisplayTweaks.ini")
|
|
|
|
if [[ "$gamevar" == "Skyrim Special Edition" && -n "$ini_files" ]]; then
|
|
while IFS= read -r ini_file; do
|
|
# Use awk to replace the lines with the new values, handling spaces in paths
|
|
awk -v res="$set_res" '/^(#?)Resolution[[:space:]]*=/ { print "Resolution=" res; next } \
|
|
/^(#?)Fullscreen[[:space:]]*=/ { print "Fullscreen=false"; next } \
|
|
/^(#?)#Fullscreen[[:space:]]*=/ { print "#Fullscreen=false"; next } \
|
|
/^(#?)Borderless[[:space:]]*=/ { print "Borderless=true"; next } \
|
|
/^(#?)#Borderless[[:space:]]*=/ { print "#Borderless=true"; next }1' "$ini_file" >"$ini_file.new"
|
|
|
|
cp "$ini_file.new" "$ini_file"
|
|
echo "Updated $ini_file with Resolution=$res, Fullscreen=false, Borderless=true" >>"$LOGFILE" 2>&1
|
|
echo -e " Done." >>"$LOGFILE" 2>&1
|
|
done <<<"$ini_files"
|
|
elif [[ "$gamevar" == "Fallout 4" ]]; then
|
|
echo "Not Skyrim, skipping SSEDisplayTweaks" >>"$LOGFILE" 2>&1
|
|
fi
|
|
|
|
##########
|
|
|
|
# Split $set_res into two variables
|
|
isize_w=$(echo "$set_res" | cut -d'x' -f1)
|
|
isize_h=$(echo "$set_res" | cut -d'x' -f2)
|
|
|
|
# Find all instances of skyrimprefs.ini, Fallout4Prefs.ini, falloutprefs.ini, or Oblivion.ini in specified directories
|
|
|
|
if [[ "$gamevar" == "Skyrim Special Edition" ]]; then
|
|
ini_files=$(find "$modlist_dir/profiles" "$modlist_dir/Stock Game" "$modlist_dir/Game Root" "$modlist_dir/STOCK GAME" "$modlist_dir/Stock Game Folder" "$modlist_dir/Stock Folder" "$modlist_dir/Skyrim Stock" -iname "skyrimprefs.ini" 2>/dev/null)
|
|
elif [[ "$gamevar" == "Fallout 4" ]]; then
|
|
ini_files=$(find "$modlist_dir/profiles" "$modlist_dir/Stock Game" "$modlist_dir/Game Root" "$modlist_dir/STOCK GAME" "$modlist_dir/Stock Game Folder" "$modlist_dir/Stock Folder" -iname "Fallout4Prefs.ini" 2>/dev/null)
|
|
elif [[ "$gamevar" == "Fallout New Vegas" ]]; then
|
|
ini_files=$(find "$modlist_dir/profiles" "$modlist_dir/Stock Game" "$modlist_dir/Game Root" "$modlist_dir/STOCK GAME" "$modlist_dir/Stock Game Folder" "$modlist_dir/Stock Folder" -iname "falloutprefs.ini" 2>/dev/null)
|
|
elif [[ "$gamevar" == "Oblivion" ]]; then
|
|
ini_files=$(find "$modlist_dir/profiles" "$modlist_dir/Stock Game" "$modlist_dir/Game Root" "$modlist_dir/STOCK GAME" "$modlist_dir/Stock Game Folder" "$modlist_dir/Stock Folder" -iname "Oblivion.ini" 2>/dev/null)
|
|
fi
|
|
|
|
if [ -n "$ini_files" ]; then
|
|
while IFS= read -r ini_file; do
|
|
# Use awk to replace the lines with the new values in the appropriate ini file
|
|
if [[ "$gamevar" == "Skyrim Special Edition" ]] || [[ "$gamevar" == "Fallout 4" ]] || [[ "$gamevar" == "Fallout New Vegas" ]]; then
|
|
awk -v isize_w="$isize_w" -v isize_h="$isize_h" '/^iSize W/ { print "iSize W = " isize_w; next } \
|
|
/^iSize H/ { print "iSize H = " isize_h; next }1' "$ini_file" >"$HOME/temp_file" && mv "$HOME/temp_file" "$ini_file"
|
|
elif [[ "$gamevar" == "Oblivion" ]]; then
|
|
awk -v isize_w="$isize_w" -v isize_h="$isize_h" '/^iSize W=/ { print "iSize W=" isize_w; next } \
|
|
/^iSize H=/ { print "iSize H=" isize_h; next }1' "$ini_file" >"$HOME/temp_file" && mv "$HOME/temp_file" "$ini_file"
|
|
fi
|
|
|
|
echo "Updated $ini_file with iSize W=$isize_w, iSize H=$isize_h" >>"$LOGFILE" 2>&1
|
|
done <<<"$ini_files"
|
|
else
|
|
echo "No suitable prefs.ini files found in specified directories. Please set manually using the INI Editor in MO2." | tee -a "$LOGFILE"
|
|
fi
|
|
|
|
echo -e "Done." | tee -a "$LOGFILE"
|
|
|
|
}
|
|
|
|
###################
|
|
# Edit resolution #
|
|
###################
|
|
|
|
edit_resolution() {
|
|
if [[ -n "$selected_resolution" ]]; then
|
|
log_status "DEBUG" "Applying resolution: $selected_resolution"
|
|
set_res="$selected_resolution"
|
|
update_ini_resolution
|
|
else
|
|
log_status "DEBUG" "Resolution setup skipped"
|
|
fi
|
|
}
|
|
|
|
##########################
|
|
# Small additional tasks #
|
|
##########################
|
|
|
|
small_additional_tasks() {
|
|
|
|
# Delete MO2 plugins that don't work via Proton
|
|
|
|
file_to_delete="$modlist_dir/plugins/FixGameRegKey.py"
|
|
|
|
if [ -e "$file_to_delete" ]; then
|
|
rm "$file_to_delete"
|
|
echo "File deleted: $file_to_delete" >>$LOGFILE 2>&1
|
|
else
|
|
echo "File does not exist: $file_to_delete" >>"$LOGFILE" 2>&1
|
|
fi
|
|
|
|
# Download Font to support Bethini
|
|
wget https://github.com/mrbvrz/segoe-ui-linux/raw/refs/heads/master/font/seguisym.ttf -q -nc -O "$compat_data_path/pfx/drive_c/windows/Fonts/seguisym.ttf"
|
|
|
|
}
|
|
|
|
###############################
|
|
# Set Steam Artwork Function #
|
|
###############################
|
|
|
|
set_steam_artwork() {
|
|
# Only run for Tuxborn modlist
|
|
if [[ "$MODLIST" == *"Tuxborn"* ]]; then
|
|
log_status "DEBUG" "Setting up Steam artwork for Tuxborn..."
|
|
|
|
# Source directory with artwork
|
|
local source_dir="$modlist_dir/Steam Icons"
|
|
|
|
if [[ ! -d "$source_dir" ]]; then
|
|
log_status "WARN" "Steam Icons directory not found at $source_dir"
|
|
return 1
|
|
fi
|
|
|
|
# Find all Steam userdata directories
|
|
for userdata_dir in "$HOME/.local/share/Steam/userdata" "$HOME/.steam/steam/userdata"; do
|
|
if [[ ! -d "$userdata_dir" ]]; then
|
|
continue
|
|
fi
|
|
|
|
# Process each user ID directory
|
|
for user_id_dir in "$userdata_dir"/*; do
|
|
if [[ ! -d "$user_id_dir" || "$user_id_dir" == *"0"* ]]; then
|
|
continue # Skip non-directories and the anonymous user
|
|
fi
|
|
|
|
# Create grid directory if it doesn't exist
|
|
local grid_dir="$user_id_dir/config/grid"
|
|
mkdir -p "$grid_dir"
|
|
|
|
# Copy grid-tall.png to both APPID.png and APPIDp.png
|
|
if [[ -f "$source_dir/grid-tall.png" ]]; then
|
|
cp "$source_dir/grid-tall.png" "$grid_dir/${APPID}.png"
|
|
log_status "DEBUG" "Copied grid-tall.png to ${APPID}.png"
|
|
cp "$source_dir/grid-tall.png" "$grid_dir/${APPID}p.png"
|
|
log_status "DEBUG" "Copied grid-tall.png to ${APPID}p.png"
|
|
fi
|
|
|
|
# Copy grid-hero.png to APPID_hero.png
|
|
if [[ -f "$source_dir/grid-hero.png" ]]; then
|
|
cp "$source_dir/grid-hero.png" "$grid_dir/${APPID}_hero.png"
|
|
log_status "DEBUG" "Copied grid-hero.png to ${APPID}_hero.png"
|
|
fi
|
|
|
|
# Copy grid-logo.png to APPID_logo.png
|
|
if [[ -f "$source_dir/grid-logo.png" ]]; then
|
|
cp "$source_dir/grid-logo.png" "$grid_dir/${APPID}_logo.png"
|
|
log_status "DEBUG" "Copied grid-logo.png to ${APPID}_logo.png"
|
|
fi
|
|
|
|
log_status "DEBUG" "Tuxborn artwork copied for user ID $(basename "$user_id_dir")"
|
|
done
|
|
done
|
|
|
|
log_status "DEBUG" "Steam artwork setup complete for Tuxborn"
|
|
fi
|
|
}
|
|
|
|
|
|
##########################
|
|
# Modlist Specific Steps #
|
|
##########################
|
|
|
|
modlist_specific_steps() {
|
|
local modlist_lower=$(echo "${MODLIST// /}" | tr '[:upper:]' '[:lower:]')
|
|
|
|
# Call the Steam artwork function for all modlists
|
|
set_steam_artwork | tee -a "$LOGFILE"
|
|
|
|
# Handle Wildlander specially due to its custom spinner animation
|
|
if [[ "$MODLIST" == *"Wildlander"* ]]; then
|
|
log_status "INFO" "\nRunning steps specific to \e[32m$MODLIST\e[0m. This can take some time, be patient!"
|
|
|
|
# Install dotnet with spinner animation
|
|
spinner=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
|
run_protontricks --no-bwrap "$APPID" -q dotnet472 >/dev/null 2>&1 &
|
|
|
|
pid=$! # Store the PID of the background process
|
|
|
|
while kill -0 "$pid" >/dev/null 2>&1; do
|
|
for i in "${spinner[@]}"; do
|
|
echo -en "\r${i}\c"
|
|
sleep 0.1
|
|
done
|
|
done
|
|
|
|
wait "$pid" # Wait for the process to finish
|
|
|
|
# Clear the spinner and move to the next line
|
|
echo -en "\r\033[K" # Clear the spinner line
|
|
|
|
if [[ $? -ne 0 ]]; then
|
|
log_status "ERROR" "Component install failed with exit code $?"
|
|
else
|
|
log_status "SUCCESS" "Wine Component install completed successfully."
|
|
fi
|
|
|
|
new_output="$(run_protontricks --no-bwrap "$APPID" list-installed 2>/dev/null)"
|
|
log_status "DEBUG" "Components Found: $new_output"
|
|
return 0
|
|
fi
|
|
|
|
# Handle the rest of the modlists with the compact approach
|
|
for pattern in "${!modlist_configs[@]}"; do
|
|
if [[ "$pattern" != "wildlander" ]] && [[ "$modlist_lower" =~ ${pattern//|/|.*} ]]; then
|
|
log_status "INFO" "\nRunning steps specific to \e[32m$MODLIST\e[0m. This can take some time, be patient!"
|
|
|
|
IFS=' ' read -ra components <<< "${modlist_configs[$pattern]}"
|
|
for component in "${components[@]}"; do
|
|
if [[ "$component" == "dotnet8" ]]; then
|
|
log_status "INFO" "\nDownloading .NET 8 Runtime"
|
|
wget https://download.visualstudio.microsoft.com/download/pr/77284554-b8df-4697-9a9e-4c70a8b35f29/6763c16069d1ab8fa2bc506ef0767366/dotnet-runtime-8.0.5-win-x64.exe -q -nc --show-progress --progress=bar:force:noscroll -O "$HOME/Downloads/dotnet-runtime-8.0.5-win-x64.exe"
|
|
|
|
log_status "INFO" "Installing .NET 8 Runtime...."
|
|
WINEDEBUG=-all run_protontricks --no-bwrap -c 'wine "$HOME/Downloads/dotnet-runtime-8.0.5-win-x64.exe" /Q' "$APPID" >/dev/null 2>&1
|
|
log_status "SUCCESS" "Done."
|
|
else
|
|
log_status "INFO" "Installing .NET ${component#dotnet}..."
|
|
WINEDEBUG=-all run_protontricks --no-bwrap "$APPID" -q "$component" >/dev/null 2>&1
|
|
log_status "SUCCESS" "Done."
|
|
fi
|
|
done
|
|
|
|
set_win10_prefix
|
|
new_output="$(run_protontricks --no-bwrap "$APPID" list-installed 2>/dev/null)"
|
|
log_status "DEBUG" "Components Found: $new_output"
|
|
break
|
|
fi
|
|
done
|
|
}
|
|
|
|
######################################
|
|
# Create DXVK Graphics Pipeline file #
|
|
######################################
|
|
|
|
create_dxvk_file() {
|
|
echo "Use SDCard for DXVK File?: $basegame_sdcard" >>"$LOGFILE" 2>&1
|
|
echo -e "\nCreating dxvk.conf file - Checking if Modlist uses Game Root, Stock Game or Vanilla Game Directory.." >>"$LOGFILE" 2>&1
|
|
|
|
game_path_line=$(grep '^gamePath' "$modlist_ini")
|
|
echo "Game Path Line: $game_path_line" >>"$LOGFILE" 2>&1
|
|
|
|
if [[ "$game_path_line" == *Stock\ Game* || "$game_path_line" == *STOCK\ GAME* || "$game_path_line" == *Stock\ Game\ Folder* || "$game_path_line" == *Stock\ Folder* || "$game_path_line" == *Skyrim\ Stock* || "$game_path_line" == *Game\ Root* ]]; then
|
|
# Get the end of our path
|
|
if [[ $game_path_line =~ Stock\ Game\ Folder ]]; then
|
|
echo "dxvk.enableGraphicsPipelineLibrary = False" >"$modlist_dir/Stock Game Folder/dxvk.conf"
|
|
elif [[ $game_path_line =~ Stock\ Folder ]]; then
|
|
echo "dxvk.enableGraphicsPipelineLibrary = False" >"$modlist_dir/Stock Folder/dxvk.conf"
|
|
elif [[ $game_path_line =~ Skyrim\ Stock ]]; then
|
|
echo "dxvk.enableGraphicsPipelineLibrary = False" >"$modlist_dir/Skyrim Stock/dxvk.conf"
|
|
elif [[ $game_path_line =~ Game\ Root ]]; then
|
|
echo "dxvk.enableGraphicsPipelineLibrary = False" >"$modlist_dir/Game Root/dxvk.conf"
|
|
elif [[ $game_path_line =~ STOCK\ GAME ]]; then
|
|
echo "dxvk.enableGraphicsPipelineLibrary = False" >"$modlist_dir/STOCK GAME/dxvk.conf"
|
|
elif [[ $game_path_line =~ Stock\ Game ]]; then
|
|
echo "dxvk.enableGraphicsPipelineLibrary = False" >"$modlist_dir/Stock Game/dxvk.conf"
|
|
elif [[ $game_path_line =~ root\\Skyrim\ Special\ Edition ]]; then
|
|
echo "dxvk.enableGraphicsPipelineLibrary = False" >"$modlist_dir/root/Skyrim Special Edition/dxvk.conf"
|
|
fi
|
|
|
|
if [[ "$modlist_sdcard" -eq "1" ]]; then
|
|
echo "Using SDCard" >>"$LOGFILE" 2>&1
|
|
modlist_gamedir_sdcard="${modlist_gamedir#*mmcblk0p1}"
|
|
echo "dxvk.enableGraphicsPipelineLibrary = False" >"$modlist_gamedir/dxvk.conf"
|
|
fi
|
|
|
|
elif [[ "$game_path_line" == *steamapps* ]]; then
|
|
echo -ne "Vanilla Game Directory required, editing Game Path.. " >>"$LOGFILE" 2>&1
|
|
modlist_gamedir="$steam_library"
|
|
echo "dxvk.enableGraphicsPipelineLibrary = False" >"$modlist_gamedir/dxvk.conf"
|
|
if [[ "$basegame_sdcard" -eq "1" ]]; then
|
|
echo "Using SDCard" >>"$LOGFILE" 2>&1
|
|
modlist_gamedir_sdcard="${modlist_gamedir#*mmcblk0p1}"
|
|
echo "dxvk.enableGraphicsPipelineLibrary = False" >"$modlist_dir/$gamevar/dxvk.conf"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#############################
|
|
# Create protontricks alias #
|
|
#############################
|
|
|
|
protontricks_alias() {
|
|
if [[ "$which_protontricks" = "flatpak" ]]; then
|
|
local protontricks_alias_exists=$(grep "^alias protontricks=" ~/.bashrc)
|
|
local launch_alias_exists=$(grep "^alias protontricks-launch" ~/.bashrc)
|
|
|
|
if [[ -z "$protontricks_alias_exists" ]]; then
|
|
echo -e "\nAdding protontricks alias to ~/.bashrc"
|
|
echo "alias protontricks='flatpak run com.github.Matoking.protontricks'" >> ~/.bashrc
|
|
source ~/.bashrc
|
|
else
|
|
echo "protontricks alias already exists in ~/.bashrc" >> "$LOGFILE" 2>&1
|
|
fi
|
|
|
|
if [[ -z "$launch_alias_exists" ]]; then
|
|
echo -e "\nAdding protontricks-launch alias to ~/.bashrc"
|
|
echo "alias protontricks-launch='flatpak run --command=protontricks-launch com.github.Matoking.protontricks'" >> ~/.bashrc
|
|
source ~/.bashrc
|
|
else
|
|
echo "protontricks-launch alias already exists in ~/.bashrc" >> "$LOGFILE" 2>&1
|
|
fi
|
|
else
|
|
echo "Protontricks is not installed via flatpak, skipping alias creation." >> "$LOGFILE" 2>&1
|
|
fi
|
|
}
|
|
|
|
############################
|
|
# FNV Launch Option Notice #
|
|
############################
|
|
|
|
fnv_launch_options() {
|
|
log_status "DEBUG" "fnv_launch_options: gamevar='$gamevar', compat_data_path='$compat_data_path'"
|
|
if [[ "$gamevar" == "Fallout New Vegas" ]]; then
|
|
if [[ -n "$compat_data_path" && -d "$compat_data_path" ]]; then
|
|
log_status "WARN" "\nFor $MODLIST, please add the following line to the Launch Options in Steam for your '$MODLIST' entry:"
|
|
log_status "SUCCESS" "\nSTEAM_COMPAT_DATA_PATH=\"$compat_data_path\" %command%"
|
|
log_status "WARN" "\nThis is essential for the modlist to load correctly."
|
|
else
|
|
log_status "ERROR" "\nCould not determine the compatdata path for Fallout New Vegas. Please manually set the correct path in the Launch Options."
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#####################
|
|
# Exit more cleanly #
|
|
#####################
|
|
|
|
cleaner_exit() {
|
|
# Clean up wine and winetricks processes
|
|
cleanup_wine_procs
|
|
log_status "DEBUG" "Cleanup complete"
|
|
exit 1
|
|
}
|
|
|
|
####################
|
|
# END OF FUNCTIONS #
|
|
####################
|
|
|
|
#######################
|
|
# Note Script Version #
|
|
#######################
|
|
|
|
echo -e "Script Version $script_ver" >>"$LOGFILE" 2>&1
|
|
|
|
######################
|
|
# Note Date and Time #
|
|
######################
|
|
|
|
echo -e "Script started at: $(date +'%Y-%m-%d %H:%M:%S')" >>"$LOGFILE" 2>&1
|
|
|
|
#############################
|
|
# Detect if running on deck #
|
|
#############################
|
|
|
|
detect_steamdeck
|
|
|
|
###########################################
|
|
# Detect Protontricks (flatpak or native) #
|
|
###########################################
|
|
|
|
detect_protontricks
|
|
|
|
###############################
|
|
# Detect Protontricks Version #
|
|
###############################
|
|
|
|
protontricks_version
|
|
|
|
##########################################
|
|
# Create protontricks alias in ~/.bashrc #
|
|
##########################################
|
|
|
|
protontricks_alias
|
|
|
|
##############################################################
|
|
# List Skyrim and Fallout Modlists from Steam (protontricks) #
|
|
##############################################################
|
|
|
|
IFS=$'\n' readarray -t output_array < <(run_protontricks -l | tr -d $'\r' | grep -i 'Non-Steam shortcut' | grep -i 'Skyrim\|Fallout\|FNV\|Oblivion' | cut -d ' ' -f 3-)
|
|
|
|
if [[ ${#output_array[@]} -eq 0 ]]; then
|
|
echo "" | tee -a "$LOGFILE"
|
|
log_status "ERROR" "No modlists detected for Skyrim, Oblivion or Fallout/FNV!"
|
|
log_status "INFO" "Please make sure your entry in Steam is something like 'Skyrim - ModlistName'"
|
|
log_status "INFO" "or 'Fallout - ModlistName' AND that you have pressed play in Steam at least once!"
|
|
cleaner_exit
|
|
fi
|
|
|
|
echo "" | tee -a "$LOGFILE"
|
|
echo -e "\e[33mDetected Modlists:\e[0m" | tee -a "$LOGFILE"
|
|
|
|
# Print numbered list with color
|
|
for i in "${!output_array[@]}"; do
|
|
echo -e "\e[32m$((i + 1)))\e[0m ${output_array[$i]}"
|
|
done
|
|
|
|
# Read user selection with proper prompt
|
|
echo "───────────────────────────────────────────────────────────────────"
|
|
while true; do
|
|
read -p $'\e[33mSelect a modlist (1-'"${#output_array[@]}"$'): \e[0m' choice_num
|
|
|
|
# Add a debug flag at the top for easy toggling
|
|
DEBUG_MODLIST_SELECTION=0 # Set to 1 to enable extra debug output
|
|
|
|
# After reading user input for choice_num:
|
|
if [[ $DEBUG_MODLIST_SELECTION -eq 1 ]]; then
|
|
echo "[DEBUG] Raw user input: '$choice_num'" | tee -a "$LOGFILE"
|
|
fi
|
|
choice_num=$(echo "$choice_num" | xargs) # Trim whitespace
|
|
if [[ $DEBUG_MODLIST_SELECTION -eq 1 ]]; then
|
|
echo "[DEBUG] Trimmed user input: '$choice_num'" | tee -a "$LOGFILE"
|
|
fi
|
|
|
|
# Before the selection validation if-statement:
|
|
if [[ $DEBUG_MODLIST_SELECTION -eq 1 ]]; then
|
|
echo "[DEBUG] Validating: '$choice_num' =~ ^[0-9]+$ && $choice_num -ge 1 && $choice_num -le ${#output_array[@]}" | tee -a "$LOGFILE"
|
|
fi
|
|
|
|
# Validate selection properly
|
|
if [[ "$choice_num" =~ ^[0-9]+$ ]] && [[ "$choice_num" -ge 1 ]] && [[ "$choice_num" -le "${#output_array[@]}" ]]; then
|
|
if [[ $DEBUG_MODLIST_SELECTION -eq 1 ]]; then
|
|
echo "[DEBUG] Selection valid. Index: $((choice_num - 1)), Value: '${output_array[$((choice_num - 1))]}'" | tee -a "$LOGFILE"
|
|
fi
|
|
choice="${output_array[$((choice_num - 1))]}"
|
|
MODLIST=$(echo "$choice" | cut -d ' ' -f 3- | rev | cut -d ' ' -f 2- | rev)
|
|
log_status "DEBUG" "MODLIST: $MODLIST"
|
|
break # Exit the loop if selection is valid
|
|
else
|
|
if [[ $DEBUG_MODLIST_SELECTION -eq 1 ]]; then
|
|
echo "[DEBUG] Invalid selection. choice_num: '$choice_num', output_array length: ${#output_array[@]}" | tee -a "$LOGFILE"
|
|
fi
|
|
log_status "ERROR" "Invalid selection. Please enter a number between 1 and ${#output_array[@]}."
|
|
# Removed exit 1, so the loop continues
|
|
fi
|
|
done
|
|
|
|
# Add a newline after the selection for cleaner output
|
|
echo ""
|
|
|
|
# Initial detection phase
|
|
cleanup_wine_procs
|
|
set_appid
|
|
detect_game
|
|
detect_steam_library
|
|
detect_modlist_dir_path
|
|
|
|
# Set modlist_sdcard if required
|
|
modlist_sdcard=0
|
|
if [[ "$modlist_dir" =~ ^/run/media ]]; then
|
|
modlist_sdcard=1
|
|
fi
|
|
|
|
# Detect compatdata path and Proton version
|
|
detect_compatdata_path
|
|
detect_proton_version
|
|
fnv_launch_options
|
|
|
|
# Get resolution preference
|
|
if [ "$steamdeck" -eq 1 ]; then
|
|
selected_resolution="1280x800"
|
|
log_status "INFO" "Steam Deck detected - Resolution will be set to 1280x800"
|
|
else
|
|
echo -e "Do you wish to set the display resolution? (This can be changed manually later)"
|
|
read -p $'\e[33mSet resolution? (y/N): \e[0m' response
|
|
|
|
if [[ "$response" =~ ^[Yy]$ ]]; then
|
|
while true; do
|
|
read -p $'\e[33mEnter resolution (e.g., 1920x1080): \e[0m' user_res
|
|
if [[ "$user_res" =~ ^[0-9]+x[0-9]+$ ]]; then
|
|
selected_resolution="$user_res"
|
|
log_status "DEBUG" "Resolution will be set to: $selected_resolution"
|
|
break
|
|
else
|
|
log_status "ERROR" "Invalid format. Please use format: 1920x1080"
|
|
fi
|
|
done
|
|
else
|
|
log_status "INFO" "Resolution setup skipped"
|
|
fi
|
|
fi
|
|
|
|
# Then show the detection summary including the resolution if set
|
|
echo -e "\n\e[1mDetection Summary:\e[0m" | tee -a "$LOGFILE"
|
|
echo -e "===================" | tee -a "$LOGFILE"
|
|
echo -e "Selected Modlist: \e[32m$MODLIST\e[0m" | tee -a "$LOGFILE"
|
|
echo -e "Game Type: \e[32m$gamevar\e[0m" | tee -a "$LOGFILE"
|
|
echo -e "Steam App ID: \e[32m$APPID\e[0m" | tee -a "$LOGFILE"
|
|
echo -e "Modlist Directory: \e[32m$modlist_dir\e[0m" | tee -a "$LOGFILE"
|
|
echo -e "Proton Version: \e[32m$proton_ver\e[0m" | tee -a "$LOGFILE"
|
|
if [[ -n "$selected_resolution" ]]; then
|
|
echo -e "Resolution: \e[32m$selected_resolution\e[0m" | tee -a "$LOGFILE"
|
|
fi
|
|
|
|
# Show simple confirmation with minimal info
|
|
read -rp $'\e[32mDo you want to proceed with the installation? (y/N)\e[0m ' proceed
|
|
|
|
if [[ $proceed =~ ^[Yy]$ ]]; then
|
|
# Function to update progress
|
|
update_progress() {
|
|
local percent=$1
|
|
local bar_length=50
|
|
local filled_length=$((percent * bar_length / 100))
|
|
local bar=""
|
|
|
|
# Create the bar string with = for filled portions
|
|
for ((i = 0; i < bar_length; i++)); do
|
|
if [ $i -lt $filled_length ]; then
|
|
bar+="="
|
|
else
|
|
bar+=" "
|
|
fi
|
|
done
|
|
|
|
# Use \r to return to start of line and overwrite previous progress
|
|
printf "\r[%-${bar_length}s] %d%%" "$bar" "$percent"
|
|
}
|
|
|
|
{
|
|
# Add newline before progress bar starts
|
|
echo ""
|
|
|
|
# Protontricks setup (10%)
|
|
printf "\r\033[KProgress: [%-50s] %d%% - Setting up Protontricks..." " " "10"
|
|
set_protontricks_perms >/dev/null 2>&1
|
|
|
|
# Dotfiles (20%)
|
|
printf "\r\033[KProgress: [%-50s] %d%% - Enabling dotfiles..." "========== " "20"
|
|
enable_dotfiles >/dev/null 2>&1
|
|
|
|
# Wine components (40%)
|
|
printf "\r\033[KProgress: [%-50s] %d%% - Installing Wine components..." "==================== " "40"
|
|
install_wine_components >/dev/null 2>&1
|
|
|
|
# Windows 10 prefix (50%)
|
|
printf "\r\033[KProgress: [%-50s] %d%% - Setting Windows 10 prefix..." "========================= " "50"
|
|
set_win10_prefix >/dev/null 2>&1
|
|
|
|
# ModOrganizer configuration (70%)
|
|
printf "\r\033[KProgress: [%-50s] %d%% - Configuring Mod Organizer..." "=================================== " "70"
|
|
backup_modorganizer >/dev/null 2>&1
|
|
blank_downloads_dir >/dev/null 2>&1
|
|
replace_gamepath >/dev/null 2>&1
|
|
edit_binary_working_paths >/dev/null 2>&1
|
|
|
|
# Resolution and additional tasks (90%)
|
|
printf "\r\033[KProgress: [%-50s] %d%% - Setting resolution and additional tasks..." "============================================ " "90"
|
|
edit_resolution >/dev/null 2>&1
|
|
small_additional_tasks >/dev/null 2>&1
|
|
create_dxvk_file >/dev/null 2>&1
|
|
|
|
# Final steps (100%)
|
|
printf "\r\033[KProgress: [%-50s] %d%% - Completing installation...\n" "==================================================" "100"
|
|
|
|
# Remove user-facing artwork and debug output
|
|
# echo "" # Add spacing
|
|
# echo "ABOUT TO CALL MODLIST_SPECIFIC_STEPS FOR: $MODLIST" | tee -a "$LOGFILE"
|
|
modlist_specific_steps
|
|
# echo "FINISHED CALLING MODLIST_SPECIFIC_STEPS" | tee -a "$LOGFILE"
|
|
|
|
# Add two newlines after progress bar completes
|
|
# printf "\n\n"
|
|
|
|
chown_chmod_modlist_dir
|
|
fnv_launch_options >/dev/null 2>&1
|
|
|
|
} 2>>$LOGFILE
|
|
|
|
# Show completion message
|
|
{
|
|
echo "" # Add blank line before success message
|
|
echo -e "\e[32m✓ Installation completed successfully!\e[0m"
|
|
echo -e "\n📝 Next Steps:"
|
|
echo " • Launch your modlist through Steam"
|
|
echo " • When Mod Organizer opens, verify the game path is correct"
|
|
if [[ "$gamevar" == "Skyrim Special Edition" || "$gamevar" == "Fallout 4" ]]; then
|
|
echo " • Run the game through SKSE/F4SE launcher"
|
|
fi
|
|
echo -e "\n💡 Detailed log available at: $LOGFILE\n"
|
|
} | tee -a "$LOGFILE"
|
|
|
|
# Show SD Card status if detected
|
|
if [[ "$steamdeck" -eq 1 ]]; then
|
|
# On Steam Deck, SD card is /run/media/deck/<UUID> or /run/media/mmcblk0p1
|
|
if [[ "$modlist_dir" =~ ^/run/media/deck/[^/]+(/.*)?$ ]] || [[ "$modlist_dir" == "/run/media/mmcblk0p1"* ]]; then
|
|
echo -e "SD Card: \e[32mDetected\e[0m" | tee -a "$LOGFILE"
|
|
fi
|
|
else
|
|
# On non-Deck, just show the path if it's /run/media, but don't call it SD card
|
|
if [[ "$modlist_dir" == "/run/media"* ]]; then
|
|
echo -e "Removable Media: \e[33mDetected at $modlist_dir\e[0m" | tee -a "$LOGFILE"
|
|
fi
|
|
fi
|
|
else
|
|
log_status "INFO" "Installation cancelled."
|
|
cleaner_exit
|
|
fi
|
|
|
|
# After the block that prints the completion message and next steps:
|
|
# (Find the line: echo -e "\n💡 Detailed log available at: $LOGFILE\n")
|
|
# Add this immediately after:
|
|
fnv_launch_options |