Files
winehelper/winehelper
2025-08-24 20:56:34 +06:00

2197 lines
84 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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
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"
CHANGELOG_FILE="$(realpath "/usr/share/doc/winehelper"-*/CHANGELOG)"
WH_ICON_PATH="$DATA_PATH/image/gui/winehelper.svg"
LICENSE_FILE="$(realpath "/usr/share/doc/winehelper"-*/LICENSE)"
AGREEMENT="$(realpath "/usr/share/doc/winehelper"-*/LICENSE_AGREEMENT)"
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"
# минимальная проверка синтаксиса скриптов
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 "$@" && 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
}
extract_icon() {
check_prefix_var
local exe_file="$1"
local ico_name="$(basename "$exe_file" .exe).ico"
local png_name="$(basename "$exe_file" .exe).png"
local tmp_ico_dir="$WH_TMP_DIR/icons"
local user_icons="$WINEPREFIX/icons"
create_new_dir "$tmp_ico_dir"
if ! wrestool -x -t 14 "$exe_file" -o "$tmp_ico_dir/$ico_name" ; then
print_warning "Не удалось извлечь иконку из $exe_file"
try_remove_file "$tmp_ico_dir"
return 1
fi
if ! icotool -x -i 1 "$tmp_ico_dir/$ico_name" -o "$tmp_ico_dir/$png_name" ; then
print_warning "Не удалось извлечь иконку из $ico_name"
try_remove_file "$tmp_ico_dir"
return 1
fi
create_new_dir "$user_icons"
if ! try_copy_file "$tmp_ico_dir/$png_name" "$user_icons" ; then
print_warning "Не удалось копировать иконку в префикс"
try_remove_file "$user_icons"
return 1
fi
try_remove_dir "$tmp_ico_dir"
print_ok "Иконка сохранена: $user_icons/$png_name"
return 0
}
create_desktop () {
local name_desktop exe_file desktop_filename icon_file desktop_path
name_desktop="$1"
exe_file="$2"
if [[ -n $4 ]] && [[ $4 != "nocopy" ]];
then desktop_filename="$4"
else desktop_filename="$(basename "$exe_file" .exe | sed "s| |_|")"
fi
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
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 () {
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
local download_url wine_package_name wine_package check_sum_arg
WINE_METADATA_FILE="$WH_TMP_DIR/wine_metadata.json"
# Сначала пытаемся сформировать URL по старой схеме, как основной для автоустановки
local old_schema_url="$CLOUD_URL/$WH_WINE_USE.tar.xz"
# `curl -f -s -I` делает HEAD-запрос. `-f` заставляет его вернуть код ошибки при 404.
if curl -f -s -I "$old_schema_url" > /dev/null ; then
download_url="$old_schema_url"
elif [[ -f "$WINE_METADATA_FILE" ]]; then
# Если по старому URL ничего нет, ищем в метаданных
print_info "Версия '$WH_WINE_USE' не найдена на основном сервере, ищем в метаданных..."
download_url=$(jq -r --arg name "$WH_WINE_USE" '.[] | .[] | select(.name == $name) | .url' "$WINE_METADATA_FILE" | head -n 1)
else
# Если и метаданных нет, то считаем, что должен был быть старый URL
print_warning "Файл метаданных не найден. Предполагается, что версия '$WH_WINE_USE' находится на основном сервере."
download_url="$old_schema_url"
fi
# Если URL так и не был найден, выводим ошибку.
if [[ -z "$download_url" ]]; then
fatal "Не удалось найти URL для скачивания версии '$WH_WINE_USE'."
fi
wine_package_name="$(basename "$download_url")"
wine_package="$WH_TMP_DIR/$wine_package_name"
# Проверяем хэш-сумму только для файлов с нашего сервера
if [[ "$download_url" == *"$CLOUD_URL"* ]]; then
check_sum_arg="check256sum"
fi
try_download "$download_url" "$wine_package" "$check_sum_arg"
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 "Проверьте правильность установки 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
# Устанавливаем дополнительные шрифты (включая штрих-код)
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
}
generate_wine_metadata () {
WINE_METADATA_FILE="$WH_TMP_DIR/wine_metadata.json"
if [[ -f "$WINE_METADATA_FILE" ]]; then
if find "$WINE_METADATA_FILE" -mmin -1440 | grep -q . ; then
print_info "Файл метаданных $WINE_METADATA_FILE обновлялся менее 24 часов назад. Пропускаем генерацию."
return 0
fi
print_info "Файл метаданных $WINE_METADATA_FILE устарел."
print_info "Начинаем обновление..."
else
print_info "Генерации метаданных..."
fi
TMP_WINE_META="$WH_TMP_DIR/wine_metadata"
local FETCH_ERROR_FLAG="$TMP_WINE_META/fetch.error"
rm -f "$FETCH_ERROR_FLAG"
mkdir -p "$TMP_WINE_META"
cleanup() {
rm -rf "$TMP_WINE_META"
}
trap cleanup EXIT
fetch_github_releases() {
local repo="$1"
local wine_metadata_file="$2"
local url="https://api.github.com/repos/$repo/releases"
if ! curl -s --fail -H "Accept: application/vnd.github.v3+json" "$url" > "$wine_metadata_file"; then
touch "$FETCH_ERROR_FLAG"
return 1
fi
if ! jq -e 'type == "array"' "$wine_metadata_file" >/dev/null 2>&1; then
try_remove_file "$wine_metadata_file"
touch "$FETCH_ERROR_FLAG"
return 1
fi
print_info "Получение данных для $repo"
}
create_wine_entries() {
local input_file="$1"
local file_extension="$2"
local exclude_patterns="$3"
jq -r --arg ext "$file_extension" '
.[] |
.assets[] |
select(.browser_download_url | test($ext)) |
{
name: (.name | gsub($ext; "")),
url: .browser_download_url
}
' "$input_file" | \
if [[ -n "$exclude_patterns" ]]; then
jq -c --arg patterns "$exclude_patterns" '
select(.name | test($patterns) | not)
'
else
jq -c '.'
fi
}
# Формат: "ключ_json;репозиторий;расширениеайлааблон_исключения"
local sources=(
"proton_ge;GloriousEggroll/proton-ge-custom;\\.tar\\.gz$;github-action"
"wine_kron4ek;Kron4ek/Wine-Builds;\\.tar\\.xz$;"
"proton_lg;Castro-Fidel/wine_builds;\\.tar\\.xz$;plugins"
"proton_cachyos;CachyOS/proton-cachyos;\\.tar\\.xz$;znver"
"proton_sarek;pythonlover02/Proton-Sarek;\\.tar\\.gz$;"
"proton_em;Etaash-mathamsetty/Proton;\\.tar\\.xz$;"
)
for source_data in "${sources[@]}"; do
(
IFS=';' read -r key repo extension exclude_pattern <<< "$source_data"
local releases_file="$TMP_WINE_META/${key}_releases.json"
local entries_file="$TMP_WINE_META/${key}.json"
fetch_github_releases "$repo" "$releases_file" || exit 1
create_wine_entries "$releases_file" "$extension" "$exclude_pattern" > "$entries_file"
) &
done
wait
if [[ -f "$FETCH_ERROR_FLAG" ]]; then
fatal "Ошибка при получении релизов. Возможно, превышен лимит запросов к API GitHub или проблема с сетью."
fi
print_ok "Все данные получены."
print_info "Создание итогового JSON файла..."
jq -n \
--slurpfile proton_ge "$TMP_WINE_META/proton_ge.json" \
--slurpfile wine_kron4ek "$TMP_WINE_META/wine_kron4ek.json" \
--slurpfile proton_lg "$TMP_WINE_META/proton_lg.json" \
--slurpfile proton_cachyos "$TMP_WINE_META/proton_cachyos.json" \
--slurpfile proton_sarek "$TMP_WINE_META/proton_sarek.json" \
--slurpfile proton_em "$TMP_WINE_META/proton_em.json" \
'{
proton_ge: $proton_ge,
wine_kron4ek: $wine_kron4ek,
proton_lg: $proton_lg,
proton_cachyos: $proton_cachyos,
proton_sarek: $proton_sarek,
proton_em: $proton_em
}' > "$WINE_METADATA_FILE"
if jq empty "$WINE_METADATA_FILE" 2>/dev/null; then
print_ok "JSON файл создан успешно: $WINE_METADATA_FILE"
else
print_error "Ошибка создания JSON файла"
exit 1
fi
}
select_wine_version() {
if ! command -v jq &> /dev/null; then
print_warning "Команда 'jq' не найдена. Невозможно отобразить список версий WINE/Proton."
print_warning "Будет использована версия по умолчанию: $WH_WINE_USE"
return
fi
generate_wine_metadata
WINE_METADATA_FILE="$WH_TMP_DIR/wine_metadata.json"
[[ ! -f "$WINE_METADATA_FILE" ]] && fatal "Файл метаданных WINE не найден."
local arch_filter_jq
if [[ "$WINEARCH" == "win64" ]]; then
print_info "Фильтруем версии для 64-битного префикса..."
# Для 64-битных префиксов показываем сборки с 'amd64', 'x86_64', 'wow64'
# или те, у которых нет явного указания на 32-битную архитектуру.
arch_filter_jq='select((.name | test("amd64|x86_64|wow64")) or (.name | test("i[3-6]86|x86(?!_64)") | not))'
else # win32
print_info "Фильтруем версии для 32-битного префикса..."
# Для 32-битных префиксов показываем только сборки с явным указанием 32-битной архитектуры.
arch_filter_jq='select(.name | test("i[3-6]86|x86(?!_64)"))'
fi
local options=()
local total_versions_found=0
# --- System ---
options+=("--- System ---")
options+=("system")
# --- Other versions from JSON ---
local group_keys
mapfile -t group_keys < <(jq -r 'keys_unsorted | .[]' "$WINE_METADATA_FILE")
for key in "${group_keys[@]}"; do
local group_versions
mapfile -t group_versions < <(jq -r --arg key "$key" '.[$key] | .[] | '"$arch_filter_jq"' | .name' "$WINE_METADATA_FILE" | sort -V -r | uniq)
if [[ ${#group_versions[@]} -gt 0 ]]; then
# Prettify the group name (e.g., "proton_ge" -> "Proton Ge")
local pretty_key
pretty_key=$(echo "$key" | tr '_' ' ' | sed -e "s/\b\(.\)/\u\1/g")
options+=("--- $pretty_key ---")
options+=("${group_versions[@]}")
((total_versions_found+=${#group_versions[@]}))
fi
done
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
# Помощник для переноса текущей группы элементов в основной массив display_groups
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
local max_choice=$(( ${#selectable_options[@]} - 1 ))
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
print_info "Создание префикса отменено."
exit 0
fi
local selected_opt="${selectable_options[$user_choice]}"
export WH_WINE_USE="$selected_opt"
break
else
print_error "Неверный выбор. Введите число от 0 до $max_choice."
fi
done
}
create_prefix() {
print_info "Существующие префиксы:"
local prefixes=()
for prefix in "$WH_PREFIXES_DIR"/*; do
if [[ -d "$prefix" ]] ; then
prefixes+=("$(basename "$prefix")")
echo " - $(basename "$prefix")"
fi
done
if [[ ${#prefixes[@]} -eq 0 ]]; then
print_info "Нет существующих префиксов."
fi
echo
read -p "Введите имя для нового префикса или 0 для отмены (по умолчанию: default): " prefix_name
if [[ "$prefix_name" == "0" ]] ; then
print_info "Создание префикса отменено."
exit 0
fi
prefix_name=${prefix_name:-default}
if [[ ! "$prefix_name" =~ ^[a-zA-Z0-9_.-]+$ ]] ; then
fatal "Имя префикса может содержать только латинские буквы, цифры, точки, дефисы и подчеркивания"
fi
if [[ -d "$WH_PREFIXES_DIR/$prefix_name" ]] ; then
fatal "Префикс с именем '$prefix_name' уже существует. Создание отменено."
fi
print_info "Создается префикс с именем: \"$prefix_name\""
print_info "Выберите разрядность префикса:"
echo " 0) Отмена создания префикса"
echo " 1) 32-bit"
echo " 2) 64-bit"
echo
local arch_choice
read -p "Ваш выбор [0-2] (по умолчанию 1): " arch_choice
case "${arch_choice:-1}" in
0) print_info "Создание префикса отменено." ; exit 0 ;;
1) export WINEARCH="win32" ;;
2) export WINEARCH="win64" ;;
*) fatal "Неверный выбор. Операция отменена." ;;
esac
select_wine_version
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
}
wh_info () {
echo "Использование: $SCRIPT_NAME [команда]
Команды:
install list список возможных установочных скриптов
install [скрипт] запустить скрипт установки программы
install [скрипт] --clear-pfx не использовать готовый префикс для установки ПО
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 "$@" ;;
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 "$@" ;;
generate-db) generate_wine_metadata "$@" ;;
init-prefix) prepair_wine ; wait_wineserver ;;
*)
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