diff --git a/.gitmodules b/.gitmodules index 1b4b5c46392..9b621258901 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,8 @@ url = https://github.com/BeyondDimension/DirectoryPackages.git [submodule "ref/ArchiSteamFarm"] path = ref/ArchiSteamFarm - url = https://github.com/BeyondDimension/ArchiSteamFarm.git + url = https://github.com/JustArchiNET/ArchiSteamFarm.git + branch = main [submodule "ref/Avalonia.Image2"] path = ref/Avalonia.Image2 url = https://github.com/BeyondDimension/Avalonia.Image2.git diff --git a/build/linux/ISACheck.sh b/build/linux/ISACheck.sh new file mode 100644 index 00000000000..b56fa7b3539 --- /dev/null +++ b/build/linux/ISACheck.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +set -euo pipefail + +# 获取CPU架构 +arch=$(uname -m) + +# 检查CPU指令集扩展 +case $arch in + x86_64) + # 检查是否支持SSE4.2 + if grep -iq 'sse4_2' /proc/cpuinfo; then + echo "该CPU及Linux发行版支持SSE4.2扩展指令集,可以正常运行Watt Toolkit" + else + echo "该CPU或Linux发行版不支持SSE4.2扩展指令集,可能不能正常运行Watt Toolkit!" + exit 1 + fi + ;; + aarch64 | arm64) + # 检查是否支持NEON + if grep -iq -e 'neon' -e 'asimd' /proc/cpuinfo; then + echo "该CPU及Linux发行版支持ARM SIMD扩展指令集(NEON/ASIMD),可以正常运行Watt Toolkit" + else + echo "该CPU或Linux发行版不支持ARM SIMD扩展指令集(NEON/ASIMD),可能不能正常运行Watt Toolkit!" + exit 1 + fi + ;; + loongarch64) + # 检查是否支持LSX和LASX,Watt Toolkit龙芯版的运行同时需要LSX与LASX。先检查Linux内核是否大于6.6 + major=$(uname -r | awk -F '[.-]' '{print $1}') + minor=$(uname -r | awk -F '[.-]' '{print $2}' | sed 's/[^0-9].*//') + minor=${minor:-0} + if [ "$major" -gt 6 ] || { [ "$major" -eq 6 ] && [ "$minor" -ge 6 ]; }; then + if grep -iq 'lsx' /proc/cpuinfo && grep -iq 'lasx' /proc/cpuinfo; then + echo "该CPU及Linux发行版同时支持LSX和LASX扩展指令集,可以正常运行Watt Toolkit" + else + echo "Watt Toolkit所需的.NET需要强制LSX或LASX,该CPU至少缺LSX或LASX不满足要求。" + exit 1 + fi + else + echo "内核版本<6.6,不足以运行Watt Toolkit新世界发行版" + exit 1 + fi + ;; + riscv64) + echo "由于绝大部分RISC-V 64开发版不支持RVV 1.0指令,且检测该指令命令复杂,此处不检测。" + ;; + i?86 | arm*) + # 32位不再受到支持 + echo "Watt Toolkit不再支持32位,32位用户请自行在Github/Gitee下载旧版使用,谢谢。" + exit 1 + ;; + *) + echo "未知CPU架构:$arch" + exit 1 + ;; +esac diff --git a/build/linux/Linux.sh b/build/linux/Linux.sh new file mode 100644 index 00000000000..bbcf80e0eb8 --- /dev/null +++ b/build/linux/Linux.sh @@ -0,0 +1,210 @@ +#!/bin/bash +default_base_path="$HOME/WattToolkit" +base_path="$default_base_path" +[ "$EUID" -eq 0 ] && { echo "此脚本不能以root身份执行。"; exit 1; } +command -v ldd &>/dev/null || { echo "Error: ldd tools is missing, please install ldd in your system."; exit 1; } +ldd --version 2>&1 | grep -qi 'musl' && { echo "Error: Watt Toolkit didn't support Linux distribution which use musl library, please use another tools." >&2; exit 1; } || echo "系统使用Glibc,继续运行..." +command -v dialog &>/dev/null && dialog1="dialog" || dialog1="whiptail" +while true; do + if command -v zenity &>/dev/null; then + custom_base_path=$(zenity --entry --title="安装路径" --text="请输入安装路径(默认为 "$default_base_path",不输入则使用默认路径)") + else + custom_base_path=$($dialog1 --title "安装路径" --inputbox "请输入安装路径(默认为 "$default_base_path",不输入则使用默认路径)" 10 60 3>&1 1>&2 2>&3) + fi + [ -n "$custom_base_path" ] && { base_path="$custom_base_path"; break; } || [ -z "$custom_base_path" ] && break +done +mkdir -p "$base_path" || { echo "无法创建安装路径 $base_path,请检查权限或路径是否正确。"; exit 1; } +cd "$base_path" || { echo "无法切换到安装路径 $base_path,请检查权限或路径是否正确。"; exit 1; } +appVer_path="$base_path/WattToolkit.AppVer" +exec_name="Steam++" +tar_name="WattToolkit.tgz" +tar_path="$base_path/$tar_name" +base_url="https://api.steampp.net" +architecture=1 +app_name="Watt Toolkit" +title="下载 $app_name" +PROCESS_NAMES=("$exec_name" "$app_name") +Check_LC_Code() { + current_lang="${LC_ALL:-$LANG}" + echo "当前语言环境: $current_lang" + if [ "$current_lang" = "C" ] || [ "$current_lang" = "C.UTF-8" ]; then + echo "当前语言环境是 C 或 C.UTF-8,需要修改。" + supported_locales=$(locale -a) + [ -z "$supported_locales" ] && { echo "未能获取到支持的语言环境列表。请检查系统配置。"; return 1; } + for locale in $supported_locales; do + [ "$locale" != "C" ] && [ "$locale" != "C.UTF-8" ] && { export LC_ALL="$locale"; echo "语言环境已修改为 $locale。"; break; } + done + else + echo "当前语言环境不是 C 或 C.UTF-8,无需修改。" + fi +} +Check_LC_Code +Determine_distribution() { + os_id=$(grep "^ID=" /etc/os-release|cut -d'=' -f2|tr -d '"'|tr '[:upper:]' '[:lower:]') + echo "OS ID: $os_id" + case "$os_id" in + "ubuntu"|"debian"|"kali"|"mx"|"devuan"|"pureos"|"parrot"|"trisquel"|"bunsenlabs"|"deepin"|"antix"|"uos"|"kylin"|"openkylin"|"loongnix"|"gxde"|"nfsdesktop") + installprefix="sudo apt install -y"; nssvar="libnss3-tools"; packageupdate="sudo apt update" ;; + "fedora"|"neokylin") installprefix="sudo dnf install -y"; nssvar="nss-tools" ;; + "centos"|"rhel"|"rocky"|"alma"|"amzn"|"alt") installprefix="sudo yum install -y"; nssvar="nss-tools";; + "opensuse") installprefix="sudo zypper install"; nssvar="mozilla-nss-tools"; packageupdate="sudo zypper refresh" ;; + "arch"|"manjaro"|"artix"|"chakra"|"blackarch"|"frugalware") installprefix="sudo pacman -Sy"; nssvar="nss" ;; + "mageia"|"pclinuxos"|"openmandriva"|"rosa"|"vectorlinux") installprefix="sudo urpmi"; nssvar="nss-tools"; packageupdate="sudo urpmi.update -a";; + "slackware"|"salix"|"porteus"|"slacko") installprefix="sudo slackpkg install"; nssvar="nss"; packageupdate="sudo slackpkg update gpg; sudo slackpkg update;";; + "aosc") installprefix="sudo oma install -y"; nssvar="nss" ;; + "gentoo") installprefix="sudo emerge -av"; nssvar="nss"; packageupdate="sudo emerge --sync" ;; + "solus") installprefix="sudo eopkg install"; nssvar="nss-tools"; packageupdate="sudo eopkg update-repo" ;; + *) manualins="1" ;; + esac +} +Determine_distribution +Install_wget() { + if command -v wget &>/dev/null; then + echo "wget 工具已安装。" + elif [ "$manualins" == "1" ]; then + echo "请手动安装 wget 工具。" + else + echo "安装包网上下载需要使用 wget 工具。" + if [ "$os_id" == "gentoo" ]; then + $installprefix net-misc/wget + else + $installprefix wget + fi + echo "wget 工具已安装。" + fi +} +Install_certutil() { + command -v certutil &>/dev/null && echo "certutil 工具已安装。" || { [ "$manualins" == "1" ] && echo "请手动安装 certutil 工具。" || { echo "证书导入以及验证需要使用 certutil 工具。";[-z "$packageupdate"]&&$packageupdate; $installprefix $nssvar;certutil_Init; [ "$os_id" == "loongnix" ] && { sudo ln -s /usr/sbin/setcap /usr/bin/setcap; sudo apt update; } || echo "certutil 工具已安装。"; } } +} +certutil_Init() { + certutil -d $HOME/.pki/nssdb -L || { echo "即将初始化 certutil \$HOME/.pki/nssdb"; mkdir -p $HOME/.pki/nssdb; chmod 700 $HOME/.pki/nssdb; certutil -d $HOME/.pki/nssdb -N --empty-password; } +} +Install_jq() { + command -v jq &>/dev/null && echo "jq 工具已安装。" || { [ "$manualins" == "1" ] && echo "请手动安装 jq 工具。" || { echo "jq 用来解析版本更新。";[-z "$packageupdate"]&&$packageupdate;$installprefix jq; echo "jq 工具已安装。"; } } +} +Install_zenity() { + command -v zenity &>/dev/null && echo "zenity 工具已安装。" || { [ "$manualins" == "1" ] && echo "请手动安装 zenity 工具。" || { echo "安装过程需要 zenity 工具。";[-z "$packageupdate"]&&$packageupdate;$installprefix zenity; echo "zenity 工具已安装。"; } } +} +Show_Run() { + if [ "$os_id" != "yongbao" ]; then zenity --question --text="$1" --width=400; else whiptail --yesno "$1" 10 60; fi + [ $? -eq 0 ] && { echo "程序已启动。"; exit 0 & /bin/sh -c "$base_path/$exec_name.sh"; } || exit 0 +} +Get_NewVer() { + arch=$(uname -m) + case $arch in + x86_64) architecture=1 ;; + aarch64) architecture=3 ;; + loongarch64 | loong64) architecture=6 ;; + i?86 | arm*) zenity --info --text="Watt Toolkit不再支持32位,32位用户请自行在Github/Gitee下载旧版使用,谢谢。" --width=300; exit 244 ;; + *) [ "$os_id" != "yongbao" ] && zenity --info --text="未知的设备架构:$arch!" --width=300 || whiptail --msgbox "未知的设备架构:$arch!" 10 60; exit 244 ;; + esac + [ "$os_id" != "yongbao" ] && os_version=$(grep -E 'VERSION_ID=' /etc/os-release | awk -F'=' '{ print $2 }' | tr -d '"') || os_version=$(grep -E 'VERSION=' /etc/os-release | awk -F'=' '{ print $2 }' | tr -d '"') + [ -z "$os_version" ] && os_version=$(grep -E 'BUILD_ID=' /etc/os-release | awk -F'=' '{ print $2 }' | tr -d '"') + IFS='.' read -ra version_parts <<<"$os_version" + major_version=${version_parts[0]} + minor_version=${version_parts[1]:-0} + case "$os_id" in + "arch" | "manjaro" | "artix" | "chakra" | "blackarch" | "frugalware") wget "$base_url/basic/versions/8/16/$architecture/1/1/-1/0/" -O "$appVer_path" 2>&1 ;; + *) wget "$base_url/basic/versions/8/16/$architecture/$major_version/$minor_version/-1/0/" -O "$appVer_path" 2>&1 ;; + esac + n_sha384=$(jq -r '.["\uD83E\uDD93"].Downloads[0].SHA384' "$appVer_path") + downloads_url=$(jq -r '.["\uD83E\uDD93"].Downloads[0].DownloadUrl' "$appVer_path") + [ -z "$n_sha384" ] && { [ "$os_id" != "yongbao" ] && zenity --info --text="未知的最新版本 Hash:$n_sha384!" --width=300 || whiptail --msgbox "未知的最新版本 Hash:$n_sha384!" 10 60; exit 244; } + [ -f "AppVer" ] && o_sha384=$(cat "AppVer") + [ -e "AppVer" ] && [ "${o_sha384,,}" = "${n_sha384,,}" ] && { Show_Run "已是最新版本,是否启动程序?"; exit 0; } +} +Download_File() { + rm -rf $tar_path + [ "$os_id" != "yongbao" ] && dialog1=zenity || dialog1=whiptail + for i in {1..3}; do + if [ "$os_id" != "yongbao" ]; then + wget "$downloads_url" -O "$tar_path" 2>&1 | sed -u 's/.* \([0-9]\+%\)\ \+\([0-9.]\+.\) \(.*\)/\1\n# 下载中 \2\/s, 剩余时间: \3/' | zenity --progress --title="$title" --auto-close --width=500 + else + wget "$downloads_url" -O "$tar_path" 2>&1 | sed -u 's/.* \([0-9]\+\)%.*/\1/' | whiptail --title "$title" --gauge "正在下载中" 10 60 0 + fi + RUNNING=0 + while [ $RUNNING -eq 0 ]; do + [ -z "$(pidof $dialog1)" ] && { pkill wget; RUNNING=1; } + sleep 0.1 + done + echo 正在计算哈希值 + actual_hash=$(sha384sum "$tar_name" | awk '{ print $1 }') + [ "${actual_hash,,}" = "${n_sha384,,}" ] && { rm "AppVer"; echo "${actual_hash,,}" >>"$base_path/AppVer"; break 2; } + [ "$i" -ge "3" ] && { [ "$os_id" != "yongbao" ] && zenity --error --text="下载错误。" --width=500 || whiptail --msgbox "下载错误。" 10 60; exit 1; } + done +} +Kill_Process() { + Kill_MAX_RETRIES=3 + for process_name in "${PROCESS_NAMES[@]}"; do + kill_retry=1 + while [ $kill_retry -le $Kill_MAX_RETRIES ]; do + pid=$(pgrep "$process_name") + [ -n "$pid" ] && { echo "尝试 $kill_retry: 进程 $process_name 正在运行中。正在终止..."; kill $pid; sleep 2; } || break + kill_retry=$((kill_retry + 1)) + done + done + for process_name in "${PROCESS_NAMES[@]}"; do + pgrep -x "$process_name" >/dev/null && { echo "无法终止程序 $process_name。尝试次数已达上限。"; exit 1; } || echo "程序 $process_name 已成功终止。" + done +} +show_progress() { +if [ "$os_id" != "yongbao" ]; then + zenity --progress --title="$app_name" --text="获取压缩包信息中..." --width=500 --auto-close --no-cancel +else + whiptail --gauge "$app_name" 10 60 0 +fi +} +Decompression() { + echo "正在校验安装包" + { + TOTAL_FILES=$(tar tf "$tar_name" 2>/dev/null | wc -l) + COUNTER=0 + tar -xzvf "$tar_name" 2>/dev/null | while read -r FILE; do + COUNTER=$((COUNTER + 1)) + PERCENTAGE=$((COUNTER * 100 / TOTAL_FILES)) + echo "# 解压 $FILE" + echo "$PERCENTAGE" + done + echo "100" + }| { ([ "$os_id" != "yongbao" ] && zenity --progress --title="安装中" --text="正在解压文件..." --width=500 --percentage=0 --auto-close --no-cancel || whiptail --title "安装中" --gauge "正在解压文件..." 10 60 0)} + rm -f "$appVer_path" &>/dev/null + dotnet_path="$base_path/dotnet" + dotnet_exec="$dotnet_path/dotnet" + [ -x "$dotnet_exec" ] || chmod +x "$dotnet_exec" + chmod +x "$base_path/$exec_name.sh" +} +Install_wget +Install_certutil +[ "$os_id" != "yongbao" ] && Install_zenity || echo 勇豹没有包管理器,不能安装zenity,此处以whiptail代替 +Install_jq +Get_NewVer +if [ -f "$tar_path" ]; then + temp_hash=$(sha384sum "$tar_path" | awk '{ print $1 }') + [ "${temp_hash,,}" != "${n_sha384,,}" ] && Download_File || { rm "$base_path/AppVer"; echo "${temp_hash,,}" >>"$base_path/AppVer"; if [ "$os_id" != "yongbao" ]; then zenity --question --text="本地已有最新安装包是否继续解压?" --width=400; else whiptail --yesno "本地已有最新安装包是否继续解压?" 10 60; fi; [ $? -eq 0 ] && echo "继续解压" || exit 0; } +else + Download_File +fi +Kill_Process +Decompression +InitDesktop() { + XDG_DESKTOP_DIR=$(command -v xdg-user-dir &>/dev/null && xdg-user-dir DESKTOP || echo "$HOME/Desktop") + while true; do + choice=$([ "$os_id" != "yongbao" ] && { zenity --list --radiolist --title="请选择要添加到的位置" --column="选择" --column="路径" TRUE "$XDG_DESKTOP_DIR" FALSE "$HOME/.local/share/applications/";} || { whiptail --title "请选择要添加到的位置" --radiolist "" 10 60 2 "$XDG_DESKTOP_DIR" "" ON "$HOME/.local/share/applications/" "" OFF 3>&1 1>&2 2>&3;} ) + [ "$choice" == "$HOME/.local/share/applications/" ] && { target_dir="$HOME/.local/share/applications/"; break; } || [ "$choice" == "$XDG_DESKTOP_DIR" ] && { target_dir="$XDG_DESKTOP_DIR"; break; } || [ "$os_id" != "yongbao" ] && zenity --info --text="无效选项,请重新选择。" --width=300 || whiptail --msgbox "无效选项,请重新选择。" 10 60 + done + rm -rf "$target_dir/Watt Toolkit.desktop" 2>/dev/null + cat < "$target_dir/Watt Toolkit.desktop" +[Desktop Entry] +Name=Watt Toolkit +Exec=$base_path/$exec_name.sh +Icon=$base_path/Icons/Watt-Toolkit.png +Terminal=false +Type=Application +StartupNotify=false +EOT + chmod +x "$target_dir/Watt Toolkit.desktop" +} +InitDesktop +if [ "$os_id" = "yongbao" ]; then sudo chmod u+s $(which pkexec); fi +Show_Run "下载安装完成,是否启动程序?" +exit 0 diff --git a/build/linux/Steam++.sh b/build/linux/Steam++.sh index 0838ac3e977..656a31384e5 100644 --- a/build/linux/Steam++.sh +++ b/build/linux/Steam++.sh @@ -6,10 +6,30 @@ export DOTNET_ROOT="$dotnet_path" link_exec="$run_path/Steam++" # 判断符号链接是否存在 if [ -L "$link_exec" ]; then - echo "符号链接 $link_exec 已存在" + case $LANG in + zh_CN.UTF-8) + echo "符号链接 $link_exec 已存在" + ;; + en_US.UTF-8) + echo "Symbolic link $link_exec has been existed" + ;; + *) + echo "Symbolic link $link_exec has been existed" + ;; + esac else rm -rf "$link_exec" 2>/dev/null - echo "创建符号链接 $dotnet_exec 到 $link_exec" + case $LANG in + zh_CN.UTF-8) + echo "创建符号链接 $dotnet_exec 到 $link_exec" + ;; + en_US.UTF-8) + echo "Create symbolic link $dotnet_exec to $link_exec" + ;; + *) + echo "Create symbolic link $dotnet_exec to $link_exec" + ;; + esac ln -s "$dotnet_exec" "$link_exec" chmod +x "$link_exec" fi diff --git a/build/linux/environment_check.sh b/build/linux/environment_check.sh index dbfe5844636..b35f2d17af6 100644 --- a/build/linux/environment_check.sh +++ b/build/linux/environment_check.sh @@ -1,6 +1,84 @@ #!/bin/bash base_path="$HOME/WattToolkit" export LC_ALL=en_US.UTF-8 +Determine_distribution() { + # 判断发行版类型 + # 由于Linux发行版包管理器可以混装,如Debian安装Arch Linux的pacman,此处采用/etc/os-release的形式进行一次判断。 + # 读取 /etc/os-release 文件并提取 ID 字段,转换为小写 + # $installprefix是该发行版包管理器安装软件前缀 + # $nssvar是该发行版certutil包名称 + os_id=$(grep "^ID=" /etc/os-release | cut -d'=' -f2 | tr -d '"' | tr '[:upper:]' '[:lower:]') + # 输出 ID + echo "OS ID: $os_id" + + case "$os_id" in + "ubuntu" | "debian" | "kali" | "mx" | "devuan" | "pureos" | "parrot" | "trisquel" | "bunsenlabs" | "deepin" | "antix" | "uos" | "kylin" | "openkylin" | "loongnix" | "gxde" | "nfsdesktop") + echo 默认包管理器:apt + sudo apt update + installprefix="sudo apt install -y" + nssvar="libnss3-tools" + ;; + "fedora" | "neokylin") + echo 默认包管理器:dnf + installprefix="sudo dnf install -y" + nssvar="nss-tools" + ;; + "centos" | "rhel" | "rocky" | "alma" | "amzn" | "alt") + echo 默认包管理器:yum + installprefix="sudo yum install -y" + nssvar="nss-tools" + ;; + "opensuse") + echo 默认包管理器:zypper + sudo zypper refresh + installprefix="sudo zypper install" + nssvar="mozilla-nss-tools" + ;; + "arch" | "manjaro" | "artix" | "chakra" | "blackarch" | "frugalware") + echo 默认包管理器:pacman + installprefix="sudo pacman -Sy" + nssvar="nss" + ;; + "mageia" | "pclinuxos" | "openmandriva" | "rosa" | "vectorlinux") + echo 默认包管理器:urpmi + sudo urpmi.update -a + installprefix="sudo urpmi" + nssvar="nss-tools" + ;; + "slackware" | "salix" | "porteus" | "slacko") + echo 默认包管理器:slackpkg + sudo slackpkg update gpg + sudo slackpkg update + installprefix="sudo slackpkg install" + nssvar="nss" + ;; + "aosc") + echo 默认包管理器:oma + installprefix="sudo oma install -y" + nssvar="nss" + ;; + "gentoo") + echo 默认包管理器:emerge + sudo emerge --sync + installprefix="sudo emerge -av" + nssvar="nss" + ;; + "solus") + echo 默认包管理器:eopkg + sudo eopkg update-repo + installprefix="sudo eopkg install" + nssvar="nss-tools" + ;; + "clearlinux" | "nixos" | "void" | "puppy" | "tinycore" | "yongbao") + # 冷门发行版,手动安装判断变量 + manualins="1" + ;; + *) + echo 未知发行版 + manualins="1" + ;; + esac +} if [ "$1" = "-c" ]; then # 只验证 certutil 是否可用 if command -v certutil &>/dev/null; then @@ -11,32 +89,21 @@ if [ "$1" = "-c" ]; then fi else Install_certutil() { - # 判断发行版类型 if command -v certutil &>/dev/null; then echo "certutil 工具已安装。" + elif [ "$manualins" == "1" ]; then + echo "请手动安装 certutil 工具。" else echo "证书导入以及验证需要使用 certutil 工具。" - # 判断包管理器 - if command -v apt &>/dev/null; then - # 使用 apt (Debian/Ubuntu) + $installprefix $nssvar + # Loongnix 25特殊情况 + if [ "$os_id" == "loongnix" ]; then + sudo ln -s /usr/sbin/setcap /usr/bin/setcap sudo apt update - sudo apt install -y libnss3-tools - elif command -v dnf &>/dev/null; then - # 使用 dnf (Fedora) - sudo dnf install -y nss-tools - elif command -v yum &>/dev/null; then - # 使用 yum (CentOS/Red Hat) - sudo yum install -y nss-tools - elif command -v pacman &>/dev/null; then - # 使用 pacman (Arch Linux) - # sudo pacman -S nss - echo "请手动安装 certutil 工具。" - exit 1 + # sudo apt dist-upgrade else - echo "请手动安装 certutil 工具。" - exit 1 + echo "certutil 工具已安装。" fi - echo "certutil 工具已安装。" fi } certutil_Init() { @@ -52,6 +119,7 @@ else echo "certutil nssdb 正常" fi } + Determine_distribution Install_certutil certutil_Init exit 0 diff --git a/build/linux/init_desktop.sh b/build/linux/init_desktop.sh index 361c4f38a03..f3c954c18ed 100644 --- a/build/linux/init_desktop.sh +++ b/build/linux/init_desktop.sh @@ -3,7 +3,13 @@ run_path=$(dirname "$0") base_path="$(dirname "$run_path")" exec_name="Steam++" -rm -rf "$HOME/Desktop/Watt Toolkit.desktop" 2>/dev/null +# 检查XDG_DESKTOP_DIR环境变量,如果未设置则使用默认值,支持KDE的中文桌面路径 +if command -v xdg-user-dir &>/dev/null; then + XDG_DESKTOP_DIR=$(xdg-user-dir DESKTOP) +else + XDG_DESKTOP_DIR="$HOME/Desktop" +fi +rm -rf "$XDG_DESKTOP_DIR/Watt Toolkit.desktop" 2>/dev/null echo "#!/usr/bin/env xdg-open [Desktop Entry] Name=Watt Toolkit @@ -11,6 +17,6 @@ Exec=$base_path/$exec_name.sh Icon=$base_path/Icons/Watt-Toolkit.png Terminal=false Type=Application -StartupNotify=false" >"$HOME/Desktop/Watt Toolkit.desktop" -chmod +x "$HOME/Desktop/Watt Toolkit.desktop" +StartupNotify=false" >"$XDG_DESKTOP_DIR/Watt Toolkit.desktop" +chmod +x "$XDG_DESKTOP_DIR/Watt Toolkit.desktop" exit 0 diff --git a/build/linux/offline_init.sh b/build/linux/offline_init.sh index 1eb059c7d50..ac28a31d671 100644 --- a/build/linux/offline_init.sh +++ b/build/linux/offline_init.sh @@ -10,33 +10,117 @@ tar_path="$base_path/$tar_name" app_name="Watt Toolkit" PROCESS_NAMES=("$exec_name" "$app_name") export LC_ALL=en_US.UTF-8 -Install_certutil() { +Determine_distribution() { # 判断发行版类型 + # 由于Linux发行版包管理器可以混装,如Debian安装Arch Linux的pacman,此处采用/etc/os-release的形式进行一次判断。 + # 读取 /etc/os-release 文件并提取 ID 字段,转换为小写 + # $installprefix是该发行版包管理器安装软件前缀 + # $nssvar是该发行版certutil包名称 + os_id=$(grep "^ID=" /etc/os-release | cut -d'=' -f2 | tr -d '"' | tr '[:upper:]' '[:lower:]') + # 输出 ID + echo "OS ID: $os_id" + + case "$os_id" in + "ubuntu" | "debian" | "kali" | "mx" | "devuan" | "pureos" | "parrot" | "trisquel" | "bunsenlabs" | "deepin" | "antix" | "uos" | "kylin" | "openkylin" | "loongnix" | "gxde" | "nfsdesktop") + echo 默认包管理器:apt + sudo apt update + installprefix="sudo apt install -y" + nssvar="libnss3-tools" + ;; + "fedora" | "neokylin") + echo 默认包管理器:dnf + installprefix="sudo dnf install -y" + nssvar="nss-tools" + ;; + "centos" | "rhel" | "rocky" | "alma" | "amzn" | "alt") + echo 默认包管理器:yum + installprefix="sudo yum install -y" + nssvar="nss-tools" + ;; + "opensuse") + echo 默认包管理器:zypper + sudo zypper refresh + installprefix="sudo zypper install" + nssvar="mozilla-nss-tools" + ;; + "arch" | "manjaro" | "artix" | "chakra" | "blackarch" | "frugalware") + echo 默认包管理器:pacman + installprefix="sudo pacman -Sy" + nssvar="nss" + ;; + "mageia" | "pclinuxos" | "openmandriva" | "rosa" | "vectorlinux") + echo 默认包管理器:urpmi + sudo urpmi.update -a + installprefix="sudo urpmi" + nssvar="nss-tools" + ;; + "slackware" | "salix" | "porteus" | "slacko") + echo 默认包管理器:slackpkg + sudo slackpkg update gpg + sudo slackpkg update + installprefix="sudo slackpkg install" + nssvar="nss" + ;; + "aosc") + echo 默认包管理器:oma + installprefix="sudo oma install -y" + nssvar="nss" + ;; + "gentoo") + echo 默认包管理器:emerge + sudo emerge --sync + installprefix="sudo emerge -av" + nssvar="nss" + ;; + "solus") + echo 默认包管理器:eopkg + sudo eopkg update-repo + installprefix="sudo eopkg install" + nssvar="nss-tools" + ;; + "clearlinux" | "nixos" | "void" | "puppy" | "tinycore" | "yongbao") + # 冷门发行版,手动安装判断变量 + manualins="1" + ;; + *) + echo 未知发行版 + manualins="1" + ;; + esac +} +Determine_distribution +Install_wget() { + if command -v wget &>/dev/null; then + echo "wget 工具已安装。" + elif [ "$manualins" == "1" ]; then + echo "请手动安装 wget 工具。" + else + echo "安装包网上下载需要使用 wget 工具。" + # Gentoo特殊情况与一般情况 + if [ "$os_id" == "gentoo" ]; then + $installprefix net-misc/wget + else + $installprefix wget + fi + echo "wget 工具已安装。" + fi +} +Install_certutil() { if command -v certutil &>/dev/null; then echo "certutil 工具已安装。" + elif [ "$manualins" == "1" ]; then + echo "请手动安装 certutil 工具。" else echo "证书导入以及验证需要使用 certutil 工具。" - # 判断包管理器 - if command -v apt &>/dev/null; then - # 使用 apt (Debian/Ubuntu) + $installprefix $nssvar + # Loongnix 25特殊情况 + if [ "$os_id" == "loongnix" ]; then + sudo ln -s /usr/sbin/setcap /usr/bin/setcap sudo apt update - sudo apt install -y libnss3-tools - elif command -v dnf &>/dev/null; then - # 使用 dnf (Fedora) - sudo dnf install -y nss-tools - elif command -v yum &>/dev/null; then - # 使用 yum (CentOS/Red Hat) - sudo yum install -y nss-tools - elif command -v pacman &>/dev/null; then - # 使用 pacman (Arch Linux) - # sudo pacman -S nss - echo "请手动安装 certutil 工具。" - exit 1 + # sudo apt dist-upgrade else - echo "请手动安装 certutil 工具。" - exit 1 + echo "certutil 工具已安装。" fi - echo "certutil 工具已安装。" fi } certutil_Init() { @@ -56,55 +140,22 @@ Install_jq() { # Check if jq is already installed if command -v jq &>/dev/null; then echo "jq 工具已安装。" + elif [ "$manualins" == "1" ]; then + echo "请手动安装 jq 工具。" else echo "jq 用来解析版本更新。" - # Check the package manager - if command -v apt &>/dev/null; then - # Using apt (Debian/Ubuntu) - sudo apt update - sudo apt install -y jq - elif command -v dnf &>/dev/null; then - # Using dnf (Fedora) - sudo dnf install -y jq - elif command -v yum &>/dev/null; then - # Using yum (CentOS/Red Hat) - sudo yum install -y jq - elif command -v pacman &>/dev/null; then - # Using pacman (Arch Linux) - sudo pacman -S jq - else - echo "请手动安装 jq 工具。" - exit 1 - fi - echo "请手动安装 jq 工具。" + $installprefix jq + echo "jq 工具已安装。" fi } Install_zenity() { - # 判断发行版类型 if command -v zenity &>/dev/null; then echo "zenity 工具已安装。" + elif [ "$manualins" == "1" ]; then + echo "请手动安装 zenity 工具。" else echo "安装过程需要 zenity 工具。" - # 判断包管理器 - if command -v apt &>/dev/null; then - # 使用 apt (Debian/Ubuntu) - sudo apt update - sudo apt install -y zenity - elif command -v dnf &>/dev/null; then - # 使用 dnf (Fedora) - sudo dnf install -y zenity - elif command -v yum &>/dev/null; then - # 使用 yum (CentOS/Red Hat) - sudo yum install -y zenity - elif command -v pacman &>/dev/null; then - # 使用 pacman (Arch Linux) - # sudo pacman -S zenity - echo "请手动安装 zenity 工具。" - exit 1 - else - echo "请手动安装 zenity 工具。" - exit 1 - fi + $installprefix zenity echo "zenity 工具已安装。" fi } @@ -158,8 +209,9 @@ Kill_Process() { done } +Install_wget Install_certutil -Install_zenity +[ "$os_id" != "yongbao" ] && Install_zenity || echo 勇豹没有包管理器,不能安装zenity,此处以whiptail代替 Install_jq certutil_Init Kill_Process @@ -173,16 +225,23 @@ fi chmod +x "$base_path/$exec_name.sh" # xdg-icon-resource install "$base_path/Icons/Watt-Toolkit.png" --size 128 Watt-Toolkit InitDesktop() { + # 检查XDG_DESKTOP_DIR环境变量,如果未设置则使用默认值,支持KDE的中文桌面路径 + if command -v xdg-user-dir &>/dev/null; then + XDG_DESKTOP_DIR=$(xdg-user-dir DESKTOP) + else + XDG_DESKTOP_DIR="$HOME/Desktop" + fi + while true; do # 使用 zenity 提示用户选择安装路径或使用默认路径 - choice=$(zenity --list --radiolist --title="请选择要添加到的位置" --column="选择" --column="路径" TRUE "$HOME/.local/share/applications/" FALSE "$HOME/Desktop") + choice=$(zenity --list --radiolist --title="请选择要添加到的位置" --column="选择" --column="路径" TRUE "$HOME/.local/share/applications/" FALSE "$XDG_DESKTOP_DIR") # 检查用户输入 if [ "$choice" == "$HOME/.local/share/applications/" ]; then target_dir="$HOME/.local/share/applications/" break - elif [ "$choice" == "$HOME/Desktop" ]; then - target_dir="$HOME/Desktop/" + elif [ "$choice" == "$XDG_DESKTOP_DIR" ]; then + target_dir="$XDG_DESKTOP_DIR" break else echo "无效选项,请输入 1 或 2。" diff --git a/build/linux/online_install.sh b/build/linux/online_install.sh index 10acbaf0a67..32a921edba1 100644 --- a/build/linux/online_install.sh +++ b/build/linux/online_install.sh @@ -9,11 +9,25 @@ if [ "$EUID" -eq 0 ]; then exit 1 fi +#判断系统是使用Glibc库还是musl库 +command -v ldd &>/dev/null || { echo "Error: ldd tools is missing, please install ldd in your system."; exit 1; } +ldd --version 2>&1 | grep -qi 'musl' && { echo "Error: Watt Toolkit didn't support Linux distribution which use musl library, please use another tools." >&2; exit 1; } || echo "系统使用Glibc,继续运行..." + +command -v dialog &>/dev/null && dialog1="dialog" || dialog1="whiptail" # 循环直到用户输入有效路径或直接回车 while true; do - # 使用 zenity 提示用户选择安装路径或使用默认路径 - custom_base_path=$(zenity --entry --title="安装路径" --text="请输入安装路径(默认为 $default_base_path,不输入则使用默认路径)") - + # 使用 zenity 提示用户选择安装路径或使用默认路径(若未安装zenity,调用默认对话框工具) + if command -v zenity &>/dev/null; then + custom_base_path=$(zenity --entry --title="安装路径" --text="请输入安装路径(默认为 "$default_base_path",不输入则使用默认路径)") + else + custom_base_path=$($dialog1 --title "安装路径" --inputbox "请输入安装路径(默认为 "$default_base_path",不输入则使用默认路径)" 10 60 3>&1 1>&2 2>&3) + fi + # 点确定即使不输入也进行下一步,点取消取消安装 + case $? in + 0) ;; + 1) exit 1 ;; + *) echo "发生意外错误" ;; + esac # 如果用户提供了自定义路径,则使用该路径 if [ -n "$custom_base_path" ]; then base_path="$custom_base_path" @@ -86,33 +100,117 @@ Check_LC_Code() { fi } Check_LC_Code -Install_certutil() { +Determine_distribution() { # 判断发行版类型 + # 由于Linux发行版包管理器可以混装,如Debian安装Arch Linux的pacman,此处采用/etc/os-release的形式进行一次判断。 + # 读取 /etc/os-release 文件并提取 ID 字段,转换为小写 + # $installprefix是该发行版包管理器安装软件前缀 + # $nssvar是该发行版certutil包名称 + os_id=$(grep "^ID=" /etc/os-release | cut -d'=' -f2 | tr -d '"' | tr '[:upper:]' '[:lower:]') + # 输出 ID + echo "OS ID: $os_id" + + case "$os_id" in + "ubuntu" | "debian" | "kali" | "mx" | "devuan" | "pureos" | "parrot" | "trisquel" | "bunsenlabs" | "deepin" | "antix" | "uos" | "kylin" | "openkylin" | "loongnix" | "gxde" | "nfsdesktop") + echo 默认包管理器:apt + sudo apt update + installprefix="sudo apt install -y" + nssvar="libnss3-tools" + ;; + "fedora" | "neokylin") + echo 默认包管理器:dnf + installprefix="sudo dnf install -y" + nssvar="nss-tools" + ;; + "centos" | "rhel" | "rocky" | "alma" | "amzn" | "alt") + echo 默认包管理器:yum + installprefix="sudo yum install -y" + nssvar="nss-tools" + ;; + "opensuse") + echo 默认包管理器:zypper + sudo zypper refresh + installprefix="sudo zypper install" + nssvar="mozilla-nss-tools" + ;; + "arch" | "manjaro" | "artix" | "chakra" | "blackarch" | "frugalware") + echo 默认包管理器:pacman + installprefix="sudo pacman -Sy" + nssvar="nss" + ;; + "mageia" | "pclinuxos" | "openmandriva" | "rosa" | "vectorlinux") + echo 默认包管理器:urpmi + sudo urpmi.update -a + installprefix="sudo urpmi" + nssvar="nss-tools" + ;; + "slackware" | "salix" | "porteus" | "slacko") + echo 默认包管理器:slackpkg + sudo slackpkg update gpg + sudo slackpkg update + installprefix="sudo slackpkg install" + nssvar="nss" + ;; + "aosc") + echo 默认包管理器:oma + installprefix="sudo oma install -y" + nssvar="nss" + ;; + "gentoo") + echo 默认包管理器:emerge + sudo emerge --sync + installprefix="sudo emerge -av" + nssvar="nss" + ;; + "solus") + echo 默认包管理器:eopkg + sudo eopkg update-repo + installprefix="sudo eopkg install" + nssvar="nss-tools" + ;; + "clearlinux" | "nixos" | "void" | "puppy" | "tinycore" | "yongbao") + # 冷门发行版,手动安装判断变量 + manualins="1" + ;; + *) + echo 未知发行版 + manualins="1" + ;; + esac +} +Determine_distribution +Install_wget() { + if command -v wget &>/dev/null; then + echo "wget 工具已安装。" + elif [ "$manualins" == "1" ]; then + echo "请手动安装 wget 工具。" + else + echo "安装包网上下载需要使用 wget 工具。" + # Gentoo特殊情况与一般情况 + if [ "$os_id" == "gentoo" ]; then + $installprefix net-misc/wget + else + $installprefix wget + fi + echo "wget 工具已安装。" + fi +} +Install_certutil() { if command -v certutil &>/dev/null; then echo "certutil 工具已安装。" + elif [ "$manualins" == "1" ]; then + echo "请手动安装 certutil 工具。" else echo "证书导入以及验证需要使用 certutil 工具。" - # 判断包管理器 - if command -v apt &>/dev/null; then - # 使用 apt (Debian/Ubuntu) + $installprefix $nssvar + # Loongnix 25特殊情况 + if [ "$os_id" == "loongnix" ]; then + sudo ln -s /usr/sbin/setcap /usr/bin/setcap sudo apt update - sudo apt install -y libnss3-tools - elif command -v dnf &>/dev/null; then - # 使用 dnf (Fedora) - sudo dnf install -y nss-tools - elif command -v yum &>/dev/null; then - # 使用 yum (CentOS/Red Hat) - sudo yum install -y nss-tools - elif command -v pacman &>/dev/null; then - # 使用 pacman (Arch Linux) - # sudo pacman -S nss - echo "请手动安装 certutil 工具。" - exit 1 + # sudo apt dist-upgrade else - echo "请手动安装 certutil 工具。" - exit 1 + echo "certutil 工具已安装。" fi - echo "certutil 工具已安装。" fi } certutil_Init() { @@ -132,56 +230,23 @@ Install_jq() { # Check if jq is already installed if command -v jq &>/dev/null; then echo "jq 工具已安装。" + elif [ "$manualins" == "1" ]; then + echo "请手动安装 jq 工具。" else echo "jq 用来解析版本更新。" - # Check the package manager - if command -v apt &>/dev/null; then - # Using apt (Debian/Ubuntu) - sudo apt update - sudo apt install -y jq - elif command -v dnf &>/dev/null; then - # Using dnf (Fedora) - sudo dnf install -y jq - elif command -v yum &>/dev/null; then - # Using yum (CentOS/Red Hat) - sudo yum install -y jq - elif command -v pacman &>/dev/null; then - # Using pacman (Arch Linux) - sudo pacman -S jq - else - echo "请手动安装 jq 工具。" - exit 1 - fi - echo "请手动安装 jq 工具。" + $installprefix jq + echo "jq 工具已安装。" fi } #精简版系统可能没有该工具 Install_zenity() { - # 判断发行版类型 if command -v zenity &>/dev/null; then echo "zenity 工具已安装。" + elif [ "$manualins" == "1" ]; then + echo "请手动安装 zenity 工具。" else echo "安装过程需要 zenity 工具。" - # 判断包管理器 - if command -v apt &>/dev/null; then - # 使用 apt (Debian/Ubuntu) - sudo apt update - sudo apt install -y zenity - elif command -v dnf &>/dev/null; then - # 使用 dnf (Fedora) - sudo dnf install -y zenity - elif command -v yum &>/dev/null; then - # 使用 yum (CentOS/Red Hat) - sudo yum install -y zenity - elif command -v pacman &>/dev/null; then - # 使用 pacman (Arch Linux) - # sudo pacman -S zenity - echo "请手动安装 zenity 工具。" - exit 1 - else - echo "请手动安装 zenity 工具。" - exit 1 - fi + $installprefix zenity echo "zenity 工具已安装。" fi } @@ -189,7 +254,7 @@ Install_zenity() { Show_Run() { local param1=$1 # 显示提示框,询问是否运行程序 - zenity --question --text="$1" --width=400 + if [ "$os_id" != "yongbao" ]; then zenity --question --text="$1" --width=400; else whiptail --yesno "$1" 10 60; fi # 获取上一个命令的退出码 response=$? @@ -205,24 +270,25 @@ Show_Run() { fi } Get_NewVer() { - #获取系统架构 + #获取系统架构(32位不再受到本软件支持。32位处理器不可能运行勇豹yongbao系统,不做另行判断;“未知的设备架构”处适用于64位处理器与yongbao系统等Linux发行版) arch=$(uname -m) case $arch in x86_64) architecture=1 ;; - i?86) - architecture=0 - ;; - arm*) - architecture=2 - ;; aarch64) architecture=3 ;; + loongarch64 | loong64) + architecture=6 + ;; + i?86 | arm*) + zenity --info --text="Watt Toolkit不再支持32位,32位用户请自行在Github/Gitee下载旧版使用,谢谢。" --width=300 + exit 244 + ;; *) - zenity --info --text="未知的设备架构:$arch!" --width=300 - exit 500 + [ "$os_id" != "yongbao" ] && zenity --info --text="未知的设备架构:$arch!" --width=300 || whiptail --msgbox "未知的设备架构:$arch!" 10 60 + exit 244 ;; esac @@ -234,6 +300,9 @@ Get_NewVer() { os_version=$(cat /etc/os-release | grep -E 'BUILD_ID=' | awk -F'=' '{ print $2 }' | tr -d '"') fi + # 假如是勇豹系统用下面的命令判断 + [ "$os_id" != "yongbao" ] && os_version=$(grep -E 'VERSION_ID=' /etc/os-release | awk -F'=' '{ print $2 }' | tr -d '"') + # 分割版本号 IFS='.' read -ra version_parts <<<"$os_version" @@ -244,18 +313,25 @@ Get_NewVer() { minor_version=0 fi # 通过 SHA384 文件来判断是否需要更新 - wget "$base_url/basic/versions/8/16/$architecture/$major_version/$minor_version/-1/0/" -O "$appVer_path" 2>&1 + # ArchLinux特殊情况与一般情况(ArchLinux版本号为rolling,之前的判断方法会导致下载失败) + case "$os_id" in + "arch" | "manjaro" | "artix" | "chakra" | "blackarch" | "frugalware") + wget "$base_url/basic/versions/8/16/$architecture/1/1/-1/0/" -O "$appVer_path" 2>&1 + ;; + *) + wget "$base_url/basic/versions/8/16/$architecture/$major_version/$minor_version/-1/0/" -O "$appVer_path" 2>&1 + ;; + esac n_sha384=$(jq -r '.["\uD83E\uDD93"].Downloads[0].SHA384' "$appVer_path") downloads_url=$(jq -r '.["\uD83E\uDD93"].Downloads[0].DownloadUrl' "$appVer_path") # 检查 SHA384 值是否为空 if [ "$n_sha384" = "" ]; then - zenity --info --text="未知的最新版本 Hash:$n_sha384!" --width=300 - exit 500 + [ "$os_id" != "yongbao" ] && zenity --info --text="未知的最新版本 Hash:$n_sha384!" --width=300 || whiptail --msgbox "未知的最新版本 Hash:$n_sha384!" 10 60 + exit 244 fi - sleep 1 #本地版本 Hash if [ -f "AppVer" ]; then o_sha384=$(cat "AppVer") @@ -278,21 +354,26 @@ Download_File() { else title="更新" fi + [ "$os_id" != "yongbao" ] && dialog1=zenity || dialog1=whiptail for i in {1..3}; do #下载文件到目标目录 - wget "$downloads_url" -O "$tar_path" 2>&1 | sed -u 's/.* \([0-9]\+%\)\ \+\([0-9.]\+.\) \(.*\)/\1\n# 下载中 \2\/s, 剩余时间: \3/' | zenity --progress --title="$title Watt Toolkit" --auto-close --width=500 + if [ "$os_id" != "yongbao" ]; then + wget "$downloads_url" -O "$tar_path" 2>&1 | sed -u 's/.* \([0-9]\+%\)\ \+\([0-9.]\+.\) \(.*\)/\1\n# 下载中 \2\/s, 剩余时间: \3/' | zenity --progress --title="$title Watt Toolkit" --auto-close --width=500 + else + wget "$downloads_url" -O "$tar_path" 2>&1 | sed -u 's/.* \([0-9]\+\)%.*/\1/' | whiptail --title "$title" --gauge "正在下载中" 10 60 0 + fi RUNNING=0 while [ $RUNNING -eq 0 ]; do - if [ -z "$(pidof zenity)" ]; then + if [ -z "$(pidof $dialog1)" ]; then pkill wget RUNNING=1 fi sleep 0.1 done - sleep 1 # 校验下载文件 Hash + echo 正在校验哈希值 actual_hash=$(sha384sum "$tar_name" | awk '{ print $1 }') if [ "${actual_hash,,}" = "${n_sha384,,}" ]; then rm "AppVer" @@ -301,7 +382,7 @@ Download_File() { fi if [ "$i" -ge "3" ]; then - zenity --error --text="下载错误。" --width=500 + [ "$os_id" != "yongbao" ] && zenity --error --text="下载错误。" --width=500 || whiptail --msgbox "下载错误。" 10 60 exit 1 fi done @@ -340,32 +421,29 @@ Kill_Process() { } Decompression() { - echo "开始解压更新。" - - # 使用 zenity 显示进度条对话框,并将解压命令输出重定向到文件 - tar -xzvf "$tar_name" 2>&1 | - zenity --progress \ - --title="安装中" \ - --text="正在解压 $tar_name..." \ - --percentage=20 \ - --auto-close \ - --width=500 - - # 删除本地版本缓存 + echo "正在校验安装包" + TOTAL_FILES=$(tar tf "$tar_name" 2>/dev/null | wc -l) + { + COUNTER=0 + tar -xzvf "$tar_name" 2>/dev/null | while read -r FILE; do + COUNTER=$((COUNTER + 1)) + PERCENTAGE=$((COUNTER * 100 / TOTAL_FILES)) + echo "# 解压 $FILE" + echo "$PERCENTAGE" + done + echo "100" + }| { ([ "$os_id" != "yongbao" ] && zenity --progress --title="安装中" --text="正在解压文件..." --width=500 --percentage=0 --auto-close --no-cancel || whiptail --title "安装中" --gauge "正在解压文件..." 10 60 0)} rm -f "$appVer_path" &>/dev/null dotnet_path="$base_path/dotnet" dotnet_exec="$dotnet_path/dotnet" - if [ -x "$dotnet_exec" ]; then - echo "文件具有执行权限。" - else - chmod +x "$dotnet_exec" - fi + [ -x "$dotnet_exec" ] || chmod +x "$dotnet_exec" chmod +x "$base_path/$exec_name.sh" } #先安装依赖; +Install_wget Install_certutil -Install_zenity +[ "$os_id" != "yongbao" ] && Install_zenity || echo 勇豹没有包管理器,不能安装zenity,此处以whiptail代替 Install_jq certutil_Init #版本检查更新; @@ -382,7 +460,7 @@ if [ -f "$tar_path" ]; then rm "$base_path/AppVer" #版本号是最新缓存 输出到文件 echo "${temp_hash,,}" >>"$base_path/AppVer" - zenity --question --text="本地已有最新安装包是否继续解压?" --width=400 + if [ "$os_id" != "yongbao" ]; then zenity --question --text="本地已有最新安装包是否继续解压?" --width=400; else whiptail --yesno "本地已有最新安装包是否继续解压?" 10 60; fi # 获取上一个命令的退出码 response=$? @@ -404,15 +482,16 @@ Kill_Process Decompression # xdg-icon-resource install "$base_path/Icons/Watt-Toolkit.png" --size 128 Watt-Toolkit InitDesktop() { - # 检查XDG_DESKTOP_DIR环境变量,如果未设置则使用默认值 - XDG_DESKTOP_DIR="${XDG_DESKTOP_DIR:-$HOME/Desktop}" + # 检查XDG_DESKTOP_DIR环境变量,如果未设置则使用默认值,支持KDE的中文桌面路径 + if command -v xdg-user-dir &>/dev/null; then + XDG_DESKTOP_DIR=$(xdg-user-dir DESKTOP) + else + XDG_DESKTOP_DIR="$HOME/Desktop" + fi while true; do # 使用 zenity 提示用户选择安装路径或使用默认路径 - choice=$(zenity --list --radiolist --title="请选择要添加到的位置" \ - --column="选择" --column="路径" \ - TRUE "$XDG_DESKTOP_DIR" \ - FALSE "$HOME/.local/share/applications/") + choice=$([ "$os_id" != "yongbao" ] && { zenity --list --radiolist --title="请选择要添加到的位置" --column="选择" --column="路径" TRUE "$XDG_DESKTOP_DIR" FALSE "$HOME/.local/share/applications/";} || { whiptail --title "请选择要添加到的位置" --radiolist "" 10 60 2 "$XDG_DESKTOP_DIR" "" ON "$HOME/.local/share/applications/" "" OFF 3>&1 1>&2 2>&3;} ) # 检查用户输入 if [ "$choice" == "$HOME/.local/share/applications/" ]; then @@ -423,7 +502,7 @@ InitDesktop() { break else # 无效选项时给出提示,并继续循环 - zenity --info --text="无效选项,请重新选择。" + [ "$os_id" != "yongbao" ] && zenity --info --text="无效选项,请重新选择。" --width=300 || whiptail --msgbox "无效选项,请重新选择。" 10 60 fi done @@ -445,5 +524,6 @@ EOT InitDesktop # update-desktop-database ~/.local/share/applications #运行程序 +if [ "$os_id" = "yongbao" ]; then sudo chmod u+s $(which pkexec); fi Show_Run "下载安装完成,是否启动程序?" exit 0 diff --git a/build/linux/uninstall.sh b/build/linux/uninstall.sh index 128fd0d38b6..155c16acf7c 100644 --- a/build/linux/uninstall.sh +++ b/build/linux/uninstall.sh @@ -95,5 +95,11 @@ fi rm -rf $Cache 2>/dev/null rm -rf $Cache_t 2>/dev/null rm -rf $base_path 2>/dev/null -rm -rf "$HOME/Desktop/Watt Toolkit.desktop" 2>/dev/null +# 检查XDG_DESKTOP_DIR环境变量,如果未设置则使用默认值,支持KDE的中文桌面路径 +if command -v xdg-user-dir &>/dev/null; then + XDG_DESKTOP_DIR=$(xdg-user-dir DESKTOP) +else + XDG_DESKTOP_DIR="$HOME/Desktop" +fi +rm -rf "$XDG_DESKTOP_DIR/Watt Toolkit.desktop" 2>/dev/null zenity --info --text="卸载完成!" --width=300 diff --git a/build/loongarch build/neko_build.sh b/build/loongarch build/neko_build.sh new file mode 100644 index 00000000000..541d8c136ce --- /dev/null +++ b/build/loongarch build/neko_build.sh @@ -0,0 +1,159 @@ +#!/bin/bash +set -e + +# ========== 添加龙芯 NuGet 源(如尚未添加)========== +echo "Configuring NuGet sources..." +if ! dotnet nuget list source | grep -q "lnuget.loongnix.cn"; then + dotnet nuget add source -n lnuget.loongnix.cn --protocol-version 3 \ + https://lnuget.loongnix.cn/v3/index.json \ + --allow-insecure-connections + echo "Added loongarch64 NuGet source." +else + echo "Loongarch64 NuGet source already configured." +fi + +# ========== 覆盖中央包管理:显式添加指定版本的原生库 ========== +echo "Adding explicit native package versions (overriding Central Package Management)..." + +# SkiaSharp 和 HarfBuzzSharp:官方源已有 loongarch64 支持 +dotnet add src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj \ + package SkiaSharp.NativeAssets.Linux --version 3.119.4 + +dotnet add src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj \ + package HarfBuzzSharp.NativeAssets.Linux --version 8.3.1.5 + +# SQLite:必须从龙芯源获取 loongarch64 原生库 +dotnet add src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj \ + package SQLitePCLRaw.lib.e_sqlite3 --version 2.1.11 \ + --source https://lnuget.loongnix.cn/v3/index.json + +# 还原工作负载和包 +dotnet workload restore src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj + +# ========== 发布主程序 ========== +echo "Publishing main application..." +dotnet publish -c Release src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj \ + -p:UseAppHost=false \ + -p:PublishDir=realbuild/assemblies \ + -p:PublishSingleFile=false \ + -p:PublishReadyToRun=false \ + -p:PublishDocumentationFile=false \ + -p:PublishDocumentationFiles=false \ + -p:PublishReferencesDocumentationFiles=false \ + -f net10.0 \ + -r linux-loongarch64 \ + -v q \ + /property:WarningLevel=1 \ + --sc false \ + --force \ + --nologo \ + -o "realbuild/assemblies" + +# ========== 发布插件 ========== +echo "Publishing plugins..." + +dotnet publish -c Release src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy.csproj \ + -p:UseAppHost=true \ + -p:PublishDir=realbuild/modules/Accelerator \ + -p:PublishSingleFile=true \ + -p:PublishReadyToRun=false \ + -p:PublishDocumentationFile=false \ + -p:PublishDocumentationFiles=false \ + -p:PublishReferencesDocumentationFiles=false \ + -f net10.0 \ + -r linux-loongarch64 \ + -v q \ + /property:WarningLevel=1 \ + --sc false \ + --force \ + --nologo \ + -o "realbuild/modules/Accelerator" + +dotnet build -c Release src/BD.WTTS.Client.Plugins.GameAccount/BD.WTTS.Client.Plugins.GameAccount.csproj \ + --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly \ + -o "realbuild/modules/GameAccount" + +dotnet build -c Release src/BD.WTTS.Client.Plugins.GameList/BD.WTTS.Client.Plugins.GameList.csproj \ + --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly \ + -o "realbuild/modules/GameList" + +dotnet build -c Release src/BD.WTTS.Client.Plugins.Authenticator/BD.WTTS.Client.Plugins.Authenticator.csproj \ + --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly \ + -o "realbuild/modules/Authenticator" + +dotnet build -c Release src/BD.WTTS.Client.Plugins.SteamIdleCard/BD.WTTS.Client.Plugins.SteamIdleCard.csproj \ + --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly \ + -o "realbuild/modules/SteamIdleCard" + +dotnet build -c Release src/BD.WTTS.Client.Plugins.Accelerator/BD.WTTS.Client.Plugins.Accelerator.csproj \ + --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly \ + -o "realbuild/modules/Accelerator" + +# dotnet test src/BD.WTTS.UnitTest/BD.WTTS.UnitTest.csproj -c Release -p:GeneratePackageOnBuild=false --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly -a loongarch64 -o "realbuild/" + +echo "Publish Done!" +echo "Start Copy File" + +# ========== 组装输出目录 ========== +mkdir -p WattToolkit +cp -a realbuild/assemblies WattToolkit/ + +cd WattToolkit +mkdir -p modules +cd modules +mkdir -p Accelerator Authenticator GameAccount GameList SteamIdleCard + +cp "../../realbuild/modules/Accelerator/BD.WTTS.Client.Plugins.Accelerator.dll" \ + "../../realbuild/modules/Accelerator/Steam++.Accelerator" Accelerator/ +cp "../../realbuild/modules/Authenticator/BD.WTTS.Client.Plugins.Authenticator.dll" Authenticator/ +cp "../../realbuild/modules/GameAccount/BD.WTTS.Client.Plugins.GameAccount.dll" GameAccount/ +cp "../../realbuild/modules/GameList/BD.WTTS.Client.Plugins.GameList.dll" GameList/ +cp "../../realbuild/modules/SteamIdleCard/BD.WTTS.Client.Plugins.SteamIdleCard.dll" SteamIdleCard/ + +cd .. +mkdir -p dotnet +dotnetloc=$(readlink -f $(which dotnet)) +cp -a "$dotnetloc" \ + "$(dirname "$dotnetloc")/host" \ + "$(dirname "$dotnetloc")/shared" \ + "$(dirname "$dotnetloc")/LICENSE.txt" \ + "$(dirname "$dotnetloc")/ThirdPartyNotices.txt" \ + dotnet/ + +# ========== 严格模式:原生库必须从 publish 输出复制 ========== +mkdir -p native/linux-loongarch64 + +RUNTIME_NATIVE_DIR="../realbuild/assemblies/runtimes/linux-loongarch64/native" + +if [ ! -d "$RUNTIME_NATIVE_DIR" ]; then + echo "ERROR: Native runtime directory not found: $RUNTIME_NATIVE_DIR" + echo "This means dotnet publish did not deploy linux-loongarch64 native assets." + echo "Possible causes:" + echo " 1. The NuGet packages lack linux-loongarch64 runtime support" + echo " 2. The RuntimeIdentifier is not correctly set" + echo " 3. The packages were not restored properly" + echo " 4. SQLitePCLRaw.lib.e_sqlite3 was not fetched from lnuget.loongnix.cn" + exit 1 +fi + +cp -a "$RUNTIME_NATIVE_DIR"/*.so native/linux-loongarch64/ +echo "Native libraries successfully copied from publish output." + +cd .. +mkdir -p Icons +cp -a "../src/BD.WTTS.Client.Avalonia.App/Assets.xcassets/AppIcon.appiconset/Icon128.png" Icons/Watt-Toolkit.png + +mkdir -p script +cp -a "../build/linux/environment_check.sh" \ + "../build/linux/init_desktop.sh" \ + "../build/linux/ISACheck.sh" \ + "../build/linux/Linux.sh" \ + "../build/linux/offline_init.sh" \ + "../build/linux/online_install.sh" \ + "../build/linux/uninstall.sh" \ + script/ + +cp -a "../build/linux/Steam++.sh" Steam++.sh +cd .. + +echo "finished" diff --git a/build/macos/build.sh b/build/macos/build.sh index e5f3649688f..ab359d3aba2 100644 --- a/build/macos/build.sh +++ b/build/macos/build.sh @@ -14,7 +14,7 @@ CodesignKey_Name="mossimo code" App_Modules_Path="$App_Path/$Publish_Path/modules" echo "编译主程序" cd "$App_Path" -dotnet publish -c Release -p:PublishDir="$Publish_Path" -f net8.0-macos -p:CreatePackage=false -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" +dotnet publish -c Release -p:PublishDir="$Publish_Path" -f net10.0-macos -p:CreatePackage=false -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" rm -r "$App_Modules_Path" mkdir -p "$App_Modules_Path" @@ -30,59 +30,59 @@ Copy_Plugins() { echo "编译加速插件" Plugins_Accelerator_Path="$Base_Path/src/BD.WTTS.Client.Plugins.Accelerator" cd "$Plugins_Accelerator_Path" -dotnet publish -c Release -p:PublishDir="$Publish_Path" -f net8.0-macos -Copy_Plugins "$Plugins_Release_Path/net8.0-macos" "Accelerator" "BD.WTTS.Client.Plugins.Accelerator.dll" +dotnet publish -c Release -p:PublishDir="$Publish_Path" -f net10.0-macos +Copy_Plugins "$Plugins_Release_Path/net10.0-macos" "Accelerator" "BD.WTTS.Client.Plugins.Accelerator.dll" Plugins_Accelerator_ReverseProxy_Path="$Base_Path/src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy" cd "$Plugins_Accelerator_ReverseProxy_Path" # 根据 Build_Mode 执行不同逻辑 if [ "$Build_Mode" = "any" ]; then - dotnet publish -c Release -p:PublishDir="$Publish_Path/arm64" -f net8.0 -r osx-arm64 -p:PublishSingleFile=true --self-contained -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" - dotnet publish -c Release -p:PublishDir="$Publish_Path/x64" -f net8.0 -r osx-x64 -p:PublishSingleFile=true --self-contained -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" + dotnet publish -c Release -p:PublishDir="$Publish_Path/arm64" -f net10.0 -r osx-arm64 -p:PublishSingleFile=true --self-contained -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" + dotnet publish -c Release -p:PublishDir="$Publish_Path/x64" -f net10.0 -r osx-x64 -p:PublishSingleFile=true --self-contained -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" lipo -create "$Publish_Path/arm64/Steam++.Accelerator" "$Publish_Path/x64/Steam++.Accelerator" -output "$Publish_Path/Steam++.Accelerator" Copy_Plugins "$Publish_Path" "Accelerator" "Steam++.Accelerator" elif [ "$Build_Mode" = "arm64" ]; then - dotnet publish -c Release -p:PublishDir="$Publish_Path/arm64" -f net8.0 -r osx-arm64 -p:PublishSingleFile=true --self-contained -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" + dotnet publish -c Release -p:PublishDir="$Publish_Path/arm64" -f net10.0 -r osx-arm64 -p:PublishSingleFile=true --self-contained -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" Copy_Plugins "$Publish_Path/arm64" "Accelerator" "Steam++.Accelerator" elif [ "$Build_Mode" = "x64" ]; then - dotnet publish -c Release -p:PublishDir="$Publish_Path/x64" -f net8.0 -r osx-x64 -p:PublishSingleFile=true --self-contained -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" + dotnet publish -c Release -p:PublishDir="$Publish_Path/x64" -f net10.0 -r osx-x64 -p:PublishSingleFile=true --self-contained -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" Copy_Plugins "$Publish_Path/x64" "Accelerator" "Steam++.Accelerator" fi echo "编译令牌插件" Plugins_Authenticator_Path="$Base_Path/src/BD.WTTS.Client.Plugins.Authenticator" cd "$Plugins_Authenticator_Path" -dotnet publish -c Release -p:PublishDir="$Publish_Path" -f net8.0-macos -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" -Copy_Plugins "$Plugins_Release_Path/net8.0-macos" "Authenticator" "BD.WTTS.Client.Plugins.Authenticator.dll" +dotnet publish -c Release -p:PublishDir="$Publish_Path" -f net10.0-macos -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" +Copy_Plugins "$Plugins_Release_Path/net10.0-macos" "Authenticator" "BD.WTTS.Client.Plugins.Authenticator.dll" echo "编译账户插件" Plugins_GameAccount_Path="$Base_Path/src/BD.WTTS.Client.Plugins.GameAccount" cd "$Plugins_GameAccount_Path" -dotnet publish -c Release -p:PublishDir="$Publish_Path" -f net8.0-macos -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" -Copy_Plugins "$Plugins_Release_Path/net8.0-macos" "GameAccount" "BD.WTTS.Client.Plugins.GameAccount.dll" +dotnet publish -c Release -p:PublishDir="$Publish_Path" -f net10.0-macos -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" +Copy_Plugins "$Plugins_Release_Path/net10.0-macos" "GameAccount" "BD.WTTS.Client.Plugins.GameAccount.dll" echo "编译游戏库插件" Plugins_GameList_Path="$Base_Path/src/BD.WTTS.Client.Plugins.GameList" cd "$Plugins_GameList_Path" -dotnet publish -c Release -p:PublishDir="$Publish_Path" -f net8.0-macos -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" -Copy_Plugins "$Plugins_Release_Path/net8.0-macos" "GameList" "BD.WTTS.Client.Plugins.GameList.dll" +dotnet publish -c Release -p:PublishDir="$Publish_Path" -f net10.0-macos -p:EnableCodeSigning=true -p:CodesignKey="$CodesignKey_Name" +Copy_Plugins "$Plugins_Release_Path/net10.0-macos" "GameList" "BD.WTTS.Client.Plugins.GameList.dll" echo "复制插件到程序目录" cd "$App_Path" # 根据 Build_Mode 执行不同逻辑 if [ "$Build_Mode" = "any" ]; then - cp -r "$App_Modules_Path" "$Plugins_Release_Path/net8.0-macos/Steam++.app/Contents/MonoBundle" + cp -r "$App_Modules_Path" "$Plugins_Release_Path/net10.0-macos/Steam++.app/Contents/MonoBundle" elif [ "$Build_Mode" = "arm64" ]; then - cp -r "$App_Modules_Path" "$Plugins_Release_Path/net8.0-macos/osx-arm64/Steam++.app/Contents/MonoBundle" - cp -r "$Plugins_Release_Path/net8.0-macos/Steam++.app/Contents/Resources" "$Plugins_Release_Path/net8.0-macos/Steam++.app/Contents" + cp -r "$App_Modules_Path" "$Plugins_Release_Path/net10.0-macos/osx-arm64/Steam++.app/Contents/MonoBundle" + cp -r "$Plugins_Release_Path/net10.0-macos/Steam++.app/Contents/Resources" "$Plugins_Release_Path/net10.0-macos/Steam++.app/Contents" elif [ "$Build_Mode" = "x64" ]; then - cp -r "$App_Modules_Path" "$Plugins_Release_Path/net8.0-macos/osx-x64/Steam++.app/Contents/MonoBundle" - cp -r "$Plugins_Release_Path/net8.0-macos/Steam++.app/Contents/Resources" "$Plugins_Release_Path/net8.0-macos/Steam++.app/Contents" + cp -r "$App_Modules_Path" "$Plugins_Release_Path/net10.0-macos/osx-x64/Steam++.app/Contents/MonoBundle" + cp -r "$Plugins_Release_Path/net10.0-macos/Steam++.app/Contents/Resources" "$Plugins_Release_Path/net10.0-macos/Steam++.app/Contents" fi echo "代码签名重新手动签名" -App_Publish_Path="$App_Path/bin/Release/net8.0-macos/Steam++.app" +App_Publish_Path="$App_Path/bin/Release/net10.0-macos/Steam++.app" # #代码签名重新手动签名 codesign --remove-signature "$App_Publish_Path" codesign --remove-signature "$App_Publish_Path/Contents/MacOS/Steam++" diff --git a/build/publish_apphost_linux_x64.sh b/build/publish_apphost_linux_x64.sh index 2c8ffd322e1..32813261970 100644 --- a/build/publish_apphost_linux_x64.sh +++ b/build/publish_apphost_linux_x64.sh @@ -1 +1 @@ -dotnet publish "..\src\BD.WTTS.Client.AppHost\BD.WTTS.Client.AppHost.csproj" -f net7.0 -c Release -p:PublishProfile=linux-x64 -p:DeployOnBuild=true --nologo \ No newline at end of file +dotnet publish "..\src\BD.WTTS.Client.AppHost\BD.WTTS.Client.AppHost.csproj" -f net10.0 -c Release -p:PublishProfile=linux-x64 -p:DeployOnBuild=true --nologo diff --git a/build/risc-v64 build/rv64_build.sh b/build/risc-v64 build/rv64_build.sh new file mode 100644 index 00000000000..6931451d584 --- /dev/null +++ b/build/risc-v64 build/rv64_build.sh @@ -0,0 +1,149 @@ +#!/bin/bash +set -e + +# ========== 覆盖中央包管理:显式添加指定版本的原生库 ========== +echo "Adding explicit native package versions (overriding Central Package Management)..." + +dotnet add src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj \ + package SkiaSharp.NativeAssets.Linux --version 3.119.4 + +dotnet add src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj \ + package HarfBuzzSharp.NativeAssets.Linux --version 8.3.1.5 + +dotnet add src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj \ + package SQLitePCLRaw.lib.e_sqlite3 --version 2.1.11 + +# 还原工作负载和包 +dotnet workload restore src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj + +# ========== 发布主程序 ========== +echo "Publishing main application..." +dotnet publish -c Release src/BD.WTTS.Client.Avalonia.App/BD.WTTS.Client.Avalonia.App.csproj \ + -p:UseAppHost=false \ + -p:PublishDir=realbuild/assemblies \ + -p:PublishSingleFile=false \ + -p:PublishReadyToRun=false \ + -p:PublishDocumentationFile=false \ + -p:PublishDocumentationFiles=false \ + -p:PublishReferencesDocumentationFiles=false \ + -f net10.0 \ + -r linux-riscv64 \ + -v q \ + /property:WarningLevel=1 \ + --sc false \ + --force \ + --nologo \ + -o "realbuild/assemblies" + +# ========== 发布插件 ========== +echo "Publishing plugins..." + +dotnet publish -c Release src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy.csproj \ + -p:UseAppHost=true \ + -p:PublishDir=realbuild/modules/Accelerator \ + -p:PublishSingleFile=true \ + -p:PublishReadyToRun=false \ + -p:PublishDocumentationFile=false \ + -p:PublishDocumentationFiles=false \ + -p:PublishReferencesDocumentationFiles=false \ + -f net10.0 \ + -r linux-riscv64 \ + -v q \ + /property:WarningLevel=1 \ + --sc false \ + --force \ + --nologo \ + -o "realbuild/modules/Accelerator" + +dotnet build -c Release src/BD.WTTS.Client.Plugins.GameAccount/BD.WTTS.Client.Plugins.GameAccount.csproj \ + --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly \ + -o "realbuild/modules/GameAccount" + +dotnet build -c Release src/BD.WTTS.Client.Plugins.GameList/BD.WTTS.Client.Plugins.GameList.csproj \ + --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly \ + -o "realbuild/modules/GameList" + +dotnet build -c Release src/BD.WTTS.Client.Plugins.Authenticator/BD.WTTS.Client.Plugins.Authenticator.csproj \ + --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly \ + -o "realbuild/modules/Authenticator" + +dotnet build -c Release src/BD.WTTS.Client.Plugins.SteamIdleCard/BD.WTTS.Client.Plugins.SteamIdleCard.csproj \ + --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly \ + -o "realbuild/modules/SteamIdleCard" + +dotnet build -c Release src/BD.WTTS.Client.Plugins.Accelerator/BD.WTTS.Client.Plugins.Accelerator.csproj \ + --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly \ + -o "realbuild/modules/Accelerator" + +# dotnet test src/BD.WTTS.UnitTest/BD.WTTS.UnitTest.csproj -c Release -p:GeneratePackageOnBuild=false --nologo -v q --property:WarningLevel=1 --property:DebugType=pdbonly -a loongarch64 -o "realbuild/" + +echo "Publish Done!" +echo "Start Copy File" + +# ========== 组装输出目录 ========== +mkdir -p WattToolkit +cp -a realbuild/assemblies WattToolkit/ + +cd WattToolkit +mkdir -p modules +cd modules +mkdir -p Accelerator Authenticator GameAccount GameList SteamIdleCard + +cp "../../realbuild/modules/Accelerator/BD.WTTS.Client.Plugins.Accelerator.dll" \ + "../../realbuild/modules/Accelerator/Steam++.Accelerator" Accelerator/ +cp "../../realbuild/modules/Authenticator/BD.WTTS.Client.Plugins.Authenticator.dll" Authenticator/ +cp "../../realbuild/modules/GameAccount/BD.WTTS.Client.Plugins.GameAccount.dll" GameAccount/ +cp "../../realbuild/modules/GameList/BD.WTTS.Client.Plugins.GameList.dll" GameList/ +cp "../../realbuild/modules/SteamIdleCard/BD.WTTS.Client.Plugins.SteamIdleCard.dll" SteamIdleCard/ + +cd .. +mkdir -p dotnet +dotnetloc=$(readlink -f $(which dotnet)) +cp -a "$dotnetloc" \ + "$(dirname "$dotnetloc")/host" \ + "$(dirname "$dotnetloc")/shared" \ + "$(dirname "$dotnetloc")/LICENSE.txt" \ + "$(dirname "$dotnetloc")/ThirdPartyNotices.txt" \ + dotnet/ + +# ========== 严格模式:原生库必须从 publish 输出复制 ========== +mkdir -p native/linux-riscv64 + +RUNTIME_NATIVE_DIR="../realbuild/assemblies/runtimes/linux-riscv64/native" + +if [ ! -d "$RUNTIME_NATIVE_DIR" ]; then + echo "ERROR: Native runtime directory not found: $RUNTIME_NATIVE_DIR" + echo "This means dotnet publish did not deploy linux-riscv64 native assets." + echo "Possible causes:" + echo " 1. The NuGet packages lack linux-riscv64 runtime support" + echo " 2. The RuntimeIdentifier is not correctly set" + echo " 3. The packages were not restored properly" + exit 1 +fi + +cp -a "$RUNTIME_NATIVE_DIR"/*.so native/linux-riscv64/ +echo "Native libraries successfully copied from publish output." + +cd .. +mkdir -p Icons +cp -a ../res/icons/app/v3/Logo_512.png Icons/Watt-Toolkit.png + +mkdir -p script +cp -a "../build/linux/environment_check.sh" \ + "../build/linux/init_desktop.sh" \ + "../build/linux/ISACheck.sh" \ + "../build/linux/offline_init.sh" \ + "../build/linux/online_install.sh" \ + "../build/linux/uninstall.sh" \ + script/ + +cd script +dos2unix *.sh +cd ../.. + +cp -a "build/linux/Steam++.sh" WattToolkit/ +cd WattToolkit +dos2unix *.sh +cd .. + +echo "finished" diff --git a/doc/open-source-library.md b/doc/open-source-library.md index ebb76732599..43beb0d9082 100644 --- a/doc/open-source-library.md +++ b/doc/open-source-library.md @@ -76,3 +76,6 @@ * [.NET Runtime](https://github.com/dotnet/runtime) * [Fluent UI System Icons](https://github.com/microsoft/fluentui-system-icons) * [Material design icons](https://github.com/google/material-design-icons) + +For LoongArch64, it also included: +* [libpng](https://github.com/winlibs/libpng) diff --git a/libpng/loongson/filter_lsx_intrinsics.c b/libpng/loongson/filter_lsx_intrinsics.c new file mode 100644 index 00000000000..003c340acde --- /dev/null +++ b/libpng/loongson/filter_lsx_intrinsics.c @@ -0,0 +1,412 @@ +/* filter_lsx_intrinsics.c - LSX optimized filter functions + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + * All rights reserved. + * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2016 Glenn Randers-Pehrson + * Contributed by Jin Bo (jinbo@loongson.cn) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#if PNG_LOONGARCH_LSX_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */ + +#include + +#define LSX_LD(psrc) __lsx_vld((psrc), 0) + +#define LSX_LD_2(psrc, stride, out0, out1) \ +{ \ + out0 = LSX_LD(psrc); \ + out1 = LSX_LD(psrc + stride); \ +} + +#define LSX_LD_4(psrc, stride, out0, out1, out2, out3) \ +{ \ + LSX_LD_2(psrc, stride, out0, out1); \ + LSX_LD_2(psrc + stride * 2, stride, out2, out3); \ +} + +#define LSX_ST(in, pdst) __lsx_vst(in, (pdst), 0) + +#define LSX_ST_2(in0, in1, pdst, stride) \ +{ \ + LSX_ST(in0, pdst); \ + LSX_ST(in1, pdst + stride); \ +} + +#define LSX_ST_4(in0, in1, in2, in3, pdst, stride) \ +{ \ + LSX_ST_2(in0, in1, pdst, stride); \ + LSX_ST_2(in2, in3, pdst + stride * 2, stride); \ +} + +#define LSX_ADD_B(in0, in1, out0) \ +{ \ + out0 = __lsx_vadd_b(in0, in1); \ +} + +#define LSX_ADD_B_2(in0, in1, in2, in3, out0, out1) \ +{ \ + LSX_ADD_B(in0, in1, out0); \ + LSX_ADD_B(in2, in3, out1); \ +} + +#define LSX_ADD_B_4(in0, in1, in2, in3, in4, in5, \ + in6, in7, out0, out1, out2, out3) \ +{ \ + LSX_ADD_B_2(in0, in1, in2, in3, out0, out1); \ + LSX_ADD_B_2(in4, in5, in6, in7, out2, out3); \ +} + +#define LSX_ABS_B_3(in0, in1, in2, out0, out1, out2) \ +{ \ + out0 = __lsx_vadda_h(in0, zero); \ + out1 = __lsx_vadda_h(in1, zero); \ + out2 = __lsx_vadda_h(in2, zero); \ +} + +#define LSX_ILVL_B(in_h, in_l, out0) \ +{ \ + out0 = __lsx_vilvl_b(in_h, in_l); \ +} + +#define LSX_ILVL_B_2(in0_h, in0_l, in1_h, in1_l, out0, out1) \ +{ \ + LSX_ILVL_B(in0_h, in0_l, out0); \ + LSX_ILVL_B(in1_h, in1_l, out1); \ +} + +#define LSX_HSUB_HU_BU_2(in0, in1, out0, out1) \ +{ \ + out0 = __lsx_vhsubw_hu_bu(in0, in0); \ + out1 = __lsx_vhsubw_hu_bu(in1, in1); \ +} + +#define LSX_CMP_PICK_SMALLER(in0, in1, in2, in3, in4, in5, out0) \ +{ \ + __m128i _cmph, _cmpb, _in0, _in3; \ + _cmph = __lsx_vslt_h(in1, in0); \ + _cmpb = __lsx_vpickev_b(_cmph, _cmph); \ + _in0 = __lsx_vmin_bu(in0,in1); \ + _in3 = __lsx_vbitsel_v(in3, in4, _cmpb); \ + _cmph = __lsx_vslt_h(in2, _in0); \ + _cmpb = __lsx_vpickev_b(_cmph, _cmph); \ + _in3 = __lsx_vbitsel_v(_in3, in5, _cmpb); \ + out0 = __lsx_vadd_b(out0, _in3); \ +} + +void png_read_filter_row_up_lsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t n = row_info->rowbytes; + png_bytep rp = row; + png_const_bytep pp = prev_row; + __m128i vec_0, vec_1, vec_2, vec_3; + __m128i vec_4, vec_5, vec_6, vec_7; + + while (n >= 64) + { + LSX_LD_4(rp, 16, vec_0, vec_1, vec_2, vec_3); + LSX_LD_4(pp, 16, vec_4, vec_5, vec_6, vec_7); + pp += 64; + LSX_ADD_B_4(vec_0 ,vec_4, vec_1, vec_5, vec_2, vec_6, + vec_3, vec_7, vec_0, vec_1, vec_2, vec_3); + LSX_ST_4(vec_0, vec_1, vec_2, vec_3, rp, 16); + rp += 64; + n -= 64; + } + if (n & 63) + { + if (n >= 32) + { + LSX_LD_2(rp, 16, vec_0, vec_1); + LSX_LD_2(pp, 16, vec_2, vec_3); + pp += 32; + LSX_ADD_B_2(vec_0, vec_2, vec_1, vec_3, vec_0, vec_1); + LSX_ST_2(vec_0, vec_1, rp, 16); + rp += 32; + n -= 32; + } + if (n & 31) + { + if (n >= 16) + { + vec_0 = LSX_LD(rp); + vec_1 = LSX_LD(pp); + pp += 16; + LSX_ADD_B(vec_0, vec_1, vec_0); + LSX_ST(vec_0, rp); + rp += 16; + n -= 16; + } + if (n >= 8) + { + vec_0 = __lsx_vldrepl_d(rp, 0); + vec_1 = __lsx_vldrepl_d(pp, 0); + vec_0 = __lsx_vadd_b(vec_0, vec_1); + __lsx_vstelm_d(vec_0, rp, 0, 0); + rp += 8; + pp += 8; + n -= 8; + } + while (n--) + { + *rp = *rp + *pp++; + rp++; + } + } + } +} + +void png_read_filter_row_sub3_lsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t n = row_info->rowbytes; + png_uint_32 tmp; + png_bytep nxt = row; + __m128i vec_0, vec_1; + + PNG_UNUSED(prev_row); + + vec_0 = __lsx_vldrepl_w(nxt, 0); + nxt += 3; + n -= 3; + + while (n >= 3) + { + vec_1 = __lsx_vldrepl_w(nxt, 0); + vec_1 = __lsx_vadd_b(vec_1, vec_0); + __lsx_vstelm_h(vec_1, nxt, 0, 0); + vec_0 = vec_1; + nxt += 2; + __lsx_vstelm_b(vec_1, nxt, 0, 2); + nxt += 1; + n -= 3; + } + + row = nxt - 3; + while (n--) + { + *nxt = *nxt + *row++; + nxt++; + } +} + +void png_read_filter_row_sub4_lsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t n = row_info->rowbytes; + __m128i vec_0, vec_1; + + PNG_UNUSED(prev_row); + + vec_0 = __lsx_vldrepl_w(row, 0); + row += 4; + n -= 4; + + while (n >= 4) + { + vec_1 = __lsx_vldrepl_w(row, 0); + vec_1 = __lsx_vadd_b(vec_1, vec_0); + __lsx_vstelm_w(vec_1, row, 0, 0); + vec_0 = vec_1; + row += 4; + n -= 4; + } +} + +void png_read_filter_row_avg3_lsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t n = row_info->rowbytes; + png_bytep nxt = row; + png_const_bytep prev_nxt = prev_row; + __m128i vec_0, vec_1, vec_2; + + vec_0 = __lsx_vldrepl_w(nxt, 0); + vec_1 = __lsx_vldrepl_w(prev_nxt, 0); + prev_nxt += 3; + vec_1 = __lsx_vsrli_b(vec_1, 1); + vec_1 = __lsx_vadd_b(vec_1, vec_0); + __lsx_vstelm_h(vec_1, nxt, 0, 0); + nxt += 2; + __lsx_vstelm_b(vec_1, nxt, 0, 2); + nxt += 1; + n -= 3; + + while (n >= 3) + { + vec_2 = vec_1; + vec_0 = __lsx_vldrepl_w(nxt, 0); + vec_1 = __lsx_vldrepl_w(prev_nxt, 0); + prev_nxt += 3; + + vec_1 = __lsx_vavg_bu(vec_1, vec_2); + vec_1 = __lsx_vadd_b(vec_1, vec_0); + + __lsx_vstelm_h(vec_1, nxt, 0, 0); + nxt += 2; + __lsx_vstelm_b(vec_1, nxt, 0, 2); + nxt += 1; + n -= 3; + } + + row = nxt - 3; + while (n--) + { + vec_2 = __lsx_vldrepl_b(row, 0); + row++; + vec_0 = __lsx_vldrepl_b(nxt, 0); + vec_1 = __lsx_vldrepl_b(prev_nxt, 0); + prev_nxt++; + + vec_1 = __lsx_vavg_bu(vec_1, vec_2); + vec_1 = __lsx_vadd_b(vec_1, vec_0); + + __lsx_vstelm_b(vec_1, nxt, 0, 0); + nxt++; + } +} + +void png_read_filter_row_avg4_lsx(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t n = row_info->rowbytes; + __m128i vec_0, vec_1, vec_2; + + vec_0 = __lsx_vldrepl_w(row, 0); + vec_1 = __lsx_vldrepl_w(prev_row, 0); + prev_row += 4; + vec_1 = __lsx_vsrli_b(vec_1, 1); + vec_1 = __lsx_vadd_b(vec_1, vec_0); + __lsx_vstelm_w(vec_1, row, 0, 0); + row += 4; + n -= 4; + + while (n >= 4) + { + vec_2 = vec_1; + vec_0 = __lsx_vldrepl_w(row, 0); + vec_1 = __lsx_vldrepl_w(prev_row, 0); + prev_row += 4; + + vec_1 = __lsx_vavg_bu(vec_1, vec_2); + vec_1 = __lsx_vadd_b(vec_1, vec_0); + + __lsx_vstelm_w(vec_1, row, 0, 0); + row += 4; + n -= 4; + } +} + +void png_read_filter_row_paeth3_lsx(png_row_infop row_info, + png_bytep row, + png_const_bytep prev_row) +{ + size_t n = row_info->rowbytes; + png_bytep nxt = row; + png_const_bytep prev_nxt = prev_row; + __m128i vec_a, vec_b, vec_c, vec_d; + __m128i vec_pa, vec_pb, vec_pc; + __m128i zero = {0}; + + vec_a = __lsx_vldrepl_w(nxt, 0); + vec_b = __lsx_vldrepl_w(prev_nxt, 0); + prev_nxt += 3; + vec_d = __lsx_vadd_b(vec_a, vec_b); + __lsx_vstelm_h(vec_d, nxt, 0, 0); + nxt += 2; + __lsx_vstelm_b(vec_d, nxt, 0, 2); + nxt += 1; + n -= 3; + + while (n >= 3) + { + vec_a = vec_d; + vec_c = vec_b; + vec_b = __lsx_vldrepl_w(prev_nxt, 0); + prev_nxt += 3; + vec_d = __lsx_vldrepl_w(nxt, 0); + + LSX_ILVL_B_2(vec_b, vec_c, vec_a, vec_c, vec_pa, vec_pb); + LSX_HSUB_HU_BU_2(vec_pa, vec_pb, vec_pa, vec_pb); + vec_pc = __lsx_vadd_h(vec_pa, vec_pb); + LSX_ABS_B_3(vec_pa, vec_pb, vec_pc, vec_pa, vec_pb, vec_pc); + LSX_CMP_PICK_SMALLER(vec_pa, vec_pb, vec_pc, vec_a, vec_b, vec_c, vec_d); + + __lsx_vstelm_h(vec_d, nxt, 0, 0); + nxt += 2; + __lsx_vstelm_b(vec_d, nxt, 0, 2); + nxt += 1; + n -= 3; + } + + prev_row = prev_nxt - 3; + row = nxt - 3; + while (n--) + { + vec_a = __lsx_vldrepl_b(row, 0); + row++; + vec_b = __lsx_vldrepl_b(prev_nxt, 0); + prev_nxt++; + vec_c = __lsx_vldrepl_b(prev_row, 0); + prev_row++; + vec_d = __lsx_vldrepl_b(nxt, 0); + + LSX_ILVL_B_2(vec_b, vec_c, vec_a, vec_c, vec_pa, vec_pb); + LSX_HSUB_HU_BU_2(vec_pa, vec_pb, vec_pa, vec_pb); + vec_pc = __lsx_vadd_h(vec_pa, vec_pb); + LSX_ABS_B_3(vec_pa, vec_pb, vec_pc, vec_pa, vec_pb, vec_pc); + LSX_CMP_PICK_SMALLER(vec_pa, vec_pb, vec_pc, vec_a, vec_b, vec_c, vec_d); + + __lsx_vstelm_b(vec_d, nxt, 0, 0); + nxt++; + } +} + +void png_read_filter_row_paeth4_lsx(png_row_infop row_info, + png_bytep row, + png_const_bytep prev_row) +{ + size_t n = row_info->rowbytes; + __m128i vec_a, vec_b, vec_c, vec_d; + __m128i vec_pa, vec_pb, vec_pc; + __m128i zero = {0}; + + vec_a = __lsx_vldrepl_w(row, 0); + vec_b = __lsx_vldrepl_w(prev_row, 0); + prev_row += 4; + vec_d = __lsx_vadd_b(vec_a, vec_b); + __lsx_vstelm_w(vec_d, row, 0, 0); + row += 4; + n -= 4; + + while (n >= 4) + { + vec_a = vec_d; + vec_c = vec_b; + vec_b = __lsx_vldrepl_w(prev_row, 0); + prev_row += 4; + vec_d = __lsx_vldrepl_w(row, 0); + + LSX_ILVL_B_2(vec_b, vec_c, vec_a, vec_c, vec_pa, vec_pb); + LSX_HSUB_HU_BU_2(vec_pa, vec_pb, vec_pa, vec_pb); + vec_pc = __lsx_vadd_h(vec_pa, vec_pb); + LSX_ABS_B_3(vec_pa, vec_pb, vec_pc, vec_pa, vec_pb, vec_pc); + LSX_CMP_PICK_SMALLER(vec_pa, vec_pb, vec_pc, vec_a, vec_b, vec_c, vec_d); + + __lsx_vstelm_w(vec_d, row, 0, 0); + row += 4; + n -= 4; + } +} + +#endif /* PNG_LOONGARCH_LSX_IMPLEMENTATION == 1 (intrinsics) */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/libpng/loongson/loongarch_lsx_init.c b/libpng/loongson/loongarch_lsx_init.c new file mode 100644 index 00000000000..02006e0c6cb --- /dev/null +++ b/libpng/loongson/loongarch_lsx_init.c @@ -0,0 +1,65 @@ +/* loongarch_lsx_init.c - LSX optimized filter functions + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + * All rights reserved. + * Contributed by Jin Bo + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED +#if PNG_LOONGARCH_LSX_IMPLEMENTATION == 1 + +#include + +#define LA_HWCAP_LSX (1<<4) +static int png_has_lsx(void) +{ + int flags = 0; + int flag = (int)getauxval(AT_HWCAP); + + if (flag & LA_HWCAP_LSX) + return 1; + + return 0; +} + +void +png_init_filter_functions_lsx(png_structp pp, unsigned int bpp) +{ + /* IMPORTANT: any new external functions used here must be declared using + * PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the + * 'prefix' option to configure works: + * + * ./configure --with-libpng-prefix=foobar_ + * + * Verify you have got this right by running the above command, doing a build + * and examining pngprefix.h; it must contain a #define for every external + * function you add. (Notice that this happens automatically for the + * initialization function.) + */ + + if (png_has_lsx()) + { + pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_lsx; + if (bpp == 3) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_lsx; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_lsx; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth3_lsx; + } + else if (bpp == 4) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_lsx; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_lsx; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = png_read_filter_row_paeth4_lsx; + } + } +} + +#endif /* PNG_LOONGARCH_LSX_IMPLEMENTATION == 1 */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/ref/ArchiSteamFarm b/ref/ArchiSteamFarm index a768ec43a54..c5f403322e0 160000 --- a/ref/ArchiSteamFarm +++ b/ref/ArchiSteamFarm @@ -1 +1 @@ -Subproject commit a768ec43a54ef886180925a1c4ca189026ffd348 +Subproject commit c5f403322e07a1b50b99276068278328abacb54a diff --git a/src/BD.WTTS.Client.AppHost.Bridge/Steam++.csproj b/src/BD.WTTS.Client.AppHost.Bridge/Steam++.csproj index 39f5411d96c..f2dc2f140d8 100644 --- a/src/BD.WTTS.Client.AppHost.Bridge/Steam++.csproj +++ b/src/BD.WTTS.Client.AppHost.Bridge/Steam++.csproj @@ -2,7 +2,7 @@ Steam++ - net472 + net10.0;net10.0-windows10.0.19041 disable false Exe diff --git a/src/BD.WTTS.Client.AppHost/Program.cs b/src/BD.WTTS.Client.AppHost/Program.cs index 6e4a9647b67..9562185acd9 100644 --- a/src/BD.WTTS.Client.AppHost/Program.cs +++ b/src/BD.WTTS.Client.AppHost/Program.cs @@ -6,6 +6,8 @@ #if NETFRAMEWORK using System.Configuration; #endif +using System.Collections.Generic; +using System.Runtime.InteropServices; using static BD.WTTS.AssemblyInfo; using static BD.WTTS.Client.Resources.Strings; @@ -143,6 +145,8 @@ static Architecture GetProcessArchitecture() Architecture.X64 => "x64", Architecture.Arm => "Arm32", Architecture.Arm64 => "Arm64", + Architecture.LoongArch64 => "LoongArch64", + Architecture.RiscV64 => "RiscV64", #if !NETFRAMEWORK Architecture.Armv6 => "Armv6", #endif @@ -187,13 +191,79 @@ static void DownloadDotNetRuntime() OpenCoreByProcess(url); } + /// + /// 尝试通过 dotnet 命令获取 dotnet 根目录 + /// + static string GetDotNetRootFromCommand() + { + try + { + var psi = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = "--list-runtimes", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + using (var process = Process.Start(psi)) + { + if (process == null) + return null; + + // 读取第一行输出 + var output = process.StandardOutput.ReadLine(); + process.WaitForExit(5000); + + if (string.IsNullOrEmpty(output)) + return null; + + // 提取路径,格式为 [...] + var startIndex = output.LastIndexOf('['); + var endIndex = output.LastIndexOf(']'); + if (startIndex >= 0 && endIndex > startIndex) + { + var sharedPath = output.Substring(startIndex + 1, endIndex - startIndex - 1); + // 从 shared/Microsoft.NETCore.App 向上两级得到 dotnet 根目录 + var dotnetRoot = Path.GetDirectoryName(Path.GetDirectoryName(sharedPath)); + return dotnetRoot; + } + } + } + catch + { + // 忽略所有异常,返回 null 让调用方尝试其他方式 + } + return null; + } + + /// + /// 获取当前操作系统的 hostfxr 库文件名 + /// + static string GetHostFxrLibName() + { +#if NET35 || NET40 || NET451 + // .NET 3.5/4.0/4.5.1 只能运行在 Windows 上 + return "hostfxr.dll"; +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return "hostfxr.dll"; + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return "libhostfxr.dylib"; + return "libhostfxr.so"; +#endif + } + enum DotNetRootType : byte { BaseDir, -#if NETFRAMEWORK || WINDOWS +#if WINDOWS || NETFRAMEWORK || _WINDOWS ProgramFiles, #endif EnvironmentVariable, + DotNetCommand, } #if NET40_OR_GREATER @@ -293,6 +363,7 @@ static int MainCore(string[] args) //var requireAspNetCore = RequireAspNetCore(baseDirectory); const bool requireAspNetCore = true; string hostfxr_path, dotnet_runtime_path, aspnetcore_runtime_path, config_path, dotnetlib_path; + string selected_dotnet_root = string.Empty; // STEP 0: Search HostFxr for (byte i = 0; true; i++) @@ -300,10 +371,11 @@ static int MainCore(string[] args) var dotnet_root = i switch { (byte)DotNetRootType.BaseDir => Path.Combine(baseDirectory, "dotnet"), // 优先使用根目录上的运行时 -#if NETFRAMEWORK || WINDOWS +#if WINDOWS || NETFRAMEWORK || _WINDOWS (byte)DotNetRootType.ProgramFiles => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet"), // 查找已安装的运行时 #endif (byte)DotNetRootType.EnvironmentVariable => Environment.GetEnvironmentVariable("DOTNET_ROOT") ?? string.Empty, // 检查环境变量中设定的路径 + (byte)DotNetRootType.DotNetCommand => GetDotNetRootFromCommand(), // 通过 dotnet 命令获取路径 _ => null, }; if (dotnet_root == null) @@ -346,60 +418,225 @@ static int MainCore(string[] args) Path.Combine #endif (dotnet_root, "host", "fxr"); - string usable_dotnet_version = dotnet_version; - var dotnet_version_max = Directory.GetDirectories(dir_hostfxr_path, $"{dotnet_version_major}.*").Select(x => - { - try - { - return new Version(Path.GetFileName(x)); - } - catch - { - return null; - } - }).Where(x => x != null).Max(); - if (dotnet_version_max != null) + +#if DEBUG + Console.WriteLine($"Checking dotnet_root: {dotnet_root}"); + Console.WriteLine($"hostfxr_path: {dir_hostfxr_path}"); + Console.WriteLine($"Directory exists: {Directory.Exists(dir_hostfxr_path)}"); +#endif + + // 获取 hostfxr 的所有可用版本(只考虑与目标版本相同主版本的) + var hostfxr_versions = new List(); + if (Directory.Exists(dir_hostfxr_path)) { - usable_dotnet_version = dotnet_version_max.ToString(); + foreach (var dir in Directory.GetDirectories(dir_hostfxr_path, "*.*")) + { + try + { + var ver = new Version(Path.GetFileName(dir)); +#if DEBUG + Console.WriteLine($"Found hostfxr version: {ver}"); +#endif + if (ver.Major == int.Parse(dotnet_version_major)) + hostfxr_versions.Add(ver); + } + catch { } + } } +#if DEBUG + Console.WriteLine($"hostfxr_versions count: {hostfxr_versions.Count}"); +#endif + if (hostfxr_versions.Count == 0) + continue; + hostfxr_versions.Sort((a, b) => b.CompareTo(a)); // 降序排序 - hostfxr_path = + // 获取 .NET Runtime 的所有可用版本 + var dotnet_runtime_dir = +#if NET35 + PathCombine +#else + Path.Combine +#endif + (dotnet_root, "shared", dotnet_runtime); + var dotnet_runtime_versions = new List(); + if (Directory.Exists(dotnet_runtime_dir)) + { + foreach (var dir in Directory.GetDirectories(dotnet_runtime_dir, "*.*")) + { + try + { + var ver = new Version(Path.GetFileName(dir)); + dotnet_runtime_versions.Add(ver); + } + catch { } + } + dotnet_runtime_versions.Sort((a, b) => b.CompareTo(a)); // 降序排序 + } +#if DEBUG + Console.WriteLine($"dotnet_runtime_dir: {dotnet_runtime_dir}"); + Console.WriteLine($"dotnet_runtime_versions count: {dotnet_runtime_versions.Count}"); + foreach (var v in dotnet_runtime_versions) + Console.WriteLine($" - {v}"); +#endif + + // 获取 ASP.NET Core Runtime 的所有可用版本(如果需要) + var aspnetcore_runtime_dir = #if NET35 PathCombine #else Path.Combine #endif - (dir_hostfxr_path, usable_dotnet_version, -#if NETFRAMEWORK || WINDOWS - "hostfxr.dll" -#elif MACOS || MACCATALYST - "libhostfxr.dylib" + (dotnet_root, "shared", aspnetcore_runtime); + var aspnetcore_runtime_versions = new List(); + if (requireAspNetCore && Directory.Exists(aspnetcore_runtime_dir)) + { + foreach (var dir in Directory.GetDirectories(aspnetcore_runtime_dir, "*.*")) + { + try + { + var ver = new Version(Path.GetFileName(dir)); + aspnetcore_runtime_versions.Add(ver); + } + catch { } + } + aspnetcore_runtime_versions.Sort((a, b) => b.CompareTo(a)); // 降序排序 + } +#if DEBUG + Console.WriteLine($"aspnetcore_runtime_dir: {aspnetcore_runtime_dir}"); + Console.WriteLine($"aspnetcore_runtime_versions count: {aspnetcore_runtime_versions.Count}"); + foreach (var v in aspnetcore_runtime_versions) + Console.WriteLine($" - {v}"); +#endif + + // 查找最佳匹配的版本组合 + string usable_dotnet_version = null; + var target_version = new Version(dotnet_version); +#if DEBUG + Console.WriteLine($"target_version: {target_version}"); +#endif + + foreach (var hostfxr_ver in hostfxr_versions) + { + // 检查是否存在兼容的 runtime 版本(相同或更高) + Version matching_runtime = null; + foreach (var r in dotnet_runtime_versions) + { + if (r >= target_version) + { + matching_runtime = r; + break; + } + } + if (matching_runtime == null) + continue; + + // 如果需要 ASP.NET Core,检查是否存在兼容版本 + if (requireAspNetCore) + { + Version matching_aspnetcore = null; + foreach (var r in aspnetcore_runtime_versions) + { + if (r >= target_version) + { + matching_aspnetcore = r; + break; + } + } + if (matching_aspnetcore == null) + continue; + } + + // 找到可用的版本组合 + usable_dotnet_version = hostfxr_ver.ToString(); + break; + } + + if (usable_dotnet_version == null) + { +#if DEBUG + Console.WriteLine("usable_dotnet_version is null, continuing..."); +#endif + continue; + } + +#if DEBUG + Console.WriteLine($"Selected usable_dotnet_version: {usable_dotnet_version}"); +#endif + + // 根据操作系统选择正确的 hostfxr 库文件名 + string hostfxr_lib_name = GetHostFxrLibName(); + hostfxr_path = +#if NET35 + PathCombine #else - "libhostfxr.so" + Path.Combine #endif - ); + (dir_hostfxr_path, usable_dotnet_version, hostfxr_lib_name); + // 使用实际存在的 runtime 版本路径(可能与 hostfxr 版本不同) + string actual_runtime_version = usable_dotnet_version; + foreach (var v in dotnet_runtime_versions) + { + if (v >= target_version) + { + actual_runtime_version = v.ToString(); + break; + } + } dotnet_runtime_path = #if NET35 PathCombine #else Path.Combine #endif - (dotnet_root, "shared", dotnet_runtime, usable_dotnet_version); + (dotnet_root, "shared", dotnet_runtime, actual_runtime_version); + string actual_aspnetcore_version = null; + if (requireAspNetCore) + { + actual_aspnetcore_version = usable_dotnet_version; + foreach (var v in aspnetcore_runtime_versions) + { + if (v >= target_version) + { + actual_aspnetcore_version = v.ToString(); + break; + } + } + } aspnetcore_runtime_path = requireAspNetCore ? #if NET35 PathCombine #else Path.Combine #endif - (dotnet_root, "shared", aspnetcore_runtime, usable_dotnet_version) : null!; + (dotnet_root, "shared", aspnetcore_runtime, actual_aspnetcore_version) : null; + +#if DEBUG + Console.WriteLine($"hostfxr_path: {hostfxr_path}"); + Console.WriteLine($"hostfxr exists: {File.Exists(hostfxr_path)}"); + Console.WriteLine($"dotnet_runtime_path: {dotnet_runtime_path}"); + Console.WriteLine($"dotnet_runtime exists: {Directory.Exists(dotnet_runtime_path)}"); + Console.WriteLine($"dotnet_runtime empty: {PathIsDirectoryEmpty(dotnet_runtime_path)}"); + Console.WriteLine($"aspnetcore_runtime_path: {aspnetcore_runtime_path}"); + Console.WriteLine($"aspnetcore_runtime exists: {Directory.Exists(aspnetcore_runtime_path)}"); + if (Directory.Exists(aspnetcore_runtime_path)) + Console.WriteLine($"aspnetcore_runtime empty: {PathIsDirectoryEmpty(aspnetcore_runtime_path)}"); +#endif + if (File.Exists(hostfxr_path) && Directory.Exists(dotnet_runtime_path) && !PathIsDirectoryEmpty(dotnet_runtime_path) && (!requireAspNetCore || (Directory.Exists(aspnetcore_runtime_path) && !PathIsDirectoryEmpty(aspnetcore_runtime_path)))) { +#if DEBUG + Console.WriteLine("All checks passed, breaking loop"); +#endif + selected_dotnet_root = dotnet_root; break; } +#if DEBUG + Console.WriteLine("Checks failed, continuing to next dotnet_root..."); +#endif } } @@ -426,8 +663,30 @@ static int MainCore(string[] args) if (!File.Exists(config_path) || !File.Exists(dotnetlib_path)) { #if DEBUG - config_path = string.Join(Path.DirectorySeparatorChar.ToString(), new[] { ProjectUtils.ProjPath, "src", "BD.WTTS.Client.Avalonia.App", "bin", "Debug", $"net{dotnet_version_major}.{dotnet_version_minor}-windows10.0.19041", $"{dotnet_dll_name}.runtimeconfig.json" }); - dotnetlib_path = string.Join(Path.DirectorySeparatorChar.ToString(), new[] { ProjectUtils.ProjPath, "src", "BD.WTTS.Client.Avalonia.App", "bin", "Debug", $"net{dotnet_version_major}.{dotnet_version_minor}-windows10.0.19041", $"{dotnet_dll_name}.dll" }); + // 根据操作系统选择不同的路径 + string tfmPath; +#if NET35 || NET40 || NET451 + // .NET 3.5/4.0/4.5.1 只能是 Windows + tfmPath = $"net{dotnet_version_major}.{dotnet_version_minor}-windows10.0.19041"; +#else + // .NET Core/.NET 5+ 使用 RuntimeInformation + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + tfmPath = $"net{dotnet_version_major}.{dotnet_version_minor}-windows10.0.19041"; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + tfmPath = $"net{dotnet_version_major}.{dotnet_version_minor}"; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + tfmPath = $"net{dotnet_version_major}.{dotnet_version_minor}-macos"; + else + tfmPath = $"net{dotnet_version_major}.{dotnet_version_minor}"; +#endif + config_path = string.Join(Path.DirectorySeparatorChar.ToString(), new[] { ProjectUtils.ProjPath, "src", "BD.WTTS.Client.Avalonia.App", "bin", "Debug", tfmPath, $"{dotnet_dll_name}.runtimeconfig.json" }); + dotnetlib_path = string.Join(Path.DirectorySeparatorChar.ToString(), new[] { ProjectUtils.ProjPath, "src", "BD.WTTS.Client.Avalonia.App", "bin", "Debug", tfmPath, $"{dotnet_dll_name}.dll" }); + Debug.WriteLine($"DEBUG: Using path for current OS:"); + Debug.WriteLine($" tfmPath: {tfmPath}"); + Debug.WriteLine($" config_path: {config_path}"); + Debug.WriteLine($" config_path exists: {File.Exists(config_path)}"); + Debug.WriteLine($" dotnetlib_path: {dotnetlib_path}"); + Debug.WriteLine($" dotnetlib_path exists: {File.Exists(dotnetlib_path)}"); #else ShowErrMessageBox($"Loading assembly failed \"{dotnetlib_path}\""); return (int)ExitCode.EntryPointFileNotFound; @@ -479,6 +738,7 @@ static int MainCore(string[] args) load_assembly_and_get_function_pointer = default; // Load .NET Core + // Windows 上使用 UTF-16 (wchar_t*) var config_path_ = Marshal.StringToHGlobalUni(config_path); int rc = default; nint cxt = default; @@ -498,6 +758,8 @@ static int MainCore(string[] args) Debug.WriteLine #endif ("Init failed: 0x{0}", new object[] { Convert.ToString(rc, 16), }); + Debug.WriteLine($" config_path used: {config_path}"); + Debug.WriteLine($" hostfxr_path: {hostfxr_path}"); close_fptr(cxt); } else @@ -528,6 +790,7 @@ static int MainCore(string[] args) } // STEP 3: Load managed assembly and get function pointer to a managed method + // Windows 上使用 UTF-16 (wchar_t*) var dotnetlib_path_ = Marshal.StringToHGlobalUni(dotnetlib_path); var dotnet_type_ = Marshal.StringToHGlobalUni(dotnet_type); var dotnet_type_method_ = Marshal.StringToHGlobalUni(dotnet_type_method); @@ -605,9 +868,10 @@ enum Architecture Arm64 = 3, //Wasm = 4, //S390x = 5, - //LoongArch64 = 6, // SkiaSharp incompatibility. + LoongArch64 = 6, Armv6 = 7, //Ppc64le = 8, + RiscV64 = 9, } #endif #endif diff --git a/src/BD.WTTS.Client.Tools.Publish/Commands/IDotNetPublishCommand.cs b/src/BD.WTTS.Client.Tools.Publish/Commands/IDotNetPublishCommand.cs index 507930bec2e..79d1da7239e 100644 --- a/src/BD.WTTS.Client.Tools.Publish/Commands/IDotNetPublishCommand.cs +++ b/src/BD.WTTS.Client.Tools.Publish/Commands/IDotNetPublishCommand.cs @@ -1131,9 +1131,19 @@ static void SetPublishCommandArgumentList( argumentList.Add("-f"); argumentList.Add(arg.Framework); - // 发布针对给定运行时的应用程序。 有关运行时标识符 (RID) 的列表,请参阅 RID 目录。 - argumentList.Add("-r"); - argumentList.Add(arg.RuntimeIdentifier); + if (arg.Architecture.HasValue && (Architecture.LoongArch64 | Architecture.RiscV64).HasFlag(arg.Architecture.Value)) + { + argumentList.Add($"--arch"); + argumentList.Add(ArchToString(arg.Architecture.Value)); + argumentList.Add($"--os"); + argumentList.Add("linux"); + } + else + { + // 发布针对给定运行时的应用程序。 有关运行时标识符 (RID) 的列表,请参阅 RID 目录。 + argumentList.Add("-r"); + argumentList.Add(arg.RuntimeIdentifier); + } argumentList.Add("-v"); argumentList.Add("q");