#!/sbin/sh

# ******************************************************
# Description: 	Installer script for OrangeFox Recovery
# Author: 	DarthJabba9
# Date: 	23 June 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=""

# build a vanilla version that skips all OrangeFox patches?
OF_VANILLA_BUILD=0

# R11?
FOX_R11=0

# last_modified
MOD_DATE="20200622"
  
# 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;
}

# mount a partition, if not mounted already
# mount_part <partition>
mount_part() {
  if [ -z "$1" ]; then
     return
  fi

  local F=$(is_mounted "$1")
  if [ "$F" = "1" ]; then
     return
  fi

  mount $1
}

# unmount a partition, if mounted
# umount_part <partition>
umount_part() {
  if [ -z "$1" ]; then
     return
  fi

  local F=$(is_mounted "$1")
  if [ "$F" = "1" ]; then
     umount "$1"
  fi
}

# 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 -w "$TARGET_DEVICE")
   if [ -n "$F" ]; then
       ui_print "- OrangeFox (R10.1_4) 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 "$TARGET_DEVICE_ALT" | grep -w "$PRODUCT_DEVICE")
       if [ -n "$F" ]; then
          ui_print "- OrangeFox (R10.1_4) 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" -a "$(getprop ro.twrp.sar)" != "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"
}

# file/directory/block exists
Exists() {
  [ -z "$1" ] && { echo "0"; return; } # error
  [ -d "$1" ] && { echo "1"; return; } # directory
  [ -f "$1" ] && { echo "2"; return; } # file
  [ -h "$1" ] && { echo "3"; return; } # link
  [ -e "$1" ] && { echo "4"; return; } # other kind of file  
  stat "$1" > /dev/null 2>&1;
  [ "$?" = "0" ] && echo "5" || echo "0"; # 5 = what kind of file is this?
}

# 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() {
   umount_part "$SYS_ROOT"
   umount_part "/system" > /dev/null 2>&1
   umount_part "/vendor"
   umount_part "/cache"
   umount_part "/persist"
   umount_part "/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
/system/vendor/recovery-from-boot.p
/vendor/recovery-from-boot.p
/vendor/bin/install-recovery.sh
/vendor/etc/install-recovery.sh
/vendor/etc/recovery-resource.dat"

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

# Whether this is really a proper A/B device
Is_AB_Device() {
local S=$(getprop "ro.boot.slot_suffix")
local U=$(getprop "ro.build.ab_update")
   if [ -n "$S" -a "$U" = "true" ]; then
      echo "1"
   else
      echo "0"
   fi
}

# get the R10 cache dir
Fox_R10_GetCacheDir() {
local C="/cache/" 
local AB="/data/cache/"
local P="/persist/cache/"
 if [ "$OF_AB_DEVICE" = "1" -o "$(Is_AB_Device)" = "1" ]; then
    [ -e $AB ] && { echo "$AB"; return; }
    [ -e $P ] && { echo "$P"; return; }
    mount_part "/data"
    echo "$AB"
    return
 fi
 [ -e $C ] && { echo "$C"; return; }
 [ -e $AB ] && { echo "$AB"; return; }
 [ -e $P ] && { echo "$P"; return; }
 echo "$C"
}

# get the cache dir
GetCacheDir() {
local C="/cache/" 
local D="/data/"
local P="/persist/"
local AB="/data/cache/"
local PP="/persist/cache/"

  if [ "$FOX_R11" != "1" ]; then
    C=$(Fox_R10_GetCacheDir)
    echo "$C"
    return
  fi

 [ -e $C ] && { echo "$C"; return; }
 [ -e $D ] && { mount_part "/data"; echo "$D"; return; }
 [ -e $P ] && { echo "$P"; return; }
 [ -e $AB ] && { echo "$AB"; return; }
 [ -e $PP ] && { echo "$PP"; return; }

 echo "$C"
}

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

  if [ "$cache_dir" = "/cache/" ]; then
     mount_part "/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/" -o "$cache_dir" = "/persist/" ]; then
     	mount_part "/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 tool="$EX_DIR"/magiskboot
local target="$EX_DIR"/boot.img
local slot

   cd "$EX_DIR"
   chmod 0755 "$tool"
   
   ui_print "- Extracting the OrangeFox ramdisk ..."
   "$tool" unpack recovery.img > /dev/null 2>&1
   rm -f kernel dtb
   mv -f ramdisk.cpio ramdisk-ofrp.cpio
   
   for slot in a b; do
  	ui_print "Running boot image patcher on slot $slot...";
	dd if=/dev/block/bootdevice/by-name/boot_"$slot" of=$target;
	"$tool" unpack -h boot.img > /dev/null 2>&1;
	
  	# kernel string want_initramfs -> skip_initramfs (Magisk)
  	"$tool" hexpatch kernel 77616E745F696E697472616D6673 736B69705F696E697472616D6673;
  	
  	# kernel string trip_initramfs -> skip_initramfs (SuperSU)
  	"$tool" hexpatch kernel 747269705F696E697472616D6673 736B69705F696E697472616D6673;

  	# boot.img header cmdline remove skip_override (flar2 patch)
  	sed -i "s|$(grep '^cmdline=' header | cut -d= -f2-)|$(grep '^cmdline=' header | cut -d= -f2- | sed -e 's/skip_override//' -e 's/  */ /g' -e 's/[ \t]*$//')|" header;

	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_"$slot";
	rm -f boot.img dtb kernel new-boot.img ramdisk.cpio header;
   done
   
   rm -f ramdisk-ofrp.cpio
   "$tool" cleanup
   cd /tmp
   ui_print " ";
   ui_print "*** NOTE: You are now unrooted! ***";   
}

# 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)
local ttmp=$PART
local Is_ABD="0"

    [ -z "$PART" ] && PART=$RECOVERY_PARTITION

    # either we've been told that it is A/B, or, it really is A/B
    [ "$OF_AB_DEVICE" = "1" -o "$(Is_AB_Device)" = "1" ] && Is_ABD="1"
    [ "$Is_ABD" = "1" ] && {
       ui_print "- The device is an A/B device."
       [ -z "$ttmp" ] && {
          AB_Install_Recovery
          return
       }
       # if we get here, it is A/B, but it has a recovery partition!
       ui_print "- But it also has a recovery partition!"
    }

    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
       [ "$Is_ABD" = "1" -a "$oldF" = "0" ] && {
          ui_print "- Hmmm ... an A/B device with a zero byte recovery partition!"
          ui_print "- Resorting to the A/B installation procedure..."
          AB_Install_Recovery
          return
       }
       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
}

# 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
	rm -rf $F/Fox/.theme
	rm -rf $F/Fox/.navbar
	mount_part "/persist"
	rm -f /persist/.foxs
	umount_part /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"
	mount_part "$internal_SD"
	F=$(is_mounted "$internal_SD")
	[ "$F" = "0" ] && {
	   internal_SD="$EXTERNAL_STORAGE"
	   [ -z "$internal_SD" ] && internal_SD="/data/media/0"
	   mount_part "$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
	MountThemAll
	
	if [ "$OF_VANILLA_BUILD" = "1" ]; then
	   ui_print "- Not disabling stock recovery overwrites!"	
	else
	   ui_print "- Disabling stock recovery overwrites..."
	   Disable_stock_recovery_overwrites
	fi
	
	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_part "$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
# --
