#! /bin/bash

###############################################################################
# UExtract v3.18 by SFR'2013-2016                                             #
# GNU GPL v2 applies                                                          #
###############################################################################

set -o pipefail

APPNAME="UExtract v3.18"
MYPATH="$(dirname "$(readlink -f "$0")")"
PATH="${MYPATH}/resources:${PATH}"
LD_LIBRARY_PATH="${MYPATH}/resources:${LD_LIBRARY_PATH}"

export TEXTDOMAINDIR="${MYPATH}/locale"
export TEXTDOMAIN=uextract
export OUTPUT_CHARSET=UTF-8

OLDDIR="$(pwd)"
WORKDIR=/tmp/uextract_${$}
trap 'func_cleanup' EXIT	# Clean up things on exit

################################ FUNCTIONS ####################################

func_cleanup () {
  cd "$OLDDIR"
  umount "$WORKDIR" 2>/dev/null
  [ "$FREELOOP" ] && $LOSETUP -d "$FREELOOP" 2>/dev/null
  [ "`which qemu-nbd 2>/dev/null`" ] && [ "$ARCHPATH" ] && [ "$NBD_DEV" ] && qemu-nbd -d /dev/nbd${NBD_DEV} >/dev/null 2>&1
  case "`basename "${ARCHEXT##*.}"`" in
    gc|gst)	CMND="$GOSTCRYPT"	;;
    hc|vc)	CMND="$VERACRYPT"	;;
    tc)		CMND="$TRUECRYPT"	;;
    *)		CMND=''				;;
  esac
  [ "$CMND" ] && [ "`which $CMND 2>/dev/null`" ] && [ "$ARCHPATH" ] && [ "`$CMND -t -l 2>/dev/null | grep ".*${ARCHPATH} "`" ] && $CMND -d "$ARCHPATH" 2>/dev/null
  [ "$MAPPER_NAME" ] && [ -e "/dev/mapper/${MAPPER_NAME}" ] && cryptsetup close "/dev/mapper/${MAPPER_NAME}"
  rm -rf "$WORKDIR" 2>/dev/null
  echo -ne "\e[00m"
}

# -----------------------------------------------------------------------------

func_hr () {
  printf -- "${1}%.0s" `eval echo {2..$([ $(which tput 2>/dev/null) ] && tput cols || stty size | cut -f2 -d ' ')}`
}

# -----------------------------------------------------------------------------

func_precheck () {
  local RET=0
  for i in "$@"; do
    [ "`which $i 2>/dev/null`" ] || { echo -e "${REDCOL}$(gettext 'ERROR:') ${BLUECOL}${i}${OFFCOL} $(gettext 'is not installed or not executable!')"; RET=1; }
  done
  return $RET
}

# -----------------------------------------------------------------------------

func_list_cp_mntpt () {
  case "$MODE" in
    list)	find "$WORKDIR" -printf '%M %u:%g %s\r\t\t\t\t\t%P\n' || return 1	;;
    *)		if [ "`which stdbuf 2>/dev/null`" ]; then
              stdbuf -o0 -e0 cp -ravi "${WORKDIR}/." -t "$1" | sed -e "s|${WORKDIR}/./||" -e 's/ -> .*//' || return 1
			else
			  cp -ravi "${WORKDIR}/." -t "$1" || return 1
			fi
	;;
  esac
}

# -----------------------------------------------------------------------------

func_mnt_disk_image () {
  INFO="$(fdisk -lu "$1" 2>/dev/null)"
  UNIT="$(echo "$INFO" | grep '^Units.*bytes' | grep -oE '[0-9]+ bytes' | tr -c -d [:digit:])"
  PARTITIONS="$(echo "$INFO" | grep "^$1" | grep -vE 'Extended$| swap' | tr -d '*' | tr -s ' ' | cut -f2 -d ' ')"
  [ "${PARTITIONS}" = "" ] || [[ "${PARTITIONS}" == *"?"* ]] && return 1
  CNT=0
  for OFFSET in $PARTITIONS; do
    mount -o loop,ro,offset=$((OFFSET*UNIT)) "$1" "$WORKDIR" || { ERROR=1; continue; }
    ((CNT++))
    echo -e "${BLUECOL}$(gettext 'Partition')_${CNT}:${OFFCOL}"
    if [ ! $(find $WORKDIR -maxdepth 0 -type d -empty) ]; then
      [ "$MODE" != "list" ] && mkdir -p "$(gettext 'Partition')_${CNT}"
      func_list_cp_mntpt "$(gettext 'Partition')_${CNT}" || ERROR=1
    else
      echo -e "${YELLOWCOL}$(gettext 'WARNING:') ${OFFCOL}$(gettext 'Partition does not contain any files, it is empty!')"
    fi
    umount "$WORKDIR" 2>/dev/null
    echo
  done
  [ $ERROR -eq 0 ] && return 0 || return 1
}

# -----------------------------------------------------------------------------
#The Heart of the Script!

func_uextract () {  
  if [ "$MODE" != "list" ]; then
    DESTDIR="${CUSTOMDIR}/$(basename "$ARCHPATH").$(gettext 'extracted')"
    CNT=1
    TMP="$DESTDIR" 
    while [ -e "$TMP" ]; do
      TMP="$DESTDIR($CNT)"
      ((CNT++))
    done
    DESTDIR="$TMP"
    mkdir -p -- "$DESTDIR"	|| { echo -e "\n${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'Cannot create destination directory!')"; return 1; }
    cd -- "$DESTDIR"		|| { echo -e "\n${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'Cannot access destination directory!')"; return 1; }
  fi
  
  ERROR=0; FREELOOP=''
  ARCHEXT="$(echo "$ARCHPATH" | tr [:upper:] [:lower:])"
    
  # Try to determine .run installers that have no .run extension
  [ "`echo "${ARCHPATH##*.}" | tr '[:upper:]' '[:lower:]'`" = "sh" ] && ARCHEXT=".run" 
  [ ! "`echo "${ARCHPATH##*.}" | tr '[:upper:]' '[:lower:]' | grep -E 'package|shar|shr'`" ] && [[ "`head -n2 "$ARCHPATH" | file -b - | cut -f1 -d ','`" == *"shell script"* ]] && [[ "`grep -o -m1 '' "$ARCHPATH"`" == "Binary"* ]] && ARCHEXT=".run"
  ([ "`file -b "$ARCHPATH" | grep -i '^ELF'`" ] && [ "$(od -v -An -tc -j32769 -w5 -N5 "$ARCHPATH" 2>/dev/null | tr -d ' \n')" = "CD001" ]) && ARCHEXT=".run"

  # Determine fusecompress(ed) files and apply fake extension
  [ "`file -b "$ARCHPATH" | grep -i 'FuseCompress'`" ] && ARCHEXT="fusecompress"
  
  # Determine UPX-ed files
  [ "`which upx 2>/dev/null`" ] && [ "`upx -l "$ARCHPATH" 2>&1 | grep -w ' .*Exception:'`" = "" ] && ARCHEXT="upx"
  
  # Gostcrypt/Truecrypt/Veracrypt volume check, if no extension
  if ([ "`which $GOSTCRYPT 2>/dev/null`" ] || [ "`which $TRUECRYPT 2>/dev/null`" ] || [ "`which $VERACRYPT 2>/dev/null`" ]) && [ "`basename "${ARCHPATH##*.}"`" = "`basename "${ARCHPATH}"`" ] && [ "`file -b "$ARCHPATH" | cut -f1 -d ','`" = "data" ]; then
    TSIZE=`stat -c %s "$ARCHPATH"`
    if [ $(( ${TSIZE} % 512 )) -eq 0 ]; then
      echo -ne "${BLUECOL}$(gettext "Possible Gostcrypt/Truecrypt/Veracrypt volume, checking...")${OFFCOL}"
      [ ${TSIZE} -ge $((1024*256)) ] && TSIZE=$((1024*256))	# too slow, only the first 256KiB
      ENTROPY=( `printf "0 %.0s" {0..255}` )
      while read -r FREQ BYTE; do
        ENTROPY[${BYTE}]=$(( (FREQ * 10000) / TSIZE ))
      done <<< "`od -v -N${TSIZE} -An -tu1 -w1 "$ARCHPATH" | sort -n | uniq -c | tr -s ' '`"
      ENTROPY=`echo ${ENTROPY[@]} | tr ' ' '\n' | sort -nr | head -n 1`
      if [ ${ENTROPY} -lt 50 ]; then
        echo -e "${GREENCOL}$(gettext "OK!")${OFFCOL}\n"
        read -p "$(gettext 'Use Gostcrypt, Truecrypt or Veracrypt (g/t/v)?') " -n1 KEY
        case "$KEY" in
          g|G)	ARCHEXT=".gc"	;;
          t|T)	ARCHEXT=".tc"	;;
          v|V)	ARCHEXT=".vc"	;;
        esac
        echo; echo
      else
        echo -e "${REDCOL}$(gettext "No!")${OFFCOL}\n"
      fi
    fi
  fi
  
  # Let's begin...
  case "$ARCHEXT" in
	# -----------
    *.tar.7z)
		func_precheck $SEVENZ tar || return 1
		$SEVENZ -so x "$ARCHPATH" 2>/dev/null | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }	# hack for exit code 141 ('set -o pipefail' + usually piping through (but not only!) tail/head pair produces that!)
	;;
	# -----------
    *.tar.bz|*.tar.bz2|*.tar.gz2|*.tb2|*.tbz|*.tbz2|*.slp|*.xjtbz2)
		[ "$(head -c 3 "$ARCHPATH")" = "BZ0" ] && CMND=bunzip || CMND="$BUNZIP2"
		func_precheck $CMND tar || return 1
		$CMND -c -d "$ARCHPATH" | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
    *.tar.f)
		func_precheck $UNFREEZE tar || return 1
		$UNFREEZE -c -d "$ARCHPATH" | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
    *.tar.gz|*.tar.gzip|*.tg|*.tgz|*.depot.gz|*.emerald|*.epk|*.fat|*.gtp|*.iar|*.nif|*.oar|*.obt|*.qpk|*.qpr|*.rub|*.unitypackage|*.vbox-extpack|*.xjtgz)
		func_precheck $GUNZIP tar || return 1
		$GUNZIP -c -d "$ARCHPATH" | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.tar.lz4)
	    func_precheck $LZ4 tar || return 1
	    $LZ4 -d "$ARCHPATH" | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }	    
	;;
	# -----------
	*.tar.lz|*.tlz)
	    func_precheck $LUNZIP tar || return 1
	    $LUNZIP -c -d "$ARCHPATH" | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }	    
	;;
	# -----------
    *.tar.lzma|*.tlzma|*.tzma)
		func_precheck $UNLZMA tar || return 1
		$UNLZMA -c -d "$ARCHPATH" | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
    *.tar.lzo|*.tzo)
		func_precheck $UNLZOP tar || return 1
		$UNLZOP -c -d "$ARCHPATH" | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
    *.tar.xz|*.txz|*.tpkg)
		func_precheck $UNXZ tar || return 1
		$UNXZ -c -d "$ARCHPATH" | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
    *.tar.z|*.tarz|*.tz|*.taz)
		func_precheck $UNCOMPRESS tar || return 1
		$UNCOMPRESS -c -d "$ARCHPATH" | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.tar.zip)
	    func_precheck tar || return 1
	    func_precheck unzip && { unzip -p "$ARCHPATH" | tar $TAR_OPTS - && return 0; }
	    echo -e "\n${BLUECOL}$(gettext 'Trying') '${SEVENZ}'...${OFFCOL}"
	    func_precheck $SEVENZ || return 1
	    $SEVENZ x -so "$ARCHPATH" 2>/dev/null | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.7z)
		func_precheck $SEVENZ cpio || return 1
		$SEVENZ x -so "$ARCHPATH" 2>/dev/null | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.bz|*.cpio.bz2)
		[ "$(head -c 3 "$ARCHPATH")" = "BZ0" ] && CMND=bunzip || CMND="$BUNZIP2"
		func_precheck $CMND cpio || return 1
		$CMND -c -d "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.f)
		func_precheck $UNFREEZE cpio || return 1
		$UNFREEZE -c -d "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.gz|*.cpgz|*.cgz)
		func_precheck $GUNZIP cpio || return 1
		$GUNZIP -c -d "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.lz4)
		func_precheck $LZ4 cpio || return 1
		$LZ4 -d "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.lz)
		func_precheck $LUNZIP cpio || return 1
		$LUNZIP -c -d "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.lzma)
		func_precheck $UNLZMA cpio || return 1
		$UNLZMA -c -d "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.lzo)
		func_precheck $UNLZOP cpio || return 1
		$UNLZOP -c -d "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.xz)
		func_precheck $UNXZ cpio || return 1
		$UNXZ -c -d "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.z)
		func_precheck $UNCOMPRESS cpio || return 1
		$UNCOMPRESS -c -d "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cpio.zip)
	    func_precheck cpio || return 1
	    func_precheck unzip && { unzip -p "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS && return 0; }
	    echo -e "\n${BLUECOL}$(gettext 'Trying') '${SEVENZ}'...${OFFCOL}"
	    func_precheck $SEVENZ || return 1
	    $SEVENZ x -so "$ARCHPATH" 2>/dev/null | cpio $CPIO_OPTS $BOPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.adz|*.blend|*.gnumeric|*.graphmlz|*.jgz|*.psz|*.svgz|*.vgz|*.wrz|*.x3d|*.xcfgz|*.zabw|*.mo|*.dms|*.pack|*.wv|*.xcfbz2|*.aes|*.b64|*.balz|*.bbb|*.bfe|*.bgz|*.bz|*.bz2|*.cpt|*.dia|*.emz|*.f|*.flzp|*.gz|*.gz2|*.gzi|*.gzip|*.igz|*.lpaq1|*.lpaq8|*.lz4|*.lz|*.lzma|*.lzo|*.lrz|*.mpz|*.ogz|*.quad|*.rz|*.sfe|*.tor|*.xz|*.z|*.zl)
	    case "${ARCHEXT##*.}" in
	      adz)		EXT=.adf		;;
	      blend)	EXT=.blend		;;
	      gnumeric) EXT=.gnumeric	;;
	      graphmlz)	EXT=.graphml	;;
	      jgz)		EXT=.js			;;
	      psz)		EXT=.ps			;;
	      svgz)		EXT=.svg		;;
	      vgz)		EXT=.vgm		;;
	      wrz)		EXT=.wrl		;;
	      x3d)		EXT=.x3d		;;
	      xcfgz)	EXT=.xcf		;;
	      zabw)		EXT=.abw		;;
	      mo)		EXT=.po			;;
	      dms)		EXT=.adf		;;
	      pack)		EXT=.jar		;;
	      wv)		EXT=.wav		;;
	      xcfbz2)	EXT=.xcf		;;
	      *)		EXT=''			;;
	    esac
	    [ "$MODE" = "list" ] && { echo "$(basename "${ARCHPATH%.*}")${EXT}" && return 0 || return 1; }
	    echo "$(gettext 'Extracting...')"
	    case "${ARCHEXT##*.}" in
	      # -----------
	      adz|blend|gnumeric|graphmlz|jgz|psz|svgz|vgz|wrz|x3d|xcfgz|zabw)
	        func_precheck $GUNZIP || return 1
	        $GUNZIP -S ".${ARCHEXT##*.}" -c -d -v "$ARCHPATH" > "$(basename "${ARCHPATH%.*}")${EXT}" || { rm -f -- "$(basename "${ARCHPATH%.*}").${EXT}"; return 1; }
	      ;;
	      # -----------
	      mo)
	        func_precheck msgunfmt || return 1
		    msgunfmt -v "$ARCHPATH" -o "./$(basename "${ARCHPATH%.*}")${EXT}" || { rm -f -- "$(basename "${ARCHPATH%.*}")${EXT}"; return 1; }
	      ;;
	      # -----------
	      dms)
	      	func_precheck undms || return 1
			undms "$ARCHPATH" "$(basename "${ARCHPATH%.*}")${EXT}" || { rm -f -- "$(basename "${ARCHPATH%.*}")${EXT}"; return 1; }	      
	      ;;
	      # -----------
	      pack)
			func_precheck unpack200 || return 1
			unpack200 -v "$ARCHPATH" "$(basename "${ARCHPATH%.*}")${EXT}" || { rm -f -- "$(basename "${ARCHPATH%.*}")${EXT}"; return 1; }	      
	      ;;
	      # -----------
	      wv)
			func_precheck wvunpack || return 1
			wvunpack -o . "$ARCHPATH" || return 1
	      ;;
	      # -----------
	      xcfbz2)
			func_precheck $BUNZIP2 || return 1
			$BUNZIP2 -c -d -v "$ARCHPATH"  > "$(basename "${ARCHPATH%.*}")${EXT}" || { rm -f -- "$(basename "${ARCHPATH%.*}")${EXT}"; return 1; }
	      ;;
	      # -----------
	      aes)
	          func_precheck aescrypt || return 1
	          aescrypt -d -o ./"$(basename "${ARCHPATH%.*}")" "$ARCHPATH" || return 1
	      ;;
	      # -----------
	      b64)
			  func_precheck base64 || return 1
			  base64 -d "$ARCHPATH" > "$(basename "${ARCHPATH%.*}")" || return 1	        
	      ;;
	      # -----------
	      balz)
			  func_precheck balz || return 1
			  balz d "$ARCHPATH" "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }	        
	      ;;
		  # -----------
		  bbb|flzp|lpaq1|lpaq8)
		      CMND="${ARCHEXT##*.}"	# will be bbb or flzp or lpaq1 or lpaq8
		      func_precheck $CMND || return 1
		      $CMND d "$ARCHPATH" "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  ;;
	      # -----------
	      bfe)
			  func_precheck bcrypt || return 1
			  bcrypt -o "$ARCHPATH" > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }	        
	      ;;
	      # -----------
	      bz|bz2|gz2)
			  [ "$(head -c 3 "$ARCHPATH")" = "BZ0" ] && CMND="bunzip" || CMND="$BUNZIP2"
			  func_precheck $CMND || return 1
			  $CMND -c -d -v "$ARCHPATH" > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }        
	      ;;
	      # -----------
	      cpt)
	          func_precheck ccrypt || return 1
	          ccrypt -d -c "$ARCHPATH" > ./"$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }	        
	      ;;
	      # -----------
	      f)
			  func_precheck $UNFREEZE || return 1
			  $UNFREEZE -c -d "$ARCHPATH" > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
	      ;;
	      # -----------
		  gz|gzi|gzip|emz|bgz|dia|mpz|ogz)
			  func_precheck $GUNZIP || return 1
			  $GUNZIP -S ".${ARCHEXT##*.}" -c -d -v "$ARCHPATH" > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  ;;
		  # -----------
		  igz)
		      [ "$(file -b "$ARCHPATH" | cut -f1 -d ',' | grep -wi 'gzip')" ] && CMND="$GUNZIP" || CMND="$UNXZ"
		      func_precheck $CMND || return 1
		      $CMND -c -d -v "$ARCHPATH" > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  ;;
		  # -----------
		  lrz)
			  func_precheck $LRUNZIP || return 1
			  $LRUNZIP -d "$ARCHPATH" -O . || return 1
		  ;;
	      # -----------
	      lz4)
			  func_precheck $LZ4 || return 1
			  $LZ4 -d "$ARCHPATH" > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
	      ;;
	      # -----------
	      lz)
			  func_precheck $LUNZIP || return 1
			  $LUNZIP -c -d -v "$ARCHPATH"  > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  ;;
	      # -----------
	      lzma)
			  func_precheck $UNLZMA || return 1
			  $UNLZMA -c -d -v "$ARCHPATH"  > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  ;;
	      # -----------
	      lzo)
			  func_precheck $UNLZOP || return 1
			  $UNLZOP -c -d -v "$ARCHPATH"  > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  ;;
		  # -----------
		  quad)
			  func_precheck quad || return 1
			  quad -d "$ARCHPATH" "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  ;;
		  # -----------
		  rz)
		      func_precheck $RUNZIP || return 1
		      $RUNZIP -k -d "$ARCHPATH" -o "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  ;;
	      # -----------
	      sfe)
			  func_precheck scrypt || return 1
			  scrypt dec "$ARCHPATH" "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }	        
	      ;;
	      # -----------
	      tor)
			  func_precheck tor || return 1
			  tor "$ARCHPATH" -o"$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
	      ;;
		  # -----------
		  xz)
			  func_precheck $UNXZ || return 1
			  $UNXZ -c -d -v "$ARCHPATH"  > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  ;;
		  # -----------
		  z)
			  func_precheck $UNCOMPRESS || return 1
			  $UNCOMPRESS -c -d "$ARCHPATH"  > "$(basename "${ARCHPATH%.*}")" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  ;;
		  # -----------
		  zl)
			  func_precheck $GUNZIP || return 1
			  ERRLOG=$( { printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" | cat - "${ARCHPATH}" | $GUNZIP -c -d -v > "$(basename "${ARCHPATH%.*}")"; } 2>&1)
			  [ $? -ne 0 ] && [ "$(echo "$ERRLOG" | grep -v 'unexpected end of file')" != "" ] && { echo "$ERRLOG"; rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
		  # -----------
	    esac
	;;
	# -----------
	*.2fs|*.3fs|*.4fs)
	    FREELOOP="$($LOSETUP -f)"
	    [ "$FREELOOP" = "" ] && return 1
		EXT=ext${ARCHEXT:$((${#ARCHEXT}-3)):1}		# 2fs -> ext2, 3fs -> ext3, 4fs -> ext4    
	    modprobe -a cryptoloop aes_generic aes crypto_blkcipher cbc 2>/dev/null	# for encrypted savefiles
	    case "$ARCHEXT" in
	      *_cryptx*) 	[ "`which losetup-klibc 2>/dev/null`" ] && { echo -n "$(gettext "Password: ")"; losetup-klibc -p0 -e 1 "$FREELOOP" "$ARCHPATH"; }	|| $LOSETUP -E 1 "$FREELOOP" "$ARCHPATH"	;;
	      *_crypta*) 	[ "`which losetup-klibc 2>/dev/null`" ] && { echo -n "$(gettext "Password: ")"; losetup-klibc -p0 -e aes "$FREELOOP" "$ARCHPATH"; }	|| $LOSETUP -e aes "$FREELOOP" "$ARCHPATH"	;;
	      *)			$LOSETUP "$FREELOOP" "$ARCHPATH" ;;
	    esac
	    mount -t "$EXT" -o loop,ro "$FREELOOP" "$WORKDIR" || ERROR=1
	    if [ $ERROR -eq 0 ]; then
		  if [ ! $(find $WORKDIR -maxdepth 0 -type d -empty) ]; then
		    func_list_cp_mntpt . || ERROR=1
		  else
		    echo -e "${YELLOWCOL}$(gettext 'WARNING:') ${OFFCOL}$(gettext 'Savefile does not contain any files, it is empty!')"
		  fi
	    fi
	    umount "$WORKDIR" 2>/dev/null
	    $LOSETUP -d "$FREELOOP" 2>/dev/null
	    FREELOOP=''
	;;
	# -----------
    *.7z|*.7z.001|.7zip|*.a|*.ar|*.a[0-9][0-9]|*.arj|*.cab|*.cb7|*.cpl|*.crx|*.dll|*.dmg|*.exe|*.fwp|*.hme|*.icl|*.lha|*.lza|*.lzh|*.mct|*.msg|*.msi|*.msp|*.msu|*.mzz|*.nex|*.ndr|*.ngr|*.nlr|*.nsis|*.onepkg|*.safariextz|*.scr|*.sfx|*.swm|*.sys|*.themepack|*.tsk|*.vhd|*.wim|*.wsp|*.xar|*.xsn|*.zipx|*.??_)
        case "${ARCHEXT##*.}" in
          a|ar)				func_precheck ar  			&& { ar  $AR_OPTS "$ARCHPATH"	&& return 0	|| return 1; } ;;
          arj|a[0-9][0-9])	func_precheck arj 			&& { arj $ARJ_OPTS "$ARCHPATH"	&& return 0	|| return 1; } ;;
          cab)				func_precheck cabextract	&& { cabextract $CABEXTRACT_OPTS "$ARCHPATH" && return 0 || return 1; } ;;
          lha|lza|lzh)		func_precheck lha 			&& { lha $LHA_OPTS "$ARCHPATH"	&& return 0 || return 1; } ;;
          xar)				func_precheck xar 			&& { xar $XAR_OPTS "$ARCHPATH"	&& return 0 || return 1; } ;;
        esac
        case "${ARCHEXT##*.}" in
          a|ar|arj|a[0-9][0-9]|cab|lha|lza|lzh|xar) echo -e "\n${BLUECOL}$(gettext 'Trying') '${SEVENZ}'...${OFFCOL}" ;;
        esac
		func_precheck $SEVENZ || return 1
		$SEVENZ $SEVENZ_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.ace|*.cba)
	    func_precheck unace || return 1
	    unace $UNACE_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.alz)
	    func_precheck unalz || return 1
	    unalz $UNALZ_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.arc|*.ark|*.sue)
	    echo -e "${BLUECOL}$(gettext 'Trying') '(un)arc'...${OFFCOL}\n"
	    func_precheck $UNARC && { $UNARC $UNARC_OPTS "$ARCHPATH" && return 0 || echo;echo; }
	    [ "$UNARC" = "unarc" ] && [ "`which arc 2>/dev/null`" ] && { arc $UNARC_OPTS "$ARCHPATH" && return 0; } # workaround - arc/unarc/nomarch are incompatibile with FreeArc's arc/unarc and nomarch doesn't support Unicode
	    echo -e "\n${BLUECOL}$(gettext 'Trying') 'nomarch'...${OFFCOL}\n"
	    func_precheck nomarch && { nomarch $NOMARCH_OPTS "$ARCHPATH" && return 0; }
	    return 1
	;;
	# -----------
	*.ani)
	    IMAGES=( $(grep -abo 'icon' "$ARCHPATH" | cut -f1 -d ':') $(stat -c %s "$ARCHPATH") )
        [ ${#IMAGES[@]} -eq 1 ] && { echo -e "${REDCOL}ERROR: ${OFFCOL}$(gettext 'No images found!')"; return 1; }
	    echo -e "${BLUECOL}$(gettext "Images found:") $(( ${#IMAGES[@]} - 1))${OFFCOL}"
	    [ "$MODE" = "list" ] && return 0
        for ((CNT=0; CNT<$((${#IMAGES[@]}-1)); CNT++)); do
          echo "$(gettext 'Extracting image') $(($CNT+1))"
          tail -c +$((${IMAGES[$CNT]}+9)) "$ARCHPATH" | head -c $(( ${IMAGES[$(($CNT+1))]} - ${IMAGES[$CNT]} )) > "$(($CNT+1)).cur" || { [ $? -ne 141 ] && ERROR=1; }
        done
	;;
    # -----------
	*.asc|*.gpg)
		func_precheck $GPG || return 1
		[ "$MODE" = "list" ] && { echo "$(basename "${ARCHPATH%.*}")"; $GPG --list-only "$ARCHPATH" && return 0 || return 1; }
		$GPG -o "$(basename "${ARCHPATH%.*}")" -d "$ARCHPATH" || { rm -f -- "$(basename "${ARCHPATH%.*}")"; return 1; }
	;;
	# -----------
	*.bin|*.daa|*.mdf|*.mds|*.nrg)
	    func_precheck poweriso || return 1
	    poweriso $POWERISO_OPTS "$ARCHPATH" / -r -od . || return 1
	;;
	# -----------
	*.bxy|*.sdk|*.sh2|*.shk)
	    func_precheck $NULIB || return 1
	    [ "$NULIB" = "nulib2" ] && OPTS="$NULIB2_OPTS" || OPTS="$NULIB_OPTS"
	    $NULIB $OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.chm)
		[ "$MODE" = "list" ] && { CMND=enum_chmLib; OUTDIR=''; } || { CMND=extract_chmLib; OUTDIR='.'; }
	    func_precheck $CMND && { $CMND "$ARCHPATH" $OUTDIR && return 0; }
	    echo -e "\n${BLUECOL}$(gettext 'Trying') '${SEVENZ}'...${OFFCOL}"
	    func_precheck $SEVENZ || return 1
	    $SEVENZ $SEVENZ_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.cpio)
	    func_precheck cpio || return 1
	    cpio $CPIO_OPTS $BOPTS < "$ARCHPATH" || return 1
	;;
	# -----------
	*.cvd)
	    func_precheck $GUNZIP tar || return 1
	    tail -c +513 "$ARCHPATH" | $GUNZIP -c -d | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.cld)
	    func_precheck tar || return 1
	    tail -c +513 "$ARCHPATH" | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.dar)
	    func_precheck dar || return 1
  	    dar $DAR_OPTS "${ARCHPATH%.*.dar*}" $DAR_OPTS2 || return 1
	;;
	# -----------
	*.dat|*.tnef)
	    func_precheck tnef || return 1
	    tnef $TNEF_OPTS --unix-paths --save-body="$(basename "${ARCHPATH%.*}")" -v "$ARCHPATH" || return 1
	;;
	# -----------
    *.deb|*.udeb|*.ipk)
        if [ "$(file -b "$ARCHPATH" | cut -f1 -d ',' | grep -wi 'gzip')" ]; then
	      func_precheck $GUNZIP tar || return 1
	      [ "$MODE" != "list" ] && { mkdir control data; OPTS1='-C control'; OPTS2='-C data'; }
          $GUNZIP -c -d "$ARCHPATH" | tar $TAR_OPTS - --exclude=control.tar.gz --exclude=data.tar.gz || { [ $? -ne 141 ] && ERROR=1; }
          echo; $GUNZIP -c -d "$ARCHPATH" | tar x --to-stdout -f - ./control.tar.gz	| $GUNZIP -c -d | tar $TAR_OPTS - $OPTS1 || { [ $? -ne 141 ] && ERROR=1; }
          echo; $GUNZIP -c -d "$ARCHPATH" | tar x --to-stdout -f - ./data.tar.gz	| $GUNZIP -c -d | tar $TAR_OPTS - $OPTS2 || { [ $? -ne 141 ] && ERROR=1; }
          [ $ERROR -eq 0 ] && return 0 || return 1
        fi
        echo -e "${BLUECOL}$(gettext 'Trying') 'ar'...${OFFCOL}\n"
        func_precheck ar tar && {
		  for i in control data; do
		    EXT="$(ar t "$ARCHPATH" 2>/dev/null | grep -io "${i}.tar.*")"
		    case "${EXT##*.}" in
		      gz)	CMND="$GUNZIP -c -d" 	;;
		      bz2)	CMND="$BUNZIP2 -c -d"	;;
		      lzma)	CMND="$UNLZMA -c -d"	;;
		      xz)	CMND="$UNXZ -c -d"		;;
		      *)	CMND="cat"				;;	# plain tar
		    esac
		    func_precheck ${CMND%% *} && {
			  ar -p "$ARCHPATH" "$EXT" | $CMND | tar $TAR_OPTS - || ERROR=1
			} || { ERROR=1; break; }
		  done
		  [ $ERROR -eq 0 ] && return 0 || ERROR=0
		}
		echo -e "\n${BLUECOL}$(gettext 'Trying') 'dpkg-deb'...${OFFCOL}\n"
        func_precheck dpkg-deb && {
		  for CMND in 'dpkg-deb' 'busybox dpkg-deb'; do
		    if [ "$MODE" = "list" ]; then
		      $CMND -c "$ARCHPATH" || ERROR=1
		    else
		      $CMND -e "$ARCHPATH" . || ERROR=1
		      $CMND -X "$ARCHPATH" . || ERROR=1
		    fi
		    ([ $ERROR -eq 0 ] || [ "$(readlink -f "`which dpkg-deb 2>/dev/null`" | grep 'busybox')" ] || [ ! "$(busybox | grep -wo 'dpkg-deb')" ]) && break
		  done
		  [ $ERROR -eq 0 ] && return 0 || ERROR=0
        }
        [ "${ARCHEXT##*.}" = "deb" ] && echo -e "\n${BLUECOL}$(gettext 'Trying') 'exploderpm'...${OFFCOL}\n" || return 1
        func_precheck exploderpm || return 1
		exploderpm $EXPLODERPM_OPTS "$ARCHPATH"; RET=$?
		if [ $RET -eq 0 ] && [ `exploderpm -l "$ARCHPATH" | grep '^./$' | wc -l` -lt 2 ]; then
		  echo -e "\n${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'Archive has not been fully listed/extracted!')"
		  return 1
		fi
		[ $RET -ne 0 ] && { echo "$(gettext "Error...")"; return 1; }
	;;
	# -----------
	*.djvu)
	    func_precheck ddjvu || return 1
	    if [ "$MODE" = "list" ]; then
	      ddjvu -verbose -format=rle -scale=1% "$ARCHPATH" > /dev/null || return 1
	    else
	      PAGES="$(ddjvu -verbose -format=ppm -scale=1% -page=999999999 "$ARCHPATH" 2>&1 | grep -E '^-.*[0-9]+.*-' | tr -cd '[:digit:]')"
	      [ "$PAGES" ] || { echo -e "${REDCOL}$(gettext "Error...")${OFFCOL}"; return 1; }
	      for i in `eval echo {1..${PAGES}}`; do
	        ddjvu -verbose -format=ppm -page=$i "$ARCHPATH" "${i}.$(basename "${ARCHPATH%.*}").ppm" || ERROR=1
	      done
	    fi
	;;
	# -----------
	*.eml|*.mht|*.mim|*.mime|*.mbox)
		func_precheck ripmime || return 1
		[ "$MODE" = "list" ] && OUTDIR="$WORKDIR" || OUTDIR="$DESTDIR"
		ripmime -v -i "$ARCHPATH" -d "$OUTDIR" || return 1
		[ $(find "$OUTDIR" -maxdepth 0 -type d -empty) ] && { echo -e "${REDCOL}$(gettext "ERROR:")${OFFCOL} $(gettext "Nothing listed/extracted!")"; return 1; }
	;;
	# -----------
	*.gc|*.gst|*.tc|*.hc|*.vc)
		case "`basename "${ARCHEXT##*.}"`" in
		  gc|gst)	CMND="$GOSTCRYPT"	;;
		  hc|vc)	CMND="$VERACRYPT"	;;
		  *)		CMND="$TRUECRYPT"	;;
		esac
		func_precheck $CMND || return 1
		$CMND --text --fs-options=ro --mount-options=nokernelcrypto --mount "$ARCHPATH" "$WORKDIR" || return 1
		echo
		if [ ! $(find "$WORKDIR" -maxdepth 0 -type d -empty) ]; then
          func_list_cp_mntpt . || ERROR=1
		else
		  echo -e "${YELLOWCOL}$(gettext 'WARNING:') ${OFFCOL}$(gettext 'Volume does not contain any files, it is empty!')"
		fi
        $CMND -d "$ARCHPATH"
        umount "$WORKDIR" 2>/dev/null
	;;
	# -----------
	*.gif)
	    func_precheck gifsicle || return 1
	    gifsicle $GIFSICLE_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.gifar)
	    BOUNDARY=$(grep -aboU -m1 "$(echo -e "\x3b\x50\x4b\x03\x04")" "$ARCHPATH" | cut -f1 -d ':')
        [ "$BOUNDARY" = "" ] && { echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'Cannot find appended .jar file!')"; return 1; }
        echo -e "${BLUECOL}$(gettext 'This file contains appended .jar file!')${OFFCOL}"
	    [ "$MODE" = "list" ] && return 0
	    echo -e "$(gettext 'Extracting GIF')"
	    head -c $(($BOUNDARY+1)) "$ARCHPATH" > "$(basename "${ARCHPATH%.*}").gif" || ERROR=1
	    echo -e "$(gettext 'Extracting JAR')"
	    tail -c +$(($BOUNDARY+2)) "$ARCHPATH" > "$(basename "${ARCHPATH%.*}").jar" || ERROR=1
	;;
	# -----------
	*.ha)
	    func_precheck ha || return 1
	    ha $HA_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.hqx)
	    func_precheck hexbin || return 1
	    hexbin $HEXBIN_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.iso|*.cfs|*.ext2|*.ext3|*.ext4|*.ima|*.img|*.cramfs|*.dsk|*.disk|*.flp|*.hfs|*.hfv|*.ntfs|*.udf|*.vfd)
		case "${ARCHEXT##*.}" in
		  iso|cfs)
		    for DUMMY in DUMMY; do	# a workaround to avoid a separate function or nested if-fi
			  [ "$(od -v -An -tc -j$((17*2048+1)) -w29 -N29 "$ARCHPATH" | tr -d ' \t\n')" != "CD001001ELTORITOSPECIFICATION" ] && break
			  read B1 B2 B3 B4 <<< $(od -v -An -tu1 -j$((17*2048+71)) -w4 -N4 "$ARCHPATH")
			  INFOSECTOR=$(( ($B4*16777216)+($B3*65536)+($B2*256)+($B1) ))
			  read B1 B2 B3 B4 <<< $(od -v -An -tu1 -j$(($INFOSECTOR*2048+40)) -w4 -N4 "$ARCHPATH")
			  STARTSECTOR=$(( ($B4*16777216)+($B3*65536)+($B2*256)+($B1) ))
			  read ISBOOT TYPE NULL NULL NULL NULL S1 S2 <<< $(od -v -An -tx1 -j$(($INFOSECTOR*2048+32)) -w8 -N8 "$ARCHPATH")
			  [ $ISBOOT -ne 88 ] && break
			  case $TYPE in
			    01) COUNT=$((1200*1024/512)); [ "$MODE" = "list" ] && echo -e "${BLUECOL}$(gettext 'ISO contains 1.2M floppy boot image.')${OFFCOL}"	|| echo -e "${BLUECOL}$(gettext 'Extracting 1.2M floppy boot image.')${OFFCOL}" ;;
			    02) COUNT=$((1440*1024/512)); [ "$MODE" = "list" ] && echo -e "${BLUECOL}$(gettext 'ISO contains 1.44M floppy boot image.')${OFFCOL}"	|| echo -e "${BLUECOL}$(gettext 'Extracting 1.44M floppy boot image.')${OFFCOL}" ;;
			    03) COUNT=$((2880*1024/512)); [ "$MODE" = "list" ] && echo -e "${BLUECOL}$(gettext 'ISO contains 2.88M floppy boot image.')${OFFCOL}"	|| echo -e "${BLUECOL}$(gettext 'Extracting 2.88M floppy boot image.')${OFFCOL}" ;;
			    *)	break ;;
			  esac
			  if [ "$MODE" != "list" ]; then
			    mkdir 0_UExtract_BOOTIMAGE || return 1
			    dd if="$ARCHPATH" of="./0_UExtract_BOOTIMAGE/FLOPPY.img" bs=512 skip=$(( ${STARTSECTOR}*4 )) count=${COUNT} 2>/dev/null || ERROR=1
			  fi
			done
			mount -o loop,ro -t iso9660 "$ARCHPATH" "$WORKDIR"
		  ;;
		  ext2|ext3|ext4)
		    if [ "$(od -v -An -tc -j0 -w4 -N4 "$ARCHPATH" | tr -d ' \t\n')" = "LUKS" ]; then
		      func_precheck cryptsetup || return 1
		      MAPPER_NAME="$(basename "${ARCHPATH%.*}")"
		      cryptsetup open "$ARCHPATH" "$MAPPER_NAME" || return 1
              mount -o ro "/dev/mapper/${MAPPER_NAME}" "$WORKDIR" || { cryptsetup close "$MAPPER_NAME"; return 1; }
		      if [ ! $(find "$WORKDIR" -maxdepth 0 -type d -empty) ]; then
                func_list_cp_mntpt . || ERROR=1
		      else
		        echo -e "${YELLOWCOL}$(gettext 'WARNING:') ${OFFCOL}$(gettext 'Image does not contain any files, it is empty!')"
		      fi
		      umount "$WORKDIR" 2>/dev/null
		      cryptsetup close "$MAPPER_NAME"
		      MAPPER_NAME=''
		      [ $ERROR -eq 0 ] && return 0 || return 1
		    else
		      func_mnt_disk_image "$ARCHPATH" && return 0 || mount -o loop,ro "$ARCHPATH" "$WORKDIR"
		    fi
		  ;;
		  *)  func_mnt_disk_image "$ARCHPATH" && return 0 || mount -o loop,ro "$ARCHPATH" "$WORKDIR" ;;
		esac
		RET=$?; ((ERROR+=RET))
		if [ $RET -eq 0 ]; then
		  if [ ! $(find "$WORKDIR" -maxdepth 0 -type d -empty) ]; then
            func_list_cp_mntpt . || ERROR=1
		  else
		    echo -e "${YELLOWCOL}$(gettext 'WARNING:') ${OFFCOL}$(gettext 'Image does not contain any files, it is empty!')"
		  fi
		fi
		umount "$WORKDIR" 2>/dev/null
	;;
	# -----------
	*.jpg|*.jpeg|*.bmp|*.wav|*.au)
		func_precheck steghide || return 1
		steghide $STEGHIDE_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
    *.lbr)
        func_precheck lbrate || return 1
        lbrate $LBRATE_OPTS "$ARCHPATH" || return 1
    ;;
	# -----------
	*.lzx)
		func_precheck unlzx || return 1
		unlzx $UNLZX_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
    *.mar)
        func_precheck $BUNZIP2 || return 1
        ERROR=1
		[ "$(head -c4 "$ARCHPATH")" != "MAR1" ] && { echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'It is not a .mar archive!')"; return 1; }
		read I1 I2 I3 I4 <<< $(od -v -An -tu1 -j4 -w4 -N4 "$ARCHPATH")
		INDEX=$(echo $(( ($I1*16777216)+($I2*65536)+($I3*256)+($I4) +4 )) ) || return 1
		ARCHSIZE=$(stat -c %s "$ARCHPATH")
		while [ ${INDEX} -lt ${ARCHSIZE} ]; do
		  read O1 O2 O3 O4 S1 S2 S3 S4 F1 F2 F3 F4 <<< $(od -v -An -tu1 -j${INDEX} -w12 -N12 "$ARCHPATH")
	      OFFSET=$(echo $(( ($O1*16777216)+($O2*65536)+($O3*256)+($O4) )) )			|| return 1
		  SIZE=$(echo $(( ($S1*16777216)+($S2*65536)+($S3*256)+($S4) )) )			|| return 1
		  FLAGS=$(printf '%o' $(( ($F1*16777216)+($F2*65536)+($F3*256)+($F4) )) )	|| return 1
		  ((INDEX+=12))
		  NAME=''
		  while read -r CHAR; do
		    [[ "$CHAR" == "\\0" ]] && break
            NAME="${NAME}${CHAR}"
          done < <(od -v -An -tc -j${INDEX} -w1 "$ARCHPATH")
          [ "$NAME" = "" ] && return 1 || ERROR=0
          if [ "$MODE" = "list" ]; then
            [ $ERROR -eq 0 ] && echo "${NAME}"
          else
            mkdir -p "$(dirname "$NAME")"
            echo "$(gettext 'Extracting:') ${NAME}"
            tail -c +$((${OFFSET}+1)) "$ARCHPATH" | head -c ${SIZE} | $BUNZIP2 -c -d > "${NAME}" || { [ $? -ne 141 ] && ERROR=1; }
            chmod $FLAGS "${NAME}"
          fi
          INDEX=$(( ${INDEX}+${#NAME}+1 ))
        done
	;;
	# -----------
	*.nz)
	    func_precheck nz || return 1
	    nz $NZ_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.package)
	    func_precheck $GUNZIP tar || return 1
		COMPRESSION=$(grep -m1 -a -E 'compression=".*"' "$ARCHPATH" | cut -f2 -d '"')
		SKIP_LINES=$(grep -m1 -a -E 'skip_lines=".*"|skipLines=".*"' "$ARCHPATH" | cut -f2 -d '"')
		META_SIZE=$(grep -m1 -a -E 'meta_size=".*"|metaSize=".*"' "$ARCHPATH" | cut -f2 -d '"')
		DATA_SIZE=$(grep -m1 -a -E 'data_size=".*"|dataSize=".*"' "$ARCHPATH" | cut -f2 -d '"')
		[ "$MODE" != "list" ] && { mkdir meta payload; OPTS1='xvf - -C meta'; OPTS2='xvf - -C payload'; } || { OPTS1='tvf -'; OPTS2='tvf -'; }
		tail -n +$SKIP_LINES "$ARCHPATH" | head -c $META_SIZE | $GUNZIP -c -d | tar $OPTS1 || { [ $? -ne 141 ] && ERROR=1; }
		echo
		case "$COMPRESSION" in
		  lzma)	func_precheck $UNLZMA	&&	{ tail -c $DATA_SIZE "$ARCHPATH" | $UNLZMA -c	| tar $OPTS2	|| { [ $? -ne 141 ] && ERROR=1 || ERROR=0; }; } || ERROR=1 ;;
     	  *)	func_precheck $BUNZIP2	&&	{ tail -c $DATA_SIZE "$ARCHPATH" | $BUNZIP2 -c	| tar $OPTS2	|| { [ $? -ne 141 ] && ERROR=1 || ERROR=0; }; } || ERROR=1 ;;
		esac
	    [ "$MODE" != "list" ] && find ./meta ./payload -maxdepth 0 -type d -empty -delete 2>/dev/null
	;;
	# -----------
	*.paq8l|*.paq8n|*.paq8o|*.kgb)
		CMND="${ARCHEXT##*.}"	# will be paq8l or paq8n or paq8o or kgb
		[ "${CMND}" = "kgb" ] && { ID_STRING="KGB_arch"; OUTDIR=''; } || { ID_STRING="paq8"; OUTDIR='.'; }
	    if [ "$MODE" = "list" ]; then
	      cat "$ARCHPATH" | while read -r LINE; do echo "$LINE" | grep -qE "^[0-9]+$(echo -e "\t")|^${ID_STRING}" && echo "$LINE" || break; done
	    else
	      func_precheck $CMND || return 1
	      $CMND "$ARCHPATH" $OUTDIR || return 1
	    fi
	;;
	# -----------
	*.pbp)
	    [ "$(head -c 4 "$ARCHPATH" | tail -c 3)" != "PBP" ] && { echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'It is not a .pbp file!')"; return 1; }
        FILES=( PARAM.SFO ICON0.PNG ICON1.PMF PIC0.PNG PIC1.PNG SND0.AT3 DATA.PSP DATA.PSAR )
        for i in {0..7}; do
          read B1 B2 B3 B4 <<< $(od -v -An -tu1 -j$((8+(i*4))) -w4 -N4 "$ARCHPATH")
          OFFSET[$i]=$(echo $(( ($B4*16777216)+($B3*65536)+($B2*256)+($B1) )) ) || return 1
        done
        OFFSET[8]=$(stat -c %s "$ARCHPATH")
        for i in {0..7}; do
          if [ "$MODE" = "list" ]; then
            echo -e "${FILES[$i]}\t\t$(( ${OFFSET[$(($i+1))]} - ${OFFSET[$i]} )) $(gettext 'bytes')"
          else
            echo -e "$(gettext 'Extracting:') ${FILES[$i]}\t\t$(( ${OFFSET[$(($i+1))]} - ${OFFSET[$i]} )) $(gettext 'bytes')"
            tail -c +$((${OFFSET[$i]}+1)) "$ARCHPATH" | head -c $(( ${OFFSET[$(($i+1))]} - ${OFFSET[$i]} )) > ${FILES[$i]} || { [ $? -ne 141 ] && ERROR=1; }  
          fi
        done
	;;
	# -----------
	*.pdf)
	    func_precheck pdfinfo || return 1
	    if [ "`pdfinfo "$ARCHPATH" 2>&1 | grep 'Incorrect password'`" != "" ]; then
	      OLDIFS="$IFS"; IFS=
	      read -s -r -p "$(gettext "PDF is encrypted, enter password:") " PASSWORD
	      IFS="$OLDIFS"
	      echo; echo
	    fi
	    if [ "$MODE" = "list" ]; then
	      func_precheck pdftotext && {
		    echo -e "${BLUECOL}$(gettext 'Listing text...')${OFFCOL}\n"
		    RESULT="$(pdftotext -opw "$PASSWORD" -upw "$PASSWORD" -layout -nopgbrk -enc UTF-8 "$ARCHPATH" - | wc -c | tr -d '\n')"
		    [ $? -eq 0 ] && echo -e "${RESULT} $(gettext 'bytes')\n" || { echo; ERROR=1; }
		  } || ERROR=1
		  func_precheck pdfimages && {
		    echo -e "${BLUECOL}$(gettext 'Listing images...')${OFFCOL}\n"
		    pdfimages -opw "$PASSWORD" -upw "$PASSWORD" -list "$ARCHPATH" || ERROR=1
		  } || ERROR=1
		  func_precheck pdfdetach && {
		    echo -e "\n${BLUECOL}$(gettext 'Listing attachments...')${OFFCOL}\n"
		    pdfdetach -opw "$PASSWORD" -upw "$PASSWORD" -list "$ARCHPATH" || ERROR=1
		  } || ERROR=1
		else
		  func_precheck pdftotext && {
		    echo -e "${BLUECOL}$(gettext 'Extracting text...')${OFFCOL}"
		    pdftotext -opw "$PASSWORD" -upw "$PASSWORD" -raw -nopgbrk -enc UTF-8 "$ARCHPATH" "./$(basename "${ARCHPATH%.*}"_raw.txt)" || ERROR=1
		    pdftotext -opw "$PASSWORD" -upw "$PASSWORD" -layout -nopgbrk -enc UTF-8 "$ARCHPATH" "./$(basename "${ARCHPATH%.*}"_layout.txt)" || ERROR=1
		  } || ERROR=1
		  func_precheck pdfimages && {
	        echo -e "${BLUECOL}$(gettext 'Extracting images...')${OFFCOL}"
		    mkdir ./Images
		    pdfimages -opw "$PASSWORD" -upw "$PASSWORD" -p "$ARCHPATH" ./Images/image || ERROR=1
		    find ./Images -maxdepth 0 -type d -empty -delete 2>/dev/null
		    [ ! -d ./Images ] && echo -e "${OFFCOL}$(gettext 'No images found!')" || echo;:
		  } || ERROR=1
		  func_precheck pdfdetach && {
		    echo -e "${BLUECOL}$(gettext 'Extracting attachments...')${OFFCOL}"
		    mkdir ./Attachments
		    CNT=1
		    for EXT in $(pdfdetach -opw "$PASSWORD" -upw "$PASSWORD" -list "$ARCHPATH" | tail -n +2 | rev | cut -f1 -d '.' | rev); do
		      pdfdetach -opw "$PASSWORD" -upw "$PASSWORD" -save $CNT "$ARCHPATH" -o "./Attachments/attachment_${CNT}.${EXT}" || ERROR=1
		      ((CNT++))
		    done
		    find ./Attachments -maxdepth 0 -type d -empty -delete 2>/dev/null
		    [ ! -d ./Attachments ] && echo -e "${OFFCOL}$(gettext 'No attachments found!')";: 
		  } || ERROR=1
		fi
	;;
	# -----------
    *.pet)
		[ "$(file -b "$ARCHPATH" | cut -f1 -d ',' | grep -wi 'gzip')" ] && CMND="$GUNZIP" || CMND="$UNXZ"
		func_precheck $CMND tar || return 1
		head -c -32 "$ARCHPATH" | $CMND -c -d | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.pisi)
	    func_precheck unzip $UNXZ tar || return 1
	    unzip $UNZIP_OPTS "$ARCHPATH" -x "install.tar.*" || return 1
	    unzip -pv "$ARCHPATH" "install.tar.*" | $UNXZ -c -d | tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.pkg)
	    func_precheck $SEVENZ $GUNZIP cpio || return 1
	    if [ "$MODE" = "list" ]; then
		  echo -e "\n${BLUECOL}$(gettext 'Listing the main archive...')${OFFCOL}\n"
		  $SEVENZ l -txar "$ARCHPATH" || return 1
		  for SUBARCHIVE in $($SEVENZ l -txar "$ARCHPATH" | grep -E 'Payload|Scripts' | rev | cut -f1 -d ' ' | rev); do
		    echo -e "\n${BLUECOL}$(gettext 'Listing subarchive:') ${OFFCOL}'$SUBARCHIVE'\n"
		    $SEVENZ e -txar -so "$ARCHPATH" "$SUBARCHIVE" 2>/dev/null | $GUNZIP -c -d | cpio -itv $BOPTS || { [ $? -ne 141 ] && ERROR=1; }
		  done
		else
		  $SEVENZ x -txar "$ARCHPATH" || return 1
		  while read -r SUBDIR; do
		    cd "$SUBDIR"
		    for i in Payload Scripts; do
		      if [ -f "${i}" ]; then
		        mv "${i}" "${i}.cpio.gz"
		        $GUNZIP -c -d -v "${i}.cpio.gz" | cpio -idmv $BOPTS || { [ $? -ne 141 ] && ERROR=1; }
		        rm "${i}.cpio.gz"
		      fi
		    done
		    [ "$SUBDIR" != "." ] && cd ..
		  done < <(find . -maxdepth 1 -type d -iname "*.pkg"; echo . )
	    fi
	;;
	# -----------
    *.rar|*.rar5|*.r[0-9]|*.r[0-9][0-9]|*.cbr|*.lemon|*.rsn)
		func_precheck $UNRAR && { $UNRAR $UNRAR_OPTS "$ARCHPATH" && return 0; }
		echo -e "\n${BLUECOL}$(gettext 'Trying') '${SEVENZ}'...${OFFCOL}\n"
		func_precheck $SEVENZ || return 1
		$SEVENZ $SEVENZ_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
    *.rpm|*.spm)
        echo -e "${BLUECOL}$(gettext 'Trying') 'rpm2cpio' + 'cpio'...${OFFCOL}\n"
		func_precheck rpm2cpio cpio && {
		  rpm2cpio "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS && [ $? -eq 0 -o $? -eq 141 ] && return 0
		}
        echo -e "\n${BLUECOL}$(gettext 'Trying') 'rpm2cpio' + '(un)xz' + 'cpio'...${OFFCOL}\n"
        func_precheck rpm2cpio $UNXZ && {
		  rpm2cpio "$ARCHPATH" | $UNXZ -c -d | cpio $CPIO_OPTS $BOPTS && [ $? -eq 0 -o $? -eq 141 ] && return 0
		}
        echo -e "\n${BLUECOL}$(gettext 'Trying') 'exploderpm'...${OFFCOL}\n"
        func_precheck exploderpm && {
		  exploderpm $EXPLODERPM_OPTS "$ARCHPATH" && return 0
		}
		echo -e "\n${BLUECOL}$(gettext 'Trying') '${SEVENZ}' + 'cpio'...${OFFCOL}\n"
		func_precheck $SEVENZ cpio && {
		  $SEVENZ -so x "$ARCHPATH" | cpio $CPIO_OPTS $BOPTS && [ $? -eq 0 -o $? -eq 141 ] && return 0
		}
		return 1
	;;
	# -----------
	*.run)	# supports at least amd/nvidia/vbox/truecrypt/veracrypt installers + makeself.sh + portablelinuxapps
	    if ([ "`file -b "$ARCHPATH" | grep -iE '^ELF|^ISO|^# ISO'`" ] && [ "$(od -v -An -tc -j32769 -w5 -N5 "$ARCHPATH" | tr -d ' \n')" = "CD001" ]); then
          mount -o loop,ro -t iso9660 "$ARCHPATH" "$WORKDIR" || ERROR=1
          if [ $ERROR -eq 0 ]; then
            func_list_cp_mntpt . || ERROR=1
          fi
          umount "$WORKDIR" 2>/dev/null
        else
	      for DUMMY in DUMMY; do
	        OFFSET=$(grep -a -m1 -E '^skip=|^PACKAGE_START=' "$ARCHPATH" | tr -c -d '[:digit:]')
	        [ "$OFFSET" ] && { METHOD="-n +$OFFSET"; break; }
		    OFFSET=$(grep -a -m1 '^filesizes=' "$ARCHPATH" | tr -c -d '[:digit:]')
		    [ "$OFFSET" ] && { METHOD="-c $OFFSET"; break; }
		    [ "$(head -n 3 "$ARCHPATH" | grep 'BOINC.*binstall.sh')" ] && { METHOD="-n +4"; break; }	# BOINC
		    echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'Unsupported .run installer!')"
		    return 1
		  done
		  case "$(tail $METHOD "$ARCHPATH" | file -b - | cut -f1 -d ',' | tr '[:upper:]' '[:lower:]')" in
		    *'tar'*)		func_precheck tar && 				{ tail $METHOD "$ARCHPATH" | 				 		tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1 || return 0; }; } || return 1 ;;
		    *'xz'*)			func_precheck tar $UNXZ &&			{ tail $METHOD "$ARCHPATH" | $UNXZ -c -d		|	tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1 || return 0; }; } || return 1 ;;
		    *'gzip'*)		func_precheck tar $GUNZIP &&		{ tail $METHOD "$ARCHPATH" | $GUNZIP -c -d		|	tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1 || return 0; }; } || return 1 ;;
		    *'bzip2'*)		func_precheck tar $BUNZIP2 &&		{ tail $METHOD "$ARCHPATH" | $BUNZIP2 -c -d		|	tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1 || return 0; }; } || return 1 ;;
		    *'compress'*)	func_precheck tar $UNCOMPRESS &&	{ tail $METHOD "$ARCHPATH" | $UNCOMPRESS -c -d	|	tar $TAR_OPTS - || { [ $? -ne 141 ] && return 1 || return 0; }; } || return 1 ;;
		    *)	echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'Compression method unknown!')"; return 1 ;;
		  esac
		fi
	;;
	# -----------
	*.sfs|*.slm|*.srm|*.usfs|*.pfs|*.sb|*.scm|*.sqf|*.sqfs|*.sqsh|*.squashfs|*.tcz|*.xzm)
		func_precheck $UNSQUASHFS || return 1
		$UNSQUASHFS $UNSQUASHFS_OPTS "$ARCHPATH" || return 1
		[ `$DF . | tail -n -1 | tr -s ' ' | cut -f4 -d ' '` -eq 0 ] && return 1	# unsquashfs exits with 0 if 'No space left on device' !!!
	;;
	# -----------
	*.shar|*.shr)
	    if [ "$MODE" = "list" ]; then
		  while read LINE; do
		    [ "$LINE" != "#" ] && echo "$LINE" || break 
	      done < <(grep -A999999999999 "# This shar contains:" "$ARCHPATH")
	    else
		  echo -e "${REDCOL}$(gettext 'WARNING! This is an executable script!')${OFFCOL}"
		  read -p "$(gettext 'Are you sure you want to launch it?') ($YESKEY/$NOKEY)" -t11 -n1 KEY
		  echo; echo
		  ([ "$KEY" = "$YESKEY" ] || [ "$KEY" = "$YESKEY_UP" ]) || { echo -e "${YELLOWCOL}$(gettext 'Aborted...')${OFFCOL}"; return 1; }
		  sh "$ARCHPATH" || return 1
	    fi
	;;
	# -----------
	*.spk)
	    func_precheck $GUNZIP tar || return 1
	    [ "$MODE" != "list" ] && { mkdir package; OPTS="-C package"; } || OPTS=''
	    tar $TAR_OPTS "$ARCHPATH" --exclude=package.tgz || return 1
	    echo; tar x --to-stdout -f "$ARCHPATH" package.tgz | $GUNZIP -c -d | tar $TAR_OPTS - $OPTS || { [ $? -ne 141 ] && return 1; }
	;;
	# -----------
	*.swf)
	    func_precheck swfextract || return 1
	    swfextract $SWFEXTRACT_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
    *.tar|*.tar.md5|*.gem|*.gnutar|*.gtar|*.cbt|*.hid|*.ova|*.pat|*.tardist|*.thm|*.ustar|*.wbm|*.xjt)
		func_precheck tar || return 1
		tar $TAR_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.tazpkg)
	    func_precheck $UNLZMA cpio || return 1
	    if [ "$MODE" = "list" ]; then
		  cpio $CPIO_OPTS $BOPTS < "$ARCHPATH" || return 1
		  echo; cpio --to-stdout -iv -E <(echo "fs.cpio.lzma") < "$ARCHPATH" 2>/dev/null | $UNLZMA -c | cpio -itv || { [ $? -ne 141 ] && return 1; }
		else
		  cpio $CPIO_OPTS $BOPTS < "$ARCHPATH" || return 1
		  $UNLZMA -c fs.cpio.lzma | cpio -idmv $BOPTS || { [ $? -ne 141 ] && return 1; }
		  rm -f fs.cpio.lzma
	    fi
	;;
	# -----------
	*.upx)
		func_precheck upx || return 1
		upx $UPX_OPTS "$ARCHPATH" -o "./$(basename "$ARCHPATH")" || return 1
	;;
	# -----------
	*.uu|*.uue)
		OUTNAME="$(head -n 1 "$ARCHPATH" | cut -f3- -d ' ')"
		[ "$MODE" = "list" ] && { echo "${OUTNAME##*/}" && return 0 || return 1; }
		func_precheck uudecode || return 1
		echo "$(gettext 'Extracting...')"
		uudecode "$ARCHPATH" -o "${OUTNAME##*/}" || return 1	# ${OUTNAME##*/} - can contain path (!), so leave only last segment
    ;;
	# -----------
	*.vdi|*.vmdk|*.qcow|*.qcow2)
	    func_precheck qemu-nbd || return 1
	    modprobe nbd || return 1
	      sleep 1
	      NBD_DEV=0
	      while :; do
	        if [ -b /dev/nbd${NBD_DEV} ]; then
	          qemu-nbd -c /dev/nbd${NBD_DEV} "$ARCHPATH" 2>/dev/null && break
	          ((NBD_DEV++))
	        else
	          NBD_DEV=''; return 1
	        fi
	      done
          func_mnt_disk_image "/dev/nbd${NBD_DEV}" || ERROR=1
	      qemu-nbd -d /dev/nbd${NBD_DEV} >/dev/null 2>&1 
	      NBD_DEV=''
	      [ $ERROR -eq 0 ] && return 0 || { echo -e "${REDCOL}$(gettext "Error...")${OFFCOL}"; return 1; }
	;;
    # -----------
    *.wot)
        func_precheck $BUNZIP2 || return 1
        $BUNZIP2 -c -d -v "$ARCHPATH" > "${WORKDIR}/$(basename "${ARCHPATH%.*}").ar" || return 1      
        func_precheck ar && { ar $AR_OPTS "${WORKDIR}/$(basename "${ARCHPATH%.*}").ar" && return 0 || return 1; }
        echo -e "\n${BLUECOL}$(gettext 'Trying') '${SEVENZ}'...${OFFCOL}\n"
        func_precheck $SEVENZ || return 1
        $SEVENZ $SEVENZ_OPTS "${WORKDIR}/$(basename "${ARCHPATH%.*}").ar" && return 0 || return 1
    ;;
	# -----------
    *.zip|*.a2theme|*.pup|*.acp|*.apk|*.avastsounds|*.azw2|*.bau|*.bmz|*.capx|*.cbz|*.cdmtz|*.cdmz*|*.chrt|.dbk|*.dazip|*.docx|*.dotx|*.ear|*.ecs|*.egg|*.eaz|*.eftx|*.epub|*.esriaddin|*.flf|*.g3x|*.gadget|*.gg|*.goomod|*.gps|*.gszip|*.hmxz|*.htmlz|*.hwp|*.i5z|*.imz|*.ip|*.ipa|*.ipcc|*.ipg|*.ipsw|*.ita|*.itz|*.iwd|*.ja|*.jar|*.jic|*.kfo|*.kmz|*.kpr|*.ksf|*.ksp|*.kwd|*.lca|*.little|*.love|*.lpk|*.lxf|*.maff|*.mcgame|*.mcworld|*.mscz|*.mdz|*.mdzip|*.mga|*.mmip|*.mpk|*.mpzip|*.mxl|*.mzp|*.nbm|*.nfl|*.odb|*.odc|*.odf|*.odg|*.odi|*.odm|*.odp|*.ods|*.odt|*.oex|*.otg|*.oth|*.otp|*.ots|*.ott|*.ovp|*.oxt|*.p2s|*.pak|*.par|*.pcv|*.pigm|*.pigs|*.piz|*.pk3|*.pk4|*.pmlz|*.potx|*.pptx|*.pvga|*.quiz|*.rmskin|*.s3z|*.sam|*.sdz|*.sh3d|*.sh3f|*.sh3t|*.sle|*.smzip|*.sob|*.sox|*.stc|*.std|*.sti|*.studyarch|*.stw|*.styx|*.sxc|*.sxd|*.sxg|*.sxi|*.sxm|*.sxw|*.thmx|*.twz|*.twzip|*.u3p|*.utz|*.wal|*.war|*.wba|*.webz|*.wgt|*.wgz|*.whl|*.widget|*.wlz|*.wmd|*.wmga|*.wmz|*.wsz|*.xap|*.xlam|*.xlsm|*.xlsx|*.xltx|*.xmz|*.xpi|*.xrns|*.z[0-9][0-9]|*.zab|*.zad|*.zfsendtotarget|*.zi|*.zpi|*.zm9|*.ztd|*.zxp)
		[[ "$ARCHEXT" == *.egg ]] && [[ ! "`file -b "$ARCHPATH" | cut -f1 -d ','`" == Zip* ]] && {
		  func_precheck unegg || return 1
		  unegg $UNEGG_OPTS "$ARCHPATH" && return 0 || return 1	
		}
		[[ "$ARCHEXT" == *.apk ]] && [[ "`file -b "$ARCHPATH" | cut -f1 -d ','`" == gzip* ]] && {
		  func_precheck $GUNZIP tar || return 1
		  $GUNZIP -c -d "$ARCHPATH" | tar $TAR_OPTS - && return 0 || { [ $? -ne 141 ] && return 1 || return 0; }
		}
		if [ "$MODE" = "list" ]; then
		  [[ "$ARCHEXT" == *.sdz ]] && [[ "`file -b "$ARCHPATH" | cut -f1 -d ','`" == gzip* ]] && {
		    echo "$(basename "${ARCHPATH%.*}").sdf" && return 0 || return 1	
		  } 
		  func_precheck unzip && {
		    unzip -lv "$ARCHPATH" && return 0
		  }
		else
		  [[ "$ARCHEXT" == *.sdz ]] && [[ "`file -b "$ARCHPATH" | cut -f1 -d ','`" == gzip* ]] && {
		    func_precheck $GUNZIP || return 1
		    $GUNZIP -S ".${ARCHEXT##*.}" -c -d -v "$ARCHPATH" > "$(basename "${ARCHPATH%.*}").sdf" && return 0 || return 1	
		  }
		  func_precheck unzip && {
		    unzip "$ARCHPATH" && { [ "$(file -b -- "./-" | cut -f1 -d ',' | grep 'ISO')" ] && mv -- "./-" "$(basename "${ARCHPATH%%.*}").iso"; return 0; }
		    # In case of some extensions ignore warning (1) and don't continue with 7z, because unzip handles backslashes properly (but warns) and 7z does conversely
		    [ $? -eq 1 ] && [ "$(echo ${ARCHEXT##*.} | grep -E 'capx|eaz|esriaddin|lpk|mpk|xrns')" ] && return 0	    
		  }
	    fi
	    echo -e "\n${BLUECOL}$(gettext 'Trying') '${SEVENZ}'...${OFFCOL}"
		func_precheck $SEVENZ || return 1
		$SEVENZ $SEVENZ_OPTS "$ARCHPATH" && return 0 || return 1
	;;
	# -----------
	*.zoo)
	    func_precheck zoo || return 1
	    zoo $ZOO_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.zpaq)
	    func_precheck zpaq || return 1
	    zpaq $ZPAQ_OPTS "$ARCHPATH" $ZPAQ_OPTS2 && return 0
	    echo -e "\n${BLUECOL}$(gettext 'Archive might be encrypted, trying with '-k' option...')${OFFCOL}\n"
	    zpaq $ZPAQ_OPTS "$ARCHPATH" $ZPAQ_OPTS2 -k || return 1
	;;
	# -----------
	*.zz)
	    func_precheck zzip || return 1
	    zzip $ZZIP_OPTS "$ARCHPATH" || return 1
	;;
	# -----------
	*.flac|*.mp3|*.wma)
		func_precheck $FFMPEG || return 1
	    if [ "$MODE" = "list" ]; then
	      echo -e "${BLUECOL}$(gettext 'Listing album art...')${OFFCOL}"
	      TMP="$($FFMPEG -i "$ARCHPATH" 2>&1 | grep "Video: mjpeg")"
	      [ "$TMP" != "" ] && echo "$TMP" || { echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'No album art found!')${OFFCOL}"; return 1; }
	    else
	      echo -e "${BLUECOL}$(gettext 'Extracting album art...')${OFFCOL}"
          CNT=0
	      for STREAM in $($FFMPEG -i "$ARCHPATH" 2>&1 | grep ' Video: mjpeg' | cut -f2 -d '#' | cut -b1-3); do
		    [ $CNT -eq 0 ] && CNTEXT= || CNTEXT="_(${CNT})"
		    $FFMPEG -i "$ARCHPATH" -map ${STREAM//./:} -vcodec copy -an "$(basename "${ARCHPATH%.*}")${CNTEXT}.jpg" || ERROR=1
	        ((CNT++))
	      done
          [ $CNT -eq 0 ] && { echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'No album art found!')${OFFCOL}"; return 1; }
	    fi
	;;
	# -----------
	*.3gp|*.avi|*.divx|*.flv|*.mkv|*.mov|*.mp4|*.mpeg|*.mpg|*.ogg|*.ogv|*.vob|*.webm|*.wmv)
		func_precheck $FFMPEG || return 1	
		if [ "$MODE" = "list" ]; then
		  echo -e "${BLUECOL}$(gettext 'Listing audio streams...')${OFFCOL}"
		  TMP="$($FFMPEG -i "$ARCHPATH" 2>&1 | grep ' Audio: ')"
		  [ "$TMP" != "" ] && echo "$TMP" || { echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'No audio streams found!')"; ERROR=1; }
		  echo -e "\n${BLUECOL}$(gettext 'Listing video streams...')${OFFCOL}"
		  TMP="$($FFMPEG -i "$ARCHPATH" 2>&1 | grep ' Video: ')"
		  [ "$TMP" != "" ] && echo "$TMP" || { echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'No video streams found!')"; ERROR=1; }
		  echo -e "\n${BLUECOL}$(gettext 'Listing subtitles streams...')${OFFCOL}"
		  TMP="$($FFMPEG -i "$ARCHPATH" 2>&1 | grep ' Subtitle: ')"
		  [ "$TMP" != "" ] && echo "$TMP" || echo -e "${YELLOWCOL}$(gettext 'WARNING:') ${OFFCOL}$(gettext 'No subtitles streams found.')"
		else
		  CNT=0
		  echo -e "${BLUECOL}$(gettext 'Extracting audio streams...')${OFFCOL}"
		  for STREAM in $($FFMPEG -i "$ARCHPATH" 2>&1 | grep ' Audio: ' | cut -f2 -d '#' | cut -b1-3); do
		    EXT=$($FFMPEG -i "$ARCHPATH" 2>&1 | grep "#$STREAM" | grep -o " Audio: .*" | cut -f3 -d ' ' | cut -b1-3)
		    for i in adp:wav vor:ogg opu:opus fla:flac; do
		      [ "$EXT" = "${i%:*}" ] && { EXT="${i##*:}"; break; }
		    done
		    [ $CNT -eq 0 ] && CNTEXT= || CNTEXT="_(${CNT})"
		    $FFMPEG -i "$ARCHPATH" -map ${STREAM//./:} -acodec copy -vn "$(basename "${ARCHPATH%.*}")${CNTEXT}.${EXT}" || ERROR=1
		    ((CNT++))
		  done
		  [ $CNT -eq 0 ] && { ERROR=1; echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'No audio streams found!')"; }
		  CNT=0
		  EXT="${ARCHPATH##*.}"; [ "$EXT" = "divx" ] && EXT=mp4
		  echo -e "\n${BLUECOL}$(gettext 'Extracting video streams...')${OFFCOL}"
		  for STREAM in $($FFMPEG -i "$ARCHPATH" 2>&1 | grep ' Video: ' | cut -f2 -d '#' | cut -b1-3); do
  		    [ $CNT -eq 0 ] && CNTEXT= || CNTEXT="_(${CNT})"
  		    $FFMPEG -i "$ARCHPATH" -map ${STREAM//./:} -vcodec copy -an "$(basename "${ARCHPATH%.*}")${CNTEXT}.${EXT}" || ERROR=1
		    ((CNT++))
		  done
		  [ $CNT -eq 0 ] && { ERROR=1; echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'No video streams found!')"; } 
		  CNT=0
		  echo -e "\n${BLUECOL}$(gettext 'Extracting subtitles streams...')${OFFCOL}"
		  for STREAM in $($FFMPEG -i "$ARCHPATH" 2>&1 | grep ' Subtitle: ' | cut -f2 -d '#' | cut -b1-3); do
	        EXT=srt	#EXT=$($FFMPEG -i "$ARCHPATH" 2>&1 | grep "#$STREAM" | grep -o " Subtitle: .*" | cut -f3 -d ' ' | cut -b1-3)
	        [ $CNT -eq 0 ] && CNTEXT= || CNTEXT="_(${CNT})"
	        $FFMPEG -i "$ARCHPATH" -map ${STREAM//./:} -scodec srt -an -vn "$(basename "${ARCHPATH%.*}")${CNTEXT}.${EXT}" || ERROR=1
		    ((CNT++))
		  done		  
		  [ $CNT -eq 0 ] && echo -e "${YELLOWCOL}$(gettext 'WARNING:') ${OFFCOL}$(gettext 'No subtitles streams found.')"
	    fi
	;;
	# -----------
	fusecompress)	# fake extension
		[ "$MODE" = "list" ] && { echo "$(basename "$ARCHPATH")" && return 0 || return 1; }
		func_precheck fusecompress_offline || return 1
		cp -a "$ARCHPATH" . || return 1
		fusecompress_offline -v -r null -- "$(basename "$ARCHPATH")" || { rm -- "$(basename "$ARCHPATH")"; return 1; }
	;;
	# -----------
    *)  if [[ "$(basename "$ARCHPATH")" == init* && "$(file -b "$ARCHPATH" | cut -f1 -d ',')" == *" cpio "* ]]; then
		  func_precheck cpio || return 1
		  cpio $CPIO_OPTS $BOPTS < "$ARCHPATH" || return 1
		elif [[ "$(basename "$ARCHPATH")" == Thumbs*.db ]]; then
		  func_precheck $SEVENZ || return 1
		  IMAGES="$($SEVENZ l "$ARCHPATH" | tail -n -1 | tr -s ' ' | cut -f4 -d ' ')" || { echo -e "${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext "No thumbnails found!")"; return 1; }
		  echo -e "${BLUECOL}$(gettext 'Thumbnails found:') $(($IMAGES-1))${OFFCOL}"
		  [ "$MODE" = "list" ] && return 0
		  $SEVENZ x "$ARCHPATH" || return 1
		  rm -f Catalog
		  echo -e "\n${BLUECOL}$(gettext 'Postprocessing, please wait...')${OFFCOL}"
		  for NAME in *; do
		    tail -c +13 "$NAME" > "$(gettext 'Thumbnail')_$(echo "$NAME" | rev).jpg" || ERROR=1
		    rm -f "$NAME"
		  done
        elif [ "`file -b "$ARCHPATH" | cut -f1 -d ',' | grep -wiE 'Xcursor|X11 cursor'`" ]; then
          func_precheck xcur2png || return 1
          xcur2png $XCUR2PNG_OPTS "$ARCHPATH"
          [ $? -eq 255 ] && { rm -f *.conf; return 1; }
		else
          echo -e "${YELLOWCOL}$(gettext 'WARNING:') ${OFFCOL}$(gettext 'Format not supported!')\n"
		  echo -e "${BLUECOL}$(gettext 'Trying') '${SEVENZ}'...${OFFCOL}"
		  func_precheck $SEVENZ || return 1
		  $SEVENZ $SEVENZ_OPTS "$ARCHPATH" && return 0 || return 1
	    fi
	;;
	# -----------
  esac
  [ $ERROR -eq 0 ] && return 0 || return 1
}

