#!/sbin/sh
#Dynamic Installer by @BlassGO --- Also uses code from @osm0sis and @topjohnwu

unmount() { ( umount "$1" || umount -l "$1";) 2>/dev/null; is_mounted "$1" && return 1 || return 0; }

write_raw_image() { local bs; if is_number "$(string force remove M "$extraction_speed")"; then bs=$extraction_speed; else bs=1048576; fi; dd if="$1" of="$2" bs=$bs; }

is_substring() { [[ "$2" == *"$1"* ]]; }

is_mounted() { grep -q " $(readlink -f "$1") " /proc/mounts 2>/dev/null; }

is_less() { compare "$1 < $2";}

is_greater() { compare "$1 > $2";}

is_less_equal() { compare "$1 <= $2"; }

is_greater_equal() { compare "$1 >= $2"; }

is_equal() { [[ "$1" == "$2" ]]; }

is_zip() { ( xxd -p "$1" | head -n1 | grep -q "504b0304" ) 2>/dev/null; }

is_gzip() { ( xxd -p "$1" | head -n1 | grep -q "1f8b08" ) 2>/dev/null; }

is_bzip() { ( xxd -p "$1" | head -n1 | grep -q "425a" ) 2>/dev/null; }

is_xz() { ( xxd -p "$1" | head -n1 | grep -q "fd377a585a00" ) 2>/dev/null; }

is_tar() { ( xxd -p -s 257 "$1" | head -n1 | grep -q "7573746172" ) 2>/dev/null; }

is_number() { echo "$1" | grep -Eq "^-?[0-9]+([.][0-9]+)?$"; }

is_hex() { echo "$1" | grep -Eq "^[[:xdigit:]]+$"; }

is_abc() { echo "$1" | tr -d '[[:space:]]' | grep -Eq "^[[:alpha:]]+$"; }

is_text() { echo "$1" | tr -d '[[:space:]]' | grep -Eq "^[[:print:]]+$"; }

is64bit() { local byte=$(od -An -t x1 -j 4 -N 1 "$1" | tr -d " "); if [[ "$byte" == "02" ]]; then return 0; elif [[ "$byte" == "01" ]]; then return 1; else echo Unknown && return 1; fi }

show_progress() { [ -e "$OUTFD" ] && echo "progress $1 $2" >> $OUTFD; }

apply_patch() { LD_LIBRARY_PATH=/system/lib$base_lib applypatch "$@"; }

apply_patch_check() { LD_LIBRARY_PATH=/system/lib$base_lib applypatch -c "$@"; }

apply_patch_space() { LD_LIBRARY_PATH=/system/lib$base_lib applypatch -s $1; }

rename() { local path=$(fullpath "$1"); if [ -n "$path" ]; then echo2 "rename: \"$path\" with \"$2\""; mv -f "$path" "$(dirname "$path")/$2"; else echo2 "CANT FIND: $1" && return 1; fi; }

delete() { rm -f "$@"; }

delete_recursive() { rm -rf "$@"; }

repeat() { local this=$((printf %${1}s) 2>/dev/null); [ -n "$this" ] && printf "${this//" "/"$2"}" || return 1; }

calc() { awk "BEGIN{print $*}"; }

compare() { eval 'awk "BEGIN{if (!('$@')) exit 1}"'; }

echo2() { >&2 echo "$@"; }

contains_array() { local e match="$1"; shift; for e; do [[ "$e" == "$match" ]] && return 0; done; return 1; }

get_array() { local e match="$1"; shift; for e; do [[ "$e" == *"$match"* ]] && echo "$e" && return 0; done; return 1; }

binarch() { local byte=$(od -An -t x1 -j 4 -N 1 "$1" | tr -d " "); if [[ "$byte" == "02" ]]; then echo 64bits; elif [[ "$byte" == "01" ]]; then echo 32bits; else echo Unknown && return 1; fi }

reboot() { [ -f "$(readlink -f /system/bin/reboot)" ] && /system/bin/reboot "$@" || "$(which reboot)" "$@";}

create_dir() { local dir return=0; for dir; do if [ ! -d "$dir" ]; then mkdir -p "$dir" 2>/dev/null; if [ -d "$dir" ]; then set_perm2 $di_uid $di_gid $di_perm_d "$dir" ; elif [ -e "$dir" ]; then echo2 "Already exist some reference called: $dir" && return=1; else testrw "$(dirname "$dir")" || return=1; echo2 "Cant create dir: $dir" && return=1; fi ; else testrw "$dir" || return=1; fi; done; return $return;  }

create_file() { local file return=0; for file; do create_dir "$(dirname "$file")" || return 1; delete "$file"; touch "$file" 2>/dev/null; if [ -f "$file" ]; then set_perm2 $di_uid $di_gid $di_perm_f "$file" ; elif [ -e "$file" ]; then echo2 "Already exist some reference called: $file" && return=1; else echo2 "Cant create file: $file" && return=1; fi; done; return $return;  }

testrw() { local test return=0; for test; do if [ -d "$test" ]; then if grep " $(readlink -f "$test") " /proc/mounts | grep -Eq '(\(|[[:space:]]|,)ro(,|[[:space:]]|\))'; then echo2 '!'"Read-Only-Mount: $test" && return=2; else touch "$test/.rw$$" 2>/dev/null; if [ -f "$test/.rw$$" ]; then delete "$test/.rw$$" ; else echo2 '!'"Read-Only: $test" && return=1; fi; fi; else echo2 "Cant find: $test directory" && return=1; fi; done; return $return; }

testvarname() { echo "$@" | grep -Eq '^[a-zA-Z_][a-zA-Z_0-9]*$'; }

encode_b64() { echo "$@" | base64; }

decode_b64() { echo "$@" | base64 -d; }

run_b64() { local try0109191_=$(echo "$@" | base64 -d 2>/dev/null); is_text "$try0109191_" && eval "$try0109191_" || return 1 ; }

int() { awk "BEGIN{print int($*)}"; }

float() { is_number "$float_length" || local float_length=9; [ $# == 0 ] && set -- 0; awk 'BEGIN{printf("%.'$float_length'f\n", '$*')}'; }

round() { [ $# == 0 ] && set -- 0; awk 'BEGIN{printf("%.0f\n", '$*')}'; }

is_static() { local file="$1" result; [ -n "$file" -a ! -e "$file" ] && file=$(which "$1"); [ ! -e "$file" ] && echo2 "is_static: Cant find: $1" && return 1; result=$(strings "$file" | grep -E "^/(bin|etc|system|system_ext|vendor|product|odm|apex)(/[^/]+)*/bin/[^/]+" | while read need; do echo "$file needs \"$need\""; done); [ -n "$result" ] && echo2 "$result" && return 1 || return 0; }

mount_file() { if [ ! -e "$1" ]; then echo2 "Cant find: $1" && return 1; elif [ ! -e "$2" ]; then create_file "$2" || return 1; fi; mount -o bind "$1" "$2";}

support_option() { can_run "$2" && ("$2" --help 2>&1) | grep -Eq -m1 "[[:space:]]+$1[[:space:]]+";}

###################################
#Common EDIFY extra references (For new users)

file_getprop() { get_file_prop "$@"; }

ifelse() { eval "( $1 ) || ( $2 )"; }

read_file() { cat "$@"; }

run_program() { local program=$(fullpath "$1"); if [ ! -f "$program" ]; then echo2 "CANT FIND: $1" && return 1; fi ; shift; chmod 777 "$program"; "$program" "$@"; }

greater_than_int() { is_greater "$@"; }

less_than_int() { is_less "$@"; }

stdout() { "$@" 2>&1; }

stderr() { "$@" 1>&2; }

stdprint() { "$@" | while read print; do ui_print "$print"; done; } 

#concat() { local con; for c; do con="${con}$($1 2>/dev/null || echo "$1")"; shift; done; [ -n "$con" ] && echo "$con" || return 1; }

concat() { local con; for c; do con="${con}$1"; shift; done; [ -n "$con" ] && echo "$con" || return 1; }


###################################
#Specific POSIX functions

grep_o() (
    #POSIX grep -o matching find by @BlassGO
    if [ -z "$2" ]; then
        grep=$(cat)
    else
        grep=$(cat "$2")
    fi  
    echo "$grep" | awk '
    function start(str) {
        matches="" 
        return receiver(str)
    }
    function receiver(str2) {
        find=match(str2, /'$1'/)
        if(find!=0) {
            matches=(matches substr(str2, RSTART, RLENGTH) "\n")
            receiver(substr(str2, RSTART+RLENGTH, length(str2)))
        }    
        return matches
    }
    {print start($0)}' | grep -Ev "^$"
)

###################################

exist() {
   local flag file folder symlink block any
   flag=$(echo "$1" | grep -E '^(any|file|folder|symlink|block)$')
   if [ -n "$flag" ]; then eval "$flag=true"; shift; else any=true; fi
   [ $# == 0 ] && return 1
   while [ $# != 0 ]; do
      [[ -n "$any" && -e "$1" ]] || [[ -n "$file" && -f "$1" ]] || [[ -n "$folder" && -d "$1" ]] || [[ -n "$symlink" && -L "$1" ]] || [[ -n "$block" && -b "$1" ]] || return 1
      shift;
   done
   return 0
}

check_content() {
   local array file content func
   [ -z "$1" ] && return 1
   array=( "$@" )
   file=${array[${#array[@]} - 1]}
   if [ ! -e "$file" ]; then echo "CANT FIND: $file" && return 1; fi
   if ! is_zip "$file"; then echo "check_content: $file inst a ZIP " && return 1; fi
   unset "array[${#array[@]}-1]"
   func=${#array[@]}
   [ $func == 0 ] && return 1
   for content in "${array[@]}"; do
      unzip -l "$file" "$content" | grep -Fq "$content" || return 1
   done
   return 0
}

find_content() {
   local restore flag regex=false nf="[^/]$" limit pass=false
   local array file content func fix='s/[]()\/$*.^[]/\\&/g'
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -regex|-r)
       regex=true
       shift;
       ;;
       -include-dirs|-id)
       nf=
       shift;
       ;;
       -dirs|-d)
       nf="[/]"
       shift;
       ;;
       -maxdepth|-max)
       if is_number "$2"; then limit="$2"; else echo2 "find_content: Invalid option: $2" && return 1; fi
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   [ -z "$1" ] && return 1
   array=( "$@" )
   file=${array[${#array[@]} - 1]}
   if [ ! -e "$file" ]; then echo "CANT FIND: $file" && return 1; fi
   if ! is_zip "$file"; then echo "find_content: $file inst a ZIP " && return 1; fi
   unset "array[${#array[@]}-1]"
   func=${#array[@]}
   [ $func == 0 ] && return 1
   for content in "${array[@]}"; do
      $regex || content=$(echo "$content" | sed -e $fix)
      if ! unzip -l "$file" | tail -n+4 | grep -Eo "[0-9]+:[0-9]+([^/]+/*){0,$limit}${nf}" | sed -E 's/.*[0-9]+:[0-9]+[[:space:]]+(.*)$/\1/g' | sort -bu | grep -E "$content"; then
         echo2 "Cant find contents with: \"$content\" in \"$file\""
      else pass=true
      fi
   done
   $pass && return 0 || return 1
}

is_valid() {
   [ $# == 0 ] && return 1
   while [ $# != 0 ]; do
      [ -e "$1" ] && grep -q '[^[:space:]]' "$1" || return 1
      shift;
   done
   return 0
}

defined() {
   [ $# == 0 ] && return 1
   while [ $# != 0 ]; do
      [ -z "$(checkvar "$1")" ] && return 1
      shift;
   done
   return 0
}

undefined() {
   [ $# == 0 ] && return 1
   while [ $# != 0 ]; do
      [ -n "$(checkvar "$1")" ] && return 1
      shift;
   done
   return 0
}

copy() {
   if ([ -d "$2" ] || create_dir "$(dirname "$2")") && cp -prf "$1" "$2" 2>/dev/null; then
      return 0
   else
      echo2 "Cant copy: \"$1\" in \"$2\""
      return 1
   fi
}

move() {
   if ([ -d "$2" ] || create_dir "$(dirname "$2")") && (mv -f "$1" "$2" || cp -prf "$1" "$2") 2>/dev/null; then
      if testrw "$(dirname "$1")" 2>/dev/null; then rm -rf "$1"
      else echo2 "Copied to \"$2\", but cant remove \"$1\""
      fi
      return 0
   else
      echo2 "Cant move: \"$1\" in \"$2\""
      return 1
   fi
}

fullpath() {
   local path fix='s/\/\/*/\//g'
   path=$(echo "$(cd "$(dirname "$1")" && pwd -P)/$(basename "$1")" | sed -e $fix)
   [ -e "$path" ] && echo "$path" || return 1
}

contains() {
   local array file content func
   [ -z "$1" ] && return 1
   array=( "$@" )
   file=${array[${#array[@]} - 1]}
   if [ ! -e "$file" ]; then echo "CANT FIND: $file" && return 1; fi
   unset "array[${#array[@]}-1]"
   func=${#array[@]}
   [ $func == 0 ] && return 1
   file=$(cat "$file")
   for content in "${array[@]}"; do
      is_substring "$content" "$file" || return 1
   done
   return 0
}

magic_file() {
   #Extra method:
   #od -An -v -w $bytes -t x1 -j $offset "$file" | tr -d " "
   local restore flag offset type line show showline bytes
   local magic file pass result get options return=0
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -offset|-o)
       offset=$2
       shift;shift;
       ;;
       -bytes|-b)
       bytes=$2
       shift;shift;
       ;;
       -type|-t)
       type=$2
       shift;shift;
       ;;
       -line|-l)
       line=$2
       shift;shift;
       ;;
       -show|-s)
       show=true
       shift
       ;;
       -show-line|-sl)
       showline=true
       shift
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   if [ -n "$type" ]; then
      file="$1"
      get=$(get_file_prop "$l/file_types.config" "$type " 2>/dev/null)
      if [ -n "$get" ]; then
        magic=$(echo "$get" | cut -d, -f1 | tr -d " " | tr : " ")
        options=$(echo "$get" | grep -o "," | wc -l)
        if [ "$options"  = "1" ]; then
           offset=$(echo "$get" | cut -d, -f2 | tr -d " ")
        elif [ "$options" = "2" ]; then
           offset=$(echo "$get" | cut -d, -f2 | tr -d " ")
           bytes=$(echo "$get" | cut -d, -f3 | tr -d " ")
        elif [ "$options" = "3" ]; then
           offset=$(echo "$get" | cut -d, -f2 | tr -d " ")
           bytes=$(echo "$get" | cut -d, -f3 | tr -d " ")
           line=$(echo "$get" | cut -d, -f4 | tr -d " ")
        fi
      else
        echo2 "magic_file: Cant find Magic Config for: $type" && return 1
      fi
   else
      magic="$1"
      file="$2"
   fi
   if [ -z "$magic" ]; then echo2 "magic_file: Null magic" && return 1; fi
   if [ ! -f "$file" ]; then echo2 "CANT FIND: $file" && return 1; fi
   if [ -n "$offset" ] && ! is_number "$offset"; then echo2 "magic_file: Invalid offset: $offset" && return 1; fi
   if [ -n "$line" ] && ! is_number "$line"; then echo2 "magic_file: Invalid line: $line" && return 1; fi
   if [ -n "$bytes" ] && ! is_number "$bytes"; then echo2 "magic_file: Invalid bytes: $bytes" && return 1; fi
   [ -z "$line" ] && line=1
   [ -z "$bytes" ] && bytes=100
   echo2 '>> Magic File v1.0.0'
   for magic in $magic; do
      result=
      pass=
      if ! is_hex "$magic"; then magic=$(printf "$magic" | od -An -t x1 | tr -d " "); fi
      echo2 " "
      echo2 " MAGIC: $magic"
      echo2 " FILE: $(basename "$file")"
      [ -n "$offset" ] && echo2 " OFFSET: $offset"
      [ -n "$bytes" ] && echo2 " BYTES: $bytes"
      if [ -z "$offset" ]; then result=$(( xxd -p -c $bytes "$file" | head -n$line ) 2>/dev/null)
      elif [ -n "$offset" ]; then result=$(( xxd -p -c $bytes -s $offset "$file" | head -n$line ) 2>/dev/null)
      fi
      pass=$(echo "$result" | grep -m1 "$magic")
      if [ -n "$showline" ]; then
         [ -n "$pass" ] && echo2 " LINE: $pass" || echo2 " LINE: NULL"
      elif [ -n "$show" ]; then
         if [ -n "$result" ]; then
            echo2 " RESULT: "
            echo2 "$result"
         else echo2 " RESULT: NULL"
         fi
      fi
      if [ -n "$pass" ]; then echo2 " PASSED: YES" && return 0
      else echo2 " PASSED: NO" && return=1 && continue
      fi
   done
   return $return
}

convert() {
   local num from to header group group2 section part result base base2 eq eq2 invert=false can can_too hm best conv first end pass=false try
   local restore flag d show=false
   local num1 num2 unit1 unit2
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -decimals|-d)
       d=$2
       shift;shift;
       ;;
       -show|-s)
       show=true
       shift
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   num1=$(echo "$1" | tr -d " " | grep -Eo "^[0-9]+([.][0-9]+)?")
   unit1=$(echo "$1" | grep -Eo "([[:alpha:]]|[[:punct:]])+([0-9]+)?" | tail -n1)
   unit2=$(echo "$2" | grep -Eo "([[:alpha:]]|[[:punct:]])+([0-9]+)?" | tail -n1)
   if [ -z "$unit1" ] && [ -n "$convert_from" ]; then unit1=$convert_from; fi
   if [ -z "$unit2" ] && [ -n "$convert_to" ]; then unit2=$convert_to; fi
   is_number "$d" || d=16
   if defined unit1 && defined unit2; then
      is_number "$num1" && num=$num1 || num=1; from=$unit1; to=$unit2
   else echo2 "convert: Invalid line" && return 1
   fi
   header=$(grep -E -m1 "[\[]+[[:alpha:]]+([[:punct:]])?+[/:].*[\"]$from[\"].*" "$l/units.config")
   group=$(echo "$header" | cut -d: -f1 | tr -d "[]")
   if [ -z "$group" ]; then echo2 "Cant find any group for: $from unit" && return 1; fi
   section=$(string extract "[$group:" "[$group end" -f "$l/units.config")
   if [ -z "$section" ]; then echo2 "Empty group: $group" && return 1; fi
   part=$(echo "$section" | grep -E -wm1 "[0-9]+([.][0-9]+)?+$from = [0-9]+([.][0-9]+)?+$to")
   if [ -z "$part" ]; then
      part=$(echo "$section" | grep -E -wm1 "[0-9]+([.][0-9]+)?+$to = [0-9]+([.][0-9]+)?+$from")
      if [ -n "$part" ]; then invert=true
      else
         group2=$(grep -E -m1 "[\[]+[[:alpha:]]+([[:punct:]])?+[/:].*[\"]$to[\"].*" "$l/units.config" | cut -d: -f1 | tr -d "[]")
         if [ -n "$group2" ] && [ "$group" == "$group2" ]; then
            can=$(echo "$section" | grep -E -w "[0-9]+([.][0-9]+)?+$to" | tr ' ' '\n' | grep -Eo "([[:alpha:]]|[[:punct:]])+([0-9]+)?" | grep -Ev "[0-9]+([.][0-9]+)?" | grep -E -wv "($to|=)" | tr '\n' ' ' )
            range=$(echo "$header" | grep -Eo "([\"]$from[\"].*[\"]$to[\"])?([\"]$to[\"].*[\"]$from[\"])?" | tr '"' ' ')
            if [[ "$(echo "$range" | awk '{print $1;}')" == "$to" ]]; then range=$(echo "$range" | tr ' ' '\n'| tac |tr '\n' ' '); fi
            range=$(echo "$range" | tr ' ' '\n' | grep -E -wv "($from|$to)" |  tr '\n' ' ')
            for hm in $can; do
               can_too="$can_too $(echo "$section" | grep -E -w "[0-9]+([.][0-9]+)?+$hm" | tr ' ' '\n' | grep -Eo "([[:alpha:]]|[[:punct:]])+([0-9]+)?" | grep -Ev "[0-9]+([.][0-9]+)?" | grep -E -wv "($hm|$to|=)" | tr '\n' ' ')"
            done
            for test in $can; do
               if echo "$section" | grep -Eq -wm1 "[0-9]+([.][0-9]+)?+($test|$from) = [0-9]+([.][0-9]+)?+($from|$test)"; then
                  best="$best $test"
               fi
            done
            if [ -z "$best" ]; then
               for test in $can; do
                  for test2 in $can_too; do
                     if echo "$section" | grep -Eq -wm1 "[0-9]+([.][0-9]+)?+($test2|$test) = [0-9]+([.][0-9]+)?+($test|$test2)"; then
                        #echo "$test2 $test"
                        if echo "$section" | grep -Eq -wm1 "[0-9]+([.][0-9]+)?+($test2|$from) = [0-9]+([.][0-9]+)?+($from|$test2)"; then
                           best="$best $test2 $test"
                           break
                        fi
                     fi
                  done
               done
            fi
            #echo "CAN:"
            #echo "$can"
            #echo "CAN TOO:"
            #echo "$can_too"
            #echo "BEST:"
            #echo "$best"
            #echo "RANGE:"
            #echo "$range"
            first=$(echo "$best" | awk '{print $1;}')
            end=$(echo "$best" | awk '{print $NF}')
            if echo "$section" | grep -Eq -wm1 "[0-9]+([.][0-9]+)?+($first|$from) = [0-9]+([.][0-9]+)?+($from|$first)" && echo "$section" | grep -Eq -wm1 "[0-9]+([.][0-9]+)?+($end|$to) = [0-9]+([.][0-9]+)?+($to|$end)"; then pass=true; fi
            if ! $pass && [ -n "$range" ]; then
               best="$range"
               first=$(echo "$best" | awk '{print $1;}')
               end=$(echo "$best" | awk '{print $NF}')
               if echo "$section" | grep -Eq -wm1 "[0-9]+([.][0-9]+)?+($first|$from) = [0-9]+([.][0-9]+)?+($from|$first)" && echo "$section" | grep -Eq -wm1 "[0-9]+([.][0-9]+)?+($end|$to) = [0-9]+([.][0-9]+)?+($to|$end)"; then pass=true; fi
            fi
            if ! $pass; then
               echo2 "Oops: Cant generate an automatic conversion sequence for: $from to $to"
               return 1
            fi
            for conv in $best; do
               invert=false
               runit2=$from
               runit=$conv
               part=$(echo "$section" | grep -E -wm1 "[0-9]+([.][0-9]+)?+$from = [0-9]+([.][0-9]+)?+$conv")
               if [ -z "$part" ]; then
                  part=$(echo "$section" | grep -E -wm1 "[0-9]+([.][0-9]+)?+$conv = [0-9]+([.][0-9]+)?+$from")
                  invert=true
               fi
               if [ -n "$part" ]; then
                  #echo "$part"
                  from=$runit
                  base=$(echo "$part" | cut -d= -f1 | tr -d " " | grep -Eo "^[0-9]+([.][0-9]+)?")
                  base2=$(echo "$part" | cut -d= -f2 | tr -d " " | grep -Eo "^[0-9]+([.][0-9]+)?")
                  [ -z "$base" ] && eq=1 || eq=$base
                  [ -z "$base2" ] && eq2=1 || eq2=$base2
                  $invert && eq=$base2
                  $invert && eq2=$base
                  num=$(calc '(' $num '*' $eq2 ')' / $eq)
                  $show && echo "$num$runit"
               fi
               invert=false
            done
         elif [ -n "$group2" ] && [ "$group" != "$group2" ]; then echo2 "Hm you try to convert a unit from group $group to a unit from group $group2" && return 1 
         else echo2 "Oops: Cant find $from to $to conversion in group $group" && return 1
         fi
      fi
   fi
   if [ "$from" != "$unit1" ]; then
      part=$(echo "$section" | grep -E -wm1 "[0-9]+([.][0-9]+)?+$from = [0-9]+([.][0-9]+)?+$to")
      if [ -z "$part" ]; then
         part=$(echo "$section" | grep -E -wm1 "[0-9]+([.][0-9]+)?+$to = [0-9]+([.][0-9]+)?+$from")
         if [ -n "$part" ]; then invert=true
         else
            echo2 "Oops: Cant continue the conversion sequence"
            echo2 "Sequence: $best"
            echo2 "Cant: $num$from to $to"
            return 1
         fi
      fi
   fi
   base=$(echo "$part" | cut -d= -f1 | tr -d " " | grep -Eo "^[0-9]+([.][0-9]+)?")
   base2=$(echo "$part" | cut -d= -f2 | tr -d " " | grep -Eo "^[0-9]+([.][0-9]+)?")
   [ -z "$base" ] && eq=1 || eq=$base
   [ -z "$base2" ] && eq2=1 || eq2=$base2
   $invert && eq=$base2
   $invert && eq2=$base
   result=$(calc '(' $num '*' $eq2 ')' / $eq)
   #Ensure decimal numbers / fully integers
   result=$(printf "%.${d}f" "$result" | sed '/\./ s/\.\{0,1\}0\{1,\}$//')
   if is_number "$result"; then 
      $show && echo $result$to || echo $result
   else return 1
   fi
}

convert_edify() {
   #Generic edify function name format = \([[:alnum:]]\+_\{0,1\}[[:alnum:]]\+_\{0,1\}[[:alnum:]]\+\)
   local tmp="$TMP/edify001.tmp" support func check
   cat "$1" | dos2unix > "$tmp"
   #Get all supported functions (Regex)
   #For commands: $(ls $(echo $PATH | tr ':' ' ') 2>/dev/null | grep -Ev '/|\[' | grep . | sort)
   for func in $(declare -F 2>/dev/null | awk '{ print $3; }'); do
      [ -n "$support" ] && support+="\>|"
      [ -n "$func" ] && support+="\<${func}"
   done
   support=$(echo "${support}|\<getprop\>|\<sleep\>|\<if\>|\<fi\>|\<then\>|\<else\>" | dos2unix)
   #Checking supported actions
   check=$(grep -Eo "[[:alnum:]]+_{0,1}[[:alnum:]]+_{0,1}[[:alnum:]]+[(]" "$tmp" | uniq | cut -d'(' -f1 | grep -Ewv "$support" | while read action; do
      [ -z "$action" ] && break
      echo "Unsupported action: $action"
   done)
   [ -n "$check" ] && echo2 "$check" && return 1
   #sed -i -r '/.*[[:alnum:]]+_{0,1}[[:alnum:]]+_{0,1}[[:alnum:]]+[(].*[)]/ {/'$support'/! { s/.*/#Unsupported line: &/g} }' "$tmp"
   #Standardizing ifelse(action, action) to action || action
   sed -i -Ee :1 -e '/ifelse[(]/s/^(([^",]|"[^"]*")*),/\1||/;t1' "$tmp"
   sed -i -e 's/ifelse[\(]\(.*\)[\)].*/\1/g' "$tmp"
   sed -i -e 's/\([[:alnum:]]\+_\{0,1\}[[:alnum:]]\+_\{0,1\}[[:alnum:]]\+\)[\(]\(.*\)[\)].*||\(.*\)[\(]\(.*\)[\)]/\1 \2 || \3 \4/g' "$tmp"
   #Edify: action("arg1", "arg2"); to DI: action "arg1", "arg2"
   sed -i -e 's/\([[:alnum:]]\+_\{0,1\}[[:alnum:]]\+_\{0,1\}[[:alnum:]]\+\)[\(]\(.*\)[\)];.*/\1 \2/g' "$tmp"
   #Remove any comma that is not inside double quotes
   sed -i -Ee :1 -e 's/^(([^",]|"[^"]*")*),/\1 /;t1' "$tmp"
   #Edify subaction("arg1" "arg2") to DI: $(subaction "arg1" "arg2")
   sed -i -e 's/\([[:alnum:]]\+_\{0,1\}[[:alnum:]]\+_\{0,1\}[[:alnum:]]\+\)[\(]\(.*\)[\)]\(.*\)/$\(\1 \2\)\3/g' "$tmp"
   #Standardizing if "ARG" comparator "ARG" then to if [[ "ARG" comparator "ARG" ]]; then
   sed -i -r '/[=][=]|[!][=]|^[ \t]*then/s/^[ \t]*then|[ \t]*then[ \t]*$//g' "$tmp"
   sed -i -Ee :1 -e 's/^(([^",]|"[,^"]*")*)endif/\1fi/;t1' "$tmp"
   sed -i -e 's/\([\$][\(].*[\)]\)\(.*[==]\|[!=].*\)/"\1"\2/g' "$tmp"
   sed -i -e 's/\([\"].*\)\([=][=]\|[!][=]\)\(.*\)/[[ \1\2\3 ]]; then/g' "$tmp"
   #Justificate any indent: if fi
   sed -i -r '/^[ \t]*if/,/^[ \t]*fi/{ /^[ \t]*if/{h;s/[^[:blank:]].*//;x;b;}; /^[ \t]*if|^[ \t]*fi|^[ \t]*else/! {;G;s/[[:blank:]]*(.*)\n(.*)/\2   \1/}}' "$tmp"
   #Formating ui_print " " to ui_print ' '
   sed -i -r 's/^([ \t]*ui_print*)[^"]*"([^"]*)["]/\1 '\''\2'\''/g' "$tmp"
   #End
   if is_valid "$tmp"; then
      [ -z "$2" ] && copy "$tmp" "$1" || copy "$tmp" "$2"
      delete "$tmp"
   else echo2 "Oops! Empty result" && return 1
   fi
}

get_var_refs() {
   echo "$@" | grep -Eo '%[a-zA-Z_][a-zA-Z0-9_]*' | cut -c 2-
}

multi_option() {
   local var="$1" end="$2" i=0 loop=false varname vars vars2 ref
   local selected="${n}Selected %i" skipped="Skipped: %i"
   [ -z "$var" ] && return 0
   shift
   while [ $# -gt 0 ]; do
     case $1 in
       loop)
       loop=true
       shift;
       ;;
       *=*)
       varname="${1%%=*}"
       if testvarname "$varname"; then
          eval "$varname='${1#*=}'"
       else
          echo2 "Invalid variable name: $varname"
          return 0
       fi
       shift;
       ;;
       *)
       shift
       ;;
     esac
   done
   vars=$(get_var_refs "$selected")
   vars2=$(get_var_refs "$skipped")
   while true; do
      i=$(($i+1))
      selectedout="$selected"
      skippedout="$skipped"
      for ref in $vars; do
         selectedout="${selectedout//%$ref/${!ref}}"
      done
      for ref in $vars2; do
         skippedout="${skippedout//%$ref/${!ref}}"
      done
      if $yes; then
         ui_print "$selectedout"
         setdefault $var $i
         return $i
      else ui_print "$skippedout"
      fi
      if is_number $end && [ $i -ge $end ]; then
         if $loop; then i=0; continue
         else setdefault $var ""
         return 0
         fi
      fi
   done
}

obfuscate() {
   local _obf_ _header_ _key_ _random_ _keys_ _orig_ _prev_ _native_
   local restore flag b64=false
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -base64|-b64)
       b64=true
       shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   [ ! -z "$1" ] && _native_=$(echo "$1" | sed '/^[[:space:]]*#/d') || _native_=$(cat | sed '/^[[:space:]]*#/d')
   if [ ! -z "$_native_" ]; then
      if $b64; then
         b64=$(echo "$_native_" | base64)
         _native_="eval \"\$(echo \"$b64\" | base64 -d)\""
      fi
      _orig_="$_native_"; _prev_="$_native_"
   else return 1
   fi
   while IFS= read _obf_; do
      [ -z "$_obf_" ] && continue
      _random_=$(echo $RANDOM | tr '[0-9]' '[a-z]' | tr -d "$_obf_")
      _orig_=$(echo "$_orig_" | sed -Ee :1 -e 's/^(([^{]|\{[^{]*\})*)['"$_obf_"']/\1\$\{'"$_random_"'\}/;t1')
      if [[ "$_orig_" != "$_prev_" ]]; then [ "$_obf_" != "\"" ] && _keys_+="$_random_=\"$_obf_\";" || _keys_+="$_random_=\\$_obf_;"; fi
      _prev_="$_orig_"
   done <<< $(echo "${_orig_}" | tr -d '${}' | grep -Eo '[[:print:]]|[[:space:]]' | sort -uf)
   _orig_="eval \"$_orig_\""
   for _obf_ in " " a e l v; do
      _random_=$(echo "$_keys_" | tr ';' '\n' | grep -F "=\"$_obf_\"" | cut -d= -f1)
      if [ -z "$_random_" ]; then _random_=$(echo $RANDOM | tr '[0-9]' '[a-z]' | tr -d "$_obf_"); _keys_+="$_random_=\"$_obf_\";"; fi
      _orig_=$(echo "$_orig_" | sed -Ee :1 -e 's/^(([^{]|\{[^{]*\})*)['"$_obf_"']/\1\$\{'"$_random_"'\}/;t1')
   done
   case $_orig_ in
      *$'\n'*)
      _random_=$(echo $RANDOM | tr '[0-9]' '[a-z]')
      _keys_+="$_random_=\$'\n';"
      _orig_=$(echo "$_orig_" | sed '/^[[:space:]]*$/d')
      _orig_=${_orig_//$'\n'/\$\{$_random_\}}
      ;;
      *);;
   esac
   if [[ "$_orig_" != "$_native_" ]]; then
      _orig_=${_orig_//"\$\$"/\\\$\$}
      echo "${_keys_}$_orig_"
   else return 1
   fi
}

replace_name() {
   #regex="([^//]*/)*(${1}[^//]*)$"
   local restore flag dir=false file=false find any name newname r regex
   local fix='s/[]()\/$*.^[]/\\&/g'
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -dirs|-d)
       dir=true
       shift;
       ;;
       -files|-f)
       file=true
       shift;
       ;;
       -recursive|-r)
       r=true
       shift;
       ;;
       -regex)
       regex=true
       shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   if ($file && $dir); then if [ -z "$r" ]; then if [ ! -f "$3" -o ! -d "$3" ]; then echo2 "CANT FIND: $3" && return 1; fi; else find=" ( -type f -o -type d ) "; fi
   elif $file; then if [ -z "$r" ]; then if [ ! -f "$3" ]; then echo2 "CANT FIND FILE: $3" && return 1; fi; else find=" -type f "; fi
   elif $dir; then if [ -z "$r" ]; then if [ ! -d "$3" ]; then echo2 "CANT FIND DIR: $3" && return 1; fi; else find=" -type d "; fi
   else if [ -z "$r" ]; then if [ ! -e "$3" ]; then echo2 "CANT FIND: $3" && return 1; fi; fi
   fi
   if [ -n "$r" -a ! -d "$3" ]; then echo2 "CANT FIND DIR: $3" && return 1; fi
   while read any; do
       [ -z "$any" ] && break
       name=$(basename "$any")
       if [ -n "$regex" ]; then newname=$(echo "$name" | sed -E "s;$1;$2;g"); else newname=${name//"$1"/"$2"}; fi
       if [[ "$newname" == "$name" ]]; then continue; fi
       rename "$any" "$newname"
   done <<< $(if [ -n "$r" ]; then find "$3" -mindepth 1 $find; else echo "$3"; fi)
}

abort() {
   ui_print "$@"
   umount_all
   [ -n "$MODPATH" ] && delete_recursive "$MODPATH"
   remove_tmp
   restore_env
   exit 130
}

end() {
   ui_print "$@"
   umount_all
   [ -n "$MODPATH" ] && delete_recursive "$MODPATH"
   remove_tmp
   restore_env
   exit 0
}

bin_info() {
   local check
   if ! grep -wm1 "$1" "$l/info.txt" 2>/dev/null; then
      if [ -f "$1" ]; then
         check=$(binarch "$1")
         if [[ $check == 64bits ]]; then echo "$(basename "$1"): 64bits"
         elif [[ $check == 32bits ]]; then echo "$(basename "$1"): 32bits"
         else echo "$(basename "$1"): Unknown"
         fi
      elif [ -f "$(command -v "$1")" ]; then
         check=$(binarch "$(command -v "$1")")
         if [[ $check == 64bits ]]; then echo "$1: 64bits"
         elif [[ $check == 32bits ]]; then echo "$1: 32bits"
         else echo "$1: Unknown"
         fi
      else return 1
      fi
   fi
}

wipe() {
   #wipe system vendor data
   local wipe return=0
   for wipe in "$@"; do
      if [[ "$wipe" == "data" ]]; then
         if ! try_mount -e -rw /data; then echo "CANT WIPE: /data" && return=1 && continue; fi
         echo2 " -- Wiping /data"
         find /data -mindepth 1 ! -regex "/data/media.*" -delete
      elif [[ "$wipe" == "userdata" ]]; then
         if ! try_mount -e -rw /data; then echo "CANT WIPE: /data and /data/media" && return=1 && continue; fi
         echo2 " -- Wiping /data and /data/media"
         rm -rf /data/*
      elif [[ "$wipe" == "dalvik" ]]; then
         if ! try_mount -e -rw /data; then echo "CANT WIPE: /data/dalvik-cache" && return=1 && continue; fi
         echo2 " -- Wiping /data/dalvik-cache"
         rm -rf /data/dalvik-cache
      else
         if ! try_mount -rw "/$wipe"; then echo "CANT WIPE: /$wipe" && return=1 && continue; fi
         echo2 " -- Wiping /$wipe"
         rm -rf /$wipe/*
         unmount "/$wipe"
      fi
   done
   return $return
}

set_metadata() {
  local file i;
  file="$1";
  shift;
  testrw "$(dirname "$file")" || return 1
  while [ "$2" ]; do
    case $1 in
      uid) chown $2 "$file";;
      gid) chown :$2 "$file";;
      mode) chmod $2 "$file";;
      capabilities) if [ -f "$(which twrp)" ]; then twrp setcap "$file" $2; else echo2 'Oops!  You can't apply "capabilities" without the "twrp" binary, don't mount /system! to use it'; fi ;;
      selabel) ch_con $2 "$file";;
      *) ;;
    esac;
    shift 2;
  done;
}

set_metadata_recursive() {
  local dir uid gid fmode dmode capabilities selabel twrp=false huh;
  dir="$1";
  shift;
  testrw "$dir" || return 1
  while [ "$2" ]; do
    case $1 in
      uid) uid=$2;;
      gid) gid=$2;;
      fmode) fmode=$2;;
      dmode) dmode=$2;;
      capabilities) capabilities=$2;;
      selabel) selabel=$2;;
      *) ;;
    esac;
    shift 2;
  done;
  if [ -n "$capabilities" ]; then [ -f "$(which twrp)" ] && twrp=true || echo2 'Oops!  You can't apply "capabilities" without the "twrp" binary, don't mount /system! to use it'; fi
  [ -n "$uid" ] && chown -R $uid "$dir"
  [ -n "$gid" ] && chown -R :$gid "$dir"
  if [ -n "$fmode" ]; then
     chmod -R $fmode "$dir"
     find "$dir" -type l -exec chmod $fmode {} +;
  fi
  [ -n "$dmode" ] && find "$dir" -type d -exec chmod $dmode {} +;
  find "$dir" -mindepth 1 | while read huh; do
     $twrp && [ -n "$capabilities" ] && twrp setcap "$huh" $capabilities
     [ -n "$selabel" ] && ch_con $selabel "$huh"
  done
}

update_file() {
   local file get newprop prop result propc xml test newprop2 space split
   local restore flag tmp huh save result save2 save3 TMP2
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -delim|-d)
       save=("$1" "$2" "$3")
       shift;shift;shift;
       ;;
       -no-spaces|-ns)
       save2=("$1")
       shift
       ;;
       -p|-pattern)
       save3=("$1" "$2")
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   file="$2"
   if [ ! -e "$file" ]; then echo "CANT FIND: $file" && return 1; fi
   if [ ! -e "$1" ]; then echo "CANT FIND: $1" && return 1; fi
   testrw "$(dirname "$file")" || return 1
   start_tmp
   tmp="$TMP2/$(basename "$file")"
   cat "$file" > "$tmp"
   while IFS='' read -r prop || [[ -n "$prop" ]]; do
         huh+=("$prop")
   done < "$1"
   update_file_string "${save[@]}" "${save2[@]}" "${save3[@]}" "${huh[@]}" "$tmp"
   result=$?
   inject "$tmp" "$(dirname "$file")"
   end_tmp
   return $result
}

update_file_string() {
   local vars file array newprop prop propc xml result new old
   local restore flag d d2 huh tmp extract newprop2 backdir space space2 newspace nospace
   local p TMP2 ended
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -delim|-d)
       d="$2"
       d2="$3"
       shift;shift;shift;
       ;;
       -no-spaces|-ns)
       nospace=true
       shift
       ;;
       -p|-pattern)
       p="$2"
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   array=( "$@" )
   file=${array[${#array[@]} - 1]}
   if [ ! -e "$file" ]; then echo "CANT FIND: $file" && return 1; fi
   testrw "$(dirname "$file")" || return 1
   savestate old "$file"
   start_tmp 
   tmp="$TMP2/$(basename "$file")"
   unset "array[${#array[@]}-1]"
   cat "$file" > "$tmp"
   if [ -n "$d" ]; then
      huh=$(cat "$file")
      backdir="$file"
      if [ -z "$p" ]; then
         extract=$(string complete_extract "$d" "$d2" "$huh")
      else
         extract=$(string -r -p "$p" complete_extract "$d" "$d2" "$huh")
      fi
      file="$tmp"
      if [ -n "$extract" ]; then echo "$extract" > "$tmp"; else echo "CANT GET CONTENT: $d $d2" && return 1; fi
   fi
   for prop in "${array[@]}"; do
         if [ -z "$prop" ]; then continue; fi
         newprop=
         propc=
         xml=
         space=
         space2=
         newspace=
         test "${prop#*=}" != "$prop" && propc=1
         test "${prop#*/>}" != "$prop" && xml=1
         test "${prop#*</}" != "$prop" && xml=1
         if [[ -n "$xml" && -n "$propc" ]]; then propc=0; fi
         if [[ "$xml" == "1" ]]; then
             newprop=$(string inside_symbol '="' '"' "$prop")
             newprop2=$(string inside_symbol ' ' ='"'$newprop'"' "$prop") 
             if [ -n "$newprop" ]; then
               if [[ $(grep -o "$newprop2" "$file" | wc -l) == "1"  ]]; then
                  newprop="$newprop2"
               elif ! is_substring "$newprop2" "$(grep -m1 "$newprop" "$file")"; then
                  newprop=
               fi
             fi
             if [[ -z "$newprop" || "$newprop" == "$prop" ]]; then
                if is_substring '</' "$prop"; then
                   newprop=$(echo "$prop" | sed -e 's/.*<\/\(.*\)>.*/\1/') 
                elif is_substring '/>' "$prop"; then
                   newprop=$(echo "$prop" | sed -e 's/.*<\(.*\)\/>.*/\1/')
                fi
             fi
             if [[ -n "$newprop" && "$newprop" != "$prop" ]]; then
                space=$(grep -m1 "$newprop" "$file" | cut -d'<' -f1)
                space2=$(echo "$prop" | cut -d'<' -f1)
                if [[ "$space" != "$space2" && -z "$nospace" ]]; then
                   newspace="$space"
                fi
             else
                newprop=
                echo2 " WARNING: Abnormal xml $prop"
             fi
             if [ -z "$newprop" ]; then continue; fi
             echo2 ">>$newprop"
             prop=$(echo "${newspace}${prop}" | sed -e 's/[]\/$*.^[]/\\&/g')
             result=$(sed "s/.*$(string force escape "$newprop").*/${prop}/" "$tmp")
             ended=$?
         fi   
         if [[ "$propc" == "1" ]]; then  
             newprop=$(echo "$prop" | cut -d '=' -f1 | tr -d " ")
             if [ -z "$newprop" ]; then continue; fi
             echo2 "::$newprop"
             prop=$(echo "${newspace}${prop}" | sed -e 's/[]\/$*.^[]/\\&/g')
             result=$(sed "s/^[ \t]*$(string force escape "$newprop")\=.*/${prop}/" "$tmp")
             ended=$?
         fi  
         if [ ! -z "$result" ]; then echo "$result" > "$tmp"; fi
   done
   if [ -n "$d" ]; then
      file="$backdir"
      echo "${huh/$extract/$result}" > "$tmp"
   fi
   if ! is_valid "$tmp"; then echo2 " FATAL ERROR: Invalid $tmp " && return 1; fi
   inject "$tmp" "$(dirname "$file")"
   end_tmp 
   savestate new "$file"
   if [[ "$old" != "$new" ]]; then true; else echo2 "ERROR: No real matches, content not modified: $file"; false; fi
}

update_file_addon() {
   local restore flag save save2 save3
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -delim|-d)
       save=("$1" "$2" "$3")
       shift;shift;shift;
       ;;
       -no-spaces|-ns)
       save2=("$1")
       shift
       ;;
       -p|-pattern)
       save3=("$1" "$2")
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   update_file "${save[@]}" "${save2[@]}" "${save3[@]}" "$addons/$1" "$2"
}

update_file_zip() {
   local restore flag save result save2 save3 tmp TMP2
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -delim|-d)
       save=("$1" "$2" "$3")
       shift;shift;
       ;;
       -no-spaces|-ns)
       save2=("$1")
       shift
       ;;
       -p|-pattern)
       save3=("$1" "$2")
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   start_tmp
   tmp="$TMP2/update.tmp"
   package_extract_file "$1" "$tmp"
   update_file "${save[@]}" "${save2[@]}" "${save3[@]}" "$tmp" "$2"
   result=$?
   end_tmp
   return $result
}

