#! /bin/bash
# vim: set tabstop=4 shiftwidth=4 noexpandtab filetype=sh:

# set default AppleCommander location if ADTPro is not installed
defaultAcPath=$(echo -n /usr/local/bin/AppleCommander-*-ac.jar)

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

helpExit () {
	if [[ -s $acmdStdErr ]]; then
		if [[ $(grep CommandLineHelp $acmdStdErr) ]]; then
			grep -v ^-[pge][[:space:]] $acmdStdErr | grep -v '^    or' | grep -v 0x2000 1>&2
			echoerr "-g  <imagename> <filename> [<outputFilename>] copy filename out of any"
			echoerr "    disk image. Using - for outputFilename will copy to stdout."
			echoerr "-e  <imagename> <filename> [<outputFilename>] like -g, with conversion"
			echoerr "    to modern file format if possible."
			echoerr "-p  <imagename> <filename> [[$|0x]<type>] [[$|0x]<auxtype>] copy filename"
			echoerr "    into ProDOS disk image. <type> is either three-letter or numeric"
			echoerr "    ProDOS file type (BIN if omitted). Will read from stdin if supplied."
			echoerr "    ProDOS subdirectories in <filename> will be created if needed."
			echoerr "    If an AppleDouble file or Nulib2 extended filename is detected,"
			echoerr "    <type> and <auxtype> are automatically set, and resource forks are kept."
			if hash cppo &> /dev/null; then
				echoerr "    (To extract these from a ProDOS image file, use cppo -ad or cppo -e.)"
			fi
			echoerr "-c  <filename> <imagename> [[$|0x]<type>] [[$|0x]<auxtype>] synonym for -p"
			echoerr "    with filename and imagename reversed."
		else
			cat $acmdStdErr
		fi
		if [[ $arg1 == "-h" ]]; then
			exitVal=0
		else
			exitVal=1
		fi
	else
		if [[ $vsd1_md5 && ( "$vsd1_md5" != "$(md5sum /usr/local/adtpro/disks/Virtual.po)" || "$vsd2_md5" != "$(md5sum /usr/local/adtpro/disks/Virtual2.po)" ) ]]; then
			if [[ "$vsd1_md5" != "$(md5sum /usr/local/adtpro/disks/Virtual.po)" || "$vsd2_md5" != "$(md5sum /usr/local/adtpro/disks/Virtual2.po)" ]]; then
				echoerr "One of the virtual drive image files has changed while ADTPro server is active."
				echoerr "    If using VSDRIVE, type 'vsdsync' now to see changes and prevent corruption."
			fi
		fi
		exitval=0
	fi
	rm $acmdStdErr &> /dev/null
	exit $exitVal
}

decToBin () {
	dec="$(( 10#$1 ))"
	bits=""
	for i in 7 6 5 4 3 2 1 0; do
		bits+=$(( (dec & (2**i)) >> i ))
	done
	echo -n "$(( 10#$bits ))"
}

arg1=$1

acmdStdErr="/tmp/acmd_$RANDOM$RANDOM"

[[ -f /usr/libexec/java_home ]] && osx=1 || osx=

if [[ $osx ]]; then
	if ! /usr/libexec/java_home &> /dev/null; then
		echo -n "AppleCommander requires the Java JDK. Do you want to install it now? "
		read
		if [[ ${REPLY:0:1} == "Y" || ${REPLY:0:1} == "y" ]]; then
			# install Oracle Java for OS X
			echo "Downloading Java JDK..."
			mkdir -p /tmp/jdk
			curl -L --cookie oraclelicense=accept-securebackup-cookie $(curl -L "http://www.oracle.com/"$(curl -L "http://www.oracle.com/technetwork/java/javase/downloads/" 2> /dev/null | grep -o -m 1 '/technetwork/java/javase/downloads/jdk.-downloads-[0-9]*\.html') 2> /dev/null | grep -o 'http://.*macosx-x64.dmg' | sort | tail -1) 2> /dev/null > /tmp/jdk/jdk.dmg
			echo "Installing Java JDK..."
			hdiutil attach /tmp/jdk/jdk.dmg &> /dev/null
			sudo installer -pkg /Volumes/JDK*/JDK*.pkg -target /
			diskutil unmountDisk /Volumes/JDK*
			rm -r /tmp/jdk 2> /dev/null
			if ! /usr/libexec/java_home &> /dev/null; then
				echo "The Java JDK could not be installed."
				echo "Type \"java\" and then click \"More Info...\" to download and install it manually."
				exit 1
			fi
		else
			exit 1
		fi
	fi
