Created
March 25, 2026 06:11
-
-
Save chalkers/3509410db7d381ad1f96c542d65b5a60 to your computer and use it in GitHub Desktop.
Install .deb of xTools Studio on Omarchy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| DEFAULT_INSTALL_PATH="$HOME/Apps/xtools-studio" | |
| CREATE_LAUNCHER=0 | |
| FORCE_INSTALL=0 | |
| INSTALL_PATH="$DEFAULT_INSTALL_PATH" | |
| DEB_PATH="" | |
| usage() { | |
| cat <<'EOF' | |
| Usage: | |
| install-xtool-studio.sh [options] /path/to/xTool-Studio.deb | |
| Options: | |
| -i, --install-path PATH Install destination (default: ~/Apps/xtools-studio) | |
| -l, --launcher-app Create a launcher script, desktop entry, and ~/.local/bin symlink | |
| -f, --force Replace the install path if it already exists | |
| -h, --help Show this help | |
| Examples: | |
| install-xtool-studio.sh ~/Downloads/xTool-Studio-x64-1.4.10.deb | |
| install-xtool-studio.sh --install-path ~/Apps/xtool-studio --launcher-app ~/Downloads/xTool-Studio-x64-1.4.10.deb | |
| EOF | |
| } | |
| require_cmd() { | |
| local cmd="$1" | |
| if ! command -v "$cmd" >/dev/null 2>&1; then | |
| echo "Missing required command: $cmd" >&2 | |
| exit 1 | |
| fi | |
| } | |
| expand_path() { | |
| local raw="$1" | |
| if [[ "$raw" == "~" ]]; then | |
| printf '%s\n' "$HOME" | |
| elif [[ "$raw" == ~/* ]]; then | |
| printf '%s\n' "$HOME/${raw#~/}" | |
| else | |
| printf '%s\n' "$raw" | |
| fi | |
| } | |
| cleanup() { | |
| if [[ -n "${WORK_DIR:-}" && -d "${WORK_DIR:-}" ]]; then | |
| rm -rf "$WORK_DIR" | |
| fi | |
| } | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -i|--install-path) | |
| INSTALL_PATH="${2:-}" | |
| shift 2 | |
| ;; | |
| -l|--launcher-app) | |
| CREATE_LAUNCHER=1 | |
| shift | |
| ;; | |
| -f|--force) | |
| FORCE_INSTALL=1 | |
| shift | |
| ;; | |
| -h|--help) | |
| usage | |
| exit 0 | |
| ;; | |
| --) | |
| shift | |
| break | |
| ;; | |
| -*) | |
| echo "Unknown option: $1" >&2 | |
| usage >&2 | |
| exit 1 | |
| ;; | |
| *) | |
| if [[ -n "$DEB_PATH" ]]; then | |
| echo "Only one .deb path is supported." >&2 | |
| usage >&2 | |
| exit 1 | |
| fi | |
| DEB_PATH="$1" | |
| shift | |
| ;; | |
| esac | |
| done | |
| if [[ $# -gt 0 ]]; then | |
| echo "Unexpected arguments: $*" >&2 | |
| usage >&2 | |
| exit 1 | |
| fi | |
| if [[ -z "$DEB_PATH" ]]; then | |
| echo "Missing .deb path." >&2 | |
| usage >&2 | |
| exit 1 | |
| fi | |
| require_cmd ar | |
| require_cmd tar | |
| require_cmd npx | |
| require_cmd patchelf | |
| require_cmd readelf | |
| DEB_PATH="$(expand_path "$DEB_PATH")" | |
| INSTALL_PATH="$(expand_path "$INSTALL_PATH")" | |
| if [[ ! -f "$DEB_PATH" ]]; then | |
| echo "File not found: $DEB_PATH" >&2 | |
| exit 1 | |
| fi | |
| if [[ "${DEB_PATH##*.}" != "deb" ]]; then | |
| echo "Expected a .deb file: $DEB_PATH" >&2 | |
| exit 1 | |
| fi | |
| if [[ -e "$INSTALL_PATH" ]]; then | |
| if [[ "$FORCE_INSTALL" -ne 1 ]]; then | |
| echo "Install path already exists: $INSTALL_PATH" >&2 | |
| echo "Use --force to replace it." >&2 | |
| exit 1 | |
| fi | |
| rm -rf "$INSTALL_PATH" | |
| fi | |
| WORK_DIR="$(mktemp -d)" | |
| trap cleanup EXIT | |
| DEB_EXTRACT_DIR="$WORK_DIR/deb" | |
| ASAR_EXTRACT_DIR="$WORK_DIR/app" | |
| mkdir -p "$DEB_EXTRACT_DIR" "$ASAR_EXTRACT_DIR" "$INSTALL_PATH" | |
| echo "Extracting Debian package..." | |
| ( | |
| cd "$DEB_EXTRACT_DIR" | |
| ar x "$DEB_PATH" | |
| ) | |
| DATA_ARCHIVE="" | |
| for candidate in \ | |
| "$DEB_EXTRACT_DIR/data.tar.zst" \ | |
| "$DEB_EXTRACT_DIR/data.tar.xz" \ | |
| "$DEB_EXTRACT_DIR/data.tar.gz" \ | |
| "$DEB_EXTRACT_DIR/data.tar"; do | |
| if [[ -f "$candidate" ]]; then | |
| DATA_ARCHIVE="$candidate" | |
| break | |
| fi | |
| done | |
| if [[ -z "$DATA_ARCHIVE" ]]; then | |
| echo "Could not find data.tar.* inside $DEB_PATH" >&2 | |
| exit 1 | |
| fi | |
| case "$DATA_ARCHIVE" in | |
| *.tar.zst) tar --zstd -xf "$DATA_ARCHIVE" -C "$INSTALL_PATH" ;; | |
| *.tar.xz) tar -xJf "$DATA_ARCHIVE" -C "$INSTALL_PATH" ;; | |
| *.tar.gz) tar -xzf "$DATA_ARCHIVE" -C "$INSTALL_PATH" ;; | |
| *.tar) tar -xf "$DATA_ARCHIVE" -C "$INSTALL_PATH" ;; | |
| esac | |
| ASAR_PATH="$INSTALL_PATH/usr/lib/xtool-studio/resources/app.asar" | |
| UNPACKED_NODE_PATH="$INSTALL_PATH/usr/lib/xtool-studio/resources/app.asar.unpacked/node_modules/@xtool/atomm_cpp_module/prebuild/electron/36.2/atomm_cpp_module.node" | |
| EXTRACTED_NODE_PATH="$ASAR_EXTRACT_DIR/node_modules/@xtool/atomm_cpp_module/prebuild/electron/36.2/atomm_cpp_module.node" | |
| if [[ ! -f "$ASAR_PATH" ]]; then | |
| echo "Could not find app.asar at: $ASAR_PATH" >&2 | |
| exit 1 | |
| fi | |
| if [[ ! -f "$UNPACKED_NODE_PATH" ]]; then | |
| echo "Could not find native addon at: $UNPACKED_NODE_PATH" >&2 | |
| exit 1 | |
| fi | |
| echo "Extracting app.asar..." | |
| npx -y @electron/asar extract "$ASAR_PATH" "$ASAR_EXTRACT_DIR" | |
| if [[ ! -f "$EXTRACTED_NODE_PATH" ]]; then | |
| echo "Could not find embedded native addon in extracted app.asar." >&2 | |
| exit 1 | |
| fi | |
| echo "Patching native addon exec-stack flags..." | |
| patchelf --clear-execstack "$UNPACKED_NODE_PATH" | |
| patchelf --clear-execstack "$EXTRACTED_NODE_PATH" | |
| if [[ ! -f "$ASAR_PATH.bak-original" ]]; then | |
| cp "$ASAR_PATH" "$ASAR_PATH.bak-original" | |
| fi | |
| echo "Repacking app.asar..." | |
| npx -y @electron/asar pack "$ASAR_EXTRACT_DIR" "$ASAR_PATH" | |
| echo "Verifying ELF flags..." | |
| readelf -W -l "$UNPACKED_NODE_PATH" | grep 'GNU_STACK' || true | |
| if [[ "$CREATE_LAUNCHER" -eq 1 ]]; then | |
| LAUNCHER_SCRIPT="$INSTALL_PATH/xtool-studio" | |
| LOCAL_BIN_DIR="$HOME/.local/bin" | |
| DESKTOP_DIR="$HOME/.local/share/applications" | |
| DESKTOP_FILE="$DESKTOP_DIR/xtool-studio-local.desktop" | |
| ICON_PATH="$INSTALL_PATH/usr/share/pixmaps/xtool-studio.png" | |
| BINARY_PATH="$INSTALL_PATH/usr/lib/xtool-studio/xTool Studio" | |
| mkdir -p "$LOCAL_BIN_DIR" "$DESKTOP_DIR" | |
| cat >"$LAUNCHER_SCRIPT" <<EOF | |
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| env ELECTRON_OZONE_PLATFORM_HINT=x11 "$BINARY_PATH" --gtk-version=3 --ozone-platform=x11 "\$@" | |
| EOF | |
| chmod +x "$LAUNCHER_SCRIPT" | |
| ln -sfn "$LAUNCHER_SCRIPT" "$LOCAL_BIN_DIR/xtool-studio-local" | |
| cat >"$DESKTOP_FILE" <<EOF | |
| [Desktop Entry] | |
| Type=Application | |
| Version=1.0 | |
| Name=xTool Studio (Local) | |
| Comment=xTool Studio patched for Linux loader compatibility | |
| Exec=$LAUNCHER_SCRIPT | |
| Icon=$ICON_PATH | |
| Terminal=false | |
| Categories=Graphics; | |
| StartupNotify=true | |
| EOF | |
| echo "Launcher created:" | |
| echo " Script: $LAUNCHER_SCRIPT" | |
| echo " Symlink: $LOCAL_BIN_DIR/xtool-studio-local" | |
| echo " Desktop entry: $DESKTOP_FILE" | |
| fi | |
| echo | |
| echo "Install complete:" | |
| echo " Deb: $DEB_PATH" | |
| echo " Install path: $INSTALL_PATH" | |
| echo " Binary: $INSTALL_PATH/usr/lib/xtool-studio/xTool Studio" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment