Created
September 12, 2026 22:42
-
-
Save themakunga/71e46035abef70b9132cf29a5bf88d35 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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