#################################### START ####################################

CUSTOMDIR="$(pwd)"
MODE=extract
BADOPT=false
[ "$*" ] && EXTRAOPT=none || EXTRAOPT=help
OFFCOL="\e[00m"; REDCOL="\e[0;31m"; BLUECOL="\e[0;34m"; GREENCOL="\e[0;32m"; YELLOWCOL="\e[0;33m"; UNDERLINE="\e[0;32;4m"
unset FILESSTACK

while (($#)); do
  case "$1" in
	-o|--output)		CUSTOMDIR="$2"; shift; shift	;;	# 'shift 2' hangs if only one arg left
    --output=*)			CUSTOMDIR="${1#*=}"; shift	;;
    -l|--list)			MODE=list; shift	;;
    -n|--no-colors) 	unset REDCOL BLUECOL GREENCOL YELLOWCOL UNDERLINE; shift	;;
    -b|--backends)		EXTRAOPT=backends; shift	;;
    -f|--formats)		EXTRAOPT=formats; shift	;;
    -h|--help|"")		EXTRAOPT=help; shift	;;
    --) 				shift; while (($#)); do FILESSTACK+=( "$1" ); shift; done; break ;;
    -*) 				BADOPT=true; break ;;
    *) 					FILESSTACK+=( "$1" ); shift ;;
  esac
done

echo -e "\n${UNDERLINE}${APPNAME} by SFR'2013-2016; GNU GPL v2 applies${OFFCOL}"

[ "$BADOPT" = "true" ] && { echo -e "\n${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'Unknown option:') ${1}\n"; exit 1; }

SEVENZ=7z;				for i in 7z 7za 7zr; do [ "`which $i 2>/dev/null`" ] && SEVENZ="$i" && break; done
UNSQUASHFS=unsquashfs;	for i in unsquashfs4 unsquashfs unsquashfs3; do [ "`which $i 2>/dev/null`" ] && UNSQUASHFS="$i" && break; done

for i in 'DF|df-FULL|df' 'NULIB|nulib2|nulib' 'UNRAR|unrar|rar' 'GPG|gpg|gpg2' 'GOSTCRYPT|gostcrypt_cli|gostcrypt' 'TRUECRYPT|truecrypt_cli|truecrypt' 'VERACRYPT|veracrypt_cli|veracrypt' 'BUNZIP2|bunzip2|bzip2' 'GUNZIP|gunzip|gzip' 'UNCOMPRESS|uncompress|compress' 'UNXZ|unxz|xz' 'UNLZMA|unlzma|lzma' 'UNLZOP|unlzop|lzop' 'UNARC|unarc|arc' 'UNFREEZE|unfreeze|freeze' 'LZ4|lz4c|lz4' 'LRUNZIP|lrunzip|lrzip' 'LUNZIP|lunzip|lzip' 'RUNZIP|runzip|rzip' 'FFMPEG|avconv|ffmpeg'; do
  read -r CMND IN1 IN2 <<< "${i//|/ }"
  [ "`which "$IN1" 2>/dev/null`" ] && eval "$CMND=$IN1" || eval "$CMND=$IN2"
done

[ "`which $UNLZMA 2>/dev/null`" ] || UNLZMA="$UNXZ"		# (un)xz can handle lzma, too
[ "$(readlink -f "`which losetup 2>/dev/null`" | grep 'busybox')" ] && LOSETUP='losetup-FULL' || LOSETUP='losetup'
[ "$(readlink -f "`which cpio 2>/dev/null`" | grep 'busybox')" ] 	&& BOPTS='' || BOPTS="--no-absolute-filenames"

[ "$MODE" = "list" ] 	&&  { AR_OPTS='tv'; ARJ_OPTS='l -y -v'; CABEXTRACT_OPTS='-l'; CPIO_OPTS='-ivt';  DAR_OPTS='-l'; DAR_OPTS2='';	EXPLODERPM_OPTS='-l'; GIFSICLE_OPTS='-I';                 	HA_OPTS='l'; HEXBIN_OPTS='-i';  LBRATE_OPTS='-l'; LHA_OPTS='-l'; NOMARCH_OPTS='-l'; NULIB_OPTS='tv'; NULIB2_OPTS='-v'; NZ_OPTS='l -v'; POWERISO_OPTS='list';    SEVENZ_OPTS='l'; STEGHIDE_OPTS='info';			SWFEXTRACT_OPTS='';                                                 	TAR_OPTS='tvf'; TNEF_OPTS='-t'; UNACE_OPTS='l -y'; UNALZ_OPTS='-l'; UNARC_OPTS='l'; UNEGG_OPTS='-l'; UNLZX_OPTS='-v'; UNRAR_OPTS='l'; UNSQUASHFS_OPTS='-l';      UNZIP_OPTS='-lv'; UPX_OPTS='-l'; XAR_OPTS='-t -f';    XCUR2PNG_OPTS='-n'; ZOO_OPTS='l'; ZPAQ_OPTS='l'; ZPAQ_OPTS2=''; 			ZZIP_OPTS='l'; }
[ "$MODE" = "extract" ]	&&	{ AR_OPTS='xv'; ARJ_OPTS='x -y -v'; CABEXTRACT_OPTS='';   CPIO_OPTS='-idmv'; DAR_OPTS='-x'; DAR_OPTS2='-v';	EXPLODERPM_OPTS='-x'; GIFSICLE_OPTS='--no-background -eVU';	HA_OPTS='e'; HEXBIN_OPTS='-fl'; LBRATE_OPTS='';   LHA_OPTS='-x'; NOMARCH_OPTS='';   NULIB_OPTS='x';  NULIB2_OPTS='-x'; NZ_OPTS='x -v'; POWERISO_OPTS='extract'; SEVENZ_OPTS='x'; STEGHIDE_OPTS='extract -sf';	SWFEXTRACT_OPTS='--outputformat '"$(gettext 'Item')_%06d.%s"' -a 1-'; 	TAR_OPTS='xvf'; TNEF_OPTS='';   UNACE_OPTS='x';    UNALZ_OPTS='';   UNARC_OPTS='x'; UNEGG_OPTS='-x'; UNLZX_OPTS='-x'; UNRAR_OPTS='x'; UNSQUASHFS_OPTS='-f -d .'; UNZIP_OPTS='';    UPX_OPTS='-d'; XAR_OPTS='-x -v -f'; XCUR2PNG_OPTS='';   ZOO_OPTS='x'; ZPAQ_OPTS='x'; ZPAQ_OPTS2='-to ./';	ZZIP_OPTS='x'; }

case "$EXTRAOPT" in
  help)		echo -e "\n$(gettext 'Usage:')"
			echo "  $(basename "$(readlink -f "$0")") [$(gettext "options")] $(gettext "files")"
			echo "  $(basename "$(readlink -f "$0")") [-n] <$(gettext "extra_option")>"
			echo -e "\n$(gettext 'Options:')"
			echo "  -o, --output		$(gettext 'set output directory (ignored if -l was also specified)')"
			echo "  -l, --list		$(gettext 'list contents instead of extracting')"
			echo "  -n, --no-colors	$(gettext 'disable ANSI colors in output')"
			echo -e "\n$(gettext 'Extra options:')"
			echo "  -b, --backends	$(gettext "check availability of backends")"
			echo "  -f, --formats		$(gettext 'show supported formats/extensions')"
			echo "  -h, --help		$(gettext 'this very help')"
			echo -e "${OFFCOL}"; exit	;;
  backends)	echo -e "\n${YELLOWCOL}$(gettext 'Backends:')"
			for i in $(echo 7z 7za 7zr aescrypt ar arj balz base64 bbb bcrypt bunzip $BUNZIP2 cabextract ccrypt cpio cryptsetup dar ddjvu dpkg-deb enum_chmLib exploderpm extract_chmLib fdisk $FFMPEG flzp fusecompress_offline gifsicle $GPG $GOSTCRYPT $GUNZIP ha hexbin kgb lbrate lha lpaq1 lpaq8 $LRUNZIP $LZ4 $LUNZIP msgunfmt nomarch $NULIB nz paq8l paq8n paq8o pdfdetach pdfimages pdfinfo pdftotext poweriso qemu-nbd quad ripmime rpm2cpio $RUNZIP scrypt steghide swfextract tar tnef $TRUECRYPT tor unace unalz $UNARC $UNCOMPRESS undms $UNFREEZE $UNLZMA $UNLZOP unlzx unpack200 unegg $UNRAR $UNSQUASHFS $UNXZ unzip upx uudecode $VERACRYPT wvunpack xar xcur2png zoo zpaq zzip | tr ' ' '\n' | sort -u); do
			  printf "${BLUECOL}%-32s" "> $i"
			  [ "`which $i 2>/dev/null`" ] && printf "${GREENCOL}%s${OFFCOL}\n" "[$(gettext 'OK')]" || printf "${REDCOL}%s${OFFCOL}\n" "[$(gettext 'MISSING')]"
			done
			echo -e "${OFFCOL}"; exit	;;
  formats)	echo -e "\n${YELLOWCOL}$(gettext 'Supported formats/extensions:')${BLUECOL}"
			grep -woE "[^#\"\*\?]\*\..*\)" "$(readlink -f "$0")" | tr -d '\t \*)' | tr '|' '\n' | sort | uniq
			echo -e "${OFFCOL}"; exit	;;
