#!/bin/sh

## hyphop ##

#= Rockchip burn online
#< https://github.com/hyphop/rockchip-burn

VER=0.161

HELP(){ cat <<HELP_
USAGE

[ENV] rockchip-burn [board] [IMAGE_NAME|TAG|INDEX] [ACTION] [ARGS...]

## VER $VER

BOARD

    edge2 | edge-2l

    If board is not specified, it is auto-detected from USB.
    If USB detection fails, default is edge2.

TAGS

    oowow      exclusive OOWOW/SPI mode
    android    normal images catalog filter
    ubuntu     normal images catalog filter

INDEX

    Index from --list output. Accepted anywhere in arguments.

    ./rockchip-burn edge-2l --list
    ./rockchip-burn edge-2l 1 --dl
    ./rockchip-burn edge-2l --write 2

    ./rockchip-burn edge-2l oowow --list
    ./rockchip-burn edge-2l oowow --dl 1
    ./rockchip-burn edge-2l oowow --write 2

IMAGE_NAME

    edge-2l-oowow-260529.000-spi.img.gz
    edge-2l-ubuntu-24.04-server-linux-6.1-fenix-1.7.6-260519.img.xz
    edge-2l-android-14-v260428.raw.img.xz

    Local file/path is used directly when it exists.
    http/https URL is used directly.
    Bare filename is resolved through catalog.

ACTIONS

    --devices   show detected USB devices only
    --list      list images only, enumerated
    --dl        download image only
    --write     download/decompress/write image
    --clean     clean selected storage
    --test      show devices and upgrade_tool version if installed
    --update    self update
    --help      show help

ARGS

    --spi       force SPI destination
    --sd        force eMMC/SD destination
    --refresh   re-download image / refresh catalog cache
    --no-reset  do not reset device after write

CLEAR USAGE

    ./rockchip-burn --devices

    ./rockchip-burn
    ./rockchip-burn edge-2l

    # normal images catalog
    ./rockchip-burn --list
    ./rockchip-burn edge-2l --list
    ./rockchip-burn edge-2l ubuntu --list
    ./rockchip-burn edge-2l android --list
    ./rockchip-burn edge-2l --dl 1
    ./rockchip-burn edge-2l --write 2

    # oowow exclusive mode
    ./rockchip-burn edge-2l oowow
    ./rockchip-burn edge-2l oowow --list
    ./rockchip-burn edge-2l oowow --dl
    ./rockchip-burn edge-2l oowow --write
    ./rockchip-burn edge-2l oowow --dl 1
    ./rockchip-burn edge-2l oowow --write 2

    # exact image
    ./rockchip-burn edge-2l-oowow-260529.000-spi.img.gz --dl
    ./rockchip-burn edge-2l-oowow-260529.000-spi.img.gz --write
    ./rockchip-burn edge-2l-ubuntu-24.04-server-linux-6.1-fenix-1.7.6-260519.img.xz --write

ONLINE USAGE

    curl dl.khadas.com/online/rockchip-burn | sh -s -
    curl dl.khadas.com/online/rockchip-burn | sh -s - --devices
    curl dl.khadas.com/online/rockchip-burn | sh -s - edge-2l --list
    curl dl.khadas.com/online/rockchip-burn | sh -s - edge-2l oowow --list
    curl dl.khadas.com/online/rockchip-burn | sh -s - edge-2l oowow --write

ENV VAR=value

    MIRROR = dl.khadas.com | dl.khadas.cn
    BOARD  = edge2 | edge-2l
    DEBUG  = 1

HELP_
exit
}

debug(){
	[ "$DEBUG" ] || return
	echo "$@">&2
}

FAIL(){
	echo "[e] $@">&2
	exit 1
}

MSG(){
	echo "[i] $@"
}

CMD(){
	echo "### $@">&2
	"$@"
}

CMDF(){
	echo "### $@">&2
	"$@" || exit 1
}

lowcase(){
	echo "$@" | tr '[:upper:]' '[:lower:]'
}

usb_val(){
	[ -f "$1" ] || return
	IFS= read -r v < "$1"
	echo "$v"
}

usb_board_by_product(){
	case $1 in
		edge-2l-*|edge2l-*)
		echo edge-2l
		;;
		edge2-*)
		echo edge2
		;;
	esac
}

usb_board_by_id(){
	case $1 in
		2207:350e)
		echo edge-2l
		;;
		2207:350b)
		echo edge2
		;;
	esac
}

usb_board_find(){
	local d
	local v
	local p
	local product
	local b

	# 1st pass: Khadas rescue/RNDIS with product string
	for d in /sys/bus/usb/devices/*; do
		[ -f "$d/idVendor" ] || continue

		v=$(usb_val "$d/idVendor")
		p=$(usb_val "$d/idProduct")
		product=$(usb_val "$d/product")

		case "$v:$p" in
			0b05:7774)
			b=$(usb_board_by_product "$product")
			[ "$b" ] && echo "$b" && return
			;;
		esac
	done

	# 2nd pass: Rockchip burn/loader IDs
	for d in /sys/bus/usb/devices/*; do
		[ -f "$d/idVendor" ] || continue

		v=$(usb_val "$d/idVendor")
		p=$(usb_val "$d/idProduct")

		b=$(usb_board_by_id "$v:$p")
		[ "$b" ] && echo "$b" && return
	done
}

usb_list(){
	local d
	local v
	local p
	local product
	local manufacturer
	local serial
	local board
	local mode

	echo "Devices:"

	for d in /sys/bus/usb/devices/*; do
		[ -f "$d/idVendor" ] || continue

		v=$(usb_val "$d/idVendor")
		p=$(usb_val "$d/idProduct")
		product=$(usb_val "$d/product")
		manufacturer=$(usb_val "$d/manufacturer")
		serial=$(usb_val "$d/serial")

		board=
		mode=

		case "$v:$p" in
			0b05:7774)
			board=$(usb_board_by_product "$product")
			mode=rescue
			;;
			2207:330c)
			mode=rockchip-maskrom
			;;
			2207:350b)
			board=edge2
			mode=rockchip-burn
			;;
			2207:350e)
			board=edge-2l
			mode=rockchip-burn
			;;
			*)
			continue
			;;
		esac

		printf "  %s:%s" "$v" "$p"
		[ "$mode" ] && printf " mode=%s" "$mode"
		[ "$board" ] && printf " board=%s" "$board"
		[ "$product" ] && printf " product='%s'" "$product"
		[ "$manufacturer" ] && printf " manufacturer='%s'" "$manufacturer"
		[ "$serial" ] && printf " serial='%s'" "$serial"
		echo
	done

	board=$(usb_board_find)
	[ "$board" ] && echo "AUTO_BOARD $board"
}

UP(){
	gz=${GZ-$(command -v pigz || command -v gzip)}
	xz=${XZ-$(command -v pixz || command -v xz)}
	get=${GET-$(command -v curl || command -v wget)}
}

INSTALL(){
	MSG "install $@, need admin permissions!"
	CMD sudo apt install "$@" || FAIL "please install it manually"
	UP
}

need_get(){
	UP
	[ "$get" ] && return

	MSG "need install wget or curl"
	INSTALL wget

	UP
	[ "$get" ] || FAIL "curl or wget not found"
}

need_xz(){
	UP
	[ "$xz" ] && return

	MSG "need install xz or pixz"
	INSTALL pixz xz

	UP
	[ "$xz" ] || FAIL "xz or pixz not found"
}

GET(){
	need_get

	(
	[ "$3" ] && CMD cd "$3"

	case $get in
		*wget*)
		CMD wget "$2" -O"$1"
		;;
		*curl*)
		CMD curl -fR -jkL "$2" -o"$1"
		;;
		*)
		FAIL "curl or wget not found"
		;;
	esac
	)
}

CHK(){
	local f
	local url
	local dir
	local out

	f=$1
	url=$2
	dir=$3
	out=$dir$f

	if [ -s "$out.okay" ] && [ -s "$out.url" ] && grep -qxF "$url" "$out.url" 2>/dev/null; then
		return 0
	fi

	rm -f "$out" "$out.okay" "$out.url"

	GET "$f" "$url" "$dir" || return 1
	[ -s "$out" ] || return 1
	md5sum "$out" > "$out.okay" || return 1
	echo "$url" > "$out.url" || return 1
}

image_info(){
	local f

	IMG_BOARD=
	IMG_TYPE=
	IMG_TAG=

	f=$(basename "$1")

	case $f in
		edge-2l-*)
		IMG_BOARD=edge-2l
		;;
		edge2l-*)
		IMG_BOARD=edge-2l
		;;
		edge2-*)
		IMG_BOARD=edge2
		;;
	esac

	case $f in
		*-spi.img.gz|*-spi.img.xz|*-spi.raw.img.gz|*-spi.raw.img.xz)
		IMG_TYPE=spi
		;;
		*-sd.img.gz|*-sd.img.xz|*-sd.raw.img.gz|*-sd.raw.img.xz)
		IMG_TYPE=sd
		;;
		*.raw.img.xz|*.img.xz)
		IMG_TYPE=sd
		;;
	esac

	case $f in
		*-oowow-*)
		IMG_TAG=oowow
		;;
		*-android-*)
		IMG_TAG=android
		;;
		*-ubuntu-*)
		IMG_TAG=ubuntu
		;;
	esac
}

set_type_dst(){
	case $1 in
		spi)
		TYPE=spi
		DST=spi
		DEST=SPINOR
		;;
		sd|emmc)
		TYPE=sd
		DST=emmc
		DEST=
		;;
	esac
}

adopt_image_info(){
	image_info "$1"

	[ "$IMG_BOARD" ] && [ "$ARG_BOARD" ] && [ "$IMG_BOARD" != "$ARG_BOARD" ] && {
		FAIL "image board is $IMG_BOARD but argument requested $ARG_BOARD"
	}

	if [ "$IMG_TAG" ] && [ "$TAG" ] && [ "$IMG_TAG" != "$TAG" ]; then
		FAIL "image tag is $IMG_TAG but argument requested $TAG"
	fi

	if [ "$IMG_TAG" ] && [ ! "$TAG" ]; then
		TAG=$IMG_TAG
		TAG_SRC=image
	fi

	if [ "$IMG_TYPE" ] && [ "$OPT_TYPE" ] && [ "$IMG_TYPE" != "$OPT_TYPE" ]; then
		FAIL "image type is $IMG_TYPE but option requested $OPT_TYPE"
	fi

	if [ "$IMG_TYPE" ]; then
		TYPE=$IMG_TYPE
		TYPE_SRC=image
		set_type_dst "$TYPE"
	fi
}

normal_base(){
	echo "$MIRROR/.images/$board/"
}

normal_catalog(){
	echo "$(normal_base)_images_"
}

normal_list_base(){
	case $TAG in
		android|ubuntu)
		echo "dl/$board-$TAG-images.list"
		;;
		*)
		echo "dl/$board-images.list"
		;;
	esac
}

normal_image_paths_all(){
	need_xz

	GET - "$(normal_catalog)" 2>/dev/null |
		$xz -dc 2>/dev/null |
		tar -tf - 2>/dev/null |
		sed -n 's|^\(.*\.img\.xz\)\.meta$|\1|p' |
		sort -u
}

normal_image_paths(){
	case $TAG in
		android|ubuntu)
		normal_image_paths_all | grep "$TAG"
		;;
		*)
		normal_image_paths_all
		;;
	esac
}

enum_list(){
	awk '{ printf "[%d] %s\n", NR, $0 }'
}

get_list_normal(){
	local LL

	LL=$(normal_list_base)

	MSG "list normal images $(normal_catalog)"

	normal_image_paths |
		tee "$LL.raw" |
		enum_list |
		tee "$LL"

	echo "$(normal_base)" > "$LL.url"
}

get_list_oowow(){
	local DD
	local LL

	DD="${DL_OOWOW}versions/$board/"
	LL="dl/$board-oowow-$TYPE.list"

	MSG "list oowow images $DD"

	GET - "$DD" 2>/dev/null |
		grep -o "$board-oowow-..........-$TYPE.img.gz" |
		sort -u |
		tee "$LL.raw" |
		enum_list |
		tee "$LL"

	echo "$DD" > "$LL.url"
}

get_list(){
	case $TAG in
		oowow)
		get_list_oowow
		;;
		android|ubuntu|"")
		get_list_normal
		;;
		*)
		FAIL "unknown list tag $TAG"
		;;
	esac
}

normal_image_resolve(){
	local P

	[ "$FILE" ] || return 1

	P=$(
		normal_image_paths_all |
			awk -v f="$FILE" '
				{
					n=$0
					sub(/^.*\//, "", n)
					if (n == f) {
						print $0
						exit
					}
				}
			'
	)

	[ "$P" ] || return 1

	IMG_PATH=$P
	SRC="$(normal_base)$IMG_PATH"
	DL="$(normal_base)$(dirname "$IMG_PATH")/"

	return 0
}

index_resolve(){
	local LL
	local P

	[ "$INDEX" ] || return 0

	case $TAG in
		oowow)
		LL="dl/$board-oowow-$TYPE.list.raw"
		[ -s "$LL" ] && [ ! "$REFRESH" ] || get_list_oowow >/dev/null
		P=$(sed -n "${INDEX}p" "$LL")
		[ "$P" ] || FAIL "bad index $INDEX"

		IMG_PATH=$P
		FILE=$(basename "$P")
		SRC="${DL_OOWOW}versions/$board/$FILE"
		DL="${DL_OOWOW}versions/$board/"
		adopt_image_info "$FILE"
		;;

		android|ubuntu|"")
		LL=$(normal_list_base)
		LL="$LL.raw"
		[ -s "$LL" ] && [ ! "$REFRESH" ] || get_list_normal >/dev/null
		P=$(sed -n "${INDEX}p" "$LL")
		[ "$P" ] || FAIL "bad index $INDEX"

		IMG_PATH=$P
		FILE=$(basename "$P")
		SRC="$(normal_base)$P"
		DL="$(normal_base)$(dirname "$P")/"
		adopt_image_info "$FILE"
		;;

		*)
		FAIL "unknown tag $TAG"
		;;
	esac

	MSG "selected [$INDEX] $P"
}

resolve_image(){
	[ "$INDEX" ] && index_resolve

	[ "$FILE" ] || return 1

	# local existing file/path
	[ "$LOCAL" ] && return 0

	# direct URL
	[ "$URL" ] && return 0

	# already resolved by index or URL parser
	[ "$SRC" ] && return 0

	case $TAG in
		oowow)
		SRC="${DL_OOWOW}versions/$board/$FILE"
		DL="${DL_OOWOW}versions/$board/"
		return 0
		;;

		*)
		normal_image_resolve || FAIL "cant resolve image path for $FILE; use --list"
		return 0
		;;
	esac
}

source_label(){
	case $1 in
		image) echo "[image]" ;;
		arg) echo "[arg]" ;;
		usb) echo "[auto detected]" ;;
		default) echo "[default]" ;;
		tag) echo "[tag]" ;;
		option) echo "[option]" ;;
		index) echo "[index]" ;;
	esac
}

print_info(){
	echo
	echo "BOARD $board $(source_label "$BOARD_SRC")"
	[ "$USB_BOARD" ] && echo "USB   $USB_BOARD"
	[ "$TAG" ] && echo "TAG   $TAG $(source_label "$TAG_SRC")"
	[ "$INDEX" ] && echo "INDEX $INDEX"
	[ ! "$TAG" ] && [ ! "$FILE" ] && echo "MODE  normal images catalog"
	echo "TYPE  $TYPE $(source_label "$TYPE_SRC")"
	echo "DST   $DST"
	[ "$FILE" ] && echo "FILE  $FILE"
	[ "$IMG_PATH" ] && echo "PATH  $IMG_PATH"
	[ "$LOCAL" ] && echo "LOCAL $LOCAL"
	[ "$URL" ] && echo "URL   $URL"
	[ "$SRC" ] && echo "SRC   $SRC"
	[ "$DL" ] && echo "DL    $DL"

	echo
	echo "use:"

	case $TAG in
		oowow)
		echo "  $NAME $board oowow --list"
		echo "  $NAME $board oowow --dl"
		echo "  $NAME $board oowow --write"
		echo "  $NAME $board oowow --dl 1"
		echo "  $NAME $board oowow --write 2"
		;;
		android|ubuntu)
		echo "  $NAME $board $TAG --list"
		echo "  $NAME $board $TAG --dl 1"
		echo "  $NAME $board $TAG --write 1"
		;;
		*)
		if [ "$FILE" ]; then
			echo "  $NAME $FILE --dl"
			echo "  $NAME $FILE --write"
		else
			echo "  $NAME $board --list"
			echo "  $NAME $board --dl 1"
			echo "  $NAME $board --write 2"
			echo "  $NAME $board ubuntu --list"
			echo "  $NAME $board android --list"
			echo "  $NAME $board oowow --list"
			echo "  $NAME $board oowow --write"
		fi
		;;
	esac

	echo
}

download_image(){
	[ "$FILE" ] || FAIL "no exact image selected; use --list first or use oowow mode"

	mkdir -p dl || FAIL "mkdir dl"

	if [ "$LOCAL" ]; then
		MSG "local image $LOCAL"
		echo "$LOCAL"
		return
	fi

	[ "$SRC" ] || FAIL "no source URL resolved for $FILE"

	[ "$REFRESH" ] && {
		MSG "refresh $FILE"
		rm -f "dl/$FILE" "dl/$FILE.okay" "dl/$FILE.url"
	}

	CHK "$FILE" "$SRC" dl/ || FAIL "download fail $SRC"

	echo "$PWD/dl/$FILE"
}

decompress_image(){
	PKG=dl/$FILE
	IMG=${PKG%.*}

	case "$LOCAL" in
		"")
		;;
		*)
		PKG="$LOCAL"
		IMG=$(basename "$LOCAL")
		IMG=${IMG%.??}
		;;
	esac

	case $PKG in
		*.gz)
		[ "$gz" ] || INSTALL gzip pigz
		MSG "decompress $PKG wait..."
		CMD $gz -dc < "$PKG" > "$IMG" || FAIL "decompress fail"
		;;
		*.xz)
		[ "$xz" ] || INSTALL pixz xz
		MSG "decompress $PKG wait..."
		CMD $xz -dc < "$PKG" > "$IMG" || FAIL "decompress fail"
		;;
		*)
		IMG=$PKG
		;;
	esac
}

prepare_tool(){
	mkdir -p "$TOOL_BIN" || FAIL "mkdir $TOOL_BIN"

	ARCH=$(uname -m 2>/dev/null)

	case $ARCH in
		x86_64)
		;;
		*)
		FAIL "not supported arch $ARCH"
		;;
	esac

	(
	cd "$TOOL_BIN" || FAIL "cd $TOOL_BIN"

	CHK "$TOOL" "$TOOL_DL" || FAIL "tool"

	[ -x "$TOOL" ] || chmod 0755 "$TOOL"

	CHK "$LOADER" "$LOADER_DL" || FAIL "loader"

	UDEV=/etc/udev/rules.d/80-rockchip-usb.rules

	grep -q OKAY2 "$UDEV" 2>/dev/null || {

	MSG "install usb device permission to $UDEV, need admin permissions!"

	cat <<EOF | sudo tee "$UDEV"
#OKAY2
#Maskrom
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="2207", ATTR{idProduct}=="330c", MODE:="0666"
#Loader
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="2207", ATTR{idProduct}=="350b", MODE:="0666"
#Rockchip Maskrom / new generation
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="2207", ATTR{idProduct}=="350e", MODE:="0666"
#Khadas rescue/RNDIS
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="0b05", ATTR{idProduct}=="7774", MODE:="0666"
EOF

	}

	)

	export PATH="$TOOL_BIN:$PATH"

	command -v "$TOOL" >/dev/null 2>&1 || FAIL "cant find $TOOL"
}

loader(){
	CMDF $TOOL LD | grep Mode=Maskrom
	CMDF $TOOL DB "bin/$LOADER" $DEST
	sleep 1
	CMDF $TOOL RCI
	CMDF $TOOL RCB
	sleep 1
}

setup_dst(){
	case $DST in
		spi)
		CMD $TOOL SSD 5
		;;
		*)
		CMD $TOOL SSD 2 # EMMC
		;;
	esac

	sleep 1
}

wait_maskrom(){
	TRY=1

	while [ "1" ]; do
		$TOOL LD | grep Mode=Maskrom && break
		printf "\r[i] wait device with upgrade mode - need press KEY_FN 3x times - $TRY"
		sleep 1
		TRY=$((TRY+1))
	done

	echo
}

write_image(){
	MSG "write $IMG to $DST wait..."

	wait_maskrom

	echo "[i] check is ready ..."

	loader    || FAIL "oops loader fail"
	setup_dst || FAIL "oops can setup DST: $DST"

	CMD $TOOL WL 0 "$IMG" || FAIL "oops"

	[ "$FILE" ] && echo && echo "[i] OKAY $FILE was written to $DST"
	[ "$RESET" ] && MSG "reset device" && CMD $TOOL RD 0
	[ "$RESET" ] || MSG "no reset device"
}

clean_storage(){
	echo "Clean storage $DST"

	wait_maskrom

	loader    || FAIL "oops loader fail"
	setup_dst || FAIL "oops can setup DST: $DST"

	case $DST in
		SPI|spi)
		CMDF $TOOL EL 0x4000 0x10
		CMDF $TOOL EL 0x5000 0x10
		;;
		*)
		CMDF $TOOL EL 0x0 0x100000
		;;
	esac
}

NAME=rockchip-burn
PROG=$(realpath -s "$0" 2>/dev/null || echo "$0")
WORK=$(dirname "$PROG")

TOOL_HOME=~/upgrade_tool
TOOL_BIN="$TOOL_HOME/bin"

: ${MIRROR:=dl.khadas.com}
: ${SOURCE:=https://raw.githubusercontent.com/hyphop/rockchip-burn/main/$NAME}
: ${DL_OOWOW:=$MIRROR/products/oowow/system/}

ACTION=info
RESET=1

ARG_BOARD=${BOARD-}
IMG_BOARD=
IMG_TYPE=
IMG_TAG=
OPT_TYPE=
TAG=
TAG_SRC=
FILE=
URL=
SRC=
LOCAL=
DL=
TYPE=
TYPE_SRC=
DST=
DEST=
INDEX=
IMG_PATH=
REFRESH=

while [ "$1" ]; do
	a=$1
	shift

	case $a in
		edge2|Edge2)
		ARG_BOARD=edge2
		;;
		edge2l|edge-2l|Edge-2l|Edge-2L)
		ARG_BOARD=edge-2l
		;;

		oowow|android|ubuntu)
		TAG=$a
		TAG_SRC=arg
		;;

		http://*|https://*)
		URL=$a
		SRC=$a
		DL=$(dirname "$a")/
		FILE=$(basename "$a")
		adopt_image_info "$FILE"
		;;

		*.img.gz|*.img.xz|*.raw.img.gz|*.raw.img.xz)
		FILE=$(basename "$a")
		adopt_image_info "$FILE"

		case $a in
			*/*)
			[ -s "$a" ] || FAIL "local image not found $a"
			LOCAL=$(realpath "$a")
			MSG "use local file $LOCAL"
			;;
			*)
			if [ -s "$a" ]; then
				LOCAL=$(realpath "$a")
				MSG "use local file $LOCAL"
			fi
			;;
		esac
		;;

		--devices|--device)
		ACTION=devices
		;;
		-w|--write)
		ACTION=write
		;;
		-l|--list)
		ACTION=list
		;;
		--dl|--download|-g|--get)
		ACTION=dl
		;;
		--clean)
		ACTION=clean
		;;
		-t|--test)
		ACTION=test
		;;
		-v|--version)
		echo "$VER"
		exit
		;;
		-h|--help)
		ACTION=help
		;;
		-u|--update)
		ACTION=update
		;;
		--readme)
		echo "# $NAME"
		echo
		echo '```'
		sh "$0" --help
		echo '```'
		exit
		;;

		--spi)
		OPT_TYPE=spi
		;;
		--sd|--emmc)
		OPT_TYPE=sd
		;;
		--no-reset)
		RESET=
		;;
		-r|--refresh)
		REFRESH=1
		;;

		[0-9]*)
		case $a in
			*[!0-9]*)
			FAIL "unknown argument $a"
			;;
			*)
			[ "$INDEX" ] && FAIL "only one index allowed"
			INDEX=$a
			;;
		esac
		;;

		-*)
		FAIL "unknown option $a"
		;;

		*)
		FAIL "unknown argument $a"
		;;
	esac
done

[ "$INDEX" ] && [ "$FILE" ] && FAIL "use either image filename or index, not both"

case $ACTION in
	help)
	HELP
	;;
	devices)
	usb_list
	exit
	;;
	update)
	need_get

	RND=${RANDOM-$$}
	VER_OLD=$VER

	MSG "self update mode $PROG from $VER_OLD to ..."

	[ -d "$WORK/.git" ] && FAIL "inside Git not allowed"

	GET "$NAME.new" "$SOURCE?$RND" /tmp/ || FAIL "download update fail"
	VER_NEW=$(grep -m1 "^VER=" "/tmp/$NAME.new")
	cp "/tmp/$NAME.new" "$PROG" || FAIL "update fail..."
	MSG "OKAY self updated $VER_NEW"
	exit 0
	;;
esac

USB_BOARD=$(usb_board_find)
debug "usb_board: +$USB_BOARD+"

if [ "$IMG_BOARD" ]; then
	BOARD=$IMG_BOARD
	BOARD_SRC=image
elif [ "$ARG_BOARD" ]; then
	BOARD=$ARG_BOARD
	BOARD_SRC=arg
elif [ "$USB_BOARD" ]; then
	BOARD=$USB_BOARD
	BOARD_SRC=usb
else
	BOARD=edge2
	BOARD_SRC=default
fi

board=$(lowcase "$BOARD")

if [ "$IMG_TAG" ] && [ ! "$TAG" ]; then
	TAG=$IMG_TAG
	TAG_SRC=image
fi

[ "$TAG_SRC" ] || [ ! "$TAG" ] || TAG_SRC=arg

if [ "$IMG_TYPE" ] && [ "$OPT_TYPE" ] && [ "$IMG_TYPE" != "$OPT_TYPE" ]; then
	FAIL "image type is $IMG_TYPE but option requested $OPT_TYPE"
fi

if [ "$IMG_TYPE" ]; then
	TYPE=$IMG_TYPE
	TYPE_SRC=image
elif [ "$OPT_TYPE" ]; then
	TYPE=$OPT_TYPE
	TYPE_SRC=option
else
	case $TAG in
		oowow)
		TYPE=spi
		TYPE_SRC=tag
		;;
		*)
		TYPE=sd
		TYPE_SRC=default
		;;
	esac
fi

set_type_dst "$TYPE"

case $board in
	edge-2l)
	LOADER=rk3576_spl_loader_v1.10.108.bin
	;;
esac

: ${LOADER:=rk3588_spl_loader_v1.08.111.bin}
: ${TOOL:=upgrade_tool}
: ${TOOL_DL:=https://raw.githubusercontent.com/khadas/utils/master/rk-flash-tool/tools/Linux_Upgrade_Tool/$TOOL}
: ${LOADER_DL:=$MIRROR/products/$board/firmware/boot/$LOADER}
: ${UT:=$TOOL_BIN/$TOOL}

case $TAG in
	oowow)
	DL=${DL:-${DL_OOWOW}versions/$board/}
	[ "$FILE" ] || [ "$INDEX" ] || FILE=$board-oowow-latest-$TYPE.img.gz
	;;

	android|ubuntu)
	DL=${DL:-$(normal_base)}
	;;

	"")
	DL=${DL:-$(normal_base)}
	;;

	*)
	FAIL "unknown tag $TAG"
	;;
esac

case $FILE-$DST in
	*-spi.img.gz-emmc|*-spi.img.xz-emmc|*-spi.raw.img.gz-emmc|*-spi.raw.img.xz-emmc)
	FAIL "refuse: SPI image selected but destination is emmc"
	;;
	*-sd.img.gz-spi|*-sd.img.xz-spi|*-sd.raw.img.gz-spi|*-sd.raw.img.xz-spi)
	FAIL "refuse: SD/EMMC image selected but destination is spi"
	;;
esac

case $ACTION in
	test)
	echo "Test mode"
	usb_list
	[ -x "$UT" ] && CMD "$UT" -v
	exit
	;;

	info)
	print_info
	exit
	;;

	list|dl|write|clean)
	;;

	*)
	FAIL "unknown action $ACTION"
	;;
esac

mkdir -p "$TOOL_HOME/dl" || FAIL "mkdir dl"
CMD cd "$TOOL_HOME" || FAIL "cd fail"

case $ACTION in
	list)
	get_list
	exit
	;;

	dl)
	resolve_image || FAIL "no exact image selected; use --list first or use oowow mode"

	echo MODE dl
	echo BOARD "$board"
	[ "$TAG" ] && echo TAG "$TAG"
	[ "$INDEX" ] && echo INDEX "$INDEX"
	echo FILE "$FILE"
	[ "$IMG_PATH" ] && echo PATH "$IMG_PATH"
	echo TYPE "$TYPE"
	echo DST "$DST"
	echo SRC "$SRC"
	echo DL "$DL"

	download_image
	exit
	;;

	write)
	resolve_image || FAIL "no exact image selected; use --list first or use oowow mode"

	echo MODE write
	echo BOARD "$board"
	[ "$TAG" ] && echo TAG "$TAG"
	[ "$INDEX" ] && echo INDEX "$INDEX"
	echo FILE "$FILE"
	[ "$IMG_PATH" ] && echo PATH "$IMG_PATH"
	echo TYPE "$TYPE"
	echo DST "$DST"
	echo SRC "$SRC"
	echo DL "$DL"

	prepare_tool
	download_image
	decompress_image
	write_image
	exit
	;;

	clean)
	echo MODE clean
	echo BOARD "$board"
	echo TYPE "$TYPE"
	echo DST "$DST"

	prepare_tool
	clean_storage
	exit
	;;
esac

exit
<<EOF

# edge2l rescue / RNDIS
Bus 008 Device 006: ID 0b05:7774 ASUSTek Computer, Inc.
iManufacturer rescueip:172.22.1.1:172.22.1.2
iProduct edge-2l-14aa0

# edge2l Rockchip burn
Bus 007 Device 011: ID 2207:350e Fuzhou Rockchip Electronics Company

# edge2 Rockchip burn
Bus 007 Device 035: ID 2207:350b Fuzhou Rockchip Electronics Company

EOF