else # not OS X, so presumably Linux
	if ! hash java &> /dev/null; then
		echo -n "AppleCommander requires the Java JDK. "
		if hash apt-get; then
			echo -n "Do you want to install it now? "
			read
			if [[ ${REPLY:0:1} == "Y" || ${REPLY:0:1} == "y" ]]; then
				# from http://www.webupd8.org/2012/06/how-to-install-oracle-java-7-in-debian.html
				if { ! grep -q webupd8team /etc/apt/sources.list; }; then
					{
						echo;
						echo "# Oracle Java JDK";
						echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu precise main";
						echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu precise main";
					} | sudo tee -a /etc/apt/sources.list > /dev/null
				fi
				sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys EEA14886
				sudo apt-get -y update
				echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selections
				sudo apt-get -y install oracle-java8-installer
				sudo apt-get -y clean
			else
				exit 1
			fi
		fi
	fi
	if ! hash java &> /dev/null; then
		echo
		echo "Get it from http://www.oracle.com/technetwork/java/javase/downloads/"
		exit 1
	fi
fi

setAcPath () {
	acPath=$defaultAcPath
	adtPath=
	if [[ ! -f $acPath ]]; then
		if [[ -f /usr/local/adtpro/adtpro.sh ]]; then
			adtPath="/usr/local/adtpro"
		elif [[ $osx ]]; then
			adtPath=$(echo -n /Applications/ADTPro* | tr ' ' '\n' | sort | head -1);
		fi
		if [[ -d "$adtPath" ]]; then
			acPath="$adtPath"/lib/AppleCommander/AppleCommander-ac.jar
			[[ ! -f $acPath ]] && acPath=$(echo -n "$adtPath"/lib/AppleCommander/AppleCommander-*-ac.jar)
		fi
	fi
	acPath=$(echo -n $acPath)
}

setAcPath
if [[ ! -f $acPath ]]; then
	echo "Installing AppleCommander..."
	sudo mkdir -p /usr/local/bin
	acUrl="http://sourceforge.net/projects/applecommander/files/AppleCommander%20-%20Interim/testcase/AppleCommander-1.3.5.13id-ac.jar/download"
	if [[ $osx ]]; then
		curl -L "$acUrl" 2> /dev/null | sudo tee /usr/local/bin/AppleCommander-1.3.5.13id-ac.jar > /dev/null
	else
		sudo wget -qO /usr/local/bin/AppleCommander-1.3.5.13id-ac.jar "$acUrl"
	fi
	setAcPath
	if [[ ! -f "$acPath" ]]; then
		echo "AppleCommander couldn't be installed. Download it from"
		echo "http://applecommander.sourceforge.net and put it in /usr/local/bin."
		exit 1
	fi
fi

ac="java -Xmx128m -jar $acPath"

if [[ ! $2 || $arg1 == "-h" ]]; then
	java -jar "$acPath" 2> $acmdStdErr
	[[ $? -eq 127 ]] && exit 127 || helpExit $arg1
fi

if [[ $arg1 != "-i" && $arg1 != "-ls" && $arg1 != "-l" && $arg1 != "-ll" && $arg1 != "-x" && $arg1 != "-g" && $arg1 != "-e" && $(ps aux | grep [A]DTPro) ]]; then
	vsd1_md5="$(md5sum /usr/local/adtpro/disks/Virtual.po)"
	vsd2_md5="$(md5sum /usr/local/adtpro/disks/Virtual2.po)"
fi