esac

set -- "${FILESSTACK[@]}"
[ "$1" ] || { echo -e "\n${REDCOL}$(gettext 'ERROR:') ${OFFCOL}$(gettext 'Nothing to process, exiting...')\n"; exit 1; }
[ -f "$CUSTOMDIR" ] && { echo -e "\n${REDCOL}ERROR: ${OFFCOL}Cannot create this directory. A file with the same name already exists!\n> ${CUSTOMDIR}\n"; exit 1; }

[ "$MODE" = "list" ] && { T1S="$(gettext "Listing:")"; T2S="$(gettext "Listing failed!")"; T3S="$(gettext "Listing successful!")"; } || { T1S="$(gettext "Extracting:")"; T2S="$(gettext "Extraction failed!")"; T3S="$(gettext "Extraction successful!")"; }

YESKEY="$(gettext 'y')"; YESKEY_UP="$(echo "$YESKEY" | tr '[:lower:]' '[:upper:]')"; NOKEY="$(gettext 'n')"

# -----------------------------------------------------------------------------

SUCCESSCNT=0; FAILCNT=0; SKIPCNT=0

while (($#)); do
  ARCHPATH_ORG="$1"
  ARCHPATH="$(readlink -f -- "$1" 2>/dev/null)"
  
  echo -e "\n${BLUECOL}$(func_hr '=')${OFFCOL}"
  echo -e "${BLUECOL}${T1S}${OFFCOL} ${ARCHPATH:-${ARCHPATH_ORG}}"
  echo -e "${BLUECOL}$(func_hr '-')${OFFCOL}"
  
  (file -b "${ARCHPATH:-${ARCHPATH_ORG}}" 2>/dev/null; file -bi "${ARCHPATH:-${ARCHPATH_ORG}}" 2>/dev/null) | uniq
  echo -e "${BLUECOL}$(func_hr '=')${OFFCOL}\n"

  [ -d "$ARCHPATH" ] && { echo -e "${YELLOWCOL}$(gettext 'WARNING:') ${OFFCOL}$(gettext "It is a directory; skipping...")"; ((SKIPCNT++)); SKIPFILES[$SKIPCNT]="$ARCHPATH"; shift; continue; }
  [ ! -f "$ARCHPATH" ] && { echo -e "${YELLOWCOL}$(gettext 'WARNING:') ${OFFCOL}$(gettext "File does not exist or it's a device file; skipping...")"; ((SKIPCNT++)); SKIPFILES[$SKIPCNT]="${ARCHPATH:-${ARCHPATH_ORG}}"; shift; continue; }
  
  mkdir -p "$WORKDIR"
  func_uextract
  RET=$?
  cd "$OLDDIR"
  umount "$WORKDIR" 2>/dev/null
  rm -rf "$WORKDIR" 2>/dev/null
  stty sane	# some applications' weird output can break the terminal
  echo -e "\n${BLUECOL}$(func_hr '-')${OFFCOL}"

  if [ $RET -ne 0 ]; then
    [ "$MODE" != "list" ] && find "$(readlink -f "$DESTDIR" 2>/dev/null)" -maxdepth 0 -type d -empty -delete 2>/dev/null
    echo -e "${REDCOL}${T2S}${OFFCOL}"
    FAILFILES[$FAILCNT]="$ARCHPATH"
    ((FAILCNT++))
  else
    echo -e "${GREENCOL}${T3S}${OFFCOL}"
    SUCCESSFILES[$SUCCESSCNT]="$ARCHPATH"
    ((SUCCESSCNT++))
  fi

  shift
done

# -----------------------------------------------------------------------------

echo -e "\n${BLUECOL}$(func_hr '*')${OFFCOL}"
[ $SUCCESSCNT -ne 0 ]	&& { echo -e "\n${GREENCOL}$(gettext 'Successful files'):${OFFCOL}";	printf '> %s\n' "${SUCCESSFILES[@]}";	TMPCOL="${GREENCOL}"; }
[ $SKIPCNT -ne 0 ]		&& { echo -e "\n${YELLOWCOL}$(gettext 'Skipped files'):${OFFCOL}";		printf '> %s\n' "${SKIPFILES[@]}";		TMPCOL="${YELLOWCOL}"; }
[ $FAILCNT -ne 0 ]		&& { echo -e "\n${REDCOL}$(gettext 'Failed files'):${OFFCOL}";			printf '> %s\n' "${FAILFILES[@]}";		TMPCOL="${REDCOL}"; }
echo -e "\n${BLUECOL}$(func_hr '*')${OFFCOL}"
echo -e "\n${BLUECOL}$((SUCCESSCNT+FAILCNT+SKIPCNT))${OFFCOL} $(gettext 'file(s) processed'): ${GREENCOL}${SUCCESSCNT}${OFFCOL} $(gettext 'successfully'), ${YELLOWCOL}${SKIPCNT}${OFFCOL} $(gettext 'skipped'), ${REDCOL}${FAILCNT}${OFFCOL} $(gettext 'failed')."
echo -e "\n${TMPCOL}$(gettext 'Finished!')${OFFCOL}\n"
[ $FAILCNT -eq 0 ] && exit 0 || exit 1

################################ (_)3><+.-@<+ #################################