#!/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 WH_ICON_TRAY GENERAL WH_WINETRICKS

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="/usr/share/icons/hicolor/scalable/apps/winehelper.svg"
    WH_ICON_TRAY="/usr/share/icons/hicolor/symbolic/apps/winehelper-symbolic.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"
    GENERAL="/usr/share/doc/winehelper-$WH_VERSION/GENERAL"
    WH_WINETRICKS="/usr/bin/winetricks"
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"
    WH_ICON_TRAY="$DATA_PATH/image/gui/winehelper-symbolic.svg"
    LICENSE_FILE="$DATA_PATH/LICENSE"
    AGREEMENT="$DATA_PATH/LICENSE_AGREEMENT"
    THIRD_PARTY_FILE="$DATA_PATH/THIRD-PARTY"
    GENERAL="$DATA_PATH/GENERAL"
    WH_WINETRICKS="/usr/bin/winetricks"
    WH_DEVEL="1"

    # минимальная проверка синтаксиса скриптов
    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

export WH_DIST_DIR="$USER_WORK_PATH/dist"

##### 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 "$@ Аварийное завершение работы WineHelper!"
    [[ -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 NOEXEC FOR /HOME #####
if mount -l | grep -E "[[:space:]]/home[[:space:]]" | grep -q "noexec" ; then
    export WH_NOEXEC_HOME="1"
else
    export WH_NOEXEC_HOME="0"
fi

if [[ $1 == "gui" ]] ; then
    export WH_USE_GUI="1"
    /usr/bin/env python3 "$DATA_PATH/winehelper_gui.py" &
    exit 0
fi

##### CHECK VARIABLES #####
check_variables () { [[ -z ${!1} ]] && export $1="$2" ;}

##### CHECK DEBUG #####
if [[ "$1" == "--debug" ]] ; then
    check_variables WINEDEBUG "+loaddll"
    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

##### DEFAULT VARIABLES #####
WH_VULKAN_LIBDIR="$USER_WORK_PATH/vulkan"
WH_PREFIXES_DIR="$USER_WORK_PATH/prefixes"
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_MENU_DIR="$HOME/.local/share/applications/WineHelper"

# TODO: system menu directory
# /usr/share/desktop-directories/WineHelper.directory
# /etc/xdg/menus/applications-merged/WineHelper.menu

# user menu directory
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-10.18.1-alt1-wow64" # or system

check_variables WH_USE_CPCSP_PROXY "0"
check_variables CPCSP_PROXY_X86_64_VER "0.6.1-alt1"
check_variables CPCSP_PROXY_WOW64_VER "0.7.7-alt1-wow64"

check_variables WH_USE_EXTRA_FONTS "0"
check_variables EXTRA_FONTS_VER "01"
check_variables WH_FONT_MSS_REPLACE "0"
check_variables WH_FONT_SMOOTHING "0"
# check_variables FREETYPE_PROPERTIES "truetype:interpreter-version=35"

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_MESA_GL_OVERRIDE "0"
check_variables WH_USE_WINE_DXGI "0"
check_variables WH_DLL_INSTALL ""
check_variables WH_MAIN_DECORATED "default"
check_variables WH_MC_DECORATED "default"
check_variables WH_VIRTUAL_DESKTOP "default"

check_variables WINE_WIN_START "start /wait /high /unix"

check_variables WINE_CPU_TOPOLOGY "0"

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

##### 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 #####
check_deps_i586 () {
    if [[ $WH_USE_GUI != "1" ]] && [[ ! -t 0 ]] ; then
        print_warning "Скрипт запущен из desktop файла."
        return 0
    fi

    if [[ $WH_SKIP_DEPS == "1" ]] ; then
        unset WH_SKIP_DEPS
        return 0
    fi

    if ! rpm -q i586-{wine,glibc-core,libstdc++6,glibc-pthread,glibc-nss,\
libnm,libnss,libnss-mdns,libnsl1,libunwind,libunixODBC2,ocl-icd,libfreetype,\
libcups,libfontconfig1,libgnutls30,libGL,libEGL,libvulkan1,xorg-dri-swrast,\
xorg-dri-intel,xorg-dri-radeon} 1>/dev/null
    then
        print_warning "Необходимо установить 32-битные зависимости!"
        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"
    try_remove_file "$OUT_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"
        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"
    elif [[ -d "$WINEDIR/lib64/wine/i386-windows" ]] ; then
        WINE_BUILD_DLL_32="$WINEDIR/lib64/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
}

tmp_winedlloverride_update () {
    var_winedlloverride_update "$1"
}

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
}

check_wayland_session () {
    [[ $XDG_SESSION_TYPE =~ "wayland" ]] && return 0 || return 1
}

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
        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
            print_error "Для создания ярлыка не найден исполняемый файл: $BASENAME_EXE"
            return 1
        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="$WH_IMAGE_PATH/wh_default.png" # Запасной вариант
            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="$WH_IMAGE_PATH/wh_default.png"
    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" <<EOF
[Desktop Entry]
Type=Directory
Name=WineHelper
Icon=winehelper
EOF
    fi

    # Создаем файл меню для всех приложений WineHelper
    create_new_dir "$HOME/.config/menus/applications-merged"
    if [[ ! -f "$WH_MENU_CONFIG" ]] ; then
    cat > "$WH_MENU_CONFIG" <<EOF
<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
  "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
<Menu>
  <Name>Applications</Name>
  <Menu>
    <Name>WineHelper</Name>
    <Directory>WineHelper.directory</Directory>
    <Include>
      <Category>WineHelper</Category>
    </Include>
  </Menu>
</Menu>
EOF
    fi

    # Обновляем кэш desktop-файлов
    update-desktop-database "$HOME/.local/share/applications"

    if [[ "$desktop_filename_arg" != "nocopy" ]] \
    && [[ "$WH_NOEXEC_HOME" != "1" ]]
    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_" -e "WH_MAIN_DECORATED" \
             -e "WH_VIRTUAL_DESKTOP" -e "WINE_TOP_WINDOW" -e "WH_FONT_SMOOTHING "\
             -e "WH_FONT_MSS_REPLACE" -e "FREETYPE_PROPERTIES" -e "WINE_D3D_CONFIG" \
             -e "WH_USE_MESA_GL_OVERRIDE" -e "WINEDLLOVERRIDES" \
             "$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"
}

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
}

