2197 lines
		
	
	
		
			84 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			2197 lines
		
	
	
		
			84 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env bash
 | ||
| 
 | ||
| ##### CHECK ROOT #####
 | ||
| if [[ $(id -u) -eq 0 ]] ; then
 | ||
|     echo "Перезапустите скрипт $0 от обычного пользователя!"
 | ||
|     exit 1
 | ||
| fi
 | ||
| 
 | ||
| ##### DEFAULT PATH #####
 | ||
| export SCRIPT_NAME USER_WORK_PATH RUN_SCRIPT DATA_PATH CHANGELOG_FILE WH_ICON_PATH LICENSE_FILE AGREEMENT
 | ||
| 
 | ||
| SCRIPT_NAME="$(basename "$0")"
 | ||
| if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then
 | ||
|     # переменные для установленного WineHelper в систему
 | ||
|     USER_WORK_PATH="$HOME/.local/share/$SCRIPT_NAME"
 | ||
|     RUN_SCRIPT="/usr/bin/$SCRIPT_NAME"
 | ||
|     DATA_PATH="/usr/share/$SCRIPT_NAME"
 | ||
|     CHANGELOG_FILE="$(realpath "/usr/share/doc/winehelper"-*/CHANGELOG)"
 | ||
|     WH_ICON_PATH="$DATA_PATH/image/gui/winehelper.svg"
 | ||
|     LICENSE_FILE="$(realpath "/usr/share/doc/winehelper"-*/LICENSE)"
 | ||
|     AGREEMENT="$(realpath "/usr/share/doc/winehelper"-*/LICENSE_AGREEMENT)"
 | ||
| else
 | ||
|     # переменные для тестового запуска WineHelper из репозитория
 | ||
|     USER_WORK_PATH="$HOME/test-$SCRIPT_NAME"
 | ||
|     RUN_SCRIPT="$(realpath "$0")"
 | ||
|     DATA_PATH="$(dirname "$RUN_SCRIPT")"
 | ||
|     CHANGELOG_FILE="$DATA_PATH/CHANGELOG"
 | ||
|     WH_ICON_PATH="$DATA_PATH/image/gui/winehelper-devel.svg"
 | ||
|     LICENSE_FILE="$DATA_PATH/LICENSE"
 | ||
|     AGREEMENT="$DATA_PATH/LICENSE_AGREEMENT"
 | ||
| 
 | ||
|     # минимальная проверка синтаксиса скриптов
 | ||
|     for self_check_script in "$RUN_SCRIPT" \
 | ||
