#!/usr/bin/env bash ##### CHECK ROOT ##### if [[ $(id -u) -eq 0 ]] ; then echo "Перезапустите скрипт $0 от обычного пользователя!" exit 1 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 "$@" ; exit 1 ;} ##### CHECK VARIABLES ##### check_variables () { [[ -z ${!1} ]] && export $1="$2" ;} ##### CHECK DEBUG ##### if [[ "$1" == "--debug" ]] ; then export DEBUG="full" export LOG_FILE="$HOME/winehelper.log" shift print_warning "Включен режим подробного логирования работы WINE." print_warning "Лог будет сохранен по пути: $LOG_FILE" sleep 3 else check_variables DEBUG "0" fi ##### DEFAULT VARIABLES ##### SCRIPT_NAME="$(basename "$0")" if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then USER_WORK_PATH="$HOME/.local/share/$SCRIPT_NAME" RUN_SCRIPT="/usr/bin/$SCRIPT_NAME" DATA_PATH="/usr/share/$SCRIPT_NAME" else USER_WORK_PATH="$HOME/test-$SCRIPT_NAME" RUN_SCRIPT="$(realpath "$0")" DATA_PATH="$(dirname "$RUN_SCRIPT")" fi 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_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 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 grep "/home" /etc/fstab | grep -q "noexec" ; then fatal "/home примонтирован в /etc/fstab с аргументом noexec.\nЗапуск портативной версии wine не возможен из домашнего каталога." fi ##### ROOT ##### su_run () { print_info "Для продолжения установки зависимостей введите root пароль..." su - -c "$@" } ##### CHECK DEPENDENCIES ##### # yad zenity cups-pdf 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} \ winetricks ca-certificates cups-pdf 1>/dev/null then : # Зависимости установлены. Пропускаем... else if su_run "$DATA_PATH/dependencies.sh" then print_info "Зависимости успешно установлены. Продолжаем работу $SCRIPT_NAME" else fatal "Не удалось установить зависимости. Работа $SCRIPT_NAME прервана." fi fi ##### HELPER FUNCTIONS ##### check_command () { if command -v "$1" &>/dev/null ; then return 0 else print_warning "command: $1 - not found!" return 1 fi } 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 "file $1 not found for copy" && return 1 elif [[ -z "$2" ]] ; then print_error "no way to copy file $1" && return 1 elif [[ -L "$2" ]] ; then print_warning "$2 is a file with a symbolic link" 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 "directory $1 not found for copy" elif [[ -z "$2" ]] ; then print_error "no way to copy directory $1" else cp -fr "$1" "$2" [[ "$?" != 0 ]] && print_error "failed to copy directory $1 to $2" || return 0 fi return 1 } try_force_link_file () { if [[ ! -f "$1" ]] ; then print_warning "file not found for link: $1" if [[ -f "$2" ]] ; then try_remove_file "$2" print_warning "removed old link: $2" fi return 1 elif [[ -z "$2" ]] ; then print_error "no way to link file $1" && return 1 else try_remove_file "$2" ln -s -f -r "$1" "$2" return 0 fi return 1 } check_symlink () { local CHK_SYMLINK_FILE="$(file "$1")" if [[ -n "$(echo "$CHK_SYMLINK_FILE" | grep -v "broken" | grep "symbolic link to" | awk '{print $1}')" ]] ; then return 0 elif [[ -n "$(echo "$CHK_SYMLINK_FILE" | grep "broken symbolic link to" | awk '{print $1}')" ]] ; then print_error "remove broken symlink: $CHK_SYMLINK_FILE" rm -fr "$CHK_SYMLINK_FILE" return 1 else return 1 fi } try_force_link_dir () { if [[ ! -d "$1" ]] ; then print_info "directory $1 not found for link" elif [[ -z "$2" ]] ; then print_error "no way to link directory $1" else ln -s -f -r "$1" "$2" [[ "$?" != 0 ]] && print_error "failed to link directory $1 to $2" || return 0 fi return 1 } create_new_dir () { if [[ ! -d "$1" ]] ; then mkdir -p "$1" fi return 0 } unpack () { print_info "Запуск распаковки архива $1" case $1 in *.tar.xz) local command="tar -Jxhf";; *.tar.gz) local command="tar -xhzf" ;; *.tar.zst) local command="tar -I zstd -xhf" ;; *.tar) local command="tar -xhf" ;; esac create_new_dir "$2" if $command "$1" -C "$2" ; then print_ok "Архив $1 распакован." return 0 else print_error "Распаковать архив $1 не удалось!" return 1 fi } try_download () { local DOWNLOAD_FILE_URL="$1" 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 } ##### OVERRIDE VARIABLES FUNCTIONS ##### var_winedlloverride_update () { if [[ -n "${WINEDLLOVERRIDES}" ]] then export WINEDLLOVERRIDES="${1};${WINEDLLOVERRIDES}" else export WINEDLLOVERRIDES="${1}" fi } var_vkd3d_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 FILE ##### create_desktop () { local name_desktop="$1" local exe_file="$2" local desktop_filename="$(basename "$exe_file" .exe | sed "s| |_|")" local icon_file="$WH_IMAGE_PATH/$3.png" 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 create_new_dir "$WH_MENU_DIR" [[ ! -f "$icon_file" ]] && icon_file=wine # Создаем .desktop файл { 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/" # Создаем файл категории для меню 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 # Обновляем меню if check_command update-menus ; then update-menus fi fi # Обновляем кэш desktop файлов if check_command update-desktop-database ; then update-desktop-database "$HOME/.local/share/applications" fi 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 FILE ##### 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" # Обновляем меню if check_command update-menus ; then update-menus fi fi # Обновляем кэш desktop файлов if check_command update-desktop-database ; then update-desktop-database "$HOME/.local/share/applications" fi fi } ##### INSTALLED PROGRAMS ##### 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 } ##### USED WINED3D LIBRARY ##### 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 } ##### CHECK VULKAN LIBRARY ##### 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 } ##### DOWNLOAD WINE ##### download_wine () { if [[ ! -d "$WH_DIST_DIR/$WH_WINE_USE" ]] ; 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 } ##### INITIAL WINE SETTINGS ##### init_wine_ver () { if [[ "$WH_WINE_USE" != system* ]] ; then export WINEDIR="$WH_DIST_DIR/$WH_WINE_USE" if [[ ! -d "$WINEDIR" ]] ; then download_wine 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 if [[ "${WINEARCH}" == "win64" ]] ; 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 [[ ! -f "${WINEDIR}/version" ]] && echo "${WH_WINE_USE}" > "${WINEDIR}/version" if [[ ! -d "${WINEDIR}/lib64/" ]] && [[ -d "${WINEDIR}/lib/wine/x86_64-unix" ]] ; then create_new_dir "${WINEDIR}/lib64/wine" mv -f "${WINEDIR}/lib/wine/x86_64-unix" "${WINEDIR}/lib64/wine/" mv -f "${WINEDIR}/lib/wine/x86_64-windows" "${WINEDIR}/lib64/wine/" fi else # use system WINE if ! command -v "wine" &>/dev/null ; then fatal "system WINE - not found." fi export WINEDIR="/usr" export WINELOADER="wine" export WINESERVER="wineserver" fi 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" if [[ $DEBUG == "0" ]] ; then check_variables WINEDEBUG "-all" elif [[ $DEBUG == "full" ]] ; 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" else check_variables WINEDEBUG "err+all" fi print_info "Используется версия wine: $WH_WINE_USE" } ##### INITIAL WINEPREFIX SETTINGS ##### 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) name_for_find="\"$name_for_find\"=dword:" name_for_set=$(convert_dec_and_hex --dec "$name_for_set") ;; REG_SZ) name_for_find="\"$name_for_find\"=" name_for_set="\"$name_for_set\"" ;; *) if [[ $name_add_or_del == --delete ]] ; then name_for_find="\"$name_for_find\"" 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 в файле реестра" sed -i "${find_number_line}s|$name_for_find.*|$name_for_find$name_for_set|" "$find_file" else print_info "Добавляем $name_for_find_old в файл реестра" 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 из файла реестра" sed -i "${find_number_line}d" "$find_file" fi } wait_wineserver () { # while [[ -n "$(ls -l /proc/*/exe 2>/dev/null | grep -ie portproton | grep -E 'wine(64)?-preloader|wineserver' | awk -F/ '{print $3}')" ]] ; do # sleep 1 # done "$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 } init_wineprefix () { if [[ -z "$WINEPREFIX" ]] ; then print_warning "Префикс не выбран, используйте пееременную: WINEPREFIX=имя_префикса" print_info "Список существующих префиксов:" ls -1 "$WH_PREFIXES_DIR" echo exit 1 else if echo "$WINEPREFIX" | grep -v '/' ; then export WINEPREFIX="$WH_PREFIXES_DIR/$WINEPREFIX" fi fi 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 env WINEDLLOVERRIDES="winegstreamer=" "$WINELOADER" wineboot -u else env WINEDLLOVERRIDES="winegstreamer=" "$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 env WINEDLLOVERRIDES="winegstreamer=" "$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 [[ ! -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 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 } ##### KILL AUTOSTART AFTER INSTALLING ##### 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 } update_winetricks () { W_TRX_URL="https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks" W_TRX_EXT_VER="$(curl -s --list-only "$W_TRX_URL" | grep -i 'WINETRICKS_VERSION=' | sed 's/WINETRICKS_VERSION=//')" print_info "Актуальная версия winetricks: $W_TRX_EXT_VER" if [[ -f "$WH_TMP_DIR/winetricks" ]] ; then W_TRX_INT_VER="$(cat "$WH_TMP_DIR/winetricks" | grep -i 'WINETRICKS_VERSION=' | sed 's/WINETRICKS_VERSION=//')" print_info "Установленная портативная версия winetricks: $W_TRX_INT_VER" fi if [[ -n "$W_TRX_EXT_VER" ]] && [[ "$W_TRX_INT_VER" != "$W_TRX_EXT_VER" ]] ; then if try_download "$W_TRX_URL" "$WH_TMP_DIR/winetricks_new" ; then mv -f "$WH_TMP_DIR/winetricks_new" "$WH_TMP_DIR/winetricks" W_TRX_INT_VER="$(cat "$WH_TMP_DIR/winetricks" | grep -i 'WINETRICKS_VERSION=' | sed 's/_VERSION=//')" print_info "Портативная версия winetricks обновлена до (${W_TRX_INT_VER})" chmod u+x "$WH_TMP_DIR/winetricks" fi fi export WINETRICKS="$WH_TMP_DIR/winetricks" } 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 [[ "$need_install_dll_to_pfx" == vcrun201[5-9] ]] ; then sed -i "/${need_install_dll_to_pfx}/d" "$WINEPREFIX/winetricks.log" need_install_dll_to_pfx="vcrun2022" fi 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 if update_winetricks ; then print_info "Пробуем установить библиотеки: ${WH_DLL_NEED_INSTALL}" print_info "Запускаем WINETRICKS..." export WINETRICKS_DOWNLOADER="curl" env WINEDLLOVERRIDES="winegstreamer=" "$WH_TMP_DIR/winetricks" -q ${WH_DLL_NEED_INSTALL} wait_wineserver fi 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 () { 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' WH_FIND_DB_FILE="$(grep -ilw "#$WHDB.exe" "$WH_DB_DIR"/* )" if [[ $(echo "$WH_FIND_DB_FILE" | wc -l) -gt 1 ]] ; then print_error "Найдено более одного файла настроек: $WHDB.exe" WHDB_FILE="$(echo "$WH_FIND_DB_FILE" | head -n1)" else WHDB_FILE="$WH_FIND_DB_FILE" fi IFS="$orig_IFS" fi if [[ "$WHDB_FILE" ]] ; then print_info "Используется файл настроек: $WHDB_FILE" . "$WHDB_FILE" else print_warning "Файл настроек не найден. Пропускаем." fi 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" init_database 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 [[ $DEBUG == "0" ]] ; then $MANGOHUD_RUN "$WINELOADER" "$@" $LAUNCH_PARAMETERS else print_var WH_WINE_USE WINEARCH WINEPREFIX WINEDLLOVERRIDES WH_WINDOWS_VER | tee "$LOG_FILE" $MANGOHUD_RUN "$WINELOADER" "$@" $LAUNCH_PARAMETERS 2>&1 | tee -a "$LOG_FILE" fi wait_wineserver } wine_run_install () { print_info "Запускаем установку приложения $PROG_NAME." 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" 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 } wh_info () { echo "Использование: $SCRIPT_NAME [команда] Команды: install list список возможных установочных скриптов install [скрипт] запустить скрипт установки программы install [скрипт] --clear-pfx не использовать подготовленный префикс для установки ПО installed список установленных программ run [программа.exe] запуск программы (отладка) Параметры: --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 case "${1,,}" 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) shift ; update_winetricks ; prepair_wine ; "$WINETRICKS" -q "$@" ;; desktop) shift ; create_desktop "$@" ; exit 0 ;; install|-i) shift ; run_autoinstall "$@" ;; installed) shift ; check_installed_programs "$1" ;; run|-r) shift ; run_installed_programs "$1" ;; *) if [[ -f "$1" ]] ; then WIN_FILE_EXEC="$(readlink -f "$1")" WIN_FILE_NAME="$(basename "$WIN_FILE_EXEC")" shift 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 wh_info exit 1 fi ;; esac