#!/sbin/sh

# ******************************************************
# Description: 	Installer script for OrangeFox Recovery
# Author: 	DarthJabba9
# Date: 	12 January 2020
# ******************************************************

# the target device(s)
TARGET_DEVICE="ginkgo"
TARGET_DEVICE_ALT="willow"
PRODUCT_DEVICE=$(getprop "ro.product.device")

# target partition
RECOVERY_PARTITION="/dev/block/bootdevice/by-name/recovery"

# the display screen
SCREEN=/proc/self/fd/$2

# the current zip installer
ZIPFILE="$3"

# the zip extract directory
EX_DIR=/tmp/ofox_installer

# global error code (0=good; 1=error)
ERROR_CODE=0

# AB device?
OF_AB_DEVICE=""

# Reset OrangeFox settings to default?
FOX_RESET_SETTINGS=""

# sundry glodal defaults
SYS_DIR="/system"
SYS_ROOT="$SYS_DIR"
ETC_DIR="$SYS_DIR/etc"
SAR="0"

# ui_print "<message>" ["<message 2>" ...]
ui_print() {
  until [ ! "$1" ]; do
    echo -e "ui_print $1\nui_print" >> $SCREEN
    shift
  done
}

# is_mounted <partition>
is_mounted() {
  case `mount` in
    *" $1 "*) echo 1;;
    *) echo 0;;
  esac;
}

# package_extract_file <file> <destination_file>
old_package_extract_file() { 
   mkdir -p "$(dirname "$2")"
   unzip -o "$ZIPFILE" "$1" -p > "$2" 
}

# package_extract_dir <dir> <destination_dir>
old_package_extract_dir() {
  for entry in $(unzip -l "$ZIPFILE" "$1/*" 2>/dev/null | tail -n+4 | grep -o " $1.*$" | cut -c2-); do
    outfile=$(echo "$entry" | sed "s|${1}|${2}|")
    mkdir -p "$(dirname "$outfile")"
    unzip -o "$ZIPFILE" "$entry" -p > "$outfile"
  done;
}