|     "$DATA_PATH/dependencies.sh" "$DATA_PATH/autoinstall"/* \
 | ||
|     "$DATA_PATH/manualinstall"/* "$DATA_PATH/testinstall"/* \
 | ||
|     "$DATA_PATH/database"/*
 | ||
|     do /usr/bin/env bash -n "$self_check_script" || exit 1
 | ||
|     done
 | ||
| fi
 | ||
| 
 | ||
| if [[ $1 == "gui" ]] ; then
 | ||
|     export WH_USE_GUI="1"
 | ||
|     /usr/bin/env python3 "$DATA_PATH/winehelper_gui.py" &
 | ||
|     exit 0
 | ||
| fi
 | ||
| 
 | ||
| ##### MESSAGES FUNCTIONS #####
 | ||
| if [[ $WH_USE_GUI != "1" ]] ; then
 | ||
|     print_error () { printf "\E[31m%s Ошибка: $@ %s\e[0m\n" ;}
 | ||
|     print_warning () { printf "\E[33m%s Предупреждение: $@ %s\e[0m\n" ;}
 | ||
|     print_info () { printf "\E[36m%s Информация: \"$@\" %s\e[0m\n" ;}
 | ||
|     print_ok () { printf "\E[35m%s Успех: $@ %s\e[0m\n" ;}
 | ||
| else
 | ||
|     print_error () { echo -e "Ошибка: $@" ;}
 | ||
|     print_warning () { echo -e "Предупреждение: $@" ;}
 | ||
|     print_info () { echo -e "Информация: \"$@\"" ;}
 | ||
|     print_ok () { echo -e "Успех: $@" ;}
 | ||
| fi
 | ||
| 
 | ||
| print_var () { for vp in $@ ; do echo "${vp}=${!vp}" ; done ;}
 | ||
| 
 | ||
| fatal () {
 | ||
|     print_error "$@"
 | ||
|     [[ -n "$WINESERVER" ]] && "$WINESERVER" -w
 | ||
|     exit 1
 | ||
| }
 | ||
| 
 | ||
| print_confirmation () {
 | ||
|     local answer
 | ||
|     read -p "$@ (y/N): " answer
 | ||
|     if [[ ! "$answer" =~ ^[Yy]$ ]] ; then
 | ||
|         print_info "Отменено пользователем."
 | ||
|         return 1
 | ||
|     fi
 | ||
|     return 0
 | ||
| }
 | ||
| 
 | ||
| ##### CHECK VARIABLES #####
 | ||
| check_variables () { [[ -z ${!1} ]] && export $1="$2" ;}
 | ||
| 
 | ||
| ##### CHECK DEBUG #####
 | ||
| if [[ "$1" == "--debug" ]] ; then
 | ||
|     check_variables WINEDEBUG "+loaddll,+seh"
 | ||
|     export DXVK_LOG_LEVEL="error"
 | ||
|     export VKD3D_DEBUG="error"
 | ||
|     export WINE_MONO_TRACE="E:System.NotImplementedException"
 | ||
|     export VK_LOADER_DEBUG="error"
 | ||
|     export VKBASALT_LOG_LEVEL="error"
 | ||
|     export DXVK_NVAPI_LOG_LEVEL="error"
 | ||
|     shift
 | ||
| else
 | ||
|     check_variables WINEDEBUG "-all"
 | ||
|     check_variables DXVK_LOG_LEVEL "none"
 | ||
|     check_variables VKD3D_SHADER_DEBUG "none"
 | ||
|     check_variables VKD3D_DEBUG "none"
 | ||
|     check_variables DXVK_LOG_PATH "none"
 | ||
|     check_variables VKBASALT_LOG_LEVEL "none"
 | ||
|     check_variables DXVK_NVAPI_LOG_LEVEL "none"
 | ||
| fi
 | ||
| 
 | ||
| if [[ $WINEDEBUG != "-all" ]] ; then
 | ||
|     export LOG_FILE="$HOME/winehelper.log"
 | ||
|     date > "$LOG_FILE"
 | ||
|     print_warning "Включен режим логирования работы WINE."
 | ||
|     print_warning "Лог будет сохранен по пути: $LOG_FILE"
 | ||
|     sleep 3
 | ||
| fi
 | ||
| 
 | ||
| ##### WINETRICKS VERSION #####
 | ||
| WINETRICKS_VERSION="20250102"
 | ||
| 
 | ||
| ##### DEFAULT VARIABLES #####
 | ||
| WH_VULKAN_LIBDIR="$USER_WORK_PATH/vulkan"
 | ||
| WH_PREFIXES_DIR="$USER_WORK_PATH/prefixes"
 | ||
| WH_DIST_DIR="$USER_WORK_PATH/dist"
 | ||
| WH_TMP_DIR="$USER_WORK_PATH/tmp"
 | ||
| 
 | ||
| WH_IMAGE_PATH="$DATA_PATH/image"
 | ||
| WH_DB_DIR="$DATA_PATH/database"
 | ||
| WH_AUTOINSTALL_DIR="$DATA_PATH/autoinstall"
 | ||
| WH_MANUALINSTALL_DIR="$DATA_PATH/manualinstall"
 | ||
| WH_TESTINSTALL_DIR="$DATA_PATH/testinstall"
 | ||
| WH_WINETRICKS="$DATA_PATH/winetricks_$WINETRICKS_VERSION"
 | ||
| 
 | ||
| WH_MENU_DIR="$HOME/.local/share/applications/WineHelper"
 | ||
| WH_MENU_CATEGORY="$HOME/.local/share/desktop-directories/WineHelper.directory"
 | ||
| WH_MENU_CONFIG="$HOME/.config/menus/applications-merged/WineHelper.menu"
 | ||
| 
 | ||
| # export WINEDLLOVERRIDES=mshtml,mscoree="
 | ||
| 
 | ||
| check_variables WINEESYNC "0"
 | ||
| check_variables WINEFSYNC "0"
 | ||
| 
 | ||
| check_variables WINEUSERNAME "xuser"
 | ||
| check_variables WINEARCH "win64" # or "win32"
 | ||
| check_variables WH_WINE_USE "wine_x_tkg_10-0_amd64" # or system
 | ||
| 
 | ||
| check_variables WH_USE_CPCSP_PROXY "0"
 | ||
| check_variables CPCSP_PROXY_VER "0.6.1-alt1"
 | ||
| 
 | ||
| check_variables WH_USE_EXTRA_FONTS "0"
 | ||
| check_variables EXTRA_FONTS_VER "01"
 | ||
| 
 | ||
| check_variables STAGING_SHARED_MEMORY "1"
 | ||
| check_variables WINE_LARGE_ADDRESS_AWARE "1"
 | ||
| check_variables WINE_FULLSCREEN_FSR "1"
 | ||
| check_variables WINE_DO_NOT_CREATE_DXGI_DEVICE_MANAGER "0"
 | ||
| check_variables WINE_HEAP_DELAY_FREE "0"
 | ||
| check_variables WINE_ALLOW_XIM "0"
 | ||
| 
 | ||
| check_variables WH_WINDOWS_VER "10"
 | ||
| # check_variables WH_USE_GSTREAMER "1"
 | ||
| # check_variables WH_USE_D3D_EXTRAS "1"
 | ||
| check_variables WH_USE_SHADER_CACHE "1"
 | ||
| check_variables WH_USE_WINE_DXGI "0"
 | ||
| check_variables WH_DLL_INSTALL ""
 | ||
| 
 | ||
| check_variables WINE_WIN_START "start /wait /high /unix"
 | ||
| 
 | ||
| check_variables WINE_CPU_TOPOLOGY "8"
 | ||
| 
 | ||
| check_variables USE_RENDERER "opengl" # opengl, damavand, proton
 | ||
| 
 | ||
| check_variables DXVK_VER "1.10.3-28"
 | ||
| # check_variables DXVK_CONFIG_FILE "path/to/dxvk.conf"
 | ||
| 
 | ||
| check_variables VKD3D_VER "1.1-2602"
 | ||
| # check_variables VKD3D_LIMIT_TESS_FACTORS 64
 | ||
| # check_variables VKD3D_FEATURE_LEVEL "12_0"
 | ||
| 
 | ||
| export CLOUD_URL="https://cloud.linux-gaming.ru/portproton"
 | ||
| 
 | ||
| if env | grep license_agreement_file
 | ||
| then fatal "Обнаружена подмена переменной license_agreement_file!"
 | ||
| else readonly license_agreement_file="$(mktemp -d)/$((RANDOM % RANDOM))"
 | ||
| fi
 | ||
| 
 | ||
| ##### CHECK NOEXEC FOR /HOME #####
 | ||
| if mount -l | grep -E "[[:space:]]/home[[:space:]]" | grep -q "noexec" ; then
 | ||
|     fatal "/home примонтирован в /etc/fstab с аргументом noexec.\nЗапуск портативной версии wine не возможен из домашнего каталога."
 | ||
| fi
 | ||
| 
 | ||
| ##### ROOT #####
 | ||
| su_run () {
 | ||
|     if [[ $WH_USE_GUI != "1" ]] ; then
 | ||
|         local i="1"
 | ||
|         while [[ $i -le "3" ]] ; do
 | ||
|             print_info "Для продолжения установки введите root пароль (попытка $i из 3)..."
 | ||
|             su - -c "$@" && return 0
 | ||
|             ((i++))
 | ||
|         done
 | ||
|     else
 | ||
|         pkexec "$@" && return 0
 | ||
|     fi
 | ||
|     fatal "Не удалось установить необходимые компоненты!"
 | ||
| }
 | ||
| 
 | ||
| ##### CHECK DEPENDENCIES #####
 | ||
| # fonts-ttf-ms
 | ||
| 
 | ||
| if ! rpm -q {i586-,}{wine,glibc-core,libstdc++6,glibc-pthread,glibc-nss,\
 | ||
| libnss-mdns,libunixODBC2,ocl-icd,libfreetype,libfontconfig1,libgnutls30,libGL,\
 | ||
| libEGL,xorg-dri-swrast,xorg-dri-intel,xorg-dri-radeon,libvulkan1,libcups} 1>/dev/null
 | ||
| then
 | ||
|     if su_run "$DATA_PATH/dependencies.sh"
 | ||
|     then print_info "Зависимости успешно установлены. Продолжаем работу $SCRIPT_NAME"
 | ||
|     else fatal "Не удалось установить зависимости. Работа $SCRIPT_NAME прервана."
 | ||
|     fi
 | ||
| fi
 | ||
| 
 | ||
| ##### HELPER FUNCTIONS #####
 | ||
| add_to_var () {
 | ||
|     if ! echo ${!1} | grep "$2" &>/dev/null
 | ||
|     then export $1="${!1} $2"
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| rm_from_var () {
 | ||
|     if echo ${!1} | grep "$2" &>/dev/null
 | ||
|     then export $1="$(echo "${!1//$2/}" | tr -s " ")"
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| try_remove_file () {
 | ||
|     if [[ -f "$1" ]] || [[ ! -e "$1" ]] ; then
 | ||
|         rm -f "$1"
 | ||
|         [[ "$?" == 0 ]] && return 0 || return 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| try_remove_dir () {
 | ||
|     if [[ -d "$1" ]] ; then
 | ||
|         rm -fr "$1"
 | ||
|         [[ "$?" == 0 ]] && return 0 || return 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| try_copy_file () {
 | ||
|     if [[ ! -f "$1" ]] ; then print_info "файла $1 не существует для копирования" && return 1
 | ||
|     elif [[ -z "$2" ]] ; then fatal "нет пути для копирования файла $1"
 | ||
|     elif [[ -L "$2" ]] ; then
 | ||
|         try_remove_file "$2"
 | ||
|         cp -f "$1" "$2" && return 0 || return 1
 | ||
|     else
 | ||
|         [[ -e "$2/$(basename "$1")" ]] && rm -f "$2/$(basename "$1")"
 | ||
|         cp -f "$1" "$2" && return 0 || return 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| try_copy_dir () {
 | ||
|     if [[ ! -d "$1" ]] ; then print_info "каталога $1 не существует для копирования"
 | ||
|     elif [[ -z "$2" ]] ; then fatal "нет пути для копирования каталога $1"
 | ||
|     else
 | ||
|         cp -fr "$1" "$2"
 | ||
|         [[ "$?" != 0 ]] && print_error "не удалось скопировать каталог $1 в $2" || return 0
 | ||
|     fi
 | ||
|     return 1
 | ||
| }
 | ||
| 
 | ||
| try_force_link_file () {
 | ||
|     if [[ ! -f "$1" ]] ; then
 | ||
|         print_warning "нет файла для создания символьной ссылки: $1"
 | ||
|         if [[ -f "$2" ]] ; then
 | ||
|             try_remove_file "$2"
 | ||
|             print_warning "удаляем символьную ссылку: $2"
 | ||
|         fi
 | ||
|         return 1
 | ||
|     elif [[ -z "$2" ]] ; then fatal "нет пути для создания символьной ссылки на файл $1"
 | ||
|     else
 | ||
|         try_remove_file "$2"
 | ||
|         ln -s -f -r "$1" "$2"
 | ||
|         return 0
 | ||
|     fi
 | ||
|     return 1
 | ||
| }
 | ||
| 
 | ||
| try_force_link_dir () {
 | ||
|     if [[ ! -d "$1" ]] ; then print_info "каталога $1 не существует для создания символьной сссылки"
 | ||
|     elif [[ -z "$2" ]] ; then fatal "не указан путь для создания символьной ссылки на каталог $1"
 | ||
|     else
 | ||
|         ln -s -f -r "$1" "$2"
 | ||
|         [[ "$?" != 0 ]] && print_error "не удалось сделать символьную ссылку на каталог $1 по пути $2" || return 0
 | ||
|     fi
 | ||
|     return 1
 | ||
| }
 | ||
| 
 | ||
| create_new_dir () {
 | ||
|     if [[ ! -d "$1" ]] ; then
 | ||
|         mkdir -p "$1"
 | ||
|     fi
 | ||
|     return 0
 | ||
| }
 | ||
| 
 | ||
| unpack () {
 | ||
|     if [[ $1 == "--skip-xattr" ]] ; then
 | ||
|         local skip_xattr="1"
 | ||
|         shift
 | ||
|     else
 | ||
|         unset skip_xattr
 | ||
|     fi
 | ||
|     print_info "Запуск распаковки архива $1"
 | ||
|     local command outarg
 | ||
|     case $1 in
 | ||
|         *.tar.xz) command="tar -Jxhf" ; outarg="-C " ;;
 | ||
|         *.tar.gz) command="tar -xhzf" ; outarg="-C " ;;
 | ||
|         *.tar.zst) command="tar -I zstd -xhf" ; outarg="-C " ;;
 | ||
|         *.tar) command="tar -xhf" ; outarg="-C " ;;
 | ||
|         *.zip|*.exe|*.rar) command="7z x -y -bso0" ; outarg="-o" ;;
 | ||
|     esac
 | ||
|     create_new_dir "$2"
 | ||
|     if [[ $skip_xattr == "1" ]] \
 | ||
|     && $command "$1" ${outarg}"$2" 2>&1 | sed "/xattr/d"
 | ||
|     then print_ok "Файл $1 распакован."
 | ||
|     elif $command "$1" ${outarg}"$2"
 | ||
|     then print_ok "Файл $1 распакован."
 | ||
|     else
 | ||
|         try_remove_file "$1"
 | ||
|         fatal "Распаковать файл $1 не удалось!"
 | ||
|     fi
 | ||
|     return 0
 | ||
| }
 | ||
| 
 | ||
| try_get_page () {
 | ||
|     local url_page="$1"
 | ||
|     export OUT_PAGE_TMP="${WH_TMP_DIR}/url_page.tmp"
 | ||
|     print_info "Чтение страницы: $url_page"
 | ||
|     if ! curl -o "$OUT_PAGE_TMP" -A "Mozilla/5.0 (compatible; Konqueror/2.1.1; X11)" "$url_page" \
 | ||
|     || grep -q "Forbidden" "$OUT_PAGE_TMP"
 | ||
|     then
 | ||
|         try_remove_file "$OUT_PAGE_TMP"
 | ||
|         fatal "Страница сайта $1 не доступна, или превышено количество запросов к странице."
 | ||
|     else
 | ||
|         return 0
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| read_page () {
 | ||
|     if [[ -n $OUT_PAGE_TMP ]] \
 | ||
|     && [[ -f "$OUT_PAGE_TMP" ]]
 | ||
|     then
 | ||
|         cat "$OUT_PAGE_TMP"
 | ||
|         try_remove_file "$OUT_PAGE_TMP"
 | ||
|         unset OUT_PAGE_TMP
 | ||
|     else
 | ||
|         echo "Используй try_get_page перед read_page"
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| check_file_sum () {
 | ||
|     local sha256sum_ext sha256sum_int check_file_path check_file_name
 | ||
|     check_file_path="$1"
 | ||
|     check_file_name="$(basename "$check_file_path")"
 | ||
| 
 | ||
|     sha256sum_ext=$(sha256sum "$check_file_path" | awk '{print $1}')
 | ||
|     sha256sum_int="$(grep "$check_file_name" "$DATA_PATH/sha256sum.list" | awk '{print $1}')"
 | ||
| 
 | ||
|     if [[ "$sha256sum_ext" == "$sha256sum_int" ]] ; then
 | ||
|         print_ok "Хэш-сумма файла $check_file_name успешно проверена."
 | ||
|         return 0
 | ||
|     else
 | ||
|         try_remove_file "$check_file_path"
 | ||
|         fatal "Хэш-сумма файла $check_file_name не совпадает!\n Попробуйте перезапустить установку."
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| print_license_agreement () {
 | ||
|     if [[ -f  "$license_agreement_file" ]] \
 | ||
|     && [[ "$(stat -c %a "$license_agreement_file" 2>/dev/null)" == "600" ]]
 | ||
|     then return 0
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -f "$AGREEMENT" ]]; then
 | ||
|         echo
 | ||
|         print_warning "$(cat "$AGREEMENT")"
 | ||
|     else
 | ||
|         fatal "Файл лицензионного соглашения не найден: $AGREEMENT"
 | ||
|     fi
 | ||
| 
 | ||
|     echo
 | ||
|     if print_confirmation "Подтвердите продолжение установки" ; then
 | ||
|         touch "$license_agreement_file"
 | ||
|         chmod 600 "$license_agreement_file"
 | ||
|         cleanup_laf () {
 | ||
|             local cleanup_laf_dir="$(dirname "$license_agreement_file")"
 | ||
|             rm -r "$cleanup_laf_dir" || echo "Не удалось удалить каталог $cleanup_laf_dir"
 | ||
|         }
 | ||
|         trap "cleanup_laf" EXIT
 | ||
|         return 0
 | ||
|     else
 | ||
|         exit 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| try_download () {
 | ||
|     if [[ $WH_USE_GUI == "1" ]] \
 | ||
|     && [[ $(ps -o command= -p "$PPID" | awk '{print $2}') =~ "$DATA_PATH/winehelper_gui.py" ]]
 | ||
|     then print_ok "Соглашения приняты из графического интерфейса."
 | ||
|     else print_license_agreement
 | ||
|     fi
 | ||
|     local download_file_url output_file output_file_name
 | ||
|     download_file_url="${1// /%20}"
 | ||
|     output_file="$2"
 | ||
|     output_file_name="$(basename "$output_file")"
 | ||
| 
 | ||
|     if [[ -f "$output_file" ]] ; then
 | ||
|         print_info "Файл найден: $output_file"
 | ||
|         [[ $3 == "check256sum" ]] && check_file_sum "$output_file"
 | ||
|         return 0
 | ||
|     else
 | ||
|         print_info "Скачивание файла $output_file_name..."
 | ||
|         if curl -f --progress-bar -A "Mozilla/5.0 (compatible; Konqueror/2.1.1; X11)" \
 | ||
|         -L "$download_file_url" -o "$output_file"
 | ||
|         then
 | ||
|             print_ok "Скачивание файла $output_file_name прошло успешно."
 | ||
|             [[ $3 == "check256sum" ]] && check_file_sum "$output_file"
 | ||
|             return 0
 | ||
|         else
 | ||
|             try_remove_file "$output_file"
 | ||
|             fatal "Скачивание файла: $output_file_name завершилось с ошибкой!"
 | ||
|             return 1
 | ||
|         fi
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| try_copy_other_dll_to_pfx_64 () {
 | ||
|     if [[ "$WINEARCH" == "win64" ]] ; then
 | ||
|         cmp -s "$1" "${WINEPREFIX}/drive_c/windows/system32/$(basename $1)" && return 0
 | ||
|         try_copy_file "$1" "${WINEPREFIX}/drive_c/windows/system32/" && return 0 || return 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| try_copy_other_dll_to_pfx_32() {
 | ||
|     if [[ "$WINEARCH" == "win64" ]] ; then
 | ||
|         cmp -s "$1" "${WINEPREFIX}/drive_c/windows/syswow64/$(basename $1)" && return 0
 | ||
|         try_copy_file "$1" "${WINEPREFIX}/drive_c/windows/syswow64/" && return 0 || return 1
 | ||
|     else
 | ||
|         cmp -s "$1" "${WINEPREFIX}/drive_c/windows/system32/$(basename $1)" && return 0
 | ||
|         try_copy_file "$1" "${WINEPREFIX}/drive_c/windows/system32/" && return 0 || return 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| try_copy_wine_dll_to_pfx_64 () {
 | ||
|     if [[ "$WINEARCH" == "win64" ]] ; then
 | ||
|         if [[ -d "$WINEDIR/lib64/wine/x86_64-windows" ]]
 | ||
|         then WINE_BUILD_DLL_64="$WINEDIR/lib64/wine/x86_64-windows"
 | ||
|         else WINE_BUILD_DLL_64="$WINEDIR/lib64/wine"
 | ||
|         fi
 | ||
|         cmp -s "$WINE_BUILD_DLL_64/$1" "${WINEPREFIX}/drive_c/windows/system32/$(basename $1)" && return 0
 | ||
|         try_copy_file "$WINE_BUILD_DLL_64/$1" "${WINEPREFIX}/drive_c/windows/system32/" && return 0 || return 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| try_copy_wine_dll_to_pfx_32() {
 | ||
|     if [[ -d "$WINEDIR/lib/wine/i386-windows" ]]
 | ||
|     then WINE_BUILD_DLL_32="$WINEDIR/lib/wine/i386-windows"
 | ||
|     else WINE_BUILD_DLL_32="$WINEDIR/lib/wine"
 | ||
|     fi
 | ||
|     if [[ "$WINEARCH" == "win64" ]] ; then
 | ||
|         cmp -s "$WINE_BUILD_DLL_32/$1" "${WINEPREFIX}/drive_c/windows/syswow64/$1" && return 0
 | ||
|         try_copy_file "$WINE_BUILD_DLL_32/$1" "${WINEPREFIX}/drive_c/windows/syswow64/" && return 0 || return 1
 | ||
|     else
 | ||
|         cmp -s "$WINE_BUILD_DLL_32/$1" "${WINEPREFIX}/drive_c/windows/system32/$1" && return 0
 | ||
|         try_copy_file "$WINE_BUILD_DLL_32/$1" "${WINEPREFIX}/drive_c/windows/system32/" && return 0 || return 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| var_winedlloverride_update () {
 | ||
|     if [[ -n "${WINEDLLOVERRIDES}" ]]
 | ||
|     then export WINEDLLOVERRIDES="${1};${WINEDLLOVERRIDES}"
 | ||
|     else export WINEDLLOVERRIDES="${1}"
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| var_dxvk_config_update () {
 | ||
|     if [[ -n "${DXVK_CONFIG}" ]]
 | ||
|     then export DXVK_CONFIG="${1};${DXVK_CONFIG}"
 | ||
|     else export DXVK_CONFIG="${1}"
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| var_vkd3d_config_update () {
 | ||
|     if [[ -n "${VKD3D_CONFIG}" ]]
 | ||
|     then export VKD3D_CONFIG="${1};${VKD3D_CONFIG}"
 | ||
|     else export VKD3D_CONFIG="${1}"
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| var_radv_perftest_config_update () {
 | ||
|     if [[ -n "${RADV_PERFTEST}" ]]
 | ||
|     then export RADV_PERFTEST="${1};${RADV_PERFTEST}"
 | ||
|     else export RADV_PERFTEST="${1}"
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| var_vk_istance_layers_config_update () {
 | ||
|     if [[ -n "${VK_INSTANCE_LAYERS}" ]]
 | ||
|     then export VK_INSTANCE_LAYERS="${1}:${VK_INSTANCE_LAYERS}"
 | ||
|     else export VK_INSTANCE_LAYERS="${1}"
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| var_ld_library_path_update () {
 | ||
|     if echo "$LD_LIBRARY_PATH" | grep "$1"
 | ||
|     then return 0
 | ||
|     elif [[ -n "$LD_LIBRARY_PATH" ]]
 | ||
|     then export LD_LIBRARY_PATH="$1:$LD_LIBRARY_PATH"
 | ||
|     else export LD_LIBRARY_PATH="$1"
 | ||
|     fi
 | ||
|     return 0
 | ||
| }
 | ||
| 
 | ||
| extract_icon() {
 | ||
|     check_prefix_var
 | ||
|     local exe_file="$1"
 | ||
|     local ico_name="$(basename "$exe_file" .exe).ico"
 | ||
|     local png_name="$(basename "$exe_file" .exe).png"
 | ||
|     local tmp_ico_dir="$WH_TMP_DIR/icons"
 | ||
|     local user_icons="$WINEPREFIX/icons"
 | ||
| 
 | ||
|     create_new_dir "$tmp_ico_dir"
 | ||
| 
 | ||
|     if ! wrestool -x -t 14 "$exe_file" -o "$tmp_ico_dir/$ico_name" ; then
 | ||
|         print_warning "Не удалось извлечь иконку из $exe_file"
 | ||
|         try_remove_file "$tmp_ico_dir"
 | ||
|         return 1
 | ||
|     fi
 | ||
| 
 | ||
|     if ! icotool -x -i 1 "$tmp_ico_dir/$ico_name" -o "$tmp_ico_dir/$png_name" ; then
 | ||
|         print_warning "Не удалось извлечь иконку из $ico_name"
 | ||
|         try_remove_file "$tmp_ico_dir"
 | ||
|         return 1
 | ||
|     fi
 | ||
| 
 | ||
|     create_new_dir "$user_icons"
 | ||
| 
 | ||
|     if ! try_copy_file "$tmp_ico_dir/$png_name" "$user_icons" ; then
 | ||
|         print_warning "Не удалось копировать иконку в префикс"
 | ||
|         try_remove_file "$user_icons"
 | ||
|         return 1
 | ||
|     fi
 | ||
| 
 | ||
|     try_remove_dir "$tmp_ico_dir"
 | ||
|     print_ok "Иконка сохранена: $user_icons/$png_name"
 | ||
|     return 0
 | ||
| }
 | ||
| 
 | ||
| create_desktop () {
 | ||
|     local name_desktop exe_file desktop_filename icon_file desktop_path
 | ||
|     name_desktop="$1"
 | ||
|     exe_file="$2"
 | ||
|     if [[ -n $4 ]] && [[ $4 != "nocopy" ]];
 | ||
|     then desktop_filename="$4"
 | ||
|     else desktop_filename="$(basename "$exe_file" .exe | sed "s| |_|")"
 | ||
|     fi
 | ||
|     if [[ "$RESTORE_FROM_BACKUP" == "1" ]] && [[ -f "$3" ]]
 | ||
|     then icon_file="$3"
 | ||
|     elif [[ -f "$WH_IMAGE_PATH/$3.png" ]]
 | ||
|     then icon_file="$WH_IMAGE_PATH/$3.png"
 | ||
|     else icon_file="wine"
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -z "$name_desktop" ]] || [[ -z "$exe_file" ]] ; then
 | ||
|         fatal "Used: $SCRIPT_NAME --desktop \"desktop_name\" \"path_to_exe\" \"name_png_from_image\""
 | ||
|     elif [[ ! -f "$exe_file" ]] ; then
 | ||
|         print_warning "Для создания ярлыка не найден исполняемый файл: $exe_file"
 | ||
| 
 | ||
|         BASENAME_EXE="$(basename "$exe_file")"
 | ||
|         print_info "Запускаем поиск $BASENAME_EXE"
 | ||
|         if [[ -z "$DRIVE_C" ]] || [[ ! -d "$DRIVE_C" ]]
 | ||
|         then FIND_PATH="$WH_PREFIXES_DIR"
 | ||
|         else FIND_PATH="$DRIVE_C"
 | ||
|         fi
 | ||
|         exe_file="$(find "$FIND_PATH" -type f -not -type l \
 | ||
|                     -not -path "*/windows/*" -not -path "*/dosdevices/*" \
 | ||
|                     -iname "$BASENAME_EXE")"
 | ||
|         if [[ -z "$exe_file" ]] || [[ ! -f "$exe_file" ]]
 | ||
|         then fatal "Для создания ярлыка не найден исполняемый файл: $BASENAME_EXE"
 | ||
|         else print_ok "Исполняемый файл $BASENAME_EXE найден по пути $(dirname "$exe_file")/"
 | ||
|         fi
 | ||
|     fi
 | ||
| 
 | ||
|     # создаем .desktop файл
 | ||
|     create_new_dir "$WH_MENU_DIR"
 | ||
|     {
 | ||
|         echo "[Desktop Entry]"
 | ||
|         echo "Name=$name_desktop"
 | ||
|         echo "Exec=env \"$RUN_SCRIPT\" \"$exe_file\" %F"
 | ||
|         echo "Type=Application"
 | ||
|         echo "Categories=WineHelper;"
 | ||
|         echo "StartupNotify=true"
 | ||
|         echo "Path=$DATA_PATH"
 | ||
|         echo "Icon=$icon_file"
 | ||
|     } > "$USER_WORK_PATH/$desktop_filename.desktop"
 | ||
|     chmod +x "$USER_WORK_PATH/$desktop_filename.desktop"
 | ||
| 
 | ||
|     cp -f "$USER_WORK_PATH/$desktop_filename.desktop" "$WH_MENU_DIR/"
 | ||
| 
 | ||
|     if [[ "$RESTORE_FROM_BACKUP" == "1" ]] ; then
 | ||
|         print_info "Пропускаем обновление desktop.list (режим восстановления из бэкапа)"
 | ||
|     else
 | ||
|         # добавляем информацию о приложении в "$WINEPREFIX/desktop.list"
 | ||
|         if [[ -f "$WINEPREFIX/desktop.list" ]] \
 | ||
|         && grep -qe "^${name_desktop}=" "$WINEPREFIX/desktop.list"
 | ||
|         then sed -i "/^$name_desktop=/d" "$WINEPREFIX/desktop.list"
 | ||
|         fi
 | ||
|         create_new_dir "$WINEPREFIX/icons"
 | ||
|         try_copy_file "$icon_file" "$WINEPREFIX/icons/"
 | ||
|         echo "$name_desktop=${exe_file//$WINEPREFIX/}=$(basename "$icon_file")" >> "$WINEPREFIX/desktop.list"
 | ||
|     fi
 | ||
| 
 | ||
|     # создаем файл категории для меню
 | ||
|     create_new_dir "$HOME/.local/share/desktop-directories"
 | ||
|     if [[ ! -f "$WH_MENU_CATEGORY" ]] ; then
 | ||
|     cat > "$WH_MENU_CATEGORY" <<EOF
 | ||
| [Desktop Entry]
 | ||
| Type=Directory
 | ||
| Name=WineHelper
 | ||
| Icon=wine
 | ||
| EOF
 | ||
|     fi
 | ||
| 
 | ||
|     # Создаем файл меню для всех приложений
 | ||
|     create_new_dir "$HOME/.config/menus/applications-merged"
 | ||
|     if [[ ! -f "$WH_MENU_CONFIG" ]] ; then
 | ||
|     cat > "$WH_MENU_CONFIG" <<EOF
 | ||
| <!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
 | ||
|   "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
 | ||
| <Menu>
 | ||
|   <Name>Applications</Name>
 | ||
|   <Menu>
 | ||
|     <Name>WineHelper</Name>
 | ||
|     <Directory>WineHelper.directory</Directory>
 | ||
|     <Include>
 | ||
|       <Category>WineHelper</Category>
 | ||
|     </Include>
 | ||
|   </Menu>
 | ||
| </Menu>
 | ||
| EOF
 | ||
|     fi
 | ||
| 
 | ||
|     # Обновляем кэш desktop файлов
 | ||
|     update-desktop-database "$HOME/.local/share/applications"
 | ||
| 
 | ||
|     if [[ $4 != "nocopy" ]] ; then
 | ||
|         desktop_path="$(xdg-user-dir DESKTOP)"
 | ||
|         print_info "В меню и на рабочем столе создан $desktop_filename.desktop"
 | ||
|         cp -f "$USER_WORK_PATH/$desktop_filename.desktop" "$desktop_path"
 | ||
|     else
 | ||
|         print_info "В меню создан $desktop_filename.desktop"
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -n "$INSTALL_SCRIPT_NAME" ]] \
 | ||
|     && [[ -f "$exe_file" ]]
 | ||
|     then
 | ||
|         {
 | ||
|             echo '#!/usr/bin/env bash'
 | ||
|             echo "# cmd_name: $INSTALL_SCRIPT_NAME"
 | ||
|         } > "$exe_file".whdb
 | ||
|         grep -e "info_" -e "#####" -e "export" -e "var_" "$INSTALL_SCRIPT" \
 | ||
|         | grep -vE "LAUNCH_PARAMETERS|AUTOINSTALL|WIN_FILE_EXEC|echo" \
 | ||
|         | awk '{$1=$1;print}' >> "$exe_file".whdb
 | ||
|         print_info "Создан файл настроек для $exe_file"
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| remove_desktop () {
 | ||
|     if [[ -n "$1" ]] ; then
 | ||
|         try_remove_file "$USER_WORK_PATH/$1.desktop"
 | ||
|         try_remove_file "$WH_MENU_DIR/$1.desktop"
 | ||
|         try_remove_file "$HOME/.local/share/applications/$1.desktop"
 | ||
|         try_remove_file "$(xdg-user-dir DESKTOP)/$1.desktop"
 | ||
|     fi
 | ||
| 
 | ||
|     # Удаляем категорию если она пуста
 | ||
|     if [[ -d "$WH_MENU_DIR" ]] && [[ -z "$(ls -A "$WH_MENU_DIR")" ]]; then
 | ||
|         try_remove_dir "$WH_MENU_DIR"
 | ||
|         try_remove_file "$WH_MENU_CATEGORY"
 | ||
|         try_remove_file "$WH_MENU_CONFIG"
 | ||
|     fi
 | ||
| 
 | ||
|     # Обновляем кэш desktop файлов
 | ||
|     update-desktop-database "$HOME/.local/share/applications"
 | ||
| 
 | ||
| }
 | ||
| 
 | ||
| check_installed_programs () {
 | ||
|     for desktop_file in "$USER_WORK_PATH"/*.desktop ; do
 | ||
|         if [[ ! -f "$desktop_file" ]] ; then
 | ||
|             print_info "Установленные программы не найдены."
 | ||
|             return 1
 | ||
|         fi
 | ||
|         EXE_PATH="$(grep "Exec" "$desktop_file" | awk -F'"' '{print $4}')"
 | ||
|         case "$1" in
 | ||
|             check_only)
 | ||
|                 if [[ -z $2 ]] ; then
 | ||
|                     print_error "Нет аргумента для проверки файла запуска!"
 | ||
|                     print_info "Список установленных программ:"
 | ||
|                     check_installed_programs
 | ||
|                     exit 1
 | ||
|                 elif [[ "$desktop_file" =~ ${2}.desktop ]] ; then
 | ||
|                     export EXE_PATH
 | ||
|                     return 0
 | ||
|                 fi
 | ||
|             ;;
 | ||
|             *)
 | ||
|                 if [[ -f "$EXE_PATH.whdb" ]] ; then
 | ||
|                     WH_INFO_RU="$(grep "info_ru:" "$EXE_PATH.whdb" | awk -F"info_ru: " '{print $2}')"
 | ||
|                     WH_PROG_NAME="$(grep "PROG_NAME" "$EXE_PATH.whdb" | awk -F"=" '{print $2}')"
 | ||
|                     printf "\E[36m%s $SCRIPT_NAME run $(basename "$desktop_file" .desktop) %s\e[0m- $WH_PROG_NAME\n"
 | ||
|                     echo -e "$WH_INFO_RU\n"
 | ||
|                 fi
 | ||
|             ;;
 | ||
|         esac
 | ||
|     done
 | ||
|     [[ -n $2 ]] && fatal "Не найден файл запуска для $2"
 | ||
| }
 | ||
| 
 | ||
| run_installed_programs () {
 | ||
|     if check_installed_programs check_only "$1" ; then
 | ||
|         /usr/bin/env bash -c "\"$RUN_SCRIPT\" \"$EXE_PATH\"" &
 | ||
|         exit 0
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| init_wined3d () {
 | ||
|     if [[ "$USE_RENDERER" != "proton" ]] ; then
 | ||
|         WINED3D_FILES="d3d8 d3d9 d3d10_1 d3d10 d3d10core d3d11 dxgi d3d12 d3d12core"
 | ||
|         for wined3dfiles in $WINED3D_FILES ; do
 | ||
|             try_copy_wine_dll_to_pfx_64 "$wined3dfiles.dll"
 | ||
|             try_copy_wine_dll_to_pfx_32 "$wined3dfiles.dll"
 | ||
|         done
 | ||
| #         if [[ "$USE_RENDERER" == "damavand" ]]
 | ||
| #         then export WINE_D3D_CONFIG="renderer=vulkan"
 | ||
| #         else export WINE_D3D_CONFIG="renderer=gl"
 | ||
| #         fi
 | ||
|         return 0
 | ||
|     else
 | ||
|         return 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| init_dxvk () {
 | ||
|     check_variables USE_DXVK_VER "$1"
 | ||
| 
 | ||
|     get_dxvk () {
 | ||
|         DXVK_URL="$1"
 | ||
|         DXVK_PACKAGE="${WH_VULKAN_LIBDIR}/dxvk-${DXVK_VAR_VER}.tar.$(echo ${DXVK_URL#*.tar.})"
 | ||
|         if try_download "$DXVK_URL" "$DXVK_PACKAGE" check256sum \
 | ||
|         && unpack "$DXVK_PACKAGE" "$WH_VULKAN_LIBDIR"
 | ||
|         then
 | ||
|             try_remove_file "$DXVK_PACKAGE"
 | ||
|             return 0
 | ||
|         fi
 | ||
|         return 1
 | ||
|     }
 | ||
| 
 | ||
|     for DXVK_VAR_VER in "$USE_DXVK_VER" $@ ; do
 | ||
|         if [[ ! -d "${WH_VULKAN_LIBDIR}/dxvk-$DXVK_VAR_VER" ]] ; then
 | ||
|             get_dxvk "$CLOUD_URL/dxvk-${DXVK_VAR_VER}.tar.xz"
 | ||
|         fi
 | ||
|     done
 | ||
| 
 | ||
|     if [[ "${WH_USE_WINE_DXGI}" == 1 ]] ; then
 | ||
|         DXVK_FILES="d3d9 d3d10_1 d3d10 d3d11" # dxvk_config openvr_api_dxvk"
 | ||
|         try_copy_wine_dll_to_pfx_64 "dxgi.dll"
 | ||
|         try_copy_wine_dll_to_pfx_32 "dxgi.dll"
 | ||
|     else
 | ||
|         DXVK_FILES="d3d9 d3d10_1 d3d10 d3d11 dxgi" # dxvk_config openvr_api_dxvk"
 | ||
|     fi
 | ||
| 
 | ||
|     for dxvkfiles in $DXVK_FILES ; do
 | ||
|         try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/dxvk-$USE_DXVK_VER/x64/$dxvkfiles.dll"
 | ||
|         if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/dxvk-$USE_DXVK_VER/x32/$dxvkfiles.dll"
 | ||
|         then var_winedlloverride_update "$dxvkfiles=n"
 | ||
|         fi
 | ||
|     done
 | ||
| }
 | ||
| 
 | ||
| init_vkd3d () {
 | ||
|     check_variables USE_VKD3D_VER "$1"
 | ||
| 
 | ||
|     get_vkd3d () {
 | ||
|         VKD3D_URL="$1"
 | ||
|         VKD3D_PACKAGE="${WH_VULKAN_LIBDIR}/vkd3d-proton-${VKD3D_VAR_VER}.tar.$(echo ${VKD3D_URL#*.tar.})"
 | ||
|         if try_download "$VKD3D_URL" "$VKD3D_PACKAGE" check256sum \
 | ||
|         && unpack "$VKD3D_PACKAGE" "$WH_VULKAN_LIBDIR"
 | ||
|         then
 | ||
|             try_remove_file "$VKD3D_PACKAGE"
 | ||
|             return 0
 | ||
|         fi
 | ||
|         return 1
 | ||
|     }
 | ||
| 
 | ||
|     for VKD3D_VAR_VER in "$USE_VKD3D_VER" $@ ; do
 | ||
|         if [[ ! -d "${WH_VULKAN_LIBDIR}/vkd3d-proton-$VKD3D_VAR_VER" ]] ; then
 | ||
|             get_vkd3d "$CLOUD_URL/vkd3d-proton-${VKD3D_VAR_VER}.tar.xz"
 | ||
|         fi
 | ||
|     done
 | ||
| 
 | ||
|     VKD3D_FILES="d3d12 d3d12core libvkd3d-shader-1 libvkd3d-1" # libvkd3d-proton-utils-3
 | ||
|     for vkd3dfiles in $VKD3D_FILES ; do
 | ||
|         try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/vkd3d-proton-$USE_VKD3D_VER/x64/$vkd3dfiles.dll"
 | ||
|         if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/vkd3d-proton-$USE_VKD3D_VER/x86/$vkd3dfiles.dll"
 | ||
|         then var_winedlloverride_update "$vkd3dfiles=n"
 | ||
|         fi
 | ||
|     done
 | ||
| }
 | ||
| 
 | ||
| init_wine_ver () {
 | ||
|     if [[ "$WH_WINE_USE" != system* ]] ; then
 | ||
|         export WINEDIR="$WH_DIST_DIR/$WH_WINE_USE"
 | ||
| 
 | ||
|         if [[ ! -d "$WINEDIR" ]] ; then
 | ||
|             local download_url wine_package_name wine_package check_sum_arg
 | ||
|             WINE_METADATA_FILE="$WH_TMP_DIR/wine_metadata.json"
 | ||
| 
 | ||
|             # Сначала пытаемся сформировать URL по старой схеме, как основной для автоустановки
 | ||
|             local old_schema_url="$CLOUD_URL/$WH_WINE_USE.tar.xz"
 | ||
| 
 | ||
|             # `curl -f -s -I` делает HEAD-запрос. `-f` заставляет его вернуть код ошибки при 404.
 | ||
|             if curl -f -s -I "$old_schema_url" > /dev/null ; then
 | ||
|                 download_url="$old_schema_url"
 | ||
|             elif [[ -f "$WINE_METADATA_FILE" ]]; then
 | ||
|                 # Если по старому URL ничего нет, ищем в метаданных
 | ||
|                 print_info "Версия '$WH_WINE_USE' не найдена на основном сервере, ищем в метаданных..."
 | ||
|                 download_url=$(jq -r --arg name "$WH_WINE_USE" '.[] | .[] | select(.name == $name) | .url' "$WINE_METADATA_FILE" | head -n 1)
 | ||
|             else
 | ||
|                 # Если и метаданных нет, то считаем, что должен был быть старый URL
 | ||
|                 print_warning "Файл метаданных не найден. Предполагается, что версия '$WH_WINE_USE' находится на основном сервере."
 | ||
|                 download_url="$old_schema_url"
 | ||
|             fi
 | ||
| 
 | ||
|             # Если URL так и не был найден, выводим ошибку.
 | ||
|             if [[ -z "$download_url" ]]; then
 | ||
|                 fatal "Не удалось найти URL для скачивания версии '$WH_WINE_USE'."
 | ||
|             fi
 | ||
| 
 | ||
|             wine_package_name="$(basename "$download_url")"
 | ||
|             wine_package="$WH_TMP_DIR/$wine_package_name"
 | ||
| 
 | ||
|             # Проверяем хэш-сумму только для файлов с нашего сервера
 | ||
|             if [[ "$download_url" == *"$CLOUD_URL"* ]]; then
 | ||
|                 check_sum_arg="check256sum"
 | ||
|             fi
 | ||
| 
 | ||
|             try_download "$download_url" "$wine_package" "$check_sum_arg"
 | ||
|             unpack "$wine_package" "$WH_DIST_DIR/"
 | ||
|             try_remove_file "$wine_package"
 | ||
| 
 | ||
|             # Управление структурой подкаталога Proton "files", перемещая содержимое вверх
 | ||
|             if [[ -d "$WINEDIR/files" ]]; then
 | ||
|                 print_info "Обнаружена структура каталогов Proton, исправляем пути..."
 | ||
|                 mv "$WINEDIR"/files/* "$WINEDIR/"
 | ||
|                 rmdir "$WINEDIR/files"
 | ||
|             fi
 | ||
|         fi
 | ||
| 
 | ||
|         [[ ! -f "$WINEDIR/version" ]] && echo "$WH_WINE_USE" > "$WINEDIR/version"
 | ||
| 
 | ||
|         export WINE="$WINEDIR/bin/wine"
 | ||
|         export WINELOADER="$WINEDIR/bin/wine"
 | ||
|         export WINESERVER="$WINEDIR/bin/wineserver"
 | ||
| 
 | ||
|         if [[ -n "${PATH}" ]]
 | ||
|         then export PATH="$WINEDIR/bin:${PATH}"
 | ||
|         else export PATH="$WINEDIR/bin"
 | ||
|         fi
 | ||
| 
 | ||
|         var_ld_library_path_update "$WINEDIR/lib"
 | ||
|         export WINEDLLPATH="$WINEDIR/lib/wine"
 | ||
| 
 | ||
|         if [[ ! -d "$WINEDIR/lib64/wine" ]] \
 | ||
|         && [[ -d "$WINEDIR/lib/wine/x86_64-unix" ]]
 | ||
|         then
 | ||
|             create_new_dir "$WINEDIR/lib64/"
 | ||
|             try_force_link_dir "$WINEDIR/lib/wine/" "$WINEDIR/lib64/"
 | ||
|         fi
 | ||
| 
 | ||
|         if [[ -d "$WINEDIR/lib64" ]] \
 | ||
|         && [[ ! -L "$WINEDIR/lib64/wine" ]]
 | ||
|         then
 | ||
|             var_ld_library_path_update "$WINEDIR/lib64"
 | ||
|             export WINEDLLPATH+=":$WINEDIR/lib64/wine"
 | ||
|         fi
 | ||
| 
 | ||
|         if [[ -d "$WINEDIR/lib/gstreamer-1.0" ]] ; then
 | ||
|             export GST_PLUGIN_SYSTEM_PATH_1_0="$WINEDIR/lib/gstreamer-1.0"
 | ||
|             if [[ -d "$WINEDIR/lib64/gstreamer-1.0" ]] ; then
 | ||
|                 export GST_PLUGIN_SYSTEM_PATH_1_0+=":$WINEDIR/lib64/gstreamer-1.0"
 | ||
|             fi
 | ||
|         fi
 | ||
| 
 | ||
|         if [[ $WH_USE_CPCSP_PROXY == "1" ]] \
 | ||
|         && ! grep -q "$CPCSP_PROXY_VER" "$WINEDIR/cpcsp_proxy.ver"
 | ||
|         then
 | ||
|             CPCSP_PROXY_NAME="wine-cpcsp_proxy-$CPCSP_PROXY_VER"
 | ||
|             CPCSP_PROXY_URL="$CLOUD_URL/$CPCSP_PROXY_NAME.tar.xz"
 | ||
| 
 | ||
|             try_download "$CPCSP_PROXY_URL" "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" check256sum
 | ||
|             unpack "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" "$WH_TMP_DIR"
 | ||
| 
 | ||
|             cp -fr "$WH_TMP_DIR/$CPCSP_PROXY_NAME/"i386-* "$WINEDIR/lib/wine/"
 | ||
|             if [[ -d "$WINEDIR/lib64" ]] ; then
 | ||
|                 cp -fr "$WH_TMP_DIR/$CPCSP_PROXY_NAME/"x86_64-* "$WINEDIR/lib64/wine/"
 | ||
|             fi
 | ||
|             try_remove_dir "$WH_TMP_DIR/$CPCSP_PROXY_NAME"
 | ||
|             echo "$CPCSP_PROXY_VER" > "$WINEDIR/cpcsp_proxy.ver"
 | ||
|         fi
 | ||
|     else
 | ||
|         # use system WINE
 | ||
|         if [[ ! -f "/usr/bin/wine" ]] ; then
 | ||
|             fatal "system WINE - not found."
 | ||
|         fi
 | ||
|         export WINEDIR="/usr"
 | ||
|         export WINELOADER="wine"
 | ||
|         export WINESERVER="wineserver"
 | ||
|     fi
 | ||
| 
 | ||
|     print_info "Используется версия wine: $WH_WINE_USE"
 | ||
| }
 | ||
| 
 | ||
| get_and_set_reg_file () {
 | ||
| 
 | ||
|     convert_dec_and_hex () {
 | ||
|         local type=$1
 | ||
|         local num=$2
 | ||
| 
 | ||
|         case "$type" in
 | ||
|             --dec)
 | ||
|             # Преобразование из десятичного в шестнадцатеричный
 | ||
|             echo -n "$(printf "%08x" "$num")" ;;
 | ||
|             --hex)
 | ||
|             # Преобразование из шестнадцатеричного в десятичный
 | ||
|             echo $(( 0x$num )) ;;
 | ||
|             *)
 | ||
|             echo "Неверный тип преобразования. Используйте --dec или --hex." ;;
 | ||
|         esac
 | ||
|         }
 | ||
| 
 | ||
|     local name_block name_for_find find_block find_file find_line count name_for_new_block name_for_find_old
 | ||
|     local line_reg find_number_line find_check_file name_for_set name_type_reg name_fatal name_add_or_del
 | ||
|     name_add_or_del=$1
 | ||
|     name_block=$2
 | ||
|     name_for_find=$3
 | ||
|     name_type_reg=$4
 | ||
|     name_for_set=$5
 | ||
|     name_for_new_block=$6
 | ||
|     name_for_find_old=$name_for_find
 | ||
|     name_fatal="$name_block $name_for_find"
 | ||
| 
 | ||
|     case $name_type_reg in
 | ||
|         REG_DWORD)
 | ||
|             if [[ $name_for_find != '@=' ]]
 | ||
|             then name_for_find="\"$name_for_find\"=dword:"
 | ||
|             else name_for_find="@=dword:"
 | ||
|             fi
 | ||
|             name_for_set=$(convert_dec_and_hex --dec "$name_for_set") ;;
 | ||
|         REG_SZ)
 | ||
|             if [[ $name_for_find != '@=' ]]
 | ||
|             then name_for_find="\"$name_for_find\"="
 | ||
|             else name_for_find="@="
 | ||
|             fi
 | ||
|             name_for_set="\"$name_for_set\"" ;;
 | ||
|         *)
 | ||
|             if [[ $name_add_or_del == --delete ]] ; then
 | ||
|                 if [[ $name_for_find != '@=' ]]
 | ||
|                 then name_for_find="\"$name_for_find\""
 | ||
|                 else name_for_find="@="
 | ||
|                 fi
 | ||
|             else
 | ||
|                 print_error "не задан тип ветки реестра: $name_fatal"
 | ||
|                 return 1
 | ||
|             fi ;;
 | ||
|     esac
 | ||
|     name_block=${name_block//\\/\\\\\\\\}
 | ||
|     if [[ -n $name_for_new_block ]] ; then
 | ||
|         find_block=$(grep -n "\[$name_block\]" "$WINEPREFIX/$name_for_new_block.reg")
 | ||
|     else
 | ||
|         find_block=$(grep -n "\[$name_block\]" "$WINEPREFIX/"*.reg)
 | ||
|     fi
 | ||
|     if [[ -n $find_block ]] ; then
 | ||
|         if [[ -n $name_for_new_block ]] ; then
 | ||
|             find_file="$WINEPREFIX/$name_for_new_block.reg"
 | ||
|             find_line=${find_block//:*/}
 | ||
|         else
 | ||
|             find_file=${find_block//:*/}
 | ||
|             find_line=${find_block//$find_file:/}
 | ||
|             find_line=${find_line//:*/}
 | ||
|         fi
 | ||
|         count=-1
 | ||
|         while read -r line_reg ; do
 | ||
|             ((count++))
 | ||
|             if [[ $line_reg =~ $name_for_find ]] ; then
 | ||
|                 if [[ $line_reg == $name_for_find$name_for_set ]] ; then
 | ||
|                     return 0
 | ||
|                 fi
 | ||
|                 find_number_line=$(( count + find_line ))
 | ||
|                 find_check_file=1
 | ||
|                 break
 | ||
|             fi
 | ||
|             [[ -z $line_reg ]] && break
 | ||
|         done <<< "$(sed -n "$find_line"',$p' "$find_file")"
 | ||
|     fi
 | ||
|     if [[ $name_add_or_del == --add ]] ; then
 | ||
|         if [[ -z $find_block ]] ; then
 | ||
|             if [[ -n $name_for_new_block ]] ; then
 | ||
|                 sed -i '$a\\n'\["$name_block"\] "$WINEPREFIX/$name_for_new_block.reg"
 | ||
|                 find_file="$WINEPREFIX/$name_for_new_block.reg"
 | ||
|                 find_line=$(wc -l "$find_file" | awk -F" " '{print $1}')
 | ||
|                 find_line=$(( find_line - 1 ))
 | ||
|             else
 | ||
|                 print_error "$name_fatal не найден в файле реестра"
 | ||
|                 return 1
 | ||
|             fi
 | ||
|         fi
 | ||
|         if [[ $find_check_file == 1 ]] ; then
 | ||
|             print_info "Меняем $name_for_find_old в ветке реестра: $name_block"
 | ||
|             sed -i "${find_number_line}s|$name_for_find.*|$name_for_find$name_for_set|" "$find_file"
 | ||
|         else
 | ||
|             print_info "Добавляем $name_for_find_old в ветку реестра: $name_block"
 | ||
|             sed -i "$(( find_line + 1 ))a$name_for_find$name_for_set" "$find_file"
 | ||
|         fi
 | ||
|     elif [[ $name_add_or_del == --delete ]] ; then
 | ||
|         [[ $find_check_file != 1 ]] && return 0
 | ||
|         print_info "Удаляем $name_for_find_old из ветки реестра: $name_block"
 | ||
|         sed -i "${find_number_line}d" "$find_file"
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| wait_wineserver () {
 | ||
|     "$WINESERVER" -w
 | ||
| }
 | ||
| 
 | ||
| get_base_pfx () {
 | ||
|     local pfx_file_name pfx_file_url pfx_tmp_path
 | ||
|     pfx_file_name="${1}.tar.xz"
 | ||
|     pfx_file_url="$CLOUD_URL/$pfx_file_name"
 | ||
|     pfx_tmp_path="$WH_TMP_DIR/pfx"
 | ||
| 
 | ||
|     create_new_dir "$pfx_tmp_path"
 | ||
|     print_info "Загрузка архива базового префикса: $pfx_file_name"
 | ||
|     try_download "$pfx_file_url" "$pfx_tmp_path/$pfx_file_name" check256sum
 | ||
|     unpack --skip-xattr "$pfx_tmp_path/$pfx_file_name" "$WINEPREFIX/"
 | ||
| }
 | ||
| 
 | ||
| check_prefix_var () {
 | ||
|     if [[ -z "$WINEPREFIX" ]] ; then
 | ||
|         local prefixes=()
 | ||
|         local count=1
 | ||
|         for prefix in "$WH_PREFIXES_DIR"/* ; do
 | ||
|             if [[ -d "$prefix" ]]; then
 | ||
|                 prefixes+=("$prefix")
 | ||
|                 ((count++))
 | ||
|             fi
 | ||
|         done
 | ||
| 
 | ||
|         if [[ ${#prefixes[@]} -eq 0 ]]
 | ||
|         then fatal "Не найдено ни одного префикса!"
 | ||
|         else print_info "Доступные префиксы WineHelper:"
 | ||
|         fi
 | ||
| 
 | ||
|         echo "0 - Отмена"
 | ||
| 
 | ||
|         for ((i=0; i<${#prefixes[@]}; i++)); do
 | ||
|             echo "$((i+1)) - $(basename "${prefixes[$i]}")"
 | ||
|         done
 | ||
| 
 | ||
|         local max_choice=${#prefixes[@]}
 | ||
|         read -p "Выберите префикс (0-$max_choice): " choice
 | ||
| 
 | ||
|         if [[ "$choice" == "0" ]]; then
 | ||
|             print_info "Выбор префикса отменен."
 | ||
|             exit 0
 | ||
|         elif [[ "$choice" -ge 1 && "$choice" -le "$max_choice" ]] ; then
 | ||
|             export WINEPREFIX="${prefixes[$choice-1]}"
 | ||
|         else
 | ||
|             fatal "Неверный выбор."
 | ||
|         fi
 | ||
|     fi
 | ||
| 
 | ||
|     if echo "$WINEPREFIX" | grep -qv '/' ; then
 | ||
|         export WINEPREFIX="$WH_PREFIXES_DIR/$WINEPREFIX"
 | ||
|     fi
 | ||
| 
 | ||
|     export PREFIX_NAME="$(basename "$WINEPREFIX")"
 | ||
|     print_info "Выбран префикс: $PREFIX_NAME"
 | ||
| 
 | ||
|     return 0
 | ||
| }
 | ||
| 
 | ||
| init_wineprefix () {
 | ||
|     check_prefix_var
 | ||
| 
 | ||
|     if [[ "$WINEARCH" == "win32" ]]
 | ||
|     then check_variables BASE_PFX "defpfx_x86_v01"
 | ||
|     else check_variables BASE_PFX "defpfx_x64_v01"
 | ||
|     fi
 | ||
| 
 | ||
|     export DRIVE_C="$WINEPREFIX/drive_c"
 | ||
|     export XUSER_PATH="$DRIVE_C/users/xuser"
 | ||
| 
 | ||
|     if [[ ! -f "$WINEPREFIX/.firstboot" ]] ; then
 | ||
|         create_new_dir "$WINEPREFIX"
 | ||
|         if [[ "$CLEAR_PREFIX" == "1" ]]
 | ||
|         then print_warning "Используется переменная \"CLEAR_PREFIX=1\" что принудительно создает чистый префикс с установкой компонентов с помощью winetricks."
 | ||
|         elif [[ "$BASE_PFX" != "none" ]]
 | ||
|         then get_base_pfx "$BASE_PFX"
 | ||
|         fi
 | ||
|         print_info "Обновление префикса $WINEPREFIX."
 | ||
|         if [[ -d "$WINEPREFIX/drive_c/windows" ]]
 | ||
|         then "$WINELOADER" wineboot -u
 | ||
|         else "$WINELOADER" wineboot -i
 | ||
|         fi
 | ||
|         touch "$WINEPREFIX/.firstboot"
 | ||
|         wait_wineserver
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -f "$WINEPREFIX/system.reg" ]] \
 | ||
|     && [[ -z $(grep "Windows $WH_WINDOWS_VER" "$WINEPREFIX/system.reg") ]]
 | ||
|     then
 | ||
|         if [[ $(echo "$WH_WINDOWS_VER" | sed 's/.*/\L&/') == "xp" ]] \
 | ||
|         && [[ "$WINEARCH" != "win32" ]]
 | ||
|         then export WH_WINDOWS_VER="xp64"
 | ||
|         fi
 | ||
|         "$WINELOADER" winecfg -v $(echo "win${WH_WINDOWS_VER}" | sed 's/.*/\L&/')
 | ||
|         wait_wineserver
 | ||
|         print_info "Windows версия изменена на win${WH_WINDOWS_VER}"
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -d "$XUSER_PATH" ]] && [[ ! -d "$DRIVE_C/users/$USER" ]]
 | ||
|     then try_force_link_dir "$XUSER_PATH" "$DRIVE_C/users/$USER"
 | ||
|     elif [[ ! -d "$XUSER_PATH" ]] && [[ -d "$DRIVE_C/users/$USER" ]]
 | ||
|     then try_force_link_dir "$DRIVE_C/users/$USER" "$XUSER_PATH"
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ ! -f "$WINEPREFIX/.update-timestamp" ]] ; then
 | ||
|         print_info "Обновление префикса $WINEPREFIX."
 | ||
|         "$WINELOADER" wineboot -u
 | ||
|         wait_wineserver
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -L "$XUSER_PATH/Desktop" ]]
 | ||
|     then rm -f "$XUSER_PATH/Desktop"
 | ||
|     fi
 | ||
|     create_new_dir "$XUSER_PATH/Desktop"
 | ||
| 
 | ||
|     if [[ ! -L "$WINEPREFIX/dosdevices/h:" ]]
 | ||
|     then try_force_link_dir "$HOME" "$WINEPREFIX/dosdevices/h:"
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ $WH_USE_MESA_GL_OVERRIDE == "1" ]] \
 | ||
|     && ! lspci | grep -i nvidia > /dev/null ; then
 | ||
|         export MESA_GL_VERSION_OVERRIDE="3.3"
 | ||
|         export MESA_GLSL_VERSION_OVERRIDE="330"
 | ||
|     fi
 | ||
| 
 | ||
|     # хак для XRDP сессии
 | ||
|     if [[ $DISPLAY == *:10.0 ]] ; then
 | ||
|         print_warning "Обнаружена сессия XRDP, настраиваем реестр:"
 | ||
|         get_and_set_reg_file --add 'Software\Wine\X11 Driver' 'UseXRandR' 'REG_SZ' "N" "user"
 | ||
