#!/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 THIRD_PARTY_FILE SCRIPT_NAME="$(basename "$0")" if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then # переменные для установленного WineHelper в систему WH_VERSION="$(rpm -q winehelper | awk -F'-' '{print $2}')" USER_WORK_PATH="$HOME/.local/share/$SCRIPT_NAME" RUN_SCRIPT="/usr/bin/$SCRIPT_NAME" DATA_PATH="/usr/share/$SCRIPT_NAME" WH_ICON_PATH="$DATA_PATH/image/gui/winehelper.svg" CHANGELOG_FILE="/usr/share/doc/winehelper-$WH_VERSION/CHANGELOG" LICENSE_FILE="/usr/share/doc/winehelper-$WH_VERSION/LICENSE" AGREEMENT="/usr/share/doc/winehelper-$WH_VERSION/LICENSE_AGREEMENT" THIRD_PARTY_FILE="/usr/share/doc/winehelper-$WH_VERSION/THIRD-PARTY" 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" THIRD_PARTY_FILE="$DATA_PATH/THIRD-PARTY" # минимальная проверка синтаксиса скриптов 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 DXVK_VER "none" # check_variables DXVK_CONFIG_FILE "path/to/dxvk.conf" check_variables VKD3D_VER "none" # 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 bash -c "$@" && 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 [[ $1 != "cloud" ]] ; then 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 else shift 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 } find_prefix () { # Автоматическое определение префикса, если он не задан if [[ -z "$WINEPREFIX" ]] && [[ "$1" == "$WH_PREFIXES_DIR"* ]]; then local extracted_prefix extracted_prefix="$(echo "$1" | grep -o ".*/prefixes/[^/]*")" if [[ -d "$extracted_prefix" ]]; then export WINEPREFIX="$extracted_prefix" print_info "Префикс автоматически определен: $(basename "$WINEPREFIX")" fi fi } create_desktop () { local name_desktop exe_file desktop_filename icon_file desktop_path icon_arg desktop_filename_arg name_desktop="$1" exe_file="$2" icon_arg="$3" desktop_filename_arg="$4" # Определяем имя desktop-файла if [[ -n "$desktop_filename_arg" ]] && [[ "$desktop_filename_arg" != "nocopy" ]]; then desktop_filename="$desktop_filename_arg" else desktop_filename="$(basename "$exe_file" .exe | sed "s| |_|g")" fi # Проверяем обязательные аргументы и наличие exe-файла if [[ -z "$name_desktop" ]] || [[ -z "$exe_file" ]] ; then fatal "Использование: $0 desktop \"Имя ярлыка\" \"/путь/к/файлу.exe\" [иконка|auto] [имя_desktop_файла]" elif [[ ! -f "$exe_file" ]] ; then print_warning "Для создания ярлыка не найден исполняемый файл: $exe_file" local BASENAME_EXE="$(basename "$exe_file")" print_info "Запускаем поиск $BASENAME_EXE" local FIND_PATH 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 find_prefix "$exe_file" # --- Логика обработки иконки --- local user_icons_dir="$WINEPREFIX/icons" create_new_dir "$user_icons_dir" # Случай 1: Восстановление из бэкапа (передан явный путь) if [[ "$RESTORE_FROM_BACKUP" == "1" ]] && [[ -f "$icon_arg" ]]; then icon_file="$icon_arg" # Случай 2: 'auto' или пустой аргумент - пытаемся извлечь из EXE elif [[ -z "$icon_arg" ]] || [[ "$icon_arg" == "auto" ]]; then print_info "Попытка извлечь иконку из $exe_file..." local png_name="$(basename "$exe_file" .exe).png" local extracted_icon_path="$user_icons_dir/$png_name" # Проверяем, существует ли иконка, чтобы избежать повторного извлечения if [[ -f "$extracted_icon_path" ]]; then print_info "Иконка уже существует: $extracted_icon_path" icon_file="$extracted_icon_path" else local tmp_ico_dir="$WH_TMP_DIR/icons_$$" # Используем PID для временного каталога create_new_dir "$tmp_ico_dir" local ico_name="$(basename "$exe_file" .exe).ico" if wrestool -x -t 14 "$exe_file" -o "$tmp_ico_dir/$ico_name" &>/dev/null && \ icotool -x -i 1 "$tmp_ico_dir/$ico_name" -o "$tmp_ico_dir/$png_name" &>/dev/null && \ try_copy_file "$tmp_ico_dir/$png_name" "$user_icons_dir/"; then icon_file="$extracted_icon_path" print_ok "Иконка успешно извлечена и сохранена: $icon_file" else print_warning "Не удалось извлечь иконку из $exe_file. Используется иконка по умолчанию." icon_file="wine" # Запасной вариант fi try_remove_dir "$tmp_ico_dir" fi # Случай 3: Передано конкретное имя иконки elif [[ -f "$WH_IMAGE_PATH/$icon_arg.png" ]]; then icon_file="$WH_IMAGE_PATH/$icon_arg.png" # Случай 4: Запасной вариант по умолчанию else print_info "Иконка '$icon_arg' не найдена. Используется иконка по умолчанию." icon_file="wine" 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" echo "StartupWMClass=$(basename "$exe_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 # Копируем финальную иконку в директорию иконок префикса, если ее там нет local final_icon_name if [[ -f "$icon_file" ]]; then final_icon_name=$(basename "$icon_file") if [[ ! -f "$user_icons_dir/$final_icon_name" ]]; then try_copy_file "$icon_file" "$user_icons_dir/" fi else final_icon_name="$icon_file" # например, "wine" fi echo "$name_desktop=${exe_file//$WINEPREFIX/}=${final_icon_name}" >> "$WINEPREFIX/desktop.list" fi # Создаем файл категории для меню create_new_dir "$HOME/.local/share/desktop-directories" if [[ ! -f "$WH_MENU_CATEGORY" ]] ; then cat > "$WH_MENU_CATEGORY" < "$WH_MENU_CONFIG" < Applications WineHelper WineHelper.directory WineHelper EOF fi # Обновляем кэш desktop-файлов update-desktop-database "$HOME/.local/share/applications" if [[ "$desktop_filename_arg" != "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 "PROG_URL=" -e "WINEPREFIX=" -e "INSTALL_DLL=" \ -e "PROG_NAME=" -e "PROG_ICON=" -e "var_" "$INSTALL_SCRIPT" \ | 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 } copy_wined3d () { for wined3dfiles in $1 ; do try_copy_wine_dll_to_pfx_64 "$wined3dfiles.dll" try_copy_wine_dll_to_pfx_32 "$wined3dfiles.dll" done } init_dxvk () { DXVK_VER="$1" if [[ $DXVK_VER == "none" ]] ; then copy_wined3d "d3d8 d3d9 d3d10_1 d3d10 d3d10core d3d11 dxgi" return 0 fi get_dxvk() { local DXVK_URL="$1" local DXVK_VAR_VER="$2" local DXVK_PACKAGE="${WH_VULKAN_LIBDIR}/${DXVK_VAR_VER}.tar.$(echo "${DXVK_URL#*.tar.}")" if try_download cloud "$DXVK_URL" "$DXVK_PACKAGE" check256sum \ && unpack "$DXVK_PACKAGE" "$WH_VULKAN_LIBDIR" then try_remove_file "$DXVK_PACKAGE" return 0 fi return 1 } if [[ ! -d "${WH_VULKAN_LIBDIR}/${DXVK_VER}" ]] ; then get_dxvk "$CLOUD_URL/${DXVK_VER}.tar.xz" "$DXVK_VER" fi if [[ $WH_USE_WINE_DXGI == "1" ]] ; then DXVK_FILES="d3d9 d3d10_1 d3d10 d3d11" # dxvk_config openvr_api_dxvk" copy_wined3d "dxgi" 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_VER}/x64/$dxvkfiles.dll" if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/${DXVK_VER}/x32/$dxvkfiles.dll" then var_winedlloverride_update "$dxvkfiles=n" fi done } init_vkd3d () { VKD3D_VER="$1" if [[ $VKD3D_VER == "none" ]] ; then copy_wined3d "d3d12 d3d12core" return 0 fi get_vkd3d() { local VKD3D_URL="$1" local VKD3D_VAR_VER="$2" local VKD3D_PACKAGE="${WH_VULKAN_LIBDIR}/${VKD3D_VAR_VER}.tar.$(echo "${VKD3D_URL#*.tar.}")" if try_download cloud "$VKD3D_URL" "$VKD3D_PACKAGE" check256sum \ && unpack "$VKD3D_PACKAGE" "$WH_VULKAN_LIBDIR" then try_remove_file "$VKD3D_PACKAGE" return 0 fi return 1 } if [[ ! -d "${WH_VULKAN_LIBDIR}/${VKD3D_VER}" ]] ; then get_vkd3d "$CLOUD_URL/${VKD3D_VER}.tar.xz" "$VKD3D_VER" fi 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_VER}/x64/$vkd3dfiles.dll" if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/${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 download_url="$CLOUD_URL/$WH_WINE_USE.tar.xz" wine_package="$WH_TMP_DIR/$WH_WINE_USE.tar.xz" try_download cloud "$download_url" "$wine_package" "check256sum" 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 cloud "$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 [[ -d "$XUSER_PATH" ]] \ && [[ ! -d "$DRIVE_C/users/$USER" ]] then try_force_link_dir "$XUSER_PATH" "$DRIVE_C/users/$USER" fi 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" # удаляем старые ассоциации, которых нет в новом списке sed -i '/@="xdg-open"/d' "$WINEPREFIX/system.reg" # добавляем ассоциации файлов для запуска с помощью 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 # удаление всех ассоциаций for old_ext in $old_xdg_open; do get_and_set_reg_file --delete "Software\Classes\.$old_ext" '@=' done 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 "Проверьте правильность установки КриптоПро в системе.\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 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" fi if check_prefix_var && [[ -f "$WINEPREFIX/last.conf" ]] ; then print_info "Найдены настройки из предыдущего использования префикса: $WINEPREFIX" cat "$WINEPREFIX/last.conf" . "$WINEPREFIX/last.conf" fi } prepair_wine () { if [[ -n "$INSTALL_SCRIPT_NAME" ]] then print_info "Используются настройки из скрипта установки: $INSTALL_SCRIPT_NAME" else init_database fi init_wine_ver init_wineprefix use_winetricks init_dxvk "$DXVK_VER" init_vkd3d "$VKD3D_VER" [[ "$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 } select_wine_version() { local sha256_file="$DATA_PATH/sha256sum.list" [[ ! -f "$sha256_file" ]] && fatal "Файл с версиями WINE/Proton не найден: $sha256_file" if [[ "$WINEARCH" == "win64" ]]; then print_info "Фильтруем версии для 64-битного префикса..." else # win32 print_info "Фильтруем версии для 32-битного префикса..." fi local options=() local total_versions_found=0 # --- System --- local system_wine_display_name="system" if command -v wine &>/dev/null; then local system_wine_version system_wine_version=$(wine --version 2>/dev/null) [[ -n "$system_wine_version" ]] && system_wine_display_name="$system_wine_version" fi options+=("--- System ---") options+=("$system_wine_display_name") # --- Other versions from sha256sum.list --- local current_group="" local group_versions=() flush_group() { if [[ ${#group_versions[@]} -gt 0 ]]; then IFS=$'\n' sorted_versions=($(sort -Vr <<<"${group_versions[*]}")) unset IFS options+=("${sorted_versions[@]}") ((total_versions_found+=${#group_versions[@]})) group_versions=() fi } while IFS= read -r line; do if [[ "$line" =~ ^#+[[:space:]]([^#[:space:]]+)[[:space:]]#* ]]; then flush_group current_group="${BASH_REMATCH[1]}" # Отображаем только группы, которые являются сборками WINE или PROTON case "$current_group" in WINE|WINE_LG|PROTON_LG|PROTON_STEAM) local pretty_key=$(echo "$current_group" | tr '_' ' ' | sed -e "s/\b\(.\)/\u\1/g") options+=("--- $pretty_key ---") ;; *) current_group="" ;; esac elif [[ -n "$current_group" ]] && [[ "$line" =~ [a-f0-9]{64} ]]; then local filename=$(echo "$line" | awk '{print $2}') local version_name=${filename%.tar.xz} if [[ "$WINEARCH" == "win64" ]]; then if [[ "$version_name" =~ (amd64|x86_64|wow64) ]] || ! [[ "$version_name" =~ i[3-6]86 ]]; then group_versions+=("$version_name") fi else # win32 group_versions+=("$version_name") fi fi done < "$sha256_file" flush_group 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 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= 0 && user_choice <= max_choice )); then if [[ "$user_choice" == "0" ]]; then print_info "Операция отменена." return 1 fi local selected_opt selected_opt="${selectable_options[$user_choice]}" if [[ "$selected_opt" == "$system_wine_display_name" ]]; then export WH_WINE_USE="system" else export WH_WINE_USE="$selected_opt" fi break else print_error "Неверный выбор. Введите число от 0 до $max_choice." fi done return 0 } 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 || exit 0 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 } update_last_conf_var() { local var_name="$1" local new_value="$2" local conf_file="$WINEPREFIX/last.conf" if [[ ! -f "$conf_file" ]] ; then print_warning "Файл last.conf не найден, не могу обновить переменную $var_name." return 1 fi if grep -q "export $var_name=" "$conf_file"; then sed -i "s|^export $var_name=.*|export $var_name=\"$new_value\"|" "$conf_file" else echo "export $var_name=\"$new_value\"" >> "$conf_file" fi } list_component_versions() { local component_group="$1" local sha256_file="$DATA_PATH/sha256sum.list" [[ ! -f "$sha256_file" ]] && fatal "Файл с версиями не найден: $sha256_file" print_info "Доступные версии для $component_group:" awk -v group="$component_group" ' /^#+\s*([^#]+?)\s*#*$/ { current_group = $0 gsub(/^#+\s*|\s*#*$/, "", current_group) } /^[a-f0-9]{64}/ { if (current_group == group) { filename = $2 sub(/\.tar\.xz$/, "", filename) print " - " filename } } ' "$sha256_file" | sort -Vr } select_component_version() { local component_group="$1" local sha256_file="$DATA_PATH/sha256sum.list" [[ ! -f "$sha256_file" ]] && fatal "Файл с версиями не найден: $sha256_file" local versions=() local current_group="" while IFS= read -r line; do if [[ "$line" =~ ^#+[[:space:]]([^#[:space:]]+)[[:space:]]#* ]] ; then current_group="${BASH_REMATCH[1]}" elif [[ "$current_group" == "$component_group" ]] && [[ "$line" =~ [a-f0-9]{64} ]] ; then local filename filename=$(echo "$line" | awk '{print $2}') local version_name=${filename%.tar.xz} versions+=("$version_name") fi done < "$sha256_file" IFS=$'\n' versions=($(sort -Vr <<<"${versions[*]}")) unset IFS if [[ ${#versions[@]} -eq 0 ]] ; then print_warning "Не найдено доступных версий для $component_group." >&2 return 1 fi print_info "Выберите версию $component_group для установки:" >&2 echo >&2 local items_to_print=(" 0) Отмена") for i in "${!versions[@]}" ; do items_to_print+=(" $((i+1))) ${versions[$i]}") done 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&2 done echo >&2 done local max_choice=${#versions[@]} local user_choice while true; do echo >&2 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 return 1 fi echo "${versions[$((user_choice-1))]}" return 0 else print_error "Неверный выбор. Введите число от 0 до $max_choice." >&2 fi done } run_install_to_prefix() { export WINEPREFIX="$1" local WIN_FILE_EXEC="$2" if [[ -z "$WINEPREFIX" ]] || [[ -z "$WIN_FILE_EXEC" ]]; then fatal "Использование: $SCRIPT_NAME install-to-prefix <имя_префикса> <путь_к_установщику>" fi check_prefix_var prepair_wine wine_run_install "$WIN_FILE_EXEC" } run_install_dxvk() { local version="$1" if [[ -z "$version" ]] ; then version=$(select_component_version "DXVK") [[ $? -ne 0 ]] && print_info "Установка DXVK отменена." && return elif [[ "$version" == "list" ]]; then list_component_versions "DXVK" return fi check_prefix_var init_database export DXVK_VER="$version" init_wine_ver init_wineprefix if [[ "$DXVK_VER" == "none" ]] then print_info "Удаление DXVK..." else print_info "Установка DXVK: $DXVK_VER" fi init_dxvk "$DXVK_VER" wait_wineserver } run_install_vkd3d() { local version="$1" if [[ -z "$version" ]] ; then version=$(select_component_version "VKD3D") [[ $? -ne 0 ]] && print_info "Установка VKD3D отменена." && return elif [[ "$version" == "list" ]] ; then list_component_versions "VKD3D" return fi check_prefix_var init_database export VKD3D_VER="$version" init_wine_ver init_wineprefix if [[ "$VKD3D_VER" == "none" ]] then print_info "Удаление VKD3D..." else print_info "Установка VKD3D: $VKD3D_VER" fi init_vkd3d "$VKD3D_VER" wait_wineserver } run_change_wine_version() { local new_version="$1" check_prefix_var init_database if [[ -z "$new_version" ]]; then select_wine_version || exit 0 new_version="$WH_WINE_USE" else export WH_WINE_USE="$new_version" fi init_wine_ver init_wineprefix wait_wineserver print_ok "Версия Wine для префикса $PREFIX_NAME успешно изменена на $WH_WINE_USE." } wh_info () { echo "Использование: $SCRIPT_NAME [команда] Команды: install list список возможных установочных скриптов install [скрипт] запустить скрипт установки программы install [скрипт] --clear-pfx не использовать готовый префикс для установки ПО install-dxvk [версия|none|list] установить, удалить или показать версии DXVK install-vkd3d [версия|none|list] установить, удалить или показать версии VKD3D change-wine [версия] изменить версию Wine/Proton для текущего префикса 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 # отключаем создание .desktop файлов средствами wine # и отключаем winebth, так как может сломать winedevice.exe var_winedlloverride_update "winemenubuilder.exe,winebth.sys=d" 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 "$@" ;; install-to-prefix) run_install_to_prefix "$@" ;; install-dxvk) run_install_dxvk "$@" ;; install-vkd3d) run_install_vkd3d "$@" ;; change-wine) run_change_wine_version "$@" ;; 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 "$@" ;; init-prefix) prepair_wine ; wait_wineserver ;; *) if [[ -f "$arg1" ]] ; then WIN_FILE_EXEC="$(readlink -f "$arg1")" WIN_FILE_NAME="$(basename "$WIN_FILE_EXEC")" find_prefix "$WIN_FILE_EXEC" 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