Skip to content

Instantly share code, notes, and snippets.

@themakunga
Created September 12, 2026 22:42
Show Gist options
  • Select an option

  • Save themakunga/71e46035abef70b9132cf29a5bf88d35 to your computer and use it in GitHub Desktop.

Select an option

Save themakunga/71e46035abef70b9132cf29a5bf88d35 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -Eeuo pipefail
export PATH="$HOME/.local/bin:$PATH"
VN_ROOT="${VN_ROOT:-$HOME/Games/VisualNovels}"
PROTON="${VN_PROTON:-GE-Proton}"
VN_LOCALE="${VN_LOCALE:-ja_JP.UTF-8}"
GAME_EXE=""
die() {
echo "ERROR: $*" >&2
exit 1
}
usage() {
cat <<'EOF'
Uso:
vn-install.sh [opciones] INSTALLER "Nombre del juego"
Opciones:
--exe PATH
Ejecutable del juego después de instalar.
Si no se especifica, el script intentará detectarlo.
--proton PATH
Proton a utilizar.
Por defecto:
GE-Proton
Ejemplo con versión fija:
--proton ~/.steam/root/compatibilitytools.d/GE-Proton11-5
--root PATH
Directorio donde guardar las novelas.
Ejemplos:
vn-install.sh ~/Downloads/setup.exe "Sakura no Uta"
vn-install.sh \
--proton ~/.steam/root/compatibilitytools.d/GE-Proton11-5 \
~/Downloads/setup.exe \
"Sakura no Uta"
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--exe)
GAME_EXE="$2"
shift 2
;;
--proton)
PROTON="$2"
shift 2
;;
--root)
VN_ROOT="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
--)
shift
break
;;
-*)
die "Opción desconocida: $1"
;;
*)
break
;;
esac
done
[[ $# -ge 1 ]] || {
usage
exit 1
}
INSTALLER="$(realpath "$1")"
shift
[[ -f "$INSTALLER" ]] || die "No existe el instalador: $INSTALLER"
NAME="${1:-$(basename "${INSTALLER%.*}")}"
UMU="$(command -v umu-run || true)"
[[ -n "$UMU" ]] || die \
"No encuentro umu-run. Instala UMU Launcher primero."
STL="$(command -v steamtinkerlaunch || true)"
if [[ -z "$STL" ]] && [[ -x "$HOME/stl/prefix/steamtinkerlaunch" ]]; then
STL="$HOME/stl/prefix/steamtinkerlaunch"
fi
GAME_ID="$(
printf '%s' "$NAME" |
sha256sum |
cut -c1-12
)"
GAME_DIR="$VN_ROOT/$GAME_ID"
PREFIX="$GAME_DIR/prefix"
LAUNCHER="$GAME_DIR/launch.sh"
METADATA="$GAME_DIR/vn.conf"
MARKER="$GAME_DIR/.install-start"
mkdir -p "$GAME_DIR"
mkdir -p "$PREFIX"
echo
echo "=========================================="
echo " Visual Novel Installer"
echo "=========================================="
echo
echo "Nombre : $NAME"
echo "ID : $GAME_ID"
echo "Prefix : $PREFIX"
echo "Proton : $PROTON"
echo "Locale : $VN_LOCALE"
echo
touch "$MARKER"
run_umu() {
env \
LANG="$VN_LOCALE" \
WINEPREFIX="$PREFIX" \
GAMEID="umu-default" \
STORE="none" \
PROTONPATH="$PROTON" \
"$UMU" "$@"
}
echo "Ejecutando instalador..."
echo
case "${INSTALLER,,}" in
*.msi)
run_umu msiexec /i "$INSTALLER"
;;
*)
run_umu "$INSTALLER"
;;
esac
echo
echo "Instalación finalizada."
echo
find_executables() {
find "$PREFIX/drive_c" \
-type f \
-iname '*.exe' \
! -path '*/windows/*' \
! -path '*/Windows/*' \
! -iname 'unins*.exe' \
! -iname 'uninstall*.exe' \
! -iname 'setup*.exe' \
! -iname '*installer*.exe' \
! -iname '*crashhandler*.exe' \
! -iname '*crashreporter*.exe' \
! -iname 'vc_redist*.exe' \
! -iname 'vcredist*.exe' \
! -iname 'dxsetup.exe' \
-printf '%s\t%p\0' |
sort -z -nr
}
if [[ -z "$GAME_EXE" ]]; then
declare -a CANDIDATES=()
while IFS= read -r -d '' ROW; do
EXE="${ROW#*$'\t'}"
CANDIDATES+=("$EXE")
done < <(find_executables)
[[ ${#CANDIDATES[@]} -gt 0 ]] ||
die "No pude encontrar ningún ejecutable dentro del prefix."
if [[ ${#CANDIDATES[@]} -eq 1 ]]; then
GAME_EXE="${CANDIDATES[0]}"
else
echo "Encontré varios ejecutables."
echo
echo "Selecciona el ejecutable principal:"
echo
MAX="${#CANDIDATES[@]}"
if (( MAX > 30 )); then
MAX=30
fi
for ((i = 0; i < MAX; i++)); do
printf ' %2d) %s\n' \
"$((i + 1))" \
"${CANDIDATES[$i]}"
done
echo
read -r -p "Ejecutable [1]: " SELECTION
SELECTION="${SELECTION:-1}"
[[ "$SELECTION" =~ ^[0-9]+$ ]] ||
die "Selección inválida."
(( SELECTION >= 1 && SELECTION <= MAX )) ||
die "Selección fuera de rango."
GAME_EXE="${CANDIDATES[$((SELECTION - 1))]}"
fi
else
GAME_EXE="$(realpath "$GAME_EXE")"
fi
[[ -f "$GAME_EXE" ]] ||
die "No existe el ejecutable seleccionado: $GAME_EXE"
echo
echo "Ejecutable seleccionado:"
echo
echo " $GAME_EXE"
echo
#
# Launcher
#
{
echo '#!/usr/bin/env bash'
echo
echo 'set -Eeuo pipefail'
echo
echo 'export PATH="$HOME/.local/bin:$PATH"'
printf 'export LANG=%q\n' "$VN_LOCALE"
printf 'export WINEPREFIX=%q\n' "$PREFIX"
printf 'export GAMEID=%q\n' "umu-default"
printf 'export STORE=%q\n' "none"
printf 'export PROTONPATH=%q\n' "$PROTON"
echo
printf 'exec %q %q "$@"\n' \
"$UMU" \
"$GAME_EXE"
} > "$LAUNCHER"
chmod +x "$LAUNCHER"
#
# Metadata
#
{
printf 'NAME=%q\n' "$NAME"
printf 'GAME_ID=%q\n' "$GAME_ID"
printf 'PREFIX=%q\n' "$PREFIX"
printf 'EXECUTABLE=%q\n' "$GAME_EXE"
printf 'PROTON=%q\n' "$PROTON"
printf 'LOCALE=%q\n' "$VN_LOCALE"
} > "$METADATA"
#
# Desktop entry
#
APPLICATIONS_DIR="$HOME/.local/share/applications"
DESKTOP_FILE="$APPLICATIONS_DIR/vn-$GAME_ID.desktop"
mkdir -p "$APPLICATIONS_DIR"
cat > "$DESKTOP_FILE" <<EOF
[Desktop Entry]
Type=Application
Name=$NAME
Exec=$LAUNCHER
Path=$GAME_DIR
Terminal=false
Categories=Game;
EOF
chmod +x "$DESKTOP_FILE"
#
# Steam
#
if [[ -n "$STL" ]]; then
echo
echo "Registrando juego en Steam..."
echo
if pgrep -x steam >/dev/null 2>&1; then
echo "Steam necesita estar cerrado para modificar shortcuts.vdf."
echo
read -r -p "¿Cerrar Steam ahora? [S/n]: " ANSWER
ANSWER="${ANSWER:-S}"
if [[ ! "$ANSWER" =~ ^[Nn]$ ]]; then
steam -shutdown >/dev/null 2>&1 || true
for _ in {1..30}; do
if ! pgrep -x steam >/dev/null 2>&1; then
break
fi
sleep 1
done
fi
fi
if pgrep -x steam >/dev/null 2>&1; then
echo
echo "Steam sigue abierto."
echo "No modificaré shortcuts.vdf para evitar corromperlo."
echo
echo "Launcher creado:"
echo
echo " $LAUNCHER"
echo
else
"$STL" ansg \
-an="$NAME" \
-ep="$LAUNCHER" \
-sd="$GAME_DIR" \
-t="Visual Novels"
echo
echo "Añadido a Steam."
fi
else
echo
echo "SteamTinkerLaunch no está instalado."
echo
echo "La VN quedó instalada, pero tendrás que añadir este archivo"
echo "manualmente como juego no-Steam:"
echo
echo " $LAUNCHER"
fi
echo
echo "=========================================="
echo " Instalación terminada"
echo "=========================================="
echo
echo "Juego:"
echo " $NAME"
echo
echo "Launcher:"
echo " $LAUNCHER"
echo
echo "Prefix:"
echo " $PREFIX"
echo
echo "Ejecutable:"
echo " $GAME_EXE"
echo
echo "Proton:"
echo " $PROTON"
echo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment