Skip to content

Instantly share code, notes, and snippets.

@looselyrigorous
Last active February 19, 2026 02:04
Show Gist options
  • Select an option

  • Save looselyrigorous/201468b712696d24edc5287aa4e7d100 to your computer and use it in GitHub Desktop.

Select an option

Save looselyrigorous/201468b712696d24edc5287aa4e7d100 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# install-shaders.sh — Download and install ReShade shaders
#
# Usage:
# ./install-shaders.sh [--pick]
#
# Flags:
#
# --pick
# Force interactive fzf picker mode (overrides auto-detection).
# Required packages (e.g. "Standard effects") are always installed
# and are not shown in the picker. Requires fzf.
#
# Environment variables:
#
# INTERACTIVE
# Set to 1 to force interactive mode, 0 to disable.
# Default: auto-detect (enabled if fzf is installed and INCLUDE_REPOS is not set)
#
# INCLUDE_REPOS
# Comma-separated list of package names to include (case-insensitive substring match).
# If set, disables interactive mode and uses the specified filter instead.
# If unset and fzf is not available, ONLY required packages are installed.
# Example: INCLUDE_REPOS="SweetFX,qUINT,iMMERSE" ./install-shaders.sh
#
# MAIN_PATH
# Override the base reshade data path.
# Default: $XDG_DATA_HOME/reshade (typically ~/.local/share/reshade)
#
# UPDATE_SHADERS
# Set to 0 to skip running "git pull" on already-cloned repos. Default: 1
#
# DRY_RUN
# Set to 1 to preview actions without making changes. Default: 0
#
# REBUILD_MERGE
# Set to 0 to skip clearing the Merged/ directory before rebuilding.
# Default: 1 (always rebuild from scratch to remove stale links)
set -euo pipefail
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
# Parse command-line flags first
for arg in "$@"; do
case "$arg" in
--pick) INTERACTIVE=1 ;;
*)
echo "Unknown option: $arg" >&2
echo "Usage: $0 [--pick]" >&2
exit 1
;;
esac
done
# Auto-detect interactive mode if not explicitly set
if [[ -z "${INTERACTIVE:-}" ]]; then
# Enable interactive mode if fzf is available AND INCLUDE_REPOS is not set
if command -v fzf &>/dev/null && [[ -z "${INCLUDE_REPOS:-}" ]]; then
INTERACTIVE=1
else
INTERACTIVE=0
fi
else
INTERACTIVE="${INTERACTIVE}"
fi
XDG_DATA_HOME="${XDG_DATA_HOME:-"$HOME/.local/share"}"
MAIN_PATH="${MAIN_PATH:-"$XDG_DATA_HOME/reshade"}"
MERGED_DIR="$MAIN_PATH/ReShade_shaders/Merged"
UPDATE_SHADERS="${UPDATE_SHADERS:-1}"
DRY_RUN="${DRY_RUN:-0}"
REBUILD_MERGE="${REBUILD_MERGE:-1}"
PACKAGES_URL="https://raw.githubusercontent.com/crosire/reshade-shaders/refs/heads/list/EffectPackages.ini"
SEPARATOR="───────────────────────────────────────────────────────────────────────────"
# ---------------------------------------------------------------------------
# Dependency check
# ---------------------------------------------------------------------------
for cmd in curl git; do
if ! command -v "$cmd" &>/dev/null; then
echo "Error: Required program '$cmd' is not installed." >&2
exit 1
fi
done
if [[ "$INTERACTIVE" -eq 1 ]] && ! command -v fzf &>/dev/null; then
echo "Error: Interactive mode requires 'fzf' but it is not installed." >&2
echo "Install fzf or set INTERACTIVE=0 to disable interactive mode." >&2
exit 1
fi
# ---------------------------------------------------------------------------
# Include filter
# ---------------------------------------------------------------------------
declare -a INCLUDES=()
if [[ -n "${INCLUDE_REPOS:-}" ]]; then
IFS=',' read -ra INCLUDES <<< "$(echo "$INCLUDE_REPOS" | tr '[:upper:]' '[:lower:]')"
for i in "${!INCLUDES[@]}"; do
INCLUDES[$i]="$(echo "${INCLUDES[$i]}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
done
fi
# In interactive mode, this array is populated by the fzf picker.
declare -A INTERACTIVE_SELECTED=()
# Populated after parsing; required packages are always installed.
declare -A REQUIRED_NAMES=()
is_included() {
local name="$1"
# Always include required packages (matches ReShade installer's Required=1)
if [[ -n "${REQUIRED_NAMES[$name]+x}" ]]; then
return 0
fi
# Interactive mode: check the picker selections
if [[ "$INTERACTIVE" -eq 1 ]]; then
[[ -n "${INTERACTIVE_SELECTED[$name]+x}" ]]
return $?
fi
# No filter specified: only required packages
if [[ ${#INCLUDES[@]} -eq 0 ]]; then
return 1
fi
local name_lower
name_lower="$(echo "$name" | tr '[:upper:]' '[:lower:]')"
for pattern in "${INCLUDES[@]}"; do
if [[ "$name_lower" == *"$pattern"* ]]; then
return 0
fi
done
return 1
}
# ---------------------------------------------------------------------------
# Comma-separated list helper (for DenyEffectFiles)
# ---------------------------------------------------------------------------
is_in_csv() {
local needle="$1"
local csv="$2"
[[ -z "$csv" ]] && return 1
local IFS=','
for entry in $csv; do
entry="${entry#"${entry%%[![:space:]]*}"}" # ltrim
entry="${entry%"${entry##*[![:space:]]}"}" # rtrim
if [[ "$needle" == "$entry" ]]; then
return 0
fi
done
return 1
}
# ---------------------------------------------------------------------------
# Git helpers
# ---------------------------------------------------------------------------
# Extract git clone URL and branch from RepositoryUrl / DownloadUrl.
# The ReShade installer uses DownloadUrl (a GitHub archive zip URL) which
# encodes the branch. We extract that for git clone --branch.
get_git_info() {
local repo_url="$1"
local download_url="$2"
local git_url=""
local branch=""
# Try RepositoryUrl first (may contain /tree/<branch>)
if [[ "$repo_url" =~ ^(https://github\.com/[^/]+/[^/]+)/tree/(.+)$ ]]; then
git_url="${BASH_REMATCH[1]}"
branch="${BASH_REMATCH[2]}"
elif [[ "$repo_url" =~ ^(https://github\.com/[^/]+/[^/]+)/?$ ]]; then
git_url="${BASH_REMATCH[1]}"
fi
# Extract branch from DownloadUrl if we don't have one yet
if [[ -z "$branch" && -n "$download_url" ]]; then
if [[ "$download_url" =~ /archive/refs/heads/(.+)\.zip$ ]]; then
branch="${BASH_REMATCH[1]}"
elif [[ "$download_url" =~ /archive/(.+)\.zip$ ]]; then
branch="${BASH_REMATCH[1]}"
fi
fi
# Fallback: extract git URL from DownloadUrl
if [[ -z "$git_url" && -n "$download_url" ]]; then
if [[ "$download_url" =~ (https://github\.com/[^/]+/[^/]+)/archive/ ]]; then
git_url="${BASH_REMATCH[1]}"
fi
fi
printf '%s|%s' "$git_url" "$branch"
}
# Produce a filesystem-safe local directory name from a git URL + branch.
make_local_name() {
local git_url="$1"
local branch="$2"
if [[ "$git_url" =~ github\.com/([^/]+)/([^/]+)$ ]]; then
local owner="${BASH_REMATCH[1]}"
local repo="${BASH_REMATCH[2]}"
local base
base="$(printf '%s' "${owner}-${repo}" | tr '[:upper:]' '[:lower:]' | tr -c '[:alnum:]-' '-' | sed 's/-*$//')"
if [[ -n "$branch" && "$branch" != "master" && "$branch" != "main" ]]; then
local clean_branch
clean_branch="$(printf '%s' "$branch" | tr '[:upper:]' '[:lower:]' | tr -c '[:alnum:]-' '-' | sed 's/-*$//')"
base="${base}-${clean_branch}"
fi
echo "$base"
else
printf '%s' "$git_url" | md5sum | cut -c1-12
fi
}
# ---------------------------------------------------------------------------
# InstallPath parsing
#
# The official EffectPackages.ini uses Windows-style relative paths like:
# .\reshade-shaders\Shaders\OtisFX
# .\reshade-shaders\Textures\OtisFX
# .\reshade-shaders\Shaders (root — no subdirectory)
# .\reshade-shaders\Textures (root — no subdirectory)
#
# We extract the subdirectory portion AFTER "Shaders" or "Textures" so we
# can place files under Merged/Shaders/<subdir>/ or Merged/Textures/<subdir>/
# ---------------------------------------------------------------------------
# Given an InstallPath like ".\reshade-shaders\Shaders\OtisFX", return "OtisFX".
# For root paths like ".\reshade-shaders\Shaders" or empty, return "".
extract_subdir() {
local install_path="$1"
local kind="$2" # "Shaders" or "Textures"
# Normalise: convert backslashes to forward slashes, strip leading ./ or .\
local norm
norm="$(echo "$install_path" | tr '\\' '/' | sed 's|^\./||')"
# Strip trailing slash
norm="${norm%/}"
# Match Shaders or Textures as a full path component (preceded by /),
# NOT as a substring (e.g. "reshade-shaders" contains "shaders").
# We want: .../Shaders/OtisFX → "OtisFX"
# .../Shaders → ""
# .../Shaders/ → ""
local lower
lower="$(echo "$norm" | tr '[:upper:]' '[:lower:]')"
local kind_lower
kind_lower="$(echo "$kind" | tr '[:upper:]' '[:lower:]')"
# Match: /shaders/subpath (as a full path component, preceded by /)
if [[ "$lower" =~ /${kind_lower}/(.+)$ ]]; then
local prefix_len=$(( ${#lower} - ${#BASH_REMATCH[0]} + 1 + ${#kind_lower} + 1 ))
echo "${norm:$prefix_len}"
# Match: /shaders at end (no trailing subpath)
elif [[ "$lower" =~ /${kind_lower}$ ]]; then
echo ""
else
echo ""
fi
}
# ---------------------------------------------------------------------------
# File linking
#
# Mirrors the ReShade installer's MoveFiles() — recursively copies/links all
# files from source to target, preserving directory structure.
#
# We use symlinks instead of copies so that git pull updates propagate.
# Denied files are skipped. Conflicts are resolved first-come-first-served
# (matching ReShade installer behavior where the first search path wins).
# ---------------------------------------------------------------------------
link_tree() {
local src_dir="$1" # Source directory (inside the cloned repo)
local dst_dir="$2" # Destination under Merged/
local deny_list="$3" # Comma-separated filenames to deny
[[ -d "$src_dir" ]] || return 0
if [[ "$DRY_RUN" -eq 0 ]]; then
mkdir -p "$dst_dir"
fi
# Link files in this directory
local file
for file in "$src_dir"/*; do
[[ -e "$file" ]] || continue
local basename
basename="$(basename "$file")"
if [[ -d "$file" ]]; then
# Recurse into subdirectories (mirrors MoveFiles recursive behavior)
link_tree "$file" "$dst_dir/$basename" "$deny_list"
continue
fi
# Skip .addon files (same as ReShade installer MoveFiles)
case "$basename" in
*.addon|*.addon32|*.addon64) continue ;;
esac
# Skip denied effect files
if [[ -n "$deny_list" ]] && is_in_csv "$basename" "$deny_list"; then
echo " ✗ denied: $basename"
continue
fi
# Skip if target already exists (first-come-first-served)
if [[ -L "$dst_dir/$basename" || -e "$dst_dir/$basename" ]]; then
continue
fi
local real_src
real_src="$(realpath "$file")"
if [[ "$DRY_RUN" -eq 1 ]]; then
echo " → would link: $basename"
else
ln -s "$real_src" "$dst_dir/$basename"
fi
done
}
# ---------------------------------------------------------------------------
# Find a directory by name (case-insensitive) inside a repo clone.
#
# The ReShade installer does:
# tempPathEffects = Directory.EnumerateDirectories(tempPath, "Shaders", SearchOption.AllDirectories).FirstOrDefault();
#
# We replicate this: find the first directory named "Shaders" (or "Textures")
# anywhere inside the repo, case-insensitively.
# ---------------------------------------------------------------------------
find_dir_ci() {
local base="$1"
local name="$2"
find "$base" ! -path "$base" -type d -iname "$name" -print -quit 2>/dev/null
}
# ---------------------------------------------------------------------------
# Process a single parsed package
# ---------------------------------------------------------------------------
process_package() {
local name="$1"
local repo_url="$2"
local download_url="$3"
local deny_files="$4"
local install_path="$5"
local texture_install_path="$6"
# Skip if not in the include filter
if [[ -n "$name" ]] && ! is_included "$name"; then
((skipped_count++)) || true
return 0
fi
# Determine git URL and branch
local git_info
git_info="$(get_git_info "$repo_url" "$download_url")"
local git_url="${git_info%%|*}"
local branch="${git_info#*|}"
if [[ -z "$git_url" ]]; then
echo " ⚠ Could not determine git URL for '$name', skipping."
return 0
fi
local local_name
local_name="$(make_local_name "$git_url" "$branch")"
local repo_dir="$MAIN_PATH/ReShade_shaders/$local_name"
# Determine the subdirectory under Shaders/ and Textures/ from InstallPath
local shaders_subdir
shaders_subdir="$(extract_subdir "$install_path" "Shaders")"
local textures_subdir
textures_subdir="$(extract_subdir "$texture_install_path" "Textures")"
echo " 📦 $name"
echo " repo: $git_url (branch: ${branch:-default})"
echo " shaders → Merged/Shaders/${shaders_subdir:-(root)}"
echo " textures→ Merged/Textures/${textures_subdir:-(root)}"
[[ -n "$deny_files" ]] && echo " deny: $deny_files"
if [[ "$DRY_RUN" -eq 1 ]]; then
echo " [DRY RUN] would clone/update to $repo_dir"
((installed_count++)) || true
return 0
fi
# Clone or update the repository
if [[ -d "$repo_dir" ]]; then
if [[ "$UPDATE_SHADERS" -eq 1 ]]; then
echo " updating..."
(cd "$repo_dir" && git pull --quiet 2>&1) || echo " ⚠ git pull failed for $git_url"
else
echo " already cloned, skipping update"
fi
else
echo " cloning..."
local branch_args=()
if [[ -n "$branch" ]]; then
branch_args=(--branch "$branch")
fi
if ! git clone --quiet "${branch_args[@]}" "$git_url" "$repo_dir" 2>&1; then
echo " ⚠ git clone failed for $git_url"
return 0
fi
fi
# Find the Shaders and Textures directories inside the repo (case-insensitive)
# This mirrors the ReShade installer's behavior:
# tempPathEffects = Directory.EnumerateDirectories(tempPath, "Shaders", ...).FirstOrDefault();
# tempPathTextures = Directory.EnumerateDirectories(tempPath, "Textures", ...).FirstOrDefault();
local repo_shaders_dir
repo_shaders_dir="$(find_dir_ci "$repo_dir" "Shaders")"
local repo_textures_dir
repo_textures_dir="$(find_dir_ci "$repo_dir" "Textures")"
# Fallback: if no Shaders dir, look for directory containing .fx files
# (mirrors installer: effects.Select(x => Path.GetDirectoryName(x)).OrderBy(x => x.Length).FirstOrDefault())
if [[ -z "$repo_shaders_dir" ]]; then
local first_fx
first_fx="$(find "$repo_dir" -type f -iname '*.fx' -print -quit 2>/dev/null)"
if [[ -n "$first_fx" ]]; then
repo_shaders_dir="$(dirname "$first_fx")"
fi
fi
# Fallback: if no Textures dir, look for directory containing images
if [[ -z "$repo_textures_dir" ]]; then
local first_tex
first_tex="$(find "$repo_dir" -type f \( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' \) -print -quit 2>/dev/null)"
if [[ -n "$first_tex" ]]; then
repo_textures_dir="$(dirname "$first_tex")"
fi
fi
# Build target paths under Merged/
local target_shaders="$MERGED_DIR/Shaders"
[[ -n "$shaders_subdir" ]] && target_shaders="$MERGED_DIR/Shaders/$shaders_subdir"
local target_textures="$MERGED_DIR/Textures"
[[ -n "$textures_subdir" ]] && target_textures="$MERGED_DIR/Textures/$textures_subdir"
# Link shader files
if [[ -n "$repo_shaders_dir" && -d "$repo_shaders_dir" ]]; then
link_tree "$repo_shaders_dir" "$target_shaders" "$deny_files"
fi
# Link texture files
if [[ -n "$repo_textures_dir" && -d "$repo_textures_dir" ]]; then
link_tree "$repo_textures_dir" "$target_textures" ""
fi
((installed_count++)) || true
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
echo "╔══════════════════════════════════════════════════════════════════════════╗"
echo "║ ReShade Shader Installer (For use with reshade-linux.sh) ║"
echo "╚══════════════════════════════════════════════════════════════════════════╝"
echo ""
echo " Base path: $MAIN_PATH"
echo " Dry run: $( [[ $DRY_RUN -eq 1 ]] && echo "yes" || echo "no" )"
echo " Update: $( [[ $UPDATE_SHADERS -eq 1 ]] && echo "yes" || echo "no" )"
echo " Rebuild: $( [[ $REBUILD_MERGE -eq 1 ]] && echo "yes" || echo "no" )"
echo " Picker: $( [[ $INTERACTIVE -eq 1 ]] && echo "yes" || echo "no" )"
if [[ "$INTERACTIVE" -eq 0 && ${#INCLUDES[@]} -gt 0 ]]; then
echo " Filter: ${INCLUDES[*]}"
fi
echo ""
# Download EffectPackages.ini
echo "Downloading EffectPackages.ini ..."
PACKAGES_INI="$(curl -sL "$PACKAGES_URL")"
if [[ -z "$PACKAGES_INI" ]]; then
echo "Error: Failed to download EffectPackages.ini" >&2
exit 1
fi
echo " ✓ downloaded"
echo ""
echo "$SEPARATOR"
echo " Parsing shader packages"
echo "$SEPARATOR"
# ---------------------------------------------------------------------------
# First pass: collect all packages into arrays
# ---------------------------------------------------------------------------
declare -a ALL_PKG_NAMES=()
declare -a ALL_PKG_REPO_URLS=()
declare -a ALL_PKG_DOWNLOAD_URLS=()
declare -a ALL_PKG_DENY_FILES=()
declare -a ALL_PKG_INSTALL_PATHS=()
declare -a ALL_PKG_TEXTURE_INSTALL_PATHS=()
declare -a ALL_PKG_DESCRIPTIONS=()
declare -a ALL_PKG_REQUIRED=()
pkg_name=""
pkg_repo_url=""
pkg_download_url=""
pkg_deny_files=""
pkg_effect_files=""
pkg_enabled=""
pkg_install_path=""
pkg_texture_install_path=""
pkg_description=""
pkg_required=""
_store_package() {
if [[ -n "$pkg_repo_url" || -n "$pkg_download_url" ]]; then
ALL_PKG_NAMES+=("$pkg_name")
ALL_PKG_REPO_URLS+=("$pkg_repo_url")
ALL_PKG_DOWNLOAD_URLS+=("$pkg_download_url")
ALL_PKG_DENY_FILES+=("$pkg_deny_files")
ALL_PKG_INSTALL_PATHS+=("$pkg_install_path")
ALL_PKG_TEXTURE_INSTALL_PATHS+=("$pkg_texture_install_path")
ALL_PKG_DESCRIPTIONS+=("$pkg_description")
ALL_PKG_REQUIRED+=("$pkg_required")
fi
}
while IFS= read -r line || [[ -n "$line" ]]; do
# Strip carriage return
line="${line%$'\r'}"
# New section — store the previously accumulated package
if [[ "$line" =~ ^\[.+\]$ ]]; then
_store_package
# Reset state for the next package
pkg_name=""
pkg_repo_url=""
pkg_download_url=""
pkg_deny_files=""
pkg_effect_files=""
pkg_enabled=""
pkg_install_path=""
pkg_texture_install_path=""
pkg_description=""
pkg_required=""
continue
fi
# Parse key=value lines
if [[ "$line" =~ ^PackageName=(.+)$ ]]; then
pkg_name="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^PackageDescription=(.+)$ ]]; then
pkg_description="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^RepositoryUrl=(.+)$ ]]; then
pkg_repo_url="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^DownloadUrl=(.+)$ ]]; then
pkg_download_url="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^DenyEffectFiles=(.+)$ ]]; then
pkg_deny_files="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^EffectFiles=(.+)$ ]]; then
pkg_effect_files="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^Enabled=(.+)$ ]]; then
pkg_enabled="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^Required=(.+)$ ]]; then
pkg_required="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^InstallPath=(.+)$ ]]; then
pkg_install_path="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^TextureInstallPath=(.+)$ ]]; then
pkg_texture_install_path="${BASH_REMATCH[1]}"
fi
done <<< "$PACKAGES_INI"
# Store the last package (no trailing section header)
_store_package
# Build the REQUIRED_NAMES lookup from parsed data
for i in "${!ALL_PKG_NAMES[@]}"; do
if [[ "${ALL_PKG_REQUIRED[$i]}" == "1" ]]; then
REQUIRED_NAMES["${ALL_PKG_NAMES[$i]}"]=1
fi
done
echo " ✓ found ${#ALL_PKG_NAMES[@]} packages"
echo ""
# ---------------------------------------------------------------------------
# Interactive picker (--pick / INTERACTIVE=1)
# ---------------------------------------------------------------------------
if [[ "$INTERACTIVE" -eq 1 ]]; then
# Build the list for fzf, excluding required packages
fzf_input=""
for i in "${!ALL_PKG_NAMES[@]}"; do
if [[ "${ALL_PKG_REQUIRED[$i]}" == "1" ]]; then
continue
fi
local_desc="${ALL_PKG_DESCRIPTIONS[$i]}"
if [[ -n "$local_desc" ]]; then
fzf_input+="${ALL_PKG_NAMES[$i]} — ${local_desc}"$'\n'
else
fzf_input+="${ALL_PKG_NAMES[$i]}"$'\n'
fi
done
# Count required packages for the header
required_list=""
for i in "${!ALL_PKG_NAMES[@]}"; do
if [[ "${ALL_PKG_REQUIRED[$i]}" == "1" ]]; then
required_list+=" ✓ ${ALL_PKG_NAMES[$i]} (always installed)"$'\n'
fi
done
header="Tab: toggle · Ctrl-A: all · Ctrl-D: none · Enter: confirm"
if [[ -n "$required_list" ]]; then
header="${required_list}${header}"
fi
selected_lines="$(printf '%s' "$fzf_input" | fzf \
--multi \
--cycle \
--header="$header" \
--prompt="Shaders> " \
--delimiter=' — ' \
--with-nth=1.. \
--bind='ctrl-a:select-all,ctrl-d:deselect-all' \
)" || {
echo ""
echo " Selection cancelled."
exit 0
}
# Parse selected names (everything before the " — " delimiter)
while IFS= read -r sel_line; do
[[ -z "$sel_line" ]] && continue
sel_name="${sel_line%% — *}"
INTERACTIVE_SELECTED["$sel_name"]=1
done <<< "$selected_lines"
echo ""
echo " Selected ${#INTERACTIVE_SELECTED[@]} package(s)"
echo ""
fi
# ---------------------------------------------------------------------------
# Second pass: process packages
# ---------------------------------------------------------------------------
# Prepare Merged/ directory
if [[ "$DRY_RUN" -eq 0 ]]; then
if [[ "$REBUILD_MERGE" -eq 1 && -d "$MERGED_DIR" ]]; then
echo "Removing old Merged/ directory ..."
rm -rf "$MERGED_DIR"
fi
mkdir -p "$MERGED_DIR/Shaders"
mkdir -p "$MERGED_DIR/Textures"
fi
echo "$SEPARATOR"
echo " Processing shader packages"
echo "$SEPARATOR"
installed_count=0
skipped_count=0
for i in "${!ALL_PKG_NAMES[@]}"; do
process_package \
"${ALL_PKG_NAMES[$i]}" \
"${ALL_PKG_REPO_URLS[$i]}" \
"${ALL_PKG_DOWNLOAD_URLS[$i]}" \
"${ALL_PKG_DENY_FILES[$i]}" \
"${ALL_PKG_INSTALL_PATHS[$i]}" \
"${ALL_PKG_TEXTURE_INSTALL_PATHS[$i]}"
done
echo "$SEPARATOR"
# ---------------------------------------------------------------------------
# Post-processing: symlink ReShade.fxh into Merged/ for "../ReShade.fxh"
#
# Several shaders (Warp-FX, AstrayFX, Depth3D, fubax, qUINT, etc.) use
# #include "../ReShade.fxh"
# which resolves to the parent of the Shaders/ directory.
# Symlink the ReShade headers into Merged/ so these resolve.
# ---------------------------------------------------------------------------
if [[ "$DRY_RUN" -eq 0 ]]; then
for header in ReShade.fxh ReShadeUI.fxh; do
if [[ -e "$MERGED_DIR/Shaders/$header" && ! -e "$MERGED_DIR/$header" ]]; then
ln -s "$(realpath "$MERGED_DIR/Shaders/$header")" "$MERGED_DIR/$header"
echo " ✓ Linked $header → Merged/ (for #include \"../$header\")"
fi
done
fi
# ---------------------------------------------------------------------------
# Handle External_shaders (user's custom shaders, same as reshade-linux.sh)
# ---------------------------------------------------------------------------
if [[ -d "$MAIN_PATH/External_shaders" && "$DRY_RUN" -eq 0 ]]; then
echo ""
echo "Linking External_shaders ..."
for file in "$MAIN_PATH/External_shaders"/*; do
[[ -f "$file" ]] || continue
local_basename="$(basename "$file")"
if [[ ! -L "$MERGED_DIR/Shaders/$local_basename" && ! -e "$MERGED_DIR/Shaders/$local_basename" ]]; then
ln -s "$(realpath "$file")" "$MERGED_DIR/Shaders/$local_basename"
echo " ✓ $local_basename"
fi
done
fi
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
echo ""
echo "$SEPARATOR"
echo " Done! $installed_count installed, $skipped_count skipped"
echo "$SEPARATOR"
echo ""
echo " Shaders: $MERGED_DIR/Shaders/"
echo " Textures: $MERGED_DIR/Textures/"
echo ""
echo " ReShade search paths should include two asterisks (**) at the end to search recursively:"
echo ""
echo " Settings -> Effect search paths = .\\reshade-shaders\\Shaders\\**"
echo " Settings -> Texture search paths = .\\reshade-shaders\\Textures\\**"
echo ""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment