#!/sbin/sh

OUTFD="/proc/self/fd/$2"
ZIPFILE="$3"
TARGET_DEVICES=xuanyuan
READONLY_BLOCKS=
BLOCK_IDS=
STAGED_IMAGE="/tmp/xffc-image-$$"
umask 077

ui_print() {
  printf 'ui_print %s\nui_print\n' "$1" >> "$OUTFD"
}

abort() {
  ui_print "$1"
  exit 1
}

require_tools() {
  for tool in blockdev dd getprop head readlink rm sed sha256sum stat tr unzip; do
    command -v "$tool" >/dev/null 2>&1 || abort "[ERROR] Missing required command: $tool"
  done
  (set -o pipefail) 2>/dev/null || abort "[ERROR] Recovery shell does not support pipefail"
  set -o pipefail

  blockdev_help=$(blockdev --help 2>&1 || :)
  for option in --getsize64 --getro --setrw --setro; do
    case "$blockdev_help" in *"$option"*) ;; *)
      abort "[ERROR] blockdev does not support $option" ;;
    esac
  done

  probe="/tmp/xffc-dd-probe-$$"
  printf x | dd of="$probe" bs=1 conv=fsync >/dev/null 2>&1 || {
    rm -f "$probe"
    abort "[ERROR] dd does not support conv=fsync"
  }
  [ "$(dd if="$probe" iflag=count_bytes count=1 2>/dev/null)" = x ] || {
    rm -f "$probe"
    abort "[ERROR] dd does not support byte-count reads"
  }
  rm -f "$probe"
}

is_target_device() {
  value="$1"
  for target in $TARGET_DEVICES; do
    [ "$value" = "$target" ] && return 0
  done
  return 1
}

is_target_device_list() {
  values=$(printf '%s' "$1" | tr '|,:' '\n\n\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
  while IFS= read -r value; do
    is_target_device "$value" && return 0
  done <<EOF
$values
EOF
  return 1
}

validate_device() {
  for property in ro.product.device ro.product.name ro.product.vendor.device; do
    is_target_device "$(getprop "$property" 2>/dev/null)" && return 0
  done
  is_target_device_list "$(getprop ro.twrp.target.devices 2>/dev/null)" && return 0
  abort "[ERROR] This firmware is only for: $TARGET_DEVICES"
}

read_bootarg() {
  key="$1"
  for source in /proc/cmdline /proc/bootconfig; do
    [ -r "$source" ] || continue
    value=$(
      sed -e 's/ = /=/g' -e 's/"//g' "$source" |
        tr ' ' '\n' |
        sed -n "s/^${key}=//p" |
        head -n 1
    )
    [ -n "$value" ] && printf '%s\n' "$value" && return 0
  done
  return 1
}

set_slots() {
  active=$(getprop ro.boot.slot_suffix 2>/dev/null)
  [ -n "$active" ] || active=$(getprop ro.boot.slot 2>/dev/null)
  [ -n "$active" ] || active=$(read_bootarg androidboot.slot_suffix)
  [ -n "$active" ] || active=$(read_bootarg androidboot.slot)

  case "$active" in
    a|_a) ACTIVE_SLOT=a; INACTIVE_SLOT=b ;;
    b|_b) ACTIVE_SLOT=b; INACTIVE_SLOT=a ;;
    *) abort "[ERROR] Unable to determine active slot" ;;
  esac
}

check_image() {
  partition="$1"
  expected_hash="$2"
  image="firmware-update/$partition.img"
  output=$(unzip -p "$ZIPFILE" "$image" | sha256sum) ||
    abort "[ERROR] Could not read firmware image: $partition"
  actual_hash=${output%% *}
  [ "$actual_hash" = "$expected_hash" ] ||
    abort "[ERROR] Invalid firmware image: $partition"
}

