#!/usr/bin/env bash ##### CHECK ROOT ##### if [[ $(id -u) -eq 0 ]] ; then echo "Перезапустите скрипт $0 от обычного пользователя!" exit 1 fi ##### DEFAULT PATH ##### 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" else # переменные для тестового запуска WineHelper из репозитория USER_WORK_PATH="$HOME/test-$SCRIPT_NAME" RUN_SCRIPT="$(realpath "$0")" DATA_PATH="$(dirname "$RUN_SCRIPT")" # минимальная проверка синтаксиса скриптов for self_check_script in "$RUN_SCRIPT" \ "$DATA_PATH/dependencies.sh" "$DATA_PATH/autoinstall"/* \ "$DATA_PATH/manualinstall"/* "$DATA_PATH/database"/* do /usr/bin/env bash -n "$self_check_script" || exit 1 done fi ##### MESSAGES FUNCTIONS ##### 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" ;} print_var () { for vp in $@ ; do echo "${vp}=${!vp}" ; done ;} fatal () { print_error "$@" [[ -n "$WINESERVER" ]] && "$WINESERVER" -w exit 1 } ##### 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 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 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" ##### 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 () { print_info "Для продолжения установки введите root пароль..." su - -c "$@" } ##### CHECK DEPENDENCIES ##### # fonts-ttf-ms if ! rpm -q {i586-,}{wine,glibc-core,libstdc++6,glibc-pthread,glibc-nss,libOSMesa,\ 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 () { 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" ; outarg="-o" ;; esac create_new_dir "$2" if $command "$1" ${outarg}"$2" ; then print_ok "Файл $1 распакован." return 0 else fatal "Распаковать файл $1 не удалось!" fi } 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 } try_download () { local DOWNLOAD_FILE_URL="${1// /%20}" local OUTPUT_FILE="$2" local OUTPUT_FILE_NAME="$(basename "$OUTPUT_FILE")" 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 прошло успешно." if [[ "$3" == check256sum ]] ; then SHA256SUM_EXT=$(sha256sum "$OUTPUT_FILE" | awk '{print $1}') SHA256SUM_INT="$(grep "$OUTPUT_FILE_NAME" "$DATA_PATH/sha256sum.list" | awk '{print $1}')" if [[ "$SHA256SUM_EXT" == "$SHA256SUM_INT" ]] ; then print_ok "Хэш-сумма файла $OUTPUT_FILE_NAME успешно проверена." return 0 else fatal "Хэш-сумма файла $OUTPUT_FILE_NAME не совподает!" return 1 fi fi return 0 else fatal "Скачивание файла: $OUTPUT_FILE_NAME завершилось с ошибкой!" return 1 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 } create_desktop () { local name_desktop="$1" local exe_file="$2" local desktop_filename="$(basename "$exe_file" .exe | sed "s| |_|")" local icon_file 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" < "$WH_MENU_CONFIG" < Applications WineHelper WineHelper.directory WineHelper EOF fi # Обновляем кэш desktop файлов update-desktop-database "$HOME/.local/share/applications" if [[ $4 != "nocopy" ]] ; then if [[ -n $4 ]] ; then local desktop_path="$(xdg-user-dir DESKTOP)/$4" create_new_dir "$desktop_path" print_info "В меню создан $desktop_filename.desktop и скопирован на рабочий стол в каталог $4" else local desktop_path="$(xdg-user-dir DESKTOP)" print_info "В меню и на рабочем столе создан $desktop_filename.desktop" fi 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 [[ "$EXE_PATH" =~ ${2}$ ]] ; 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}')" printf "\E[36m%s $SCRIPT_NAME run $(basename "$EXE_PATH") - $(basename "$desktop_file") %s\e[0m\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 WINE_URL="$CLOUD_URL/$WH_WINE_USE.tar.xz" local WINE_PACKAGE="$WH_TMP_DIR/${WH_WINE_USE}.tar.xz" if try_download "$WINE_URL" "$WINE_PACKAGE" check256sum ; then if unpack "$WINE_PACKAGE" "$WH_DIST_DIR/" ; then echo "$WH_WINE_USE" > "$WH_DIST_DIR/$WH_WINE_USE/version" try_remove_file "$WINE_PACKAGE" fi fi fi 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 if [[ -n "${LD_LIBRARY_PATH}" ]] then export LD_LIBRARY_PATH="${WINEDIR}/lib:${LD_LIBRARY_PATH}" else export LD_LIBRARY_PATH="${WINEDIR}/lib" fi export WINEDLLPATH="${WINEDIR}/lib/wine" if [[ -d "${WINEDIR}/lib/gstreamer-1.0" ]] ; then export GST_PLUGIN_SYSTEM_PATH_1_0="${WINEDIR}/lib/gstreamer-1.0" fi [[ ! -f "${WINEDIR}/version" ]] && echo "${WH_WINE_USE}" > "${WINEDIR}/version" 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" ]] ; then export LD_LIBRARY_PATH+=":${WINEDIR}/lib64" export WINEDLLPATH+=":${WINEDIR}/lib64/wine" 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" if [[ ! -f "$WH_TMP_DIR/$CPCSP_PROXY_NAME" ]] ; then print_info "Загрузка $CPCSP_PROXY_NAME.tar.xz" if try_download "$CPCSP_PROXY_URL" "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" check256sum then unpack "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" "$WH_TMP_DIR" else try_remove_file "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" fi else unpack "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" "$WH_TMP_DIR" fi 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 ! rpm -q "wine" 1>/dev/null ; 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 FILE_NAME_PFX="$1" local PFX_URL="$CLOUD_URL/${FILE_NAME_PFX}.tar.xz" local PFX_TMP="$WH_TMP_DIR/pfx" create_new_dir "$PFX_TMP" if [[ ! -f "$PFX_TMP/$FILE_NAME_PFX.tar.xz" ]] ; then print_info "Загрузка базового префикса: ${FILE_NAME_PFX}" if try_download "$PFX_URL" "$PFX_TMP/$FILE_NAME_PFX.tar.xz" check256sum then unpack "$PFX_TMP/$FILE_NAME_PFX.tar.xz" "$WINEPREFIX/" else try_remove_file "$PFX_TMP/$FILE_NAME_PFX.tar.xz" fi else if ! unpack "$PFX_TMP/$FILE_NAME_PFX.tar.xz" "$WINEPREFIX/" ; then try_remove_file "${PFX_TMP}/${FILE_NAME_PFX}.tar.xz" get_base_pfx "$FILE_NAME_PFX" fi fi } check_prefix_var () { if [[ -z "$WINEPREFIX" ]] ; then print_warning "Префикс не выбран." local prefixes=() local count=1 print_info "Доступные префиксы WineHelper:" for prefix in "$WH_PREFIXES_DIR"/* ; do if [[ -d "$prefix" ]]; then prefixes+=("$prefix") echo "$count. $(basename "$prefix")" ((count++)) fi done [[ ${#prefixes[@]} -eq 0 ]] && fatal "Не найдено ни одного префикса!" local max_choice=${#prefixes[@]} read -p "Выберите префикс (1-$max_choice): " choice if [[ "$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" 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 "$DRIVE_C/users/xuser" ]] && [[ ! -d "$DRIVE_C/users/$USER" ]] then try_force_link_dir "$DRIVE_C/users/xuser" "$DRIVE_C/users/$USER" elif [[ ! -d "$DRIVE_C/users/xuser" ]] && [[ -d "$DRIVE_C/users/$USER" ]] then try_force_link_dir "$DRIVE_C/users/$USER" "$DRIVE_C/users/xuser" fi if [[ ! -f "$WINEPREFIX/.update-timestamp" ]] ; then print_info "Обновление префикса $WINEPREFIX." "$WINELOADER" wineboot -u wait_wineserver fi if [[ ! -L "$WINEPREFIX/dosdevices/h:" ]] then try_force_link_dir "$HOME" "$WINEPREFIX/dosdevices/h:" fi try_remove_file "$DRIVE_C/windows/system32/winemenubuilder.exe" # хак для XRDP сессии if [[ $DISPLAY == *:10.0 ]] ; then if ! lspci | grep -i nvidia > /dev/null ; then export MESA_GL_VERSION_OVERRIDE="3.3" export MESA_GLSL_VERSION_OVERRIDE="330" fi 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 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 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 () { if [[ -n "$INSTALL_SCRIPT_NAME" ]] \ && [[ "$FORCE_INSTALL" != "1" ]] then print_warning "Лицензионные соглашения использования сторонних компонентов: Некоторые компоненты, установленные в префикс и необходимые для запуска приложений, могут быть защищены авторским правом или лицензионными соглашениями. Вы обязаны самостоятельно убедиться в законности использования этих компонентов в вашей юрисдикции. Мы не несём ответственности за нарушение лицензионных соглашений, связанное с использованием подготовленного префикса. Продолжая использовать данное ПО вы подтверждаете, что ознакомились с данным отказом от ответственности и принимаете все риски, связанные с его использованием. " print_info "Для продолжения нажмите любую клавишу на клавиатуре." read -s -n 1 fi 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 () { 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 print_info "Список программ с возможностью автоматической установки:" for list in $(ls "$WH_AUTOINSTALL_DIR") ; do AI_INFO="$(grep "info_ru:" "$WH_AUTOINSTALL_DIR/$list" | awk -F"info_ru: " '{print $2}')" AI_PROGNAME="$(grep "PROG_NAME=" "$WH_AUTOINSTALL_DIR/$list" | awk -F"PROG_NAME=" '{print $2}')" echo -e "\n$list - $AI_PROGNAME" echo "$AI_INFO" done echo print_info "Список программ с возможностью установки из существующего дистрибутива:" for list in $(ls "$WH_MANUALINSTALL_DIR") ; do AI_INFO="$(grep "info_ru:" "$WH_MANUALINSTALL_DIR/$list" | awk -F"info_ru: " '{print $2}')" AI_PROGNAME="$(grep "PROG_NAME=" "$WH_MANUALINSTALL_DIR/$list" | awk -F"PROG_NAME=" '{print $2}')" echo -e "\n$list - $AI_PROGNAME" echo "$AI_INFO" done 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_error "Не указано имя префикса для удаления!" 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 "======================================================" read -p "Продолжить удаление? (y/N): " answer if [[ ! "$answer" =~ ^[YyДд]$ ]]; then print_info "Удаление префикса отменено." return 0 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 } remove_winehelper () { local answer if [[ $1 =~ --force|-y ]] ; then answer="y" else echo "======================================================" print_warning "Вы собираетесь удалить WineHelper и все связанные с ним данные." echo " Это удалит:" echo " - Все настройки WineHelper" echo " - Все приложения/программы, установленные через WineHelper" echo " - Все ярлыки из меню и с рабочего стола созданные с помощью WineHelper" echo "======================================================" echo "Продолжить? (y/N)" read -r answer if [[ "$answer" =~ ^[YyДд]$ ]] ; then # Второе подтверждение echo "----------------------------------------------" print_warning " ВЫ ТОЧНО УВЕРЕНЫ?" echo "----------------------------------------------" echo "Продолжить? (y/N)" read -r answer else print_info "Удаление отменено." return 1 fi fi if [[ "$answer" =~ ^[YyДд]$ ]] ; then # Удаление рабочих каталогов 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 и все связанные данные успешно удалены." else print_info "Удаление отменено." fi } backup_prefix() { export WINEPREFIX="$1" check_prefix_var local backup_base_dir="$(xdg-user-dir DESKTOP)" local backup_archive_name="backup_${PREFIX_NAME}_$(date +%Y.%m.%d).whpack" local backup_dest_path="$backup_base_dir/$backup_archive_name" local temp_backup_dir="$WH_TMP_DIR/backup_${PREFIX_NAME}_$(date +%Y.%m.%d)" local temp_prefix_dir="$temp_backup_dir/prefixes/$PREFIX_NAME" local 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 print_info "Создание squashfs архива..." if mksquashfs "$temp_backup_dir" "$backup_dest_path" -comp zstd ; then echo -e "\n# PREFIX_NAME=$PREFIX_NAME" >> "$backup_dest_path" 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" prefix_name="$(tail -n1 "$backup_archive_path" | sed -e 's/.*PREFIX_NAME=//')" if [[ -d "$WH_PREFIXES_DIR/$prefix_name" ]] ; then fatal "Префикс $prefix_name уже существует! Удалите его, если действительно желаете восстановить префикс заново. Команда для удаления: $SCRIPT_NAME remove-prefix $prefix_name" fi 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 [[ -d "$temp_extract_dir/prefixes" ]] ; then local prefix_name prefix_name="$(ls -1 "$temp_extract_dir/prefixes")" 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 line; do local name_desktop=$(echo "$line" | cut -d'=' -f1) local exe_path=$(echo "$line" | cut -d'=' -f2) local icon_name=$(echo "$line" | cut -d'=' -f3) 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 "Директория prefixes не найдена в архиве." 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 [программа.exe] запуск программы (отладка) remove-all удалить WineHelper и все связанные данные remove-prefix [имя_префикса] удалить префикс и все связанные данные backup-prefix [имя_префикса] создать резерную копию префикса restore-prefix \"путь/до/whpack\" восстановить префикс из резервной копии Параметры: --help показать эту справку и выйти --version показать информацию о пакете и его версии --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) rpm -qi "$SCRIPT_NAME" ; exit 0 ;; --help) wh_info ; 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 ; "$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 "$@" ;; remove-prefix) remove_prefix "$@" ;; *) 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