# Подготовка Wine для работы с noexec на /home
# ELF бинарники копируются во временный каталог
# Windows DLL остаются в /home через симлинки
prepare_wine_noexec() {
    print_warning "/home примонтирован с noexec."

    local wine_name="$1"
    local wine_home_dir="$WH_DIST_DIR/$wine_name"
    local dist_tmp_dir="/var/tmp/winehelper-$USER/dist"
    local wine_tmp_dir="$dist_tmp_dir/$wine_name"

    export WH_DIST_DIR="$dist_tmp_dir"
    export WINEDIR="$wine_tmp_dir"

    update_cpscp_proxy () {
        if [[ $UPDATE_CPCSP_PROXY == "1" ]] ; then
            print_info "Обновляем cpcsp_proxy во временном каталоге wine..."
            if [[ $WH_WINE_WOW64 == "1" ]] ; then
                cp -fv "$wine_home_dir/lib64/wine/x86_64-unix/cpcsp_proxy.so" "$wine_tmp_dir/lib64/wine/x86_64-unix/"
                cp -fv "$wine_home_dir/lib64/wine/x86_64-windows/cpcsp_proxy.dll" "$wine_tmp_dir/lib64/"
                cp -fv "$wine_home_dir/lib64/wine/i386-windows/cpcsp_proxy.dll" "$wine_tmp_dir/lib/"

                local i386_proxy_dll="$wine_tmp_dir/lib64/wine/i386-windows/cpcsp_proxy.dll"
                if [[ ! -L "$i386_proxy_dll" ]] ; then
                    try_remove_file "$i386_proxy_dll"
                    try_force_link_file "$wine_tmp_dir/lib/cpcsp_proxy.dll" "$i386_proxy_dll"
                fi

                local x86_64_proxy_dll="$wine_tmp_dir/lib64/wine/x86_64-windows/cpcsp_proxy.dll"
                if [[ ! -L "$x86_64_proxy_dll" ]] ; then
                    try_remove_file "$x86_64_proxy_dll"
                    try_force_link_file "$wine_tmp_dir/lib64/cpcsp_proxy.dll" "$x86_64_proxy_dll"
                fi
            fi
        fi
    }

    if [[ -f "$wine_tmp_dir/bin/wine" ]] ; then
        update_cpscp_proxy
        print_ok "Wine готов для работы с noexec. Пропускаем настройку..."
        return 0
    fi
    print_info "Частичное копирование Wine во временный каталог..."

    create_new_dir "$wine_tmp_dir/bin"
    cp -a "$wine_home_dir/bin/"* "$wine_tmp_dir/bin/"

    create_new_dir "$wine_tmp_dir/lib/wine"
    if [[ -f "$wine_home_dir/lib/wine/wine" ]] \
    || [[ -f "$wine_home_dir/lib/wine/wine64" ]]
    then
        cp -a "$wine_home_dir/lib/wine"/wine* "$wine_tmp_dir/lib/wine/"
    fi

    for unix_dir in i386-unix x86_64-unix; do
        if [[ -d "$wine_home_dir/lib/wine/$unix_dir" ]] ; then
            cp -a "$wine_home_dir/lib/wine/$unix_dir" "$wine_tmp_dir/lib/wine/"
        fi
    done

    for win_dir in i386-windows x86_64-windows; do
        if [[ -d "$wine_home_dir/lib/wine/$win_dir" ]] ; then
            try_force_link_dir "$wine_home_dir/lib/wine/$win_dir" "$wine_tmp_dir/lib/wine/$win_dir"
        fi
    done

    if [[ -d "$wine_home_dir/lib/gstreamer-1.0" ]] ; then
        cp -a "$wine_home_dir/lib/gstreamer-1.0" "$wine_tmp_dir/lib/"
    fi

    if [[ -L "$wine_home_dir/lib64/wine" ]] ; then
        create_new_dir "$wine_tmp_dir/lib64/"
        try_force_link_dir "$wine_tmp_dir/lib/wine/" "$wine_tmp_dir/lib64/"
    elif [[ -d "$wine_home_dir/lib64" ]]; then
        create_new_dir "$wine_tmp_dir/lib64/wine"
        for unix_dir in i386-unix x86_64-unix; do
            if [[ -d "$wine_home_dir/lib64/wine/$unix_dir" ]] ; then
                cp -a "$wine_home_dir/lib64/wine/$unix_dir" "$wine_tmp_dir/lib64/wine/"
            fi
        done
        for win_dir in i386-windows x86_64-windows; do
            if [[ -d "$wine_home_dir/lib64/wine/$win_dir" ]] ; then
                try_force_link_dir "$wine_home_dir/lib64/wine/$win_dir" "$wine_tmp_dir/lib64/wine/$win_dir"
            fi
        done
        if [[ -d "$wine_home_dir/lib64/gstreamer-1.0" ]] ; then
            cp -a "$wine_home_dir/lib64/gstreamer-1.0" "$wine_tmp_dir/lib64/"
        fi
    fi

    update_cpscp_proxy
    try_force_link_file "$wine_home_dir/cpcsp_proxy.ver" "$wine_tmp_dir/cpcsp_proxy.ver"

    try_force_link_dir "$wine_home_dir/share" "$wine_tmp_dir/share"
    try_copy_file "$wine_home_dir/version" "$wine_tmp_dir/"

    print_ok "Wine подготовлен для работы с noexec"
}

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"
        fi

        if [[ ${WH_WINE_USE,,} =~ -cx- ]] ; then
            export WINEUSERNAME="crossover"
        fi

        # Управление структурой подкаталога Proton "files", перемещая содержимое вверх
        if [[ ${WH_WINE_USE,,} =~ proton ]] \
        && [[ -d "$WINEDIR/files" ]]
        then
            print_info "Обнаружена структура каталогов Proton, исправляем пути..."
            mv "$WINEDIR"/files/* "$WINEDIR/"
            try_remove_dir "$WINEDIR/files"
        fi

        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 [[ ! -f "$WINEDIR/version" ]] ; then
            echo "$WH_WINE_USE" > "$WINEDIR/version"
        fi

        if [[ $WH_WINE_USE =~ wow64 ]] ; then
            export WH_WINE_WOW64="1"
        else
            export WH_WINE_WOW64="0"
            check_deps_i586
        fi

        if [[ $WH_USE_CPCSP_PROXY == "1" ]] ; then
            unset UPDATE_CPCSP_PROXY
            local cpcsp_proxy_ver
            if [[ $WH_WINE_WOW64 == "1" ]]
            then cpcsp_proxy_ver="$CPCSP_PROXY_WOW64_VER"
            else cpcsp_proxy_ver="$CPCSP_PROXY_X86_64_VER"
            fi
            if [[ ! -f "$WINEDIR/cpcsp_proxy.ver" ]] \
            || ! grep -q "$cpcsp_proxy_ver" "$WINEDIR/cpcsp_proxy.ver"
            then
                export UPDATE_CPCSP_PROXY="1"
                local cpcsp_proxy_name="wine-cpcsp_proxy-$cpcsp_proxy_ver"
                local 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"

                if [[ $WH_WINE_WOW64 == "1" ]] ; then
                    for dir_name in x86_64-unix i386-windows x86_64-windows ; do
                        if [[ -f "$WINEDIR/lib64/wine/$dir_name/cpcsp_proxy.dll" ]] ; then
                            try_remove_file "$WINEDIR/lib64/wine/$dir_name/cpcsp_proxy.dll"
                        fi
                        cp -f "$WH_TMP_DIR/$cpcsp_proxy_name/$dir_name"/* "$WINEDIR/lib64/wine/$dir_name/"
                    done
                else
                    for dir_name in i386-unix i386-windows ; do
                        cp -f "$WH_TMP_DIR/$cpcsp_proxy_name/$dir_name"/* "$WINEDIR/lib/wine/$dir_name/"
                    done
                    if [[ -d "$WINEDIR/lib64" ]] ; then
                        for dir_name in x86_64-unix x86_64-windows ; do
                            cp -f "$WH_TMP_DIR/$cpcsp_proxy_name/$dir_name"/* "$WINEDIR/lib64/wine/$dir_name/"
                        done
                    fi
                fi
                try_remove_dir "$WH_TMP_DIR/$cpcsp_proxy_name"
                echo "$cpcsp_proxy_ver" > "$WINEDIR/cpcsp_proxy.ver"
            fi
        fi

        # При noexec на /home: готовим временную копию ELF файлов wine перед запуском
        if [[ "$WH_NOEXEC_HOME" == "1" ]] ; then
            prepare_wine_noexec "$WH_WINE_USE"
        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

        var_ld_library_path_update "$WINEDIR/lib"
        export WINEDLLPATH="$WINEDIR/lib/wine"

        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
    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

    check_variables BASE_PFX "none"

    export DRIVE_C="$WINEPREFIX/drive_c"
    export XUSER_PATH="$DRIVE_C/users/$WINEUSERNAME"

    if [[ ! -d "$WINEPREFIX" ]] ; 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
    fi

    if [[ ! -d "$WINEPREFIX/drive_c/windows" ]] ; then
        print_info "Создание префикса $WINEPREFIX."
        "$WINELOADER" wineboot -i
        wait_wineserver
    elif [[ ! -f "$WINEPREFIX/.update-timestamp" ]] \
      || [[ ! -d "$WINEPREFIX/drive_c/users" ]]
    then
        print_info "Обновление префикса $WINEPREFIX."
        "$WINELOADER" wineboot -u
        wait_wineserver
    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 [[ -L "$XUSER_PATH/Desktop" ]]
    then rm -f "$XUSER_PATH/Desktop"
    fi

    create_new_dir "$XUSER_PATH/Desktop"
    create_new_dir "$XUSER_PATH/AppData/Local/Temp"
    create_new_dir "$DRIVE_C/ProgramData/Package Cache"
    create_new_dir "$DRIVE_C/windows/temp"
    create_new_dir "$DRIVE_C/windows/Installer"

    if [[ ! -d "$WINEPREFIX/dosdevices" ]] ; then
        create_new_dir "$WINEPREFIX/dosdevices"
        export REBOOT_PFX="1"
    fi

    if [[ ! -d "${WINEPREFIX}/dosdevices/c:" ]] ; then
        try_force_link_dir "${WINEPREFIX}/drive_c/" "${WINEPREFIX}/dosdevices/c:"
    fi
    if [[ ! -d "${WINEPREFIX}/dosdevices/z:" ]] ; then
        try_force_link_dir "/" "${WINEPREFIX}/dosdevices/z:"
    fi
    if [[ ! -d "${WINEPREFIX}/dosdevices/h:" ]] ; then
        try_force_link_dir "$HOME" "${WINEPREFIX}/dosdevices/h:"
    fi

    if [[ $REBOOT_PFX == "1" ]] ; then
        "$WINELOADER" wineboot -r
        wait_wineserver
    fi

    local winver
    if [[ "${WH_WINDOWS_VER,,}" == "xp64" ]]
    then winver="xp 64"
    else winver="$WH_WINDOWS_VER"
    fi
    if [[ -f "$WINEPREFIX/system.reg" ]] \
    && ! grep -iq "Microsoft Windows ${winver}\"" "$WINEPREFIX/system.reg"
    then
        [[ "$winver" == "xp 64" ]] && winver="xp64"
        "$WINELOADER" winecfg -v $(echo "win${winver}" | sed 's/.*/\L&/')
        wait_wineserver
        print_info "Windows версия изменена на win${winver}"
    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

    if check_wayland_session ; then
        # добаляем возможность запуска в нативном wayland если WH_USE_WAYLAND=1
        if [[ $WH_USE_WAYLAND == "1" ]] ; then
            export WINE_WAYLAND_HACKS="1"
            var_winedlloverride_update "winex11.drv=d;winewayland.drv=b"
            get_and_set_reg_file --add 'Software\Wine\Drivers' 'Graphics' 'REG_SZ' "x11,wayland" "user"
        else
            unset WINE_WAYLAND_HACKS
            get_and_set_reg_file --delete 'Software\Wine\Drivers' 'Graphics'
        fi
    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

    # включаем виртуальный рабочий стол при необходимости
    if [[ $WH_VIRTUAL_DESKTOP == "1" ]] ; then
        get_and_set_reg_file --add 'Software\Wine\Explorer' 'Desktop' 'REG_SZ' "Default" "user"
        WH_SCREEN_RESOLUTION="$(xrandr | sed -rn 's/^.*primary.* ([0-9]+x[0-9]+).*$/\1/p')"
        [[ $WH_SCREEN_RESOLUTION != *x* ]] && WH_SCREEN_RESOLUTION="1920x1080"
        get_and_set_reg_file --add 'Software\Wine\Explorer\Desktops' 'Default' 'REG_SZ' "$WH_SCREEN_RESOLUTION" "user"
    elif [[ $WH_VIRTUAL_DESKTOP == *x* ]] ; then
        get_and_set_reg_file --add 'Software\Wine\Explorer' 'Desktop' 'REG_SZ' "Default" "user"
        get_and_set_reg_file --add 'Software\Wine\Explorer\Desktops' 'Default' 'REG_SZ' "$WH_VIRTUAL_DESKTOP" "user"
    elif [[ $WH_VIRTUAL_DESKTOP == "0" ]] ; then
        get_and_set_reg_file --delete 'Software\Wine\Explorer\Desktops' 'Default'
        get_and_set_reg_file --delete 'Software\Wine\Explorer' 'Desktop'
    fi

    # управляем декоратором для ПО по умолчанию
    if [[ $WH_MAIN_DECORATED = "0" ]] ; then
        get_and_set_reg_file --add 'Software\Wine\X11 Driver' 'Decorated' 'REG_SZ' "N" "user"
    elif [[ $WH_MAIN_DECORATED = "1" ]] ; then
        get_and_set_reg_file --add 'Software\Wine\X11 Driver' 'Decorated' 'REG_SZ' "Y" "user"
    fi

    # управляем декоратором для maincontroller.exe
    if [[ $WH_MC_DECORATED = "0" ]] ; then
        # заменяет патч: https://git.altlinux.org/gears/w/wine.git?p=wine.git;a=blob;f=patches/0009-wine.inf.in-disable-decorated-window-for-maincontrol.patch;h=887a5e90e130cddeefdead831ef7a78a32588f11;hb=d097f4e4b64873c82ec31542c6f49f70829ab2b4
        get_and_set_reg_file --add 'Software\Wine\AppDefaults\maincontroller.exe\X11 Driver' 'Decorated' 'REG_SZ' "N" "user"
    elif [[ $WH_MC_DECORATED = "1" ]] ; then
        get_and_set_reg_file --add 'Software\Wine\AppDefaults\maincontroller.exe\X11 Driver' 'Decorated' 'REG_SZ' "Y" "user"
    fi

    # заменям шрифт Microsoft Sans Serif на Tahoma
    if [[ $WH_FONT_MSS_REPLACE = "1" ]] ; then
        # заменяет патч: https://git.altlinux.org/gears/w/wine.git?p=wine.git;a=blob;f=patches/0003-wine.inf-Add-the-font-replacement-for-Microsoft-Sans.patch;h=26b8ae2192d94a2b8ddd8565b90b62a2c2b0ed52;hb=d097f4e4b64873c82ec31542c6f49f70829ab2b4
        get_and_set_reg_file --add 'Software\Wine\Fonts\Replacements' 'Microsoft Sans Serif' 'REG_SZ' "Tahoma" "user"
    fi

    # добавляем сглаживание шрифтов
    if [[ $WH_FONT_SMOOTHING = "1" ]] ; then
        # заменяет патч: https://git.altlinux.org/gears/w/wine.git?p=wine.git;a=blob;f=patches/0002-Add-font-smoothing.patch;h=d7c252899499e9ee0e1a93f7c02548cc79025358;hb=d097f4e4b64873c82ec31542c6f49f70829ab2b4
        get_and_set_reg_file --add 'Control Panel\Desktop' 'FontSmoothing' 'REG_SZ' "2" "user"
        get_and_set_reg_file --add 'Control Panel\Desktop' 'FontSmoothingGamma' 'REG_DWORD' "0x00000578" "user"
        get_and_set_reg_file --add 'Control Panel\Desktop' 'FontSmoothingOrientation' 'REG_DWORD' "0x00000001" "user"
        get_and_set_reg_file --add 'Control Panel\Desktop' 'FontSmoothingType' 'REG_DWORD' "0x00000002" "user"
    fi

    # добавление ассоциаций файлов для запуска нативного приложения из wine
    # пример переменной: WH_XDG_OPEN="txt doc pdf"
    check_variables WH_XDG_OPEN "default"

    local WRAPPER="${WH_TMP_DIR}/wh-xdg-open.sh"
    local XDG_OPEN_REG="Software\Classes\xdg-open\shell\open\command"
    if [[ $WH_XDG_OPEN == "default" ]] ; then
        : # пропускаем изменения в реестре
    elif [[ $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
        if [[ $UPDATE_CPCSP_PROXY == "1" ]] \
        || [[ $REBOOT_PFX == "1" ]] \
        || ! grep -q "cpcsp_proxy.dll" "$WINEPREFIX/system.reg"
        then
            CPCSP_PROXY_OK="0"

            try_copy_wine_dll_to_pfx_64 "cpcsp_proxy.dll"
            try_copy_wine_dll_to_pfx_32 "cpcsp_proxy.dll"

            if [[ $WH_WINE_WOW64 == "1" ]] ; then
                print_info "Запускаем настройку cpcsp_proxy версии $CPCSP_PROXY_WOW64_VER"

                print_info "Запускаем регистрацию 64-битного cpcsp_proxy.dll..."
                env WINEDEBUG="fixme-all" "$WINE" "C:\\windows\\system32\\regsvr32.exe" \
                /s /n /i cpcsp_proxy.dll 2>&1 | tee "$WINEPREFIX/cpcsp_setup.log"
                wait_wineserver

                print_info "Запускаем регистрацию 32-битного cpcsp_proxy.dll..."
                env WINEDEBUG="fixme-all" "$WINE" "C:\\windows\\syswow64\\regsvr32.exe" \
                /s /n /i cpcsp_proxy.dll 2>&1 | tee -a "$WINEPREFIX/cpcsp_setup.log"
                wait_wineserver
            else
                print_info "Запускаем настройку cpcsp_proxy версии $CPCSP_PROXY_X86_64_VER"

                try_copy_wine_dll_to_pfx_64 "cpcsp_proxy_setup.exe"
                try_copy_wine_dll_to_pfx_32 "cpcsp_proxy_setup.exe"

                if [[ "$WINEARCH" == "win32" ]]
                then cpcsp_proxy_cmd=("$WINELOADER" "cpcsp_proxy_setup.exe")
                else cpcsp_proxy_cmd=("${WINELOADER}64" "cpcsp_proxy_setup.exe")
                fi

                set -o pipefail
                for ((i=2; i < 6; i++)) ; do
                    "${cpcsp_proxy_cmd[@]}" | tee "$WINEPREFIX/cpcsp_setup.log"
                    [[ "${PIPESTATUS[0]}" == "0" ]] && break
                    print_warning "Попытка $i из 5..."
                    wait_wineserver
                done
            fi

            if grep -q "failed to load /opt/cprocsp/" "$WINEPREFIX/cpcsp_setup.log" ; then
                print_error "Проверьте правильность установки КриптоПро в системе.\n Инструкция: https://www.altlinux.org/CryptoPro"
            else
                CPCSP_PROXY_OK="1"
            fi
            try_remove_file "$WINEPREFIX/cpcsp_setup.log"

            if [[ $CPCSP_PROXY_OK == "1" ]]
            then print_info "Настройка cpcsp_proxy успешно завершена."
            else fatal "Ошибка во время настройки cpcsp_proxy."
            fi
        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

    if [[ ! -f "$WINEPREFIX/last.conf" ]]
    then echo "# переменные последнего использования префикса:" > "$WINEPREFIX/last.conf"
    fi
    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_EXTRA_FONTS \
    WINEUSERNAME WH_USE_CPCSP_PROXY CPCSP_PROXY_X86_64_VER CPCSP_PROXY_WOW64_VER
    do
        update_last_conf_var "$var" "${!var}"
    done

    unset REBOOT_PFX
}

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' \
    | grep "$USER_WORK_PATH" | 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 () {
    local whdb_file="0"
    if [[ -n "$WIN_FILE_EXEC" ]] \
    && [[ -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

    [[ "$WINE_CPU_TOPOLOGY" == "0" ]] && unset WINE_CPU_TOPOLOGY

    # отключаем создание .desktop файлов средствами wine
    var_winedlloverride_update "winemenubuilder.exe=d"

    init_wineprefix
    use_winetricks
    init_dxvk "$DXVK_VER"
    init_vkd3d "$VKD3D_VER"

    [[ "$MANGOHUD" == 1 ]] && MANGOHUD_RUN="mangohud"
}

wine_regfile () {
    print_info "Запускаем команду: $WINELOADER $@"
    "$WINELOADER" "$@" && print_ok "Выполнено." || fatal "Не выполнено: $WINELOADER $@"
    wait_wineserver

    if [[ "$WINEARCH" == "win64" ]] \
    && [[ -f "${WINELOADER}64" ]]
    then
        print_info "Запускаем команду: ${WINELOADER}64 $@"
        "${WINELOADER}64" "$@" && print_ok "Выполнено." || fatal "Не выполнено: ${WINELOADER}64 $@"
        wait_wineserver
    fi
}

wine_run () {
    local wh_add_args win_file_exec win_file_path win_file_name

    if [[ $1 =~ (winecfg|regedit|winefile|wineconsole|control) ]] ; then
        win_file_exec="$1"
        win_file_name="$win_file_exec"
        win_file_path="$DRIVE_C"
        wh_add_args=""
    elif [[ $1 =~ \.dll$ ]] ; then
        wine_regfile regsvr32 /s "$@"
        return 0
    elif [[ -f "$1" ]] ; then
        win_file_exec="$(readlink -f "$1")"
        win_file_path="$(dirname "$win_file_exec")"
        win_file_name="$(basename "$win_file_exec")"

        case "${win_file_name,,}" in
            *.exe) wh_add_args="$WINE_WIN_START" ;;
            *.msi) wh_add_args="msiexec /i" ;;
            *.bat|*.cmd) wh_add_args="" ;;
            *.reg) wine_regfile regedit "$@" ; return 0 ;;
            *) fatal "Не удалось запустить файл $1. Проверьте расширение файла." ;;
        esac

        if [[ $WINEARCH == "win32" ]] \
        && file "$win_file_exec" | grep -q "x86-64"
        then fatal "Нельзя запустить 64-битное приложение в 32-битном префиксе!"
        fi
    else
        fatal "Команда введена не правильно или не найден исполняемый файл $1"
    fi

    shift
    cd "$win_file_path"

    if [[ $WINEDEBUG != "-all" ]] ; then
        local log_dir log_file
        log_dir="$HOME/winehelper_backup_log"
        log_file="$log_dir/${PREFIX_NAME}_${win_file_name%.*}.log"
        create_new_dir "$log_dir"
        date > "$log_file"

        echo -e "\n##### Версия установленного WineHelper #####" | tee -a "$log_file"
        rpm -q winehelper | tee -a "$log_file"

        print_warning "Включен режим логирования работы WINE."
        print_warning "Лог будет сохранен по пути: $log_file"

        echo -e "\n##### Основные переменные #####" | tee -a "$log_file"
        env | grep -e "WH_" -e "WINE" -e "DXVK" -e "VKD3D" -e "LD_" \
            | grep -v "ICON" | sort | tee -a "$log_file"

        echo -e "\n##### Лог WINE #####" | tee -a "$log_file"
        $MANGOHUD_RUN "$WINELOADER" $wh_add_args "$win_file_exec" "$@" 2>&1 | tee -a "$log_file"

        # Проверка размера лога и создание архива при превышении порога
        local MAX_LOG_SIZE_MB=10
        local MAX_LOG_SIZE_BYTES=$((MAX_LOG_SIZE_MB * 1048576))
        local log_size_bytes log_size_mb
        log_size_bytes=$(stat -c%s "$log_file" 2>/dev/null || echo 0)
        log_size_mb=$((log_size_bytes / 1048576))

        if (( log_size_bytes > MAX_LOG_SIZE_BYTES )); then
            local timestamp archive_name archive_path
            timestamp=$(date +"%d.%m.%Y-%H.%M")
            archive_name="log_${PREFIX_NAME}_${win_file_name%.*}_${timestamp}.tar.xz"
            archive_path="$log_dir/$archive_name"

            print_warning "Файл лога больше ${MAX_LOG_SIZE_MB}Мб (размер: ${log_size_mb}Мб)."
            print_warning "Создаем сжатый архив: $archive_name"

            tar -cJf "$archive_path" -C "$log_dir" "$(basename "$log_file")"

            if [[ -f "$archive_path" ]]; then
                print_ok "Архив успешно создан: $archive_path"
                rm -f "$log_file"
            else
                print_error "Не удалось создать архив: $archive_path"
            fi
        fi
    else
        $MANGOHUD_RUN "$WINELOADER" $wh_add_args "$win_file_exec" "$@"
    fi

    wait_wineserver
    cd "$DRIVE_C"
}

wine_run_install () {
    print_info "Запускаем установку: $1."
    case "$WH_INSTALL_MODE" in
        "manual") print_warning "Рекомендуется не менять пути для установки приложения!" ;;
          "test") print_warning "Установка приложения из списка экспериментальных скриптов." ;;
    esac

    if [[ ! -f "$1" ]]
    then fatal "Нет файла для установки: $1"
    else wine_run "$@"
    fi
}

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 [[ -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 WH_INSTALL_MODE

    if [[ $INSTALL_SCRIPT_NAME == "list" ]] || [[ -z "$INSTALL_SCRIPT_NAME" ]] ; then

        print_install_list () {
            parse_install_scripts() {
                local parse_dir="$1"
                [[ ! -d "$parse_dir" ]] || [[ -z "$(ls -A "$parse_dir" 2>/dev/null)" ]] && return

                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
                        }
                    }
                ' "$parse_dir"/*
                echo
            }
            print_info "Список программ с возможностью автоматической установки:"
            parse_install_scripts "$WH_AUTOINSTALL_DIR"
            print_info "Список программ с возможностью установки из существующего дистрибутива:"
            parse_install_scripts "$WH_MANUALINSTALL_DIR"
            print_warning "Программы из списка экспериментальных скриптов:"
            parse_install_scripts "$WH_TESTINSTALL_DIR"
        }

        print_install_list | less -R --use-color

    elif [[ "$INSTALL_SCRIPT" != "0" ]] ; 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
        source "$INSTALL_SCRIPT" "$@"
        [[ -n $OUT_PAGE_TMP ]] && try_remove_file "$OUT_PAGE_TMP"
        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 не найден: $sha256_file"

    if [[ "$WINEARCH" == "win64" ]]; then
        print_info "Фильтруем версии для 64-битного префикса..."
    else # win32
        print_info "Фильтруем версии для 32-битного префикса..."
    fi

    local options=()
    local total_versions_found=0

    # --- System ---
    # Добавляем системную версию только для 64-битного префикса
    if [[ "$WINEARCH" == "win64" ]]; then
        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 ---" "$system_wine_display_name")
    fi

    # --- 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
            case "$current_group" in
                "WINE WOW64"|"WINE AMD64"|"WINE I586")
                    # Фильтрация групп в зависимости от архитектуры префикса
                    if [[ "$WINEARCH" == "win64" ]]; then
                        # Для 64-битного префикса скрываем группу I586
                        if [[ "$current_group" != "WINE I586" ]]; then
                            local pretty_key="$current_group"
                            options+=("--- $pretty_key ---")
                        else
                            current_group="" # Игнорируем группу I586
                        fi
                    elif [[ "$WINEARCH" == "win32" ]]; then
                        # Для 32-битного префикса скрываем только группу WOW64
                        if [[ "$current_group" != "WINE WOW64" ]]; then
                            local pretty_key="$current_group"
                            options+=("--- $pretty_key ---")
                        fi
                    fi
                    ;;
                *)
                    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
                # Для 64-битного префикса показываем только 64-битные версии
                if ! [[ "$version_name" =~ i[3-6]86 ]]; then
                    group_versions+=("$version_name")
                fi
            else # Для 32-битного префикса показываем и i586, и amd64
                if [[ "$version_name" =~ (i[3-6]86|amd64) ]]; then
                     group_versions+=("$version_name")
                fi
            fi
        fi
    done < "$sha256_file"
    flush_group

    if [[ $total_versions_found -eq 0 ]]; then
        print_warning "Не найдено подходящих версий WINE для архитектуры $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 для $WINEARCH префикса:"

    local first_block=true
    for group_data in "${display_groups[@]}"; do
        if [[ "$group_data" == "---"* ]]; then
            if [[ "$first_block" = false ]]; then
            echo
        fi
            echo "$group_data"
        else
            local items_to_print=()
            IFS='@@@' read -r -a items_to_print <<< "$group_data"

            local num_items=${#items_to_print[@]}
            local term_width=${COLUMNS:-80}
            local max_len=0
            for item in "${items_to_print[@]}"; do
                (( ${#item} > max_len )) && max_len=${#item}
            done

            ((max_len+=2))
            local num_cols=$(( term_width / max_len ))
            (( num_cols = num_cols > 0 ? num_cols : 1 ))
            local num_rows=$(( (num_items + num_cols - 1) / num_cols ))

            for ((i=0; i<num_rows; i++)); do
                for ((j=0; j<num_cols; j++)); do
                    local index=$(( i + j * num_rows ))
                    (( index < num_items )) && printf "%-*s" "$max_len" "${items_to_print[index]}"
                done
                echo
            done
        fi
        first_block=false
        done

    while true; do
        echo
        local max_choice=$(( ${#selectable_options[@]} - 1 ))
        read -p "Введите номер для выбора wine (0-$max_choice): " user_choice
        if [[ "$user_choice" =~ ^[0-9]+$ ]] && (( user_choice >= 0 && user_choice <= max_choice )); then
            if [[ "$user_choice" == "0" ]]; then
                print_info "Операция отменена."
                return 1
            fi
            export WH_WINE_USE="${selectable_options[$user_choice]}"
            break
        else
            print_error "Неверный выбор. Введите число от 0 до $max_choice."
        fi
    done
    return 0
}

select_prepared_prefix() {
    local arch="$1"
    local sha256_file="$DATA_PATH/sha256sum.list"
    [[ ! -f "$sha256_file" ]] && fatal "Файл с описаниями префиксов не найден: $sha256_file"

    options=()
    descriptions=()

    options+=("none")
    descriptions+=("Создать чистый префикс без дополнительных библиотек")

    local in_prefix_section=false
    local current_description=""
    local current_prefix_name=""
    while IFS= read -r line; do
        if [[ "$line" =~ ^#####[[:space:]]PREFIX[[:space:]]#####$ ]] ; then
            in_prefix_section=true
            continue
        elif [[ "$line" =~ ^#####.* ]] ; then
            in_prefix_section=false
        fi

        if [[ "$in_prefix_section" == true ]] ; then
            if [[ "$line" =~ ^[a-f0-9]{64} ]] ; then
                # Если у нас есть имя предыдущего префикса, добавляем его описание
                if [[ -n "$current_prefix_name" ]] ; then
                    descriptions+=("$(echo -e "${current_description}" | sed 's/\\n$//')")
                fi
                current_description=""

                local filename
                filename=$(echo "$line" | awk '{print $2}')
                current_prefix_name=${filename%.tar.xz}

                if [[ "$arch" == "win32" ]] && ([[ "$current_prefix_name" == *"_x86_"* ]] || [[ "$current_prefix_name" == *"_i586_"* ]]) ; then
                    options+=("$current_prefix_name")
                elif [[ "$arch" == "win64" ]] && ([[ "$current_prefix_name" == *"_x64_"* ]] || [[ "$current_prefix_name" == *"_amd64_"* ]]) ; then
                    options+=("$current_prefix_name")
                else
                    # Если архитектура не совпадает, сбрасываем имя, чтобы не добавлять описание
                    current_prefix_name=""
                fi
            elif [[ "$line" =~ ^#[[:space:]] ]] ; then
                local comment_line=${line:2} # Удаляем '# '
                current_description+="$comment_line\n"
            fi
        fi
    done < "$sha256_file"
    # Добавляем описание для самого последнего префикса в файле
    if [[ -n "$current_prefix_name" ]] ; then
        descriptions+=("$(echo -e "${current_description}" | sed 's/\\n$//')")
    fi

    echo
    print_info "Выберите тип создаваемого префикса:"
    for i in "${!options[@]}" ; do
        printf "\n\E[36m %s) %s \e[0m\n" "$((i+1))" "${options[$i]}"
        [[ -n "${descriptions[$i]}" ]] && printf "   \E[33m%s\e[0m\n" "$(echo -e "${descriptions[$i]}" | sed 's/^/   /g')"
    done
}

create_prefix() {
    print_info "Существующие префиксы:"
    local prefixes=()
    for prefix in "$WH_PREFIXES_DIR"/*; do
        if [[ -d "$prefix" ]] ; then
            prefixes+=("$(basename "$prefix")")
        echo " - $(basename "$prefix")"
        fi
    done

    if [[ ${#prefixes[@]} -eq 0 ]]; then
        print_info "Нет существующих префиксов."
    fi
    echo

    read -p "Введите имя для нового префикса или 0 для отмены (по умолчанию: default): " prefix_name
    if [[ "$prefix_name" == "0" ]] ; then
        print_info "Создание префикса отменено."
        exit 0
    fi

    prefix_name=${prefix_name:-default}

    if [[ ! "$prefix_name" =~ ^[a-zA-Z0-9_.-]+$ ]] ; then
        fatal "Имя префикса может содержать только латинские буквы, цифры, точки, дефисы и подчеркивания"
    fi

    if [[ -d "$WH_PREFIXES_DIR/$prefix_name" ]] ; then
        fatal "Префикс с именем '$prefix_name' уже существует. Создание отменено."
    fi

    print_info "Создается префикс с именем: \"$prefix_name\""

    print_info "Выберите разрядность префикса:"
    echo " 0) Отмена создания префикса"
    echo " 1) 32-bit"
    echo " 2) 64-bit"
    echo
    local arch_choice
    read -p "Ваш выбор [0-2] (по умолчанию 1): " arch_choice
    case "${arch_choice:-1}" in
        0) print_info "Создание префикса отменено." ; exit 0 ;;
        1) export WINEARCH="win32" ;;
        2) export WINEARCH="win64" ;;
        *) fatal "Неверный выбор. Операция отменена." ;;
    esac

    select_wine_version || exit 0

    select_prepared_prefix "$WINEARCH"
    local max_choice=${#options[@]}
    local user_choice
    while true; do
        read -p "Ваш выбор [1-$max_choice] (0 для отмены): " user_choice
        if [[ "$user_choice" == "0" ]]; then
            print_info "Создание префикса отменено." ; exit 0
        elif [[ "$user_choice" -ge 1 && "$user_choice" -le "$max_choice" ]]; then
            export BASE_PFX="${options[$((user_choice-1))]}"
            break
        else
            print_error "Неверный выбор. Введите число от 1 до $max_choice."
        fi
    done

    print_license_agreement || exit 0

    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 " - Все резервные копии и логи, созданные 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 "$HOME/winehelper_backup_log"
    try_remove_dir "/var/tmp/winehelper-$USER"

    # Удаление файлов меню
    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 () {
    [[ -n $1 ]] && export WINEPREFIX="$1"
    check_prefix_var

    if [[ -f "$WINEPREFIX/last.conf" ]] ; then
        source "$WINEPREFIX/last.conf"
    fi

    local prefix_dir drive_c_dir users_dir archive_path pfx_arch
    prefix_dir="$WINEPREFIX"
    drive_c_dir="$prefix_dir/drive_c"
    users_dir="$drive_c_dir/users"

    if [[ "$(grep "#arch" "$prefix_dir/system.reg")" =~ "64" ]]
    then pfx_arch="x64"  
    else pfx_arch="x86"
    fi
    archive_path="$WH_TMP_DIR/pfx/${PREFIX_NAME}_pfx_${pfx_arch}_vNEW.tar.xz"

    # создаем резернвую копию каталога с префиксом
    try_copy_dir "$prefix_dir" "${prefix_dir}_bak"
    create_new_dir "$WH_TMP_DIR/pfx/"

    # удаляем лишнюю информацию из winetricks.log
    for wtlog in workaround isolate internal winxp win2 win7 win10
    do sed -i "/$wtlog/d" "$prefix_dir/winetricks.log"
    done

    # переносим пользовательский каталог в общий для всех "$WINEUSERNAME"
    if [[ -d "$users_dir/$USER" ]] \
    && [[ ! -L "$users_dir/$USER" ]]
    then
        if [[ -L "$users_dir/$WINEUSERNAME" ]]
        then try_remove_dir "$users_dir/$WINEUSERNAME"
        fi
        create_new_dir "$users_dir/$WINEUSERNAME"
        cp -fr "$users_dir/$USER"/* "$users_dir/$WINEUSERNAME/"
        try_remove_dir "$users_dir/$WINEUSERNAME/Рабочий стол"
    fi

    # удаляем всё ненужное для переноса префикса
    try_remove_dir "$prefix_dir/dosdevices/"
    try_remove_dir "$users_dir/$USER"
    try_remove_dir "$users_dir/$WINEUSERNAME/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/"

    # удаляем кэш установщика .net
    rm -fr "$drive_c_dir/windows/Microsoft.NET"/*/*/SetupCache/

    # удаляем все симлинки внутри каталога windows
    find "$drive_c_dir/windows/" -type l -delete

    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")" &
        exit 0
    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="$HOME/winehelper_backup_log"
    create_new_dir "$backup_base_dir"
    backup_archive_name="backup_${PREFIX_NAME}_$(date +%d.%m.%Y-%H.%M).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)"
    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/"

    if [[ -f "$WINEPREFIX/last.conf" ]]; then
        source "$WINEPREFIX/last.conf"
    fi

    if cp -a "$WINEPREFIX" "$temp_prefix_dir" ; then
        try_remove_dir "$temp_prefix_dir/dosdevices"
        if [[ -d "$temp_users_dir/$USER" ]] \
        && [[ ! -L "$temp_users_dir/$USER" ]]
        then
            if [[ -L "$temp_users_dir/$WINEUSERNAME" ]]
            then try_remove_dir "$temp_users_dir/$WINEUSERNAME"
            fi
            create_new_dir "$temp_users_dir/$WINEUSERNAME"
            cp -fr "$temp_users_dir/$USER"/* "$temp_users_dir/$WINEUSERNAME/"
        fi
        try_remove_dir "$temp_users_dir/$WINEUSERNAME/Рабочий стол"
        try_remove_dir "$temp_users_dir/$USER"
        print_ok "Директория префикса подготовлена."
    else
        print_error "Не удалось подготовить директорию префикса."
        try_remove_dir "$temp_backup_dir"
        return 1
    fi

    if [[ $WH_WINE_USE != system* ]] ; then
        init_wine_ver
        print_info "Копирование используемой версии WINE: $WH_WINE_USE"

        if cp -aL "$WH_DIST_DIR/$WH_WINE_USE" "$temp_backup_dir/dist/"
        then print_ok "WINE скопирован."
        else fatal "Не удалось скопировать WINE."
        fi
    fi

    # Сохраняем метаданные внутри временной директории
    echo "$PREFIX_NAME" > "$temp_backup_dir/prefix_name.whmeta"

    print_info "Создание squashfs архива..."
    if mksquashfs "$temp_backup_dir" "$backup_dest_path" -comp xz ; 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 восстановлен."

            export WINEPREFIX="$WH_PREFIXES_DIR/$prefix_name"
            if [[ -f "$WINEPREFIX/last.conf" ]] ; then
                source "$WINEPREFIX/last.conf"
                prepair_wine
                if [[ -f "$WINEPREFIX/restore_pfx.sh" ]] ; then
                    source "$WINEPREFIX/restore_pfx.sh"
                fi
            fi

            if [[ -f "$WH_PREFIXES_DIR/$prefix_name/desktop.list" ]] ; then
                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
        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<num_rows; i++)) ; do
        for ((j=0; j<num_cols; j++)) ; do
            local index=$(( i + j * num_rows ))
            (( index < num_items )) && printf "%-*s" "$max_len" "${items_to_print[index]}" >&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_file_in_prefix() {
    if [[ -z "$1" ]] || [[ -z "$2" ]] || [[ ! -f "$2" ]] ; then
        fatal "Использование: $SCRIPT_NAME run-in-prefix <имя_префикса> <путь/к/фйалу>"
    fi

    export WINEPREFIX="$1"
    shift

    check_prefix_var
    prepair_wine
    wine_run "$@"
}

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."
}

clear_winetricks_cache() {
    local winetricks_cache_dir="$HOME/.cache/winetricks"
    local winehelper_wt_cache_dir="$HOME/.cache/winehelper/winetricks"

    if [[ ! -d "$winetricks_cache_dir" ]] && [[ ! -d "$winehelper_wt_cache_dir" ]]; then
        print_info "Кэш Winetricks не найден. Очистка не требуется."
        return 0
    fi

    if [[ ! $1 =~ --force|-y ]] ; then
        print_warning "Вы собираетесь очистить кэш Winetricks."
        echo "Будут удалены все скачанные установщики Winetricks и списки компонентов."
        if ! print_confirmation "Продолжить?"
        then
            print_info "Операция отменена."
            exit 1
        fi
    fi
    print_info "Очистка кэша Winetricks..."
    try_remove_dir "$winetricks_cache_dir"
    try_remove_dir "$winehelper_wt_cache_dir"
    print_ok "Кэш Winetricks успешно очищен."
}

wh_info () {
    echo "Использование: $SCRIPT_NAME [команда]

Команды:
    install list                    список возможных установочных скриптов
    install [скрипт]                запустить скрипт установки программы
    install [скрипт] --clear-pfx    не использовать готовый префикс для установки ПО

    install-dxvk [версия|none|list] установить, удалить или показать версии DXVK
    install-vkd3d [версия|none|list] установить, удалить или показать версии VKD3D
    change-wine [версия]            изменить версию Wine для текущего префикса

    installed                       список установленных программ
    run [программа]                 запуск программы (отладка)
    remove-all                      удалить WineHelper и все связанные данные
    create-prefix                   создать префикс
    remove-prefix [имя_префикса]    удалить префикс и все связанные данные
    backup-prefix [имя_префикса]    создать резервную копию префикса
    restore-prefix \"путь/до/whpack\" восстановить префикс из резервной копии
    clear-winetricks-cache          очистить кэш Winetricks

Параметры:
    --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 [[ $WH_DEVEL != "1" ]] \
&& [[ -d "$HOME/.local/share/$SCRIPT_NAME" ]] \
&& [[ ! -L "$HOME/.winehelper" ]]
then try_force_link_dir "$HOME/.local/share/$SCRIPT_NAME" "$HOME/.winehelper"
fi

if [[ -n "$1" ]] ; then
    arg1="$1"
    shift
else
    arg1="--help"
fi

case "$arg1" in
    --version|version) rpm -qi "$SCRIPT_NAME" ; exit 0 ;;
    --help|help) wh_info ; exit 0 ;;
    --changelog|changelog) less "$CHANGELOG_FILE" ; exit 0 ;;
    killall) kill_wine ;;
    winecfg) prepair_wine ; wine_run "winecfg" ;;
    winereg|regedit) prepair_wine ; wine_run "regedit" ;;
    winefile|explorer) prepair_wine ; wine_run "winefile" ;;
    wineconsole|cmd) prepair_wine ; wine_run "wineconsole" ;;
    control) prepair_wine ; wine_run "control" ;;
    winetricks) prepair_wine ; "$WH_WINETRICKS" -q "$@" ;;
    desktop) create_desktop "$@" ; exit 0 ;;
    install|-i) run_autoinstall "$@" ;;
    run-in-prefix) run_file_in_prefix "$@" ;;
    install-dxvk) run_install_dxvk "$@" ;;
    install-vkd3d) run_install_vkd3d "$@" ;;
    change-wine) run_change_wine_version "$@" ;;
    installed) check_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 ;;
    clear-winetricks-cache) clear_winetricks_cache "$@" ;;
    run|-r)
        if check_installed_programs check_only "$1" ; then
            WIN_FILE_EXEC="$(readlink -f "$EXE_PATH")"
            shift
            prepair_wine
            wine_run "$WIN_FILE_EXEC" "$@"
        fi
        ;;
    *)
        if [[ -f "$arg1" ]] ; then
            WIN_FILE_EXEC="$(readlink -f "$arg1")"
            WIN_FILE_NAME="$(basename "$WIN_FILE_EXEC")"
            find_prefix "$WIN_FILE_EXEC"
            prepair_wine

            if [[ -n "$1" ]] && [[ -f "$1" ]] ; then
                WIN_OPEN_FILE="$("$WINELOADER" winepath -w "$1")"
                shift
                wine_run "$WIN_FILE_EXEC" "$@" "$WIN_OPEN_FILE"
            else
                wine_run "$WIN_FILE_EXEC" "$@"
            fi
        elif [[ "${arg1,,}" =~ \.dll ]] ; then
            prepair_wine
            wine_run "$arg1"
        else
            print_error "Команды $arg1 не существует."
            wh_info
            exit 1
        fi
        ;;
esac

if [[ -n "$WH_BG_PID" ]] ; then
    print_info "Завершение фонового процесса: $WH_BG_PID"
    kill "$WH_BG_PID" 2>/dev/null
fi
