#!/usr/bin/env bash

##### CHECK ROOT #####
if [[ $(id -u) -eq 0 ]] ; then
    echo "Перезапустите скрипт $0 от обычного пользователя!"
    exit 1
fi

##### DEFAULT PATH #####
export SCRIPT_NAME USER_WORK_PATH RUN_SCRIPT DATA_PATH CHANGELOG_FILE WH_ICON_PATH LICENSE_FILE AGREEMENT THIRD_PARTY_FILE

SCRIPT_NAME="$(basename "$0")"
if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then
    # переменные для установленного WineHelper в систему
    WH_VERSION="$(rpm -q winehelper | awk -F'-' '{print $2}')"
    USER_WORK_PATH="$HOME/.local/share/$SCRIPT_NAME"
    RUN_SCRIPT="/usr/bin/$SCRIPT_NAME"
    DATA_PATH="/usr/share/$SCRIPT_NAME"
    WH_ICON_PATH="$DATA_PATH/image/gui/winehelper.svg"
    CHANGELOG_FILE="/usr/share/doc/winehelper-$WH_VERSION/CHANGELOG"
    LICENSE_FILE="/usr/share/doc/winehelper-$WH_VERSION/LICENSE"
    AGREEMENT="/usr/share/doc/winehelper-$WH_VERSION/LICENSE_AGREEMENT"
    THIRD_PARTY_FILE="/usr/share/doc/winehelper-$WH_VERSION/THIRD-PARTY"
else
    # переменные для тестового запуска WineHelper из репозитория
    USER_WORK_PATH="$HOME/test-$SCRIPT_NAME"
    RUN_SCRIPT="$(realpath "$0")"
    DATA_PATH="$(dirname "$RUN_SCRIPT")"
    CHANGELOG_FILE="$DATA_PATH/CHANGELOG"
    WH_ICON_PATH="$DATA_PATH/image/gui/winehelper-devel.svg"
    LICENSE_FILE="$DATA_PATH/LICENSE"
    AGREEMENT="$DATA_PATH/LICENSE_AGREEMENT"
    THIRD_PARTY_FILE="$DATA_PATH/THIRD-PARTY"

    # минимальная проверка синтаксиса скриптов
    for self_check_script in "$RUN_SCRIPT" \
    "$DATA_PATH/dependencies.sh" "$DATA_PATH/autoinstall"/* \
    "$DATA_PATH/manualinstall"/* "$DATA_PATH/testinstall"/* \
    "$DATA_PATH/database"/*
    do /usr/bin/env bash -n "$self_check_script" || exit 1
    done
fi

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

##### MESSAGES FUNCTIONS #####
if [[ $WH_USE_GUI != "1" ]] ; then
    print_error () { printf "\E[31m%s Ошибка: $@ %s\e[0m\n" ;}
    print_warning () { printf "\E[33m%s Предупреждение: $@ %s\e[0m\n" ;}
    print_info () { printf "\E[36m%s Информация: \"$@\" %s\e[0m\n" ;}
    print_ok () { printf "\E[35m%s Успех: $@ %s\e[0m\n" ;}
else
    print_error () { echo -e "Ошибка: $@" ;}
    print_warning () { echo -e "Предупреждение: $@" ;}
    print_info () { echo -e "Информация: \"$@\"" ;}
    print_ok () { echo -e "Успех: $@" ;}
fi

print_var () { for vp in $@ ; do echo "${vp}=${!vp}" ; done ;}

fatal () {
    print_error "$@"
    [[ -n "$WINESERVER" ]] && "$WINESERVER" -w
    exit 1
}

print_confirmation () {
    local answer
    read -p "$@ (y/N): " answer
    if [[ ! "$answer" =~ ^[Yy]$ ]] ; then
        print_info "Отменено пользователем."
        return 1
    fi
    return 0
}

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

##### CHECK DEBUG #####
if [[ "$1" == "--debug" ]] ; then
    check_variables WINEDEBUG "+loaddll,+seh"
    export DXVK_LOG_LEVEL="error"
    export VKD3D_DEBUG="error"
    export WINE_MONO_TRACE="E:System.NotImplementedException"
    export VK_LOADER_DEBUG="error"
    export VKBASALT_LOG_LEVEL="error"
    export DXVK_NVAPI_LOG_LEVEL="error"
    shift
else
    check_variables WINEDEBUG "-all"
    check_variables DXVK_LOG_LEVEL "none"
    check_variables VKD3D_SHADER_DEBUG "none"
    check_variables VKD3D_DEBUG "none"
    check_variables DXVK_LOG_PATH "none"
    check_variables VKBASALT_LOG_LEVEL "none"
    check_variables DXVK_NVAPI_LOG_LEVEL "none"
fi

if [[ $WINEDEBUG != "-all" ]] ; then
    export LOG_FILE="$HOME/winehelper.log"
    date > "$LOG_FILE"
    print_warning "Включен режим логирования работы WINE."
    print_warning "Лог будет сохранен по пути: $LOG_FILE"
    sleep 3
fi

##### WINETRICKS VERSION #####
WINETRICKS_VERSION="20250102"

##### DEFAULT VARIABLES #####
WH_VULKAN_LIBDIR="$USER_WORK_PATH/vulkan"
WH_PREFIXES_DIR="$USER_WORK_PATH/prefixes"
WH_DIST_DIR="$USER_WORK_PATH/dist"
WH_TMP_DIR="$USER_WORK_PATH/tmp"

WH_IMAGE_PATH="$DATA_PATH/image"
WH_DB_DIR="$DATA_PATH/database"
WH_AUTOINSTALL_DIR="$DATA_PATH/autoinstall"
WH_MANUALINSTALL_DIR="$DATA_PATH/manualinstall"
WH_TESTINSTALL_DIR="$DATA_PATH/testinstall"
WH_WINETRICKS="$DATA_PATH/winetricks_$WINETRICKS_VERSION"

WH_MENU_DIR="$HOME/.local/share/applications/WineHelper"
WH_MENU_CATEGORY="$HOME/.local/share/desktop-directories/WineHelper.directory"
WH_MENU_CONFIG="$HOME/.config/menus/applications-merged/WineHelper.menu"

# export WINEDLLOVERRIDES=mshtml,mscoree="

check_variables WINEESYNC "0"
check_variables WINEFSYNC "0"

check_variables WINEUSERNAME "xuser"
check_variables WINEARCH "win64" # or "win32"
check_variables WH_WINE_USE "wine_x_tkg_10-0_amd64" # or system

check_variables WH_USE_CPCSP_PROXY "0"
check_variables CPCSP_PROXY_VER "0.6.1-alt1"

check_variables WH_USE_EXTRA_FONTS "0"
check_variables EXTRA_FONTS_VER "01"

check_variables STAGING_SHARED_MEMORY "1"
check_variables WINE_LARGE_ADDRESS_AWARE "1"
check_variables WINE_FULLSCREEN_FSR "1"
check_variables WINE_DO_NOT_CREATE_DXGI_DEVICE_MANAGER "0"
check_variables WINE_HEAP_DELAY_FREE "0"
check_variables WINE_ALLOW_XIM "0"

check_variables WH_WINDOWS_VER "10"
# check_variables WH_USE_GSTREAMER "1"
# check_variables WH_USE_D3D_EXTRAS "1"
check_variables WH_USE_SHADER_CACHE "1"
check_variables WH_USE_WINE_DXGI "0"
check_variables WH_DLL_INSTALL ""

check_variables WINE_WIN_START "start /wait /high /unix"

check_variables WINE_CPU_TOPOLOGY "8"

check_variables USE_RENDERER "opengl" # opengl, damavand, proton

check_variables DXVK_VER "1.10.3-28"
# check_variables DXVK_CONFIG_FILE "path/to/dxvk.conf"

check_variables VKD3D_VER "1.1-2602"
# check_variables VKD3D_LIMIT_TESS_FACTORS 64
# check_variables VKD3D_FEATURE_LEVEL "12_0"

export CLOUD_URL="https://cloud.linux-gaming.ru/portproton"

if env | grep license_agreement_file
then fatal "Обнаружена подмена переменной license_agreement_file!"
else readonly license_agreement_file="$(mktemp -d)/$((RANDOM % RANDOM))"
fi

##### CHECK NOEXEC FOR /HOME #####
if mount -l | grep -E "[[:space:]]/home[[:space:]]" | grep -q "noexec" ; then
    fatal "/home примонтирован в /etc/fstab с аргументом noexec.\nЗапуск портативной версии wine невозможен из домашнего каталога."
fi

##### ROOT #####
su_run () {
    if [[ $WH_USE_GUI != "1" ]] ; then
        local i="1"
        while [[ $i -le "3" ]] ; do
            print_info "Для продолжения установки введите root пароль (попытка $i из 3)..."
            su - -c "$@" && return 0
            ((i++))
        done
    else
        pkexec bash -c "$@" && return 0
    fi
    fatal "Не удалось установить необходимые компоненты!"
}

##### CHECK DEPENDENCIES #####
# fonts-ttf-ms

if ! rpm -q {i586-,}{wine,glibc-core,libstdc++6,glibc-pthread,glibc-nss,\
libnss-mdns,libunixODBC2,ocl-icd,libfreetype,libfontconfig1,libgnutls30,libGL,\
libEGL,xorg-dri-swrast,xorg-dri-intel,xorg-dri-radeon,libvulkan1,libcups} 1>/dev/null
then
    if su_run "$DATA_PATH/dependencies.sh"
    then print_info "Зависимости успешно установлены. Продолжаем работу $SCRIPT_NAME"
    else fatal "Не удалось установить зависимости. Работа $SCRIPT_NAME прервана."
    fi
fi

##### HELPER FUNCTIONS #####
add_to_var () {
    if ! echo ${!1} | grep "$2" &>/dev/null
    then export $1="${!1} $2"
    fi
}

rm_from_var () {
    if echo ${!1} | grep "$2" &>/dev/null
    then export $1="$(echo "${!1//$2/}" | tr -s " ")"
    fi
}

try_remove_file () {
    if [[ -f "$1" ]] || [[ ! -e "$1" ]] ; then
        rm -f "$1"
        [[ "$?" == 0 ]] && return 0 || return 1
    fi
}

try_remove_dir () {
    if [[ -d "$1" ]] ; then
        rm -fr "$1"
        [[ "$?" == 0 ]] && return 0 || return 1
    fi
}

try_copy_file () {
    if [[ ! -f "$1" ]] ; then print_info "файла $1 не существует для копирования" && return 1
    elif [[ -z "$2" ]] ; then fatal "нет пути для копирования файла $1"
    elif [[ -L "$2" ]] ; then
        try_remove_file "$2"
        cp -f "$1" "$2" && return 0 || return 1
    else
        [[ -e "$2/$(basename "$1")" ]] && rm -f "$2/$(basename "$1")"
        cp -f "$1" "$2" && return 0 || return 1
    fi
}

try_copy_dir () {
    if [[ ! -d "$1" ]] ; then print_info "каталога $1 не существует для копирования"
    elif [[ -z "$2" ]] ; then fatal "нет пути для копирования каталога $1"
    else
        cp -fr "$1" "$2"
        [[ "$?" != 0 ]] && print_error "не удалось скопировать каталог $1 в $2" || return 0
    fi
    return 1
}

try_force_link_file () {
    if [[ ! -f "$1" ]] ; then
        print_warning "нет файла для создания символической ссылки: $1"
        if [[ -f "$2" ]] ; then
            try_remove_file "$2"
            print_warning "удаляем символическую ссылку: $2"
        fi
        return 1
    elif [[ -z "$2" ]] ; then fatal "нет пути для создания символической ссылки на файл $1"
    else
        try_remove_file "$2"
        ln -s -f -r "$1" "$2"
        return 0
    fi
    return 1
}

try_force_link_dir () {
    if [[ ! -d "$1" ]] ; then print_info "каталога $1 не существует для создания символической сссылки"
    elif [[ -z "$2" ]] ; then fatal "не указан путь для создания символической ссылки на каталог $1"
    else
        ln -s -f -r "$1" "$2"
        [[ "$?" != 0 ]] && print_error "не удалось сделать символическую ссылку на каталог $1 по пути $2" || return 0
    fi
    return 1
}

create_new_dir () {
    if [[ ! -d "$1" ]] ; then
        mkdir -p "$1"
    fi
    return 0
}

unpack () {
    if [[ $1 == "--skip-xattr" ]] ; then
        local skip_xattr="1"
        shift
    else
        unset skip_xattr
    fi
    print_info "Запуск распаковки архива $1"
    local command outarg
    case $1 in
        *.tar.xz) command="tar -Jxhf" ; outarg="-C " ;;
        *.tar.gz) command="tar -xhzf" ; outarg="-C " ;;
        *.tar.zst) command="tar -I zstd -xhf" ; outarg="-C " ;;
        *.tar) command="tar -xhf" ; outarg="-C " ;;
        *.zip|*.exe|*.rar) command="7z x -y -bso0" ; outarg="-o" ;;
    esac
    create_new_dir "$2"
    if [[ $skip_xattr == "1" ]] \
    && $command "$1" ${outarg}"$2" 2>&1 | sed "/xattr/d"
    then print_ok "Файл $1 распакован."
    elif $command "$1" ${outarg}"$2"
    then print_ok "Файл $1 распакован."
    else
        try_remove_file "$1"
        fatal "Распаковать файл $1 не удалось!"
    fi
    return 0
}

try_get_page () {
    local url_page="$1"
    export OUT_PAGE_TMP="${WH_TMP_DIR}/url_page.tmp"
    print_info "Чтение страницы: $url_page"
    if ! curl -o "$OUT_PAGE_TMP" -A "Mozilla/5.0 (compatible; Konqueror/2.1.1; X11)" "$url_page" \
    || grep -q "Forbidden" "$OUT_PAGE_TMP"
    then
        try_remove_file "$OUT_PAGE_TMP"
        fatal "Страница сайта $1 не доступна или превышено количество запросов к странице."
    else
        return 0
    fi
}

read_page () {
    if [[ -n $OUT_PAGE_TMP ]] \
    && [[ -f "$OUT_PAGE_TMP" ]]
    then
        cat "$OUT_PAGE_TMP"
        try_remove_file "$OUT_PAGE_TMP"
        unset OUT_PAGE_TMP
    else
        echo "Используй try_get_page перед read_page"
    fi
}

check_file_sum () {
    local sha256sum_ext sha256sum_int check_file_path check_file_name
    check_file_path="$1"
    check_file_name="$(basename "$check_file_path")"

    sha256sum_ext=$(sha256sum "$check_file_path" | awk '{print $1}')
    sha256sum_int="$(grep "$check_file_name" "$DATA_PATH/sha256sum.list" | awk '{print $1}')"

    if [[ "$sha256sum_ext" == "$sha256sum_int" ]] ; then
        print_ok "Хэш-сумма файла $check_file_name успешно проверена."
        return 0
    else
        try_remove_file "$check_file_path"
        fatal "Хэш-сумма файла $check_file_name не совпадает!\n Попробуйте перезапустить установку."
    fi
}

print_license_agreement () {
    if [[ -f  "$license_agreement_file" ]] \
    && [[ "$(stat -c %a "$license_agreement_file" 2>/dev/null)" == "600" ]]
    then return 0
    fi

    if [[ -f "$AGREEMENT" ]]; then
        echo
        print_warning "$(cat "$AGREEMENT")"
    else
        fatal "Файл лицензионного соглашения не найден: $AGREEMENT"
    fi

    echo
    if print_confirmation "Подтвердите продолжение установки" ; then
        touch "$license_agreement_file"
        chmod 600 "$license_agreement_file"
        cleanup_laf () {
            local cleanup_laf_dir="$(dirname "$license_agreement_file")"
            rm -r "$cleanup_laf_dir" || echo "Не удалось удалить каталог $cleanup_laf_dir"
        }
        trap "cleanup_laf" EXIT
        return 0
    else
        exit 1
    fi
}

try_download () {
    if [[ $WH_USE_GUI == "1" ]] \
    && [[ $(ps -o command= -p "$PPID" | awk '{print $2}') =~ "$DATA_PATH/winehelper_gui.py" ]]
    then print_ok "Соглашения приняты из графического интерфейса."
    else print_license_agreement
    fi
    local download_file_url output_file output_file_name
    download_file_url="${1// /%20}"
    output_file="$2"
    output_file_name="$(basename "$output_file")"

    if [[ -f "$output_file" ]] ; then
        print_info "Файл найден: $output_file"
        [[ $3 == "check256sum" ]] && check_file_sum "$output_file"
        return 0
    else
        print_info "Скачивание файла $output_file_name..."
        if curl -f --progress-bar -A "Mozilla/5.0 (compatible; Konqueror/2.1.1; X11)" \
        -L "$download_file_url" -o "$output_file"
        then
            print_ok "Скачивание файла $output_file_name прошло успешно."
            [[ $3 == "check256sum" ]] && check_file_sum "$output_file"
            return 0
        else
            try_remove_file "$output_file"
            fatal "Скачивание файла: $output_file_name завершилось с ошибкой!"
            return 1
        fi
    fi
}

try_copy_other_dll_to_pfx_64 () {
    if [[ "$WINEARCH" == "win64" ]] ; then
        cmp -s "$1" "${WINEPREFIX}/drive_c/windows/system32/$(basename $1)" && return 0
        try_copy_file "$1" "${WINEPREFIX}/drive_c/windows/system32/" && return 0 || return 1
    fi
}

try_copy_other_dll_to_pfx_32() {
    if [[ "$WINEARCH" == "win64" ]] ; then
        cmp -s "$1" "${WINEPREFIX}/drive_c/windows/syswow64/$(basename $1)" && return 0
        try_copy_file "$1" "${WINEPREFIX}/drive_c/windows/syswow64/" && return 0 || return 1
    else
        cmp -s "$1" "${WINEPREFIX}/drive_c/windows/system32/$(basename $1)" && return 0
        try_copy_file "$1" "${WINEPREFIX}/drive_c/windows/system32/" && return 0 || return 1
    fi
}

try_copy_wine_dll_to_pfx_64 () {
    if [[ "$WINEARCH" == "win64" ]] ; then
        if [[ -d "$WINEDIR/lib64/wine/x86_64-windows" ]]
        then WINE_BUILD_DLL_64="$WINEDIR/lib64/wine/x86_64-windows"
        else WINE_BUILD_DLL_64="$WINEDIR/lib64/wine"
        fi
        cmp -s "$WINE_BUILD_DLL_64/$1" "${WINEPREFIX}/drive_c/windows/system32/$(basename $1)" && return 0
        try_copy_file "$WINE_BUILD_DLL_64/$1" "${WINEPREFIX}/drive_c/windows/system32/" && return 0 || return 1
    fi
}

try_copy_wine_dll_to_pfx_32() {
    if [[ -d "$WINEDIR/lib/wine/i386-windows" ]]
    then WINE_BUILD_DLL_32="$WINEDIR/lib/wine/i386-windows"
    else WINE_BUILD_DLL_32="$WINEDIR/lib/wine"
    fi
    if [[ "$WINEARCH" == "win64" ]] ; then
        cmp -s "$WINE_BUILD_DLL_32/$1" "${WINEPREFIX}/drive_c/windows/syswow64/$1" && return 0
        try_copy_file "$WINE_BUILD_DLL_32/$1" "${WINEPREFIX}/drive_c/windows/syswow64/" && return 0 || return 1
    else
        cmp -s "$WINE_BUILD_DLL_32/$1" "${WINEPREFIX}/drive_c/windows/system32/$1" && return 0
        try_copy_file "$WINE_BUILD_DLL_32/$1" "${WINEPREFIX}/drive_c/windows/system32/" && return 0 || return 1
    fi
}

var_winedlloverride_update () {
    if [[ -n "${WINEDLLOVERRIDES}" ]]
    then export WINEDLLOVERRIDES="${1};${WINEDLLOVERRIDES}"
    else export WINEDLLOVERRIDES="${1}"
    fi
}

var_dxvk_config_update () {
    if [[ -n "${DXVK_CONFIG}" ]]
    then export DXVK_CONFIG="${1};${DXVK_CONFIG}"
    else export DXVK_CONFIG="${1}"
    fi
}

var_vkd3d_config_update () {
    if [[ -n "${VKD3D_CONFIG}" ]]
    then export VKD3D_CONFIG="${1};${VKD3D_CONFIG}"
    else export VKD3D_CONFIG="${1}"
    fi
}

var_radv_perftest_config_update () {
    if [[ -n "${RADV_PERFTEST}" ]]
    then export RADV_PERFTEST="${1};${RADV_PERFTEST}"
    else export RADV_PERFTEST="${1}"
    fi
}

var_vk_istance_layers_config_update () {
    if [[ -n "${VK_INSTANCE_LAYERS}" ]]
    then export VK_INSTANCE_LAYERS="${1}:${VK_INSTANCE_LAYERS}"
    else export VK_INSTANCE_LAYERS="${1}"
    fi
}

var_ld_library_path_update () {
    if echo "$LD_LIBRARY_PATH" | grep "$1"
    then return 0
    elif [[ -n "$LD_LIBRARY_PATH" ]]
    then export LD_LIBRARY_PATH="$1:$LD_LIBRARY_PATH"
    else export LD_LIBRARY_PATH="$1"
    fi
    return 0
}

find_prefix () {
    # Автоматическое определение префикса, если он не задан
    if [[ -z "$WINEPREFIX" ]] && [[ "$1" == "$WH_PREFIXES_DIR"* ]]; then
        local extracted_prefix
        extracted_prefix="$(echo "$1" | grep -o ".*/prefixes/[^/]*")"
        if [[ -d "$extracted_prefix" ]]; then
            export WINEPREFIX="$extracted_prefix"
            print_info "Префикс автоматически определен: $(basename "$WINEPREFIX")"
        fi
    fi
}

create_desktop () {
    local name_desktop exe_file desktop_filename icon_file desktop_path icon_arg desktop_filename_arg
    name_desktop="$1"
    exe_file="$2"
    icon_arg="$3"
    desktop_filename_arg="$4"

    # Определяем имя desktop-файла
    if [[ -n "$desktop_filename_arg" ]] && [[ "$desktop_filename_arg" != "nocopy" ]]; then
        desktop_filename="$desktop_filename_arg"
    else
        desktop_filename="$(basename "$exe_file" .exe | sed "s| |_|g")"
    fi

    # Проверяем обязательные аргументы и наличие exe-файла
    if [[ -z "$name_desktop" ]] || [[ -z "$exe_file" ]] ; then
        fatal "Использование: $0 desktop \"Имя ярлыка\" \"/путь/к/файлу.exe\" [иконка|auto] [имя_desktop_файла]"
    elif [[ ! -f "$exe_file" ]] ; then
        print_warning "Для создания ярлыка не найден исполняемый файл: $exe_file"

        local BASENAME_EXE="$(basename "$exe_file")"
        print_info "Запускаем поиск $BASENAME_EXE"
        local FIND_PATH
        if [[ -z "$DRIVE_C" ]] || [[ ! -d "$DRIVE_C" ]]
        then FIND_PATH="$WH_PREFIXES_DIR"
        else FIND_PATH="$DRIVE_C"
        fi
        exe_file="$(find "$FIND_PATH" -type f -not -type l \
                    -not -path "*/windows/*" -not -path "*/dosdevices/*" \
                    -iname "$BASENAME_EXE")"
        if [[ -z "$exe_file" ]] || [[ ! -f "$exe_file" ]]
        then fatal "Для создания ярлыка не найден исполняемый файл: $BASENAME_EXE"
        else print_ok "Исполняемый файл $BASENAME_EXE найден по пути: $(dirname "$exe_file")/"
        fi
    fi

    find_prefix "$exe_file"

    # --- Логика обработки иконки ---
    local user_icons_dir="$WINEPREFIX/icons"
    create_new_dir "$user_icons_dir"

    # Случай 1: Восстановление из бэкапа (передан явный путь)
    if [[ "$RESTORE_FROM_BACKUP" == "1" ]] && [[ -f "$icon_arg" ]]; then
        icon_file="$icon_arg"
    # Случай 2: 'auto' или пустой аргумент - пытаемся извлечь из EXE
    elif [[ -z "$icon_arg" ]] || [[ "$icon_arg" == "auto" ]]; then
        print_info "Попытка извлечь иконку из $exe_file..."
        local png_name="$(basename "$exe_file" .exe).png"
        local extracted_icon_path="$user_icons_dir/$png_name"

        # Проверяем, существует ли иконка, чтобы избежать повторного извлечения
        if [[ -f "$extracted_icon_path" ]]; then
            print_info "Иконка уже существует: $extracted_icon_path"
            icon_file="$extracted_icon_path"
        else
            local tmp_ico_dir="$WH_TMP_DIR/icons_$$" # Используем PID для временного каталога
            create_new_dir "$tmp_ico_dir"
            local ico_name="$(basename "$exe_file" .exe).ico"

            if wrestool -x -t 14 "$exe_file" -o "$tmp_ico_dir/$ico_name" &>/dev/null && \
               icotool -x -i 1 "$tmp_ico_dir/$ico_name" -o "$tmp_ico_dir/$png_name" &>/dev/null && \
               try_copy_file "$tmp_ico_dir/$png_name" "$user_icons_dir/"; then

                icon_file="$extracted_icon_path"
                print_ok "Иконка успешно извлечена и сохранена: $icon_file"
            else
                print_warning "Не удалось извлечь иконку из $exe_file. Используется иконка по умолчанию."
                icon_file="wine" # Запасной вариант
            fi
            try_remove_dir "$tmp_ico_dir"
        fi
    # Случай 3: Передано конкретное имя иконки
    elif [[ -f "$WH_IMAGE_PATH/$icon_arg.png" ]]; then
        icon_file="$WH_IMAGE_PATH/$icon_arg.png"
    # Случай 4: Запасной вариант по умолчанию
    else
        print_info "Иконка '$icon_arg' не найдена. Используется иконка по умолчанию."
        icon_file="wine"
    fi
    # --- Конец логики обработки иконки ---

    # Создаем .desktop файл
    create_new_dir "$WH_MENU_DIR"
    {
        echo "[Desktop Entry]"
        echo "Name=$name_desktop"
        echo "Exec=env \"$RUN_SCRIPT\" \"$exe_file\" %F"
        echo "Type=Application"
        echo "Categories=WineHelper;"
        echo "StartupNotify=true"
        echo "Path=$DATA_PATH"
        echo "Icon=$icon_file"
    } > "$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=wine
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" ]] ; then
        desktop_path="$(xdg-user-dir DESKTOP)"
        print_info "В меню и на рабочем столе создан ярлык: $desktop_filename.desktop"
        cp -f "$USER_WORK_PATH/$desktop_filename.desktop" "$desktop_path"
    else
        print_info "В меню создан ярлык: $desktop_filename.desktop"
    fi

    if [[ -n "$INSTALL_SCRIPT_NAME" ]] \
    && [[ -f "$exe_file" ]]
    then
        {
            echo '#!/usr/bin/env bash'
            echo "# cmd_name: $INSTALL_SCRIPT_NAME"
        } > "$exe_file".whdb
        grep -e "info_" -e "#####" -e "export" -e "var_" "$INSTALL_SCRIPT" \
        | grep -vE "LAUNCH_PARAMETERS|AUTOINSTALL|WIN_FILE_EXEC|echo" \
        | awk '{$1=$1;print}' >> "$exe_file".whdb
        print_info "Создан файл настроек для $exe_file"
    fi
}

remove_desktop () {
    if [[ -n "$1" ]] ; then
        try_remove_file "$USER_WORK_PATH/$1.desktop"
        try_remove_file "$WH_MENU_DIR/$1.desktop"
        try_remove_file "$HOME/.local/share/applications/$1.desktop"
        try_remove_file "$(xdg-user-dir DESKTOP)/$1.desktop"
    fi

    # Удаляем категорию если она пуста
    if [[ -d "$WH_MENU_DIR" ]] && [[ -z "$(ls -A "$WH_MENU_DIR")" ]]; then
        try_remove_dir "$WH_MENU_DIR"
        try_remove_file "$WH_MENU_CATEGORY"
        try_remove_file "$WH_MENU_CONFIG"
    fi

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

}

check_installed_programs () {
    for desktop_file in "$USER_WORK_PATH"/*.desktop ; do
        if [[ ! -f "$desktop_file" ]] ; then
            print_info "Установленные программы не найдены."
            return 1
        fi
        EXE_PATH="$(grep "Exec" "$desktop_file" | awk -F'"' '{print $4}')"
        case "$1" in
            check_only)
                if [[ -z $2 ]] ; then
                    print_error "Нет аргумента для проверки файла запуска!"
                    print_info "Список установленных программ:"
                    check_installed_programs
                    exit 1
                elif [[ "$desktop_file" =~ ${2}.desktop ]] ; then
                    export EXE_PATH
                    return 0
                fi
            ;;
            *)
                if [[ -f "$EXE_PATH.whdb" ]] ; then
                    WH_INFO_RU="$(grep "info_ru:" "$EXE_PATH.whdb" | awk -F"info_ru: " '{print $2}')"
                    WH_PROG_NAME="$(grep "PROG_NAME" "$EXE_PATH.whdb" | awk -F"=" '{print $2}')"
                    printf "\E[36m%s $SCRIPT_NAME run $(basename "$desktop_file" .desktop) %s\e[0m- $WH_PROG_NAME\n"
                    echo -e "$WH_INFO_RU\n"
                fi
            ;;
        esac
    done
    [[ -n $2 ]] && fatal "Не найден файл запуска для $2"
}

run_installed_programs () {
    if check_installed_programs check_only "$1" ; then
        /usr/bin/env bash -c "\"$RUN_SCRIPT\" \"$EXE_PATH\"" &
        exit 0
    fi
}

init_wined3d () {
    if [[ "$USE_RENDERER" != "proton" ]] ; then
        WINED3D_FILES="d3d8 d3d9 d3d10_1 d3d10 d3d10core d3d11 dxgi d3d12 d3d12core"
        for wined3dfiles in $WINED3D_FILES ; do
            try_copy_wine_dll_to_pfx_64 "$wined3dfiles.dll"
            try_copy_wine_dll_to_pfx_32 "$wined3dfiles.dll"
        done
#         if [[ "$USE_RENDERER" == "damavand" ]]
#         then export WINE_D3D_CONFIG="renderer=vulkan"
#         else export WINE_D3D_CONFIG="renderer=gl"
#         fi
        return 0
    else
        return 1
    fi
}

init_dxvk () {
    check_variables USE_DXVK_VER "$1"

    get_dxvk() {
        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 "$DXVK_URL" "$DXVK_PACKAGE" check256sum \
        && unpack "$DXVK_PACKAGE" "$WH_VULKAN_LIBDIR"
        then
            try_remove_file "$DXVK_PACKAGE"
            return 0
        fi
        return 1
    }

    for DXVK_VAR_VER in "$USE_DXVK_VER" $@ ; do
        if [[ ! -d "${WH_VULKAN_LIBDIR}/${DXVK_VAR_VER}" ]] ; then
            get_dxvk "$CLOUD_URL/${DXVK_VAR_VER}.tar.xz" "$DXVK_VAR_VER"
        fi
    done

    if [[ "${WH_USE_WINE_DXGI}" == 1 ]] ; then
        DXVK_FILES="d3d9 d3d10_1 d3d10 d3d11" # dxvk_config openvr_api_dxvk"
        try_copy_wine_dll_to_pfx_64 "dxgi.dll"
        try_copy_wine_dll_to_pfx_32 "dxgi.dll"
    else
        DXVK_FILES="d3d9 d3d10_1 d3d10 d3d11 dxgi" # dxvk_config openvr_api_dxvk"
    fi

    for dxvkfiles in $DXVK_FILES ; do
        try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/${USE_DXVK_VER}/x64/$dxvkfiles.dll"
        if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/${USE_DXVK_VER}/x32/$dxvkfiles.dll"
        then var_winedlloverride_update "$dxvkfiles=n"
        fi
    done
}

init_vkd3d () {
    check_variables USE_VKD3D_VER "$1"

    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 "$VKD3D_URL" "$VKD3D_PACKAGE" check256sum \
        && unpack "$VKD3D_PACKAGE" "$WH_VULKAN_LIBDIR"
        then
            try_remove_file "$VKD3D_PACKAGE"
            return 0
        fi
        return 1
    }

    for VKD3D_VAR_VER in "$USE_VKD3D_VER" $@ ; do
        if [[ ! -d "${WH_VULKAN_LIBDIR}/${VKD3D_VAR_VER}" ]] ; then
            get_vkd3d "$CLOUD_URL/${VKD3D_VAR_VER}.tar.xz" "$VKD3D_VAR_VER"
        fi
    done

    VKD3D_FILES="d3d12 d3d12core libvkd3d-shader-1 libvkd3d-1" # libvkd3d-proton-utils-3
    for vkd3dfiles in $VKD3D_FILES ; do
        try_copy_other_dll_to_pfx_64 "${WH_VULKAN_LIBDIR}/${USE_VKD3D_VER}/x64/$vkd3dfiles.dll"
        if try_copy_other_dll_to_pfx_32 "${WH_VULKAN_LIBDIR}/${USE_VKD3D_VER}/x86/$vkd3dfiles.dll"
        then var_winedlloverride_update "$vkd3dfiles=n"
        fi
    done
}

init_wine_ver () {
    if [[ "$WH_WINE_USE" != system* ]] ; then
        export WINEDIR="$WH_DIST_DIR/$WH_WINE_USE"

        if [[ ! -d "$WINEDIR" ]] ; then
            local download_url wine_package
            download_url="$CLOUD_URL/$WH_WINE_USE.tar.xz"
            wine_package="$WH_TMP_DIR/$WH_WINE_USE.tar.xz"

            try_download "$download_url" "$wine_package" "check256sum"
            unpack "$wine_package" "$WH_DIST_DIR/"
            try_remove_file "$wine_package"

            # Управление структурой подкаталога Proton "files", перемещая содержимое вверх
            if [[ -d "$WINEDIR/files" ]]; then
                print_info "Обнаружена структура каталогов Proton, исправляем пути..."
                mv "$WINEDIR"/files/* "$WINEDIR/"
                rmdir "$WINEDIR/files"
            fi
        fi

        [[ ! -f "$WINEDIR/version" ]] && echo "$WH_WINE_USE" > "$WINEDIR/version"

        export WINE="$WINEDIR/bin/wine"
        export WINELOADER="$WINEDIR/bin/wine"
        export WINESERVER="$WINEDIR/bin/wineserver"

        if [[ -n "${PATH}" ]]
        then export PATH="$WINEDIR/bin:${PATH}"
        else export PATH="$WINEDIR/bin"
        fi

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

        if [[ ! -d "$WINEDIR/lib64/wine" ]] \
        && [[ -d "$WINEDIR/lib/wine/x86_64-unix" ]]
        then
            create_new_dir "$WINEDIR/lib64/"
            try_force_link_dir "$WINEDIR/lib/wine/" "$WINEDIR/lib64/"
        fi

        if [[ -d "$WINEDIR/lib64" ]] \
        && [[ ! -L "$WINEDIR/lib64/wine" ]]
        then
            var_ld_library_path_update "$WINEDIR/lib64"
            export WINEDLLPATH+=":$WINEDIR/lib64/wine"
        fi

        if [[ -d "$WINEDIR/lib/gstreamer-1.0" ]] ; then
            export GST_PLUGIN_SYSTEM_PATH_1_0="$WINEDIR/lib/gstreamer-1.0"
            if [[ -d "$WINEDIR/lib64/gstreamer-1.0" ]] ; then
                export GST_PLUGIN_SYSTEM_PATH_1_0+=":$WINEDIR/lib64/gstreamer-1.0"
            fi
        fi

        if [[ $WH_USE_CPCSP_PROXY == "1" ]] \
        && ! grep -q "$CPCSP_PROXY_VER" "$WINEDIR/cpcsp_proxy.ver"
        then
            CPCSP_PROXY_NAME="wine-cpcsp_proxy-$CPCSP_PROXY_VER"
            CPCSP_PROXY_URL="$CLOUD_URL/$CPCSP_PROXY_NAME.tar.xz"

            try_download "$CPCSP_PROXY_URL" "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" check256sum
            unpack "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" "$WH_TMP_DIR"

            cp -fr "$WH_TMP_DIR/$CPCSP_PROXY_NAME/"i386-* "$WINEDIR/lib/wine/"
            if [[ -d "$WINEDIR/lib64" ]] ; then
                cp -fr "$WH_TMP_DIR/$CPCSP_PROXY_NAME/"x86_64-* "$WINEDIR/lib64/wine/"
            fi
            try_remove_dir "$WH_TMP_DIR/$CPCSP_PROXY_NAME"
            echo "$CPCSP_PROXY_VER" > "$WINEDIR/cpcsp_proxy.ver"
        fi
    else
        # use system WINE
        if [[ ! -f "/usr/bin/wine" ]] ; then
            fatal "system WINE - not found."
        fi
        export WINEDIR="/usr"
        export WINELOADER="wine"
        export WINESERVER="wineserver"
    fi

    print_info "Используется версия wine: $WH_WINE_USE"
}

get_and_set_reg_file () {

    convert_dec_and_hex () {
        local type=$1
        local num=$2

        case "$type" in
            --dec)
            # Преобразование из десятичного в шестнадцатеричный
            echo -n "$(printf "%08x" "$num")" ;;
            --hex)
            # Преобразование из шестнадцатеричного в десятичный
            echo $(( 0x$num )) ;;
            *)
            echo "Неверный тип преобразования. Используйте --dec или --hex." ;;
        esac
        }

    local name_block name_for_find find_block find_file find_line count name_for_new_block name_for_find_old
    local line_reg find_number_line find_check_file name_for_set name_type_reg name_fatal name_add_or_del
    name_add_or_del=$1
    name_block=$2
    name_for_find=$3
    name_type_reg=$4
    name_for_set=$5
    name_for_new_block=$6
    name_for_find_old=$name_for_find
    name_fatal="$name_block $name_for_find"

    case $name_type_reg in
        REG_DWORD)
            if [[ $name_for_find != '@=' ]]
            then name_for_find="\"$name_for_find\"=dword:"
            else name_for_find="@=dword:"
            fi
            name_for_set=$(convert_dec_and_hex --dec "$name_for_set") ;;
        REG_SZ)
            if [[ $name_for_find != '@=' ]]
            then name_for_find="\"$name_for_find\"="
            else name_for_find="@="
            fi
            name_for_set="\"$name_for_set\"" ;;
        *)
            if [[ $name_add_or_del == --delete ]] ; then
                if [[ $name_for_find != '@=' ]]
                then name_for_find="\"$name_for_find\""
                else name_for_find="@="
                fi
            else
                print_error "не задан тип ветки реестра: $name_fatal"
                return 1
            fi ;;
    esac
    name_block=${name_block//\\/\\\\\\\\}
    if [[ -n $name_for_new_block ]] ; then
        find_block=$(grep -n "\[$name_block\]" "$WINEPREFIX/$name_for_new_block.reg")
    else
        find_block=$(grep -n "\[$name_block\]" "$WINEPREFIX/"*.reg)
    fi
    if [[ -n $find_block ]] ; then
        if [[ -n $name_for_new_block ]] ; then
            find_file="$WINEPREFIX/$name_for_new_block.reg"
            find_line=${find_block//:*/}
        else
            find_file=${find_block//:*/}
            find_line=${find_block//$find_file:/}
            find_line=${find_line//:*/}
        fi
        count=-1
        while read -r line_reg ; do
            ((count++))
            if [[ $line_reg =~ $name_for_find ]] ; then
                if [[ $line_reg == $name_for_find$name_for_set ]] ; then
                    return 0
                fi
                find_number_line=$(( count + find_line ))
                find_check_file=1
                break
            fi
            [[ -z $line_reg ]] && break
        done <<< "$(sed -n "$find_line"',$p' "$find_file")"
    fi
    if [[ $name_add_or_del == --add ]] ; then
        if [[ -z $find_block ]] ; then
            if [[ -n $name_for_new_block ]] ; then
                sed -i '$a\\n'\["$name_block"\] "$WINEPREFIX/$name_for_new_block.reg"
                find_file="$WINEPREFIX/$name_for_new_block.reg"
                find_line=$(wc -l "$find_file" | awk -F" " '{print $1}')
                find_line=$(( find_line - 1 ))
            else
                print_error "$name_fatal не найден в файле реестра"
                return 1
            fi
        fi
        if [[ $find_check_file == 1 ]] ; then
            print_info "Меняем $name_for_find_old в ветке реестра: $name_block"
            sed -i "${find_number_line}s|$name_for_find.*|$name_for_find$name_for_set|" "$find_file"
        else
            print_info "Добавляем $name_for_find_old в ветку реестра: $name_block"
            sed -i "$(( find_line + 1 ))a$name_for_find$name_for_set" "$find_file"
        fi
    elif [[ $name_add_or_del == --delete ]] ; then
        [[ $find_check_file != 1 ]] && return 0
        print_info "Удаляем $name_for_find_old из ветки реестра: $name_block"
        sed -i "${find_number_line}d" "$find_file"
    fi
}

wait_wineserver () {
    "$WINESERVER" -w
}

get_base_pfx () {
    local pfx_file_name pfx_file_url pfx_tmp_path
    pfx_file_name="${1}.tar.xz"
    pfx_file_url="$CLOUD_URL/$pfx_file_name"
    pfx_tmp_path="$WH_TMP_DIR/pfx"

    create_new_dir "$pfx_tmp_path"
    print_info "Загрузка архива базового префикса: $pfx_file_name"
    try_download "$pfx_file_url" "$pfx_tmp_path/$pfx_file_name" check256sum
    unpack --skip-xattr "$pfx_tmp_path/$pfx_file_name" "$WINEPREFIX/"
}

check_prefix_var () {
    if [[ -z "$WINEPREFIX" ]] ; then
        local prefixes=()
        local count=1
        for prefix in "$WH_PREFIXES_DIR"/* ; do
            if [[ -d "$prefix" ]]; then
                prefixes+=("$prefix")
                ((count++))
            fi
        done

        if [[ ${#prefixes[@]} -eq 0 ]]
        then fatal "Не найдено ни одного префикса!"
        else print_info "Доступные префиксы WineHelper:"
        fi

        echo "0 - Отмена"

        for ((i=0; i<${#prefixes[@]}; i++)); do
            echo "$((i+1)) - $(basename "${prefixes[$i]}")"
        done

        local max_choice=${#prefixes[@]}
        read -p "Выберите префикс (0-$max_choice): " choice

        if [[ "$choice" == "0" ]]; then
            print_info "Выбор префикса отменен."
            exit 0
        elif [[ "$choice" -ge 1 && "$choice" -le "$max_choice" ]] ; then
            export WINEPREFIX="${prefixes[$choice-1]}"
        else
            fatal "Неверный выбор."
        fi
    fi

    if echo "$WINEPREFIX" | grep -qv '/' ; then
        export WINEPREFIX="$WH_PREFIXES_DIR/$WINEPREFIX"
    fi

    export PREFIX_NAME="$(basename "$WINEPREFIX")"
    print_info "Выбран префикс: $PREFIX_NAME"

    return 0
}

init_wineprefix () {
    check_prefix_var

    if [[ "$WINEARCH" == "win32" ]]
    then check_variables BASE_PFX "defpfx_x86_v01"
    else check_variables BASE_PFX "defpfx_x64_v01"
    fi

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

    if [[ ! -f "$WINEPREFIX/.firstboot" ]] ; then
        create_new_dir "$WINEPREFIX"
        if [[ "$CLEAR_PREFIX" == "1" ]]
        then print_warning "Используется переменная \"CLEAR_PREFIX=1\", которая принудительно создает чистый префикс с установкой компонентов с помощью winetricks."
        elif [[ "$BASE_PFX" != "none" ]]
        then get_base_pfx "$BASE_PFX"
        fi
        print_info "Обновление префикса $WINEPREFIX."
        if [[ -d "$WINEPREFIX/drive_c/windows" ]]
        then "$WINELOADER" wineboot -u
        else "$WINELOADER" wineboot -i
        fi
        touch "$WINEPREFIX/.firstboot"
        wait_wineserver
    fi

    if [[ -f "$WINEPREFIX/system.reg" ]] \
    && [[ -z $(grep "Windows $WH_WINDOWS_VER" "$WINEPREFIX/system.reg") ]]
    then
        if [[ $(echo "$WH_WINDOWS_VER" | sed 's/.*/\L&/') == "xp" ]] \
        && [[ "$WINEARCH" != "win32" ]]
        then export WH_WINDOWS_VER="xp64"
        fi
        "$WINELOADER" winecfg -v $(echo "win${WH_WINDOWS_VER}" | sed 's/.*/\L&/')
        wait_wineserver
        print_info "Windows версия изменена на win${WH_WINDOWS_VER}"
    fi

    if [[ -d "$XUSER_PATH" ]] && [[ ! -d "$DRIVE_C/users/$USER" ]]
    then try_force_link_dir "$XUSER_PATH" "$DRIVE_C/users/$USER"
    elif [[ ! -d "$XUSER_PATH" ]] && [[ -d "$DRIVE_C/users/$USER" ]]
    then try_force_link_dir "$DRIVE_C/users/$USER" "$XUSER_PATH"
    fi

    if [[ ! -f "$WINEPREFIX/.update-timestamp" ]] ; then
        print_info "Обновление префикса $WINEPREFIX."
        "$WINELOADER" wineboot -u
        wait_wineserver
    fi

    if [[ -L "$XUSER_PATH/Desktop" ]]
    then rm -f "$XUSER_PATH/Desktop"
    fi
    create_new_dir "$XUSER_PATH/Desktop"

    if [[ ! -L "$WINEPREFIX/dosdevices/h:" ]]
    then try_force_link_dir "$HOME" "$WINEPREFIX/dosdevices/h:"
    fi

    if [[ $WH_USE_MESA_GL_OVERRIDE == "1" ]] \
    && ! lspci | grep -i nvidia > /dev/null ; then
        export MESA_GL_VERSION_OVERRIDE="3.3"
        export MESA_GLSL_VERSION_OVERRIDE="330"
    fi

    # хак для XRDP сессии
    if [[ $DISPLAY == *:10.0 ]] ; then
        print_warning "Обнаружена сессия XRDP, настраиваем реестр:"
        get_and_set_reg_file --add 'Software\Wine\X11 Driver' 'UseXRandR' 'REG_SZ' "N" "user"
        get_and_set_reg_file --add 'Software\Wine\X11 Driver' 'UseXVidMode' 'REG_SZ' "N" "user"
    else
        get_and_set_reg_file --delete 'Software\Wine\X11 Driver' 'UseXRandR'
        get_and_set_reg_file --delete 'Software\Wine\X11 Driver' 'UseXVidMode'
    fi

    # добавление ассоциаций файлов для запуска нативного приложения из wine
    # пример переменной: WH_XDG_OPEN="txt doc pdf"
    check_variables WH_XDG_OPEN "0"
    local WRAPPER="${WH_TMP_DIR}/wh-xdg-open.sh"
    local XDG_OPEN_REG="Software\Classes\xdg-open\shell\open\command"
    if [[ $WH_XDG_OPEN != "0" ]] ; then
        # проверяем на наличие запрещённых расширений
        local forbidden_extensions="cpl dll exe lnk msi"
        for ext in $WH_XDG_OPEN; do
            if [[ "$forbidden_extensions" =~ $ext ]] ; then
                fatal "Расширение .$ext запрещено для добавления!"
            fi
        done

        # создаем скрипт-обёртку
        {
            echo "#!/usr/bin/env bash"
            echo "unix_path=\$(\"$WINELOADER\" winepath -u \"\$*\")"
            echo "xdg-open \"\$unix_path\""
        } > "$WRAPPER"
        chmod +x "$WRAPPER"

        # добавляем новую команду xdg-open в реестр
        get_and_set_reg_file --add "$XDG_OPEN_REG" '@=' 'REG_SZ' "$WRAPPER %1" "system"

        # добавляем ассоциации файлов для запуска с помощью xdg-open
        for ext in $WH_XDG_OPEN ; do
            get_and_set_reg_file --add "Software\Classes\.$ext" '@=' 'REG_SZ' "xdg-open" "system"
        done
        print_info "Используются ассоциации с нативными приложениями для файлов: \"$WH_XDG_OPEN\""
    else
        # удаление команды xdg-open из реестра
        get_and_set_reg_file --delete "$XDG_OPEN_REG" '@='
        # удаяем скрипт-обёртку
        try_remove_file "$WRAPPER"
    fi

    # настраиваем префикс для работы с cpcsp_proxy
    if [[ $WH_USE_CPCSP_PROXY == "1" ]] ; then
        for cpcsp_proxy_file in "cpcsp_proxy.dll" "cpcsp_proxy_setup.exe" ; do
            try_copy_wine_dll_to_pfx_64 "$cpcsp_proxy_file"
            try_copy_wine_dll_to_pfx_32 "$cpcsp_proxy_file"
        done

        if ! grep -q "cpcsp_proxy.dll" "$WINEPREFIX/system.reg" ; then
            if [[ "$WINEARCH" == "win32" ]]
            then cpcsp_proxy_cmd=("$WINELOADER" "cpcsp_proxy_setup.exe")
            else cpcsp_proxy_cmd=("${WINELOADER}64" "cpcsp_proxy_setup.exe")
            fi

            print_info "Запускаем настройку cpcsp_proxy..."
            unset CPCSP_PROXY_OK
            set -o pipefail
            for ((i=2; i < 6; i++)) ; do
                "${cpcsp_proxy_cmd[@]}" | tee "$WINEPREFIX/cpcsp_setup.log"
                local CPCSP_EXIT_STATUS="${PIPESTATUS[0]}"
                if grep -q "failed to load /opt/cprocsp/" "$WINEPREFIX/cpcsp_setup.log" ; then
                    fatal "Проверьте правильность установки КриптоПро в системе.\n Инструкция: https://www.altlinux.org/CryptoPro"
                fi

                if [[ $CPCSP_EXIT_STATUS == "0" ]] ; then
                    print_info "Настройка cpcsp_proxy успешно завершена."
                    CPCSP_PROXY_OK="1"
                    break
                fi
                print_warning "Попытка $i из 5..."
                wait_wineserver
            done
            wait_wineserver
            try_remove_file "$WINEPREFIX/cpcsp_setup.log"
            [[ $CPCSP_PROXY_OK != "1" ]] && fatal "Ошибка во время настройки cpcsp_proxy."
        fi
    fi

    # Устанавливаем дополнительные шрифты (включая штрих-код)
    local fonts_dir="$DRIVE_C/windows/Fonts"
    local x_fonts_name="extra_fonts_v${EXTRA_FONTS_VER}"

    if [[ $WH_USE_EXTRA_FONTS == "1" ]] \
    && [[ ! -e "$fonts_dir/${x_fonts_name}.installed" ]]
    then
        local x_fonts_url="$CLOUD_URL/${x_fonts_name}.tar.xz"
        local x_fonts_tar="$WH_TMP_DIR/${x_fonts_name}.tar.xz"

        print_info "Устанавливаем дополнительные шрифты..."
        try_download "$x_fonts_url" "$x_fonts_tar" check256sum
        unpack "$x_fonts_tar" "$fonts_dir/"
        touch "$fonts_dir/${x_fonts_name}.installed"
    fi

    echo "# переменные последнего использования префикса:" > "$WINEPREFIX/last.conf"
    for var in WH_WINE_USE BASE_PFX WINEARCH WH_WINDOWS_VER WINEESYNC WINEFSYNC \
    STAGING_SHARED_MEMORY WINE_LARGE_ADDRESS_AWARE WH_USE_SHADER_CACHE WH_USE_WINE_DXGI \
    WINE_CPU_TOPOLOGY USE_RENDERER DXVK_VER VKD3D_VER WH_XDG_OPEN WH_USE_MESA_GL_OVERRIDE
    do
        echo "export $var=\"${!var}\"" >> "$WINEPREFIX/last.conf"
    done
}

kill_autostart () {
    [[ -z "${1}" ]] && fatal "Нет аргумента для функции kill_autostart."
    [[ -z "${2}" ]] && SWAIT=3 || SWAIT="${2}"
    sleep 5
    while ps aux | grep -m 1 -i "$WINESERVER" | grep -v grep &>/dev/null ; do
        if  [[ -z "$(ps aux | grep -m 1 -i "$1" | grep -v grep | awk '{print $2}')" ]] ; then
            print_info "PID для $1 не найден. Ожидаем окончания установки..."
            sleep "${SWAIT}"
        else
            print_ok "PID для $1 найден. Завершаем работу автозапуска приложения."
            "$WINESERVER" -k
            break
        fi
    done
}

use_winetricks () {
    if [[ -n "$INSTALL_DLL" ]] ; then
        WH_DLL_INSTALL="$(echo "$INSTALL_DLL $WH_DLL_INSTALL" | awk '{ for(i=1;i<=NF;i++){a[$i]++} }END{ for(i in a){printf("%s ",i)} }' )"
    fi
    if [[ -n "$WH_DLL_INSTALL" ]] ; then
        WH_DLL_NEED_INSTALL=""
        USE_WT_FROM_DB=0
        [[ ! -f "$WINEPREFIX/winetricks.log" ]] && touch "$WINEPREFIX/winetricks.log"
        for need_install_dll_to_pfx in $WH_DLL_INSTALL "isolate_home" ; do
            if ! grep "$need_install_dll_to_pfx" "$WINEPREFIX/winetricks.log" &>/dev/null ; then
                if [[ -z "$WH_DLL_NEED_INSTALL" ]]
                then WH_DLL_NEED_INSTALL="$need_install_dll_to_pfx"
                else WH_DLL_NEED_INSTALL="$need_install_dll_to_pfx $WH_DLL_NEED_INSTALL"
                fi
                USE_WT_FROM_DB=1
            fi
        done
        if [[ "$USE_WT_FROM_DB" == "1" ]] ; then
            print_info "Пробуем установить компоненты: ${WH_DLL_NEED_INSTALL}"
            print_info "Запускаем WINETRICKS..."
            export WINETRICKS_DOWNLOADER="curl"
            "$WH_WINETRICKS" -q ${WH_DLL_NEED_INSTALL}
            wait_wineserver
        fi
    fi
}

kill_wine () {
    wine_pids=$(ls -l /proc/*/exe 2>/dev/null | grep -E 'wine(64)?-preloader|wineserver' | awk -F/ '{print $3}')

    for pw_kill_pids in ${wine_pids}; do
        if ps cax | grep "${pw_kill_pids}" ; then
            kill -n 9 "${pw_kill_pids}" &>/dev/null
        fi
    done
}

init_database () {
    WHDB_FILE="0"
    if [[ -f "$WIN_FILE_EXEC" ]] ; then
        WHDB="$(basename "$WIN_FILE_EXEC" .exe)"
        if [[ -f "$WIN_FILE_EXEC".whdb ]] ; then
            WHDB_FILE="$WIN_FILE_EXEC".whdb
        else
            orig_IFS="$IFS" && IFS=$'\n'
            if WH_FIND_DB_FILE="$(grep -ilw "#$WHDB.exe" "$WH_DB_DIR"/* )" ; then
                WHDB_FILE="$WH_FIND_DB_FILE"
            fi
            IFS="$orig_IFS"
        fi
    fi

    if [[ "$WHDB_FILE" != "0" ]] ; then
        print_info "Используется файл настроек: $WHDB_FILE"
        . "$WHDB_FILE"
    elif check_prefix_var && [[ -f "$WINEPREFIX/last.conf" ]] ; then
        print_info "Найдены настройки из предыдущего использования префикса: $WINEPREFIX"
        cat "$WINEPREFIX/last.conf"
        . "$WINEPREFIX/last.conf"
    else
        print_warning "Файл настроек не найден. Пропускаем."
    fi
}

prepair_wine () {
    var_winedlloverride_update "winemenubuilder.exe=d"

    if [[ -n "$INSTALL_SCRIPT_NAME" ]]
    then print_info "Используются настройки из скрипта установки: $INSTALL_SCRIPT_NAME"
    else init_database
    fi
    init_wine_ver
    init_wineprefix
    use_winetricks

    if init_wined3d ; then
        :
    else
        init_dxvk "$DXVK_VER"
        init_vkd3d "$VKD3D_VER"
    fi
    [[ "$MANGOHUD" == 1 ]] && MANGOHUD_RUN="mangohud"
}

wine_run () {
    if [[ $WINEARCH == "win32" ]] \
    && file "$WIN_FILE_EXEC" | grep -q "x86-64"
    then fatal "Нельзя запустить 64-битное приложение в 32-битном префиксе!"
    fi

    WIN_FILE_PATH="$(dirname "$WIN_FILE_EXEC")"
    [[ -d "$WIN_FILE_PATH" ]] && cd "$WIN_FILE_PATH"

    if [[ -n $LOG_FILE ]] && [[ -f "$LOG_FILE" ]] ; then
        echo "##### Основные переменные #####" | tee -a "$LOG_FILE"
        env | grep -e "WH_" -e "WINE" -e "DXVK" -e "VKD3D" | tee -a "$LOG_FILE"

        echo "##### Лог WINE #####" | tee -a "$LOG_FILE"
        $MANGOHUD_RUN "$WINELOADER" "$@" $LAUNCH_PARAMETERS 2>&1 | tee -a "$LOG_FILE"
    else
        $MANGOHUD_RUN "$WINELOADER" "$@" $LAUNCH_PARAMETERS
    fi
    wait_wineserver
}

wine_run_install () {
    print_info "Запускаем установку: $1."
    if [[ "$INSTALL_MODE" == "manual" ]]
    then print_warning "Рекомендуется не менять пути для установки приложения!"
    fi
    [[ ! -f "$1" ]] && fatal "Нет файла для установки: $1"
    case "${1,,}" in
        *.exe) wine_run $WINE_WIN_START "$@" ;;
        *.msi) wine_run msiexec /i "$@" ;;
        *.bat|*.cmd) wine_run "$@" ;;
        *) fatal "Не удалось запустить файл $1. Проверьте расширение файла." ;;
    esac
    wait_wineserver
}

run_autoinstall () {
    if [[ $1 == "--clear-pfx" ]] ; then
        export CLEAR_PREFIX="1"
        shift
    elif [[ $2 == "--clear-pfx" ]] ; then
        export CLEAR_PREFIX="1"
    fi

    INSTALL_SCRIPT_NAME="${1,,}"
    if [[ -f "$WH_AUTOINSTALL_DIR/$INSTALL_SCRIPT_NAME" ]] ; then
        INSTALL_SCRIPT="$WH_AUTOINSTALL_DIR/$INSTALL_SCRIPT_NAME"
        WH_INSTALL_MODE="auto"
    elif [[ -f "$WH_MANUALINSTALL_DIR/$INSTALL_SCRIPT_NAME" ]] ; then
        INSTALL_SCRIPT="$WH_MANUALINSTALL_DIR/$INSTALL_SCRIPT_NAME"
        WH_INSTALL_MODE="manual"
    elif [[ -d "$WH_TESTINSTALL_DIR" ]] \
    && [[ -f "$WH_TESTINSTALL_DIR/$INSTALL_SCRIPT_NAME" ]]
    then
        INSTALL_SCRIPT="$WH_TESTINSTALL_DIR/$INSTALL_SCRIPT_NAME"
        WH_INSTALL_MODE="test"
    else
        INSTALL_SCRIPT="0"
    fi
    export INSTALL_SCRIPT INSTALL_MODE

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

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

            print_info "$title"

            awk '
                FNR==1 {
                    if (progname) {
                        printf "\n%s - %s\n%s\n", filename, progname, info
                    }
                    progname=""; info=""; filename=FILENAME
                    sub(".*/", "", filename)
                }
                /info_ru:/ { sub(/.*info_ru: /, ""); info=$0 }
                /PROG_NAME=/ { sub(/.*PROG_NAME=/, ""); progname=$0 }
                END {
                    if (progname) {
                         printf "\n%s - %s\n%s\n", filename, progname, info
                    }
                }
            ' "$dir"/*
        }

        list_install_scripts "$WH_AUTOINSTALL_DIR" "Список программ с возможностью автоматической установки:"
        echo
        list_install_scripts "$WH_MANUALINSTALL_DIR" "Список программ с возможностью установки из существующего дистрибутива:"
    elif [[ "$INSTALL_SCRIPT" != "0" ]] ; then
        source "$INSTALL_SCRIPT" "$@"
        print_info "Завершена установка $INSTALL_SCRIPT_NAME"
    else
        fatal "Скрипт автоматической установки для $INSTALL_SCRIPT_NAME не найден!"
    fi
    echo
}

remove_prefix() {
    export WINEPREFIX="$1"
    if [[ -z "$WINEPREFIX" ]]
    then print_warning "Не указано имя префикса для удаления. Выберите из списка..."
    fi
    check_prefix_var
    if [[ ! -d "$WINEPREFIX" ]]
    then fatal "Префикса \"$PREFIX_NAME\" не существует!"
    fi

    if [[ ! $2 =~ --force|-y ]] ; then
        echo "======================================================"
        print_warning "Вы собираетесь удалить префикс "$PREFIX_NAME"."
        echo "Это приведёт к:"
        echo " - Полному удалению всех данных префикса"
        echo " - Удалению всех программ, установленных в этом префиксе"
        echo " - Удалению связанных ярлыков из меню и рабочего стола"
        echo "======================================================"
        if ! print_confirmation "Продолжить удаление?"
        then exit 1
        fi
    fi

    if try_remove_dir "$WINEPREFIX" ; then
        print_ok "Префикс "$PREFIX_NAME" успешно удален."

        for desktop_file in "$WH_MENU_DIR"/*.desktop; do
            if grep -q "$WINEPREFIX" "$desktop_file"; then
                desktop_name=$(basename "$desktop_file")
                remove_desktop "${desktop_name%.*}"
            fi
        done

        return 0
    else
        print_error "Не удалось удалить префикс "$PREFIX_NAME"."
        return 1
    fi
}

select_wine_version() {
    local sha256_file="$DATA_PATH/sha256sum.list"
    [[ ! -f "$sha256_file" ]] && fatal "Файл с версиями WINE/Proton не найден: $sha256_file"

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

    local options=()
    local total_versions_found=0

    # --- System ---
    local system_wine_display_name="system"
    if command -v wine &>/dev/null; then
        local system_wine_version
        system_wine_version=$(wine --version 2>/dev/null)
        [[ -n "$system_wine_version" ]] && system_wine_display_name="$system_wine_version"
    fi
    options+=("--- System ---")
    options+=("$system_wine_display_name")

    # --- Other versions from sha256sum.list ---
    local current_group=""
    local group_versions=()

    flush_group() {
        if [[ ${#group_versions[@]} -gt 0 ]]; then
            IFS=$'\n' sorted_versions=($(sort -Vr <<<"${group_versions[*]}"))
            unset IFS
            options+=("${sorted_versions[@]}")
            ((total_versions_found+=${#group_versions[@]}))
            group_versions=()
        fi
    }

    while IFS= read -r line; do
        if [[ "$line" =~ ^#+[[:space:]]([^#[:space:]]+)[[:space:]]#* ]]; then
            flush_group
            current_group="${BASH_REMATCH[1]}"
            # Отображаем только группы, которые являются сборками WINE или PROTON
            case "$current_group" in
                WINE|WINE_LG|PROTON_LG|PROTON_STEAM)
                    local pretty_key=$(echo "$current_group" | tr '_' ' ' | sed -e "s/\b\(.\)/\u\1/g")
                    options+=("--- $pretty_key ---")
                    ;;
                *)
                    current_group=""
                    ;;
            esac
        elif [[ -n "$current_group" ]] && [[ "$line" =~ [a-f0-9]{64} ]]; then
            local filename=$(echo "$line" | awk '{print $2}')
            local version_name=${filename%.tar.xz}

            if [[ "$WINEARCH" == "win64" ]]; then
                if [[ "$version_name" =~ (amd64|x86_64|wow64) ]] || ! [[ "$version_name" =~ i[3-6]86 ]]; then
                    group_versions+=("$version_name")
                fi
            else # win32
                group_versions+=("$version_name")
            fi
        fi
    done < "$sha256_file"
    flush_group

    if [[ $total_versions_found -eq 0 ]]; then
        print_warning "Не найдено подходящих версий WINE/Proton для архитектуры $WINEARCH."
        print_warning "Будет использована версия по умолчанию: $WH_WINE_USE"
        return
    fi

    local selectable_options=("Отмена")
    local display_groups=()
    local current_group_items=()
    local choice_idx=0

    flush_current_group() {
        if ((${#current_group_items[@]} > 0)); then
            # Объединяйте элементы с помощью уникального разделителя для последующего разделения
            display_groups+=("$(IFS='@@@'; echo "${current_group_items[*]}")")
            current_group_items=()
        fi
    }

    current_group_items+=(" 0) Отмена создания префикса")

    for opt in "${options[@]}"; do
        if [[ "$opt" == "---"* ]]; then
        flush_current_group
        display_groups+=("$opt")
        else
            ((choice_idx++))
            current_group_items+=(" ${choice_idx}) $opt")
            selectable_options+=("$opt")
        fi
    done
    flush_current_group # Очистка последней группы

    print_info "Выберите версию WINE/Proton для $WINEARCH префикса:"

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

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

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

            for ((i=0; i<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/proton (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
            local selected_opt
            selected_opt="${selectable_options[$user_choice]}"
            if [[ "$selected_opt" == "$system_wine_display_name" ]]; then
                export WH_WINE_USE="system"
            else
                export WH_WINE_USE="$selected_opt"
            fi
            break
        else
            print_error "Неверный выбор. Введите число от 0 до $max_choice."
        fi
    done
    return 0
}

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

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

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

    prefix_name=${prefix_name:-default}

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

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

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

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

    select_wine_version || exit 0

    print_info "Выберите тип создаваемого префикса:"
    echo " 0) Отмена создания префикса"
    echo " 1) Чистый префикс (без библиотек)"
    echo " 2) С рекомендуемыми библиотеками"
    echo
    local pfx_type_choice
    read -p "Ваш выбор [0-2] (по умолчанию 1): " pfx_type_choice
    case "${pfx_type_choice:-1}" in
        0) print_info "Создание префикса отменено." ; exit 0 ;;
        1) export BASE_PFX="none" ;;
        2) ;; # Оставляем BASE_PFX пустым, чтобы init_wineprefix использовал значение по умолчанию
        *) fatal "Неверный выбор. Операция отменена." ;;
    esac

    export WINEPREFIX="$WH_PREFIXES_DIR/$prefix_name"

     if ! init_wine_ver || ! init_wineprefix; then
        fatal "Ошибка инициализации префикса."
    fi

    print_ok "Префикс '$prefix_name' (${WINEARCH}) успешно создан."
}

remove_winehelper () {
    local answer
    if [[ $1 =~ --force|-y ]] ; then
        answer="y"
    else
        echo "======================================================"
        print_warning "Вы собираетесь удалить WineHelper и все связанные с ним данные."
        echo " Это удалит:"
        echo " - Все настройки WineHelper"
        echo " - Все приложения/программы, установленные через WineHelper"
        echo " - Все ярлыки из меню и с рабочего стола, созданные с помощью WineHelper"
        echo "======================================================"
        if print_confirmation "Продолжить?" ; then
            echo "----------------------------------------------"
            print_warning " ВЫ ТОЧНО УВЕРЕНЫ?"
            echo "----------------------------------------------"
            if ! print_confirmation "Продолжить?"
            then exit 1
            fi
        else
            exit 1
        fi
    fi

    # Удаление рабочих каталогов
    try_remove_dir "$USER_WORK_PATH"

    # Удаление файлов меню
    try_remove_dir "$WH_MENU_DIR"
    try_remove_file "$WH_MENU_CATEGORY"
    try_remove_file "$WH_MENU_CONFIG"

    # Удаление desktop-файлов
    for desktop_file in "$(xdg-user-dir DESKTOP)"/*.desktop; do
        if grep -q "Exec=env \"$RUN_SCRIPT\"" "$desktop_file"; then
            try_remove_file "$desktop_file"
        fi
    done

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

    # Удаление символических ссылок
    try_remove_file "$HOME/.winehelper"

    print_info "WineHelper и все связанные данные успешно удалены."
}

create_base_pfx () {
    export WINEPREFIX="$1"
    check_prefix_var

    local prefix_dir drive_c_dir users_dir archive_path
    prefix_dir="$WINEPREFIX"
    drive_c_dir="$prefix_dir/drive_c"
    users_dir="$drive_c_dir/users"
    archive_path="$WH_TMP_DIR/pfx/new_${PREFIX_NAME}.tar.xz"

    try_copy_dir "$prefix_dir" "${prefix_dir}_bak"
    create_new_dir "$WH_TMP_DIR/pfx/"

    for wtlog in workaround isolate internal winxp win2 win7 win10
    do sed -i "/$wtlog/d" "$prefix_dir/winetricks.log"
    done

    if [[ -d "$users_dir/$USER" ]] \
    && [[ ! -L "$users_dir/$USER" ]]
    then
        if [[ -L "$users_dir/xuser" ]]
        then try_remove_dir "$users_dir/xuser/"
        fi
        create_new_dir "$users_dir/xuser/"
        cp -fr "$users_dir/$USER"/* "$users_dir/xuser/"
    fi

    try_remove_file "$prefix_dir/.update-timestamp"
    try_remove_file "$prefix_dir/.firstboot"
    try_remove_file "$prefix_dir/last.conf"
    try_remove_dir "$prefix_dir/dosdevices/"
    try_remove_dir "$users_dir/$USER"
    try_remove_dir "$users_dir/xuser/AppData/Local/Temp/"
    try_remove_dir "$drive_c_dir/ProgramData/Package Cache/"
    try_remove_dir "$drive_c_dir/windows/temp/"
    try_remove_dir "$drive_c_dir/windows/Installer/"

    rm -fr "$drive_c_dir/windows/Microsoft.NET"/*/*/SetupCache/

    cd "$prefix_dir"
    if tar --no-xattrs -c -I 'xz --memlimit=8000MiB -9 -T0' -f "$archive_path" ./* ; then
        print_ok "Архив создан по пути: $archive_path"
        xdg-open "$(dirname "$archive_path")" &
        cd -
    else
        try_remove_file "$archive_path"
        cd -
        fatal "Не удалось создать архив."
    fi
}

backup_prefix() {
    export WINEPREFIX="$1"
    check_prefix_var

    local backup_base_dir backup_archive_name backup_dest_path temp_backup_dir temp_prefix_dir temp_users_dir
    backup_base_dir="$(xdg-user-dir DESKTOP)"
    backup_archive_name="backup_${PREFIX_NAME}_$(date +%d.%m.%Y-%H.%M.%S).whpack"
    backup_dest_path="$backup_base_dir/$backup_archive_name"
    temp_backup_dir="$WH_TMP_DIR/backup_${PREFIX_NAME}_$(date +%d.%m.%Y-%H.%M.%S)"
    temp_prefix_dir="$temp_backup_dir/prefixes/$PREFIX_NAME"
    temp_users_dir="$temp_prefix_dir/drive_c/users"

    print_info "Начало резервного копирования префикса: $PREFIX_NAME"
    create_new_dir "$temp_backup_dir"
    create_new_dir "$temp_backup_dir/prefixes/"
    create_new_dir "$temp_backup_dir/dist/"

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

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

        if [[ -n "$WH_WINE_USE" ]] \
        && [[ $WH_WINE_USE != system* ]] \
        && [[ -d "$WH_DIST_DIR/$WH_WINE_USE" ]]
        then
            print_info "Копирование используемой версии WINE: $WH_WINE_USE"

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

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

    print_info "Создание squashfs архива..."
    if mksquashfs "$temp_backup_dir" "$backup_dest_path" -comp zstd ; then
        print_ok "Архив префикса $PREFIX_NAME успешно создан по пути:\n$backup_dest_path"
    else
        print_error "Не удалось создать архив префикса $PREFIX_NAME."
        try_remove_dir "$temp_backup_dir"
        return 1
    fi

    try_remove_dir "$temp_backup_dir"
    print_info "Создание резервной копии префикса завершено."
    return 0
}

restore_prefix() {
    local backup_archive_path="$1"
    local temp_extract_dir prefix_name


    if [[ -z "$backup_archive_path" ]] ; then
        read -e -p "Укажите путь к архиву резервной копии (/путь/к/архиву.whpack): " backup_archive_path
        backup_archive_path=$(echo "$backup_archive_path" | sed "s/'//g; s/\"//g")
        if [[ -z "$backup_archive_path" ]] ; then
            fatal "Путь к архиву не указан. Восстановление отменено."
        fi
    fi

    if [[ ! -f "$backup_archive_path" ]]
    then fatal "Файл архива не найден: $backup_archive_path"
    fi

    print_info "Восстановление из резервной копии: $backup_archive_path"

    temp_extract_dir="$WH_TMP_DIR/restore_$(basename "$backup_archive_path" .whpack)_$$"
    create_new_dir "$temp_extract_dir"

    print_info "Распаковка архива..."
    if unsquashfs -f -d "$temp_extract_dir" "$backup_archive_path" ; then
        print_ok "Архив успешно распакован."
    else
        try_remove_dir "$temp_extract_dir"
        fatal "Не удалось распаковать архив."
    fi

    # Определяем имя префикса
    if [[ -f "$temp_extract_dir/prefix_name.whmeta" ]]; then
        prefix_name="$(cat "$temp_extract_dir/prefix_name.whmeta")"
        try_remove_file "$temp_extract_dir/prefix_name.whmeta"
    elif grep -q "PREFIX_NAME=" "$backup_archive_path" ; then
        # Обратная совместимость со старыми бэкапами (с приписанной строкой)
        prefix_name="$(tail -n1 "$backup_archive_path" | sed -e 's/.*PREFIX_NAME=//')"
    elif [[ -d "$temp_extract_dir/prefixes" ]] && [[ $(find "$temp_extract_dir/prefixes" -mindepth 1 -maxdepth 1 -type d | wc -l) -eq 1 ]]; then
        # Обратная совместимость со старыми бэкапами (по имени каталога)
        prefix_name="$(basename "$(find "$temp_extract_dir/prefixes" -mindepth 1 -maxdepth 1 -type d)")"
    else
        try_remove_dir "$temp_extract_dir"
        fatal "Не удалось определить имя префикса в архиве (метаданные не найдены)."
    fi

    if [[ -z "$prefix_name" ]]; then
        try_remove_dir "$temp_extract_dir"
        fatal "Не удалось определить имя префикса в архиве."
    fi

    if [[ -d "$WH_PREFIXES_DIR/$prefix_name" ]] ; then
        try_remove_dir "$temp_extract_dir"
        fatal "Префикс $prefix_name уже существует!\n Удалите его, если действительно желаете восстановить префикс заново.\n Команда для удаления: $SCRIPT_NAME remove-prefix $prefix_name"
    fi

    if [[ -d "$temp_extract_dir/prefixes/$prefix_name" ]] ; then
        local prefix_dir="$temp_extract_dir/prefixes/$prefix_name"

        print_info "Восстановление префикса: $prefix_name"

        if [[ -d "$temp_extract_dir/dist" ]] ; then
            print_info "Восстановление версий WINE"
            if cp -fr "$temp_extract_dir/dist"/* "$WH_DIST_DIR/"
            then print_ok "Версии WINE восстановлены."
            else print_warning "Не удалось восстановить версии WINE."
            fi
            try_remove_dir "$temp_extract_dir/dist"
        fi

        if ! mv "$prefix_dir" "$WH_PREFIXES_DIR/" ; then
            try_remove_dir "$temp_extract_dir"
            fatal "Не удалось восстановить префикс $prefix_name."
        else
            print_ok "Префикс $prefix_name восстановлен."

            print_info "Восстановление ярлыков для префикса $prefix_name..."
            export RESTORE_FROM_BACKUP="1"  # Устанавливаем флаг восстановления
            while IFS='=' read -r name_desktop exe_path icon_name ; do

                local full_exe_path="$WH_PREFIXES_DIR/$prefix_name${exe_path}"
                local full_icon_path="$WH_PREFIXES_DIR/$prefix_name/icons/$icon_name"

                if [[ -f "$full_exe_path" ]]; then
                    print_info "Создание ярлыка для $name_desktop"
                    create_desktop "$name_desktop" "$full_exe_path" "$full_icon_path"
                else
                    print_warning "Исполняемый файл для ярлыка '$name_desktop' не найден: $full_exe_path"
                fi
            done < "$WH_PREFIXES_DIR/$prefix_name/desktop.list"
            unset RESTORE_FROM_BACKUP  # Снимаем флаг после завершения
            print_ok "Ярлыки для префикса $prefix_name восстановлены."
        fi
    else
        try_remove_dir "$temp_extract_dir"
        fatal "Директория префикса '$prefix_name' не найдена в архиве."
    fi

    try_remove_dir "$temp_extract_dir"
    print_ok "Полное восстановление префикса $prefix_name завершено."
    return 0
}

update_last_conf_var() {
    local var_name="$1"
    local new_value="$2"
    local conf_file="$WINEPREFIX/last.conf"

    if [[ ! -f "$conf_file" ]] ; then
        print_warning "Файл last.conf не найден, не могу обновить переменную $var_name."
        return 1
    fi

    if grep -q "export $var_name=" "$conf_file"; then
        sed -i "s|^export $var_name=.*|export $var_name=\"$new_value\"|" "$conf_file"
    else
        echo "export $var_name=\"$new_value\"" >> "$conf_file"
    fi
}

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

    print_info "Доступные версии для $component_group:"

    awk -v group="$component_group" '
        /^#+\s*([^#]+?)\s*#*$/ {
            current_group = $0
            gsub(/^#+\s*|\s*#*$/, "", current_group)
        }
        /^[a-f0-9]{64}/ {
            if (current_group == group) {
                filename = $2
                sub(/\.tar\.xz$/, "", filename)
                print " - " filename
            }
        }
    ' "$sha256_file" | sort -Vr
}

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

    local versions=()
    local current_group=""
    while IFS= read -r line; do
        if [[ "$line" =~ ^#+[[:space:]]([^#[:space:]]+)[[:space:]]#* ]] ; then
            current_group="${BASH_REMATCH[1]}"
        elif [[ "$current_group" == "$component_group" ]] && [[ "$line" =~ [a-f0-9]{64} ]] ; then
            local filename
            filename=$(echo "$line" | awk '{print $2}')
            local version_name=${filename%.tar.xz}
            versions+=("$version_name")
        fi
    done < "$sha256_file"

    IFS=$'\n' versions=($(sort -Vr <<<"${versions[*]}"))
    unset IFS

    if [[ ${#versions[@]} -eq 0 ]] ; then
        print_warning "Не найдено доступных версий для $component_group." >&2
        return 1
    fi

    print_info "Выберите версию $component_group для установки:" >&2
    echo >&2

    local items_to_print=(" 0) Отмена")
    for i in "${!versions[@]}" ; do
        items_to_print+=(" $((i+1))) ${versions[$i]}")
    done

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

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

    for ((i=0; i<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_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
    init_wine_ver
    init_wineprefix
    if [[ "$version" == "none" ]] ; then
        print_info "Удаление DXVK..."
        init_wined3d
        update_last_conf_var "DXVK_VER" ""
    else
        init_dxvk "$version"
        update_last_conf_var "DXVK_VER" "$USE_DXVK_VER"
    fi
    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
    init_wine_ver
    init_wineprefix
    if [[ "$version" == "none" ]] ; then
        print_info "Удаление VKD3D..."
        init_wined3d
        update_last_conf_var "VKD3D_VER" ""
    else
        init_vkd3d "$version"
        update_last_conf_var "VKD3D_VER" "$USE_VKD3D_VER"
    fi
    wait_wineserver
}

run_change_wine_version() {
    local new_version="$1"

    check_prefix_var
    init_database

    if [[ -z "$new_version" ]]; then
        select_wine_version || exit 0
        new_version="$WH_WINE_USE"
    else
        export WH_WINE_USE="$new_version"
    fi

    init_wine_ver

    init_wineprefix

    wait_wineserver
    print_ok "Версия Wine для префикса $PREFIX_NAME успешно изменена на $WH_WINE_USE."
}

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

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

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

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

Параметры:
    --help                          показать эту справку и выйти
    --version                       показать информацию о пакете и его версии
    --changelog                     показать историю изменений
    --debug [команда]               включить режим логирования работы WINE

"
}

##### MAIN #####
create_new_dir "$WH_TMP_DIR"
create_new_dir "$WH_DIST_DIR"
create_new_dir "$WH_PREFIXES_DIR"
create_new_dir "$WH_VULKAN_LIBDIR"

if [[ -d "$HOME/.local/share/$SCRIPT_NAME" ]] \
&& [[ ! -L "$HOME/.winehelper" ]]
then try_force_link_dir "$HOME/.local/share/$SCRIPT_NAME" "$HOME/.winehelper"
fi

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

case "$arg1" in
    --version|version) rpm -qi "$SCRIPT_NAME" ; exit 0 ;;
    --help|help) wh_info ; exit 0 ;;
    --changelog|changelog) less "$CHANGELOG_FILE" ; exit 0 ;;
    killall) kill_wine ;;
    winecfg) prepair_wine ; wine_run "winecfg" ;;
    winereg|regedit) prepair_wine ; wine_run "regedit" ;;
    winefile|explorer) prepair_wine ; wine_run "winefile" ;;
    wineconsole|cmd) prepair_wine ; wine_run "wineconsole" ;;
    winetricks) prepair_wine ; "$WH_WINETRICKS" -q "$@" ;;
    desktop) create_desktop "$@" ; exit 0 ;;
    install|-i) run_autoinstall "$@" ;;
    install-dxvk) run_install_dxvk "$@" ;;
    install-vkd3d) run_install_vkd3d "$@" ;;
    change-wine) run_change_wine_version "$@" ;;
    installed) check_installed_programs "$1" ;;
    run|-r) run_installed_programs "$1" ;;
    backup-prefix) backup_prefix "$@" ;;
    restore-prefix) restore_prefix "$@" ;;
    remove-all) remove_winehelper "$@" ;;
    create-prefix) create_prefix "$@" ;;
    remove-prefix) remove_prefix "$@" ;;
    create-base-pfx) create_base_pfx "$@" ;;
    init-prefix) prepair_wine ; wait_wineserver ;;
    *)
        if [[ -f "$arg1" ]] ; then
            WIN_FILE_EXEC="$(readlink -f "$arg1")"
            WIN_FILE_NAME="$(basename "$WIN_FILE_EXEC")"
            find_prefix "$WIN_FILE_EXEC"
            case "${WIN_FILE_NAME,,}" in
                *.exe) prepair_wine ; wine_run $WINE_WIN_START "$WIN_FILE_EXEC" "$@" ;;
                *.msi) prepair_wine ; wine_run msiexec /i "$WIN_FILE_EXEC" "$@" ;;
                *.bat|*.cmd) prepair_wine ; wine_run start "$WIN_FILE_EXEC" "$@" ;;
                *) fatal "Тип файла не поддерживается." ;;
            esac
        else
            print_error "Команды $arg1 не существует."
            wh_info
            exit 1
        fi
    ;;
esac