force_update_file() {
   local file get newprop prop result propc xml test newprop2 space split
   local restore flag tmp huh save save2 save3 TMP2
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -delim|-d)
       save=("$1" "$2" "$3")
       shift;shift;shift;
       ;;
       -no-spaces|-ns)
       save2=("$1")
       shift
       ;;
       -p|-pattern)
       save3=("$1" "$2")
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   file="$2"
   if [ ! -e "$file" ]; then echo "CANT FIND: $file" && return 1; fi
   if [ ! -e "$1" ]; then echo "CANT FIND: $1" && return 1; fi
   testrw "$(dirname "$file")" || return 1
   start_tmp
   tmp="$TMP2/$(basename "$file")"
   cat "$file" > "$tmp"
   while IFS='' read -r prop || [[ -n "$prop" ]]; do
         huh+=("$prop")
   done < "$1"
   force_update_file_string "${save[@]}" "${save2[@]}" "${save3[@]}" "${huh[@]}" "$tmp"
   result=$?
   inject "$tmp" "$(dirname "$file")"
   end_tmp
   return $result
}

force_update_file_string() {
   local vars file array newprop prop propc xml result old new
   local restore flag d d2 huh tmp extract newprop2 limit backdir space space2 newspace nospace
   local p TMP2
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -delim|-d)
       d="$2"
       d2="$3"
       shift;shift;shift;
       ;;
       -no-spaces|-ns)
       nospace=true
       shift
       ;;
       -p|-pattern)
       p="$2"
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   array=( "$@" )
   file=${array[${#array[@]} - 1]}
   if [ ! -e "$file" ]; then echo "CANT FIND: $file" && return 1; fi
   testrw "$(dirname "$file")" || return 1
   savestate old "$file"
   start_tmp 
   tmp="$TMP2/$(basename "$file")"
   unset "array[${#array[@]}-1]"
   cat "$file" > "$tmp"
   if [ -n "$d" ]; then
      huh=$(cat "$file")
      backdir="$file"
      if [ -z "$p" ]; then
         extract=$(string complete_extract "$d" "$d2" "$huh")
      else
         extract=$(string -r -p "$p" complete_extract "$d" "$d2" "$huh")
      fi
      file="$tmp"
      if [ -n "$extract" ]; then echo "$extract" > "$tmp"; else echo "CANT GET CONTENT: $d $d2" && return 1; fi
   fi
   for prop in "${array[@]}"; do
         limit=$(($limit + 1))
         newprop=
         propc=
         xml=
         space=
         space2=
         newspace=
         test "${prop#*=}" != "$prop" && propc=1
         test "${prop#*/>}" != "$prop" && xml=1
         test "${prop#*</}" != "$prop" && xml=1
         if [[ -n "$xml" && -n "$propc" ]]; then propc=0; fi
         if [[ "$xml" == "1" ]]; then
             newprop=$(string inside_symbol '="' '"' "$prop")
             newprop2=$(string inside_symbol ' ' ='"'$newprop'"' "$prop") 
             if [ -n "$newprop" ]; then
               if [[ $(grep -o "$newprop2" "$file" | wc -l) == "1"  ]]; then
                  newprop="$newprop2"
               elif ! is_substring "$newprop2" "$(grep -m1 "$newprop" "$file")"; then
                  newprop=
               fi
             fi
             if [[ -z "$newprop" || "$newprop" == "$prop" ]]; then
                if is_substring '</' "$prop"; then
                   newprop=$(echo "$prop" | sed -e 's/.*<\/\(.*\)>.*/\1/') 
                elif is_substring '/>' "$prop"; then
                   newprop=$(echo "$prop" | sed -e 's/.*<\(.*\)\/>.*/\1/')
                fi
             fi
             if [[ -n "$newprop" && "$newprop" != "$prop" ]]; then
                space=$(grep -m1 "$newprop" "$file" | cut -d'<' -f1)
                space2=$(echo "$prop" | cut -d'<' -f1)
                if [[ "$space" != "$space2" && -z "$nospace" ]]; then
                   newspace="$space"
                fi
             else
                newprop=
                echo2 " WARNING: Abnormal xml $prop"
             fi
             if [ -z "$newprop" ]; then continue; fi
             if [[ "$limit" == "1" ]]; then
                sed -i '/^$/d' "$tmp"
                last=$(tail -n 1 "$tmp")
                sed -i '$d' "$tmp"
             fi   
             echo2 ">>$newprop"
             result=$(sed "/$(string force escape "$newprop")/d" "$tmp")
         fi   
         if [[ "$propc" == "1" ]]; then  
             newprop=$(echo "$prop" | cut -d '=' -f1 | tr -d " ")
             if [ -z "$newprop" ]; then continue; fi
             echo2 "::$newprop"
             result=$(sed "/^[ \t]*$(string force escape "$newprop")\=/d" "$tmp")
         fi  
         echo "$result" > "$tmp"
         echo "${newspace}${prop}" >> "$tmp"
   done
   if [[ "$xml" == "1" ]]; then
       echo "$last" >> "$tmp"
   fi
   result=$(cat "$tmp")
   if [ -n "$d" ]; then
      file="$backdir"
      echo "${huh/$extract/$result}" > "$tmp"
   fi
   if ! is_valid "$tmp"; then echo2 " FATAL ERROR: Invalid $tmp " && return 1; fi
   inject "$tmp" "$(dirname "$file")"
   end_tmp 
   savestate new "$file"
   if [[ "$old" != "$new" ]]; then true; else false; fi
}

force_update_file_addon() {
   local restore flag save save2 save3
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -delim|-d)
       save=("$1" "$2" "$3")
       shift;shift;shift;
       ;;
       -no-spaces|-ns)
       save2=("$1")
       shift
       ;;
       -p|-pattern)
       save3=("$1" "$2")
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   force_update_file "${save[@]}" "${save2[@]}" "${save3[@]}" "$addons/$1" "$2"
}

force_update_file_zip() {
   local restore flag save result save2 save3 tmp TMP2
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -delim|-d)
       save=("$1" "$2" "$3")
       shift;shift;shift;
       ;;
       -no-spaces|-ns)
       save2=("$1")
       shift
       ;;
       -p|-pattern)
       save3=("$1" "$2")
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   start_tmp
   tmp="$TMP2/update.tmp"
   package_extract_file "$1" "$tmp"
   force_update_file "${save[@]}" "${save2[@]}" "${save3[@]}" "$tmp" "$2"
   result=$?
   end_tmp
   return $result
}

flash() {
    local fd
    if [ "$3" == "print" ]; then fd=${OUTFD##*/}
    else fd=3
    fi
    if ! is_zip "$2"; then echo2 "flash: $(basename "$2") isnt a ZIP!" && return 1; fi
	(unzip -qp "$2" "META-INF/com/google/android/update-binary" > "$TMP/update-binary-test") 2>/dev/null
    if ! is_valid "$TMP/update-binary-test"; then echo2 "flash: Empty binary: $(basename "$2") unsupported!" && return 1; fi
    chmod +x "$TMP/update-binary-test"
    setdefault "$1" "$("$TMP/update-binary-test" "$fd" "$fd" "$2" 2>&1)"
    if echo "$(checkvar "$1")" | grep -q "bad interpreter:"; then
       setdefault "$1" "$(sh "$TMP/update-binary-test" "$fd" "$fd" "$2" 2>&1)"
    fi
}

flash_addon() {
   flash "$1" "$addons/$2" "$3"
}

flash_zip() {
    package_extract_file "$2" "$TMP/$(basename "$2")" || return 1
    flash "$1" "$TMP/$(basename "$2")" "$3"
    rm -f "$TMP/$(basename "$2")"
}

add_lines() { 
    local old new restore flag al al_add bl bl_add file tmp array lines i TMP2
    local empty=false
    restore=()
    while [[ $# -gt 0 ]]; do
    flag="$1"
    case $flag in
       -al|-after-line)
       al+=("$2")
       al_add+=("$3")
       shift;shift;shift;
       ;;
       -bl|-before-line)
       bl+=("$2")
       bl_add+=("$3")
       shift;shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
    esac
    done
    set -- "${restore[@]}"
    array=( "$@" )
    file=${array[${#array[@]} - 1]}
    if [ ! -f "$file" ]; then create_file "$file" || return 1; else testrw "$(dirname "$file")" || return 1; fi
    is_valid "$file" || empty=true
    unset "array[${#array[@]}-1]"
    start_tmp 
    tmp="$TMP2/$(basename "$file")"
    cat "$file" > "$tmp"
    for lines in "${array[@]}"; do
       cat "$lines" >> "$tmp"
    done
    for i in "${!al[@]}"; do
       add_lines_string -al "${al[i]}" "$(cat "${al_add[i]}")" "$tmp"
    done
    for i in "${!bl[@]}"; do
       add_lines_string -bl "${bl[i]}" "$(cat "${bl_add[i]}")" "$tmp"
    done
    savestate old "$file"
    if ! is_valid "$tmp" && ! $empty; then echo2 " FATAL ERROR: Invalid $tmp" && return 1; fi
    inject "$tmp" "$(dirname "$file")"
    end_tmp
    savestate new "$file"
    if [[ "$old" != "$new" ]]; then true; else false; fi
}

add_lines_string() {
    local vars file array lines restore flag get try
    local al al_add bl bl_add i old new
    local empty=false TMP2 tmp
    restore=()
    while [[ $# -gt 0 ]]; do
    flag="$1"
    case $flag in
       -al|-after-line)
       al+=("$2")
       al_add+=("$3")
       shift;shift;shift;
       ;;
       -bl|-before-line)
       bl+=("$2")
       bl_add+=("$3")
       shift;shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
    esac
    done
    set -- "${restore[@]}"
    array=( "$@" )
    file=${array[${#array[@]} - 1]}
    if [ ! -f "$file" ]; then create_file "$file" || return 1; else testrw "$(dirname "$file")" || return 1; fi
    is_valid "$file" || empty=true
    savestate old "$file"
    unset "array[${#array[@]}-1]"
    start_tmp 
    tmp="$TMP2/$(basename "$file")"
	cat "$file" > "$tmp"
	for lines in "${array[@]}"; do
	   echo "$lines" >> "$tmp"
	done
    if [ -n "$al" ]; then
       for i in "${!al[@]}"; do
          try=$(string -f "$tmp" -after-line "${al[i]}" "${al_add[i]}")
          [ -n "$try" ] && echo "$try" > "$tmp"
       done
    fi
    if [ -n "$bl" ]; then
       for i in "${!bl[@]}"; do
          try=$(string -f "$tmp" -before-line "${bl[i]}" "${bl_add[i]}")
          [ -n "$try" ] && echo "$try" > "$tmp"
       done
    fi
    if ! is_valid "$tmp" && ! $empty; then echo2 " FATAL ERROR: Invalid $tmp " && return 1; fi
    inject "$tmp" "$(dirname "$file")"
    end_tmp 
    savestate new "$file"
    if [[ "$old" != "$new" ]]; then true; else false; fi
}

add_lines_addon() {
    local old new restore flag al al_add bl bl_add file tmp array lines i TMP2
    local empty=false
    restore=()
    while [[ $# -gt 0 ]]; do
    flag="$1"
    case $flag in
       -al|-after-line)
       al+=("$2")
       al_add+=("$3")
       shift;shift;shift;
       ;;
       -bl|-before-line)
       bl+=("$2")
       bl_add+=("$3")
       shift;shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
    esac
    done
    set -- "${restore[@]}"
    array=( "$@" )
    file=${array[${#array[@]} - 1]}
    if [ ! -f "$file" ]; then create_file "$file" || return 1; else testrw "$(dirname "$file")" || return 1; fi
    is_valid "$file" || empty=true
    unset "array[${#array[@]}-1]"
    start_tmp 
    tmp="$TMP2/$(basename "$file")"
    cat "$file" > "$tmp"
    for lines in "${array[@]}"; do
       cat "$addons/$lines" >> "$tmp"
    done
    for i in "${!al[@]}"; do
       add_lines_string -al "${al[i]}" "$(cat "$addons/${al_add[i]}")" "$tmp"
    done
    for i in "${!bl[@]}"; do
       add_lines_string -bl "${bl[i]}" "$(cat "$addons/${bl_add[i]}")" "$tmp"
    done
    savestate old "$file"
    if ! is_valid "$tmp" && ! $empty; then echo2 " FATAL ERROR: Invalid $tmp" && return 1; fi
    inject "$tmp" "$(dirname "$file")"
    end_tmp
    savestate new "$file"
    if [[ "$old" != "$new" ]]; then true; else false; fi
}

add_lines_zip() {
    local old new restore flag al al_add bl bl_add file tmp array lines i TMP2
    local empty=false
    restore=()
    while [[ $# -gt 0 ]]; do
    flag="$1"
    case $flag in
       -al|-after-line)
       al+=("$2")
       al_add+=("$3")
       shift;shift;shift;
       ;;
       -bl|-before-line)
       bl+=("$2")
       bl_add+=("$3")
       shift;shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
    esac
    done
    set -- "${restore[@]}"
    array=( "$@" )
    file=${array[${#array[@]} - 1]}
    if [ ! -f "$file" ]; then create_file "$file" || return 1; else testrw "$(dirname "$file")" || return 1; fi
    is_valid "$file" || empty=true
    unset "array[${#array[@]}-1]"
    start_tmp 
    tmp="$TMP2/$(basename "$file")"
    cat "$file" > "$tmp"
    for lines in "${array[@]}"; do
       delete "$TMP2/add.tmp"
       package_extract_file "$lines" "$TMP2/add.tmp"
       [ -f "$TMP2/add.tmp" ] && cat "$TMP2/add.tmp" >> "$tmp"
    done
    for i in "${!al[@]}"; do
       delete "$TMP2/add.tmp"
       package_extract_file "${al_add[i]}" "$TMP2/add.tmp"
       [ -f "$TMP2/add.tmp" ] && add_lines_string -al "${al[i]}" "$(cat "$TMP2/add.tmp")" "$tmp"
    done
    for i in "${!bl[@]}"; do
       delete "$TMP2/add.tmp"
       package_extract_file "${bl_add[i]}" "$TMP2/add.tmp"
       [ -f "$TMP2/add.tmp" ] && add_lines_string -bl "${bl[i]}" "$(cat "$TMP2/add.tmp")" "$tmp"
    done
    savestate old "$file"
    if ! is_valid "$tmp" && ! $empty; then echo2 " FATAL ERROR: Invalid $tmp" && return 1; fi
    inject "$tmp" "$(dirname "$file")"
    end_tmp
    savestate new "$file"
    if [[ "$old" != "$new" ]]; then true; else false; fi
}

find_bin() { [ -n "$2" ] && echo "$2" || echo "$binary_paths" | while read path; do [ -e "$path/$1" ] && echo "$path/$1" && break; done; }
import_info() { ( echo >> "$l/info.txt"; cat "$1" >> "$l/info.txt"; rm -f "$1" ) 2>/dev/null; }
get() {
  local args=$# skip=0 from to in get rm root
  while [ $args -gt 0 ]; do
    case $1 in
       -from)
       from="$2"
       skip=2;
       ;;
       -to)
       to="$2"
       skip=2;
       ;;
       -in)
       in="$2"
       skip=2;
       ;;
       *)
       set -- "$@" "$1"
       skip=1;
       ;;
    esac
    shift $skip; args=$(($args-$skip)); 
  done
  [ -z "$from" -o ! -f "$from" ] && abort "ERROR: Invalid ZIP: \"$from\""
  [ -z "$to" ] && to="$PWD"; to="${to%%/}/"
  [ -n "$in" ] && in="${in%%/}/"
  for get in "$@"; do
     root="${in}$get"
     echo2 "Getting: $get"
     unzip -qo "$from" "$root" -d "$to"
     if [ -f "${to}$root" ]; then
        mv -f "${to}$root" "${to}$get"
        chmod 755 "${to}$get"
        if [ -f "${to}$get" ]; then
           rm=${root%%/*}
           [ "$rm" != "$root" -a -d "${to}$rm" ] && rm -rf "${to}$rm"
        else abort "ERROR:2: Cant get $get"
        fi
     else abort "ERROR:1: Cant get $get"
     fi
  done
}
ensure_bin() {
   local path bin
   [ -z "$needed" ] && local needed=false
   [ -z "$bb" ] && bb=$(find_bin busybox)
   while [ "$1" ]; do
      bin=
      if [ ! -e "$(command -v "$1")" ]; then
         bin=$(find_bin $1); [ -n "$bin" ] && bin="\"$bin\""
         if [ -z "$bin" -a -n "$bb" ] && "$bb" --list | "$bb" grep -Eq "^$1$"; then bin="\"$bb\" $1"; fi
         [ -n "$bin" ] && eval "$1() { $bin \"\$@\"; }"
         ($needed && [ -z "$bin" ]) && abort "ERROR: Could not define \"$1\" binary"
      fi
      shift;
   done
}
change_bin() {
   local restore flag while
   local path current pass name return=0
   create_dir "$TMP/change_bin" || return 1
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -while|-w)
       while="$2"
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   echo "$PATH" | grep -Eq "^$TMP/change_bin" || export PATH="$TMP/change_bin:$PATH"
   while [ "$1" ]; do
      current=
      pass=false
      name=$((basename "$1" || echo "$1")2>/dev/null)
      [ -n "$(declare -f "$name" 2>/dev/null)" ] && echo2 "Unsetting function: \"$name\"" && unset -f "$name"
      [ -e "$TMP/change_bin/$name" ] && current=$(readlink "$TMP/change_bin/$name") || current=$(command -v "$name")
      echo2 " "
      if [ -e "$current" ]; then echo2 "Currently using: \"$current\" binary"
      else echo2 "Cant find any reference for: \"$name\" binary" && return 1
      fi
      while read bin; do
          if [ -e "$bin" -a "$bin" != "$current" ] && (if [ -n "$while" ]; then support_option "$while" "$bin"; else true; fi); then echo2 "Changing to: \"$bin\" binary"; ln -sf "$bin" "$TMP/change_bin/$name" && pass=true; break; fi
      done <<< $([ -f "$1" ] && echo "$1" || (which "$1" || command -v "$1"; find_bin "$1"; split_string : "$PATH" | while read path; do echo "$path/$1"; done) | sort -bu | grep -Ev "^$TMP/change_bin" | shuf)
      if ! $pass; then echo2 "No possible changes found for: \"$name\" binary"; return=1; fi
      echo2 " "
      shift;
   done
   return $return
}
<<"EOF"
change_bin() {
   local path current pass name return=0
   while [ "$1" ]; do
      pass=false
      name=$(basename "$1")
      echo2 " "
      current=$(declare -f "$name" 2>/dev/null | sed -n 's/^[^"]*"\([^"]*\)".*/\1/p' | head -n1)
      [ -z "$current" ] && current=$(which "$name" || command -v "$name")
      [ -n "$current" ] && echo2 "Currently using: \"$current\" binary"
      while read bin; do
          if [ -e "$bin" -a "$bin" != "$current" ]; then echo2 "Changing to: \"$bin\" binary"; eval "$name() { if [ -f \"$bin\" ]; then \"$bin\" \"\$@\"; else echo2 \"Cant find: $bin binary\"; change_bin $name && $name \"\$@\"; fi; }" && pass=true; break; fi
      done <<< $([ -f "$1" ] && echo "$1" || (which "$1" || command -v "$1"; find_bin "$1"; split_string : "$PATH" | while read path; do echo "$path/$1"; done) | sort -bu | shuf)
      if ! $pass; then echo2 "No possible changes found for: \"$name\" binary"; return=1; fi
      echo2 " "
      shift;
   done
   return $return
}
EOF
setup_bb() {
   #Try to ensure /system/bin/sh
   needed=false; ensure_bin umount ln
   if [ ! -f /system/bin/sh ]; then
      umount -l /system 2>/dev/null
      mkdir -p /system/bin
      ln -sf "$(command -v sh)" /system/bin/sh
   fi
   if [ -e "$bb" ]; then
     if ! echo "read me" | "$bb" grep -q "read me"; then
        abort "ERROR:4: BusyBox cannot load on this device!"
     elif ! "$bb" --install -s "$l"; then
        for i in $("$bb" --list); do
          if ! ln -sf "$bb" "$l/$i" && ! "$bb" ln -sf "$bb" "$l/$i" && ! "$bb" ln -f "$bb" "$l/$i"; then
            # create script wrapper if symlinking and hardlinking failed because of restrictive selinux policy
            if ! echo "#!$bb" > "$l/$i" || ! chmod 755 "$l/$i"; then
              abort "ERROR:2: Failed to setup BusyBox"
            fi
          fi
        done
     fi
   else
      abort "ERROR:1: Cant find BusyBox"
   fi
   [ ! -f "$l/sh" ] && abort "ERROR:3: Failed to setup BusyBox"
}

set_context() {
   local context huh check="$1" check2="$2" contextback path dir ext try backdir
   if exist "$1"; then context=$(eval_context "$1"); else echo2 "set_context: Invalid line" && return 1; fi
   if defined context; then contextback="$context"; else echo2 "set_context: FATAL ERROR: Cant get context from $1" && return 1; fi
   if ! exist file "$2" || exist folder "$2"; then
      testrw "$2" || return 1
      find -L "$2" \( -type f -o -type d -o -type l \) 2>/dev/null | while read huh; do
         dir=
         try=
         ext=
         undefined huh && break
         context="$contextback"
         if exist folder "$1" && exist "$1$(string remove "$2" "$huh")"; then
            context=$(eval_context "$1$(string remove "$2" "$huh")")
         else
            while read -r -d/ path || [ -n "$path" ]; do
              if defined path; then
                dir+="/$path"
                if exist folder "$dir"; then
                  context=$(eval_context "$dir")
                  backdir="$dir"
                fi
              fi
            done <<< $(dirname "$1$(string remove "$2" "$huh")")
            ext=${huh##*.}
            if exist file "$huh"; then
               if defined ext; then try=$(find -L "$backdir" -mindepth 1 -type f -name "*.$ext" | head -n1); fi
               if exist "$try"; then context=$(eval_context "$try"); fi
            fi
         fi
         if undefined context; then context="$contextback"; fi
         ch_con $context "$huh"
      done
   elif exist file "$2"; then
      ch_con $context "$2"
   else
      echo2 "set_context: Invalid line" && return 1
   fi
}

eval_context() { ls -Z "$1" | cut -d' ' -f1 | sort | uniq -c | sort -rn | head -n1 | awk '{print $2}'; }

get_context() { ([ -d "$1" ] && ls -Zd "$1" | cut -d' ' -f1 || ls -Z "$1" | cut -d' ' -f1 ) 2>/dev/null; }

eval_perm() { ([ -d "$1" ] && stat -c "%a" "$1"/* || stat -c "%a" "$1" ) 2>/dev/null | sort | uniq -c | sort -rn | head -n1 | awk '{print $2}'; }

eval_user() { ([ -d "$1" ] && stat -c "%u" "$1"/* || stat -c "%u" "$1" ) 2>/dev/null | sort | uniq -c | sort -rn | head -n1 | awk '{print $2}'; }

eval_group() { ([ -d "$1" ] && stat -c "%g" "$1"/* || stat -c "%g" "$1" ) 2>/dev/null | sort | uniq -c | sort -rn | head -n1 | awk '{print $2}'; }

get_all_perm() { stat -c "%u %g %a" "$1" 2>/dev/null; }

eval_all_perm() { echo "$(eval_user "$1") $(eval_group "$1") $(eval_perm "$1")"; }

patch_fstab() {
   #patch_fstab userdata "fileencryption" "encryptable" "FILE"
   local huh fstab result stock new
   if exist "$4"; then savestate stock "$4"; else echo2 "patch_fstab: Cant find $4" && return 1; fi
   fstab=$(grep -Em1 "^[[:space:]]*[^#].*$(string escape "$1")" "$4")
   if undefined fstab; then echo2 "patch_fstab: Cant get \"$1\" line in $4" && return 1; fi
   huh=$(split_string ',' "$fstab" | grep -m1 "$2")
   if defined huh; then
      result=$(string replace "$huh" "$3" "$fstab")
      if undefined result; then echo2 "patch_fstab: Nothing to do: \"$huh\"" && return 0; fi
      echo2 "patch_fstab: $huh to $3"
      replace "$fstab" "$result" "$4" || return 1
      savestate new "$4"
      if [[ "$stock" != "$new" ]]; then echo2 "patch_fstab: Patched $4" && return 0; else return 1; fi
   else
      echo2 "patch_fstab: Cant find \"$2\" property in \"$fstab\"" && return 1
   fi
}

import_config() {
    local ugu propc return=0
    while IFS='' read -r ugu || [[ -n "$ugu" ]]; do
          propc=
          test "${ugu#*=}" != "$ugu" && propc=1
          if [[ "$propc" == "1" ]]; then 
              if testvarname "$(split_extract = 1 "$ugu" | tr -d ' ')"; then setdefault "$(split_extract = 1 "$ugu" | tr -d ' ')" "$(split_extract = 2 "$ugu")"
              else echo2 "import_config: $(split_extract = 1 "$ugu" | tr -d ' ') cannot be converted to variable" && return=1
              fi
          fi   
    done < "$1"
    return $return
}

import_config_addon() {
    import_config "$addons/$1"
}

import_config_zip() {
    package_extract_file "$1" "$TMP/$(basename "$1")" || return 1
    import_config "$TMP/$(basename "$1")"
    rm -f "$TMP/$(basename "$1")"
}

getdefault() {
  grep -m1 "^setdefault $2" "$1" | tr -d '"' | sed s/"setdefault $2 "//
}

savestate() {
    setdefault "$1" "$(md5sum "$2" | awk '{ print $1; }')"
}

startlog() {
    setdefault logpath "$1"
    create_file "$1" || endlog
}

endlog() { logpath=; }

savelog() {
    if [ -n "$logpath" ]; then
      while [ "$1" ]; do
         echo "$1" >> "$logpath"
         shift;
      done
    fi
    return 0
}

echolog() {
    if [ -n "$logpath" ]; then
      while [ "$1" ]; do
         echo "$1" >> "$logpath"
         echo2 "$1"
         shift;
      done
    fi
    return 0
}

printlog() {
    if [ -n "$logpath" ]; then
      while [ "$1" ]; do
         echo "$1" >> "$logpath"
         ui_print "$1"
         shift;
      done
    fi
    return 0
}

symlink() {
  local file=$(fullpath "$1");
  [ ! -e "$file" ] && return 1
  while [ "$2" ]; do
    testrw "$(dirname "$2")" || return 1
    ln -sf "$file" "$2";
    shift;
  done;
}

restore_con() {
  restorecon -R "$@";
}

backup_files() {
  while [ "$1" ]; do
    testrw "$(dirname "$1")" || return 1
    test ! -e "$1.bak" && cp -pf "$1" "$1.bak";
    shift;
  done;
}

restore_files() {
  while [ "$1" ]; do
    testrw "$(dirname "$1")" || return 1
    mv -f "${1}.bak" "$1";
    shift;
  done;
}

sha3_check() {
  local sum=$(sha3sum $1 | cut -c-40);
  if [ ! "$2" -o $(is_substring $sum "$*") == 1 ]; then
    echo $sum;
  fi;
}

assert() {
  while [ "$1" ]; do
    $1;
    test $? != 0 && abort 'assert failed('"$1"')';
    shift;
  done;
}

split_string() {
    local split delim
    local IFS=$'\n'
    split="$2"
    split=${split//"$1"/$'\n'}
    for delim in $split; do
        echo "$delim"
    done
}

split_cut() {
    local split count delim
    local IFS=$'\n'
    split="$3"
    split=${split//"$1"/$'\n'}
    count=0
    for delim in $split; do
        count=$(($count + 1))
        echo "$delim"
        if [[ "$count" == "$2" ]]; then return; fi;
    done
}

split_extract() {
    local split count delim
    local IFS=$'\n'
    split="$3"
    split=${split//"$1"/$'\n'}
    count=0
    for delim in $split; do
        count=$(($count + 1))
        if [[ "$count" == "$2" ]]; then echo "$delim" && return; fi;
    done
}

checkvar() {
    while [ "$1" ]; do
      eval "[ -n \"\${${1}}\" ] && echo \"\${${1}}\""
      shift;
	done
}

filtervar() {
    local splittt13yu filter717w8jw array61716opr
    array61716opr=( "$@" )
    filter717w8jw=${array61716opr[${#array61716opr[@]} - 1]}
    unset "array61716opr[${#array61716opr[@]}-1]"
    for splittt13yu in "${array61716opr[@]}"; do
		eval splittt13yu="\${${splittt13yu}}" 
        if [ -n "$splittt13yu" ]; then
           if is_substring "$filter717w8jw" "$splittt13yu"; then echo "$splittt13yu"; fi;
    	fi
    done
}

import_bin() {
    cp -pf "$1" "$l"
    if ! chmod +x "$l/$(basename "$1")"; then echo2 " Cant import: $1" && return 1; fi
}

import_bin_addon() {
   import_bin "$addons/$1"
}

import_bin_zip() {
   local result
   package_extract_file "$1" "$TMP/$(basename "$1")" || return 1
   import_bin "$TMP/$(basename "$1")"
   result=$?
   rm -f "$TMP/$(basename "$1")"
   return $result
}

setdefault() {
    [ -z "$1" ] && return 1
    read -r -d '' "$1" <<< "$2"
    if defined "$1"; then
       if [ "$1" == "permissions" ]; then
          export di_uid=$(echo "$permissions" | cut -d: -f1 | tr -d " ")
          export di_gid=$(echo "$permissions" | cut -d: -f2 | tr -d " ")
          export di_perm_d=$(echo "$permissions" | cut -d: -f3 | tr -d " ")
          export di_perm_f=$(echo "$permissions" | cut -d: -f4 | tr -d " ")
          if ! is_number "$di_uid"; then export di_uid=0; fi
          if ! is_number "$di_gid"; then export di_gid=0; fi
          if ! is_number "$di_perm_d"; then export di_perm_d=0755; fi
          if ! is_number "$di_perm_f"; then export di_perm_f=0644; fi
       fi
       return 0
    else return 1
    fi
}

can_run() {
   local check try
   [ -z "$2" ] && try="--help" || try="$2"
   if [ -f "$1" ]; then
      check=$("$1" $try 2>&1)
      [[ -x "$1" && "$check" != *"CANNOT LINK"* ]] && return 0 || return 1
   elif [ -f "$(which "$1")" ]; then
      check=$("$(which "$1")" $try 2>&1)
      [[ -x "$(which "$1")" && "$check" != *"CANNOT LINK"* ]] && return 0 || return 1
   else return 1
   fi
}

make_overlay() {
   #make_overlay 1 com.android FOLDER result.apk
   local package content result tmp current random priority TMP2
   priority="$1"
   package="$2"
   content="$3"
   result="$4"
   start_tmp
   tmp="$TMP2/overlay"
   echo ">>Overlay Maker 1.0.0"
   if [[ -z "$package" || -z "$content" || -z "$result" || -z "$priority" || ! -d "$content" ]]; then echo " make_overlay: Invalid line " && return; fi
   echo " L: Detected $package..."
   echo " L: With $content..."
   echo " L: Building $(basename "$result")..."
   random=$(echo "$RANDOM" | tr '[0-9]' '[a-z]')
   apktool -f d "$l/overlay.apk" -o "$tmp" >/dev/null
   if ! exist "$tmp/AndroidManifest.xml"; then echo " CANT setup: make_overlay " && return 1; fi
   ( replace "overlay.template" "com.$random.dynamic.installer" "$tmp/AndroidManifest.xml"
   replace 'android:targetPackage="android"' 'android:targetPackage="'$package'"' "$tmp/AndroidManifest.xml"
   replace 'android:priority="1"' 'android:priority="'$priority'"' "$tmp/AndroidManifest.xml") 2>/dev/null
   delete_recursive "$tmp/res"
   current=${PWD}
   cd "$content"
   cp -prf * "$tmp"
   cd "$current"
   delete "$tmp/res/values/public.xml"
   dynamic_apktool -sign -r "$tmp" -o "$result" >/dev/null
   if exist "$result"; then echo " L: Success $(basename "$result")"; else echo " make_overlay: Cant compile $(basename "$result")" && return 1; fi
   end_tmp
}

make_overlay_addon() {
   make_overlay "$1" "$2" "$addons/$3" "$4"
}

make_overlay_zip() {
    local result
    delete_recursive "$TMP/00patch"
    package_extract_dir "$3" "$TMP/00patch" || return 1
    make_overlay "$1" "$2" "$TMP/00patch" "$4"
    result=$?
    delete_recursive "$TMP/00patch"
    return $result
}

make_zip() {
   local make="$TMP/NewZIP" current=${PWD}
   local restore flag script include include2 type pa pm output i base head
   local base_r="META-INF/com/google/android" base_m="META-INF/com/google/android/magisk"
   local recovery="$make/$base_r"
   local magisk="$make/$base_m"
   [ -z "$addons" ] && local addons="META-INF/addons"
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -script|-s)
       script="$2"
       shift;shift;
       ;;
       -preserve-addons|-pa)
       pa=true
       shift
       ;;
       -preserve-magisk|-pm)
       pm=true
       shift
       ;;
       -head|-h)
       head="$2"
       shift;shift;
       ;;
       -type|-t)
       type="$2"
       shift;shift;
       ;;
       -output|-o)
       output="$2"
       shift;shift;
       ;;
       -include|-i)
       include+=("$2")
       shift;shift;
       ;;
       -magisk-include|-mi)
       include2+=("$2")
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   for base in script type output; do
     if undefined $base; then echo2 "make_zip: You need to set the parameter: -$base" && return 1; fi
   done
   echo2 '>>> Make ZIP 1.0.0'
   echo2 "-- Making space..."
   delete_recursive "$make"
   delete "$output"
   package_extract_dir META-INF/zbin "$make/META-INF/zbin" || return 1
   for base in update-binary updater-script; do
      package_extract_file "$base_r/$base" "$recovery/$base" || return 1
   done
   mkdir -p "$make/$addons"
   mkdir -p "$magisk"
   if defined pa; then package_extract_dir "$addons" "$make/$addons"; fi
   if defined pm; then package_extract_dir "$base_m" "$magisk"; fi
   for i in "${!include[@]}"; do
      if exist "${include[i]}"; then 
        echo2 "Adding: ${include[i]}"
        cp -pfr "${include[i]}" "$make"
      fi
   done
   for i in "${!include2[@]}"; do
      if exist "${include2[i]}"; then 
        echo2 "Adding:magisk: ${include2[i]}"
        cp -pfr "${include2[i]}" "$magisk"
      fi
   done
   echo2 "-- Making scripts..."
   if [[ "$type" == "recovery" ]]; then
      delete "$recovery/updater-script" 
      if ! is_substring "#MAGISK" "$script"; then echo -e "#MAGISK\n" > "$recovery/updater-script"; fi
      echo "$script" >> "$recovery/updater-script" 
   elif [[ "$type" == "magisk" ]]; then
      [ -z "$head" ] && head=$(string complete_extract 'Dynamic Installer Configs' '#-----------------------' -f "$recovery/updater-script")
      if [ -n "$head" ] && is_substring setdefault "$head"; then
         delete "$recovery/updater-script"
         is_substring "#MAGISK" "$head" || echo -e "#MAGISK\n" > "$recovery/updater-script"
         echo "$head" >> "$recovery/updater-script"
      else echo2 "make_zip: Cant get: Dynamic Installer Configs: updater-script" && return 1
      fi
      echo "$script" > "$magisk/customize.sh" 
   else
      echo2 "FATAL ERROR: Invalid type" && return 1
   fi
   echo2 "-- Making ZIP..."
   create_dir "$(dirname "$output")" || return 1
   ( cd "$make" && zip -r "$output" * ) > /dev/null
   cd "$current" || cd /
   delete_recursive "$make"
   if is_valid "$output"; then echo2 "-- Done" && return 0; else echo2 "-- ERROR" && return 1; fi
}

dynamic_install_apk() {
   local userlist package f try inst_dir package2 
   local temp_dir temp_dest temp_pack check_split check_package restore flag out nr na
   local ro ra include i file check_overlay
   if ! aapt version >/dev/null; then echo " CANT LOAD AAPT: U cant use dynamic_install_apk " && return 1; fi
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -o|-output)
       out="$2"
       shift;shift;
       ;;
       -nr|-no-replace)
       nr=true
       shift
       ;;
       -na|-no-add)
       na=true
       shift
       ;;
       -ro|-remove-oat)
       ro=true
       shift
       ;;
       -i|-include)
       include+=("$2")
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   delete "$TMP/userlist.txt"
   delete "$TMP/packages.txt"
   if undefined out; then testrw "$2" || return 1; fi
   while read userlist; do
       if undefined userlist; then echo2 "dynamic_install_apk: Cant find any apk in $2 " && break; fi
       check_overlay=
       package=
       package=$(apk_package "$userlist")
       check_overlay=$(aapt dump xmlstrings "$userlist" AndroidManifest.xml | grep -w " overlay")
       if undefined check_overlay; then
          echo "$userlist" >> "$TMP/userlist.txt"
          echo "$(basename "$userlist")=$package" >> "$TMP/packages.txt"
       fi
   done <<< $(find -L "$2" -type f -name "*.apk" 2>/dev/null)
   if ! exist "$TMP/packages.txt" "$TMP/userlist.txt"; then
     #Make false files
     echo > "$TMP/packages.txt" 
     echo > "$TMP/userlist.txt"
   fi
   while read f; do
     if undefined f; then echo2 "dynamic_install_apk: Cant find any apk in $1 " && break; fi
     check_split=
     check_package=
     check_split=$(aapt dump badging "$f" | sed -n "s/.* split='\([^']*\).*$/\1/p")
     if [[ -n "$temp_pack" && -n "$temp_dir" && "$temp_dir" == "$(dirname "$f")" ]]; then
        check_package=$(apk_package "$f")
        if [[ -n "$check_split" && "$check_package" == "$temp_pack" ]]; then
           echo2 "add:split: $(basename "$f") in $temp_dest"
           inject "$f" "$temp_dest" && continue
        fi
     fi
     try=
     inst_dir=$(dirname "$f")
     inst_dir=${inst_dir#$1}
     package=$(apk_package "$f")
     try=$(grep -m1 "$package" "$TMP/packages.txt" | cut -d '=' -f1)
     package2=$(get_file_prop "$TMP/packages.txt" "$try")
     if [[ "$package" == "$package2" ]]; then try=$(grep -m1 "$try" "$TMP/userlist.txt"); fi;
     if [[ -n "$try" && -f "$try" && -z "$check_split" ]]; then
        if [ -n "$nr" ]; then continue; fi
        if [ -n "$out" ]; then try="$out$try"; fi
        temp_pack="$package"
        temp_dir=$(dirname "$f")
        temp_dest=$(dirname "$try")
        if [ -n "$ro" ]; then rm -rf "$temp_dest/oat" 2>/dev/null; fi
        if ! exist "$temp_dest"; then mkdir -p "$temp_dest"; fi
        echo2 "replace: $package in $try"
        cp -pf "$f" "$try"
        if [ -d "$temp_dir/lib" ]; then cp -pfr "$temp_dir/lib" "$temp_dest"; fi
        for i in "${!include[@]}"; do
          if exist "$temp_dir/${include[i]}"; then 
             echo2 "add:extra: ${include[i]}"
             cp -pfr "$temp_dir/${include[i]}" "$temp_dest"
          fi
        done
        set_perm_recursive2 $di_uid $di_gid $di_perm_d $di_perm_f "$temp_dest"
     elif [ -z "$check_split" ]; then
        if [ -n "$na" ]; then continue; fi
        temp_pack="$package"
        temp_dir=$(dirname "$f")
        temp_dest="$2${inst_dir}"
        if [ -n "$out" ]; then temp_dest="$out$temp_dest"; fi
        if [ -n "$ro" ]; then rm -rf "$temp_dest/oat" 2>/dev/null; fi
        echo2 "add: $package in $temp_dest"
        inject "$f" "$temp_dest"
        if [ -d "$temp_dir/lib" ]; then cp -pfr "$temp_dir/lib" "$temp_dest"; fi
        for i in "${!include[@]}"; do
          if exist "$temp_dir/${include[i]}"; then 
             echo2 "add:extra: ${include[i]}"
             cp -pfr "$temp_dir/${include[i]}" "$temp_dest"
          fi
        done
        set_perm_recursive2 $di_uid $di_gid $di_perm_d $di_perm_f "$temp_dest"
     fi
   done <<< $(find -L "$1" -mindepth 1 -type f -name "*.apk")
}

hex_patch() {
    local xxd 
    if xxd --help >/dev/null 2>&1; then
       xxd=xxd
    elif /system/bin/xxd --help >/dev/null 2>&1; then 
       xxd="/system/bin/xxd"
    elif /system/bin/toybox xxd --help >/dev/null 2>&1; then 
       xxd="/system/bin/toybox xxd"
    elif [ ! -e "/data/adb/magisk/magiskboot" ]; then
       echo "CANT LOAD: xxd or magiskboot" && return 1
    fi
    if ! exist "$3"; then echo "CANT FIND: $3" && return 1; fi
    testrw "$(dirname "$3")" || return 1
    if $(( $xxd -p "$3" | tr -d \\n | tr -d " " | sed "s/$1/$2/" | $xxd -r -p > "$3.tmp" ) 2>/dev/null ); then
      if hex_check "$2" "$3.tmp"; then
         move "$3.tmp" "$3"
         true
      elif [ -e "/data/adb/magisk/magiskboot" ]; then
         if $(/data/adb/magisk/magiskboot hexpatch "$3" "$1" "$2"); then
           if hex_check "$2" "$3"; then true; else false; fi
         else
           false
         fi   
      else
         false
      fi
    elif [ -e "/data/adb/magisk/magiskboot" ]; then
        if $(/data/adb/magisk/magiskboot hexpatch "$3" "$1" "$2"); then
           if hex_check "$2" "$3"; then true; else false; fi
        else
           false
        fi 
    else
       false  
    fi
}

hex_search() {
    local xxd restore flag
    local after before
    if xxd --help >/dev/null 2>&1; then
       xxd=xxd
    elif /system/bin/xxd --help >/dev/null 2>&1; then 
       xxd="/system/bin/xxd"
    elif /system/bin/toybox xxd --help >/dev/null 2>&1; then 
       xxd="/system/bin/toybox xxd"
    else
       echo " CANT LOAD XXD bin" && return
    fi
    restore=()
    while [[ $# -gt 0 ]]; do
    flag="$1"
    case $flag in
        -include)
        after=$(echo "$2" | grep -Eio "after:[0-9]+" | cut -f2 -d:)
        before=$(echo "$2" | grep -Eio "before:[0-9]+" | cut -f2 -d:)
        if [[ -z "$after" && -z "$before" ]]; then ui_print "hex_search: invalid line" && return; fi
        shift;shift;
        ;;
        *)   
        restore+=("$1")
        shift
        ;;
    esac
    done
    set -- "${restore[@]}"
    if ! exist "$2"; then echo "CANT FIND: $2" && return 1; fi
    if [ -z "$after" ]; then after=0; fi
    if [ -z "$before" ]; then before=0; fi
    ( $xxd -p "$2" | tr -d \\n | tr -d " " | grep -E -o ".{0,${before}}$1.{0,${after}}" ) 2>/dev/null
}

hex_check() {
    local xxd
    if xxd --help >/dev/null 2>&1; then
       xxd=xxd
    elif /system/bin/xxd --help >/dev/null 2>&1; then 
       xxd="/system/bin/xxd"
    elif /system/bin/toybox xxd --help >/dev/null 2>&1; then 
       xxd="/system/bin/toybox xxd"
    else
       echo " CANT LOAD XXD bin" && return
    fi
    if ! exist "$2"; then echo "CANT FIND: $2" && return 1; fi
    if $(( $xxd -p "$2" | tr -d \\n | tr -d " " | grep -q "$1" ) 2>/dev/null ); then true; else false; fi
}

getvalue() {
   local try
   try=$1
   shift 1
   TEMP=`getopt --long -o "$try:" "$@"`
   eval set -- "$TEMP"
   while true ; do
       case "$1" in
           -$try )
               echo "$2"
               shift 2
           ;;
           *)
               break
           ;;
       esac 
   done
}

get_equal_value() {
   local equal i
   equal="$1"
   shift 1
   for i in "$@"
   do
   case $i in
       $equal=*)
       echo "${i#*=}"
       shift
       ;;
       *)
       ;;
   esac
   done
}

get_custom_value() {
   local value key
   value="$1"
   shift 1
   while [[ $# -gt 0 ]]
   do
   key="$1"
   case $key in
       $value)
       echo "$2"
       shift 2
       ;;
       *)  
       shift
       ;;
   esac
   done
}

start_tmp(){
   local random
   random=$(echo "$RANDOM" | tr '[0-9]' '[a-z]')
   TMP2="$TMP/$random"
   kskwiiuwu+=("$TMP2")
   delete_recursive "$TMP2"
   mkdir -p "$TMP2"
}

end_tmp() {
   local i refresh
   delete_recursive "${kskwiiuwu[${#kskwiiuwu[@]} - 1]}"
   unset "kskwiiuwu[${#kskwiiuwu[@]}-1]"
   for i in "${!kskwiiuwu[@]}"; do
      refresh+=("${kskwiiuwu[i]}")
   done
   kskwiiuwu=("${refresh[@]}")
   TMP2=
}

replace() {
   local restore flag r a file try rpl old new load return=0
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -r|-recursive)
       r=true
       shift
       ;;
       -a|-all-line)
       a=true
       shift
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   file="$3"
   [ -z "$file" ] && return 1
   if ! exist "$file"; then echo "CANT FIND: $file" && return 1; fi
   try="$1"
   rpl="$2"
   if [ -n "$r" ]; then
       testrw "$file" || return 1
       while read huh; do
           undefined huh && return 1
           dir=$(echo "$huh" | cut -f1 -d:)
           testrw "$(dirname "$dir")" || return 1
           if [ -n "$a" ]; then
               load=$(string -f "$dir" replace_line "$try" "$rpl")
           else
               load=$(string -r -f "$dir" replace "$try" "$rpl")
           fi
           result=$?
           if [ $result == 0 ]; then
              if echo "$load" | grep -Fq "$(echo "$rpl" | grep -E "." | head -n1)"; then
                 echo "$load" > "$dir"
                 set_perm2 $di_uid $di_gid $di_perm_f "$dir"
              else echo2 "replace: Cant replace: \"$try\"  \"$rpl\" in $dir" && return=1
              fi
           else echo2 "replace: Nothing to do: $(basename "$dir")"
           fi
       done <<< $(grep -rn "$file" -e "$try")
   else
       testrw "$(dirname "$file")" || return 1
       if [ -n "$a" ]; then
           load=$(string -f "$file" replace_line "$try" "$rpl")
       else
           load=$(string -r -f "$file" replace "$try" "$rpl")
       fi
       result=$?
       if [ $result == 0 ]; then
          if echo "$load" | grep -Fq "$(echo "$rpl" | grep -E "." | head -n1)"; then
             echo "$load" > "$file"
             set_perm2 $di_uid $di_gid $di_perm_f "$file"
          else echo2 "replace: Cant replace: \"$try\"  \"$rpl\" in $file" && return=1
          fi
       else echo2 "replace: Nothing to do: $(basename "$file")"
       fi
   fi
   return $return
}

remove() {
   local restore flag r a file try old new load return=0 cont=0 prev
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -r|-recursive)
       r=true
       shift
       ;;
       -a|-all-line)
       a=true
       shift
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   file="$2"
   [ -z "$file" ] && return 1
   if ! exist "$file"; then echo "CANT FIND: $file" && return 1; fi
   try="$1"
   if [ -n "$r" ]; then
       testrw "$file" || return 1
       while read huh; do
           undefined huh && return 1
           dir=$(echo "$huh" | cut -f1 -d:)
           if [[ -z "$try" && $cont -ge 1 && "$dir" == "$prev" ]] ; then continue; elif [[ "$dir" != "$prev" ]]; then cont=0; fi 
           prev="$dir"
           testrw "$(dirname "$dir")" || return 1
           if [ -n "$a" ]; then
               load=$(string -f "$dir" remove_line "$try")
           else
               load=$(string -r -f "$dir" remove "$try")
           fi
           result=$?
           if [ $result == 0 ]; then
              cont=$(($cont+1))
              echo "$load" > "$dir"
              set_perm2 $di_uid $di_gid $di_perm_f "$dir"
           else echo2 "remove: Nothing to do: $(basename "$dir")"
           fi
       done <<< $(grep -rn "$file" -e "$try")
   else
       testrw "$(dirname "$file")" || return 1
       if [ -n "$a" ]; then
           load=$(string -f "$file" remove_line "$try")
       else
           load=$(string -r -f "$file" remove "$try")
       fi
       result=$?
       if [ $result == 0 ]; then
          echo "$load" > "$file"
          set_perm2 $di_uid $di_gid $di_perm_f "$file"
       else echo2 "remove: Nothing to do: $(basename "$file")"
       fi
   fi
   return $return
}

getsize() {
   stat -c%s "$1"
}

string() {
   #Multi-tool for Strings by @BlassGO
   local restore flag string array result
   local replace rpl rpl2 rpll rpll2 rm ca c i try try2 huh upper lower et et2 ex ex2 exn exn2 escape
   local al al_add bl bl_add get aln aln_add bln bln_add 
   local p1 p2 r p file back return=0
   local ga gb
   local force rmm
   local fix='s/[]\/$*.^[]/\\&/g'
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       replace)
       rpl+=("$2")
       rpl2+=("$3")
       shift;shift;shift;
       ;;
       replace_line)
       rpll+=("$2")
       rpll2+=("$3")
       shift;shift;shift;
       ;;
       remove)
       rm+=("$2")
       shift;shift;
       ;;
       remove_line)
       rmm+=("$2")
       shift;shift;
       ;;
       inside_symbol)
       et="$2"
       et2="$3"
       shift;shift;shift;
       ;;
       inside)
       p1="$2"
       p2="$3"
       shift;shift;shift;
       ;;
       extract)
       ex="$2"
       ex2="$3"
       shift;shift;shift;
       ;;
       complete_extract)
       exn="$2"
       exn2="$3"
       shift;shift;shift;
       ;;
       upper)
       upper=true
       shift
       ;;
       escape)
       escape=true
       shift
       ;;
       lower)
       lower=true
       shift
       ;;
       count)
       c=true
       shift
       ;;
       -al|-after-line)
       al+=("$2")
       al_add+=("$3")
       shift;shift;shift;
       ;;
       -bl|-before-line)
       bl+=("$2")
       bl_add+=("$3")
       shift;shift;shift;
       ;;
       -aln|-after-line-number)
       aln+=("$2")
       aln_add+=("$3")
       shift;shift;shift;
       ;;
       -bln|-before-line-number)
       bln+=("$2")
       bln_add+=("$3")
       shift;shift;shift;
       ;;
       -ga|-get-after)
       ga="$2"
       shift;shift;
       ;;
       -gb|-get-before)
       gb="$2"
       shift;shift;
       ;;
       -r|-recursive)
       r=true
       shift
       ;;
       -p|-pattern)
       p="$2"
       shift;shift;
       ;;
       -f|-file)
       file="$2"
       shift;shift;
       ;;
       force)
       force=true
       shift
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   array=( "$@" )
   if [ -z "$file" ]; then
      string=${array[${#array[@]} - 1]}
      unset "array[${#array[@]}-1]"
      if [ -z "$string" ]; then echo2 "string: Empty string" && return 1; fi
   else
      if exist file "$file"; then string=$(cat "$file"); else echo2 "string: Cant find $file" && return 1; fi
   fi
   back="$string"
   for i in "${!rpl[@]}"; do
      if [ -n "$r" ]; then
         string="${string//"${rpl[i]}"/"${rpl2[i]}"}"
      else
         string="${string/"${rpl[i]}"/"${rpl2[i]}"}"
      fi
   done
   for i in "${!rpll[@]}"; do
      try=$(echo "${rpll[i]}" | sed -e $fix)
      huh=$(echo "${rpll2[i]}" | sed -e $fix)
      if [ $(echo "${rpll2[i]}" | wc -l) -gt 1 ]; then
        huh=$(echo "$huh" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/\$/\\$/g')
      fi
      string=$(echo "$string" | sed -e "/$try/s/.*/$huh/")
   done
   for i in "${!rm[@]}"; do
      if [ -n "$r" ]; then
         string="${string//"${rm[i]}"/}"
      else
         string="${string/"${rm[i]}"/}"
      fi
   done
   for i in "${!rmm[@]}"; do
      try=$(echo "${rmm[i]}" | sed -e $fix)
      if [ -n "$try" ]; then
         string=$(echo "$string" | sed -e "/$try/d")
      else
         string=$(echo "$string" | sed -e "/^[[:space:]]*$/d")
      fi
   done
   if [ -n "$al" ]; then
       for i in "${!al[@]}"; do
          try=$(echo "${al[i]}" | sed -e $fix)
          if [ $(echo "${al_add[i]}" | wc -l) -gt 1 ]; then
             get=$(echo "${al_add[i]}" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/\$/\\$/g')
          else
             get="${al_add[i]}"
          fi
          huh=$(echo "${al_add[i]}" | grep "." | head -n1)
          string=$(echo "$string" | sed -e '/'"$try"'/a\'"$get")
          if ! echo "$string" | grep -Fq "$huh"; then return 1; fi
       done
    fi
    if [ -n "$bl" ]; then
       for i in "${!bl[@]}"; do
          try=$(echo "${bl[i]}" | sed -e $fix)
          if [ $(echo "${bl_add[i]}" | wc -l) -gt 1 ]; then
             get=$(echo "${bl_add[i]}" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/\$/\\$/g')
          else
             get="${bl_add[i]}"
          fi
          huh=$(echo "${bl_add[i]}" | grep "." | head -n1)
          string=$(echo "$string" | sed -e '/'"$try"'/i\'"$get")
          if ! echo "$string" | grep -Fq "$huh"; then return 1; fi
       done
    fi
    if [ -n "$aln" ]; then
       for i in "${!aln[@]}"; do
          if is_number "${aln[i]}"; then try=$((${aln[i]} + 1)); else echo2 "string: ${aln[i]} inst a number" && return 1; fi
          [ $try -gt $(echo "$string" | wc -l) ] && string="${string}${n}"
          if [ $(echo "${aln_add[i]}" | wc -l) -gt 1 ]; then
             get=$(echo "${aln_add[i]}" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/\$/\\$/g')
          else
             get="${aln_add[i]}"
          fi
          huh=$(echo "${aln_add[i]}" | grep "." | head -n1)
          string=$(echo "$string" | sed -e ${try}i'\'"$get")
          if ! echo "$string" | grep -Fq "$huh"; then return 1; fi
       done
    fi
    if [ -n "$bln" ]; then
       for i in "${!bln[@]}"; do
          if is_number "${bln[i]}"; then try=${bln[i]}; else echo2 "string: ${bln[i]} inst a number" && return 1; fi
          if [ $(echo "${bln_add[i]}" | wc -l) -gt 1 ]; then
             get=$(echo "${bln_add[i]}" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/\$/\\$/g')
          else
             get="${bln_add[i]}"
          fi
          huh=$(echo "${bln_add[i]}" | grep "." | head -n1)
          string=$(echo "$string" | sed -e ${try}i'\'"$get")
          if ! echo "$string" | grep -Fq "$huh"; then return 1; fi
       done
    fi
   if [ -n "$et" ]; then
      try=$(echo "$string" | awk -F "$et|$et2" '{print $2}')
      if [ -n "$try" ]; then string="$try"; fi
   fi
   if [ -n "$p1" ]; then
      try=${string#*"$p1"}
      try=${try%"$p2"*}
      if [[ -n "$try" && $(echo "$try" | wc -l) == 1 ]]; then string="$try"; fi
   fi
   if [ -n "$ga" ]; then
      try=${string#*"$ga"}
      if [[ -n "$try" && $(echo "$try" | wc -l) == 1 ]]; then string="$try"; fi
   fi
   if [ -n "$gb" ]; then
      try=${string%"$gb"*}
      if [[ -n "$try" && $(echo "$try" | wc -l) == 1 ]]; then string="$try"; fi
   fi
   if [ -n "$ex" ]; then
      try=
      huh=
      try=$(echo "$ex" | sed -e $fix)
      huh=$(echo "$ex2" | sed -e $fix)
      if [ -z "$r" ]; then
         try=$(echo "$string" | sed -n "/$try/,/$huh/{p; /$huh/q}" | sed -e '1d;$d')
         if defined p && ! echo "$try" | grep -Fq "$p"; then try=; fi
      else
         #Recursive analysis
         if [ -z "$p" ]; then
            try=$(echo "$string" | sed -n "/$try/,/$huh/{ /$try/! { /$huh/! p } }")
         else
            try=$(echo "$string" | sed -n "/$try/,/$huh/p" | sed -e "/$try/s/.*/\n\n/" | sed -e "/$huh/s/.*/\n\n/" | awk -v RS='' "/$p/")
         fi
      fi
      if [ -n "$try" ]; then string="$try"; fi
   fi
   if [ -n "$exn" ]; then
      try=
      huh=
      try=$(echo "$exn" | sed -e $fix)
      huh=$(echo "$exn2" | sed -e $fix)
      if [ -z "$r" ]; then
         try=$(echo "$string" | sed -n "/$try/,/$huh/{p; /$huh/q}")
         if defined p && ! echo "$try" | grep -Fq "$p"; then try=; fi
      else
         #Recursive analysis
         if [ -z "$p" ]; then
            try=$(echo "$string" | sed -n "/$try/,/$huh/p")
         else
            try=$(echo "$string" | sed -n "/$try/,/$huh/p" | sed -e '/'"$try"'/i\'${n}${n} | sed -e '/'"$huh"'/a\'${n}${n} | awk -v RS='' "/$p/")
         fi
      fi
      if ! echo "$try" | head -n1 | grep -Fq "$exn"; then return 1; fi
      if ! echo "$try" | tail -n1 | grep -Fq "$exn2"; then return 1; fi
      if [ -n "$try" ]; then string="$try"; fi
   fi
   if [[ -n "$upper" && -z "$lower" ]]; then
      string=$(echo "$string" | tr '[:lower:]' '[:upper:]')
   elif [[ -z "$upper" && -n "$lower" ]]; then
      string=$(echo "$string" | tr '[:upper:]' '[:lower:]')
   fi
   if [ -n "$escape" ]; then
      string=$(echo "$string" | sed -e $fix)
   fi
   if [[ -z "$force" && -z "$c" && -z "$escape" && -z "$upper" && -z "$lower" && -z "$exn" && "$string" == "$back" ]]; then return 1; fi
   if [ -n "$c" ]; then
      echo "${#string}"
   else
      echo "$string"
   fi
   return $return
}

chooseport_legacy() {
  # Keycheck binary by someone755 @Github, idea for code below by Zappo @xda-developers
  # Calling it first time detects previous input. Calling it second time will do what we want
  [ "$1" ] && local delay=$1 || local delay=3
  local error=false
  while true; do
    sleep 1
    keycheck
    local SEL=$?
    if [ $SEL -eq 42 ]; then
      return 0
    elif [ $SEL -eq 41 ]; then
      return 1
    elif $error; then
      abort "Volume key not detected!"
    else
      error=true
      echo "Volume key not detected. Try again"
    fi
  done
}

chooseport() {
  # Original idea by chainfire and ianmacd @xda-developers
  [ "$1" ] && local delay=$1 || local delay=3
  local error=false
  while true; do
    local count=0
    while true; do
      /system/bin/getevent -lqc 1 2>&1 > $TMP/events &
      sleep 1; count=$((count + 1))
      if (`grep -q 'KEY_VOLUMEUP *DOWN' $TMP/events`); then
        return 0
      elif (`grep -q 'KEY_VOLUMEDOWN *DOWN' $TMP/events`); then
        return 1
      fi
      [ $count -gt 10 ] && break
    done
    if $error; then
      # abort "Volume key not detected!"
      echo "Volume key not detected. Trying keycheck method"
      export chooseport=chooseport_legacy VKSEL=chooseport_legacy
      chooseport_legacy $delay
      return $?
    else
      error=true
      echo "Volume key not detected. Try again"
    fi
  done
}

# Keep old variable from previous versions of this
VKSEL=chooseport

find_block() {
   local BLOCK DEV DEVICE DEVNAME PARTNAME UEVENT
   local restore flag express time t=5
   restore=()
   while [[ $# -gt 0 ]]; do
     flag="$1"
     case $flag in
         -e|-express)
         express=true
         shift
         ;;
         -t|-time)
         time="$2"
         shift;shift;
         ;;
         *)   
         restore+=("$1")
         shift
         ;;
     esac
   done
   set -- "${restore[@]}"
   if [ -n "$time" ] && ! is_number "$time"; then echo2 "find_block: Invalid line" && return 1
   elif [ -n "$time" ] && is_number "$time"; then t="$time"
   fi
   for BLOCK in "$@"; do
     DEVICE=`timeout -t $t find /dev/block \( -type b -o -type c -o -type l \) -iname $BLOCK -o -iname $BLOCK$slot | head -n 1` 2>/dev/null
     if [ ! -z $DEVICE ]; then
       readlink -f $DEVICE
       return 0
     fi
   done
   if [ -z "$express" ]; then
      # Fallback by parsing sysfs uevents
      SECONDS=0
      for UEVENT in /sys/dev/block/*/uevent; do
        if [[ -n "$time" && "$time" -le "$SECONDS" ]]; then break; fi
        DEVNAME=`grep_prop DEVNAME $UEVENT`
        PARTNAME=`grep_prop PARTNAME $UEVENT`
        for BLOCK in "$@"; do
          if [[ "$(toupper $BLOCK)" == "$(toupper $PARTNAME)" || "$(toupper $BLOCK$slot)" == "$(toupper $PARTNAME)" ]]; then
            echo /dev/block/$DEVNAME
            return 0
          fi
        done
      done
   fi
   # Look just in /dev in case we're dealing with MTD/NAND without /dev/block devices/links
   for DEV in "$@"; do
     DEVICE=`timeout -t $t find /dev \( -type b -o -type c -o -type l \) -maxdepth 1 -iname $DEV -o -iname $DEV$slot | head -n 1` 2>/dev/null
     if [ ! -z $DEVICE ]; then
       readlink -f $DEVICE
       return 0
     fi
   done
   return 1
}

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

grep_cmdline() {
  local REGEX="s/^$1=//p"
  cat /proc/cmdline | tr '[:space:]' '\n' | sed -n "$REGEX" 2>/dev/null
}

grep_prop() {
  local REGEX="s/^$1=//p"
  shift
  local FILES=$@
  [ -z "$FILES" ] && FILES='/system/build.prop'
  cat $FILES | dos2unix | sed -n "$REGEX" 2>/dev/null | head -n 1
}

try_mount() {
  local try basetry part result
  local restore flag rw ro re name file LOOP express looped premounted
  restore=()
  while [[ $# -gt 0 ]]; do
  flag="$1"
  case $flag in
       -rw|-read-write)
       rw=true
       shift
       ;;
       -ro|-read-only)
       ro=true
       shift
       ;;
       -re|-remount)
       re=true
       shift
       ;;
       -e|-express)
       express=true
       shift
       ;;
       -n|-name)
       name="$2"
       shift;shift;
       ;;
       -f|-file)
       file="$2"
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
  done
  set -- "${restore[@]}"
  #Ensure root space
  mount -o rw,remount -t auto / 2>/dev/null
  if undefined ro rw; then rw=true && ro=true; fi
  for try in "$@"; do
     looped=false
     premounted=false
     part=
     if [ -z "$name" ]; then basetry=$(split_extract / 1 "$try"); else basetry="$name"; fi
     if [[ -z "$file" && -z "$express" ]]; then [ -n "$basetry" ] && part=$(find_block "$basetry")
     elif [[ -z "$file" && -n "$express" ]]; then [ -n "$basetry" ] && part=$(find_block -e "$basetry")
     else
        basetry="No needed"
        if [ ! -e "$file" ]; then echo2 " CANT FIND: $file" && return 1
        elif [[ -b "$file" && -z "$name" ]]; then
           echo2 " Using: $file"
           part="$file"
        elif can_run losetup2; then
           if [ -n "$name" ]; then
              start_loop "$name" "$file" 2>/dev/null
              part="$LOOP"
           else
              part=$(losetup2 -Pf --show "$file" 2>/dev/null)
           fi
           if [ -z "$part" ]; then echo2 " Cant loop: $file" && return 1
           elif losetup2 -a | grep -q "$part"; then
             looped=true
             echo2 " Looping: $part"
             contains_array "$part" "${all_looped_loop[@]}" || all_looped_loop+=("$part")
           else
             echo2 " Cant loop: $file" && return 1
           fi
        else
           echo2 "Unsupported: $(bin_info losetup2)" && return 1
        fi
     fi
     if [[ -n "$part" && -n "$basetry" ]]; then
         [ -n "$rw" ] && blockdev --setrw "$part" >/dev/null 2>&1
         setup_mountpoint "$try"
         if [ -n "$re" ]; then [[ -n "$name" && -d "/$name" ]] && unmount "/$name"; unmount "$try"; fi
         if ! is_mounted "$try" || [ -n "$file" ]; then
            if [ -n "$rw" ]; then
               if (mount -w "$try" && testrw "$try") 2>/dev/null; then
                 echo2 " Mounting:1: $part $try" && result=0
               elif (mount -w "$part" "$try" && testrw "$try") 2>/dev/null; then
                 echo2 " Mounting:2: $part $try" && result=0
               elif (mount -w -t auto "$part" "$try" && testrw "$try") 2>/dev/null; then
                 echo2 " Mounting:3: $part $try" && result=0
               elif (mount -w -t ext4 "$part" "$try" && testrw "$try") 2>/dev/null; then
                 echo2 " Mounting:4: $part $try" && result=0
               elif (mount -o rw,remount -t auto "$try" && testrw "$try") 2>/dev/null; then
                 echo2 " Mounting:5: $part $try" && result=0
               elif (mount -o rw,remount -t auto "$part" "$try" && testrw "$try") 2>/dev/null; then
                 echo2 " Mounting:6: $part $try" && result=0
               else
                 echo2 " CANT MOUNT: $try as RW" && result=1
               fi
            fi
            if [ -n "$ro" ] && ! is_mounted "$try"; then
               if (mount -r "$try") 2>/dev/null; then
                 echo2 " Mounting:ro:1: $part $try" && result=0
               elif (mount -r "$part" "$try") 2>/dev/null; then
                 echo2 " Mounting:ro:2: $part $try" && result=0
               else
                 echo2 " CANT MOUNT: $try as RO" && result=1
               fi
            fi
         else
            premounted=true
            if [ -n "$rw" ] && (mount -o rw,remount -t auto "$try" && testrw "$try") 2>/dev/null; then
                echo2 " Mounting:auto:1: $part $try" && result=0
            elif [ -n "$rw" ] && (mount -o rw,remount -t auto "$part" "$try" && testrw "$try") 2>/dev/null; then
                echo2 " Mounting:auto:2: $part $try" && result=0
            elif [ -n "$ro" ] && (mount -o ro,remount -t auto "$try") 2>/dev/null; then
                echo2 " Mounting:auto:ro:1: $part $try" && result=0
            elif [ -n "$ro" ] && (mount -o ro,remount -t auto "$part" "$try") 2>/dev/null; then
                echo2 " Mounting:auto:ro:2: $part $try" && result=0
            else 
                #echo2 " Already mounted but could not be remounted, resource probably busy or locked: \"$try\""
                result=1
            fi
         fi
     else
        echo2 "Attempted with: $basetry $part"
        if [ -n "$re" ]; then [[ -n "$name" && -d "/$name" ]] && unmount "/$name"; unmount "$try"; fi
        if ! is_mounted "$try"; then
           setup_mountpoint "$try"
           if [ -n "$rw" ] && (mount -w "$try" && testrw "$try") 2>/dev/null; then
              echo2 " Mounting:noblock:rw: $try" && result=0
           elif [ -n "$ro" ] && (mount -r "$try") 2>/dev/null; then
              echo2 " Mounting:noblock:ro: $try" && result=0
           else
              echo2 " CANT MOUNT: $try" && result=1
           fi
        else
            premounted=true
            if [ -n "$rw" ] && (mount -o rw,remount -t auto "$try" && testrw "$try") 2>/dev/null; then
                echo2 " Mounting:auto:noblock:1: $try" && result=0
            elif [ -n "$ro" ] && (mount -o ro,remount -t auto "$try") 2>/dev/null; then
                echo2 " Mounting:auto:noblock:ro:1: $try" && result=0
            else
                #echo2 " Already mounted but could not be remounted, resource probably busy or locked: \"$try\""
                result=1
            fi
        fi
     fi
     if [ $result != 0 ]; then
       if is_mounted "$try"; then
         if ! testrw "$try" 2>/dev/null; then
           if [ -z "$ro" ]; then
              echo2 "LOCKED: \"$try\" does NOT allow a Read/Write mount!"
              echo2 "        You need to unlock it first."
              if ! $premounted; then echo2 "        Now unmounting \"$try\"..."; unmount "$try"; fi
              result=2
           else
              echo2 "There were problems, but \"$try\" is currently mounted as Read/Only" && result=0 && continue
           fi
         fi
       fi
       $looped && losetup -d $part
     elif is_mounted "$try"; then
        if [ -n "$part" ]; then contains_array "$part" "${all_mounted_part[@]}" || all_mounted_part+=("$part"); fi ; if $looped || ! $premounted; then contains_array "$try" "${all_looped[@]}" || all_looped+=("$try"); else contains_array "$try" "${all_umount[@]}" || all_umount+=("$try"); fi
     else result=1
     fi
  done
  return $result
}

run_wait() {
    local pid count time="$1"
    if ! is_number "$time"; then echo "run_wait: Invalid Line" && return 1; fi
    if exist file "$2"; then chmod +x "$2"; fi
    shift >/dev/null 2>&1 
    $@ & pid=$! count=0
    while kill -0 $pid >/dev/null 2>&1 ; do
        read -t 1 >/dev/null 2>&1 
        count=$(( $count + 1 ))
        if [ $count -ge $time ]; then
            kill $pid >/dev/null 2>&1 && break
        fi
    done
    wait $pid
    return $?
}

run_jar() {
    local dalvikvm file main
    #Inspired in the osm0sis method
    [ -z "$dalvik_logging" ] && local dalvik_logging=false
    if /system/bin/dalvikvm -showversion >/dev/null 2>&1; then 
       dalvikvm=/system/bin/dalvikvm
    elif dalvikvm -showversion >/dev/null 2>&1; then
       dalvikvm=dalvikvm
    else
       [ -z "$ANDROID_ART_ROOT" ] && ANDROID_ART_ROOT=$(find /apex -type d -name "com.android.art*" 2>/dev/null | head -n1)
       if [ -n "$ANDROID_ART_ROOT" ]; then
          dalvikvm=$(readlink -f "$(find "$ANDROID_ART_ROOT" \( -type f -o -type l \) -name "dalvikvm")")
          if [ -z "$dalvikvm" ]; then if $is64bit; then dalvikvm=$(find "$ANDROID_ART_ROOT" \( -type f -o -type l \) -name "dalvikvm64"); else dalvikvm=$(find "$ANDROID_ART_ROOT" \( -type f -o -type l \) -name "dalvikvm32"); fi; fi
       fi
       if ! $dalvikvm -showversion >/dev/null 2>&1; then
          echo2 "--------DALVIKVM LOGGING--------"
          if [ -f "$(readlink -f "$dalvikvm")" ]; then
             echo2 "$($dalvikvm -Xuse-stderr-logger -verbose:class,collector,compiler,deopt,gc,heap,interpreter,jdwp,jit,jni,monitor,oat,profiler,signals,simulator,startup,threads,verifier,verifier-debug,image,systrace-locks,plugin,agents,dex -showversion 2>&1)"
          else
             echo2 "Unable to find dalvikvm!"
             [ -d /apex ] && echo2 "$(find /apex -type f -name "dalvikvm*")"
          fi
          echo2 "--------------------------------"
          echo "CANT LOAD DALVIKVM " && return 1
       fi
    fi
    file="$1"
    if [ ! -f "$file" ]; then echo2 "CANT FIND: $file" && return 1; fi
    main=$(unzip -qp "$file" "META-INF/MANIFEST.MF" 2>/dev/null | grep -m1 "^Main-Class:" | cut -f2 -d: | tr -d " " | dos2unix)
    if [ -z "$main" ]; then
       echo "Cant get main: $file " && return 1
    fi
    shift 1;
    if ! $dalvikvm -Djava.io.tmpdir=. -Xnodex2oat -cp "$file" $main "$@" 2>/dev/null; then if $dalvik_logging; then $dalvikvm -Xuse-stderr-logger -Djava.io.tmpdir=. -Xnoimage-dex2oat -cp "$file" $main "$@"; else $dalvikvm -Djava.io.tmpdir=. -Xnoimage-dex2oat -cp "$file" $main "$@"; fi; fi
}

run_jar_addon() {
    local file
    file="$1"
    shift 1
    run_jar "$addons/$file" "$@"
}

run_jar_zip() {
    local file result
    file="$1"
    shift 1
    package_extract_file "$file" "$TMP/$(basename "$file")" || return 1
    run_jar "$TMP/$(basename "$file")" "$@"
    result=$?
    rm -f "$TMP/$(basename "$file")"
    return $result
}

run_jar_class() {
    local dalvikvm file main
    #Inspired in the osm0sis method
    [ -z "$dalvik_logging" ] && local dalvik_logging=false
    if /system/bin/dalvikvm -showversion >/dev/null 2>&1; then 
       dalvikvm=/system/bin/dalvikvm
    elif dalvikvm -showversion >/dev/null 2>&1; then
       dalvikvm=dalvikvm
    else
       [ -z "$ANDROID_ART_ROOT" ] && ANDROID_ART_ROOT=$(find /apex -type d -name "com.android.art*" 2>/dev/null | head -n1)
       if [ -n "$ANDROID_ART_ROOT" ]; then
          dalvikvm=$(readlink -f "$(find "$ANDROID_ART_ROOT" \( -type f -o -type l \) -name "dalvikvm")")
          if [ -z "$dalvikvm" ]; then if $is64bit; then dalvikvm=$(find "$ANDROID_ART_ROOT" \( -type f -o -type l \) -name "dalvikvm64"); else dalvikvm=$(find "$ANDROID_ART_ROOT" \( -type f -o -type l \) -name "dalvikvm32"); fi; fi
       fi
       if ! $dalvikvm -showversion >/dev/null 2>&1; then
          echo2 "--------DALVIKVM LOGGING--------"
          if [ -f "$(readlink -f "$dalvikvm")" ]; then
             echo2 "$($dalvikvm -Xuse-stderr-logger -verbose:class,collector,compiler,deopt,gc,heap,interpreter,jdwp,jit,jni,monitor,oat,profiler,signals,simulator,startup,threads,verifier,verifier-debug,image,systrace-locks,plugin,agents,dex -showversion 2>&1)"
          else
             echo2 "Unable to find dalvikvm!"
             [ -d /apex ] && echo2 "$(find /apex -type f -name "dalvikvm*")"
          fi
          echo2 "--------------------------------"
          echo "CANT LOAD DALVIKVM " && return 1
       fi
    fi
    file="$1"
    main="$2"
    if [ ! -f "$file" ]; then echo2 "CANT FIND: $file" && return 1; fi
    if [ -z "$main" ]; then
       echo "Class undefined: $file " && return 1
    fi
    shift 2;
    if ! $dalvikvm -Djava.io.tmpdir=. -Xnodex2oat -cp "$file" $main "$@" 2>/dev/null; then if $dalvik_logging; then $dalvikvm -Xuse-stderr-logger -Djava.io.tmpdir=. -Xnoimage-dex2oat -cp "$file" $main "$@"; else $dalvikvm -Djava.io.tmpdir=. -Xnoimage-dex2oat -cp "$file" $main "$@"; fi; fi
}

run_jar_class_addon() {
    local file
    file="$1"
    shift 1
    run_jar_class "$addons/$file" "$@"
}

run_jar_class_zip() {
    local file result
    file="$1"
    shift 1
    package_extract_file "$file" "$TMP/$(basename "$file")" || return 1
    run_jar_class "$TMP/$(basename "$file")" "$@"
    result=$?
    rm -f "$TMP/$(basename "$file")"
    return $result
}

apktool() {
   if [ ! -e /system/framework/framework-res.apk ]; then auto_mount_partitions; fi
   [ ! -f "$TMP/1.apk" ] && cp -f /system/framework/framework-res.apk "$TMP/1.apk"
   if [ ! -e "$l/apktool.jar" ]; then ui_print " Cant find apktool.jar " && return 1; fi
   run_jar "$l/apktool.jar" --aapt "$l/aapt" -p "$TMP" "$@"
}

sign() {
   if [ ! -e "$l/zipsigner.jar" ]; then ui_print " Cant find zipsigner.jar" && return 1; fi
   run_jar "$l/zipsigner.jar" "$@"
}

decode_xml() {
   local result="$TMP/$RANDOM.xml" return=0
   if [ ! -e "$l/axml.jar" ]; then ui_print " Cant find axml.jar" && return 1; fi
   if ! is_substring "ArrayIndexOutOfBoundsException" "$(run_jar "$l/axml.jar" -d "$1" "$result")" && is_valid "$result"; then
      if [ -n "$2" ]; then copy "$result" "$2" || return=1
      else copy "$result" "$1" || return=1
      fi
   else return=1
   fi
   delete "$result"
   return $return
}

encode_xml() {
   local result="$TMP/$RANDOM.xml" return=0
   if [ ! -e "$l/axml.jar" ]; then ui_print " Cant find axml.jar" && return 1; fi
   if ! is_substring "ArrayIndexOutOfBoundsException" "$(run_jar "$l/axml.jar" -e "$1" "$result")" && is_valid "$result"; then
      if [ -n "$2" ]; then copy "$result" "$2" || return=1
      else copy "$result" "$1" || return=1
      fi
   else return=1
   fi
   delete "$result"
   return $return
}

xml_kit() {
   #XML Tool kit for Dynamic Installer by @BlassGO
   local restore flag
   local xml open close cv code try back i
   local custom1 eq1 eq2 in1 in2
   local native native2 revert result result2 backdir
   local value_1 value_2 content_1 content_2 tag_1 tag_2 in remove print only add al1 al2 bl1 bl2 nas inside
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -open|-o)
       open+=("$2")
       close+=("$3")
       if [[ -z "$2" || -z "$3" ]]; then echo2 "xml_kit: Invalid line" && return 1; fi
       shift;shift;shift;
       ;;
       -change-value)
       cv="$2"
       value_1="$2"
       value_2="$3"
       if [[ -z "$2" || -z "$3" ]]; then echo2 "xml_kit: Invalid line" && return 1; fi
       shift;shift;shift;
       ;;
       -change-content)
       cv="$2"
       content_1="$2"
       content_2="$3"
       if [[ -z "$2" || -z "$3" ]]; then echo2 "xml_kit: Invalid line" && return 1; fi
       shift;shift;shift;
       ;;
       -change-tag)
       cv="$2"
       tag_1="$2"
       tag_2="$3"
       if [[ -z "$2" || -z "$3" ]]; then echo2 "xml_kit: Invalid line" && return 1; fi
       shift;shift;shift;
       ;;
       -add)
       add="$2"
       if [ -z "$2" ]; then echo2 "xml_kit: Invalid line" && return 1; fi
       shift;shift;
       ;;
       -add-inside)
       inside="$2"
       if [ -z "$2" ]; then echo2 "xml_kit: Invalid line" && return 1; fi
       shift;shift;
       ;;
       -after-line|-al)
       al1="$2"
       al2="$3"
       if [[ -z "$2" || -z "$3" ]]; then echo2 "xml_kit: Invalid line" && return 1; fi
       shift;shift;shift;
       ;;
       -before-line|-bl)
       bl1="$2"
       bl2="$3"
       if [[ -z "$2" || -z "$3" ]]; then echo2 "xml_kit: Invalid line" && return 1; fi
       shift;shift;shift;
       ;;
       -remove|-rm)
       remove=true
       shift
       ;;
       -print)
       print=true
       shift
       ;;
       -only-result|-or)
       only=true
       shift
       ;;
       -no-auto-spaces|-nas)
       nas=true
       shift
       ;;
       -in)
       in="$2"
       if [ -z "$2" ]; then echo2 "xml_kit: Invalid line" && return 1; fi
       shift;shift;
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   array=( "$@" )
   xml=${array[${#array[@]} - 1]}
   if exist "$xml"; then backdir="$xml" && xml=$(cat "$xml") && back="$xml"; else echo2 "xml_kit: Invalid line" && return 1; fi
   unset "array[${#array[@]}-1]"
   [ -n "$in" ] && cv="$in"
   for i in "${!open[@]}"; do
      if [ -z "$xml" ]; then echo2 "xml_kit: Some error in the XML logic" && return 1; fi
      if is_substring '/>' "${close[i]}" || is_substring '</' "${close[i]}"; then custom1=true; fi
      eq1=$(echo "$xml" | grep -Fc "${open[i]}")
      eq2=$(echo "$xml" | grep -Fc "${close[i]}")
      #in1=$(echo "$xml" | grep -Fn "${open[i]}" | cut -d: -f1 | head -n1)
      #in2=$(echo "$xml" | grep -Fn "${close[i]}" | cut -d: -f1 | tail -n1)
      if [[ -z "$custom1" && $eq1 != $eq2 ]]; then
         echo2 "xml_kit: Invalid XML pattern: ${open[i]} ${close[i]}" && return 1
      elif [ $eq1 == 0 ]; then
         echo2 "xml_kit: No results for: \"${open[i]}\" \"${close[i]}\" " && return 1
      fi
      if [ -z "$cv" ] && ! is_substring "${close[i]}" "$(echo "$xml" 2>/dev/null | grep -Fm1 "${open[i]}")"; then
         xml=$(string -r complete_extract "${open[i]}" "${close[i]}" "$xml")
      elif is_substring "${close[i]}" "$(echo "$xml" 2>/dev/null | grep -Fm1 "${open[i]}")"; then
         xml=$(echo "$xml" | grep -E ".*${open[i]}.*${close[i]}.*")
      elif [ -n "$cv" ]; then
         xml=$(string -p "$cv" -r complete_extract "${open[i]}" "${close[i]}" "$xml")
      else
         echo2 "xml_kit: Unsupported format" && return 1
      fi
      if ! is_substring "${open[i]}" "$(echo "$xml" 2>/dev/null | head -n1)" || ! is_substring "${close[i]}" "$(echo "$xml" 2>/dev/null | tail -n1)"; then echo2 "xml_kit: Cant get valid result: \"${open[i]}\" \"${close[i]}\" " && return 1; fi
      revert+=("$xml")
   done
   if [ -n "$add" ]; then
      native=$(echo "$xml" | tail -n1)
      if [ -z "$nas" ]; then
         native2=$(echo "$native" | awk '{print $1;}')
         native2=$(string -get-before "$native2" "$native")
         add=$(echo "$add" | sed -e "s/^/$native2/")
      fi
      result=$(string -after-line "$native" "$add" "$xml")
      if [ -z "$result" ]; then echo2 "xml_kit: Cant add content in $native" && return 1; fi
      xml="$result"
   fi
   if [ -n "$inside" ]; then
      native=$(echo "$xml" | grep -E "." | tail -n2 | head -n1)
      if [ -z "$nas" ]; then
         native2=$(echo "$native" | awk '{print $1;}')
         native2=$(string -get-before "$native2" "$native")
         inside=$(echo "$inside" | sed -e "s/^/$native2/")
      fi
      native=$(echo "$xml" | grep -E "." | tail -n1)
      result=$(string -before-line "$native" "$inside" "$xml")
      if [ -z "$result" ]; then echo2 "xml_kit: Cant add content in $native" && return 1; fi
      xml="$result"
   fi
   if [ -n "$al1" ]; then
      native=$(echo "$xml" | grep -Fm1 "$al1")
      if [ -z "$nas" ]; then
         native2=$(echo "$native" | awk '{print $1;}')
         native2=$(string -get-before "$native2" "$native")
         al2=$(echo "$al2" | sed -e "s/^/$native2/")
      fi
      result=$(string -after-line "$native" "$al2" "$xml")
      if [ -z "$result" ]; then echo2 "xml_kit: Cant add content in $native" && return 1; fi
      xml="$result"
   fi
   if [ -n "$bl1" ]; then
      native=$(echo "$xml" | grep -Fm1 "$bl1")
      if [ -z "$nas" ]; then
         native2=$(echo "$native" | awk '{print $1;}')
         native2=$(string -get-before "$native2" "$native")
         bl2=$(echo "$bl2" | sed -e "s/^/$native2/")
      fi
      result=$(string -before-line "$native" "$bl2" "$xml")
      if [ -z "$result" ]; then echo2 "xml_kit: Cant add content in $native" && return 1; fi
      xml="$result"
   fi
   if [ -n "$value_1" ]; then
      native=$(echo "$xml" | grep -E -om1 "$value_1.*")
      native2=$(string inside_symbol '="' '"' "$native")
      if [[ -z "$native2" || $(echo "$native2" | wc -l) -gt 2 ]]; then echo2 "xml_kit: Cant get value of: $value_1" && return 1; fi
      result=$(string force replace "$native2" "$value_2" "$native")
      result=$(string replace "$native" "$result" "$xml")
      if [ -z "$result" ]; then echo2 "xml_kit: No changes: Probably the new value for \"$value_1\" already exist" && return 1; fi
      xml="$result"
   fi
   if [ -n "$content_1" ]; then
      native=$(echo "$xml" | grep -Fm1 "$content_1")
      if [[ -z "$native" || $(echo "$native" | wc -l) -gt 2 ]]; then echo2 "xml_kit: Cant get: $content_1" && return 1; fi
      result=$(string force replace "$content_1" "$content_2" "$native")
      result=$(string replace "$native" "$result" "$xml")
      if [ -z "$result" ]; then echo2 "xml_kit: No changes: Probably the changes already exist" && return 1; fi
      xml="$result"
   fi
   if [ -n "$tag_1" ]; then
      native=$(echo "$xml" | grep -Fm1 "${tag_1}>")
      native2=$(string inside "${tag_1}>" "</${tag_1}" "$native")
      if [[ -z "$native2" || $(echo "$native2" | wc -l) -gt 2 ]]; then echo2 "xml_kit: Cant get value of tag: $tag_1" && return 1; fi
      result=$(string force replace "$native2" "$tag_2" "$native")
      result=$(string replace "$native" "$result" "$xml")
      if [ -z "$result" ]; then echo2 "xml_kit: No changes: Probably the $tag_1 already has the new value" && return 1; fi
      xml="$result"
   fi
   if [ -n "$remove" ]; then
      result=" "
   fi
   if [ -n "$result" ]; then
      for i in $(echo ${!revert[@]} | rev); do
         result2=$(string replace "${revert[i]}" "$result" "${revert[i - 1]}")
         [ -n "$result2" ] && result="$result2" || break
      done
      if ! is_text "$result"; then echo2 "xml_kit: Some error in XML remaking" && return 1; fi
      if [ -z "$only" ]; then result=$(string replace "${revert[0]}" "$result" "$back"); fi
      if ! is_text "$result"; then echo2 "xml_kit: Some error in XML remaking" && return 1; fi
   fi
   if [ -n "$result" ]; then
      if [ -z "$print" ]; then
         testrw "$(dirname "$backdir")" || return 1
         echo "$result" > "$backdir"
         if is_valid "$backdir"; then set_perm2 $di_uid $di_gid $di_perm_f "$backdir"
         else echo2 "xml_kit: Cant write $backdir" && return 1
         fi
      else echo "$result"
      fi
   elif [[ -n "$xml" && "$xml" != "$back" ]]; then echo "$xml"
   else echo2 "xml_kit: Unknown problem" && return 1
   fi
}

dynamic_apktool() {
   #Dynamic Apktool for Dynamic Installer by BlassGO
   local file fullname filename filedir outdir folder outfile sign zipa add alladd move fw check out
   local flag current=${PWD} restore ext ps manifest min max try bk count result command noapi extra noextra sm
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -d|-decompile)
       file="$2"
       fullname=$(basename "$2")
       filename=${fullname%.*}
       filedir=$(dirname "$2")
       outdir="$filedir/$filename"
       shift;shift;
       ;;
       -r|-recompile)
       folder=${2%%/}
       outfile=$(cat "$folder/apktool.yml" | grep -m1 "FileName:" | tr -d " " | cut -f2 -d:)
       ext=${outfile##*.}
       shift;shift;
       ;;
       -s|-sign)
       sign=true
       shift
       ;;
       -z|-zipalign)
       zipa=true
       shift
       ;;
       -a|-add)
       add=true
       alladd+=$(echo "$2:")
       shift;shift;
       ;;
       -o|-output)
       move=${2%%/}
       shift;shift;
       ;;
       -c|-command)
       command+="$2"
       shift;shift;
       ;;
       -ps|-preserve-signature)
       ps=true
       command+=" --copy-original "
       shift
       ;;
       -f|-framework)
       fw="$2"
       shift;shift;
       ;;
       -im|-include-manifest)
       manifest=true
       shift
       ;;
       -min-sdk)
       min="$2"
       shift;shift;
       ;;
       -max-sdk)
       max="$2"
       shift;shift;
       ;;
       -use-baksmali)
       bk="$2"
       [ -e "$l/baksmali_${bk}" ] && bk="$l/baksmali_${bk}"
       if [ ! -e "$bk" ]; then echo2 "dynamic_apktool: Cant find: $bk" && return 1; fi
       command+=" --no-src "
       shift;shift;
       ;;
       -use-smali)
       sm="$2"
       [ -e "$l/smali_${sm}" ] && sm="$l/smali_${sm}"
       if [ ! -e "$sm" ]; then echo2 "dynamic_apktool: Cant find: $sm" && return 1; fi
       shift;shift;
       ;;
       -no-api)
       noapi=true
       shift
       ;;
       -no-extras)
       noextra=true
       shift
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   if [ ! -e /system/build.prop ]; then auto_mount_partitions; fi
   if [ ! -e "$l/apktool.jar" ]; then ui_print " Cant find apktool.jar " && return 1; fi
   if [ ! -e "$l/zipsigner.jar" ]; then ui_print " Cant find zipsigner.jar" && return 1; fi
   if [ ! -e "$l/axml.jar" ]; then ui_print " Cant find axml.jar" && return 1; fi
   if [[ -z "$noapi" && -n "$API" ]]; then command+=" --api-level $API "; fi
   echo ">>Dynamic Apktool 1.5.0"
   if [[ -n "$file" && -z "$folder" && -z "$zipa" && -z "$sign" ]]; then
      [ -z "$move" ] && move="$outdir"
      if [ -n "$fw" ]; then cp -f "$fw" "$TMP/1.apk"; else cp -f /system/framework/framework-res.apk "$TMP/1.apk"; fi
      if [ -n "$move" ]; then
         rm -rf "$move" 2>/dev/null
         echo " L: Decompiling $fullname... "
         if [ -n "$bk" ]; then echo " L: Using $(basename "$bk")"; fi
         echo " L: Please wait..."
         if ! run_jar "$l/apktool.jar" --aapt "$l/aapt" --no-res -p "$TMP" $command d "$file" -o "$move" >/dev/null 2>&1; then echo " ERROR: Cant decompile $fullname" && return 1; fi
         if [ -n "$bk" ]; then
            count=1
            result=
            while read dex; do
               [ -z "$dex" ] && break
               if package_extract_file "$dex" "$TMP/dex.tmp" "$file"; then
                  if [ -n "$result" ]; then count=$(($count+1)); result="${move}/smali_classes$count"; delete "${move}/classes${count}.dex"; else result="${move}/smali"; delete "${move}/classes.dex"; fi
                  if ! run_jar "$bk" d "$TMP/dex.tmp" -o "$result"; then echo "${n} ERROR: Cant decode: \"$dex\" with \"$bk\"" && return 1; fi
               fi
            done <<< $(find_content -r ".dex$" "$file")
         fi
         if [ -z "$noextra" ]; then
            echo " L: Saving extras..."
            while read extra; do
                [ -z "$extra" ] && break
                if [ ! -e "$move/original/$extra" ] && [ ! -e "$move/unknown/$extra" ]; then
                   package_extract_file "$extra" "$move/unknown/$extra" "$file"
                   if ! grep -q "unknownFiles:" "$move/apktool.yml"; then add_lines_string -after-line "sparseResources:" "unknownFiles:" "$move/apktool.yml"; fi
                   if cat "$move/apktool.yml" | grep "unknownFiles:" | grep -q '{}'; then replace -a "unknownFiles:" "unknownFiles:" "$move/apktool.yml"; fi
                   add_lines_string -after-line "unknownFiles:" "  $extra: '8'" "$move/apktool.yml"
                fi
            done <<< $(find_content -r ".*" "$file" | grep -Ev "^kotlin/.*|res/.*|resources\.arsc|AndroidManifest.xml|.*\.dex$")
         fi
         echo " L: Checking results..."
         if [ ! -d "$move" ]; then echo "ERROR: Decompiling $fullname" && return 1; fi
         if [ -n "$add" ]; then
            local IFS=$'\n'
            for add in $(split_string : "$alladd"); do
              echo " L: Adding $(basename "$add")"
              cp -rf "$add" "$move"
            done
         fi
      else
          echo " ERROR: Invalid dest dir" && return 1
      fi
      echo " L: Success "
   elif [[ -z "$file" && -n "$folder" ]]; then
         if [ -n "$fw" ]; then cp -f "$fw" "$TMP/1.apk"; else cp -f /system/framework/framework-res.apk "$TMP/1.apk"; fi
         rm -f "$folder/try.apk" 2>/dev/null
         rm -rf "$folder/dist" 2>/dev/null
         echo " L: Recompiling $(basename "$folder")... "
         if [ -n "$sm" ]; then echo " L: Using $(basename "$sm")"; fi
         if [[ -n "$min" || -n "$max" ]]; then
            if decode_xml "$folder/AndroidManifest.xml"; then
               if [ -n "$min" ]; then
                  xml_kit -open '<manifest' '</manifest>' -open '<uses-sdk' '>' -change-value android:minSdkVersion $min "$folder/AndroidManifest.xml"
                  min=$(sed -n 's/.*android:minSdkVersion="\([^"]*\).*$/\1/p' "$folder/AndroidManifest.xml")
                  [ -n "$min" ] && echo " L: Min-sdk: $min"
               fi
               if [ -n "$max" ]; then
                  xml_kit -open '<manifest' '</manifest>' -open '<uses-sdk' '>' -change-value android:maxSdkVersion $max "$folder/AndroidManifest.xml"
                  max=$(sed -n 's/.*android:maxSdkVersion="\([^"]*\).*$/\1/p' "$folder/AndroidManifest.xml")
                  [ -n "$max" ] && echo " L: Max-sdk: $max"
               fi
               encode_xml "$folder/AndroidManifest.xml"
            fi
         fi
         if [ -n "$manifest" ]; then
            echo " L: Including AndroidManifest.xml"
            cp -f "$folder/AndroidManifest.xml" "$folder/original/AndroidManifest.xml"
         fi
         if [ -z "$ps" ]; then
            echo " L: Using all changes..."
         else
            echo " L: Saving signature..."
         fi
         echo " L: Please wait..."
         if [ -n "$sm" ]; then
            count=1
            result=
            create_dir "$folder/classtmp" || return 1
            while read dex; do
               [ -z "$dex" ] && break
               if [ -n "$result" ]; then count=$(($count+1)); result="${folder}/classes${count}.dex"; else result="${folder}/classes.dex"; fi
               if ! run_jar "$sm" a "$dex" -o "$result" || ! is_valid "$result"; then echo "${n} ERROR: Cant compile: \"$dex\" with \"$sm\""; find "$folder/classtmp" -mindepth 1 -maxdepth 1 -type d -exec mv -f {} "$folder" \; ; delete_recursive "$folder/classtmp"; return 1; else move "$dex" "$folder/classtmp"; fi
            done <<< $(find "$folder" -mindepth 1 -maxdepth 1 -type d -regex '.*/smali\(_classes[0-9]*\)*$')
         fi
         run_jar "$l/apktool.jar" --aapt "$l/aapt" -p "$TMP" -f $command b "$folder" -o "$folder/try.apk" >/dev/null 2>&1
         if [ -d "$folder/classtmp" ]; then find "$folder/classtmp" -mindepth 1 -maxdepth 1 -type d -exec mv -f {} "$folder" \; ; delete_recursive "$folder/classtmp"; fi
         if [ ! -f "$folder/try.apk" ]; then echo "ERROR: Compiling $outfile" && return 1; fi
         if [ -n "$add" ]; then
            local IFS=$'\n'
            current=${PWD}
            for add in $(split_string : "$alladd"); do
                echo " L: Adding $(basename "$add")"
                cd "$(dirname "$add")"
                zip -ur "$folder/try.apk" "$(basename "$add")" >/dev/null
            done
            cd "$current"
         fi
         if [ -n "$sign" ]; then
            echo " L: Signing $outfile"
            run_jar "$l/zipsigner.jar" "$folder/try.apk" "$folder/try.zp" >/dev/null
            if [ ! -f "$folder/try.zp" ]; then echo "ERROR: sign $outfile" && return 1; fi
            mv -f "$folder/try.zp" "$folder/try.apk"
         fi
         if [[ -n "$zipa" || "$ext" == "apk" || -n "$ps" ]]; then
            echo " L: Zipaligning $(basename "$outfile")"
            zipalign -f -v 4 "$folder/try.apk" "$folder/try.zp" >/dev/null
            if [ ! -f "$folder/try.zp" ]; then echo "ERROR: zipalign $outfile" && return 1; fi
            mv -f "$folder/try.zp" "$folder/try.apk"
         fi
         if [ -n "$move" ]; then
            copy "$folder/try.apk" "$move"
            if is_valid "$move"; then
               rm -f "$folder/try.apk"
               echo " L: Success $(basename "$move")" && return 0
            else
               echo " L: ERROR $(basename "$move")" && return 1
            fi
         else
            mkdir -p "$folder/dist"
            cp -pf "$folder/try.apk" "$folder/dist/$outfile"
            if is_valid "$folder/dist/$outfile"; then
               rm -f "$folder/try.apk"
               echo " L: Success $outfile" && return 0
            else
               echo " L: ERROR $outfile" && return 1
            fi
         fi
   else
      echo "dynamic_apktool: Invalid line" && return 1
   fi
}

find_apk() {
   local userlist package try restore flag re result
   local array path try2 limit count=0 return=1
   local check_split splits overlays check
   if ! aapt version >/dev/null; then echo " CANT LOAD AAPT: U cant use find_apk " && return 1; fi
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -r|-recursive)
       re=true
       shift
       ;;
       -l|-limit)
       limit="$2"
       shift;shift;
       ;;
       -is|-include-splits)
       splits=true
       shift
       ;;
       -io|-include-overlays)
       overlays=true
       shift
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   delete "$TMP/userlist.txt"
   delete "$TMP/packages.txt"
   if defined limit && ! is_number $limit; then echo2 " find_apk: Invalid line" && return 1; fi
   array=( "$@" )
   path=${array[${#array[@]} - 1]}
   unset "array[${#array[@]}-1]"
   find -L "$path" -type f -name "*.apk" | while read userlist; do
       if undefined userlist; then echo "find_apk: Cant find any apk in $path " && return 1; fi
       package=
       package=$(apk_package "$userlist")
       if defined package; then
          echo "$userlist" >> "$TMP/userlist.txt"
          echo "$(basename "$userlist")=$package" >> "$TMP/packages.txt"
       fi
   done
   for try in "${array[@]}"; do
     while read try2; do
        undefined try2 && continue
        package=
        package=$(get_file_prop "$TMP/packages.txt" "$try2")
        check=$(grep -m1 "$try2" "$TMP/userlist.txt")
        check_split=$(aapt dump badging "$check" | sed -n "s/.* split='\([^']*\).*$/\1/p" 2>/dev/null)
        check_overlay=$(aapt dump xmlstrings "$check" AndroidManifest.xml | grep -w " overlay" 2>/dev/null)
        result=$(grep -m1 "$try2" "$TMP/userlist.txt")
        if [ -z "$re" ] && [ -n "$result" ]; then
           if [[ -z "$splits" && -z "$overlays" && -z "$check_split" && -z "$check_overlay" && "$package" == "$try" ]]; then
              echo "$result" && return=0 && count=$(( $count + 1 ))
           elif [[ -n "$splits" && -z "$overlays" && -z "$check_overlay" && "$package" == "$try" ]]; then
              echo "$result" && return=0 && count=$(( $count + 1 ))
           elif [[ -n "$overlays" && -z "$splits" && -z "$check_split" && "$package" == "$try" ]]; then
             echo "$result" && return=0 && count=$(( $count + 1 ))
           elif [[ -n "$splits" && -n "$overlays" && "$package" == "$try" ]]; then
             echo "$result" && return=0 && count=$(( $count + 1 ))
           fi
        elif [ -n "$result" ]; then
           if [[ -z "$splits" && -z "$overlays" && -z "$check_split" && -z "$check_overlay" ]]; then
              echo "$result" && return=0 && count=$(( $count + 1 ))
           elif [[ -n "$splits" && -z "$overlays" && -z "$check_overlay" ]]; then
              echo "$result" && return=0 && count=$(( $count + 1 ))
           elif [[ -n "$overlays" && -z "$splits" && -z "$check_split" ]]; then
             echo "$result" && return=0 && count=$(( $count + 1 ))
           elif [[ -n "$splits" && -n "$overlays" ]]; then
             echo "$result" && return=0 && count=$(( $count + 1 ))
           fi
        fi
        if [[ -n "$limit" && "$limit" == "$count" ]]; then break; fi
     done <<< $(grep "$try" "$TMP/packages.txt" | cut -d '=' -f1)
   done
   return $return
}

apk_package() {
   local package
   if ! aapt version >/dev/null; then echo " CANT LOAD AAPT: U cant use apk_package " && return 1; fi
   package=$((aapt dump badging "$1" | sed -n "s/.*package: name='\([^']*\).*$/\1/p") 2>/dev/null)
   if [ -n "$package" ]; then echo "$package"; else return 1; fi
}

apk_main() {
   local main
   if ! aapt version >/dev/null; then echo " CANT LOAD AAPT: U cant use apk_main " && return 1; fi
   main=$((aapt dump badging "$1" | sed -n "s/.*launchable-activity: name='\([^']*\).*$/\1/p") 2>/dev/null)
   if [ -n "$main" ]; then echo "$main"; else return 1; fi
}

apk_icon() {
   local icon
   if ! aapt version >/dev/null; then echo " CANT LOAD AAPT: U cant use apk_package " && return 1; fi
   icon=$((aapt dump badging "$1" | sed -n "s/.*application:.*icon='\([^']*\).*$/\1/p") 2>/dev/null)
   if [ -n "$icon" ]; then echo "$icon"; else return 1; fi
}

apk_launch() {
   local pkg main
   if [ ! -f "$(which am)" ]; then echo " CANT LOAD AM: U cant use apk_launch, try mounting /system" && return 1; fi
   [ -f "$1" ] && pkg=$(apk_package "$1") || pkg="$1"
   [ -z "$2" ] && main=$(apk_main "$1") || main="$2"
   if [ -z "$pkg" ]; then echo2 "apk_launch: Cant get the package of $1" && return 1; fi
   if [ -z "$main" ]; then echo2 "apk_launch: Cant get the main launchable activity of $1" && return 1; fi
   am start -n "$pkg/$main"
}

get_density_key() {
   if is_greater_equal "$1" 120 && is_less "$1" 160; then
      echo ldpi
   #elif is_greater_equal "$1" 213 && is_less "$1" 240; then
   #   echo tvdpi
   elif is_greater_equal "$1" 160 && is_less "$1" 240; then
      echo mdpi
   elif is_greater_equal "$1" 240 && is_less "$1" 320; then
      echo hdpi
   elif is_greater_equal "$1" 320 && is_less "$1" 480; then
      echo xhdpi
   elif is_greater_equal "$1" 480 && is_less "$1" 640; then
      echo xxhdpi
   elif is_greater_equal "$1" 640; then
      echo xxxhdpi
   elif [[ "$1" == 0 ]]; then
      echo nodpi
   fi
}

get_density_int() {
   if [[ "$1" == "ldpi" ]]; then
      echo 120
   elif [[ "$1" == "mdpi" ]]; then
      echo 160
   elif [[ "$1" == "tvdpi" ]]; then
      echo 213
   elif [[ "$1" == "hdpi" ]]; then
      echo 240
   elif [[ "$1" == "xhdpi" ]]; then
      echo 320
   elif [[ "$1" == "xxhdpi" ]]; then
      echo 480
   elif [[ "$1" == "xxxhdpi" ]]; then
      echo 640
   elif [[ "$1" == "nodpi" ]]; then
      #Any dpi
      echo 0
   elif is_number $1; then
      echo $1
   fi
}

apk_install() {
   local split apk count success=true try
   local size total id TMP2 lang density support dp dp_support install archi_support archi get new
   #if ! pm help >/dev/null; then echo " CANT LOAD PM: U cant use apk_install " && return 1; fi
   TMP2="/data/local/tmp/apk_install"
   create_dir "$TMP2" || return 1
   while [ "$1" ]; do
     total=0
     count=0
     try="$1"
     apk=$(echo "$1" | cut -d: -f1)
     if is_zip "$apk" && unzip -l "$apk" | grep -q "META-INF/APKMIRRO.SF" && unzip -l "$apk" | grep -q " info.json"; then
        new=
        archi=
        dp=
        lang=$(getprop persist.sys.locale | cut -d- -f1)
        density=$(cmd window density | cut -d: -f2 | tr -d " ")
        if [ -z "$lang" ]; then echo2 "apk_install: Unable to get current device language" && success=false && break; fi
        if ! is_number "$density"; then echo2 "apk_install: Unable to get device density" && success=false && break; fi
        [[ "$lang" != "en" ]] && lang+=" en"
        dp_support=$(string extract '"dpis": [' ']' "$(package_extract_file info.json "" "$apk")" | tr -d ' ,"' | tr '\n' ' ')
        if [[ "$(echo "$dp_support" | tr -d " ")" != "nodpi" ]]; then
           for support in $dp_support; do
              if [[ "$(get_density_int "$support")" == "$density" ]]; then dp=$density && break; fi
           done
           if [ -z "$dp" ]; then
              density=$(printf "%.0f" "$(calc "$density/160")")
              density=$(($density*160))
              for support in $dp_support; do
                 if [[ "$(get_density_int "$support")" == "$density" ]]; then dp=$density && break; fi
              done
           fi
           [ -z "$dp" ] && dp=$(echo "$dp_support" | tr ' ' '\n' | grep -E "." | tail -n1 | tr -d " ")
           if ! is_number "$dp"; then echo2 "apk_install: Could not get a valid dpi: $apk " && success=false && break; fi
           dp=$(get_density_key "$dp")
        fi
        archi_support=$(string extract '"arches": [' ']' "$(package_extract_file info.json "" "$apk")" | tr -d ' ,"' | tr '\n' ' ')
        for support in $archi_support; do
           if [[ "$support" == "$ABILONG" ]]; then archi=$support && break; fi
        done
        if [ -z "$archi" ]; then
           for support in $archi_support; do
              if is_substring "$arch" "$support"; then archi=$support && break; fi
           done
        fi
        if [ -z "$archi" ]; then
           for support in $archi_support; do
              if is_substring "$arch32" "$support"; then archi=$support && break; fi
           done
        fi
        if [ -z "$archi" ]; then echo2 "apk_install: Unsupported architecture: $apk " && success=false && break; fi
        archi=${archi/"-"/"_"}
        for install in base $lang $dp $archi; do
           get=$(unzip -l "$apk" | tail -n+4 | cut -c4- | grep -m1 -F ".${install}.")
           [ -z "$get" ] && get=$(unzip -l "$apk" | tail -n+4 | cut -c4- | grep -m1 -F "${install}.")
           [ -z "$get" ] && continue
           [ -n "$new" ] && new+=":"
           get=${get##* }
           unzip -qo "$apk" "$get" -d "$TMP2"
           if [ -f "$TMP2/$get" ]; then
              new+="$TMP2/$get"
           else echo2 "apk_install: Cant get \"$get\"" && success=false && break
           fi
        done
        if $success; then
           try=${try/"${apk}"/"${new}"}
           while read split; do
              [ -z "$split" ] && break
              if [ ! -f "$split" ]; then
                 package_extract_file "$split" "$TMP2/$(basename "$split")" "$apk"
                 if is_valid "$TMP2/$(basename "$split")"; then
                    try=${try/"${split}"/"$TMP2/$(basename "$split")"}
                 else echo2 "Cant find: $split" && success=false && break
                 fi
              fi
           done <<< $(split_string : "$try") 
        fi
     fi
     $success || break
     if is_substring : "$try"; then
        while read split; do
           [ -z "$split" ] && break
           if [ ! -f "$split" ]; then echo2 "Cant find: $split" && success=false && break; fi
           size=$(getsize "$split")
           total=$(($total+$size))
        done <<< $(split_string : "$try")
        $success || break
        id=$(pm install-create -S "$total" | grep -oE "[0-9]*")
        if is_number "$id"; then
           echo2 "Created session: $id: $(basename "$apk")"
           while read split; do
              [ -z "$split" ] && break
              size=$(getsize "$split")
              copy "$split" "$TMP2"
              if pm install-write -S $size $id $count "$TMP2/$(basename "$split")" >/dev/null; then
                 echo2 "Added: $(basename "$split")"
              else
                 echo2 "apk_install: Could not add: $split to $id installation" && success=false && break 
              fi
              count=$(($count+1))
           done <<< $(split_string : "$try")
           if $success; then
              if pm install-commit $id >/dev/null; then
                 echo2 "Installed successfully: $id session"
              else
                 echo2 "apk_install: CANT INSTALL SESSION: $id" && success=false
              fi
           fi
        else
           echo2 "apk_install: Cannot create a session: $try" && success=false
        fi
     else
        if [ ! -f "$try" ]; then echo2 "Cant find: $try" && success=false; fi
        $success || break
        copy "$try" "$TMP2"
        if pm install -r "$TMP2/$(basename "$try")" >/dev/null; then
           echo2 "Installed successfully: $(basename "$try")"
        else
           echo2 "apk_install: CANT INSTALL: $try" && success=false
        fi
     fi
     $success || break
     shift;
     [ "$1" ] && echo2 " "
   done;
   delete_recursive "$TMP2"
   $success && return 0 || return 1
}

apk_install_recursive() {
    local dir any apk ext list
    while [ "$1" ]; do
       dir="$1"
       while read any; do
          exist "$any" || break
          ext=$(basename "$any")
          ext=${ext##*.}
          if [ -f "$any" ] && [[ "$ext" == "apk" || "$ext" == "apkm" ]]; then
             apk_install "$any" || return 1
             echo2 " "
          elif [ -d "$any" ]; then
             echo2 "From: $any"
             list=
             list=$(find "$any" -type f -name "*.apkm" | head -n1)
             while read apk; do
                exist "$apk" || break
                [ -n "$list" ] && list+=":"
                list+="$apk"
             done <<< $(find "$any" -type f -name "*.apk")
             if [ -n "$list" ]; then apk_install "$list" || return 1; echo2 " "
             else echo2 "No .apk/.apkm found on: $any"
             fi
          fi
       done <<< $(find "$dir" -mindepth 1 -maxdepth 1)
       shift;
    done
    return 0
}

apk_pkg() {
   apk_package "$@"
}

patch_apk() {
    local current=${PWD} stock new
    if ! exist "$1" "$2"; then echo2 "patch_apk: Invalid line" && return 1; fi
    testrw "$(dirname "$2")" || return 1
    cp -pf "$2" "$TMP"
    savestate stock "$TMP/$(basename "$2")"
    cd "$1" && zip -r "$TMP/$(basename "$2")" * >/dev/null
    cd "$current"
    savestate new "$TMP/$(basename "$2")"
    if [[ "$stock" != "$new" ]]; then echo "Patched: $2"; else echo "No changes: $2" && return 1; fi
    if [[ "$3" == "zipalign" ]]; then
       zipalign -f -v 4 "$TMP/$(basename "$2")" "$TMP/$(basename "$2").zp" >/dev/null
       if [ -e "$TMP/$(basename "$2").zp" ]; then
          echo2 "Zipaligned: $2"
          mv -f "$TMP/$(basename "$2").zp" "$TMP/$(basename "$2")"
       else
          echo2 "ERROR: zipalign $2" && return 1
       fi
    fi
    if [[ "$3" == "sign" ]]; then
       if [ ! -e $l/zipsigner.jar ]; then ui_print " Cant find zipsigner.jar " && return 1; fi
       run_jar "$l/zipsigner.jar" "$TMP/$(basename "$2")" "$TMP/$(basename "$2").zp" >/dev/null
       if [ -e "$TMP/$(basename "$2").zp" ]; then
          echo2 "Signed: $2"
          mv -f "$TMP/$(basename "$2").zp" "$TMP/$(basename "$2")"
       else
          echo2 "ERROR: sign $2" && return 1
       fi
    fi
    if is_valid "$TMP/$(basename "$2")"; then inject "$TMP/$(basename "$2")" "$(dirname "$2")"; else echo2 "FATAL ERROR: $TMP/$(basename "$2")" && return 1; fi
}

patch_apk_addon() {
   patch_apk "$addons/$1" "$2" "$3"
}

patch_apk_zip() {
    local result
    rm -rf "$TMP/patch_apk"
    package_extract_dir "$1" "$TMP/patch_apk" || return 1
    patch_apk "$TMP/patch_apk" "$2" "$3"
    result=$?
    rm -rf "$TMP/patch_apk"
    return $result
}

progress() {
    # command & progress
    local pid=$! huh delay=0.1 limit=10 count=0 anim='|/-\' finish=10 mark="=========================================="
    while kill -0 "$pid" 2> /dev/null; do
        sleep $delay
        local temp=${anim#?}
        local pd=$(( $count * 73 / $finish ))
        if [[ "$count" == "2" ]]; then sleep 0.3 && count=$(( $count + 1 )); fi
        if [[ "$count" == "3" ]]; then sleep 0.3 && count=$(( $count + 1 )); fi
        if [[ "$count" -le "4" ]]; then count=$(( $count + 1 )); fi
        printf "\r %c %3d.%1d%% %.${pd}s" $anim $(( $count * 100 / $finish )) $(( ($count * 1000 / $finish) % 10 )) $mark
        local anim=$temp${anim%"$temp"}
    done
    while [[ $count -lt $limit ]]; do
        sleep $delay
        local temp=${anim#?}
        local pd=$(( $count * 73 / $finish ))
        count=$(( $count + 1 ))
        printf "\r %c %3d.%1d%% %.${pd}s" $anim $(( $count * 100 / $finish )) $(( ($count * 1000 / $finish) % 10 )) $mark
        local anim=$temp${anim%"$temp"}
    done
    echo
}

progress_print() {
    # command & progress_print " file to print"
    local pid=$! check check2 idk=0 file="$1" delay=0 count=0
    local huh limit uwu
    limit=$(cat "$file" | wc -l)
    huh=$(( $limit / 4 ))
    while read uwu; do
       while kill -0 "$pid" 2> /dev/null; do
           count=$(( $count + 1 ))
           sleep $delay
           if [[ "$uwu" != "$check" ]]; then
              if [[ "$count" -lt "$huh" ]]; then 
                 ui_print "$uwu"
              fi
              if [[ "$count" -lt "$(( $count * 2 ))" ]]; then 
                 ui_print "$uwu"
              fi
           fi
           check="$uwu"
       done
       while [[ "$idk" -lt "$limit" ]]; do
         if [[ "$uwu" != "$check2" && "$uwu" != "$check" ]]; then
            idk=$(( $idk + 1 ))
            sleep $delay
            ui_print "$uwu"
            check2="$uwu"
         else
            break
         fi
       done
    done < "$file"
    echo
}


progress_script() {
   local huh
   #progress_script script/commands
   huh="$@"
   if [ -z "$huh" ]; then return; fi
   $("$@" >/dev/null 2>&1) & progress
}

start_loading() {
   #start_loading -s 10 -f 100 -a '|/-\' -l [..................................]
   #start_loading -s(tart) 10 -f(inish) 100 -d(elay) 0.5 -a(nimation) '|/-\' -l(inear) [..................................]
   local complete=$(getvalue s $@ 2>/dev/null)
   export mark=$(getvalue l $@ 2>/dev/null)
   export loafinish=$(getvalue f $@ 2>/dev/null)
   export delay=$(getvalue d $@ 2>/dev/null)
   export loacount=0
   export anim=$(getvalue a $@ 2>/dev/null)
   for huh in complete loafinish; do
      uwu=$(checkvar $huh)
      if [ -z "$uwu" ]; then
         echo "start loading: Fatal line" && return
      fi
   done
   if [ -z "$delay" ]; then export delay="0.3" ; fi;
   while [[ $loacount -lt $loafinish && $loacount -lt $complete ]]; do
     sleep $delay
     local temp=${anim#?}
     export loacount=$(( $loacount + 1 ))
     export pd=$(( $loacount * 73 / $loafinish ))
     if [[ -n "$mark" && -n "$anim" ]]; then
        printf "\r %c %3d.%1d%% %.${pd}s" $anim $(( $loacount * 100 / $loafinish )) $(( ($loacount * 1000 / $loafinish) % 10 )) $mark
        local anim=$temp${anim%"$temp"}
     elif [[ -z "$mark" && -n "$anim" ]]; then
        printf "\r %c %3d.%1d%%" $anim $(( $loacount * 100 / $loafinish )) $(( ($loacount * 1000 / $loafinish) % 10 ))
        local anim=$temp${anim%"$temp"}
     elif [[ -n "$mark" && -z "$anim" ]]; then
        printf "\r%3d.%1d%% %.${pd}s" $(( $loacount * 100 / $loafinish )) $(( ($loacount * 1000 / $loafinish) % 10 )) $mark
     else
       printf "\r%3d.%1d%%" $(( $loacount * 100 / $loafinish )) $(( ($loacount * 1000 / $loafinish) % 10 ))
     fi
   done
   if [[ "$loacount" -ge "$loafinish" ]]; then echo ; fi;
}

add_loading() {
   local complete=$1
   local add=0
   for huh in complete loafinish; do
      uwu=$(checkvar $huh)
      if [ -z "$uwu" ]; then
         echo "add loading: Fatal line or undefined start_loading" && return
      fi
   done
   while [[ $loacount -lt $loafinish && $add -lt $complete ]]; do
     sleep $delay
     local temp=${anim#?}
     export loacount=$(( $loacount + 1 ))
     add=$(( $add + 1 ))
     export pd=$(( $loacount * 73 / $loafinish ))
     if [[ -n "$mark" && -n "$anim" ]]; then
        printf "\r %c %3d.%1d%% %.${pd}s" $anim $(( $loacount * 100 / $loafinish )) $(( ($loacount * 1000 / $loafinish) % 10 )) $mark
        local anim=$temp${anim%"$temp"}
     elif [[ -z "$mark" && -n "$anim" ]]; then
        printf "\r %c %3d.%1d%%" $anim $(( $loacount * 100 / $loafinish )) $(( ($loacount * 1000 / $loafinish) % 10 ))
        local anim=$temp${anim%"$temp"}
     elif [[ -n "$mark" && -z "$anim" ]]; then
        printf "\r%3d.%1d%% %.${pd}s" $(( $loacount * 100 / $loafinish )) $(( ($loacount * 1000 / $loafinish) % 10 )) $mark
     else
       printf "\r%3d.%1d%%" $(( $loacount * 100 / $loafinish )) $(( ($loacount * 1000 / $loafinish) % 10 ))
     fi
   done
   if [[ "$loacount" -ge "$loafinish" ]]; then echo ; fi;
}

smali_kit() {
   #Smali Tool kit for Dynamic Installer by BlassGO
   local dir num line liner old new count=0 restore load try log result
   local file path method replace limit rim newline oldline check stock edit smaliname remake
   local get al al_add bl bl_add staticname dim dim_oldline justprint printpath prev dm
   local restore flag 
   delete "$TMP/smali_kit"
   restore=()
   while [[ $# -gt 0 ]]; do
   flag="$1"
   case $flag in
       -f|-file)
       file="$2"
       shift;shift;
       ;;
       -d|-dir)
       path="$2"
       shift;shift;
       ;;
       -m|-method)
       method="$2"
       shift;shift;
       ;;
       -r|-replace)
       replace="$2"
       shift;shift;
       ;;
       -rim|-replace-in-method)
       rim=true
       oldline="$2"
       newline="$3"
       shift;shift;shift;
       ;;
       -dim|-delete-in-method)
       dim=true
       dim_oldline="$2"
       shift;shift;
       ;;
       -re|-remake)
       remake="$2"
       shift;shift;
       ;;
       -al|-after-line)
       al="$2"
       al_add="$3"
       shift;shift;shift;
       ;;
       -bl|-before-line)
       bl="$2"
       bl_add="$3"
       shift;shift;shift;
       ;;
       -c|-check)
       check=true
       shift
       ;;
       -n|-name)
       smaliname="$2"
       shift;shift;
       ;;
       -sn|-static-name)
       staticname="$2"
       shift;shift;
       ;;
       -l|-limit)
       limit="$2"
       shift;shift;
       ;;
       -pp|-print-path)
       printpath=true
       shift
       ;;
       -dm|-delete-method)
       dm=true
       shift
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
   esac
   done
   set -- "${restore[@]}"
   if [[ -z "$file" && -n "$path" && -n "$method" ]]; then
      if [ ! -d "$path" ]; then echo2 "CANT FIND: $path" && return 1; fi
      grep -rnw "$path" -e "$method" | while read huh; do
         undefined huh && break
         stock=
         edit=
         dir=$(echo "$huh" | cut -f1 -d:)
         num=$(echo "$huh" | cut -f2 -d:)
         liner=$(echo "$huh" | cut -f3 -d:)
         [ ! -f "$dir" ] && continue
         [ -z "$liner" ] && continue
         if [[ "$liner" == *".method"* && "$liner" != *".method abstract"* && "$liner" != *".method public abstract"* ]]; then
            if [[ -n "$staticname" && "$(basename "$dir")" != "$staticname" ]]; then continue; fi
            if [[ -n "$smaliname" && "$(basename "$dir")" != *"$smaliname"* ]]; then continue; fi
            if [ -n "$printpath" ]; then if [[ "$dir" != "$prev" ]]; then 
               count=$(( $count + 1 ))
               echo "$dir"
               echo 0 > "$TMP/smali_kit"
               if [[ -n "$limit" && "$limit" == "$count" ]]; then break; fi
               prev="$dir" && justprint=true && continue
            fi; fi
            line="$liner"
            savestate stock "$dir"
            if [[ -z "$replace" && -z "$rim" && -z "$remake" && -z "$al" && -z "$bl" && -z "$dim" && -z "$printpath" && -z "$dm" ]]; then
               justprint=true
               echo "path=$dir"
               string -f "$dir" complete_extract "$line" ".end method"
            fi
            if [ -z "$justprint" ]; then testrw "$(dirname "$dir")" || return 1; fi 
            if [ -n "$replace" ]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               result=${load/"$old"/"$replace"}
               echo "$result" > "$dir"
            fi
            if [[ -n "$rim" && -n "$oldline" && -n "$newline" ]]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               try=${old/"$oldline"/"$newline"}
               result=${load/"$old"/"$try"}
               echo "$result" > "$dir"
            fi
            if [[ -n "$dim" && -n "$dim_oldline" ]]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               try=${old//"$dim_oldline"/}
               result=${load/"$old"/"$try"}
               echo "$result" > "$dir"
            fi
            if [ -n "$remake" ]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               echo "$liner" > "$TMP/re.tmp"
               echo "$remake" >> "$TMP/re.tmp"
               echo "${n}.end method" >> "$TMP/re.tmp"
               try=$(cat "$TMP/re.tmp")
               rm -f "$TMP/re.tmp" 2>/dev/null
               result=${load/"$old"/"$try"}
               echo "$result" > "$dir"
            fi
            if [[ -n "$bl" && -n "$bl_add" ]]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               try=$(string -before-line "$bl" "$bl_add" "$old")
               result=${load/"$old"/"$try"}
               echo "$result" > "$dir"
            fi
            if [[ -n "$al" && -n "$al_add" ]]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               try=$(string -after-line "$al" "$al_add" "$old")
               result=${load/"$old"/"$try"}
               echo "$result" > "$dir"
            fi
            if [ -n "$dm" ]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               result=${load//"$old"/}
               echo "$result" > "$dir"
            fi
            savestate edit "$dir"
            if [ -z "$printpath" ]; then count=$(( $count + 1 )); fi
            if [ -n "$check" ]; then if [[ "$edit" != "$stock" ]]; then ui_print "Edited: \"$dir\""; else ui_print "Nothing: \"$dir\""; fi; fi
            if [[ "$edit" != "$stock" ]]; then echo 0 > "$TMP/smali_kit"; else echo 1 > "$TMP/smali_kit"; fi
            if [ -n "$justprint" ]; then echo 0 > "$TMP/smali_kit"; fi
            if [[ -n "$limit" && "$limit" == "$count" ]]; then break; fi
         fi
      done
   elif [[ -n "$file" && -z "$path" && -n "$method" ]]; then
      if [ ! -f "$file" ]; then echo2 "CANT FIND: $file" && return 1; fi
      grep -nw "$file" -e "$method" | while read huh; do
         undefined huh && break
         stock=
         edit=
         dir="$file"
         num=$(echo "$huh" | cut -f1 -d:)
         liner=$(echo "$huh" | cut -f2 -d:)
         [ -z "$liner" ] && continue
         if [[ "$liner" == *".method"* && "$liner" != *".method abstract"* && "$liner" != *".method public abstract"* ]]; then
            if [[ -n "$staticname" && "$(basename "$dir")" != "$staticname" ]]; then continue; fi
            if [[ -n "$smaliname" && "$(basename "$dir")" != *"$smaliname"* ]]; then continue; fi
            if [ -n "$printpath" ]; then if [[ "$dir" != "$prev" ]]; then 
               count=$(( $count + 1 ))
               echo "$dir"
               echo 0 > "$TMP/smali_kit"
               if [[ -n "$limit" && "$limit" == "$count" ]]; then break; fi
               prev="$dir" && justprint=true && continue
            fi; fi
            line="$liner"
            savestate stock "$dir"
            if [[ -z "$replace" && -z "$rim" && -z "$remake" && -z "$al" && -z "$bl" && -z "$dim" && -z "$printpath" && -z "$dm" ]]; then
               justprint=true
               echo "path=$dir"
               string -f "$dir" complete_extract "$line" ".end method"
            fi
            if [ -z "$justprint" ]; then testrw "$(dirname "$dir")" || return 1; fi 
            if [ -n "$replace" ]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               result=${load/"$old"/"$replace"}
               echo "$result" > "$dir"
            fi
            if [[ -n "$rim" && -n "$oldline" && -n "$newline" ]]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               try=${old/"$oldline"/"$newline"}
               result=${load/"$old"/"$try"}
               echo "$result" > "$dir"
            fi
            if [[ -n "$dim" && -n "$dim_oldline" ]]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               try=${old//"$dim_oldline"/}
               result=${load/"$old"/"$try"}
               echo "$result" > "$dir"
            fi
            if [ -n "$remake" ]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               echo "$liner" > "$TMP/re.tmp"
               echo "$remake" >> "$TMP/re.tmp"
               echo "${n}.end method" >> "$TMP/re.tmp"
               try=$(cat "$TMP/re.tmp")
               rm -f "$TMP/re.tmp" 2>/dev/null
               result=${load/"$old"/"$try"}
               echo "$result" > "$dir"
            fi
            if [[ -n "$bl" && -n "$bl_add" ]]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               try=$(string -before-line "$bl" "$bl_add" "$old")
               result=${load/"$old"/"$try"}
               echo "$result" > "$dir"
            fi
            if [[ -n "$al" && -n "$al_add" ]]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               try=$(string -after-line "$al" "$al_add" "$old")
               result=${load/"$old"/"$try"}
               echo "$result" > "$dir"
            fi
            if [ -n "$dm" ]; then
               load=$(cat "$dir")
               old=$(string complete_extract "$line" ".end method" "$load")
               result=${load//"$old"/}
               echo "$result" > "$dir"
            fi
            savestate edit "$dir"
            if [ -z "$printpath" ]; then count=$(( $count + 1 )); fi
            if [ -n "$check" ]; then if [[ "$edit" != "$stock" ]]; then ui_print "Edited: \"$dir\""; else ui_print "Nothing: \"$dir\""; fi; fi
            if [[ "$edit" != "$stock" ]]; then echo 0 > "$TMP/smali_kit"; else echo 1 > "$TMP/smali_kit"; fi
            if [ -n "$justprint" ]; then echo 0 > "$TMP/smali_kit"; fi
            if [[ -n "$limit" && "$limit" == "$count" ]]; then break; fi
         fi
      done
   else
      echo " smali_kit: Invalid line " && return 1
   fi
   if [ -f "$TMP/smali_kit" ]; then return $(cat "$TMP/smali_kit"); else return 1; fi
}

apex_pkg() {
    #Based on Magisk method
    local pattern='s/.*"name":[^"]*"\([^"]*\).*/\1/p' apex="$1" dest
    local pattern2='s/.*package="\([^"]*\).*$/\1/p'
    if [ -f "$apex/apex_manifest.json" ]; then
        dest=$(sed -n $pattern "$apex/apex_manifest.json" | tr -d " ")
    elif [ -f "$apex/apex_manifest.pb" ]; then
        dest=$(strings "$apex/apex_manifest.pb" | head -n 1 | tr -cd '[:alnum:]_.')
    fi
    [ ! -z "$dest" ] && echo "$dest" && return
    dest=$((unzip -qp "$apex" apex_manifest.pb | strings | head -n 1 | tr -cd '[:alnum:]_.') 2>/dev/null)
    [ -z "$dest" ] && dest=$((unzip -qp "$apex" apex_manifest.json | sed -n $pattern | tr -d " ") 2>/dev/null)
    [ -z "$dest" ] && dest=$((unzip -qp "$apex" apex_build_info.pb | sed -n $pattern2 | tr -d " ") 2>/dev/null)
    [ ! -z "$dest" ] && echo "$dest" || return 1
}

getarch() {
  local try info prop system_prop vendor_prop
  if ! $BOOTMODE; then
     try_mount -e -n system /systemtmp 2>/dev/null
     try_mount -e -n vendor /vendortmp 2>/dev/null
     if is_mounted /systemtmp; then
        if [ -f /systemtmp/build.prop ]; then
           system_prop=/systemtmp/build.prop
        elif [ -f /systemtmp/system/build.prop ]; then
           system_prop=/systemtmp/system/build.prop
        fi
        free_system=$((df -Pk /systemtmp | sed "1d" | awk '{print $4}') 2>/dev/null)
     fi
     if is_mounted /vendortmp; then
        if [ -f /vendortmp/build.prop ]; then
           vendor_prop=/vendortmp/build.prop
        fi
        free_vendor=$((df -Pk /vendortmp | sed "1d" | awk '{print $4}') 2>/dev/null)
     elif [ -f /systemtmp/vendor/build.prop ]; then
           vendor_prop=/systemtmp/vendor/build.prop
           free_vendor=$free_system
     elif [ -f /systemtmp/system/vendor/build.prop ]; then
           vendor_prop=/systemtmp/system/vendor/build.prop
           free_vendor=$free_system
     fi
  else
     system_prop=/system/build.prop
     vendor_prop=/vendor/build.prop
     [ -f /system/vendor/build.prop ] && vendor_prop=/system/vendor/build.prop
     free_system=$((df -Pk /system | sed "1d" | awk '{print $4}') 2>/dev/null)
     free_vendor=$((df -Pk /vendor | sed "1d" | awk '{print $4}') 2>/dev/null)
  fi
  #Get free space in root /
  free_root=$((df -Pk / | sed "1d" | awk '{print $4}') 2>/dev/null)
  #Convert KB to B
  [ -n "$free_root" ] && free_root=$(calc "$free_root * 1024")
  [ -n "$free_system" ] && free_system=$(calc "$free_system * 1024")
  [ -n "$free_vendor" ] && free_vendor=$(calc "$free_vendor * 1024")
  if [ ! -f "$system_prop" ]; then
     API=`getprop ro.build.version.sdk`
     ABI=`getprop ro.product.cpu.abi | cut -c-3`
     ABI2=`getprop ro.product.cpu.abi2 | cut -c-3`
     ABILONG=`getprop ro.product.cpu.abi`
     PROC=`getprop ro.product.board`
  else
     API=`get_file_prop "$system_prop" ro.build.version.sdk`
     ABI=`get_file_prop "$system_prop" ro.product.cpu.abi | cut -c-3`
     ABI2=`get_file_prop "$system_prop" ro.product.cpu.abi2 | cut -c-3`
     ABILONG=`get_file_prop "$system_prop" ro.product.cpu.abi`
     PROC=`get_file_prop "$system_prop" ro.hardware.chipname`
  fi
  if [ ! -f "$vendor_prop" ]; then
     virtual_partitions=`getprop ro.virtual_ab.enabled`
     dynamic_partitions=`getprop ro.boot.dynamic_partitions`
  else
     virtual_partitions=`get_file_prop "$vendor_prop" ro.virtual_ab.enabled`
     dynamic_partitions=`get_file_prop "$vendor_prop" ro.boot.dynamic_partitions`
  fi
  [ -z "$dynamic_partitions" ] && dynamic_partitions=false
  [ -z "$virtual_partitions" ] && virtual_partitions=false
  for slot in "$(grep_cmdline androidboot.slot_suffix)" "$(grep_cmdline androidboot.slot)" "$(getprop ro.boot.slot_suffix)"; do
     if [ -n "$slot" ]; then
        if is_substring _ "$slot"; then export slot=$slot && break; else export slot=_${slot} && break; fi
     fi
  done
  if [ -z "$PROC" ]; then
     PROC=$(grep_cmdline androidboot.hardware)
  fi
  #Some Exynos devices use ro.hardware
  if is_substring exynos "$(getprop ro.hardware)"; then PROC=$(getprop ro.hardware); fi
  #Magisk method
  encrypted=true
  touch /data/.rw && rm /data/.rw && encrypted=false
  grep ' /data ' /proc/mounts | grep -q 'dm-' && encrypted=true
  [ "$(getprop ro.crypto.state)" = "encrypted" ] && encrypted=true
  status=$(getenforce)
  arch=arm
  arch32=arm
  is64bit=false
  if [ -e "/proc/cpuinfo" ]; then 
     try=$(cat /proc/cpuinfo | grep -m1 "architecture" | cut -d':' -f2 | tr -d " ")
     if is_number "$try"; then
        if [[ "$try" == "8" ]]; then max_arch=arm64
        elif [[ "$try" == "7" ]]; then max_arch=arm
        fi
     else
        echo2 " Warning: ABnormal architecture"
     fi
     if $(cat /proc/cpuinfo | grep -m1 "Hardware" | grep -q "Qualcomm"); then chipname=snapdragon
     elif $(cat /proc/cpuinfo | grep -m1 "Hardware" | grep -q "Kirin"); then chipname=kirin
     elif $(cat /proc/cpuinfo | grep -m1 "Hardware" | grep -q "Unisoc"); then chipname=unisoc
     elif $(cat /proc/cpuinfo | grep -m1 "Hardware" | grep -q "MT"); then chipname=mediatek
     elif defined PROC && is_substring "exynos" "$PROC"; then chipname=exynos
     fi
  fi
  if [ "$ABI" = "x86" ]; then arch=x86; arch32=x86; fi;
  if [ "$ABI2" = "x86" ]; then arch=x86; arch32=x86; fi;
  if [ "$ABILONG" = "arm64-v8a" ]; then arch=arm64; arch32=arm; is64bit=true; base_lib=64; fi;
  if [ "$ABILONG" = "x86_64" ]; then arch=x64; arch32=x86; is64bit=true; base_lib=64; fi;
  echo2 "------------Device INFO------------"
  for info in API ABI ABI2 ABILONG PROC arch arch32 max_arch is64bit chipname status encrypted slot dynamic_partitions virtual_partitions free_root free_system free_vendor; do
     if defined $info; then echo2 "$info=$(checkvar $info)"; fi
  done
  echo2 "------------Setup INFO------------"
  for info in CUSTOM_SETUP TMP $SHARED_VARS di_version main_version; do
     if defined $info; then echo2 "$info=$(checkvar $info)"; fi
  done
  echo2 "----------------------------------"
  if ! $BOOTMODE; then
     unmount /systemtmp
     unmount /vendortmp
     is_mounted /systemtmp || delete_recursive /systemtmp
     is_mounted /vendortmp || delete_recursive /vendortmp
  fi
}

getsecurity() {
  local avb avb2 info
  if ! can_run avbctl; then echo2 "FATAL ERROR: Please import/use valid avbctl" && return 1; fi
  if $is64bit; then
     avb=$(avbctl get-verity)
     avb2=$(avbctl get-verification)
     if is_substring "verity is enabled" "$avb"; then VERITY=enabled
     elif is_substring "verity is disabled" "$avb"; then VERITY=disabled
     fi
     if is_substring "verification is enabled" "$avb2"; then VERIFICATION=enabled
     elif is_substring "verification is disabled" "$avb2"; then VERIFICATION=disabled
     fi
  fi
  for info in VERITY VERIFICATION; do
     if defined $info; then echo2 "> $info=$(checkvar $info)"; fi
  done
  echo2 " "
}

disable_avb() {
   #It may cause problems in samsung devices (Risk)
   if ! can_run avbctl; then echo2 "FATAL ERROR: Please import/use valid avbctl" && return 1; fi
   getsecurity 2>/dev/null
   if $is64bit; then
      if [[ "$VERIFICATION" == "enabled" ]]; then avbctl --force disable-verification > /dev/null; fi
      if [[ "$VERITY" == "enabled" ]]; then avbctl --force disable-verity > /dev/null; fi
   fi
   getsecurity
}

enable_avb() {
   if ! can_run avbctl; then echo2 "FATAL ERROR: Please import/use valid avbctl" && return 1; fi
   getsecurity 2>/dev/null
   if $is64bit; then
      if [[ "$VERIFICATION" == "disabled" ]]; then avbctl --force enable-verification > /dev/null; fi
      if [[ "$VERITY" == "disabled" ]]; then avbctl --force enable-verity > /dev/null; fi
   fi
   getsecurity
}

getbins() {
  local support pos
  unset bins
  while read support; do
     while read pos; do
      if [ -d "$support" ] && ! is_substring "$pos" "$bins"; then export bins+="$pos:" && echo "$pos"; fi
     done <<< $(basename "$(ls "$support" 2>/dev/null)")
  done <<< $(split_string : "$PATH")
}

run() {
    local file var
    file=$(fullpath "$2")
    var="$1"
    [ ! -f "$file" ] && return 1
    [ -z "$var" ] && return 1
    shift 2
    chmod 777 "$file"
    setdefault "$var" "$("$file" "$@" 2>&1)"
}

run_addon() {
    local file var
    file="$2"
    var="$1"
    shift 2
    run "$var" "$addons/$file" "$@"
}

run_zip() {
    local file var
    file="$2"
    var="$1"
    shift 2
    package_extract_file "$file" "$TMP/$(basename "$file")" || return 1
    run "$var" "$TMP/$(basename "$file")" "$@"
    rm -f "$TMP/$(basename "$file")"
}

package_extract_file() {
   [ -n "$3" ] && local installzip="$3"
   if [ ! -f "$installzip" ]; then echo2 "CANT FIND: $installzip" && return 1; fi
   if unzip -l "$installzip" "$1" | grep -q "$1"; then
      if [[ -n "$1" && -n "$2" ]]; then
         create_dir "$(dirname "$2")" || return 1
         unzip -qp "$installzip" "$1" > "$2"
         if exist "$2"; then set_perm2 $di_uid $di_gid $di_perm_f "$2"
         else return 1
         fi
      elif [ -n "$1" ]; then
         unzip -qp "$installzip" "$1" || return 1
      else
         return 1
      fi
   else
      echo2 "ZIP: Cant find file [$1]" && return 1
   fi
}

package_extract_dir() {
  local outfile return=0 a
  [ -n "$3" ] && local installzip="$3"
  if [ ! -f "$installzip" ]; then echo2 "CANT FIND: $installzip" && return 1; fi
  while read a; do
    if undefined a; then unzip -l "$installzip" "$1" | grep -q "$1" && echo2 "ZIP: You are trying to extract a file [$1], please use package_extract_file" || echo2 "ZIP: Cant find folder [${1%%/}]"; return=1; break; fi
    if [[ -n "$1" && -n "$2" ]]; then
       outfile=$(echo "$a" | sed "s|${1}|${2}|");
       if echo "$a" | grep -q '/$'; then 
          create_dir "$outfile" || return 1
          continue
       else create_dir "$(dirname "$outfile")" || return 1
       fi
       unzip -qp "$installzip" "$a" > "$outfile"
       if exist "$outfile"; then set_perm2 $di_uid $di_gid $di_perm_f "$outfile"
       else return=1 && continue
       fi
    elif [ -n "$1" ]; then
       if echo "$a" | grep -q '/$'; then continue; fi
       unzip -qp "$installzip" "$a" || return=1
    else return=1 && break
    fi
  done <<< $(unzip -l "$installzip" | tail -n+4 | grep -o " ${1%%/}/.*$" | cut -c2-)
  return $return
}

getblocks() {
   local block mapper bd result base
   unset all_partitions
   for block in /dev/block/platform/*/by-name/*; do
       if [ -e "$block" ]; then
          result=
          base=
          base=$(basename "$block")
          result=$(readlink -f "$block")
          if defined result && exist "$result" && testvarname "$base"; then
             base=$(string lower "$base")
             setdefault "$base" "$result"
             echo2 "$base=$result"
             export all_partitions+="$result:"
          fi
       fi
   done
   for block in /dev/block/by-name/*; do
       if [ -e "$block" ]; then
          result=
          base=
          base=$(basename "$block")
          result=$(readlink -f "$block")
          if defined result && exist "$result" && testvarname "$base" && ! is_substring "$result" "$all_partitions"; then
             base=$(string lower "$base")
             setdefault "$base" "$result"
             echo2 "$base=$result"
             export all_partitions+="$result:"
          fi
       fi
   done 
   if [[ "$dynamic_partitions" == "true" ]]; then
       while read mapper; do
           if [ -e "$mapper" ]; then
              result=
              base=
              base=$(basename "$mapper")
              result=$(readlink -f "$mapper")
              if defined result && exist "$result" && testvarname "$base" && ! is_substring "$result" "$all_partitions"; then
                 base=$(string lower "$base")
                 setdefault "$base" "$result"
                 echo2 "$base=$result"
                 export all_partitions+="$result:"
              fi
           fi
       done <<< $(find /dev/block/mapper -mindepth 1 -maxdepth 1 \( -type b -o -type c -o -type l \)) 
   fi
   if [ -d /dev/block/bootdevice ]; then
      while read bd; do
           if [ -e "$bd" ]; then
              result=
              base=
              base=$(basename "$bd")
              result=$(readlink -f "$bd")
              if defined result && exist "$result" && testvarname "$base" && ! is_substring "$result" "$all_partitions"; then
                 base=$(string lower "$base")
                 setdefault "$base" "$result"
                 echo2 "$base=$result"
                 export all_partitions+="$result:"
              fi
           fi
       done <<< $(find /dev/block/bootdevice -mindepth 1 -maxdepth 1 \( -type b -o -type c -o -type l \)) 
   fi
}

fprint() {
  local line
  while IFS='' read -r line || [[ -n "$line" ]]; do
    ui_print "$line";
  done < "$1";
}

fprint_zip() {
  package_extract_file "$1" "$TMP/$1" || return 1
  fprint "$TMP/$1"
  rm -f "$TMP/$1"
}

fprint_addon() {
  fprint "$addons/$1"
}

update() {
    local bs restore flag xz gz ro sparse orig dest
    restore=()
    while [[ $# -gt 0 ]]; do
      flag="$1"
      case $flag in
          -xz)
          xz=true
          shift;
          ;;
          -gz)
          gz=true
          shift;
          ;;
          -sparse)
          sparse=true
          shift;
          ;;
          *)   
          restore+=("$1")
          shift;
          ;;
      esac
    done
    set -- "${restore[@]}"
    orig="$1"; dest="$2"
    if is_number "$(string force remove M "$extraction_speed" 2>/dev/null)"; then bs=$extraction_speed; else bs=1048576; fi
    if exist "$dest"; then
       blockdev --setrw "$dest" 2>/dev/null
       ro=$(blockdev --getro "$dest" 2>/dev/null)
       if [[ "$ro" == "1" ]]; then
          echo2 "FATAL ERROR: Read/Only: $dest"
          if [[ "$3" == "1" ]]; then abort; else return 1; fi
       elif [[ "$ro" != "0" ]]; then
          echo2 "Cant get state: $dest"
          echo2 "Skipping: Writing in: $dest"
       fi
    else create_dir "$(dirname "$dest")"
    fi
    if defined sparse; then
       if [ -f "$(which simg2img)" ]; then
          dest="$TMP/$(basename "$orig").tmp"
       else
          echo2 "update: Please import simg2img to perform Sparse IMG installation" && return 1
       fi
    fi
    if defined xz; then
       if xz -dc "$orig" > "$dest"; then
          if undefined sparse; then
             echo2 " "
             echo2 "-- Updated:xz: $(basename "$orig")"
             echo2 " "
             return 0
          else orig="$dest"
          fi
       else
          echo2 " "
          echo2 "-- Cant update:xz: $(basename "$orig")"
          echo2 " "
          if [[ "$3" == "1" ]]; then abort; else return 1; fi
       fi
    elif defined gz; then
       if gunzip -c "$orig" > "$dest"; then
          if undefined sparse; then
             echo2 " "
             echo2 "-- Updated:gz: $(basename "$orig")"
             echo2 " "
             return 0
          else orig="$dest"
          fi
       else
          echo2 " "
          echo2 "-- Cant update:gz: $(basename "$orig")"
          echo2 " "
          if [[ "$3" == "1" ]]; then abort; else return 1; fi
       fi
    fi
    if defined sparse || magic_file -t sparse "$orig" 2>/dev/null; then
       if [ -f "$(which simg2img)" ]; then
          if simg2img "$orig" "$2"; then
              echo2 " "
              echo2 "-- Updated:sparse: $(basename "$orig")"
              echo2 " "
              [[ "$orig" != "$1" ]] && delete "$orig"
              return 0
          else
              echo2 " "
              echo2 "-- Cant update:sparse: $(basename "$orig")"
              echo2 " "
              if [[ "$3" == "1" ]]; then abort; else return 1; fi
          fi
       else
          echo2 "update: Please import simg2img to perform Sparse IMG installation" && return 1
       fi
    fi
    if dd if="$orig" of="$dest" bs=$bs; then
        echo2 " "
        echo2 "-- Updated:1: $(basename "$orig")"
        echo2 " "
    else
        echo2 " "
        echo2 "-- Cant update: $(basename "$orig")"
        echo2 " "
        if [[ "$3" == "1" ]]; then abort; else return 1; fi
    fi
}

update_addon() {
    local restore flag xz gz sparse
    restore=()
    while [[ $# -gt 0 ]]; do
      flag="$1"
      case $flag in
          -xz)
          xz="-xz"
          shift
          ;;
          -gz)
          gz="-gz"
          shift
          ;;
          -sparse)
          sparse="-sparse"
          shift;
          ;;
          *)   
          restore+=("$1")
          shift
          ;;
      esac
    done
    set -- "${restore[@]}"
    update $xz $gz $sparse "$addons/$1" "$2" "$3"
}

update_zip() {
    local bs restore flag xz gz ro sparse orig dest
    restore=()
    while [[ $# -gt 0 ]]; do
      flag="$1"
      case $flag in
          -xz)
          xz="-xz"
          shift;
          ;;
          -gz)
          gz="-gz"
          shift;
          ;;
          -sparse)
          sparse="-sparse"
          shift;
          ;;
          *)   
          restore+=("$1")
          shift;
          ;;
      esac
    done
    set -- "${restore[@]}"
    orig="$1"; dest="$2"
    if defined sparse; then
       if [ -f "$(which simg2img)" ]; then
          dest="$TMP/$(basename "$orig").tmp"
          package_extract_file "$orig" "$dest" || return 1
          if update $xz $gz $sparse "$dest" "$2" "$3"; then delete "$dest"; return 0; else return 1; fi
       else
          echo2 "update_zip: Please import simg2img to perform Sparse IMG installation" && return 1
       fi
    fi
    if is_number "$(string force remove M "$extraction_speed" 2>/dev/null)"; then bs=$extraction_speed; else bs=1048576; fi
    if exist "$dest"; then
       blockdev --setrw "$dest" 2>/dev/null
       ro=$(blockdev --getro "$dest" 2>/dev/null)
       if [[ "$ro" == "1" ]]; then
          echo2 "FATAL ERROR: Read/Only: $dest"
          if [[ "$3" == "1" ]]; then abort; else return 1; fi
       elif [[ "$ro" != "0" ]]; then
          echo2 "Cant get state: $dest"
          echo2 "Skipping: Writing in: $dest"
       fi
    else create_dir "$(dirname "$dest")"
    fi
    if defined xz; then
       if unzip -qp "$installzip" "$orig" | xz -dc > "$dest"; then
          echo2 " "
          echo2 "-- Updated:xz: $(basename "$orig")"
          echo2 " "
          return 0
       else
          echo2 " "
          echo2 "-- Cant update:xz: $(basename "$orig")"
          echo2 " "
          if [[ "$3" == "1" ]]; then abort; else return 1; fi
       fi
    elif defined gz; then
       if unzip -qp "$installzip" "$orig" | gunzip -c - > "$dest"; then
          echo2 " "
          echo2 "-- Updated:gz: $(basename "$orig")"
          echo2 " "
          return 0
       else
          echo2 " "
          echo2 "-- Cant update:gz: $(basename "$orig")"
          echo2 " "
          if [[ "$3" == "1" ]]; then abort; else return 1; fi
       fi
    fi
    if unzip -qp "$installzip" "$orig" | dd of="$dest" bs=$bs; then
        echo2 " "
        echo2 "-- Updated:1: $(basename "$orig")"
        echo2 " "
    else
        echo2 " "
        echo2 "-- Cant update: $(basename "$orig")"
        echo2 " "
        if [[ "$3" == "1" ]]; then abort; else return 1; fi
    fi
}

get_file_prop() {
  local file prop try i=0
  # a b c -> c a b
  while [ $((i+=1)) -lt $# ]; do
    set -- "$@" "$1"
    shift
  done
  prop="$1"
  shift
  [ -z "$prop" ] && return 1
  for file in "$@"; do
     try=$(cat "$file" 2>/dev/null | dos2unix | grep -Fm1 "${prop}=" | cut -d= -f2)
     [ -n "$try" ] && echo "$try" && return 0
  done
  return 1
}

set_progress() { [ -e "$OUTFD" ] && echo "set_progress $1" >> $OUTFD; }

ui_print() {
  while [ "$1" ]; do
     if ! $BOOTMODE; then
        echo "$1" | while read line; do
            [ -e "$OUTFD" ] && echo "ui_print $line${n}ui_print" >> $OUTFD || echo "$line"
        done
     else 
       echo "$1"
     fi
     shift;
  done
}

ch_con() {
  local con return=0
  if [ -n "$1" ]; then is_substring u:object "$1" && con=$1 || con=u:object_r:$1:s0; else return 1; fi
  shift
  while [ "$1" ]; do
    [[ "$con" == "$(get_context "$1")" ]] && shift && continue
    testrw "$(dirname "$1")" || return 1
    echo2 "set_context: $con in $1"
    chcon -h $con "$1" 2>/dev/null || chcon $con "$1"
    shift;
  done
  return $return
}

ch_con_recursive() {
    local dcon fcon file folder s=0
    local restore flag f d
    restore=()
    while [[ $# -gt 0 ]]; do
    flag="$1"
    case $flag in
       -f|-file)
       f=true
       shift
       ;;
       -d|-dir)
       d=true
       shift
       ;;
       *)   
       restore+=("$1")
       shift
       ;;
    esac
    done
    set -- "${restore[@]}"
    if defined d; then is_substring u:object "$1" && dcon=$1 || dcon=u:object_r:$1:s0; s=1; fi
    if defined f; then is_substring u:object "$1" && fcon=$1 || fcon=u:object_r:$1:s0; [ "$s" == 0 ] && s=1; fi
    if undefined f d; then
       is_substring u:object "$1" && dcon=$1 || dcon=u:object_r:$1:s0; s=$(( $s + 1 ))
       is_substring u:object "$2" && fcon=$2 || fcon=u:object_r:$2:s0; s=$(( $s + 1 ))
    fi
    [[ $s == 0 ]] && return 1
    shift $s
    while [ "$1" ]; do
      if [ ! -d "$1" ]; then echo2 "CANT FIND: $1" && shift && continue; fi
      testrw "$1" || return 1
      [ -n "$dcon" ] && ( find "$1" -type d | while read folder; do ch_con $dcon "$folder"; done)
      [ -n "$fcon" ] && ( find "$1" -type f -o -type l | while read file; do ch_con $fcon "$file"; done)
      shift;
    done;
}

set_perm() {
  local uid gid mod
  [ -n "$1" ] && uid=$1  || return 1
  [ -n "$2" ] && gid=$2 || return 1
  [ -n "$3" ] && mod=$3 || return 1
  shift 3
  while [ "$1" ]; do
     testrw "$(dirname "$1")" || return 1
     chown "$uid:$gid" "$1" 2>/dev/null || chown "$uid.$gid" "$1"
     chmod "$mod" "$1"
     shift;
  done;
}

set_perm2() {
  #Ensure this format
  local uid gid mod
  [ -n "$1" ] && uid=$1  || return 1
  [ -n "$2" ] && gid=$2 || return 1
  [ -n "$3" ] && mod=$3 || return 1
  shift 3
  while [ "$1" ]; do
     testrw "$(dirname "$1")" || return 1
     chown "$uid:$gid" "$1" 2>/dev/null || chown "$uid.$gid" "$1"
     chmod "$mod" "$1"
     shift;
  done;
}

set_perm_recursive() {
  local uid gid dmod fmod
  [ -n "$1" ] && uid=$1  || return 1
  [ -n "$2" ] && gid=$2 || return 1
  [ -n "$3" ] && dmod=$3 || return 1
  [ -n "$4" ] && fmod=$4 || return 1
  shift 4
  while [ "$1" ]; do
    if [ ! -d "$1" ]; then echo2 "CANT FIND: $1" && shift && continue; fi
    testrw "$1" || return 1
    chown -R $uid:$gid "$1" 2>/dev/null || chown -R $uid.$gid "$1"
    chmod -R $fmod "$1"
    find "$1" -type l -exec chmod $fmod {} +;
    find "$1" -type d -exec chmod $dmod {} +;
    shift
  done
}

set_perm_recursive2() {
  #Ensure this format
  local uid gid dmod fmod
  [ -n "$1" ] && uid=$1  || return 1
  [ -n "$2" ] && gid=$2 || return 1
  [ -n "$3" ] && dmod=$3 || return 1
  [ -n "$4" ] && fmod=$4 || return 1
  shift 4
  while [ "$1" ]; do
    if [ ! -d "$1" ]; then echo2 "CANT FIND: $1" && shift && continue; fi
    testrw "$1" || return 1
    chown -R $uid:$gid "$1" 2>/dev/null || chown -R $uid.$gid "$1"
    chmod -R $fmod "$1"
    find "$1" -type l -exec chmod $fmod {} +;
    find "$1" -type d -exec chmod $dmod {} +;
    shift
  done
}

saveperm() {
   local huh perm list="$TMP/perm_list_001.txt"
   [ ! -f "$list" ] && echo > "$list"
   while [ "$1" ]; do
      if [ -d "$1" ]; then
         echo2 "saving permissions recursively: $1"
         find -L "$1" -mindepth 1 | while read huh; do
            if ! grep -Fq "$huh=" "$list"; then
               perm=$(get_all_perm "$huh")
               if [ -n "$perm" ]; then
                  echo "$huh=$perm" >> "$list"
               else echo2 "saveperm: CANT get perm of $huh"
               fi
            fi
         done
      elif [ -f "$1" ]; then
         echo2 "saving permissions: $1"
         if ! grep -Fq "$1=" "$list"; then
            perm=$(get_all_perm "$1")
            if [ -n "$perm" ]; then
               echo "$1=$perm" >> "$list"
            else echo2 "saveperm: CANT get perm of $1"
            fi
         fi
      fi
      shift
   done
}

restoreperm() {
   local huh perm list="$TMP/perm_list_001.txt" i=0 flag
   while [ $((i+=1)) -lt $# ]; do
     flag="$1"
     case $flag in
       -file|-f)
       list="$2"
       shift;shift;
       ;;
       *)
       set -- "$@" "$1"
       shift
       ;;
     esac
   done
   while [ "$1" ]; do
      if [ -d "$1" ]; then
         find -L "$1" -mindepth 1 | while read huh; do
            perm=$(get_file_prop "$list" "$huh" 2>/dev/null)
            if [ -n "$perm" ]; then
               [[ "$perm" == "$(get_all_perm "$huh")" ]] && continue
               echo2 "set_perm: $perm in $huh"
               set_perm2 $perm "$huh" || return 1
            else echo2 "set_perm: There is no saved permission for $huh"
            fi
         done
      elif [ -f "$1" ]; then
         perm=$(get_file_prop "$list" "$1" 2>/dev/null)
         if [ -n "$perm" ]; then
            [[ "$perm" == "$(get_all_perm "$1")" ]] && shift && continue
            echo2 "set_perm: $perm in $1"
            set_perm2 $perm "$1" || return 1
         else echo2 "set_perm: There is no saved permission for $1"
         fi
      fi
      shift
   done
}

copy_perm_list() {
   list="$TMP/perm_list_001.txt"
   [ -z "$1" ] && return 1
   [ -f "$list" ] && copy "$list" "$1" || return 1
}

savecontext() {
   local huh context list="$TMP/context_list_001.txt"
   [ ! -f "$list" ] && echo > "$list"
   while [ "$1" ]; do
      if [ -d "$1" ]; then
         echo2 "saving contexts recursively: $1"
         find -L "$1" -mindepth 1 | while read huh; do
            if ! grep -Fq "$huh=" "$list"; then
               context=$(get_context "$huh")
               if [ -n "$context" ]; then
                  echo "$huh=$context" >> "$list"
               else echo2 "savecontext: CANT get context of $huh"
               fi
            fi
         done
      elif [ -f "$1" ]; then
         echo2 "saving context: $1"
         if ! grep -Fq "$1=" "$list"; then
            context=$(get_context "$1")
            if [ -n "$context" ]; then
               echo "$1=$context" >> "$list"
            else echo2 "savecontext: CANT get context of $1"
            fi
         fi
      fi
      shift
   done
}

restorecontext() {
   local huh context list="$TMP/context_list_001.txt" i=0 flag
   while [ $((i+=1)) -lt $# ]; do
     flag="$1"
     case $flag in
       -file|-f)
       list="$2"
       shift;shift;
       ;;
       *)
       set -- "$@" "$1"
       shift
       ;;
     esac
   done
   while [ "$1" ]; do
      if [ -d "$1" ]; then
         find -L "$1" -mindepth 1 | while read huh; do
            context=$(get_file_prop "$list" "$huh" 2>/dev/null)
            if [ -n "$context" ]; then
               ch_con $context "$huh" || return 1
            else echo2 "set_context: There is no saved context for $huh"
            fi
         done
      elif [ -f "$1" ]; then
         context=$(get_file_prop "$list" "$1" 2>/dev/null)
         if [ -n "$context" ]; then
            ch_con $context "$1" || return 1
         else echo2 "set_context: There is no saved context for $1"
         fi
      fi
      shift
   done
}

copy_context_list() {
   list="$TMP/context_list_001.txt"
   [ -z "$1" ] && return 1
   [ -f "$list" ] && copy "$list" "$1" || return 1
}

setup_mountpoint() {
  test -L "$1" && mv -f "$1" "${1}_link"
  if [ ! -d "$1" ]; then
    rm -f "$1"
    mkdir -p "$1"
  fi
}

is_same_mount() {
   local try try2
   is_mounted "$1" && try=$(grep " $(readlink -f "$1") " /proc/mounts | awk '{print $1}' 2>/dev/null)
   is_mounted "$2" && try2=$(grep " $(readlink -f "$2") " /proc/mounts | awk '{print $1}' 2>/dev/null)
   [ -z "$try" ] && try=$(df -Pk "$1" | sed "1d" | awk '{print $1}' 2>/dev/null)
   [ -z "$try2" ] && try2=$(df -Pk "$2" | sed "1d" | awk '{print $1}' 2>/dev/null)
   defined try try2 || return 1
   if [[ "$try" == "$try2" ]]; then echo2 "[$1:$try] == [$2:$try2]" && return 0; else echo2 "[$1:$try] != [$2:$try2]" && return 1; fi
}

remove_tmp() {
   find "$TMP" -mindepth 1 ! -name "*.log" -delete > /dev/null
}

losetup_a() {
  local dev loop_device backing_file
  for dev in /sys/block/loop*; do
    if [ -d "$dev" ]; then
      loop_device=$(basename "$dev")
      if [ -e "/dev/block/$loop_device" -a -f "/sys/block/$loop_device/loop/backing_file" ]; then
        backing_file=$(cat "/sys/block/$loop_device/loop/backing_file")
        echo "/dev/block/$loop_device:$backing_file"
      fi
    fi
  done
}

loop_setup() {
  loopdev=
  local loop minorx=1 num=0 loops=$(losetup_a)
  [ -e /dev/block/loop1 ] && minorx=$(stat -Lc '%T' /dev/block/loop1)
  while [ $num -lt 64 ]; do
    loop=/dev/block/loop$num
    if ([ ! -e $loop ] && mknod $loop b 7 $((num * minorx)) ) || ! echo "$loops" | grep -q $loop; then
      if losetup $loop "$1" 2>/dev/null; then
         loopdev=$loop
         break
      fi
    fi
    num=$((num + 1))
  done
}

mount_apex() {
  $BOOTMODE || [ ! -d /system/apex ] && return
  local apex dest linkerconfig pkg post_apex imported
  local list="$di_apex_list"
  delete "$list"
  setup_mountpoint /apex
  mount -t tmpfs tmpfs /apex -o mode=755
  for apex in /system/apex/* /system_ext/apex/*; do
    [ ! -e "$apex" ] && continue
    pkg=$(apex_pkg "$apex")
    if is_substring ".vndk." "$pkg"; then post_apex+="${apex}:" && continue; fi
    if ! grep -Eq "^$pkg=" "$list" 2>/dev/null; then apex_mount "$apex"; fi
  done
  export ANDROID_RUNTIME_ROOT=$(find /apex -type d -name "com.android.runtime*")
  export ANDROID_ART_ROOT=$(find /apex -type d -name "com.android.art*")
  export ANDROID_TZDATA_ROOT=$(find /apex -type d -name "com.android.tzdata*")
  export ANDROID_I18N_ROOT=$(find /apex -type d -name "com.android.i18n*")
  export ANDROID_DATA="$TMP/dalvik-cache"
  local APEXJARS=$(find /apex -name '*.jar' | sort | tr '\n' ':')
  local FWK=/system/framework
  delete_recursive "$ANDROID_DATA"
  mkdir -p "$ANDROID_DATA"
  export DEX2OATBOOTCLASSPATH=${APEXJARS}
  export BOOTCLASSPATH=${APEXJARS}\
$FWK/framework.jar:$FWK/ext.jar:$FWK/telephony-common.jar:\
$FWK/voip-common.jar:$FWK/ims-common.jar:$FWK/telephony-ext.jar
  #Try mount/generate linkerconfig
  if can_run linkerconfig; then linkerconfig=linkerconfig
  elif can_run $ANDROID_RUNTIME_ROOT/bin/linkerconfig; then linkerconfig=$ANDROID_RUNTIME_ROOT/bin/linkerconfig
  elif can_run /system/bin/bootstrap/linkerconfig; then linkerconfig=/system/bin/bootstrap/linkerconfig
  else echo2 "DI: No linkerconfig"
  fi
  if defined linkerconfig; then
     genre_apex_list || echo2 "CANT GENERATE: apex-info-list.xml"
     echo2 " Generating: /linkerconfig"
     setup_mountpoint /linkerconfig
     mount -t tmpfs tmpfs /linkerconfig -o mode=755
     $linkerconfig --target /linkerconfig
     linkerconfig=$(find /linkerconfig -name '*.txt' | sort | tr '\n' ':')
  fi
  #Importing APEX binaries
  for bin in /apex/*/bin/*; do
     [ ! -e "$bin" ] && continue
     if [ ! -e "$l/$(basename "$bin")" ]; then ln -sf "$bin" "$l/$(basename "$bin")"; [ -n "$imported" ] && imported+=":"; imported+="$bin"; fi
  done
  #Mount post-apex
  split_string : "$post_apex" | while read post; do
     [ -z "$post" ] && break
     apex_mount "$post"
  done
  export APEX_MOUNT_PASS=true
  echo2 "------------APEX INFO------------"
  for dest in ANDROID_RUNTIME_ROOT ANDROID_ART_ROOT \
  ANDROID_TZDATA_ROOT ANDROID_I18N_ROOT ANDROID_DATA \
  BOOTCLASSPATH DEX2OATBOOTCLASSPATH linkerconfig imported
  do
     if defined $dest; then echo2 "$dest=$(checkvar $dest)"; fi
  done
  echo2 "-----------------------------------"
}

apex_mount() {
    local apex="$1" dest loopdev
    local list="$di_apex_list"
    if [ -f "$apex" ]; then
         dest=
         dest=$(apex_pkg "$apex")
         [ ! -z "$dest" ] && dest=/apex/$dest || return 1
         [ -d "$dest" ] || mkdir -p "$dest"
         ( if unzip -l "$apex" original_apex | grep -q original_apex; then
            unzip -p "$apex" original_apex | unzip -qo - apex_payload.img -d /apex
         else unzip -qo "$apex" apex_payload.img -d /apex
         fi ) 2>/dev/null
         if [ ! -f /apex/apex_payload.img ]; then is_mounted "$dest" || delete_recursive "$dest"; return 1; fi
         loop_setup /apex/apex_payload.img
         if [ ! -z $loopdev ]; then
           echo "$(basename $dest)=$apex" >> "$list"
           echo2 " Mounting:file: $dest"
           mount -t ext4 -o ro,noatime $loopdev $dest
         fi
         rm -f /apex/apex_payload.img
    elif [ -d "$apex" ]; then
         dest=
         dest=$(apex_pkg "$apex")
         [ ! -z "$dest" ] && dest=/apex/$dest || return 1
         [ -d "$dest" ] || mkdir -p "$dest"
         echo "$(basename $dest)=$apex" >> "$list"
         echo2 " Mounting:folder: $dest" && mount -o bind "$apex" $dest
    fi
}

umount_apex() {
  test -d /apex || return
  local dest loop
  local list="$di_apex_list"
  for dest in $(cat "$list" | cut -d'=' -f1); do
    dest=/apex/$dest
    if [ -d $dest ]; then
       loop=$(mount |  grep $dest |  cut -d" " -f1)
       echo2 " Unmounting $dest "
       umount -ld $dest
       losetup -d $loop 2>/dev/null
    fi
  done
  echo2 " Unmounting /apex "
  umount -l /apex
  echo2 " Unmounting /linkerconfig "
  umount -l /linkerconfig 2>/dev/null
  for dest in ANDROID_RUNTIME_ROOT ANDROID_ART_ROOT \
  ANDROID_TZDATA_ROOT ANDROID_I18N_ROOT ANDROID_DATA \
  BOOTCLASSPATH DEX2OATBOOTCLASSPATH
  do
     unset $dest
  done
  export APEX_MOUNT_PASS=false
}

genre_apex_list() {
   local module_path name apex list count=0
   local list2="$di_apex_list"
   [ -z "$1" ] && list=/apex/apex-info-list.xml || list="$1"
   echo '<?xml version="1.0" encoding="utf-8"?>' > "$list"
   echo '<apex-info-list>' >> "$list"
   for apex in $(cat "$list2" | cut -d'=' -f1); do
     apex=/apex/$apex
     if [ -d $apex ]; then
         name=$(basename $apex)
         module_path=$(get_file_prop "$list2" $name)
         if [ -e "$module_path" ]; then
            count=$(( $count + 1))
            echo " <apex-info moduleName=\"$name\" modulePath=\"$module_path\" preinstalledModulePath=\"$module_path\" isFactory=\"true\" isActive=\"true\" />" >> "$list"
         fi
     fi
   done
   echo '</apex-info-list>' >> "$list"
   if [[ -f "$list" && $count != 0 ]]; then return 0; else delete "$list" && return 1; fi
}

unlock_all() {
   #superrepack by @munjeni
   #unlock_all for Dynamic Installer by @BlassGO
   [ "$(superrepack teSt >/dev/null; echo $?)" == "100" ] && return 1
   local super try=5 count=0 return=0 space current result
   local log=/data/unlock_all.log
   local log2=/data/local/tmp/script.log
   space="/data/superrepack" 
   current=${PWD}
   [ ! -e "$1" ] && super=$(find_block -e super) || super="$1"
   [ -z "$super" ] && echo2 "unlock_all: CANT find super partition " && return 1
   delete_recursive "$space"
   delete "$log"
   mkdir -p "$space"
   if ! exist folder "$space"; then echo2 "unlock_all: CANT make $space" && return 1; fi
   cd "$space"
   startlog "$log"
   echolog '>>> Unlock_ALL On-Fly 1.0.0'
   echo2 " "
   echolog " -- Converting $super to RW..."
   echolog " "
   while true; do
      result=
      sleep 1
      [[ "$count" == "$try" ]] && echolog "FATAL ERROR: CANT ENSURE $super as RW with $count attemps" && return=1 && break
      echolog "unlock_all:Attempt: $count"
      count=$(($count + 1))
      superrepack "$super" >> "$log" 2>&1
      result=$?
      savelog "----------------script.log----------------"
      if exist "$log2"; then cat "$log2" >> "$log"; fi
      if [[ "$result" == "0" ]]; then
         if grep -q "Could not allocate block" "$log2"; then continue; else break; fi
      fi
   done
   cd "$current"
   delete_recursive "$space"
   return $return
}

unlock() {
   #superrepack by @munjeni
   #unlock for Dynamic Installer by @BlassGO
   [ "$(superrepack teSt >/dev/null; echo $?)" == "100" ] && return 1
   local super try=5 count=0 return=0 space current result
   local log=/data/unlock.log
   local log2=/data/local/tmp/script.log
   space="/data/superrepack" 
   current=${PWD}
   [ ! -e "$2" ] && super=$(find_block -e super) || super="$2"
   [ -z "$1" ] && echo2 "unlock: Undefined subpartition" && return 1
   [ -z "$super" ] && echo2 "unlock: CANT find super partition " && return 1
   delete_recursive "$space"
   delete "$log"
   mkdir -p "$space"
   if ! exist folder "$space"; then echo2 "super_rw: CANT make $space" && return 1; fi
   cd "$space"
   startlog "$log"
   echolog '>>> Unlock On-Fly 1.0.0'
   echo2 " "
   echolog " -- Converting:$1 in $super to RW..."
   echolog " "
   while true; do
      result=
      sleep 1
      [[ "$count" == "$try" ]] && echolog "FATAL ERROR: CANT ENSURE $1 as RW with $count attemps" && return=1 && break
      echolog "unlock:Attempt: $count"
      count=$(($count + 1))
      superrepack "$super" "$1" >> "$log" 2>&1
      result=$?
      savelog "----------------script.log----------------"
      if exist "$log2"; then cat "$log2" >> "$log"; fi
      if [[ "$result" == "0" ]]; then
         if grep -q "Could not allocate block" "$log2"; then continue; else break; fi
      fi
   done
   cd "$current"
   delete_recursive "$space"
   return $return
}

superrepack() {
   local return=0
   ( delete "$TMP/vbmeta.img"
     write_raw_image "$(find_block -e vbmeta)" "$TMP/vbmeta.img") > /dev/null 2>&1
   if ! can_run superrepack; then echo2 "FATAL ERROR: CANT LOAD superrepack" && return=100; fi
   if [ "$return" != 100 ]; then "$l/superrepack" "$@"; return=$?; fi
   echo " -- Restoring vbmeta.img"
   is_valid "$TMP/vbmeta.img" && write_raw_image "$TMP/vbmeta.img" "$(find_block vbmeta)" > /dev/null 2>&1
   return $return
}

get_virtual_points() {
   local LOOP point try super
   super=$(find_block -e super)
   if undefined super && [ -z "$1" ]; then echo2 "CANT FIND SUPER PARTITION" && return 1
   elif [ -n "$1" ] && ! checksuper "$1"; then echo2 "FATAL ERROR: Invalid SUPER: $1" && return 1
   fi
   [ -n "$1" ] && super="$1"
   echo2 '-- Loading virtual points... '
   for point in system vendor product odm system_ext; do
      try="$point$slot"
      start_loop "$try" "$super" 2>/dev/null
      if defined LOOP; then all_looped_loop+=("$LOOP") && echo2 "$point=$LOOP" && setdefault "$point" "$LOOP"; else echo2 "NO POINT: $point "; fi
   done
}

get_size_ext4() { string inside ',' 'bytes' "$(fdisk -l "$1" | grep -m1 "Disk")" | tr -d " "; }

get_offset() {
  #get_offset "partition name inside SUPER" "SUPER partition/image"
  local head offset
  [ "$(superrepack teSt >/dev/null; echo $?)" == "100" ] && return 1
  [[ -z "$1" || -z "$2" ]] && return 1
  head=$(string -r -p "$1" complete_extract "partition_" "Partition:" "$(superrepack "$2" noneeded 2>/dev/null)")
  offset=$(string inside "offset =" ')' "$(echo "$head" | grep "dumping offset")" | tr -d " ")
  offset=$(printf "%d\n" "$offset" 2>/dev/null || echo FAILED)
  if ! is_substring "FAILED" "$offset"; then echo "$offset" && return 0; else return 1; fi
}

get_size() {
  #get_size "partition name inside SUPER" "SUPER partition/image"
  local head size
  [ "$(superrepack teSt >/dev/null; echo $?)" == "100" ] && return 1
  [[ -z "$1" || -z "$2" ]] && return 1
  head=$(string -r -p "$1" complete_extract "partition_" "Partition:" "$(superrepack "$2" noneeded 2>/dev/null)")
  size=$(string inside '(' 'bytes total' "$(echo "$head" | grep "bytes total")" | tr -d " ")
  size=$(printf "%d\n" "$size" 2>/dev/null || echo FAILED)
  if ! is_substring "FAILED" "$size"; then echo "$size" && return 0; else return 1; fi
}

get_total_size() {
  #get_total_size "SUPER partition/image"
  local head size bytes total=0 error=0
  [ "$(superrepack teSt >/dev/null; echo $?)" == "100" ] && return 1
  [ ! -e "$1" ] && return 1
  while read bytes; do
     undefined bytes && break
     size=$(string inside '(' 'bytes total' "$bytes" | tr -d " ")
     size=$(printf "%d\n" "$size" 2>/dev/null || echo FAILED)
     if ! is_substring "FAILED" "$size"; then total=$(calc $total + $size); else error=$(($error + 1)); fi
  done <<< $(superrepack "$1" noneeded 2>/dev/null | grep "bytes total")
  if [[ "$error" == "0" ]] && is_number "$total" && is_greater "$total" 0; then echo "$total" && return 0; else return 1; fi
}

get_all_subparts() {
  local parts
  [ "$(superrepack teSt >/dev/null; echo $?)" == "100" ] && return 1
  [ ! -e "$1" ] && return 1
  superrepack "$1" noneeded 2>/dev/null | grep -E "partition_[0-9]*_name" | cut -d= -f2 | tr -d " "
}

get_group() {
  #get_group "partition name inside SUPER" "SUPER partition"
  local head group
  [ "$(superrepack teSt >/dev/null; echo $?)" == "100" ] && return 1
  [[ -z "$1" || -z "$2" ]] && return 1
  head=$(string -r -p "$1" complete_extract "partition_" "Partition:" "$(superrepack "$2" noneeded 2>/dev/null)")
  group=$(echo "$head" | grep "partition_group" | cut -d'=' -f2 | tr -d " ")
  if defined group; then echo "$group" && return 0; else return 1; fi
}

checksuper() {
  #checksuper "SUPER partition/image"
  [ "$(superrepack teSt >/dev/null; echo $?)" == "100" ] && return 1
  [ -z "$1" ] && return 1
  if ! is_substring "This is not super image" "$(superrepack "$1" noneeded 2>/dev/null)"; then true; else false; fi
}

start_loop() {
    local loop=$(losetup2 -f) size offset
    [ "$(superrepack teSt >/dev/null; echo $?)" == "100" ] && return 1
    if ! can_run losetup2; then echo2 "FATAL ERROR: CANT LOAD losetup2" && return 1; fi
    if undefined loop; then echo2 "Cant make loop" && return 1; fi
    if ! checksuper "$2"; then echo2 "start_loop: Invalid SUPER: $2" && return 1; fi
    size=$(get_size "$1" "$2")
    offset=$(get_offset "$1" "$2")
    if ! defined size offset || ! is_number "$size" || ! is_number "$offset"; then echo2 "CANT GET INFO FROM: $2" && return 1; fi
    losetup2 --offset=$offset --sizelimit=$size $loop "$2"
    if losetup2 -a | grep -q "$loop"; then all_loopkskqiiq+=("$loop") && echo2 "$loop" && LOOP="$loop"; else return 1; fi
}

end_loop() {
   local i refresh
   LOOP=
   [[ -z "${all_loopkskqiiq[${#all_loopkskqiiq[@]} - 1]}" && -z "$1" ]] && return 1
   if [ -z "$1" ]; then
      losetup -d "${all_loopkskqiiq[${#all_loopkskqiiq[@]} - 1]}"
      unset "all_loopkskqiiq[${#all_loopkskqiiq[@]}-1]"
      for i in "${!all_loopkskqiiq[@]}"; do
         refresh+=("${all_loopkskqiiq[i]}")
      done
      all_loopkskqiiq=("${refresh[@]}")
   else
      losetup -d "$1"
   fi
}

unify_path() {
   local check eq1 eq2 only return=0
   touch "$2/testrw" 2>/dev/null
   if exist "$2/testrw"; then
      delete "$2/testrw"
   else
      echo "unify_path: $2 Read/Only or Invalid" && return 1
   fi
   while read check; do
      if is_substring " differ" "$check"; then
         eq1=$(string inside "Files " " and" "$check")
         eq2=$(string inside " and " " differ" "$check")
         if defined eq1 eq2 && exist "$eq1" "$eq2"; then
            echo2 "Replacing: $eq1 in $eq2"
            if ! copy "$eq1" "$eq2"; then echo2 "FATAL ERROR: Cant unify $eq1" && return=1; fi
         fi
      elif is_substring "Only in $1" "$check"; then
         only=$(string remove "Only in $1: " "$check")
         if defined only && exist "$1/$only"; then
            echo2 "Adding: $1/$only in $2/$only"
            if ! copy "$1/$only" "$2/$only"; then echo2 "FATAL ERROR: Cant unify $1/$only" && return=1; fi
         fi
      fi
   done <<< $(diff -qr "$1" "$2")
   return $return
}

super_rw() {
   #super_rw for Dynamic Installer by @BlassGO
   #superunpack by @munjeni
   #Inspired on @lebigmac systemrw project
   ui_print " "
   ui_print " Oops! "
   ui_print " super_rw function was discontinued, its effectiveness is not good on all devices"
   ui_print " "
   return
   [ "$(superrepack teSt >/dev/null; echo $?)" == "100" ] && return 1
   if ! can_run superunpack; then ui_print "FATAL ERROR: CANT LOAD superunpack" && return 1; fi
   if ! can_run losetup2; then ui_print "FATAL ERROR: CANT LOAD losetup2" && return 1; fi
   local try count return=0 result block size size512 part newsize error reference all_blocks
   local restore flag force boot extract baseblock add add2 try in in2 custom custom512 test loop needed total free super LOOP nw
   local current=${PWD}
   local log=/data/super_rw.log
   local log2=/data/super_rw.tmp
   local space="/data/super_rw"
   local extra_space=100
   restore=()
   while [[ $# -gt 0 ]]; do
     flag="$1"
     case $flag in
         -f|-force)
         force=true
         shift
         ;;
         -nw|-no-wipe)
         nw=true
         shift
         ;;
         -b|-bootmode)
         boot=true
         shift
         ;;
         -s|-super)
         super="$2"
         shift;shift;
         ;;
         -a|-add)
         if is_substring "MB" "$2"; then add=$(string remove MB "$2" | awk '{print int($1 * 1024 * 1024)}'); else add="$2"; fi
         if ! is_number "$2" && ! is_greater "$2" 0; then ui_print "super_rw: Invalid line" && return 1; fi
         shift;shift;
         ;;
         -ai|-add-in)
         if [[ -z "$2" || -z "$3" ]]; then ui_print "super_rw: Invalid line" && return 1; fi
         in+=("$2")
         if is_substring "MB" "$3"; then
            test=$(string remove MB "$3" | awk '{print int($1 * 1024 * 1024)}')
            if is_number "$test" && is_greater "$test" 0; then in2+=("$test"); else ui_print "super_rw: Invalid line" && return 1; fi
         elif is_number "$3" && is_greater "$3" 0; then
            in2+=("$3")
         else
            ui_print "super_rw: Invalid line" && return 1
         fi
         shift;shift;shift;
         ;;
         *)   
         restore+=("$1")
         shift
         ;;
     esac
   done
   set -- "${restore[@]}"
   [ -z "$super" ] && super=$(find_block -e super)
   [ -z "$super" ] && ui_print "super_rw: CANT find super partition " && return 1
   if ! checksuper "$super"; then ui_print "super_rw: Invalid SUPER: $super" && return 1; fi
   delete_recursive "$space"
   delete "$log"
   mkdir -p "$space"
   if ! exist folder "$space"; then ui_print "super_rw: CANT make $space" && return 1; fi
   cd "$space"
   startlog "$log"
   printlog '>>> Super_rw 1.0.0'
   #Mb to Bytes
   extra_space=$(calc $extra_space '* (1024^2)')
   #Total bytes + extra bytes
   total=$(calc $(get_total_size "$super") + $extra_space)
   #Bytes to GB
   needed=$(calc $total / '(1024^3)' )
   #Get free Kilobytes in space
   free=$(df -P "$space" | tail -1 | awk '{print $4}')
   #Kilobytes to GB
   free=$(calc $free / '(1024^2)')
   printlog " "
   printlog "super: $super "
   printlog "free_space: $free GB"
   printlog "needed_space: $needed GB"
   printlog " "
   #Check space
   if ! is_greater "$free" "$needed"; then printlog "super_rw: NO free space to extract SUPER image" && return 1; fi
   if undefined boot && $BOOTMODE && grep -q "^super_rw" "$TMP/updater-script" > /dev/null 2>&1; then
     ui_print " "
     ui_print "DI: Please install this ZIP with a Custom Recovery"
     ui_print "DI: This will allow the installer to unlock your partitions"
     ui_print " "
     [ $CUSTOM_SETUP = 1 ] && return 1 || abort
   elif undefined boot && $BOOTMODE; then
     ui_print " "
     ui_print "DI: Please add super_rw in the updater-script"
     ui_print "DI: Then install the ZIP with a Custom Recovery"
     ui_print "DI: This will allow the installer to unlock your partitions"
     ui_print " "
     [ $CUSTOM_SETUP = 1 ] && return 1 || abort
   fi
   printlog " -- Extracting:$super in $space..."
   superunpack "$super" 1 >> "$log" 2>&1
   while read part; do
         if undefined part; then printlog "FATAL ERROR: CANT FIND EXTRACTED IMGs" && break; fi
         count=0
         error=0
         size=
         size512=
         custom=
         custom512=
         baseblock=
         add2=
         baseblock=$(string remove ".ext4" "$(basename "$part")")
         if undefined baseblock; then printlog "FATAL ERROR: Cant get valid info from $part" && continue; fi
         if is_substring "shared_blocks" "$(tune2fs -l "$part" | grep "features")" || defined force; then
            printlog " -- Converting:$baseblock to RW..."
            size=$(get_size_ext4 "$part")
            size512=$(calc $size / 512)
            undefined size size512 && printlog "cant get size: $part" && continue
            printlog "$baseblock:size: $size"
            printlog "$baseblock:size512: $size512"
            newsize=$(calc '('$size' * 1.25) / 512')
            savelog "CALCULATED:$newsize"
            if ! resize2fs -f "$part" $newsize"s" >> "$log" 2>&1; then error=$(($error + 1)) && printlog "some error: resizing $part"; fi
            savelog " UNSHARING BLOCKS "
            e2fsck -y -E unshare_blocks "$part" >> "$log" 2>&1
            if is_substring "shared_blocks" "$(tune2fs -l "$part" | grep "features")"; then error=$(($error + 1)) && printlog "some error: unsharing block $part"; fi
            savelog " SHRINKING: MINIMUN SIZE"
            if ! resize2fs -f -M "$part" >> "$log" 2>&1; then error=$(($error + 1)) && printlog "some error: shrinking $part"; fi
            if is_less "$(get_size_ext4 "$part")" "$size"; then
               savelog " RESTORING SIZE "
               if ! resize2fs -f "$part" $size512"s" >> "$log" 2>&1; then error=$(($error + 1)) && printlog "some error: resizing $part"; fi
            fi
            if defined add; then
               size=$(get_size_ext4 "$part")
               custom=$(calc $size + $add)
               custom512=$(calc $custom / 512)
               printlog "$baseblock:custom_size: $custom"
               printlog "$baseblock:custom_size512: $custom512"
               if ! resize2fs -f "$part" $custom512"s" >> "$log" 2>&1; then error=$(($error + 1)) && printlog "some error: resizing $part"; fi
            fi
            if defined in; then
               for i in "${!in[@]}"; do
                  if is_substring "${in[i]}" "$baseblock"; then add2=${in2[i]} && break; fi
               done
               if defined add2; then
                  size=$(get_size_ext4 "$part")
                  custom=$(calc $size + $add2)
                  custom512=$(calc $custom / 512)
                  printlog "$baseblock:custom_size: $custom"
                  printlog "$baseblock:custom_size512: $custom512"
                  if ! resize2fs -f "$part" $custom512"s" >> "$log" 2>&1; then error=$(($error + 1)) && printlog "some error: resizing $part"; fi
               fi
            fi
            savelog " EXTRA FIX"
            e2fsck -fy "$part" >> "$log" 2>&1
            start_loop "$baseblock" "$super"
            loop="$LOOP"
            if ! defined loop; then echolog "some error: looping $block" && continue; fi
            printlog "$baseblock:loop: $loop"
            printlog " -- Unifying:$baseblock with $loop"
            try_mount -f "$loop" /mnt/stock_part
            try_mount -f "$part" /mnt/new_part
            if [ -z "$(ls -A /mnt/stock_part)" ]; then printlog "Cant mount: /mnt/stock_part" && continue; fi
            if [ -z "$(ls -A /mnt/new_part)" ]; then printlog "Cant mount: /mnt/new_part" && continue; fi
            if ! unify_path /mnt/stock_part /mnt/new_part >> "$log" 2>&1; then error=$(($error + 1)) && printlog "some error: unifying $part"; fi
            unmount /mnt/stock_part
            unmount /mnt/new_part
            printlog " -- Updating:$baseblock..."
            dd if="$part" of="$loop" bs=1M >> "$log" 2>&1
            losetup2 -c "$loop" >> "$log" 2>&1
            savelog " FINISHING"
            ( blockdev --setrw "$loop"
            end_loop "$loop" ) >> "$log" 2>&1
            size=$(get_size_ext4 "$part")
            size512=$(calc $size / 512)
            printlog "$baseblock:newsize: $size"
            printlog "$baseblock:newsize512: $size512"
            printlog "$baseblock:ended_with_error:$error"
            savelog "$(tune2fs -l "$part" | grep "features")"
            if [[ "$error" != "0" ]]; then return=1; fi
            printlog " "
        else
            printlog " -- Skipping:$baseblock..." && continue
        fi
        echo2 " "
   done <<< $(find "$space" -mindepth 1 -maxdepth 1 -type f -name "*.ext4")
   cd "$current"
   if [ -z "$nw" ]; then
      savelog " -- Wiping..."
      delete_recursive "$space"
   fi
   endlog
   return $return
}

ensure_root() {
   local result pass=false opt
   [ "$mount_all" = "ro" ] && opt=" -ro "
   free_root=$((df -Pk / | sed "1d" | awk '{print $4}') 2>/dev/null)
   [ -n "$free_root" ] && free_root=$(calc "$free_root * 1024")
   if is_number "$free_root" && ! is_greater "$free_root" "6144"; then
      ui_print " "
      ui_print 'DI: Oops! Your free space in root "/" is not enough to perform any basic operation'
      ui_print " "
      abort " "
   fi
   mount -o rw,remount -t auto / 2>/dev/null; testrw /; result=$?
   if [ $result == 0 ]; then
      pass=true
      try_mount $opt -n system /system_root || try_mount $opt -e -n system-verity /system_root; testrw /system_root; result=$?
   fi
   if [ -n "$opt" -o $result == 0 ]; then
      free_system=$((df -Pk /system_root | sed "1d" | awk '{print $4}') 2>/dev/null)
      [ -n "$free_system" ] && free_system=$(calc "$free_system * 1024")
      if is_number "$free_system" && ! is_greater "$free_system" "10485760"; then
         echo2 " "
         echo2 'DI: The free space in "system" is very low (Less than or equal to 10MB), I recommend you check the changes that required more free space'
         echo2 " "
      fi
      return 0
   elif [ "$ensure_root" != "off" -a $result == 2 ]; then
      ui_print " "
      ui_print 'DI: Read/Only partitions...'
      if $pass || $BOOTMODE; then 
         ui_print "DI: Your current SYSTEM.img are locked..."
         ui_print 'DI: You need to use a ROM/GSI that supports mounting as Read/Write'
      else
         ui_print "DI: Your current root \"/\" are locked..."
      fi
      ui_print " "
      abort " "
   else
      ($pass || $BOOTMODE) && echo2 "DI: ENSURE_ROOT FAILED: /system and /system_root are NOT writable!" || echo2 "DI: ENSURE_ROOT FAILED: root \"/\" are NOT writable!"
      return 1
   fi
}

mount_all() { auto_mount_partitions; }

mount_all_old() {
  local runtime dest part block bind
  if ! is_mounted /data; then
     mount /data
    UMOUNT_DATA=1
  fi
  #Ensure root space
  mount -o rw,remount -t auto / 2>/dev/null
  (mount /cache
  mount -o ro -t auto /persist
  mount -o ro -t auto /product
  mount -o ro -t auto /vendor
  mount -o ro -t auto /odm
  mount -o ro -t auto /system_ext) 2>/dev/null
  setup_mountpoint $ANDROID_ROOT
  if ! is_mounted $ANDROID_ROOT; then
    mount -o ro -t auto $ANDROID_ROOT 2>/dev/null
  fi
  case $ANDROID_ROOT in
    /system_root) setup_mountpoint /system;;
    /system)
      if ! is_mounted /system && ! is_mounted /system_root; then
        setup_mountpoint /system_root
        mount -o ro -t auto /system_root
      elif [ -f /system/system/build.prop ]; then
        setup_mountpoint /system_root
        mount --move /system /system_root
      fi
      if [ $? != 0 ]; then
        umount /system
        umount -l /system 
        if [ "$dynamic_partitions" = "true" ]; then
          mount -o ro -t auto /dev/block/mapper/system$slot /system_root
          for part in system_ext vendor product odm; do
             block=/dev/block/mapper/$part$slot
             if [ -e $block ]; then
                setup_mountpoint /$part
                mount -o ro -t auto $block /$part
             fi
          done
        else
          mount -o ro -t auto /dev/block/bootdevice/by-name/system$slot /system_root
        fi
      fi
    ;;
  esac
  if is_mounted /system_root; then
    contains_array "/system_root" "${all_looped[@]}" || all_looped+=("/system_root")
    if [ -f /system_root/build.prop ]; then
      echo2 " Mounting:bind: /system_root /system"
      mount -o bind /system_root /system
      contains_array "/system" "${all_binded_part[@]}" || all_binded_part+=("/system")
    else
      echo2 " Mounting:bind: /system_root/system /system"
      mount -o bind /system_root/system /system
      contains_array "/system" "${all_binded_part[@]}" || all_binded_part+=("/system")
    fi
    for bind in /system/system_ext /system/product /system/vendor; do
       if [ ! -L "$bind" ]; then
         echo2 " Mounting:bind: $bind /$(basename "$bind")"
         setup_mountpoint "/$(basename "$bind")"
         mount -o bind "$bind" "/$(basename "$bind")"
         contains_array "/$(basename "$bind")" "${all_binded_part[@]}" || all_binded_part+=("/$(basename "$bind")")
       fi
    done
  fi
  if is_mounted /vendor; then
     for bind in /vendor/odm; do
       if [ ! -L "$bind" ]; then
         echo2 " Mounting:bind: $bind /$(basename "$bind")"
         setup_mountpoint "/$(basename "$bind")"
         mount -o bind "$bind" "/$(basename "$bind")"
         contains_array "/$(basename "$bind")" "${all_binded_part[@]}" || all_binded_part+=("/$(basename "$bind")")
       fi
    done
  fi
}

umount_all() {
  local part umount post extra result=0 loop restore simple_umt
  mount -o rw,remount -t auto / 2>/dev/null
  (if $BOOTMODE; then
     for umount in /system_ext /vendor /product /odm; do
         if ! contains_array "$umount" "${all_umount[@]}"; then all_umount+=("$umount"); fi
     done
     for simple_umt in "${all_binded_part[@]}"; do
        if is_mounted "$simple_umt"; then
           echo2 " Unmounting $simple_umt "
           umount -l "$simple_umt" 2>/dev/null
        fi
     done
     for loop in "${all_looped[@]}"; do
       if is_mounted "$loop"; then
          echo2 " Unmounting $loop "
          umount -ld "$loop" 2>/dev/null
          if is_mounted "$loop"; then echo2 " CANT UNMOUNT: $loop"; fi
       fi
     done
     for loop in "${all_looped_loop[@]}"; do
       losetup -d "$loop" 2>/dev/null
     done
     for restore in "${all_umount[@]}" "${all_looped[@]}"; do
       if [ -L "${restore}_link" ]; then
          rmdir "$restore"
          mv -f "${restore}_link" "$restore"
       fi
     done
     for part in "${all_umount[@]}"; do
        if is_mounted "$part"; then
           if ! try_mount -ro "$part"; then
              for restore in "${all_mounted_part[@]}"; do
                 blockdev --setro "$restore" 2>/dev/null
              done
              if ! try_mount -ro "$part"; then
                 echo2 " "
                 echo2 " CANT RESTORE: $part as Read/Only "
                 echo2 " Oops! It is dangerous!  please save your information, you might have a brick after reboot"
                 echo2 " "
              fi
           fi
        fi
     done
  else
     for umount in /system_ext /system /system_root /cache /persist /vendor /product /odm; do
         if ! contains_array "$umount" "${all_umount[@]}"; then all_umount+=("$umount"); fi
     done
     $APEX_MOUNT_PASS && umount_apex
     for simple_umt in "${all_binded_part[@]}"; do
        if is_mounted "$simple_umt"; then
           echo2 " Unmounting $simple_umt "
           umount -l "$simple_umt" 2>/dev/null
        fi
     done
     for part in "${all_umount[@]}"; do
       if is_mounted "$part"; then
          echo2 " Unmounting $part "
          unmount "$part" || post+=("$part")
       fi
     done
     for loop in "${all_looped[@]}"; do
       if is_mounted "$loop"; then
          echo2 " Unmounting $loop "
          umount -ld "$loop" 2>/dev/null || post+=("$loop")
       fi
     done
     for loop in "${all_looped_loop[@]}"; do
       losetup -d "$loop" 2>/dev/null
     done
     for extra in "${post[@]}"; do
        unmount "$extra"
        if is_mounted "$extra"; then echo2 " CANT UNMOUNT: $extra"; fi
     done
     for restore in /apex /linkerconfig "${all_umount[@]}" "${all_looped[@]}"; do
       if [ -L "${restore}_link" ]; then
          rmdir "$restore"
          mv -f "${restore}_link" "$restore"
       fi
     done
  fi
  $BOOTMODE && mount -o ro,remount -t auto / 2>/dev/null
  if [ "$UMOUNT_DATA" ]; then
    umount /data
    umount -l /data
  fi)
  $BOOTMODE || umount -l /dev/random 2>/dev/null
  unset all_binded_part all_umount all_looped all_looped_loop all_mounted_part
  return $result
}

destroy_env() {
   [ -z "$OLD_LD_PATH" ] && OLD_LD_PATH="$LD_LIBRARY_PATH"
   [ -z "$OLD_LD_PRELOAD" ] && OLD_LD_PRELOAD="$LD_PRELOAD"
   [ -z "$OLD_LD_CONFIG" ] && OLD_LD_CONFIG="$LD_CONFIG_FILE"
   unset LD_LIBRARY_PATH
   unset LD_PRELOAD
   unset LD_CONFIG_FILE
}

restore_env() {
  [ -n "$OLD_LD_PATH" ] && export LD_LIBRARY_PATH="$OLD_LD_PATH"
  [ -n "$OLD_LD_PRELOAD" ] && export LD_PRELOAD="$OLD_LD_PRELOAD"
  [ -n "$OLD_LD_CONFIG" ] && export LD_CONFIG_FILE="$OLD_LD_CONFIG"
}

dynamic_install(){
   local f inst_dir origin
   origin=$(readlink -f "$1")
   [ ! -d "$origin" ] && return 1
   while read f; do
     undefined f && break
     inst_dir=${f/"$origin"/"$2"}
     if [ -d "$f" ]; then
        create_dir "$inst_dir" || return 1
        continue
     fi
     inject "$f" "$(dirname "$inst_dir")" || return 1
   done <<< $(find -L "$origin" -mindepth 1)
}
	
inject() {
  local file dest return=0
  file=$(basename "$1")
  dest="$2/$file"
  [ ! -f "$1" ] && return 1
  create_dir "$2" || return 1
  testrw "$2" || return 1
  if install -D "$1" "$dest" >/dev/null 2>&1 || cp -prf "$1" "$dest"; then
     if ! set_perm2 $di_uid $di_gid $di_perm_f "$dest"; then echo2 "inject: Cant set permissions in: $dest file" && return=1; fi
     if ! set_perm2 $di_uid $di_gid $di_perm_d "$2"; then echo2 "inject: Cant set permissions in: $2 folder" && return=1; fi
  else
     echo2 "Cant inject: $file" && return=1
  fi
  return $return
}

auto_mount_partitions() {
    local main block extra noexist bind opt
    [ "$mount_all" = "ro" ] && opt=" -ro "
    if ! $BOOTMODE; then
      mount -o bind /dev/urandom /dev/random
      umount_all
      mount_all_old
    fi
    if [ "$dynamic_partitions" = "true" ]; then
       find /dev/block/mapper -mindepth 1 -maxdepth 1 \( -type b -o -type c -o -type l \) | while read block; do
          if undefined block; then echo2 "WARNING: CANT FIND /dev/block/mapper partitions" && break; fi
          blockdev --setrw "$block"
       done
    fi
    #Ensure root space
    ensure_root
    if $BOOTMODE; then
       if is_mounted /system_root; then
          if [ -f /system_root/build.prop ]; then
            echo2 " Mounting:bind: /system_root /system"
            mount -o bind /system_root /system
            contains_array "/system" "${all_binded_part[@]}" || all_binded_part+=("/system")
          else
            echo2 " Mounting:bind: /system_root/system /system"
            mount -o bind /system_root/system /system
            contains_array "/system" "${all_binded_part[@]}" || all_binded_part+=("/system")
          fi
          for bind in /system/system_ext /system/product /system/vendor; do
             if [ ! -L "$bind" ]; then
               echo2 " Mounting:bind: $bind /$(basename "$bind")"
               mount -o bind "$bind" "/$(basename "$bind")"
               contains_array "/$(basename "$bind")" "${all_binded_part[@]}" || all_binded_part+=("/$(basename "$bind")")
             fi
          done
       else
          echo2 " CANT ENSURE: /system_root "
       fi
       for extra in system_ext vendor product odm; do
          if ! contains_array "/$extra" "${all_binded_part[@]}" && exist $(find_block -e $extra); then
             try_mount $opt "/$extra"
          fi
       done
       if is_mounted /vendor; then
          for bind in /vendor/odm; do
            if [ ! -L "$bind" ]; then
              echo2 " Mounting:bind: $bind /$(basename "$bind")"
              setup_mountpoint "/$(basename "$bind")"
              mount -o bind "$bind" "/$(basename "$bind")"
              contains_array "/$(basename "$bind")" "${all_binded_part[@]}" || all_binded_part+=("/$(basename "$bind")")
            fi
         done
       fi
    else
       for main in system system_ext vendor product odm; do
          if ! is_mounted "/$main"; then
             if [[ "$main" == "system" ]]; then 
                ensure_root
                if is_mounted /system_root; then
                   setup_mountpoint /system
                   if [ -f /system_root/build.prop ]; then
                      echo2 " Mounting:bind: /system_root /system"
                      mount -o bind /system_root /system
                      contains_array "/system" "${all_binded_part[@]}" || all_binded_part+=("/system")
                   else
                      echo2 " Mounting:bind: /system_root/system /system"
                      mount -o bind /system_root/system /system
                      contains_array "/system" "${all_binded_part[@]}" || all_binded_part+=("/system")
                   fi
                   for bind in /system/system_ext /system/product /system/vendor; do
                      if [ ! -L "$bind" ]; then
                        echo2 " Mounting:bind: $bind /$(basename "$bind")"
                        setup_mountpoint "/$(basename "$bind")"
                        mount -o bind "$bind" "/$(basename "$bind")"
                        contains_array "/$(basename "$bind")" "${all_binded_part[@]}" || all_binded_part+=("/$(basename "$bind")")
                      fi
                   done
                else
                   echo2 " CANT ENSURE: /system_root "
                fi
             else
                if exist $(find_block -e $main); then
                   contains_array "/$main" "${all_binded_part[@]}" || try_mount -remount "/$main"
                else
                   noexist+=("$main")
                fi
             fi
             if contains_array "$main" "${noexist[@]}"; then
                echo2 " No block: $main "
             elif ! is_mounted "/$main"; then
                echo2 " CANT MOUNT: /$main "
             fi
          else
             echo2 " Mounted:main: /$main"
          fi
       done
       if is_mounted /vendor; then
          for bind in /vendor/odm; do
            if [ ! -L "$bind" ] && ! contains_array "/$(basename "$bind")" "${all_binded_part[@]}"; then
              echo2 " Mounting:bind: $bind /$(basename "$bind")"
              setup_mountpoint "/$(basename "$bind")"
              mount -o bind "$bind" "/$(basename "$bind")"
              all_binded_part+=("/$(basename "$bind")")
            fi
         done
       fi
    fi
    if [[ -n "$apex_mount" && "$apex_mount" != "off" ]]; then 
       $BOOTMODE || mount_apex
    elif ! $BOOTMODE; then
       runtime=$(find /system/apex -name "com.android.runtime*" -print | head -n 1)
       [[ -z "$runtime" && -d /system_ext/apex ]] && runtime=$(find /system_ext/apex -name "com.android.runtime*" -print | head -n 1)
       if [ -z "$runtime" ]; then echo2 "FATAL ERROR: Cant find RUNTIME"
       else apex_mount "$runtime"
       fi
    fi
    [ -z "$opt" ] && ( mount -o rw,remount -t auto /system || mount -o rw,remount -t auto /
    mount -o rw,remount -t auto /vendor 
    mount -o rw,remount -t auto /product
    mount -o rw,remount -t auto /odm
    mount -o rw,remount -t auto /system_ext
    mount -o remount,rw /system_root ) 2>/dev/null
}
if [ -z "$BOOTMODE" ]; then
   ps | grep zygote | grep -qv grep && export BOOTMODE=true || export BOOTMODE=false
   $BOOTMODE || ps -A 2>/dev/null | grep zygote | grep -qv grep && export BOOTMODE=true
fi
if [ -z "$di_version" ]; then
   if [ -f "$installzip" ]; then
      di_version=$(package_extract_file META-INF/zbin/version.txt 2>/dev/null | grep_o "[0-9]+[.][0-9]+[-]*[[:alnum:]]*" | head -n1)
   elif [ -f "$1/version.txt" ]; then
      di_version=$(grep_o "[0-9]+[.][0-9]+[-]*[[:alnum:]]*" "$1/version.txt" 2>/dev/null | head -n1)
   elif [ -f version.txt ]; then
      di_version=$(grep_o "[0-9]+[.][0-9]+[-]*[[:alnum:]]*" version.txt 2>/dev/null | head -n1)
   fi
fi
test "$di_version" && main_version=$(echo "$di_version" | cut -d- -f1)
test "$ANDROID_ROOT" || ANDROID_ROOT=/system
if test ! "$CUSTOM_SETUP"; then [ -z "$installzip" ] && export CUSTOM_SETUP=1 || export CUSTOM_SETUP=0; fi
getarch

export di_apex_list="$TMP/apex_list_0001.txt"
export binary_paths=$(echo -e "/sbin\n/system/bin")
export APEX_MOUNT_PASS=false
export yes=chooseport
export n='
'
PS1="di-$main_version> "
shopt -s expand_aliases
alias not=!

setdefault permissions "0:0:0755:0777"

#Recovery Mode + Test Mode Implementation
#(Maintenance or just testing for Devs)
if [ $CUSTOM_SETUP = 1 ]; then
   export MYPATH="$1"
   if ! $BOOTMODE; then
      export apex_mount=on
      echo " >> Recovery Mode 1.1.2 "
   else
      export PATH="/data/data/com.termux/files/usr/bin:/data/data/com.termux/files/usr/bin/applets:/sbin:/sbin/su:/su/bin:/su/xbin:/system/bin:/system/xbin:$PATH"
      echo " >> Test Mode 1.7.2 "
   fi
   echo " @BlassGO "
   echo " "
   [ -d "$MYPATH" ] && echo " -- Working from: $MYPATH"
   echo " -- Loading environment..."
   echo " "
   
   #Start
   needed=true; ensure_bin unzip mkdir chmod mv
   needed=false; ensure_bin rm cp

   if defined TMPDIR Dynamic_Setup; then export TMP="$TMPDIR"
   else
       for TMP in /tmp /data/tmp /dev/tmp /cache/tmp /mnt/tmp; do
          (mkdir -p $TMP; testrw $TMP) 2>/dev/null && break
       done
   fi
   if ! testrw "$TMP"; then echo2 "FATAL ERROR: Failed to create temporary directory" && exit 1; fi

   #Global vars
   export TMPDIR="$TMP"
   export DNM="META-INF/com/google/android/magisk"
   export cert="META-INF/zbin/version.txt"
   export addons="$TMP/addons"
   export l="$TMP/ugu"

   #Magisk vars
   export ZIPFILE="$3"
   export PERSISTDIR=/sbin/.magisk/mirror/persist

   bb="$TMP/busybox"; bin="$TMP/bin.xz"; extra="$TMP/extra.zip"
   
   #Remove only zbin plugins
   ( rm -rf "$l" "$TMP/zbin"
     rm -f "$bb" "$TMP/bin" "$bin" "$extra" "$TMP/core"
   ) 2>/dev/null
   
   #Create work DIRs
   mkdir -p "$l" "$TMP/zbin" "$addons"
   if [ ! -d "$l" -o ! -d "$TMP/zbin" ]; then
     echo "ERROR: Cant startup"; exit 1
   fi
   
   #Getting zbin plugins
   if exist "$MYPATH/busybox" "$MYPATH/bin"; then
      copy "$MYPATH/bin" "$bin"
      copy "$MYPATH/busybox" "$bb"
   elif exist busybox bin; then
      copy busybox "$bb"
      copy bin "$bin"
   else
      echo "ERROR: CANT FIND BUSYBOX or BIN"
      exit 1
   fi
   
   #Getting extra.zip (Optional)
   if exist "$MYPATH/extra.zip"; then
      copy "$MYPATH/extra.zip" "$extra"
   elif exist extra.zip; then
      copy extra.zip "$extra"
   fi
   
   #Getting configs
   for config in file_types units; do
      if exist "$MYPATH/configs/${config}.config"; then
         copy "$MYPATH/configs/${config}.config" "$l"
      elif exist configs/${config}.config; then
         copy configs/${config}.config "$l"
      fi
   done
   unset config
   
   #Permissions
   chmod 755 "$bb" "$bin" "$extra" 2>/dev/null
   
   #First ensuring Busybox environment
   setup_bb; export PATH="$l:$PATH"
   
   #Loading bin
   xz -d "$bin" >/dev/null 2>&1
   if [ ! -f "$TMP/bin" ]; then echo "ERROR: CANT EMULATE BIN" && exit 1; fi;
   unzip -qo "$TMP/bin" -d "$TMP/zbin"
   import_info "$TMP/zbin/info.txt"
  
   #Loading extra.zip (Optional)
   if [ -f "$TMP/extra.zip" ]; then
      unzip -qo "$TMP/extra.zip" -d "$TMP/zbin"
      import_info "$TMP/zbin/info.txt"
   fi
  
   #Finish
   find "$TMP/zbin" -type f -exec mv -f {} "$l" \;
   find "$l" -type f -exec chmod 755 {} +;
fi

#Get AVB state
#getsecurity

#Checking TMP2 (For Dual functions operations)
start_tmp
if [ ! -d "$TMP2" ]; then abort "CANT SETUP: TMP2"; fi
tmp2="$TMP2"
end_tmp
if [ -d "$tmp2" ]; then abort "CANT SETUP:2: TMP2"; fi
unset tmp2

#Try using external unzip binary
change_bin unzip -while -p

#Checking possible exceptions (Read/Only Functions)
pattern=off_readonly
eval "$pattern() { :; }"
if defined installzip; then
   while read ole; do
      undefined ole && break
      exception+=("$ole")
   done <<< $(split_string " " "$((string -r remove "$pattern " "$(
   package_extract_file META-INF/com/google/android/updater-script | grep "^$pattern "
   package_extract_file META-INF/com/google/android/magisk/customize.sh | grep "^$pattern "
   package_extract_file META-INF/com/google/android/magisk/install.sh | grep "^$pattern "
   )"
   )2>/dev/null)"
   )
fi
unset pattern ole

if [ $CUSTOM_SETUP != 1 ]; then
   #Ensuring Dynamic Installer functions
   for func in $(declare -F 2>/dev/null | awk '{ print $3; }'); do
      if contains_array "$func" "${exception[@]}" 2>/dev/null; then echo2 "readonly:off: $func"; else readonly -f $func 2>/dev/null; fi
   done
   unset func

   #Ensuring Dynamic Installer vars
   vars="TMP TMPDIR OUTFD DNM addons l n yes di_apex_list"
   for var in $vars; do
      if contains_array "$var" "${exception[@]}" 2>/dev/null; then echo2 "readonly:off: $var"; else undefined $var || readonly $var 2>/dev/null; fi
   done
fi
unset vars var exception

#Load Virtual points
#get_virtual_points

#Destroy Recovery env
$BOOTMODE || destroy_env

#Importing APEX binaries
if $BOOTMODE && [ -d /apex ]; then
  for bin in /apex/*/bin/*; do
     [ ! -e "$bin" ] && continue 
     [ ! -e "$l/$(basename "$bin")" ] && ln -sf "$bin" "$l/$(basename "$bin")"
  done
fi
unset bin

#Check Custom Mode
if [ $CUSTOM_SETUP = 1 ]; then setdefault permissions "0:0:0755:0644"; return; fi

#Pre-setup
package_extract_file META-INF/com/google/android/updater-script "$TMP/updater-script" || abort "FATAL ERROR: Cant get updater-script"
package_extract_dir META-INF/zbin/configs "$l"
devices=$(getdefault "$TMP/updater-script" devices)
apex_mount=$(getdefault "$TMP/updater-script" apex_mount)
magisk_support=$(getdefault "$TMP/updater-script" magisk_support)
ensure_root=$(getdefault "$TMP/updater-script" ensure_root)
extraction_speed=$(getdefault "$TMP/updater-script" extraction_speed)
permissions=$(getdefault "$TMP/updater-script" permissions)
import_addons=$(getdefault "$TMP/updater-script" import_addons)
echo2 "---------Installer Configs----------"
for info in devices apex_mount magisk_support ensure_root import_addons extraction_speed permissions; do
   if defined $info; then echo2 "$info=$(checkvar $info)"; else echo2 "$info undefined!"; fi
done
echo2 "-----------------------------------"
unset info
if [[ -n "$devices" && "$devices" != "off" ]]; then
    dcount=false
    may="
    $(getprop ro.product.device 2>/dev/null) 
    $(getprop ro.build.product 2>/dev/null) 
    $(getprop ro.product.vendor.device 2>/dev/null) 
    $(getprop ro.vendor.product.device 2>/dev/null)
    $(get_file_prop /default.prop ro.product.device 2>/dev/null)
    $(getprop ro.product.model 2>/dev/null) 
    $(grep_cmdline androidboot.em.model 2>/dev/null) 
    "
    while read huh; do
       $dcount && break
       undefined huh && continue
       while read ugu; do
         undefined ugu && continue
         if [[ "$huh" == "$ugu" ]]; then
            dcount=true
            echo2 "Device checking: [$ugu] == [$huh]"
            break
         else
            echo2 "Device checking: [$ugu] != [$huh]"
         fi
       done <<< $(split_string : "$devices" | sort -bu)
    done <<< $(echo "$may" | sort -bu)
    if ! $dcount; then abort " -- Incompatible device: Its only for $(ugu=false; try=$(split_string : "$devices" | sort -bu | while read huh; do $ugu && echo -n " and "; echo -n "\"$huh\""; ugu=true; done); echo -n "$try"; is_substring " and " "$try" && echo -n " devices" || echo -n " device")"; fi;
fi
unset may huh ugu dcount

#Extracting addons after the device checking pass
package_extract_dir META-INF/addons "$addons"

#Ensure/Check new default permissions
setdefault permissions "$permissions" || setdefault permissions "0:0:0755:0644"

#Importing addons(.sh) if needed
if [[ "$import_addons" == "on" ]]; then
    while read addon; do
       if exist "$addon"; then
          echo2 "- Importing \"$(basename "$addon")\""
          . "$addon"
       fi
    done <<< $(find "$addons" -mindepth 1 -maxdepth 1 -type f -name "*.sh" | sort)
    unset addon
fi

if [[ "$magisk_support" == "off" ]] || (! $BOOTMODE && [[ "$magisk_support" != "force" ]]); then
echo2 "----------------Running SCRIPTs------------"
. "$TMP/updater-script"
remove_tmp
restore_env
echo2 "-------------------------------------------"

else

require_new_magisk() {
    if ! $BOOTMODE && $encrypted; then
       ui_print "*******************************"
       ui_print " Please decrypt the device!    "
       ui_print "*******************************"
    else
       ui_print "*******************************"
       ui_print " Please install Magisk v19.0+! "
       ui_print "*******************************"
    fi
    abort
}

mount /data 2>/dev/null
if [ $CUSTOM_SETUP != 2 ]; then
   [ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
   . /data/adb/magisk/util_functions.sh
   [ $MAGISK_VER_CODE -lt 19000 ] && require_new_magisk
fi

magisk_print() {
  ui_print "----------------------------------------"
  ui_print ' >>> Powered by Magisk '$MAGISK_VER_CODE
  ui_print "----------------------------------------"
  ui_print " "
}

is_legacy_script() {
  unzip -l "$ZIPFILE" $DNM/install.sh | grep -q install.sh
  return $?
}

setup_flashable() {
  $BOOTMODE && return
  if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
    # We will have to manually find out OUTFD
    for FD in `ls /proc/$$/fd`; do
      if readlink /proc/$$/fd/$FD | grep -q pipe; then
        if ps | grep -v grep | grep -qE " 3 $FD |status_fd=$FD"; then
          OUTFD=$FD
          break
        fi
      fi
    done
  fi
  recovery_actions
}


install_module() {
  
  cd $TMPDIR

  setup_flashable
  mount_partitions
  api_level_arch_detect
  
  # Setup busybox and binaries
  if $BOOTMODE; then
    boot_actions
  else
    recovery_actions
  fi
  
  # Extract prop file
  package_extract_file $DNM/module.prop $TMPDIR/module.prop
  [ ! -f $TMPDIR/module.prop ] && abort "! Unable to extract zip file!"
  
  local MODDIRNAME=modules
  $BOOTMODE && MODDIRNAME=modules_update
  local MODULEROOT=$NVBASE/$MODDIRNAME
  MODID=`grep_prop id $TMPDIR/module.prop`
  MODNAME=`grep_prop name $TMPDIR/module.prop`
  MODAUTH=`grep_prop author $TMPDIR/module.prop`
  MODPATH=$MODULEROOT/$MODID

  # Create mod paths
  rm -rf $MODPATH
  mkdir -p $MODPATH

  if is_legacy_script; then
    package_extract_dir $DNM $TMPDIR
    # Load install script
    magisk_print
    . $TMPDIR/install.sh

    # Callbacks
    on_install

    [ -f $TMPDIR/uninstall.sh ] && cp -af $TMPDIR/uninstall.sh $MODPATH/uninstall.sh
    $SKIPMOUNT && touch $MODPATH/skip_mount
    $PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop
    cp -af $TMPDIR/module.prop $MODPATH/module.prop
    $POSTFSDATA && cp -af $TMPDIR/post-fs-data.sh $MODPATH/post-fs-data.sh
    $LATESTARTSERVICE && cp -af $TMPDIR/service.sh $MODPATH/service.sh
    
    echo2 "- Setting permissions"
    set_permissions
  else
    magisk_print

    package_extract_file $DNM/customize.sh $MODPATH/customize.sh

    if ! grep -q '^SKIPUNZIP=1$' $MODPATH/customize.sh 2>/dev/null; then
      package_extract_dir $DNM $MODPATH

      # Default permissions
      echo2 "- Setting common permissions/contexts"
      echo2 " "
      set_perm_recursive2 $di_uid $di_gid $di_perm_d $di_perm_f $MODPATH
      ch_con_recursive system_file system_file $MODPATH
      for perm in /system/bin /system/xbin /system/system_ext/bin /system/vendor/bin; do
         if [ -d "$MODPATH$perm" ]; then
            set_perm_recursive2 0 2000 0755 0755 "$MODPATH$perm"
         fi
      done
      if [ -d "$MODPATH/system/vendor" ]; then ch_con_recursive vendor_file vendor_file "$MODPATH/system/vendor"; fi
      unset perm
    fi

    # Load customization script
    [ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh
  fi

  # Handle replace folders
  for TARGET in $REPLACE; do
    echo2 "- Replace target: $TARGET"
    mktouch $MODPATH$TARGET/.replace
  done

  if $BOOTMODE; then
    # Update info for Magisk app
    mktouch $NVBASE/modules/$MODID/update
    rm -rf $NVBASE/modules/$MODID/remove 2>/dev/null
    rm -rf $NVBASE/modules/$MODID/disable 2>/dev/null
    cp -af $MODPATH/module.prop $NVBASE/modules/$MODID/module.prop
  fi

  # Copy over custom sepolicy rules
  if [ -f $MODPATH/sepolicy.rule ]; then
    echo2 "- Installing custom sepolicy rules"
    copy_sepolicy_rules
  fi

  # Remove stuff that doesn't belong to modules and clean up any empty directories
  rm -rf \
  $MODPATH/system/placeholder $MODPATH/customize.sh \
  $MODPATH/README.md $MODPATH/.git*
  rmdir -p $MODPATH

  cd /
  
  $BOOTMODE || recovery_cleanup
  rm -rf $TMPDIR

}

install_custom_module() {
  [ -z "$MODPATH" ] && abort "! Invalid implementation: MODPATH is not defined"
  
  if is_legacy_script; then
    package_extract_dir $DNM $TMPDIR
    
    # Load install script
    . $TMPDIR/install.sh

    # Callbacks
    on_install
    
    [ -f $TMPDIR/uninstall.sh ] && cp -af $TMPDIR/uninstall.sh $MODPATH/uninstall.sh
    $PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop
    $POSTFSDATA && cp -af $TMPDIR/post-fs-data.sh $MODPATH/post-fs-data.sh
    $LATESTARTSERVICE && cp -af $TMPDIR/service.sh $MODPATH/service.sh
    
    echo2 "- Setting permissions"
    set_permissions
  else
    package_extract_file $DNM/customize.sh $MODPATH/customize.sh

    if ! grep -q '^SKIPUNZIP=1$' $MODPATH/customize.sh 2>/dev/null && [ $SKIPUNZIP != 1 ]; then
      package_extract_dir $DNM $MODPATH

      # Default permissions
      echo2 "- Setting common permissions/contexts"
      echo2 " "
      set_perm_recursive2 $di_uid $di_gid $di_perm_d $di_perm_f $MODPATH
      ch_con_recursive system_file system_file $MODPATH
      for perm in /system/bin /system/xbin /system/system_ext/bin /system/vendor/bin; do
         if [ -d "$MODPATH$perm" ]; then
            set_perm_recursive2 0 2000 0755 0755 "$MODPATH$perm"
         fi
      done
      if [ -d "$MODPATH/system/vendor" ]; then ch_con_recursive vendor_file vendor_file "$MODPATH/system/vendor"; fi
      unset perm
    fi

    # Load customization script
    [ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh
  fi

  # Remove stuff that doesn't belong to modules and clean up any empty directories
  rm -rf \
  $MODPATH/system/placeholder $MODPATH/customize.sh \
  $MODPATH/README.md $MODPATH/.git*
  rmdir -p $MODPATH
  
  rm -rf $TMPDIR
}

if [ $CUSTOM_SETUP = 2 ]; then
   echo2 "----------------Running SCRIPTs------------"
   install_custom_module
   echo2 "-------------------------------------------"
elif [ $MAGISK_VER_CODE -ge 20400 ]; then
   # New Magisk have complete installation logic within util_functions.sh
   echo2 "----------------Running SCRIPTs------------"
   install_module
   echo2 "-------------------------------------------"
fi

restore_env
exit 0

fi

