#!/bin/bash
# DJI Assistant 2 For Phantom 2.0.10.4 在 macOS 26 以上，韌體／Fly Safe 更新與 Calibrate Now 會卡 loading。
# 修法：讓內建的 Qt 5.6.0 用 RTLD_LOCAL 載入 DJI 的 service plugin（QtCore 改 2 bytes）。
# 說明：https://blog.sean.taipei/2026/10/dji-assistant-macos-zh
#
# 用法：bash dji-assistant2-mac-fix.sh [status|apply|restore] [App 路徑]
set -euo pipefail

ACTION="${1:-status}"
APP="${2:-/Applications/DJI Assistant 2 For Phantom.app}"
QTCORE="$APP/Contents/Frameworks/QtCore.framework/Versions/5/QtCore"
BACKUP_DIR="$HOME/Library/Application Support/dji-assistant2-mac-fix"
BACKUP="$BACKUP_DIR/QtCore.orig"

ORIG_SHA="f8dd6d02253d627c17e58cc04d8dbf13fc1fdcf127fd8db82543a10a80e71b4d"
FIXED_SHA="c0af2399ef046f820f839e1c88bf3a1e5a8387a7cd380d285dd328e60bc37128"
# Qt 5.6 只在 MacintoshVersion >= MV_10_4 時加 RTLD_LOCAL，但它把 macOS 11 以後都算成 0。
# 這段是 `cmpl $0x5, (MacintoshVersion); jle`，把 jle 換成兩個 nop 就一律加上 RTLD_LOCAL。
CHECK_OFFSET=$((0x1dba7d))
CHECK_BYTES="83 38 05 7e 0a"
PATCH_OFFSET=$((0x1dba80))

die() { echo "錯誤：$*" >&2; exit 1; }

sha() { shasum -a 256 "$1" | awk '{print $1}'; }

bytes_at() { od -An -tx1 -j "$1" -N "$2" "$3" | tr -s ' ' | sed 's/^ //;s/ $//'; }

state() {
	case "$(sha "$QTCORE")" in
		"$ORIG_SHA") echo original ;;
		"$FIXED_SHA") echo fixed ;;
		*) echo unknown ;;
	esac
}

[[ -f "$QTCORE" ]] || die "找不到 ${QTCORE}。App 不在預設位置的話，把路徑當第二個參數傳進來。"

require_quit() {
	if pgrep -x DJIService >/dev/null || pgrep -f "$APP/Contents/MacOS/DJIBrowser" >/dev/null; then
		die "DJI Assistant 還開著，先把它完全關掉再執行。"
	fi
}

case "$ACTION" in
	status)
		case "$(state)" in
			original) echo "原版 QtCore，還沒修。執行：bash $0 apply" ;;
			fixed) echo "已經修好了。要還原：bash $0 restore" ;;
			unknown) echo "QtCore 跟已知的 2.0.10.4 版本不同，這支腳本不處理。請參考文章裡的 prompt。" ;;
		esac
		;;
	apply)
		require_quit
		case "$(state)" in
			fixed) echo "已經修好了，不用再做。"; exit 0 ;;
			unknown) die "QtCore 跟已知的 2.0.10.4 版本不同，為了安全不動它。請參考文章裡的 prompt。" ;;
		esac
		[[ "$(bytes_at "$CHECK_OFFSET" 5 "$QTCORE")" == "$CHECK_BYTES" ]] || die "目標位置的內容不符，不動它。"
		[[ -w "$QTCORE" ]] || die "沒有寫入權限。改用 sudo 執行：sudo bash $0 apply"
		mkdir -p "$BACKUP_DIR"
		cp "$QTCORE" "$BACKUP"
		[[ "$(sha "$BACKUP")" == "$ORIG_SHA" ]] || die "備份失敗，沒有修改任何東西。"
		printf '\x90\x90' | dd of="$QTCORE" bs=1 seek="$PATCH_OFFSET" conv=notrunc status=none
		if [[ "$(state)" != fixed ]]; then
			cp "$BACKUP" "$QTCORE"
			die "修改後的檔案驗證失敗，已經還原成原版。"
		fi
		echo "完成。原檔備份在：$BACKUP"
		echo "如果飛機正開著，請先把飛機關機再開機，再開 DJI Assistant。"
		;;
	restore)
		require_quit
		case "$(state)" in
			original) echo "已經是原版了。"; exit 0 ;;
		esac
		[[ -f "$BACKUP" && "$(sha "$BACKUP")" == "$ORIG_SHA" ]] || die "找不到有效的備份（${BACKUP}）。"
		cp "$BACKUP" "$QTCORE"
		[[ "$(state)" == original ]] || die "還原後驗證失敗。"
		echo "已還原成原版 QtCore。"
		;;
	*)
		die "不認得的動作：${ACTION}（可用：status、apply、restore）"
		;;
esac