resolve_partition() {
  partition="$1"
  slot="$2"
  for root in \
    /dev/block/by-name \
    /dev/block/bootdevice/by-name \
    /dev/block/platform/*/by-name
  do
    block="$root/${partition}_${slot}"
    if [ -b "$block" ]; then
      readlink -f "$block"
      return
    fi
  done
  return 1
}

restore_readonly() {
  result=0
  for block in $READONLY_BLOCKS; do
    blockdev --setro "$block" >/dev/null 2>&1 || result=1
  done
  return "$result"
}

cleanup() {
  restore_readonly
  rm -f "$STAGED_IMAGE"
}

preflight_partition() {
  partition="$1"
  image_size="$2"

  for slot in a b; do
    block=$(resolve_partition "$partition" "$slot") ||
      abort "[ERROR] Missing block device: ${partition}_${slot}"
    device_id=$(stat -L -c '%t:%T' "$block" 2>/dev/null) ||
      abort "[ERROR] Could not identify block device: ${partition}_${slot}"
    case " $BLOCK_IDS " in
      *" $device_id "*) abort "[ERROR] Duplicate block device: ${partition}_${slot}" ;;
    esac
    BLOCK_IDS="$BLOCK_IDS $device_id"
    eval "BLOCK_${partition}_${slot}=\$block"
    eval "BLOCK_ID_${partition}_${slot}=\$device_id"

    block_size=$(blockdev --getsize64 "$block" 2>/dev/null) ||
      abort "[ERROR] Could not read partition size: ${partition}_${slot}"
    case "$block_size" in
      ''|*[!0-9]*) abort "[ERROR] Invalid partition size: ${partition}_${slot}" ;;
    esac
    [ "$image_size" -le "$block_size" ] ||
      abort "[ERROR] Image is larger than partition: ${partition}_${slot}"

    read_only=$(blockdev --getro "$block" 2>/dev/null) ||
      abort "[ERROR] Could not read partition state: ${partition}_${slot}"
    case "$read_only" in
      0) ;;
      1)
        blockdev --setrw "$block" >/dev/null 2>&1 ||
          abort "[ERROR] Could not make partition writable: ${partition}_${slot}"
        READONLY_BLOCKS="$READONLY_BLOCKS $block"
        [ "$(blockdev --getro "$block" 2>/dev/null)" = 0 ] ||
          abort "[ERROR] Partition is read-only: ${partition}_${slot}"
        ;;
      *) abort "[ERROR] Invalid partition state: ${partition}_${slot}" ;;
    esac
  done
}

flash_partition() {
  partition="$1"
  image_size="$2"
  expected_hash="$3"
  slot="$4"
  image="firmware-update/$partition.img"
  eval "block=\$BLOCK_${partition}_${slot}"
  eval "expected_device_id=\$BLOCK_ID_${partition}_${slot}"

  unzip -p "$ZIPFILE" "$image" > "$STAGED_IMAGE" ||
    abort "[ERROR] Could not stage firmware image: $partition"
  [ "$(stat -c '%s' "$STAGED_IMAGE" 2>/dev/null)" = "$image_size" ] ||
    abort "[ERROR] Staged image has wrong size: $partition"
  output=$(sha256sum "$STAGED_IMAGE") || abort "[ERROR] Could not hash staged image: $partition"
  actual_hash=${output%% *}
  [ "$actual_hash" = "$expected_hash" ] || abort "[ERROR] Invalid staged image: $partition"

  [ -b "$block" ] || abort "[ERROR] Block device disappeared: ${partition}_${slot}"
  [ "$(stat -L -c '%t:%T' "$block" 2>/dev/null)" = "$expected_device_id" ] ||
    abort "[ERROR] Block device changed: ${partition}_${slot}"
  ui_print "[INFO] Updating ${partition}_${slot}..."
  dd if="$STAGED_IMAGE" of="$block" bs=1048576 conv=fsync ||
    abort "[ERROR] Failed to flash ${partition}_${slot}"
  rm -f "$STAGED_IMAGE"

  output=$(dd if="$block" iflag=count_bytes count="$image_size" 2>/dev/null | sha256sum) ||
    abort "[ERROR] Failed to verify ${partition}_${slot}"
  actual_hash=${output%% *}
  [ "$actual_hash" = "$expected_hash" ] ||
    abort "[ERROR] Verification failed: ${partition}_${slot}"
}

flash_slot() {
  slot="$1"
flash_partition abl 335872 ffade8379ccd2f15fb68e0b945acde08581474c01dc0e9d8a867dc6026757700 "$slot"
flash_partition aop 372736 017bb2c20ede090726a63a023df50ede558e9ddfd2714ffec3025912a5b20f65 "$slot"
flash_partition aop_config 32768 9482b2db090baa3fc5c10e8d23b33d251c84bd3a9370e306ae3ea003b55eaf69 "$slot"
flash_partition bluetooth 3993600 01268abab395ffcd1a882c97d2e59be46830aebb0ece16afe0201d136d7f4275 "$slot"
flash_partition countrycode 1048576 13c1bbf38252bbc8f7cd2d417cb0c4fe32a3ee37b2cfef5bd30c43fe1bcbe92f "$slot"
flash_partition cpucp 319488 29fb8174f9bd0adf054d0acd4fe6ac9b3dd894b645a7cdc8d927c035805c9ddc "$slot"
flash_partition cpucp_dtb 16384 441eee496177c94b97ff3da2b7de50eb863a9cd261e96624eacb461bc087a66b "$slot"
flash_partition devcfg 61440 90fa7a5b2a3e8f241bc14695f9b206e1e9ae0084e5c7f85c9b9b57710b27d83b "$slot"
flash_partition dsp 67108864 50c651d5b553a57c9c235ee0f5d019ee95f2c0218eae7e929be0e3c3ed876199 "$slot"
flash_partition featenabler 106496 ce6d57a4d420fd2bd668a311a21f800a7a187878c9e0c66b68d68357c5d9dbe4 "$slot"
flash_partition hyp 1613824 e674e19e88e9ee657dccea91534234cd68d2be63af406314bcb0e91999bf7d65 "$slot"
flash_partition idmanager 69632 6aba89c850bbb1e3f133bdf1c0a7fd62a87315b64ec6507695064f6861589d4e "$slot"
flash_partition imagefv 6299648 0f9847311a3e0f83e091e10c95371989b7973e5b646de930c9b30db47861668c "$slot"
flash_partition keymaster 454656 6e1c05b9256761ca0ddd2e799427baf20cc40487f0aae7956328d486ebeabfa4 "$slot"
flash_partition modem 172568576 abdb08bc80e2b9c837b40f54df5c34c1ea929b5d1c14e7cfebfc0b433b9a6d69 "$slot"
flash_partition modemfirmware 135614464 17473155091969cc2b18f9fe705ee739aecd3f21fab81e9f5d91fddbd745ff72 "$slot"
flash_partition multiimgqti 12288 bd619dd9ef992af0ee627fbb15cac9082df167d8f47da9b1ae814bc1f6c8db77 "$slot"
flash_partition pdp 212992 824b5495d7f20e2e72ac66ee0fcd02f591a867e04928e0c2eb704bbc11c6ccca "$slot"
flash_partition pdp_cdb 16384 bd4a4fbb121b98b080780145f074e7c7c3d04b38fc6d92938bcf2acd094c0cad "$slot"
flash_partition pvmfw 1048576 9a8ac1806da23a702c411995354027be890b4e6873b69142ff5e014bbd81bd26 "$slot"
flash_partition qupfw 65536 d3ff0911b2ade01cfe7f4910b39003f2ad18d19b275f6550bc65b9b557d90744 "$slot"
flash_partition shrm 151552 996fba330c116c122f0c6849d775d2150a9bb86f7c82db5cebcea7da5fa66a7c "$slot"
flash_partition soccp_dcd 20480 47fab08c5cfe03113134ad08192466b4d2b3a411b692483f502ab3ba2a25544f "$slot"
flash_partition soccp_debug 147456 864c29f2cd4ed4abb8c1130eb9be57de14b286b162db3f3f80ac7a2626254b8c "$slot"
flash_partition spuservice 94208 2a2ae20c5ef90c0def54931cae6d841f15fdea1cd20baa48cfeaba4569bdf6cf "$slot"
flash_partition tz 4173824 8aa45df692a92e139122a8270137df8eeb6b25522333cc2d6a7f71a32bde30fe "$slot"
flash_partition uefi 2842624 b837d12e352123b20abb20e43c174f844c36e6aef1a01d0006657e4a3d0e11b0 "$slot"
flash_partition uefisecapp 204800 c047e7accf05f589c2a8c27e7279a33a72f3b42640d6d409dbb10e9b44397f92 "$slot"
flash_partition vm-bootsys 20971520 9f09b49decd93492f0e0d69f934d6777a760524b898d09485f66fa8214a0a738 "$slot"
flash_partition xbl 1191936 cb3e18f64702ab1508d2f41df24db70ffd12ff5894114c397eaf02acee4c7d6f "$slot"
flash_partition xbl_config 348160 bd1ec988c82085c177b10a5e522bffc73773ba68932c455a40fc484b087033a3 "$slot"
flash_partition xbl_ramdump 942080 9f8d7d0edcbcb9a75ed55b8efabdb7e8d5c91674e87dfac0d55434101ac80a30 "$slot"
}

trap cleanup 0
trap 'exit 1' HUP INT TERM

ui_print "####################################################"
ui_print "# Generated by Xiaomi Flashable Firmware Creator"
ui_print "####################################################"
ui_print " "

require_tools
validate_device
set_slots
rm -f "$STAGED_IMAGE"

check_image abl ffade8379ccd2f15fb68e0b945acde08581474c01dc0e9d8a867dc6026757700
check_image aop 017bb2c20ede090726a63a023df50ede558e9ddfd2714ffec3025912a5b20f65
check_image aop_config 9482b2db090baa3fc5c10e8d23b33d251c84bd3a9370e306ae3ea003b55eaf69
check_image bluetooth 01268abab395ffcd1a882c97d2e59be46830aebb0ece16afe0201d136d7f4275
check_image countrycode 13c1bbf38252bbc8f7cd2d417cb0c4fe32a3ee37b2cfef5bd30c43fe1bcbe92f
check_image cpucp 29fb8174f9bd0adf054d0acd4fe6ac9b3dd894b645a7cdc8d927c035805c9ddc
check_image cpucp_dtb 441eee496177c94b97ff3da2b7de50eb863a9cd261e96624eacb461bc087a66b
check_image devcfg 90fa7a5b2a3e8f241bc14695f9b206e1e9ae0084e5c7f85c9b9b57710b27d83b
check_image dsp 50c651d5b553a57c9c235ee0f5d019ee95f2c0218eae7e929be0e3c3ed876199
check_image featenabler ce6d57a4d420fd2bd668a311a21f800a7a187878c9e0c66b68d68357c5d9dbe4
check_image hyp e674e19e88e9ee657dccea91534234cd68d2be63af406314bcb0e91999bf7d65
check_image idmanager 6aba89c850bbb1e3f133bdf1c0a7fd62a87315b64ec6507695064f6861589d4e
check_image imagefv 0f9847311a3e0f83e091e10c95371989b7973e5b646de930c9b30db47861668c
check_image keymaster 6e1c05b9256761ca0ddd2e799427baf20cc40487f0aae7956328d486ebeabfa4
check_image modem abdb08bc80e2b9c837b40f54df5c34c1ea929b5d1c14e7cfebfc0b433b9a6d69
check_image modemfirmware 17473155091969cc2b18f9fe705ee739aecd3f21fab81e9f5d91fddbd745ff72
check_image multiimgqti bd619dd9ef992af0ee627fbb15cac9082df167d8f47da9b1ae814bc1f6c8db77
check_image pdp 824b5495d7f20e2e72ac66ee0fcd02f591a867e04928e0c2eb704bbc11c6ccca
check_image pdp_cdb bd4a4fbb121b98b080780145f074e7c7c3d04b38fc6d92938bcf2acd094c0cad
check_image pvmfw 9a8ac1806da23a702c411995354027be890b4e6873b69142ff5e014bbd81bd26
check_image qupfw d3ff0911b2ade01cfe7f4910b39003f2ad18d19b275f6550bc65b9b557d90744
check_image shrm 996fba330c116c122f0c6849d775d2150a9bb86f7c82db5cebcea7da5fa66a7c
check_image soccp_dcd 47fab08c5cfe03113134ad08192466b4d2b3a411b692483f502ab3ba2a25544f
check_image soccp_debug 864c29f2cd4ed4abb8c1130eb9be57de14b286b162db3f3f80ac7a2626254b8c
check_image spuservice 2a2ae20c5ef90c0def54931cae6d841f15fdea1cd20baa48cfeaba4569bdf6cf
check_image tz 8aa45df692a92e139122a8270137df8eeb6b25522333cc2d6a7f71a32bde30fe
check_image uefi b837d12e352123b20abb20e43c174f844c36e6aef1a01d0006657e4a3d0e11b0
check_image uefisecapp c047e7accf05f589c2a8c27e7279a33a72f3b42640d6d409dbb10e9b44397f92
check_image vm-bootsys 9f09b49decd93492f0e0d69f934d6777a760524b898d09485f66fa8214a0a738
check_image xbl cb3e18f64702ab1508d2f41df24db70ffd12ff5894114c397eaf02acee4c7d6f
check_image xbl_config bd1ec988c82085c177b10a5e522bffc73773ba68932c455a40fc484b087033a3
check_image xbl_ramdump 9f8d7d0edcbcb9a75ed55b8efabdb7e8d5c91674e87dfac0d55434101ac80a30

preflight_partition abl 335872
preflight_partition aop 372736
preflight_partition aop_config 32768
preflight_partition bluetooth 3993600
preflight_partition countrycode 1048576
preflight_partition cpucp 319488
preflight_partition cpucp_dtb 16384
preflight_partition devcfg 61440
preflight_partition dsp 67108864
preflight_partition featenabler 106496
preflight_partition hyp 1613824
preflight_partition idmanager 69632
preflight_partition imagefv 6299648
preflight_partition keymaster 454656
preflight_partition modem 172568576
preflight_partition modemfirmware 135614464
preflight_partition multiimgqti 12288
preflight_partition pdp 212992
preflight_partition pdp_cdb 16384
preflight_partition pvmfw 1048576
preflight_partition qupfw 65536
preflight_partition shrm 151552
preflight_partition soccp_dcd 20480
preflight_partition soccp_debug 147456
preflight_partition spuservice 94208
preflight_partition tz 4173824
preflight_partition uefi 2842624
preflight_partition uefisecapp 204800
preflight_partition vm-bootsys 20971520
preflight_partition xbl 1191936
preflight_partition xbl_config 348160
preflight_partition xbl_ramdump 942080

ui_print "[INFO] Updating inactive slot $INACTIVE_SLOT first..."
flash_slot "$INACTIVE_SLOT"
ui_print "[INFO] Updating active slot $ACTIVE_SLOT..."
flash_slot "$ACTIVE_SLOT"

restore_readonly || abort "[ERROR] Failed to restore partition read-only state"
READONLY_BLOCKS=
rm -f "$STAGED_IMAGE"
trap - 0
ui_print "Firmware has been successfully updated!"