|         get_and_set_reg_file --add 'Software\Wine\X11 Driver' 'UseXVidMode' 'REG_SZ' "N" "user"
 | ||
|     else
 | ||
|         get_and_set_reg_file --delete 'Software\Wine\X11 Driver' 'UseXRandR'
 | ||
|         get_and_set_reg_file --delete 'Software\Wine\X11 Driver' 'UseXVidMode'
 | ||
|     fi
 | ||
| 
 | ||
|     # добавление ассоциаций файлов для запуска нативного приложения из wine
 | ||
|     # пример переменной: WH_XDG_OPEN="txt doc pdf"
 | ||
|     check_variables WH_XDG_OPEN "0"
 | ||
|     local WRAPPER="${WH_TMP_DIR}/wh-xdg-open.sh"
 | ||
|     local XDG_OPEN_REG="Software\Classes\xdg-open\shell\open\command"
 | ||
|     if [[ $WH_XDG_OPEN != "0" ]] ; then
 | ||
|         # проверяем на наличие запрещённых расширений
 | ||
|         local forbidden_extensions="cpl dll exe lnk msi"
 | ||
|         for ext in $WH_XDG_OPEN; do
 | ||
|             if [[ "$forbidden_extensions" =~ $ext ]] ; then
 | ||
|                 fatal "Расширение .$ext запрещено для добавления!"
 | ||
|             fi
 | ||
|         done
 | ||
| 
 | ||
|         # создаем скрипт-обёртку
 | ||
|         {
 | ||
|             echo "#!/usr/bin/env bash"
 | ||
|             echo "unix_path=\$(\"$WINELOADER\" winepath -u \"\$*\")"
 | ||
|             echo "xdg-open \"\$unix_path\""
 | ||
|         } > "$WRAPPER"
 | ||
|         chmod +x "$WRAPPER"
 | ||
| 
 | ||
|         # добавляем новую команду xdg-open в реестр
 | ||
|         get_and_set_reg_file --add "$XDG_OPEN_REG" '@=' 'REG_SZ' "$WRAPPER %1" "system"
 | ||
| 
 | ||
|         # добавляем ассоциации файлов для запуска с помощью xdg-open
 | ||
|         for ext in $WH_XDG_OPEN ; do
 | ||
|             get_and_set_reg_file --add "Software\Classes\.$ext" '@=' 'REG_SZ' "xdg-open" "system"
 | ||
|         done
 | ||
|         print_info "Используется ассоциации с нативными приложениями для файлов: \"$WH_XDG_OPEN\""
 | ||
|     else
 | ||
|         # удаление команды xdg-open из реестра
 | ||
|         get_and_set_reg_file --delete "$XDG_OPEN_REG" '@='
 | ||
|         # удаяем скрипт-обёртку
 | ||
|         try_remove_file "$WRAPPER"
 | ||
|     fi
 | ||
| 
 | ||
|     # настраиваем префикс для работы с cpcsp_proxy
 | ||
|     if [[ $WH_USE_CPCSP_PROXY == "1" ]] ; then
 | ||
|         for cpcsp_proxy_file in "cpcsp_proxy.dll" "cpcsp_proxy_setup.exe" ; do
 | ||
|             try_copy_wine_dll_to_pfx_64 "$cpcsp_proxy_file"
 | ||
|             try_copy_wine_dll_to_pfx_32 "$cpcsp_proxy_file"
 | ||
|         done
 | ||
| 
 | ||
|         if ! grep -q "cpcsp_proxy.dll" "$WINEPREFIX/system.reg" ; then
 | ||
|             if [[ "$WINEARCH" == "win32" ]]
 | ||
|             then cpcsp_proxy_cmd=("$WINELOADER" "cpcsp_proxy_setup.exe")
 | ||
|             else cpcsp_proxy_cmd=("${WINELOADER}64" "cpcsp_proxy_setup.exe")
 | ||
|             fi
 | ||
| 
 | ||
|             print_info "Запускаем настройку cpcsp_proxy..."
 | ||
|             unset CPCSP_PROXY_OK
 | ||
|             set -o pipefail
 | ||
|             for ((i=2; i < 6; i++)) ; do
 | ||
|                 "${cpcsp_proxy_cmd[@]}" | tee "$WINEPREFIX/cpcsp_setup.log"
 | ||
|                 local CPCSP_EXIT_STATUS="${PIPESTATUS[0]}"
 | ||
|                 if grep -q "failed to load /opt/cprocsp/" "$WINEPREFIX/cpcsp_setup.log" ; then
 | ||
|                     fatal "Проверьте правильность установки CryptoPro в системе.\n Инструкция: https://www.altlinux.org/CryptoPro"
 | ||
|                 fi
 | ||
| 
 | ||
|                 if [[ $CPCSP_EXIT_STATUS == "0" ]] ; then
 | ||
|                     print_info "Настройка cpcsp_proxy успешно завершена."
 | ||
|                     CPCSP_PROXY_OK="1"
 | ||
|                     break
 | ||
|                 fi
 | ||
|                 print_warning "Попытка $i из 5..."
 | ||
|                 wait_wineserver
 | ||
|             done
 | ||
|             wait_wineserver
 | ||
|             try_remove_file "$WINEPREFIX/cpcsp_setup.log"
 | ||
|             [[ $CPCSP_PROXY_OK != "1" ]] && fatal "Ошибка во время настройки cpcsp_proxy."
 | ||
|         fi
 | ||
|     fi
 | ||
| 
 | ||
|     # Устанавливаем дополнительные шрифты (включая штрих-код)
 | ||
|     local fonts_dir="$DRIVE_C/windows/Fonts"
 | ||
|     local x_fonts_name="extra_fonts_v${EXTRA_FONTS_VER}"
 | ||
| 
 | ||
|     if [[ $WH_USE_EXTRA_FONTS == "1" ]] \
 | ||
|     && [[ ! -e "$fonts_dir/${x_fonts_name}.installed" ]]
 | ||
|     then
 | ||
|         local x_fonts_url="$CLOUD_URL/${x_fonts_name}.tar.xz"
 | ||
|         local x_fonts_tar="$WH_TMP_DIR/${x_fonts_name}.tar.xz"
 | ||
| 
 | ||
|         print_info "Устанавливаем дополнительные шрифты..."
 | ||
|         try_download "$x_fonts_url" "$x_fonts_tar" check256sum
 | ||
|         unpack "$x_fonts_tar" "$fonts_dir/"
 | ||
|         touch "$fonts_dir/${x_fonts_name}.installed"
 | ||
|     fi
 | ||
| 
 | ||
|     echo "# переменные последнего использования префикса:" > "$WINEPREFIX/last.conf"
 | ||
|     for var in WH_WINE_USE BASE_PFX WINEARCH WH_WINDOWS_VER WINEESYNC WINEFSYNC \
 | ||
|     STAGING_SHARED_MEMORY WINE_LARGE_ADDRESS_AWARE WH_USE_SHADER_CACHE WH_USE_WINE_DXGI \
 | ||
|     WINE_CPU_TOPOLOGY USE_RENDERER DXVK_VER VKD3D_VER WH_XDG_OPEN WH_USE_MESA_GL_OVERRIDE
 | ||
|     do
 | ||
|         echo "export $var=\"${!var}\"" >> "$WINEPREFIX/last.conf"
 | ||
|     done
 | ||
| }
 | ||
| 
 | ||
| kill_autostart () {
 | ||
|     [[ -z "${1}" ]] && fatal "Нет аргумента для функции kill_autostart."
 | ||
|     [[ -z "${2}" ]] && SWAIT=3 || SWAIT="${2}"
 | ||
|     sleep 5
 | ||
|     while ps aux | grep -m 1 -i "$WINESERVER" | grep -v grep &>/dev/null ; do
 | ||
|         if  [[ -z "$(ps aux | grep -m 1 -i "$1" | grep -v grep | awk '{print $2}')" ]] ; then
 | ||
|             print_info "PID для $1 не найден. Ожидаем окончания установки..."
 | ||
|             sleep "${SWAIT}"
 | ||
|         else
 | ||
|             print_ok "PID для $1 найден. Завершаем работу автозапуска приложения."
 | ||
|             "$WINESERVER" -k
 | ||
|             break
 | ||
|         fi
 | ||
|     done
 | ||
| }
 | ||
| 
 | ||
| use_winetricks () {
 | ||
|     if [[ -n "$INSTALL_DLL" ]] ; then
 | ||
|         WH_DLL_INSTALL="$(echo "$INSTALL_DLL $WH_DLL_INSTALL" | awk '{ for(i=1;i<=NF;i++){a[$i]++} }END{ for(i in a){printf("%s ",i)} }' )"
 | ||
|     fi
 | ||
|     if [[ -n "$WH_DLL_INSTALL" ]] ; then
 | ||
|         WH_DLL_NEED_INSTALL=""
 | ||
|         USE_WT_FROM_DB=0
 | ||
|         [[ ! -f "$WINEPREFIX/winetricks.log" ]] && touch "$WINEPREFIX/winetricks.log"
 | ||
|         for need_install_dll_to_pfx in $WH_DLL_INSTALL "isolate_home" ; do
 | ||
|             if ! grep "$need_install_dll_to_pfx" "$WINEPREFIX/winetricks.log" &>/dev/null ; then
 | ||
|                 if [[ -z "$WH_DLL_NEED_INSTALL" ]]
 | ||
|                 then WH_DLL_NEED_INSTALL="$need_install_dll_to_pfx"
 | ||
|                 else WH_DLL_NEED_INSTALL="$need_install_dll_to_pfx $WH_DLL_NEED_INSTALL"
 | ||
|                 fi
 | ||
|                 USE_WT_FROM_DB=1
 | ||
|             fi
 | ||
|         done
 | ||
|         if [[ "$USE_WT_FROM_DB" == "1" ]] ; then
 | ||
|             print_info "Пробуем установить компоненты: ${WH_DLL_NEED_INSTALL}"
 | ||
|             print_info "Запускаем WINETRICKS..."
 | ||
|             export WINETRICKS_DOWNLOADER="curl"
 | ||
|             "$WH_WINETRICKS" -q ${WH_DLL_NEED_INSTALL}
 | ||
|             wait_wineserver
 | ||
|         fi
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| kill_wine () {
 | ||
|     wine_pids=$(ls -l /proc/*/exe 2>/dev/null | grep -E 'wine(64)?-preloader|wineserver' | awk -F/ '{print $3}')
 | ||
| 
 | ||
|     for pw_kill_pids in ${wine_pids}; do
 | ||
|         if ps cax | grep "${pw_kill_pids}" ; then
 | ||
|             kill -n 9 "${pw_kill_pids}" &>/dev/null
 | ||
|         fi
 | ||
|     done
 | ||
| }
 | ||
| 
 | ||
| init_database () {
 | ||
|     WHDB_FILE="0"
 | ||
|     if [[ -f "$WIN_FILE_EXEC" ]] ; then
 | ||
|         WHDB="$(basename "$WIN_FILE_EXEC" .exe)"
 | ||
|         if [[ -f "$WIN_FILE_EXEC".whdb ]] ; then
 | ||
|             WHDB_FILE="$WIN_FILE_EXEC".whdb
 | ||
|         else
 | ||
|             orig_IFS="$IFS" && IFS=$'\n'
 | ||
|             if WH_FIND_DB_FILE="$(grep -ilw "#$WHDB.exe" "$WH_DB_DIR"/* )" ; then
 | ||
|                 WHDB_FILE="$WH_FIND_DB_FILE"
 | ||
|             fi
 | ||
|             IFS="$orig_IFS"
 | ||
|         fi
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ "$WHDB_FILE" != "0" ]] ; then
 | ||
|         print_info "Используется файл настроек: $WHDB_FILE"
 | ||
|         . "$WHDB_FILE"
 | ||
|     elif check_prefix_var && [[ -f "$WINEPREFIX/last.conf" ]] ; then
 | ||
|         print_info "Найдены настройки из предыдущего использования префикса: $WINEPREFIX"
 | ||
|         cat "$WINEPREFIX/last.conf"
 | ||
|         . "$WINEPREFIX/last.conf"
 | ||
|     else
 | ||
|         print_warning "Файл настроек не найден. Пропускаем."
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| prepair_wine () {
 | ||
|     var_winedlloverride_update "winemenubuilder.exe=d"
 | ||
| 
 | ||
|     if [[ -n "$INSTALL_SCRIPT_NAME" ]]
 | ||
|     then print_info "Используются настройки из скрипта установки: $INSTALL_SCRIPT_NAME"
 | ||
|     else init_database
 | ||
|     fi
 | ||
|     init_wine_ver
 | ||
|     init_wineprefix
 | ||
|     use_winetricks
 | ||
| 
 | ||
|     if init_wined3d ; then
 | ||
|         :
 | ||
|     else
 | ||
|         init_dxvk "$DXVK_VER"
 | ||
|         init_vkd3d "$VKD3D_VER"
 | ||
|     fi
 | ||
|     [[ "$MANGOHUD" == 1 ]] && MANGOHUD_RUN="mangohud"
 | ||
| }
 | ||
| 
 | ||
| wine_run () {
 | ||
|     if [[ $WINEARCH == "win32" ]] \
 | ||
|     && file "$WIN_FILE_EXEC" | grep -q "x86-64"
 | ||
|     then fatal "Нельзя запустить 64-битное приложение в 32-битном префиксе!"
 | ||
|     fi
 | ||
| 
 | ||
|     WIN_FILE_PATH="$(dirname "$WIN_FILE_EXEC")"
 | ||
|     [[ -d "$WIN_FILE_PATH" ]] && cd "$WIN_FILE_PATH"
 | ||
| 
 | ||
|     if [[ -n $LOG_FILE ]] && [[ -f "$LOG_FILE" ]] ; then
 | ||
|         echo "##### Основные переменные #####" | tee -a "$LOG_FILE"
 | ||
|         env | grep -e "WH_" -e "WINE" -e "DXVK" -e "VKD3D" | tee -a "$LOG_FILE"
 | ||
| 
 | ||
|         echo "##### Лог WINE #####" | tee -a "$LOG_FILE"
 | ||
|         $MANGOHUD_RUN "$WINELOADER" "$@" $LAUNCH_PARAMETERS 2>&1 | tee -a "$LOG_FILE"
 | ||
|     else
 | ||
|         $MANGOHUD_RUN "$WINELOADER" "$@" $LAUNCH_PARAMETERS
 | ||
|     fi
 | ||
|     wait_wineserver
 | ||
| }
 | ||
| 
 | ||
| wine_run_install () {
 | ||
|     print_info "Запускаем установку: $1."
 | ||
|     if [[ "$INSTALL_MODE" == "manual" ]]
 | ||
|     then print_warning "Рекомендуется не менять пути для установки приложения!"
 | ||
|     fi
 | ||
|     [[ ! -f "$1" ]] && fatal "Нет файла для установки: $1"
 | ||
|     case "${1,,}" in
 | ||
|         *.exe) wine_run $WINE_WIN_START "$@" ;;
 | ||
|         *.msi) wine_run msiexec /i "$@" ;;
 | ||
|         *.bat|*.cmd) wine_run "$@" ;;
 | ||
|         *) fatal "Не удалось запустить файл $1. Проверьте расширение файла." ;;
 | ||
|     esac
 | ||
|     wait_wineserver
 | ||
| }
 | ||
| 
 | ||
| run_autoinstall () {
 | ||
|     if [[ $1 == "--clear-pfx" ]] ; then
 | ||
|         export CLEAR_PREFIX="1"
 | ||
|         shift
 | ||
|     elif [[ $2 == "--clear-pfx" ]] ; then
 | ||
|         export CLEAR_PREFIX="1"
 | ||
|     fi
 | ||
| 
 | ||
|     INSTALL_SCRIPT_NAME="${1,,}"
 | ||
|     if [[ -f "$WH_AUTOINSTALL_DIR/$INSTALL_SCRIPT_NAME" ]] ; then
 | ||
|         INSTALL_SCRIPT="$WH_AUTOINSTALL_DIR/$INSTALL_SCRIPT_NAME"
 | ||
|         WH_INSTALL_MODE="auto"
 | ||
|     elif [[ -f "$WH_MANUALINSTALL_DIR/$INSTALL_SCRIPT_NAME" ]] ; then
 | ||
|         INSTALL_SCRIPT="$WH_MANUALINSTALL_DIR/$INSTALL_SCRIPT_NAME"
 | ||
|         WH_INSTALL_MODE="manual"
 | ||
|     elif [[ -d "$WH_TESTINSTALL_DIR" ]] \
 | ||
|     && [[ -f "$WH_TESTINSTALL_DIR/$INSTALL_SCRIPT_NAME" ]]
 | ||
|     then
 | ||
|         INSTALL_SCRIPT="$WH_TESTINSTALL_DIR/$INSTALL_SCRIPT_NAME"
 | ||
|         WH_INSTALL_MODE="test"
 | ||
|     else
 | ||
|         INSTALL_SCRIPT="0"
 | ||
|     fi
 | ||
|     export INSTALL_SCRIPT INSTALL_MODE
 | ||
| 
 | ||
|     if [[ $INSTALL_SCRIPT_NAME == "list" ]] || [[ -z "$INSTALL_SCRIPT_NAME" ]] ; then
 | ||
| 
 | ||
|         list_install_scripts() {
 | ||
|             local dir="$1"
 | ||
|             local title="$2"
 | ||
|             [[ ! -d "$dir" ]] || [[ -z "$(ls -A "$dir" 2>/dev/null)" ]] && return
 | ||
| 
 | ||
|             print_info "$title"
 | ||
| 
 | ||
|             awk '
 | ||
|                 FNR==1 {
 | ||
|                     if (progname) {
 | ||
|                         printf "\n%s - %s\n%s\n", filename, progname, info
 | ||
|                     }
 | ||
|                     progname=""; info=""; filename=FILENAME
 | ||
|                     sub(".*/", "", filename)
 | ||
|                 }
 | ||
|                 /info_ru:/ { sub(/.*info_ru: /, ""); info=$0 }
 | ||
|                 /PROG_NAME=/ { sub(/.*PROG_NAME=/, ""); progname=$0 }
 | ||
|                 END {
 | ||
|                     if (progname) {
 | ||
|                          printf "\n%s - %s\n%s\n", filename, progname, info
 | ||
|                     }
 | ||
|                 }
 | ||
|             ' "$dir"/*
 | ||
|         }
 | ||
| 
 | ||
|         list_install_scripts "$WH_AUTOINSTALL_DIR" "Список программ с возможностью автоматической установки:"
 | ||
|         echo
 | ||
|         list_install_scripts "$WH_MANUALINSTALL_DIR" "Список программ с возможностью установки из существующего дистрибутива:"
 | ||
|     elif [[ "$INSTALL_SCRIPT" != "0" ]] ; then
 | ||
|         source "$INSTALL_SCRIPT" "$@"
 | ||
|         print_info "Завершена установка $INSTALL_SCRIPT_NAME"
 | ||
|     else
 | ||
|         fatal "Скрипт автоматической установки для $INSTALL_SCRIPT_NAME не найден!"
 | ||
|     fi
 | ||
|     echo
 | ||
| }
 | ||
| 
 | ||
| remove_prefix() {
 | ||
|     export WINEPREFIX="$1"
 | ||
|     if [[ -z "$WINEPREFIX" ]]
 | ||
|     then print_warning "Не указано имя префикса для удаления. Выберите из списка..."
 | ||
|     fi
 | ||
|     check_prefix_var
 | ||
|     if [[ ! -d "$WINEPREFIX" ]]
 | ||
|     then fatal "Префикса \"$PREFIX_NAME\" не существует!"
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ ! $2 =~ --force|-y ]] ; then
 | ||
|         echo "======================================================"
 | ||
|         print_warning "Вы собираетесь удалить префикс "$PREFIX_NAME"."
 | ||
|         echo "Это приведёт к:"
 | ||
|         echo " - Полному удалению всех данных префикса"
 | ||
|         echo " - Удалению всех программ, установленных в этом префиксе"
 | ||
|         echo " - Удалению связанных ярлыков из меню и рабочего стола"
 | ||
|         echo "======================================================"
 | ||
|         if ! print_confirmation "Продолжить удаление?"
 | ||
|         then exit 1
 | ||
|         fi
 | ||
|     fi
 | ||
| 
 | ||
|     if try_remove_dir "$WINEPREFIX" ; then
 | ||
|         print_ok "Префикс "$PREFIX_NAME" успешно удален."
 | ||
| 
 | ||
|         for desktop_file in "$WH_MENU_DIR"/*.desktop; do
 | ||
|             if grep -q "$WINEPREFIX" "$desktop_file"; then
 | ||
|                 desktop_name=$(basename "$desktop_file")
 | ||
|                 remove_desktop "${desktop_name%.*}"
 | ||
|             fi
 | ||
|         done
 | ||
| 
 | ||
|         return 0
 | ||
|     else
 | ||
|         print_error "Не удалось удалить префикс "$PREFIX_NAME"."
 | ||
|         return 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| generate_wine_metadata () {
 | ||
|     WINE_METADATA_FILE="$WH_TMP_DIR/wine_metadata.json"
 | ||
| 
 | ||
|     if [[ -f "$WINE_METADATA_FILE" ]]; then
 | ||
|         if find "$WINE_METADATA_FILE" -mmin -1440 | grep -q . ; then
 | ||
|             print_info "Файл метаданных $WINE_METADATA_FILE обновлялся менее 24 часов назад. Пропускаем генерацию."
 | ||
|             return 0
 | ||
|         fi
 | ||
|         print_info "Файл метаданных $WINE_METADATA_FILE устарел."
 | ||
|         print_info "Начинаем обновление..."
 | ||
|     else
 | ||
|         print_info "Генерации метаданных..."
 | ||
|     fi
 | ||
| 
 | ||
|     TMP_WINE_META="$WH_TMP_DIR/wine_metadata"
 | ||
|     local FETCH_ERROR_FLAG="$TMP_WINE_META/fetch.error"
 | ||
|     rm -f "$FETCH_ERROR_FLAG"
 | ||
| 
 | ||
|     mkdir -p "$TMP_WINE_META"
 | ||
| 
 | ||
|     cleanup() {
 | ||
|         rm -rf "$TMP_WINE_META"
 | ||
|     }
 | ||
|     trap cleanup EXIT
 | ||
| 
 | ||
|     fetch_github_releases() {
 | ||
|         local repo="$1"
 | ||
|         local wine_metadata_file="$2"
 | ||
| 
 | ||
|         local url="https://api.github.com/repos/$repo/releases"
 | ||
| 
 | ||
|         if ! curl -s --fail -H "Accept: application/vnd.github.v3+json" "$url" > "$wine_metadata_file"; then
 | ||
|             touch "$FETCH_ERROR_FLAG"
 | ||
|             return 1
 | ||
|         fi
 | ||
| 
 | ||
|         if ! jq -e 'type == "array"' "$wine_metadata_file" >/dev/null 2>&1; then
 | ||
|             try_remove_file "$wine_metadata_file"
 | ||
|             touch "$FETCH_ERROR_FLAG"
 | ||
|             return 1
 | ||
|         fi
 | ||
| 
 | ||
|         print_info "Получение данных для $repo"
 | ||
|     }
 | ||
| 
 | ||
|     create_wine_entries() {
 | ||
|         local input_file="$1"
 | ||
|         local file_extension="$2"
 | ||
|         local exclude_patterns="$3"
 | ||
| 
 | ||
|         jq -r --arg ext "$file_extension" '
 | ||
|             .[] |
 | ||
|             .assets[] |
 | ||
|             select(.browser_download_url | test($ext)) |
 | ||
|             {
 | ||
|                 name: (.name | gsub($ext; "")),
 | ||
|                 url: .browser_download_url
 | ||
|             }
 | ||
|         ' "$input_file" | \
 | ||
|         if [[ -n "$exclude_patterns" ]]; then
 | ||
|             jq -c --arg patterns "$exclude_patterns" '
 | ||
|                 select(.name | test($patterns) | not)
 | ||
|             '
 | ||
|         else
 | ||
|             jq -c '.'
 | ||
|         fi
 | ||
|     }
 | ||
| 
 | ||
|     # Формат: "ключ_json;репозиторий;расширение_файла;шаблон_исключения"
 | ||
|     local sources=(
 | ||
|         "proton_ge;GloriousEggroll/proton-ge-custom;\\.tar\\.gz$;github-action"
 | ||
|         "wine_kron4ek;Kron4ek/Wine-Builds;\\.tar\\.xz$;"
 | ||
|         "proton_lg;Castro-Fidel/wine_builds;\\.tar\\.xz$;plugins"
 | ||
|         "proton_cachyos;CachyOS/proton-cachyos;\\.tar\\.xz$;znver"
 | ||
|         "proton_sarek;pythonlover02/Proton-Sarek;\\.tar\\.gz$;"
 | ||
|         "proton_em;Etaash-mathamsetty/Proton;\\.tar\\.xz$;"
 | ||
|     )
 | ||
| 
 | ||
|     for source_data in "${sources[@]}"; do
 | ||
|         (
 | ||
|             IFS=';' read -r key repo extension exclude_pattern <<< "$source_data"
 | ||
| 
 | ||
|             local releases_file="$TMP_WINE_META/${key}_releases.json"
 | ||
|             local entries_file="$TMP_WINE_META/${key}.json"
 | ||
| 
 | ||
|             fetch_github_releases "$repo" "$releases_file" || exit 1
 | ||
|             create_wine_entries "$releases_file" "$extension" "$exclude_pattern" > "$entries_file"
 | ||
|         ) &
 | ||
|     done
 | ||
| 
 | ||
|     wait
 | ||
| 
 | ||
|     if [[ -f "$FETCH_ERROR_FLAG" ]]; then
 | ||
|         fatal "Ошибка при получении релизов. Возможно, превышен лимит запросов к API GitHub или проблема с сетью."
 | ||
|     fi
 | ||
| 
 | ||
|     print_ok "Все данные получены."
 | ||
| 
 | ||
|     print_info "Создание итогового JSON файла..."
 | ||
| 
 | ||
|     jq -n \
 | ||
|         --slurpfile proton_ge "$TMP_WINE_META/proton_ge.json" \
 | ||
|         --slurpfile wine_kron4ek "$TMP_WINE_META/wine_kron4ek.json" \
 | ||
|         --slurpfile proton_lg "$TMP_WINE_META/proton_lg.json" \
 | ||
|         --slurpfile proton_cachyos "$TMP_WINE_META/proton_cachyos.json" \
 | ||
|         --slurpfile proton_sarek "$TMP_WINE_META/proton_sarek.json" \
 | ||
|         --slurpfile proton_em "$TMP_WINE_META/proton_em.json" \
 | ||
|         '{
 | ||
|             proton_ge: $proton_ge,
 | ||
|             wine_kron4ek: $wine_kron4ek,
 | ||
|             proton_lg: $proton_lg,
 | ||
|             proton_cachyos: $proton_cachyos,
 | ||
|             proton_sarek: $proton_sarek,
 | ||
|             proton_em: $proton_em
 | ||
|         }' > "$WINE_METADATA_FILE"
 | ||
| 
 | ||
|     if jq empty "$WINE_METADATA_FILE" 2>/dev/null; then
 | ||
|         print_ok "JSON файл создан успешно: $WINE_METADATA_FILE"
 | ||
|     else
 | ||
|         print_error "Ошибка создания JSON файла"
 | ||
|         exit 1
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| select_wine_version() {
 | ||
|     if ! command -v jq &> /dev/null; then
 | ||
|         print_warning "Команда 'jq' не найдена. Невозможно отобразить список версий WINE/Proton."
 | ||
|         print_warning "Будет использована версия по умолчанию: $WH_WINE_USE"
 | ||
|         return
 | ||
|     fi
 | ||
| 
 | ||
|     generate_wine_metadata
 | ||
|     WINE_METADATA_FILE="$WH_TMP_DIR/wine_metadata.json"
 | ||
|     [[ ! -f "$WINE_METADATA_FILE" ]] && fatal "Файл метаданных WINE не найден."
 | ||
| 
 | ||
|     local arch_filter_jq
 | ||
|     if [[ "$WINEARCH" == "win64" ]]; then
 | ||
|         print_info "Фильтруем версии для 64-битного префикса..."
 | ||
|         # Для 64-битных префиксов показываем сборки с 'amd64', 'x86_64', 'wow64'
 | ||
|         # или те, у которых нет явного указания на 32-битную архитектуру.
 | ||
|         arch_filter_jq='select((.name | test("amd64|x86_64|wow64")) or (.name | test("i[3-6]86|x86(?!_64)") | not))'
 | ||
|     else # win32
 | ||
|         print_info "Фильтруем версии для 32-битного префикса..."
 | ||
|         # Для 32-битных префиксов показываем только сборки с явным указанием 32-битной архитектуры.
 | ||
|         arch_filter_jq='select(.name | test("i[3-6]86|x86(?!_64)"))'
 | ||
|     fi
 | ||
| 
 | ||
|     local options=()
 | ||
|     local total_versions_found=0
 | ||
| 
 | ||
|     # --- System ---
 | ||
|     options+=("--- System ---")
 | ||
|     options+=("system")
 | ||
| 
 | ||
|     # --- Other versions from JSON ---
 | ||
|     local group_keys
 | ||
|     mapfile -t group_keys < <(jq -r 'keys_unsorted | .[]' "$WINE_METADATA_FILE")
 | ||
| 
 | ||
|     for key in "${group_keys[@]}"; do
 | ||
|         local group_versions
 | ||
|         mapfile -t group_versions < <(jq -r --arg key "$key" '.[$key] | .[] | '"$arch_filter_jq"' | .name' "$WINE_METADATA_FILE" | sort -V -r | uniq)
 | ||
| 
 | ||
|         if [[ ${#group_versions[@]} -gt 0 ]]; then
 | ||
|             # Prettify the group name (e.g., "proton_ge" -> "Proton Ge")
 | ||
|             local pretty_key
 | ||
|             pretty_key=$(echo "$key" | tr '_' ' ' | sed -e "s/\b\(.\)/\u\1/g")
 | ||
| 
 | ||
|             options+=("--- $pretty_key ---")
 | ||
|             options+=("${group_versions[@]}")
 | ||
|             ((total_versions_found+=${#group_versions[@]}))
 | ||
|         fi
 | ||
|     done
 | ||
| 
 | ||
|     if [[ $total_versions_found -eq 0 ]]; then
 | ||
|         print_warning "Не найдено подходящих версий WINE/Proton для архитектуры $WINEARCH."
 | ||
|         print_warning "Будет использована версия по умолчанию: $WH_WINE_USE"
 | ||
|         return
 | ||
|     fi
 | ||
| 
 | ||
|     # --- Пользовательское меню с разделением на группы и пустыми строками ---
 | ||
|     local selectable_options=("Отмена")
 | ||
|     local display_groups=()
 | ||
|     local current_group_items=()
 | ||
|     local choice_idx=0
 | ||
| 
 | ||
|     # Помощник для переноса текущей группы элементов в основной массив display_groups
 | ||
|     flush_current_group() {
 | ||
|         if ((${#current_group_items[@]} > 0)); then
 | ||
|             # Объединяйте элементы с помощью уникального разделителя для последующего разделения
 | ||
|             display_groups+=("$(IFS='@@@'; echo "${current_group_items[*]}")")
 | ||
|             current_group_items=()
 | ||
|         fi
 | ||
|     }
 | ||
| 
 | ||
|     current_group_items+=(" 0) Отмена создания префикса")
 | ||
| 
 | ||
|     # Обработка массива основных параметров для создания групп
 | ||
|     for opt in "${options[@]}"; do
 | ||
|         if [[ "$opt" == "---"* ]]; then
 | ||
|         flush_current_group
 | ||
|         display_groups+=("$opt") # Добавьте заголовок как отдельный элемент
 | ||
|         else
 | ||
|             ((choice_idx++))
 | ||
|             current_group_items+=(" ${choice_idx}) $opt")
 | ||
|             selectable_options+=("$opt")
 | ||
|         fi
 | ||
|     done
 | ||
|     flush_current_group # Очистка последней группы
 | ||
| 
 | ||
|     print_info "Выберите версию WINE/Proton для $WINEARCH префикса:"
 | ||
| 
 | ||
|     # Показывать группы одну за другой
 | ||
|     local first_block=true
 | ||
|     for group_data in "${display_groups[@]}"; do
 | ||
| 
 | ||
|         if [[ "$group_data" == "---"* ]]; then
 | ||
|             if [[ "$first_block" = false ]]; then
 | ||
|             echo
 | ||
|         fi
 | ||
|             echo "$group_data"
 | ||
|         else
 | ||
|             # Это блок элементов для печати в столбцах
 | ||
|             local items_to_print=()
 | ||
|             IFS='@@@' read -r -a items_to_print <<< "$group_data"
 | ||
| 
 | ||
|             local num_items=${#items_to_print[@]}
 | ||
|             local term_width=${COLUMNS:-80}
 | ||
|             local max_len=0
 | ||
|             for item in "${items_to_print[@]}"; do
 | ||
|                 (( ${#item} > max_len )) && max_len=${#item}
 | ||
|             done
 | ||
| 
 | ||
|             ((max_len+=2))
 | ||
|             local num_cols=$(( term_width / max_len ))
 | ||
|             (( num_cols = num_cols > 0 ? num_cols : 1 ))
 | ||
|             local num_rows=$(( (num_items + num_cols - 1) / num_cols ))
 | ||
| 
 | ||
|             for ((i=0; i<num_rows; i++)); do
 | ||
|                 for ((j=0; j<num_cols; j++)); do
 | ||
|                     local index=$(( i + j * num_rows ))
 | ||
|                     (( index < num_items )) && printf "%-*s" "$max_len" "${items_to_print[index]}"
 | ||
|                 done
 | ||
|                 echo
 | ||
|             done
 | ||
|         fi
 | ||
|         first_block=false
 | ||
|         done
 | ||
| 
 | ||
|     while true; do
 | ||
|         local max_choice=$(( ${#selectable_options[@]} - 1 ))
 | ||
|         read -p "Введите номер (0-$max_choice): " user_choice
 | ||
|         if [[ "$user_choice" =~ ^[0-9]+$ ]] && (( user_choice >= 0 && user_choice <= max_choice )); then
 | ||
|             if [[ "$user_choice" == "0" ]]; then
 | ||
|                 print_info "Создание префикса отменено."
 | ||
|                 exit 0
 | ||
|             fi
 | ||
|             local selected_opt="${selectable_options[$user_choice]}"
 | ||
|             export WH_WINE_USE="$selected_opt"
 | ||
|             break
 | ||
|         else
 | ||
|             print_error "Неверный выбор. Введите число от 0 до $max_choice."
 | ||
|         fi
 | ||
|     done
 | ||
| }
 | ||
| 
 | ||
| create_prefix() {
 | ||
|     print_info "Существующие префиксы:"
 | ||
|     local prefixes=()
 | ||
|     for prefix in "$WH_PREFIXES_DIR"/*; do
 | ||
|         if [[ -d "$prefix" ]] ; then
 | ||
|             prefixes+=("$(basename "$prefix")")
 | ||
|         echo " - $(basename "$prefix")"
 | ||
|         fi
 | ||
|     done
 | ||
| 
 | ||
|     if [[ ${#prefixes[@]} -eq 0 ]]; then
 | ||
|         print_info "Нет существующих префиксов."
 | ||
|     fi
 | ||
|     echo
 | ||
| 
 | ||
|     read -p "Введите имя для нового префикса или 0 для отмены (по умолчанию: default): " prefix_name
 | ||
|     if [[ "$prefix_name" == "0" ]] ; then
 | ||
|         print_info "Создание префикса отменено."
 | ||
|         exit 0
 | ||
|     fi
 | ||
| 
 | ||
|     prefix_name=${prefix_name:-default}
 | ||
| 
 | ||
|     if [[ ! "$prefix_name" =~ ^[a-zA-Z0-9_.-]+$ ]] ; then
 | ||
|         fatal "Имя префикса может содержать только латинские буквы, цифры, точки, дефисы и подчеркивания"
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -d "$WH_PREFIXES_DIR/$prefix_name" ]] ; then
 | ||
|         fatal "Префикс с именем '$prefix_name' уже существует. Создание отменено."
 | ||
|     fi
 | ||
| 
 | ||
|     print_info "Создается префикс с именем: \"$prefix_name\""
 | ||
| 
 | ||
|     print_info "Выберите разрядность префикса:"
 | ||
|     echo " 0) Отмена создания префикса"
 | ||
|     echo " 1) 32-bit"
 | ||
|     echo " 2) 64-bit"
 | ||
|     echo
 | ||
|     local arch_choice
 | ||
|     read -p "Ваш выбор [0-2] (по умолчанию 1): " arch_choice
 | ||
|     case "${arch_choice:-1}" in
 | ||
|         0) print_info "Создание префикса отменено." ; exit 0 ;;
 | ||
|         1) export WINEARCH="win32" ;;
 | ||
|         2) export WINEARCH="win64" ;;
 | ||
|         *) fatal "Неверный выбор. Операция отменена." ;;
 | ||
|     esac
 | ||
| 
 | ||
|     select_wine_version
 | ||
| 
 | ||
|     print_info "Выберите тип создаваемого префикса:"
 | ||
|     echo " 0) Отмена создания префикса"
 | ||
|     echo " 1) Чистый префикс (без библиотек)"
 | ||
|     echo " 2) С рекомендуемыми библиотеками (по умолчанию)"
 | ||
|     echo
 | ||
|     local pfx_type_choice
 | ||
|     read -p "Ваш выбор [0-2] (по умолчанию 1): " pfx_type_choice
 | ||
|     case "${pfx_type_choice:-1}" in
 | ||
|         0) print_info "Создание префикса отменено." ; exit 0 ;;
 | ||
|         1) export BASE_PFX="none" ;;
 | ||
|         2) ;; # Оставляем BASE_PFX пустым, чтобы init_wineprefix использовал значение по умолчанию
 | ||
|         *) fatal "Неверный выбор. Операция отменена." ;;
 | ||
|     esac
 | ||
| 
 | ||
|     export WINEPREFIX="$WH_PREFIXES_DIR/$prefix_name"
 | ||
| 
 | ||
|      if ! init_wine_ver || ! init_wineprefix; then
 | ||
|         fatal "Ошибка инициализации префикса."
 | ||
|     fi
 | ||
| 
 | ||
|     print_ok "Префикс '$prefix_name' (${WINEARCH}) успешно создан."
 | ||
| }
 | ||
| 
 | ||
| remove_winehelper () {
 | ||
|     local answer
 | ||
|     if [[ $1 =~ --force|-y ]] ; then
 | ||
|         answer="y"
 | ||
|     else
 | ||
|         echo "======================================================"
 | ||
|         print_warning "Вы собираетесь удалить WineHelper и все связанные с ним данные."
 | ||
|         echo " Это удалит:"
 | ||
|         echo " - Все настройки WineHelper"
 | ||
|         echo " - Все приложения/программы, установленные через WineHelper"
 | ||
|         echo " - Все ярлыки из меню и с рабочего стола созданные с помощью WineHelper"
 | ||
|         echo "======================================================"
 | ||
|         if print_confirmation "Продолжить?" ; then
 | ||
|             echo "----------------------------------------------"
 | ||
|             print_warning " ВЫ ТОЧНО УВЕРЕНЫ?"
 | ||
|             echo "----------------------------------------------"
 | ||
|             if ! print_confirmation "Продолжить?"
 | ||
|             then exit 1
 | ||
|             fi
 | ||
|         else
 | ||
|             exit 1
 | ||
|         fi
 | ||
|     fi
 | ||
| 
 | ||
|     # Удаление рабочих каталогов
 | ||
|     try_remove_dir "$USER_WORK_PATH"
 | ||
| 
 | ||
|     # Удаление файлов меню
 | ||
|     try_remove_dir "$WH_MENU_DIR"
 | ||
|     try_remove_file "$WH_MENU_CATEGORY"
 | ||
|     try_remove_file "$WH_MENU_CONFIG"
 | ||
| 
 | ||
|     # Удаление desktop-файлов
 | ||
|     for desktop_file in "$(xdg-user-dir DESKTOP)"/*.desktop; do
 | ||
|         if grep -q "Exec=env \"$RUN_SCRIPT\"" "$desktop_file"; then
 | ||
|             try_remove_file "$desktop_file"
 | ||
|         fi
 | ||
|     done
 | ||
| 
 | ||
|     # Обновление кэша desktop-файлов
 | ||
|     update-desktop-database "$HOME/.local/share/applications"
 | ||
| 
 | ||
|     # Удаление символических ссылок
 | ||
|     try_remove_file "$HOME/.winehelper"
 | ||
| 
 | ||
|     print_info "WineHelper и все связанные данные успешно удалены."
 | ||
| }
 | ||
| 
 | ||
| create_base_pfx () {
 | ||
|     export WINEPREFIX="$1"
 | ||
|     check_prefix_var
 | ||
| 
 | ||
|     local prefix_dir drive_c_dir users_dir archive_path
 | ||
|     prefix_dir="$WINEPREFIX"
 | ||
|     drive_c_dir="$prefix_dir/drive_c"
 | ||
|     users_dir="$drive_c_dir/users"
 | ||
|     archive_path="$WH_TMP_DIR/pfx/new_${PREFIX_NAME}.tar.xz"
 | ||
| 
 | ||
|     try_copy_dir "$prefix_dir" "${prefix_dir}_bak"
 | ||
|     create_new_dir "$WH_TMP_DIR/pfx/"
 | ||
| 
 | ||
|     for wtlog in workaround isolate internal winxp win2 win7 win10
 | ||
|     do sed -i "/$wtlog/d" "$prefix_dir/winetricks.log"
 | ||
|     done
 | ||
| 
 | ||
|     if [[ -d "$users_dir/$USER" ]] \
 | ||
|     && [[ ! -L "$users_dir/$USER" ]]
 | ||
|     then
 | ||
|         if [[ -L "$users_dir/xuser" ]]
 | ||
|         then try_remove_dir "$users_dir/xuser/"
 | ||
|         fi
 | ||
|         create_new_dir "$users_dir/xuser/"
 | ||
|         cp -fr "$users_dir/$USER"/* "$users_dir/xuser/"
 | ||
|     fi
 | ||
| 
 | ||
|     try_remove_file "$prefix_dir/.update-timestamp"
 | ||
|     try_remove_file "$prefix_dir/.firstboot"
 | ||
|     try_remove_file "$prefix_dir/last.conf"
 | ||
|     try_remove_dir "$prefix_dir/dosdevices/"
 | ||
|     try_remove_dir "$users_dir/$USER"
 | ||
|     try_remove_dir "$users_dir/xuser/AppData/Local/Temp/"
 | ||
|     try_remove_dir "$drive_c_dir/ProgramData/Package Cache/"
 | ||
|     try_remove_dir "$drive_c_dir/windows/temp/"
 | ||
|     try_remove_dir "$drive_c_dir/windows/Installer/"
 | ||
| 
 | ||
|     rm -fr "$drive_c_dir/windows/Microsoft.NET"/*/*/SetupCache/
 | ||
| 
 | ||
|     cd "$prefix_dir"
 | ||
|     if tar --no-xattrs -c -I 'xz --memlimit=8000MiB -9 -T0' -f "$archive_path" ./* ; then
 | ||
|         print_ok "Архив создан по пути: $archive_path"
 | ||
|         xdg-open "$(dirname "$archive_path")" &
 | ||
|         cd -
 | ||
|     else
 | ||
|         try_remove_file "$archive_path"
 | ||
|         cd -
 | ||
|         fatal "Не удалось создать архив."
 | ||
|     fi
 | ||
| }
 | ||
| 
 | ||
| backup_prefix() {
 | ||
|     export WINEPREFIX="$1"
 | ||
|     check_prefix_var
 | ||
| 
 | ||
|     local backup_base_dir backup_archive_name backup_dest_path temp_backup_dir temp_prefix_dir temp_users_dir
 | ||
|     backup_base_dir="$(xdg-user-dir DESKTOP)"
 | ||
|     backup_archive_name="backup_${PREFIX_NAME}_$(date +%d.%m.%Y-%H.%M.%S).whpack"
 | ||
|     backup_dest_path="$backup_base_dir/$backup_archive_name"
 | ||
|     temp_backup_dir="$WH_TMP_DIR/backup_${PREFIX_NAME}_$(date +%d.%m.%Y-%H.%M.%S)"
 | ||
|     temp_prefix_dir="$temp_backup_dir/prefixes/$PREFIX_NAME"
 | ||
|     temp_users_dir="$temp_prefix_dir/drive_c/users"
 | ||
| 
 | ||
|     print_info "Начало резервного копирования префикса: $PREFIX_NAME"
 | ||
|     create_new_dir "$temp_backup_dir"
 | ||
|     create_new_dir "$temp_backup_dir/prefixes/"
 | ||
|     create_new_dir "$temp_backup_dir/dist/"
 | ||
| 
 | ||
|     print_info "Подготовка префикса к упаковке..."
 | ||
|     if cp -a "$WINEPREFIX" "$temp_prefix_dir" ; then
 | ||
|         try_remove_dir "$temp_prefix_dir/dosdevices"
 | ||
|         try_remove_file "$temp_prefix_dir/.update-timestamp"
 | ||
|         if [[ -d "$temp_users_dir/$USER" ]] \
 | ||
|         && [[ ! -L "$temp_users_dir/$USER" ]]
 | ||
|         then
 | ||
|             if [[ -L "$temp_users_dir/xuser" ]]
 | ||
|             then try_remove_dir "$temp_users_dir/xuser"
 | ||
|             fi
 | ||
|             create_new_dir "$temp_users_dir/xuser"
 | ||
|             cp -fr "$temp_users_dir/$USER"/* "$temp_users_dir/xuser/"
 | ||
|         fi
 | ||
|         try_remove_dir "$temp_users_dir/$USER"
 | ||
|         print_ok "Директория префикса подготовлена."
 | ||
|     else
 | ||
|         print_error "Не удалось подготовить директорию префикса."
 | ||
|         try_remove_dir "$temp_backup_dir"
 | ||
|         return 1
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -f "$WINEPREFIX/last.conf" ]]; then
 | ||
|         source "$WINEPREFIX/last.conf"
 | ||
| 
 | ||
|         if [[ -n "$WH_WINE_USE" ]] \
 | ||
|         && [[ $WH_WINE_USE != system* ]] \
 | ||
|         && [[ -d "$WH_DIST_DIR/$WH_WINE_USE" ]]
 | ||
|         then
 | ||
|             print_info "Копирование используемой версии WINE: $WH_WINE_USE"
 | ||
| 
 | ||
|             if cp -a "$WH_DIST_DIR/$WH_WINE_USE" "$temp_backup_dir/dist/"
 | ||
|             then print_ok "WINE скопирован."
 | ||
|             else fatal "Не удалось скопировать WINE."
 | ||
|             fi
 | ||
|         fi
 | ||
|     fi
 | ||
| 
 | ||
|     # Сохраняем метаданные внутри временной директории
 | ||
|     echo "$PREFIX_NAME" > "$temp_backup_dir/prefix_name.whmeta"
 | ||
| 
 | ||
|     print_info "Создание squashfs архива..."
 | ||
|     if mksquashfs "$temp_backup_dir" "$backup_dest_path" -comp zstd ; then
 | ||
|         print_ok "Архив префикса $PREFIX_NAME успешно создан по пути:\n$backup_dest_path"
 | ||
|     else
 | ||
|         print_error "Не удалось создать архив префикса $PREFIX_NAME."
 | ||
|         try_remove_dir "$temp_backup_dir"
 | ||
|         return 1
 | ||
|     fi
 | ||
| 
 | ||
|     try_remove_dir "$temp_backup_dir"
 | ||
|     print_info "Создание резервной копии префикса завершено."
 | ||
|     return 0
 | ||
| }
 | ||
| 
 | ||
| restore_prefix() {
 | ||
|     local backup_archive_path="$1"
 | ||
|     local temp_extract_dir prefix_name
 | ||
| 
 | ||
| 
 | ||
|     if [[ -z "$backup_archive_path" ]] ; then
 | ||
|         read -e -p "Укажите путь к архиву резервной копии (/путь/к/архиву.whpack): " backup_archive_path
 | ||
|         backup_archive_path=$(echo "$backup_archive_path" | sed "s/'//g; s/\"//g")
 | ||
|         if [[ -z "$backup_archive_path" ]] ; then
 | ||
|             fatal "Путь к архиву не указан. Восстановление отменено."
 | ||
|         fi
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ ! -f "$backup_archive_path" ]]
 | ||
|     then fatal "Файл архива не найден: $backup_archive_path"
 | ||
|     fi
 | ||
| 
 | ||
|     print_info "Восстановление из резервной копии: $backup_archive_path"
 | ||
| 
 | ||
|     temp_extract_dir="$WH_TMP_DIR/restore_$(basename "$backup_archive_path" .whpack)_$$"
 | ||
|     create_new_dir "$temp_extract_dir"
 | ||
| 
 | ||
|     print_info "Распаковка архива..."
 | ||
|     if unsquashfs -f -d "$temp_extract_dir" "$backup_archive_path" ; then
 | ||
|         print_ok "Архив успешно распакован."
 | ||
|     else
 | ||
|         try_remove_dir "$temp_extract_dir"
 | ||
|         fatal "Не удалось распаковать архив."
 | ||
|     fi
 | ||
| 
 | ||
|     # Определяем имя префикса
 | ||
|     if [[ -f "$temp_extract_dir/prefix_name.whmeta" ]]; then
 | ||
|         prefix_name="$(cat "$temp_extract_dir/prefix_name.whmeta")"
 | ||
|         try_remove_file "$temp_extract_dir/prefix_name.whmeta"
 | ||
|     elif grep -q "PREFIX_NAME=" "$backup_archive_path" ; then
 | ||
|         # Обратная совместимость со старыми бэкапами (с приписанной строкой)
 | ||
|         prefix_name="$(tail -n1 "$backup_archive_path" | sed -e 's/.*PREFIX_NAME=//')"
 | ||
|     elif [[ -d "$temp_extract_dir/prefixes" ]] && [[ $(find "$temp_extract_dir/prefixes" -mindepth 1 -maxdepth 1 -type d | wc -l) -eq 1 ]]; then
 | ||
|         # Обратная совместимость со старыми бэкапами (по имени каталога)
 | ||
|         prefix_name="$(basename "$(find "$temp_extract_dir/prefixes" -mindepth 1 -maxdepth 1 -type d)")"
 | ||
|     else
 | ||
|         try_remove_dir "$temp_extract_dir"
 | ||
|         fatal "Не удалось определить имя префикса в архиве (метаданные не найдены)."
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -z "$prefix_name" ]]; then
 | ||
|         try_remove_dir "$temp_extract_dir"
 | ||
|         fatal "Не удалось определить имя префикса в архиве."
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -d "$WH_PREFIXES_DIR/$prefix_name" ]] ; then
 | ||
|         try_remove_dir "$temp_extract_dir"
 | ||
|         fatal "Префикс $prefix_name уже существует!\n Удалите его, если действительно желаете восстановить префикс заново.\n Команда для удаления: $SCRIPT_NAME remove-prefix $prefix_name"
 | ||
|     fi
 | ||
| 
 | ||
|     if [[ -d "$temp_extract_dir/prefixes/$prefix_name" ]] ; then
 | ||
|         local prefix_dir="$temp_extract_dir/prefixes/$prefix_name"
 | ||
| 
 | ||
|         print_info "Восстановление префикса: $prefix_name"
 | ||
| 
 | ||
|         if [[ -d "$temp_extract_dir/dist" ]] ; then
 | ||
|             print_info "Восстановление версий WINE"
 | ||
|             if cp -fr "$temp_extract_dir/dist"/* "$WH_DIST_DIR/"
 | ||
|             then print_ok "Версии WINE восстановлены."
 | ||
|             else print_warning "Не удалось восстановить версии WINE."
 | ||
|             fi
 | ||
|             try_remove_dir "$temp_extract_dir/dist"
 | ||
|         fi
 | ||
| 
 | ||
|         if ! mv "$prefix_dir" "$WH_PREFIXES_DIR/" ; then
 | ||
|             try_remove_dir "$temp_extract_dir"
 | ||
|             fatal "Не удалось восстановить префикс $prefix_name."
 | ||
|         else
 | ||
|             print_ok "Префикс $prefix_name восстановлен."
 | ||
| 
 | ||
|             print_info "Восстановление ярлыков для префикса $prefix_name..."
 | ||
|             export RESTORE_FROM_BACKUP="1"  # Устанавливаем флаг восстановления
 | ||
|             while IFS='=' read -r name_desktop exe_path icon_name ; do
 | ||
| 
 | ||
|                 local full_exe_path="$WH_PREFIXES_DIR/$prefix_name${exe_path}"
 | ||
|                 local full_icon_path="$WH_PREFIXES_DIR/$prefix_name/icons/$icon_name"
 | ||
| 
 | ||
|                 if [[ -f "$full_exe_path" ]]; then
 | ||
|                     print_info "Создание ярлыка для $name_desktop"
 | ||
|                     create_desktop "$name_desktop" "$full_exe_path" "$full_icon_path"
 | ||
|                 else
 | ||
|                     print_warning "Исполняемый файл для ярлыка '$name_desktop' не найден: $full_exe_path"
 | ||
|                 fi
 | ||
|             done < "$WH_PREFIXES_DIR/$prefix_name/desktop.list"
 | ||
|             unset RESTORE_FROM_BACKUP  # Снимаем флаг после завершения
 | ||
|             print_ok "Ярлыки для префикса $prefix_name восстановлены."
 | ||
|         fi
 | ||
|     else
 | ||
|         try_remove_dir "$temp_extract_dir"
 | ||
|         fatal "Директория префикса '$prefix_name' не найдена в архиве."
 | ||
|     fi
 | ||
| 
 | ||
|     try_remove_dir "$temp_extract_dir"
 | ||
|     print_ok "Полное восстановление префикса $prefix_name завершено."
 | ||
|     return 0
 | ||
| }
 | ||
| 
 | ||
| wh_info () {
 | ||
|     echo "Использование: $SCRIPT_NAME [команда]
 | ||
| 
 | ||
| Команды:
 | ||
|     install list                    список возможных установочных скриптов
 | ||
|     install [скрипт]                запустить скрипт установки программы
 | ||
|     install [скрипт] --clear-pfx    не использовать готовый префикс для установки ПО
 | ||
| 
 | ||
|     installed                       список установленных программ
 | ||
|     run [программа]                 запуск программы (отладка)
 | ||
|     remove-all                      удалить WineHelper и все связанные данные
 | ||
|     create-prefix                   создать префикс
 | ||
|     remove-prefix [имя_префикса]    удалить префикс и все связанные данные
 | ||
|     backup-prefix [имя_префикса]    создать резервную копию префикса
 | ||
|     restore-prefix \"путь/до/whpack\" восстановить префикс из резервной копии
 | ||
| 
 | ||
| Параметры:
 | ||
|     --help                          показать эту справку и выйти
 | ||
|     --version                       показать информацию о пакете и его версии
 | ||
|     --changelog                     показать историю изменений
 | ||
|     --debug [команда]               включить режим логирования работы WINE
 | ||
| 
 | ||
| "
 | ||
| }
 | ||
| 
 | ||
| ##### MAIN #####
 | ||
| create_new_dir "$WH_TMP_DIR"
 | ||
| create_new_dir "$WH_DIST_DIR"
 | ||
| create_new_dir "$WH_PREFIXES_DIR"
 | ||
| create_new_dir "$WH_VULKAN_LIBDIR"
 | ||
| 
 | ||
| if [[ -d "$HOME/.local/share/$SCRIPT_NAME" ]] \
 | ||
| && [[ ! -L "$HOME/.winehelper" ]]
 | ||
| then try_force_link_dir "$HOME/.local/share/$SCRIPT_NAME" "$HOME/.winehelper"
 | ||
| fi
 | ||
| 
 | ||
| if [[ -n "$1" ]] ; then
 | ||
|     arg1="$1"
 | ||
|     shift
 | ||
| else
 | ||
|     arg1="--help"
 | ||
| fi
 | ||
| 
 | ||
| case "$arg1" in
 | ||
|     --version|version) rpm -qi "$SCRIPT_NAME" ; exit 0 ;;
 | ||
|     --help|help) wh_info ; exit 0 ;;
 | ||
|     --changelog|changelog) less "$CHANGELOG_FILE" ; exit 0 ;;
 | ||
|     killall) kill_wine ;;
 | ||
|     winecfg) prepair_wine ; wine_run "winecfg" ;;
 | ||
|     winereg|regedit) prepair_wine ; wine_run "regedit" ;;
 | ||
|     winefile|explorer) prepair_wine ; wine_run "winefile" ;;
 | ||
|     wineconsole|cmd) prepair_wine ; wine_run "wineconsole" ;;
 | ||
|     winetricks) prepair_wine ; "$WH_WINETRICKS" -q "$@" ;;
 | ||
|     desktop) create_desktop "$@" ; exit 0 ;;
 | ||
|     install|-i) run_autoinstall "$@" ;;
 | ||
|     installed) check_installed_programs "$1" ;;
 | ||
|     run|-r) run_installed_programs "$1" ;;
 | ||
|     backup-prefix) backup_prefix "$@" ;;
 | ||
|     restore-prefix) restore_prefix "$@" ;;
 | ||
|     remove-all) remove_winehelper "$@" ;;
 | ||
|     create-prefix) create_prefix "$@" ;;
 | ||
|     remove-prefix) remove_prefix "$@" ;;
 | ||
|     create-base-pfx) create_base_pfx "$@" ;;
 | ||
|     generate-db) generate_wine_metadata "$@" ;;
 | ||
|     init-prefix) prepair_wine ; wait_wineserver ;;
 | ||
|     *)
 | ||
|         if [[ -f "$arg1" ]] ; then
 | ||
|             WIN_FILE_EXEC="$(readlink -f "$arg1")"
 | ||
|             WIN_FILE_NAME="$(basename "$arg1")"
 | ||
|             case "${WIN_FILE_NAME,,}" in
 | ||
|                 *.exe) prepair_wine ; wine_run $WINE_WIN_START "$WIN_FILE_EXEC" "$@" ;;
 | ||
|                 *.msi) prepair_wine ; wine_run msiexec /i "$WIN_FILE_EXEC" "$@" ;;
 | ||
|                 *.bat|*.cmd) prepair_wine ; wine_run start "$WIN_FILE_EXEC" "$@" ;;
 | ||
|                 *) fatal "Тип файла не поддерживается." ;;
 | ||
|             esac
 | ||
|         else
 | ||
|             print_error "Команды $arg1 не существует."
 | ||
|             wh_info
 | ||
|             exit 1
 | ||
|         fi
 | ||
|     ;;
 | ||
| esac
 |