if [[ ( $arg1 == "-p" || $arg1 == "-c" || $arg1 == "-g" || $arg1 == "-e" ) && $2 && $3 ]]; then

	AD=
	EX=
	prodosArg=
	prodosArgParent=
	rFile=
	getArg=
	if [[ $arg1 == "-c" || $arg1 == "-p" ]]; then
		if [[ $arg1 == "-c" ]]; then
			prodosArg="$2"
			imageArg="$3"
		elif [[ $arg1 == "-p" ]]; then
			prodosArg="$3"
			imageArg="$2"
		fi
		[[ $prodosArg == *"/"* ]] && prodosArgParent="${prodosArg%/*}/"
		rFile="${prodosArgParent}.AppleDouble/${prodosArg##*/}"
		if [[ -f "$prodosArg" && -f "$rFile" ]]; then # AppleDouble
			AD=1
		elif [ -f "${prodosArg%#*}"#?????? ]; then # Nulib2 ext filename
			EX=1
			prodosArg=$(echo -n "${prodosArg%#*}"#??????)
			rFile="${prodosArg}"r
		fi
		if [[ $AD || $EX ]]; then
			# if target is not a ProDOS disk, ignore metadata
			if ! $ac -i "$imageArg" | grep -q '^Disk Format: ProDOS'; then
				AD=
				EX=
			fi
		fi
	elif [[ $arg1 == "-g" || $arg1 == "-e" ]]; then
		fileArg="$3"
		imageArg="$2"
		getArg="$arg1"
	else
		exit 2;
	fi

	shift

	if [[ $getArg ]]; then # get file

		outFile=
		[[ $3 && $3 != "-" ]] && outFile="$3"
		[[ ! $3 ]] && outFile="${2##*/}"
		$ac $getArg "$imageArg" "$fileArg" $outFile 2> $acmdStdErr

	else # put file

		# test ProDOS name validity
		prodosPath=$(tr [:lower:] [:upper:] <<< "${prodosArg%#*}" )
		IFS_orig="$IFS"; IFS="/";
		prodosPathParts="$prodosPath"
		for thisProdosPathPart in $prodosPathParts; do
			if [[ ${#thisProdosPathPart} -gt 15 || ! $(grep ^[A-Z][0-9A-Z\.]*$ <<< $thisProdosPathPart) ]]; then
				echoerr "Invalid ProDOS name: $prodosPath"; exit 1;
			fi
		done
		IFS="$IFS_orig"

		# filetype to name table
		P_00=UNK; P_01=BAD; P_02=PCD; P_03=PTX; P_04=TXT; P_05=PDA; P_06=BIN; P_07=FNT; P_08=FOT; P_09=BA3; P_0a=DA3; P_0b=WPF; P_0c=SOS; P_0f=DIR; P_10=RPD; P_11=RPI; P_12=AFD; P_13=AFM; P_14=AFR; P_15=SCL; P_16=PFS; P_19=ADB; P_1a=AWP; P_1b=ASP; P_20=TDM; P_21=IPS; P_22=UPV; P_29=3SD; P_2a=8SC; P_2b=8OB; P_2c=8IC; P_2d=8LD; P_2e=P8C; P_41=OCR; P_42=FTD; P_50=GWP; P_51=GSS; P_52=GDB; P_53=DRW; P_54=GDP; P_55=HMD; P_56=EDU; P_57=STN; P_58=HLP; P_59=COM; P_5a=CFG; P_5b=ANM; P_5c=MUM; P_5d=ENT; P_5e=DVU; P_60=PRE; P_6b=BIO; P_6d=DVR; P_6e=PRE; P_6f=HDV; P_80=GEZ; P_81=GE1; P_82=GEO; P_83=GE3; P_84=GE4; P_85=GE5; P_86=GE6; P_87=GE7; P_88=GE8; P_89=GE9; P_8a=GEA; P_8b=GEB; P_8c=GEC; P_8d=GED; P_8e=GEE; P_8f=GEF; P_a0=WP_; P_ab=GSB; P_ac=TDF; P_ad=BDF; P_b0=SRC; P_b1=OBJ; P_b2=LIB; P_b3=S16; P_b4=RTL; P_b5=EXE; P_b6=STR; P_b7=TSF; P_b8=NDA; P_b9=CDA; P_ba=TOL; P_bb=DRV; P_bc=LDF; P_bd=FST; P_bf=DOC; P_c0=PNT; P_c1=PIC; P_c2=ANI; P_c3=PAL; P_c5=OOG; P_c6=SCR; P_c7=CDV; P_c8=FON; P_c9=FND; P_ca=ICN; P_d5=MUS; P_d6=INS; P_d7=MDI; P_d8=SND; P_db=DBM; P_e0=SHK; P_e2=DTS; P_ee=R16; P_ef=PAS; P_f0=CMD; P_f9=P16; P_fa=INT; P_fb=IVR; P_fc=BAS; P_fd=VAR; P_fe=REL; P_ff=SYS;

		# process filetype
		if [[ ! $AD && ! $EX ]]; then # no resource fork or metadata
			[[ ${3:0:2} == "0x" ]] && ftArg="\$${3:2}" || ftArg="$3"
			auxType="$4"

			# assume BIN/$2000 if filetype omitted
			if [[ ! $ftArg ]]; then
				ft="BIN"
				auxType="\$2000"
			# accept hex or decimal number for file type
			elif [[ ( ${ftArg:0:1} == '$' && ${#ftArg} -eq 3 ) || $(grep [0-9] <<< ${ftArg:0:1}) ]]; then
				if [[ ${ftArg:0:1} == '$' ]]; then
					fc=$(tr [:upper:] [:lower:] <<< ${ftArg:1:2})
				else
					fc=$(printf %02X "$ftArg" | tr [:upper:] [:lower:])
				fi
				ftVar="P_$fc";
				[[ ${!ftVar} ]] && ft=${!ftVar} || ft="\$$fc";
			else
				ft="$ftArg"
			fi

			# set auxtype to $0801 for Applesoft programs if not specified
			[[ $ft == "BAS" && ! $auxType ]] && auxType="\$0801"

			# test for absence of stdin [[ -t 0 ]] and if absent use ProDOS name
			if [[ -t 0 ]]; then
				[[ ! -f $prodosArg ]] && { echoerr "$prodosArg not found."; exit 1; }
				$ac -d "$imageArg" $prodosPath &> /dev/null
				$ac -p "$imageArg" $prodosPath $ft $auxType < $prodosArg 2> $acmdStdErr
			else
				$ac -d "$imageArg" $prodosPath &> /dev/null
				$ac -p "$imageArg" $prodosPath $ft $auxType 2> $acmdStdErr
			fi
		else # AppleDouble or nulib extended, get resource fork and file metadata from header file

			[[ ! -f $prodosArg ]] && { echoerr "$prodosArg not found."; exit 1; }
			[[ $AD && ! -f $rFile ]] && { echoerr "Not an AppleDouble file: $rFile"; exit 1; }

			if [[ $AD ]]; then
				# get metadata from AppleDouble header
				[[ $(dd if="$rFile" bs=1 count=1 skip=741 2> /dev/null | wc -c) -gt 0 ]] && isExtFile=1 || isExtFile=
				fileData=$(dd if="$rFile" bs=1 count=24 skip=637 2> /dev/null | xxd -p | tr -d '\n')
				ftVar="P_${fileData:34:2}";
				auxType="\$"${fileData:36:4}
				cDateTime=$(printf %d 0x${fileData:0:8})
				mDateTime=$(printf %d 0x${fileData:8:8})
				[[ $(printf %d 0x"${fileData:0:2}") -gt 127 ]] && (( cDateTime-=4294967296 )) # handle negative hex number
				[[ $(printf %d 0x"${fileData:8:2}") -gt 127 ]] && (( mDateTime-=4294967296 )) # handle negative hex number
				(( cDateTime+=946684800 )) # convert AD timestamp to Unix timestamp
				(( mDateTime+=946684800 )) # convert AD timestamp to Unix timestamp
			else # EX
				# get metadata from file info
				[[ -f "$rFile" ]] && isExtFile=1 || isExtFile=
				ftVar="P_${prodosArg: -6:2}"
				auxType="\$"${prodosArg: -4}
				if [[ $osx ]]; then
					mDateTime=$(stat -f "%m" "$prodosArg")
				else
					mDateTime=$(stat -c %Y "$prodosArg")
				fi
				cDateTime=$mDateTime
			fi
			[[ ${!ftVar} ]] && ft=${!ftVar} || ft="\$$fc"; # set file type
			# convert unix timestamp to ProDOS bitfield yyyyyyymmmmddddd 000hhhhh00mmmmmm
			cDateFields=($(date -d @$cDateTime +"%y %m %d %H %M" 2> /dev/null))
			[[ ! $cDateFields ]] && cDateFields=($(date -r $cDateTime +"%y %m %d %H %M")) # OS X/BSD
			mDateFields=($(date -d @$mDateTime +"%y %m %d %H %M" 2> /dev/null))
			[[ ! $mDateFields ]] && mDateFields=($(date -r $mDateTime +"%y %m %d %H %M")) # OS X/BSD
			cDateTimeHex=$(printf %08X $(( 2#$(printf %07d $(decToBin ${cDateFields[0]}))$(printf %04d $(decToBin ${cDateFields[1]}))$(printf %05d $(decToBin ${cDateFields[2]}))$(printf %08d $(decToBin ${cDateFields[3]}))$(printf %08d $(decToBin ${cDateFields[4]})) )))
			cDateTimeHex=${cDateTimeHex:2:2}${cDateTimeHex:0:2}${cDateTimeHex:6:2}${cDateTimeHex:4:2}
			mDateTimeHex=$(printf %08X $(( 2#$(printf %07d $(decToBin ${mDateFields[0]}))$(printf %04d $(decToBin ${mDateFields[1]}))$(printf %05d $(decToBin ${mDateFields[2]}))$(printf %08d $(decToBin ${mDateFields[3]}))$(printf %08d $(decToBin ${mDateFields[4]})) )))
			mDateTimeHex=${mDateTimeHex:2:2}${mDateTimeHex:0:2}${mDateTimeHex:6:2}${mDateTimeHex:4:2}

			# create forks and file entry
			### RIGHT HERE PROBLEMS IF #000000 specified and maybe if not
			fileName="${prodosPath##*/}"
			if [[ $isExtFile ]]; then
				dfName=X$(printf %04X $RANDOM $RANDOM $RANDOM)
				while [[ ! $rfName || $rfName == $dfName ]]; do
					rfName=X$(printf %04X $RANDOM $RANDOM $RANDOM)
				done
				while [[ $fileName == "${prodosPath##*/}" || $rfName == $fileName || $dfName == $fileName ]]; do
					fileName=X$(printf %04X $RANDOM $RANDOM $RANDOM)
				done
				$ac -d "$imageArg" "${prodosArgParent}$dfName" 2> /dev/null
				dd if="$prodosArg" 2> /dev/null | $ac -p "$imageArg" "${prodosArgParent}$dfName" $00 2> $acmdStdErr
				$ac -d "$imageArg" "${prodosArgParent}$rfName" 2> /dev/null
				dd if="$rFile" bs=1 skip=$(( 0$AD ? 741 : 0 )) 2> /dev/null | $ac -p "$imageArg" "${prodosArgParent}$rfName" $00 2> $acmdStdErr
			fi

			# create file entry, then find it
			$ac -d "$imageArg" "${prodosArgParent}$fileName" 2> /dev/null
			[[ $isExtFile ]] && ddsrc="if=/dev/zero bs=512 count=1" || ddsrc="if=$prodosArg"
			dd $ddsrc 2> /dev/null | $ac -p "$imageArg" "${prodosArgParent}$fileName" "$ft" "$auxType" 2> $acmdStdErr
			# thx to http://unix.stackexchange.com/a/122945/99697 for perl alternative to broken --byte-offset in grep 2.5.1 (e.g. in OS X)
			fileEntryOffset=$(perl -n0777e "print pos()-length('.$fileName') while /.$fileName/g" < "$imageArg")
			fileEntry=$(dd if="$imageArg" bs=1 count=39 skip=$fileEntryOffset 2> /dev/null | xxd -p | tr -d '\n')

			if [[ $isExtFile ]]; then
				extKeyBlockOffset=$(( ( ( $(printf %d 0x"${fileEntry:36:2}") * 256 ) + $(printf %d 0x"${fileEntry:34:2}") * 512 ) ))

				# find data fork, copy storage type, key block, block size, length to extended key block mini-entry
				# then mark as available/deleted
				dfOffset=$(perl -n0777e "print pos()-length('.$dfName') while /.$dfName/g" < "$imageArg")
				dfEntry=$(dd if="$imageArg" bs=1 count=39 skip=$dfOffset 2> /dev/null | xxd -p | tr -d '\n')
				dfStorageType=$(printf %02X $(( $(printf %d 0x${dfEntry:0:2}) >> 4 )) )
				dfBlocksUsed=$(( ( $(printf %d 0x${dfEntry:40:2}) * 256 ) + $(printf %d 0x${dfEntry:38:2}) ))
				dfInfo=${dfEntry:34:14}
				echo -n -e \\x"$dfStorageType"$(sed 's/../\\x&/g' <<< $dfInfo) \
				  | dd of="$imageArg" conv=notrunc bs=1 seek=$(( extKeyBlockOffset+0 )) 2> /dev/null
				# mark as deleted
				echo -n -e \\x0${dfEntry:1:1} \
				  | dd of="$imageArg" conv=notrunc bs=1 seek=$(( dfOffset+0 )) 2> /dev/null

				# find resource fork, copy storage type, key block, block size, length to extended key block mini-entry
				# then mark as available/deleted
				rfOffset=$(perl -n0777e "print pos()-length('.$rfName') while /.$rfName/g" < "$imageArg")
				rfEntry=$(dd if="$imageArg" bs=1 count=39 skip=$rfOffset 2> /dev/null | xxd -p | tr -d '\n')
				rfStorageType=$(printf %02X $(( $(printf %d 0x${rfEntry:0:2}) >> 4 )) )
				rfBlocksUsed=$(( ( $(printf %d 0x${rfEntry:40:2}) * 256 ) + $(printf %d 0x${rfEntry:38:2}) ))
				rfInfo=${rfEntry:34:14}
				echo -n -e \\x"$rfStorageType"$(sed 's/../\\x&/g' <<< $rfInfo) \
				  | dd of="$imageArg" conv=notrunc bs=1 seek=$(( extKeyBlockOffset+256 )) 2> /dev/null
				# mark as deleted
				echo -n -e \\x0${rfEntry:1:1} \
				  | dd of="$imageArg" conv=notrunc bs=1 seek=$(( rfOffset+0 )) 2> /dev/null

				# reduce active file count in directory by two
				parentDirKeyBlockOffset=$(( ( ( $(printf %d 0x"${fileEntry:76:2}") * 256 ) + $(printf %d 0x"${fileEntry:74:2}") * 512 ) ))
				fileCountHex=$(dd if="$imageArg" bs=1 count=2 skip=$((parentDirKeyBlockOffset+4+33)) 2> /dev/null | xxd -p)
				fileCount=$(( ( $(printf %d 0x${fileCountHex:2:2}) * 256 ) + $(printf %d 0x${fileCountHex:0:2}) ))
				fileCountHex=$(printf %04X $((fileCount - 2)))
				echo -n -e \\x${fileCountHex:2:2}\\x${fileCountHex:0:2} \
				  | dd of="$imageArg" conv=notrunc bs=1 seek=$((parentDirKeyBlockOffset+4+33)) 2> /dev/null

				# update extended file metadata

				# storage type (5), name length, name
				name="${prodosPath##*/}"
				nameLen=${#name}
				nameHeader=$(printf %02X $((nameLen + 80)) )
				nameField=$(echo -n $name | xxd -p | tr -d '\n' | sed -e :a -e 's/^.\{1,29\}$/&00/;ta')

				# blocks used
				blocksUsed=$(( dfBlocksUsed + rfBlocksUsed + 1 ))

				# store updated metadata
				fileEntry=${nameHeader}${nameField}${fileEntry:32}
			fi

			# put creation and modified date in file entry
			fileEntry=${fileEntry:0:48}${cDateTimeHex}${fileEntry:56:10}${mDateTimeHex}${fileEntry:74:4}

			# put casemask for mixed case filename in file entry
			[[ $EX ]] && prodosArg="${prodosArg%#*}"
			if [[ "${prodosPath##*/}" != "${prodosArg##*/}" ]]; then # mixed case
				caseMaskDec=32768
				mixedName="${prodosArg##*/}"
				[[ $EX ]] && mixedName="${mixedName%#*}"
				for (( i=0; i<${#mixedName}; i++ )); do
					[[ "${mixedName:$i:1}" == $(tr [:lower:]  [:upper:] <<< "${mixedName:$i:1}") ]] # $? == 0 means uppercase
					(( caseMaskDec+=$(( $? * (2**(14-i)) )) ))
				done
				caseMaskHex=$(printf %04X $caseMaskDec)
				fileEntry=${fileEntry:0:56}${caseMaskHex:2:2}${caseMaskHex:0:2}${fileEntry:60}
			fi

			# write updated metadata to file entry
			echo -n -e $(sed 's/../\\x&/g' <<< $fileEntry) | dd of="$imageArg" bs=1 conv=notrunc seek=$fileEntryOffset 2> /dev/null
		fi
	fi

else

	imageArg="$2"
	$ac "$@" 2> $acmdStdErr

fi

helpExit