package_extract_dir() {
   if [ -d $EX_DIR/$1 ]; then
      cp -a $EX_DIR/$1/* $2
   else
      ui_print "Invalid directory: $1"
   fi
} 

package_extract_file() {
   if [ -e $EX_DIR/$1 ]; then
      dd if=$EX_DIR/$1 of=$2
   else
      ui_print "Invalid file: $1"   
   fi 
}

# file_getprop <file> <property>
file_getprop() { 
  grep "^$2=" "$1" | cut -d= -f2
}

# getprop <property>
getprop() { 
   [ -e /sbin/getprop ] && /sbin/getprop $1 || grep "^$1=" /default.prop | head -n1 | cut -d= -f2
}

# abort [<message>]
abort() { 
   ui_print "$*"
   ERROR_CODE=1
   exit 1
}

# check if we have specified a target device and whether this is it
CheckRequirements() {
   local F=$(echo "$PRODUCT_DEVICE" | grep "$TARGET_DEVICE")
   if [ -n "$F" ]; then
       ui_print "- OrangeFox (R10.1_2) for $TARGET_DEVICE ..."
   else
       if [ -z "$TARGET_DEVICE_ALT" ]; then
          abort "E3004: This package is for $TARGET_DEVICE. This device ($PRODUCT_DEVICE) is not supported."
       fi
       #F=$(echo "$PRODUCT_DEVICE" | grep "$TARGET_DEVICE_ALT")
       F=$(echo "$TARGET_DEVICE_ALT" | grep -w "$PRODUCT_DEVICE")
       if [ -n "$F" ]; then
          ui_print "- OrangeFox (R10.1_2) for $PRODUCT_DEVICE ..."
       else
          abort "E3004: This package is for $TARGET_DEVICE. This device ($PRODUCT_DEVICE) is not supported."
       fi
   fi
}

# do we have a SAR build?
is_SAR() {
  local F=$(getprop "ro.build.system_root_image")
  [ "$F" != "true" ] && {
    echo "0"
    return  
  }
  [ -L "/system" -a -d "/system_root" ] && {
    echo "1"
    return
  }
  F=$(grep -s "/system_root" "/proc/mounts")
  [ -n "$F" ] && echo "1" || echo "0"
}

# mount everything we might need
MountThemAll() {
local sys="/system"
	# /system
    	SAR=$(is_SAR)
    	[ "$SAR" = "1" ] && sys="/system_root"
    	SYS_DIR="$sys"
    	SYS_ROOT="$SYS_DIR"	
	local F=$(is_mounted "$sys")
    	if [ "$F" = "0" ]; then
    	   mount "$sys" > /dev/null 2>&1
    	   F=$(is_mounted "$sys")
    	   [ "$F" = "0" ] && mount -t ext4 /dev/block/bootdevice/by-name/system "$sys"

	   # system-as-root stuff
	   [ -d "$sys/system" ] && SYS_DIR="$sys/system"
	   [ -d "$sys/system/etc" ] && ETC_DIR="$sys/system/etc"
	fi

    	# /vendor
    	F=$(is_mounted "/vendor")
    	if [ "$F" = "0" ]; then
    	   mount "/vendor" > /dev/null 2>&1    	   
    	   F=$(is_mounted "/vendor")
    	   [ "$F" = "0" ] && mount -t ext4 /dev/block/bootdevice/by-name/vendor /vendor > /dev/null 2>&1	   
	fi
} 

# unmount everything that we mounted
UnMountThemAll() {
local F=$(is_mounted "$SYS_ROOT")
   [ "$F" = "1" ] && umount $SYS_ROOT
   umount /system > /dev/null 2>&1
   F=$(is_mounted "/vendor")
   [ "$F" = "1" ] && umount /vendor
   F=$(is_mounted "/cache")
   [ "$F" = "1" ] && umount /cache
   F=$(is_mounted "/persist")
   [ "$F" = "1" ] && umount /persist
   F=$(is_mounted "/data")
   [ "$F" = "1" ] && umount /data
}

Process_StockRecovery() {
local RF=$1
   # ui_print "- File: [$RF]"
   if [ -f $RF ]; then
      ui_print "- Processing file: $RF"
      cp -af $RF $RF.bak
      rm -f $RF
   fi
}

Disable_stock_recovery_overwrites() {
local ToRename="/system/bin/install-recovery.sh
$ETC_DIR/install-recovery.sh
$ETC_DIR/recovery-resource.dat
$SYS_ROOT/recovery-from-boot.p
$SYS_DIR/recovery-from-boot.p
/system/vendor/bin/install-recovery.sh
/system/vendor/etc/install-recovery.sh
/system/vendor/etc/recovery-resource.dat
/vendor/bin/install-recovery.sh
/vendor/etc/install-recovery.sh
/vendor/etc/recovery-resource.dat"

   for i in $ToRename
   do
     Process_StockRecovery "$i"
   done
}

# get the cache dir
GetCacheDir() {
local C="/cache/" 
local AB="/data/cache/"
local P="/persist/cache/"
 if [ "$OF_AB_DEVICE" = "1" ]; then
    [ -e $AB ] && { echo "$AB"; return; }
    [ -e $P ] && { echo "$P"; return; }
    F=$(is_mounted "/data")
    [ "$F" != "1" ] && mount /data
    echo "$AB"
    return
 fi
 [ -e $C ] && { echo "$C"; return; }
 [ -e $AB ] && { echo "$AB"; return; }
 [ -e $P ] && { echo "$P"; return; }
 echo "$C"
}

# log this install
LogFreshInstall() { 
local cache_dir=$(GetCacheDir)
local F="0"

  if [ "$cache_dir" = "/cache/" ]; then
     F=$(is_mounted "/cache")
     [ "$F" != "1" ] && mount /cache
     F=$(is_mounted "/cache")
     [ "$F" != "1" ] && {
       ui_print "- Error mounting /cache; the post-install patches will not be processed."
       return
     }
 else
     if [ "$cache_dir" = "/persist/cache/" ]; then
     	F=$(is_mounted "/persist")
     	[ "$F" != "1" ] && mount /persist
     	F=$(is_mounted "/persist")
     	[ "$F" != "1" ] && {
     	  ui_print "- Error mounting the cache directory ($cache_dir); the post-install patches will not be processed."
     	  return
     	}
     fi 
 fi
 
  F="$cache_dir"recovery/
  [ ! -d $F ] && mkdir -p $F
  ui_print "- OrangeFox fresh installation - "$F"Fox_Installed"
  echo "FOX_NEW=1" > "$F"Fox_Installed
  [ ! -f "$F"Fox_Installed ] && {
    ui_print "- Error writing to the cache directory ($cache_dir); the post-install patches will not be processed."
  }
}

# unzip the installer package into /tmp
Unzip_Installer_Package() {
   cd /tmp
   mkdir -p $EX_DIR
   cd $EX_DIR
   unzip -o "$ZIPFILE"
}

# routine for flashing on A/B devices
AB_Install_Recovery() {
local toolname="/magiskboot"
local tool="$EX_DIR$toolname"
local targetfile="/boot.img"
local target="$EX_DIR$targetfile"

	cd "$EX_DIR"
	chmod 0755 "$tool"

	ui_print "- Extracting the ramdisk ..."
	"$tool" --unpack recovery.img > /dev/null 2>&1
	rm -f kernel dtb
	mv -f ramdisk.cpio ramdisk-ofrp.cpio

	ui_print "- Running boot image patcher on slot A..."
	dd if=/dev/block/bootdevice/by-name/boot_a "of=$target"
	"$tool" --unpack boot.img > /dev/null 2>&1
	cp -f ramdisk-ofrp.cpio ramdisk.cpio
	"$tool" --repack boot.img > /dev/null 2>&1
	dd if=new-boot.img of=/dev/block/bootdevice/by-name/boot_a
	rm -f boot.img dtb kernel new-boot.img ramdisk.cpio

	ui_print "- Running boot image patcher on slot B..."
	dd if=/dev/block/bootdevice/by-name/boot_b "of=$target"
	"$tool" --unpack boot.img > /dev/null 2>&1
	cp -f ramdisk-ofrp.cpio ramdisk.cpio
	"$tool" --repack boot.img > /dev/null 2>&1
	dd if=new-boot.img of=/dev/block/bootdevice/by-name/boot_b
	rm -f boot.img dtb kernel new-boot.img ramdisk.cpio ramdisk-ofrp.cpio
}

# print the size of a file
FileSize() {
  if [ -f $1 ]; then
     set -- $(ls -l "$1"); echo "$5"
  else
     echo "0"
  fi
}

# install the recovery
Install_Recovery() {
local PART=$(readlink -f $RECOVERY_PARTITION)
  [ -z "$PART" ] && PART=$RECOVERY_PARTITION

  [ "$OF_AB_DEVICE" != "1" ] && {
    local bak="/tmp/org_recovery.img"
    dd if="$PART" of=$bak > /dev/null 2>&1
    local oldF=$(FileSize $bak)
    local newF=$(FileSize recovery.img)
    ui_print "- Partition_Size=$oldF bytes; Recovery_Size=$newF bytes"
    [  $newF -gt $oldF ] && {
       rm -f $bak
       abort "The recovery image ($newF bytes) is bigger than your recovery partition ($oldF bytes)! Quitting."
    }
    
    if [ -x "/sbin/flash_image" ] && [ -x "/sbin/erase_image" ]; then
       ui_print "- Cleaning the recovery partition ($PART)"
       /sbin/erase_image "$PART"
       [ $? == 0 ] && ui_print "- Succeeded." || ui_print "- Failed."

       ui_print "- Flashing OrangeFox recovery..."
       /sbin/flash_image "$PART" "$EX_DIR/recovery.img"    
       [ $? == 0 ] && ui_print "- Succeeded." || {
       	   ui_print "- Problems encountered. Trying an alternative flashing method..."
       	   package_extract_file "recovery.img" "$PART"
       	   [ $? == 0 ] && ui_print "- Succeeded." || {
       	      ui_print "- Flashing failed. Trying to restore original recovery..."
       	      /sbin/flash_image "$PART" "$bak"
       	      [ $? == 0 ] && ui_print "- Succeeded." || abort "- Failed! There is now no recovery! Flash a working recovery via fastboot."
       	   }
       }
    else
         ui_print "- Flashing the new recovery."
         package_extract_file "recovery.img" "$PART"
    fi
    rm -f $bak
    return
  }
  # this is declared to be an A/B device
  ui_print "- A/B device ... "
  AB_Install_Recovery
}

# reset settings to default
Reset_Settings() {
local F="$1"
	[ -z "$F" ] && F=/sdcard
	ui_print "- Removing old OrangeFox configuration files ..."
	rm -f $F/Fox/.foxs
	mount /persist
	rm -f /persist/.foxs
	umount /persist
}

# main function
Main() {
	ui_print "*************************************"
	ui_print "*** |OrangeFox Recovery Project|  ***"
	ui_print "***   |By The OrangeFox Team|     ***"
	ui_print "*************************************"
	ui_print " "

# check if we have any device requirements
	CheckRequirements

# extract the zip installer
	Unzip_Installer_Package
	
# Install the recovery
	ui_print "- Installing OrangeFox recovery ..."
	Install_Recovery
	
	[ "$ERROR_CODE" = "1" ] && {
	   ui_print "- The installation of OrangeFox recovery was not successful ..."
	   exit 1
	}

# Install /sdcard stuff
	ui_print "- Installing FoxFiles on the internal SD ..."
	local internal_SD="/sdcard"
	local F=$(is_mounted "$internal_SD")
	[ "$F" = "0" ] && mount $internal_SD
	F=$(is_mounted "$internal_SD")
	[ "$F" = "0" ] && {
	   internal_SD="$EXTERNAL_STORAGE"
	   [ -z "$internal_SD" ] && internal_SD="/data/media/0"
	   mount $internal_SD
	}
	F=$(is_mounted "$internal_SD")
	[ "$F" = "1" ] && {	
	   ui_print "- internal_SD=$internal_SD"
	   rm -rf "$internal_SD/Fox/FoxFiles"
   	   rm -rf "$internal_SD/Fox/.theme"
   	   rm -rf "$internal_SD/Fox/.navbar"
   	   rm -f "$internal_SD/Fox/.wolfs"
   	   package_extract_dir "sdcard" "$internal_SD"
   	} 

# Prevent the stock recovery from replacing this recovery
	ui_print "- Disabling stock recovery overwrites"
	
	MountThemAll
	
	Disable_stock_recovery_overwrites
	
	LogFreshInstall
	
	[ "$FOX_RESET_SETTINGS" = "1" ] && Reset_Settings "$internal_SD"

# backup recovery.log -> Fox/logs/install.log
    	ui_print "- Finished."
    	mkdir -p $internal_SD/Fox/logs
    	ui_print "- Backing up recovery.log to $internal_SD/Fox/logs/install.log ..."
	cp -a /tmp/recovery.log $internal_SD/Fox/logs/install.log
   	[ "$internal_SD" = "/sdcard" ] &&  umount "$internal_SD"

# umount
	UnMountThemAll

# Finish and reboot
	ui_print "- Finished installing OrangeFox!" 
	ui_print "- Extra files are in $internal_SD/Fox/"
	ui_print " "
	ui_print " >> Rebooting to Recovery in 5 seconds ..."
	sleep 1
	ui_print "-5s...."
	sleep 1
	ui_print "-4s...."
	sleep 1
	ui_print "-3s..."
	sleep 1
	ui_print "-2s.."
	sleep 1
	ui_print "-1s."
	sleep 1
	ui_print " "
	ui_print "- Rebooting to Recovery now ..."
	reboot recovery

# if the reboot command fails, try this instead
	sleep 3
	killall recovery

} # end Main(); 

# --
Main
# --
