#!/usr/bin/env bash

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

##### DEFAULT PATH #####
SCRIPT_NAME="$(basename "$0")"
if [[ "$(realpath "$0")" == "/usr/bin/$SCRIPT_NAME" ]] ; then
    # переменные для установленного WineHelper в систему
    USER_WORK_PATH="$HOME/.local/share/$SCRIPT_NAME"
    RUN_SCRIPT="/usr/bin/$SCRIPT_NAME"
    DATA_PATH="/usr/share/$SCRIPT_NAME"
else
    # переменные для тестового запуска WineHelper из репозитория
    USER_WORK_PATH="$HOME/test-$SCRIPT_NAME"
    RUN_SCRIPT="$(realpath "$0")"
    DATA_PATH="$(dirname "$RUN_SCRIPT")"

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

##### MESSAGES FUNCTIONS #####
print_error () { printf "\E[31m%s Ошибка: $@ %s\e[0m\n" ;}
print_warning () { printf "\E[33m%s Предупреждение: $@ %s\e[0m\n" ;}
print_info () { printf "\E[36m%s Информация: \"$@\" %s\e[0m\n" ;}
print_ok () { printf "\E[35m%s Успех: $@ %s\e[0m\n" ;}
print_var () { for vp in $@ ; do echo "${vp}=${!vp}" ; done ;}
fatal () {
    print_error "$@"
    [[ -n "$WINESERVER" ]] && "$WINESERVER" -w
    exit 1
}

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

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

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

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

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

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

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

# export WINEDLLOVERRIDES=mshtml,mscoree="

check_variables WINEESYNC "0"
check_variables WINEFSYNC "0"

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

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

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

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

check_variables WINE_WIN_START "start /wait /high /unix"

check_variables WINE_CPU_TOPOLOGY "8"

check_variables USE_RENDERER "opengl" # opengl, damavand, proton

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

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

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

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

##### ROOT #####
su_run () {
    print_info "Для продолжения установки введите root пароль..."
    su - -c "$@"
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

try_download () {
    local DOWNLOAD_FILE_URL="${1// /%20}"
    local OUTPUT_FILE="$2"
    local OUTPUT_FILE_NAME="$(basename "$OUTPUT_FILE")"

    print_info "Скачивание файла $OUTPUT_FILE_NAME..."
    if curl -f --progress-bar -A "Mozilla/5.0 (compatible; Konqueror/2.1.1; X11)" \
       -L "$DOWNLOAD_FILE_URL" -o "$OUTPUT_FILE"
    then
        print_ok "Скачивание файла $OUTPUT_FILE_NAME прошло успешно."
        if [[ "$3" == check256sum ]] ; then
            SHA256SUM_EXT=$(sha256sum "$OUTPUT_FILE" | awk '{print $1}')
            SHA256SUM_INT="$(grep "$OUTPUT_FILE_NAME" "$DATA_PATH/sha256sum.list" | awk '{print $1}')"
            if [[ "$SHA256SUM_EXT" == "$SHA256SUM_INT" ]] ; then
                print_ok "Хэш-сумма файла $OUTPUT_FILE_NAME успешно проверена."
                return 0
            else
                fatal "Хэш-сумма файла $OUTPUT_FILE_NAME не совподает!"
                return 1
            fi
        fi
        return 0
    else
        fatal "Скачивание файла: $OUTPUT_FILE_NAME завершилось с ошибкой!"
        return 1
    fi
}

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

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

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

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

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

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

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

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

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

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

create_desktop () {
    local name_desktop="$1"
    local exe_file="$2"
    local desktop_filename="$(basename "$exe_file" .exe | sed "s| |_|")"
    local icon_file
    if [[ "$RESTORE_FROM_BACKUP" == "1" ]] && [[ -f "$3" ]]
    then icon_file="$3"
    elif [[ -f "$WH_IMAGE_PATH/$3.png" ]]
    then icon_file="$WH_IMAGE_PATH/$3.png"
    else icon_file="wine"
    fi

    if [[ -z "$name_desktop" ]] || [[ -z "$exe_file" ]] ; then
        fatal "Used: $SCRIPT_NAME --desktop \"desktop_name\" \"path_to_exe\" \"name_png_from_image\""
    elif [[ ! -f "$exe_file" ]] ; then
        print_warning "Для создания ярлыка не найден исполняемый файл: $exe_file"

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

    # создаем .desktop файл
    create_new_dir "$WH_MENU_DIR"
    {
        echo "[Desktop Entry]"
        echo "Name=$name_desktop"
        echo "Exec=env \"$RUN_SCRIPT\" \"$exe_file\" %F"
        echo "Type=Application"
        echo "Categories=WineHelper;"
        echo "StartupNotify=true"
        echo "Path=$DATA_PATH"
        echo "Icon=$icon_file"
    } > "$USER_WORK_PATH/$desktop_filename.desktop"
    chmod +x "$USER_WORK_PATH/$desktop_filename.desktop"

    cp -f "$USER_WORK_PATH/$desktop_filename.desktop" "$WH_MENU_DIR/"

    if [[ "$RESTORE_FROM_BACKUP" == "1" ]] ; then
        print_info "Пропускаем обновление desktop.list (режим восстановления из бэкапа)"
    else
        # добавляем информацию о приложении в "$WINEPREFIX/desktop.list"
        if [[ -f "$WINEPREFIX/desktop.list" ]] \
        && grep -qe "^${name_desktop}=" "$WINEPREFIX/desktop.list"
        then sed -i "/^$name_desktop=/d" "$WINEPREFIX/desktop.list"
        fi
        create_new_dir "$WINEPREFIX/icons"
        try_copy_file "$icon_file" "$WINEPREFIX/icons/"
        echo "$name_desktop=${exe_file//$WINEPREFIX/}=$(basename "$icon_file")" >> "$WINEPREFIX/desktop.list"
    fi


    # создаем файл категории для меню
    create_new_dir "$HOME/.local/share/desktop-directories"
    if [[ ! -f "$WH_MENU_CATEGORY" ]] ; then
    cat > "$WH_MENU_CATEGORY" <<EOF
[Desktop Entry]
Type=Directory
Name=WineHelper
Icon=wine
EOF
    fi

    # Создаем файл меню для всех приложений
    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 [[ $4 != "nocopy" ]] ; then
        if [[ -n $4 ]] ; then
            local desktop_path="$(xdg-user-dir DESKTOP)/$4"
            create_new_dir "$desktop_path"
            print_info "В меню создан $desktop_filename.desktop и скопирован на рабочий стол в каталог $4"
        else
            local desktop_path="$(xdg-user-dir DESKTOP)"
            print_info "В меню и на рабочем столе создан $desktop_filename.desktop"
        fi
        cp -f "$USER_WORK_PATH/$desktop_filename.desktop" "$desktop_path"
    else
        print_info "В меню создан $desktop_filename.desktop"
    fi

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

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

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

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

}

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

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

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

init_dxvk () {
    check_variables USE_DXVK_VER "$1"

    get_dxvk () {
        DXVK_URL="$1"
        DXVK_PACKAGE="${WH_VULKAN_LIBDIR}/dxvk-${DXVK_VAR_VER}.tar.$(echo ${DXVK_URL#*.tar.})"
        if try_download "$DXVK_URL" "$DXVK_PACKAGE" check256sum \
        && unpack "$DXVK_PACKAGE" "$WH_VULKAN_LIBDIR"
        then
            try_remove_file "$DXVK_PACKAGE"
            return 0
        fi
        return 1
    }

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

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

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

init_vkd3d () {
    check_variables USE_VKD3D_VER "$1"

    get_vkd3d () {
        VKD3D_URL="$1"
        VKD3D_PACKAGE="${WH_VULKAN_LIBDIR}/vkd3d-proton-${VKD3D_VAR_VER}.tar.$(echo ${VKD3D_URL#*.tar.})"
        if try_download "$VKD3D_URL" "$VKD3D_PACKAGE" check256sum \
        && unpack "$VKD3D_PACKAGE" "$WH_VULKAN_LIBDIR"
        then
            try_remove_file "$VKD3D_PACKAGE"
            return 0
        fi
        return 1
    }

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

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

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

        if [[ ! -d "$WINEDIR" ]] ; then
            WINE_URL="$CLOUD_URL/$WH_WINE_USE.tar.xz"
            local WINE_PACKAGE="$WH_TMP_DIR/${WH_WINE_USE}.tar.xz"
            if try_download "$WINE_URL" "$WINE_PACKAGE" check256sum ; then
                if unpack "$WINE_PACKAGE" "$WH_DIST_DIR/" ; then
                    echo "$WH_WINE_USE" > "$WH_DIST_DIR/$WH_WINE_USE/version"
                    try_remove_file "$WINE_PACKAGE"
                fi
            fi
        fi

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

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

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

        export WINEDLLPATH="${WINEDIR}/lib/wine"

        if [[ -d "${WINEDIR}/lib/gstreamer-1.0" ]] ; then
            export GST_PLUGIN_SYSTEM_PATH_1_0="${WINEDIR}/lib/gstreamer-1.0"
        fi

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

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

        if [[ -d "${WINEDIR}/lib64" ]] ; then
            export LD_LIBRARY_PATH+=":${WINEDIR}/lib64"
            export WINEDLLPATH+=":${WINEDIR}/lib64/wine"
            if [[ -d "${WINEDIR}/lib64/gstreamer-1.0" ]] ; then
                export GST_PLUGIN_SYSTEM_PATH_1_0+=":${WINEDIR}/lib64/gstreamer-1.0"
            fi
        fi

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

            if [[ ! -f "$WH_TMP_DIR/$CPCSP_PROXY_NAME" ]] ; then
                print_info "Загрузка $CPCSP_PROXY_NAME.tar.xz"
                if try_download "$CPCSP_PROXY_URL" "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" check256sum
                then unpack "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" "$WH_TMP_DIR"
                else try_remove_file "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz"
                fi
            else
                unpack "$WH_TMP_DIR/$CPCSP_PROXY_NAME.tar.xz" "$WH_TMP_DIR"
            fi

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

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

get_and_set_reg_file () {

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

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

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

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

wait_wineserver () {
    "$WINESERVER" -w
}

get_base_pfx () {
    local FILE_NAME_PFX="$1"
    local PFX_URL="$CLOUD_URL/${FILE_NAME_PFX}.tar.xz"
    local PFX_TMP="$WH_TMP_DIR/pfx"
    create_new_dir "$PFX_TMP"

    if [[ ! -f "$PFX_TMP/$FILE_NAME_PFX.tar.xz" ]] ; then
        print_info "Загрузка базового префикса: ${FILE_NAME_PFX}"
        if try_download "$PFX_URL" "$PFX_TMP/$FILE_NAME_PFX.tar.xz" check256sum
        then unpack "$PFX_TMP/$FILE_NAME_PFX.tar.xz" "$WINEPREFIX/"
        else try_remove_file "$PFX_TMP/$FILE_NAME_PFX.tar.xz"
        fi
    else
        if ! unpack "$PFX_TMP/$FILE_NAME_PFX.tar.xz" "$WINEPREFIX/" ; then
            try_remove_file "${PFX_TMP}/${FILE_NAME_PFX}.tar.xz"
            get_base_pfx "$FILE_NAME_PFX"
        fi
    fi
}

check_prefix_var () {
    if [[ -z "$WINEPREFIX" ]] ; then
        print_warning "Префикс не выбран."

        local prefixes=()
        local count=1
        print_info "Доступные префиксы WineHelper:"
        for prefix in "$WH_PREFIXES_DIR"/* ; do
            if [[ -d "$prefix" ]]; then
                prefixes+=("$prefix")
                echo "$count. $(basename "$prefix")"
                ((count++))
            fi
        done

        [[ ${#prefixes[@]} -eq 0 ]] && fatal "Не найдено ни одного префикса!"

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

        if [[ "$choice" -ge 1 && "$choice" -le "$max_choice" ]] ; then
            export WINEPREFIX="${prefixes[$choice-1]}"
        else
            fatal "Неверный выбор."
        fi
    fi

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

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

    return 0
}

init_wineprefix () {
    check_prefix_var

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

    export DRIVE_C="$WINEPREFIX/drive_c"

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

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

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

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

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

    try_remove_file "$DRIVE_C/windows/system32/winemenubuilder.exe"

    # хак для XRDP сессии
    if [[ $DISPLAY == *:10.0 ]] ; then
        if ! lspci | grep -i nvidia > /dev/null ; then
            export MESA_GL_VERSION_OVERRIDE="3.3"
            export MESA_GLSL_VERSION_OVERRIDE="330"
        fi
        print_warning "Обнаружена сессия XRDP, настраиваем реестр:"
        get_and_set_reg_file --add 'Software\Wine\X11 Driver' 'UseXRandR' 'REG_SZ' "N" "user"
        get_and_set_reg_file --add 'Software\Wine\X11 Driver' 'UseXVidMode' 'REG_SZ' "N" "user"
    else
        get_and_set_reg_file --delete 'Software\Wine\X11 Driver' 'UseXRandR'
        get_and_set_reg_file --delete 'Software\Wine\X11 Driver' 'UseXVidMode'
    fi

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

prepair_wine () {
    if [[ -n "$INSTALL_SCRIPT_NAME" ]] \
    && [[ "$FORCE_INSTALL" != "1" ]]
    then
    print_warning "Лицензионные соглашения использования сторонних компонентов:

Некоторые компоненты, установленные в префикс и необходимые для запуска приложений, могут
быть защищены авторским правом или лицензионными соглашениями. Вы обязаны самостоятельно
убедиться в законности использования этих компонентов в вашей юрисдикции.

Мы не несём ответственности за нарушение лицензионных соглашений, связанное с использованием
подготовленного префикса.

Продолжая использовать данное ПО вы подтверждаете, что ознакомились с данным отказом от
ответственности и принимаете все риски, связанные с его использованием.
"
    print_info "Для продолжения нажмите любую клавишу на клавиатуре."
    read -s -n 1
    fi

    var_winedlloverride_update "winemenubuilder.exe=d"

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

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

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

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

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

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

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

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

    if [[ $INSTALL_SCRIPT_NAME == "list" ]] || [[ -z "$INSTALL_SCRIPT_NAME" ]] ; then
        print_info "Список программ с возможностью автоматической установки:"
        for list in $(ls "$WH_AUTOINSTALL_DIR") ; do
            AI_INFO="$(grep "info_ru:" "$WH_AUTOINSTALL_DIR/$list" | awk -F"info_ru: " '{print $2}')"
            AI_PROGNAME="$(grep "PROG_NAME=" "$WH_AUTOINSTALL_DIR/$list" | awk -F"PROG_NAME=" '{print $2}')"
            echo -e "\n$list - $AI_PROGNAME"
            echo "$AI_INFO"
        done
        echo
        print_info "Список программ с возможностью установки из существующего дистрибутива:"
        for list in $(ls "$WH_MANUALINSTALL_DIR") ; do
            AI_INFO="$(grep "info_ru:" "$WH_MANUALINSTALL_DIR/$list" | awk -F"info_ru: " '{print $2}')"
            AI_PROGNAME="$(grep "PROG_NAME=" "$WH_MANUALINSTALL_DIR/$list" | awk -F"PROG_NAME=" '{print $2}')"
            echo -e "\n$list - $AI_PROGNAME"
            echo "$AI_INFO"
        done
    elif [[ "$INSTALL_SCRIPT" != "0" ]] ; then
        source "$INSTALL_SCRIPT" "$@"
        print_info "Завершена установка $INSTALL_SCRIPT_NAME"
    else
        fatal "Скрипт автоматической установки для $INSTALL_SCRIPT_NAME не найден!"
    fi
    echo
}

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

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

        if [[ ! "$answer" =~ ^[YyДд]$ ]]; then
            print_info "Удаление префикса отменено."
            return 0
        fi
    fi

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

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

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

remove_winehelper () {
    local answer
    if [[ $1 =~ --force|-y ]] ; then
        answer="y"
    else
        echo "======================================================"
        print_warning "Вы собираетесь удалить WineHelper и все связанные с ним данные."
        echo " Это удалит:"
        echo " - Все настройки WineHelper"
        echo " - Все приложения/программы, установленные через WineHelper"
        echo " - Все ярлыки из меню и с рабочего стола созданные с помощью WineHelper"
        echo "======================================================"
        echo "Продолжить? (y/N)"
        read -r answer

        if [[ "$answer" =~ ^[YyДд]$ ]] ; then
            # Второе подтверждение
            echo "----------------------------------------------"
            print_warning " ВЫ ТОЧНО УВЕРЕНЫ?"
            echo "----------------------------------------------"
            echo "Продолжить? (y/N)"
            read -r answer
        else
            print_info "Удаление отменено."
            return 1
        fi
    fi

    if [[ "$answer" =~ ^[YyДд]$ ]] ; then
        # Удаление рабочих каталогов
        try_remove_dir "$USER_WORK_PATH"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

    prefix_name="$(tail -n1 "$backup_archive_path" | sed -e 's/.*PREFIX_NAME=//')"

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

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

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

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

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

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

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

            print_info "Восстановление ярлыков для префикса $prefix_name..."
            export RESTORE_FROM_BACKUP="1"  # Устанавливаем флаг восстановления
            while IFS='=' read -r line; do
                local name_desktop=$(echo "$line" | cut -d'=' -f1)
                local exe_path=$(echo "$line" | cut -d'=' -f2)
                local icon_name=$(echo "$line" | cut -d'=' -f3)

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

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

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

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

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

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

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

"
}

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

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

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

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