#!/bin/bash
# iRetiner 0.4
# Original idea: Ardu
# Thanks to ttwj for bashtrackr, used in iTunes downloading
# Coded by dxs

# Changelog
# -----===-----
# 0.1
# - Initial attempt
#
# 0.2
# - First semi-working version
#
# 0.3
# - Fully functional, uses iTunesArtwork
# - This is the version that was defaced by HackYouriPhone
# - Code cleanup
# - Console interface
#
# 0.4
# - More code cleanup
# - Added proper credits
# - AppTrackr API support (thanks to ttwj for bashtrackr)
# - Nice cosmetic touch-ups
# - Neat progress indicator instead of long list of apps
# - Commments are more readable because of indentation
# - Fixed bug where apps which already have Retina Icons were affected
# - Resizes iTunesArtwork using libjpeg, ImageMagick, and pincrush
# 

# Generic functions
clearline() {
	echo -en \r
	printf "%$(($(tput cols)-1))s"
}
cleanup() {
	cd "$ORIG_DIR"
	if [ "$tmpdir" ] && [ -d "$tmpdir" ]; then
		rm -rf "$tmpdir"
	fi
	
	# Print prompt on a new line if unfinished
	printf "%$(($(tput cols)-1))s\r"
}
debugPause() {
	echo "DEBUG PAUSE"
	read -s -n 1 dummy
}
prompt() {
	local answer=""
	local allowed="YN"
	[ $# == 0 ] && return
	while [ $# -gt 0 ]; do
		case "$1" in
			-a | --allowed-answers )	local allowed="$(echo "$2" | tr [:lower:] [:upper:])"; shift;;
			-p | --prompt )				local prompt="$2"; shift;;
		esac
		shift
	done

	if [ "$prompt" == "$(echo "$prompt" | tr -d [])" ]; then
		local i=0
		local padd="["
		for ((i=0; i<${#allowed}; i++)); do
			[ $i != 0 ] && [ $i != "${#allowed}" ] && local padd+="/"
			local padd+="${allowed:$i:1}"
		done
		local padd+="]"
		prompt+=" $padd: "
	fi
	while [ ! $answer ]; do
		read -t 0 # Clears stdin before reading menu selection
		read -n 1 -p "$prompt" answer; echo
		answer="$(echo "$answer" | tr [:lower:] [:upper:] | tr -dc "$allowed")"
		
		local i=0
		for ((i=0; i<${#allowed}; i++)); do
			local char="${allowed:$i:1}"
			if [ "$answer" == "$char" ]; then
				ret="$answer"
				return $i
			fi
		done
		echo "Invalid response"
		answer=""
	done
}

# Functions specific to iRetiner.sh
artworkToIcon() {
	local target="$1"
	local iTunesArtwork="$2"
	[ ! -f "$iTunesArtwork" ] && continue
	
	local tmpicon="$(mktemp -u "$tmpdir/Icon-XXXXXXXX").jpg"
	djpeg -outfile "$tmpicon" "$iTunesArtwork"
	rm -rf "$target"
	convert "$tmpicon" -resize "$RETINA_SIZE" "$tmpicon.png"
	pincrush "$tmpicon.png" "$target" 2>/dev/null
	rm -rf "$tmpicon" "$tmpicon.png"
}
backup() {
	local file="$1"
	if [ -f "$file" ] && [ ! -f "$file.orig" ]; then
		local timestamp="$(mktemp "$tmpdir/timestamp-XXXXXXXX")"
		touch --reference="$file" "$timestamp"
		cp -p "$file" "$file.orig"
		touch --no-create --reference "$timestamp" "$file"
		touch --no-create --reference "$timestamp" "$file.orig"
		rm -rf "$timestamp"
	fi
}		
findCFBundleIconFile() {
	local fullpath="$1"
	local CFBundleIconFile="$(plutil -key CFBundleIconFile "$fullpath/Info.plist" 2>/dev/null)"
	if [ ! -f "$fullpath/$CFBundleIconFile" ]; then
		for CFBundleIconFile in "Icon.png" "icon.png" "Icon.PNG" "icon.PNG" "ICON.png" "ICON.PNG"; do
			[ -f "$fullpath/$CFBundleIconFile" ] && break
		done
	fi
	echo "$CFBundleIconFile"
}
ignoreicon() { # Returns 0 for continue, 1 for ignore
	local fullpath="$1"
	local CFBundleIconFile="$2"
	
	local test="$(echo -En "$CFBundleIconFile" | sed 's/\(.*\)\..*/\1/')"
	if [ ! -f "$fullpath/$CFBundleIconFile" ] || [ -f "$fullpath/$CFBundleIconFile.orig" ] || \
		[ -f "$fullpath/$test@2x.png" ] || [ -f "$fullpath/$test@2x.PNG" ] || \
		[ -f "$fullpath/$test@2X.png" ] || [ -f "$fullpath/$test@2X.PNG" ]; then
		ret=1
	else
		ret=0
	fi
	return "$ret"
}
setupvars() {
	local sandpath="$1"
	baseapp="$(basename "$sandpath/"*.app 2>/dev/null)"
	iTunesArtwork="$sandpath/iTunesArtwork"
	Info="$sandpath/$baseapp/Info.plist"
	fullpath="$sandpath/$baseapp"
}

# bashtrackr, apptrackr API bash library (by ttwj)
# http://hackulo.us/forums/index.php?/topic/98106-release-bashtrackr-apptrackr-api-library-in-bash/
# apptrackr API info as of Nov 2010
# Version 0.1, by ttwj
# Thanks to
# MadHouse - For JSON parsing methods and help with apptrackr API
# ajm2k - For JSON parsing methods
# People on #bash on freenode for helping
data_parse() {
	# data.fetch <string>
	# fetch the code
	code=$(echo "$1" | tr '{', '\n' | sed -n "2p" | sed -e 's/:/ /' | awk '{print $2}')
	if [ "${code:0:1}" != "2" ]; then
		# :(
		# echo "Error: response code $code was returned, please check http://hackulo.us/fo...-apptrackr-api/ for list of response codes"
		false
	else
		string=$(echo "$1" | tr '{', '\n' | sed -e '/"signature":\(.*\)/d;/"data":\(.*\)/d;/"signature":\(.*\)/d;/"code":\(.*\)/d' |sed -e 's/\\"//g;s/\\\\\\\//\//g;s/\\//g' | tr '}', '\n' | sed -e 's/"//' | sed '/^$/d')
		echo "$string"
	fi
}
request_send() {
	# array.send <string> <API Location (optional)>
	# quote string
	string=$(echo "$1" | sed -e 's/"/\\"/g')
	string='{"request":"'"$string"'"}'
	if [ "$2" ]; then
		api="$2"
	else
		api="http://api.apptrackr.org"
	fi
	output=$(curl -s -d "request=$string" "$api")
	echo "$output"
}
app_checkUpdates() {
	#app.checkUpdates <app ids>
	for i in ${@}; do
		appids=$((appids+1))
		if [ "$appids" -gt "100" ]; then
			echo "Error, cannot check for more than 100 updates."
			break
			return 0
		fi
		if [ "$appids" -gt "1" ]; then
			string="$string, $i"
		else
			string="$i"
		fi
	done	
	json='{"object":"App","action":"checkUpdates","args":{"appids":['"$string"']}}'
	echo "$json"
}
app_getDetails() {
	# app_getDetails <app id> <fields>
	supported_list="appid scrapeid add_date last_modification itunesurl name seller category storeid size latest_version release_date copyright price rating icon57 icon75 icon100 icon175 description whatsnew requirements languages universal"
	for i in ${@:2}; do
		if [ "$(echo "$supported_list" | grep -w "$i")" ]; then
				if [ ! "$field" ]; then
					field="\"$i\""
				else
					field="$field, \"$i\""
				fi
			else
				#echo "Error: unknown field requested, please check http://hackulo.us/fo...-apptrackr-api/ for a list of supported fields"
				return 0
		fi
	done
	json='{"object":"App","action":"getDetails","args":{"app_id":'$1',"fields":['$field']}}'
	echo "$json"
}
app_getList() {
	#app.getList <appsPerPage> <sort> <page> <search (optional)> <category (optional)> <showPageCount (optional)> <deviceid (optional)>
	#do some checks..
	if [ ! "$1" ]; then echo "Error: Missing appsPerPage"; return 0; fi
	if [ ! "$2" ]; then echo "Error: Missing sort"; return 0; fi
	if [ ! "$3" ]; then echo "Error: Missing page"; return 0; fi
	if [ ! "$(echo '15 30 60 null' | grep -w "$1")" ]; then echo "Error: appsPerPage can only be 15, 30 or 60, please check the apptrackr API documentation for more info"; return 0; fi
	if [ ! "$(echo '0 1 2 null' | grep -w "$8")" ]; then echo "Error: deviceid can only be 0 (All), 1 (iPhone/iPod Touch) and 2 (iPad)."; return 0; fi
	if [ ! "$4" = "null" ]; then 
		string=", \"search\":\"$4\""
	else
		string=""
	fi
	if [ ! "$5" = "null" ]; then
		string="$string, \"category\":$5"
	fi
	if [ ! "$6" = "null" ]; then
		string="$string, \"showPageCount\":$6"
	fi
	if [ ! "$7" = "null" ]; then
		string="$string, \"deviceid\":$7"
	fi
	json='{"object": "App","action":"getList","args":{"appsPerPage":'$1',"sort":'$2',"page":'$3''$string'}}'
	echo "$json"
}
app_scrape() {
	#this part was the easiest to do :3
	#app.scrape <itunes url>
	if [[ ! "$1" = *http*://*itunes*.*apple*.*com* ]]; then
		echo "Error: Not a valid iTunes URL"
		return 0
	fi
	json='{"object":"App","action":"scrape","args":{"itunes_url":"'$1'"}}'
	echo "$json"
}
bundle_getItunesIDs() {
	# getItunesIDs <app ids>
	[ "${#@}" -le 0 ] && return 1

	local string="\"$1\""
	shift
	local appid=""
	for appid in ${@}; do
		string="$string, \"$appid\""
		shift
	done

	json="{\"object\":\"Bundle\",\"action\":\"getItunesIDs\",\"args\":{\"bundleList\":[$string]}}"
	echo "$json"
}
category_getList() {
	json='{"object": "Category","action": "getList"}'
	echo "$json"
}
link_get() {
	#link.get <appid>
	if [ ! "$1" ]; then echo "Error: Please input an appid"; return 0; fi
	json='{"object": "Link","action": "get","args": {"app_id": '$1'}}'
	echo "$json"
}
screenshot_get() {
	#screenshot.get <appid>
	if [ ! "$1" ]; then echo "Error: Please input an appid"; return 0; fi
	json='{"object": "Screenshot","action": "get","args": {"app_id": '$1'}}'
	echo "$json"
}
user_checkAuth() {
	#user.checkAuth <username> <passhash>
	if [ ! "$1" ]; then echo "Error: Please input a username"; return 0; fi
	if [ ! "$2" ]; then echo "Error: Please input a passhash"; return 0; fi
	json='{"object": "User","action": "checkAuth","auth": {"username": "'$1'","passhash": "'$2'"}}'
	echo "$json"
}
# end bashtrackr code

# Constants
ICONCACHE_DIR="/var/mobile/Library/Caches/com.apple.IconsCache"
LIST_SPLIT_LIMIT="20"
ORIG_DIR="$PWD"
RETINA_SIZE="114x114"
SANDBOX_ROOTS="/var/mobile/Applications"
SCRIPT_NAME="iRetiner.sh"
SCRIPT_VERSION="0.4"

trap "cleanup; exit" ABRT EXIT HUP INT TERM QUIT
ret=0
menuOptions=("Adapt all icons using iTunesArtwork " "Download and adapt specific icons from iTunes" \
	"Revert adapted Retina icons to original icons" \
	"Help/About" "Exit")

# ----====== Interface begins here ======----
clear
echo "$SCRIPT_NAME ($SCRIPT_VERSION)"
printf "%$(tput cols)s" | tr " " "="

# Dependency checking
for dependency in com.ericasadun.utilities \
	coreutils curl gawk grep imagemagick \
	jpeg pincrush sed shell-cmds wget; do
	if [ ! -f "/var/lib/dpkg/info/$dependency.list" ]; then
		[ ! "$missing" ] && missing="$dependency"
		[ "$missing" ] && missing+=" $dependency"
	fi
done
if [ "$missing" ]; then
	echo "$SCRIPT_NAME depends on the missing package(s) $missing" >&2
	if [ ! -f "/usr/bin/apt-get" ]; then
		echo "Install from Cydia to fix" >&2
		exit 1
	elif [ -f "/usr/bin/apt-get" ]; then
		prompt -p "Install now"
		if [ "$ret" == "Y" ]; then
			apt-get --force-yes install $missing
		elif [ "$ret" == "N" ]; then
			echo "Aborting" >&2
			exit 1
		fi
	fi
fi

[ "$(deviceinfo -c | grep -i -e M68AP -e N82AP -e N88AP -e N45AP -e N72AP -e N18AP)" ] && RETINA_SIZE="57x57"

tmpdir="$(mktemp -d /tmp/$(basename "$0" .sh)-XXXXXXXX)"

PS3="Select an option: "
select menuSelectionText in "${menuOptions[@]}"; do
	menuSelectionNumber="$(echo "$REPLY" | tr -dc 0-9)"
	if [ ! "$menuSelectionNumber" ] || [ ! "${menuOptions[$(($menuSelectionNumber - 1))]}" ]; then
		echo "Invalid selection"
		continue
	fi
	break
done

case $menuSelectionNumber in
	${#menuOptions[@]} ) # Exit
	exit 0
	;;
		
	$((${#menuOptions[@]} - 1)) ) # Help screen
	echo -e "Usage: $(basename "$0")"
	echo -e "Coded by dxs"
	echo -e "Original idea by Ardu"
	echo -e "Thanks to ttwj for the bashtrackr library, used in the download method"
	echo
	echo -e "Report bugs to dxs444@gmail.com"
	echo -e "Adds Retina Display icons to AppStore apps"
	echo -e "As usual, use at your own risk."
	echo
	echo -e "Neither the iTunesArtwork method or the download method is superior to one another, only the source of the image differs"
	echo
	echo -e "If you have used version 0.3 of $SCRIPT_NAME, you may have noticed the speed at which apps are processed is slower"
	echo -e "This is because the new icons are now being converted and resized, so to improve operating system performance"
	exit 0
	;;
	
	* ) #Confirm action
	echo
	echo "Operation: $menuSelectionText"
	prompt -p "Confirm action"
	if [ $ret == "N" ]; then
		echo "Aborted"
		exit 3
	fi
	;;& # Special ";;&" allows case to keep on checking following conditions
	
	1 ) # iTunesArtwork method
	progress_current=0
	progress_full="$(ls -x -1 "$SANDBOX_ROOTS" | wc -l)"
	ls -x -1 "$SANDBOX_ROOTS" | while read sandbox; do	
		((progress_current++))
		progress_percent=$(($progress_current*100 / $progress_full))
		echo -en "\rAdding Retina Display icons $progress_percent% ($progress_current/$progress_full)"
		
		setupvars "$SANDBOX_ROOTS/$sandbox"
		[ ! -f "$Info" ] && continue
		[ ! -f "$iTunesArtwork" ] && continue
		
		CFBundleIconFile="$(findCFBundleIconFile "$fullpath")"
		[ ! -f "$fullpath/$CFBundleIconFile" ] && continue
		
		ignoreicon "$fullpath" "$CFBundleIconFile"
		[ "$ret" == 1 ] && continue
			
		target="$fullpath/$CFBundleIconFile"
		touch --reference="$target" "$tmpdir/timestamp.tmp"
		backup "$target"
		artworkToIcon "$target" "$iTunesArtwork"
		touch --no-create --reference="$tmpdir/timestamp.tmp" "$target"
	done; echo # This last "echo" ends the progress indicator
	;;
	
	2 ) # AppTrackr API method
	# Database is built as AppList.txt
	# Format of AppList.txt:
	# (Sandbox Path + Basename) (CFBundleIdentifier) (CFBundleIconFile) (iTunes ID) (iTunes URL) (Icon175 Link)
	echo -e "Listing apps"
	rm -rf "$tmpdir/"*.tmp
	i="$(echo -En "$SANDBOX_ROOTS/00000000-0000-0000-0000-000000000000/Dummy.app" | tr -dc / | wc -m)"
	ls -x -1 -d "$SANDBOX_ROOTS"/*/*.app 2>/dev/null | sed -e 's,\(.\)/$,\1,' | sort -f -k "$i" -t'/' > "$tmpdir/AppList.tmp"
	if [ ! -f "$tmpdir/AppList.tmp" ]; then
		echo "No apps are installed"
		exit 3
	fi
	
	oldIFS="$IFS"; IFS=$'\n'
	numApps="$(cat "$tmpdir/AppList.tmp" | wc -l 2>/dev/null)"
	numField="$(($(echo -En "$SANDBOX_ROOTS" | tr -dc "/" | wc -m) + 2))"
	PS3="Select app(s), seperated by space(s): "
	select baseapp in $(cat "$tmpdir/AppList.tmp" | awk -F / '{print $NF}' | sed 's/\.app$//g'); do
		i="$(echo -En "$REPLY" | tr -dc "0-9 ")"
		if [ "$i" == 0 ] || [ "$REPLY" == "TERMINATE" ]; then
echo "Terminated"
			exit 4
		else
			break
		fi
	done
IFS="$oldIFS"
	for selection in $i; do
		if [ ! "$selection" ] || [ "$selection" -lt 1 ] || [ "$selection" -gt "$numApps" ]; then
			echo "Invalid selection \"$selection\""
			continue
		else
			awk "NR == $selection {print; exit}" "$tmpdir/AppList.tmp" | cut -d "/" -f "$numField" >> "$tmpdir/Queue.tmp"
echo -En "Queued :"
awk "NR == $selection {print; exit}" "$tmpdir/AppList.tmp" | cut -d "/" -f "$numField"
		fi
	done

	if [ ! -f "$tmpdir/Queue.tmp" ]; then
		echo "No apps selected"
		exit 2
	fi
	# Listing apps
	progress_current=0
	progress_full="$(ls -x -1 "$SANDBOX_ROOTS" | wc -l)"
	cat "$tmpdir/Queue.tmp" | while read sandbox; do
		((progress_current++))
		progress_percent=$(($progress_current*100 / $progress_full))
		echo -en "\rListing apps $progress_percent% ($progress_current/$progress_full)"

		setupvars "$SANDBOX_ROOTS/$sandbox"
		
		# Finding CFBundleIdentifier
		CFBundleIdentifier="$(plutil -key CFBundleIdentifier "$fullpath/Info.plist" 2>/dev/null)"
		[ ! "$CFBundleIdentifier" ] && continue
	
		CFBundleIconFile="$(findCFBundleIconFile "$fullpath")"
		
		ignoreicon "$fullpath" "$CFBundleIconFile"
		[ "$ret" == 1 ] && continue
	
		# Self-explanatory
		echo -En "$fullpath" >> "$tmpdir/AppList.txt"
		echo -en $'\t' >> "$tmpdir/AppList.txt"
		echo -En "$CFBundleIdentifier" >> "$tmpdir/AppList.txt"
		echo -en $'\t' >> "$tmpdir/AppList.txt"
		echo -E "$CFBundleIconFile" >> "$tmpdir/AppList.txt"
	done; echo
	rm -rf "$tmpdir/"*.tmp
	
	# Getting iTunesIDs
	progress_current=0
	progress_full="$(cat "$tmpdir/AppList.txt" | wc -l)"
	i=0; cat "$tmpdir/AppList.txt" | while read line; do
		((progress_current++))
		progress_percent=$(($progress_current*100 / $progress_full))
		echo -en "\rReading iTunesMetadata.plist $progress_percent% ($progress_current/$progress_full)"
		
		sandboxPath="$(echo -En "$line" | cut -d $'\t' -f 1)"
		CFBundleIdentifier="$(echo -En "$line" | cut -d $'\t' -f 2)"
		iTunesMetadata="$(dirname "$sandboxPath" 2>/dev/null)/iTunesMetadata.plist"
		
		while [ 1 ]; do
			if [ -f "$iTunesMetadata" ]; then
				iTunesID="$(plutil -key itemId "$iTunesMetadata" 2>/dev/null)"
				if [ "$iTunesID" ]; then
					echo -En "$line" >> "$tmpdir/AppListNew.tmp"
					echo -en $'\t' >> "$tmpdir/AppListNew.tmp"
					echo -E "$iTunesID" >> "$tmpdir/AppListNew.tmp"
					break
				fi
			fi
			pi="$(($i / $LIST_SPLIT_LIMIT))"
			echo -E "$line" >> "$tmpdir/RequestID[$pi].tmp"
			((i++))
			break
		done
	done; echo
	
	progress_current=0
	progress_full="$(ls "$tmpdir/RequestID"*".tmp" | wc -l)"
	oldIFS="$IFS"; IFS=" "
	pi=0; while [ -f "$tmpdir/RequestID[$pi].tmp" ]; do
		((progress_current++))
		progress_percent=$(($progress_current*100 / $progress_full))
		echo -en "\rRequesting iTunesIDs $progress_percent% (batch $progress_current/$progress_full)"
		
		list_CFBundleIdentifier="$(cat "$tmpdir/RequestID[$pi].tmp" | cut -d $'\t' -f 2 | tr $'\n' ' ')"
		data_parse "$(request_send "$(bundle_getItunesIDs $list_CFBundleIdentifier)")" | cut -d ":" -f 2 > "$tmpdir/iTunesID.tmp"
		
		i=1; cat "$tmpdir/iTunesID.tmp" | while read line; do
			# Fancy abracadabra with awk gets line number "$i" from RequestID[$pi].tmp
			awk "NR == $i {print; exit}" "$tmpdir/RequestID[$pi].tmp" | tr -d $'\n' >> "$tmpdir/AppListNew.tmp"
			echo -en $'\t' >> "$tmpdir/AppListNew.tmp"
			echo -E "$line" >> "$tmpdir/AppListNew.tmp"
			((i++))
		done
		((pi++))
	done; echo
	IFS="$oldIFS"
	mv "$tmpdir/AppListNew.tmp" "$tmpdir/AppList.txt"
	rm -rf "$tmpdir/"*.tmp
	
	# Requesting iTunes URLs
	progress_current=0
	progress_full="$(cat "$tmpdir/AppList.txt" | wc -l)"
	cat "$tmpdir/AppList.txt" | while read line; do
		((progress_current++))
		progress_percent=$(($progress_current*100 / $progress_full))
		echo -en "\rRequesting iTunes URLs $progress_percent% ($progress_current/$progress_full)"
		
		iTunesID="$(echo -En "$line" | cut -d $'\t' -f 4)"
		echo -En "$line" >> "$tmpdir/AppListNew.tmp"
		echo -en $'\t' >> "$tmpdir/AppListNew.tmp"
		
		data_parse "$(request_send "$(app_getDetails $iTunesID itunesurl)")" | tr -d $'\n'| sed 's/^.*http/http/' >> "$tmpdir/AppListNew.tmp"
		echo -e >> "$tmpdir/AppListNew.tmp"
		((i++))
	done; echo
	mv "$tmpdir/AppListNew.tmp" "$tmpdir/AppList.txt"

	# Requesting icon links
	progress_current=0
	progress_full="$(cat "$tmpdir/AppList.txt" | wc -l)"
	cat "$tmpdir/AppList.txt" | while read line; do
		((progress_current++))
		progress_percent=$(($progress_current*100 / $progress_full))
		echo -en "\rRequesting image links $progress_percent% ($progress_current/$progress_full)"
		
		iTunesURL="$(echo -En "$line" | cut -d $'\t' -f 5)"
		echo -En "$line" >> "$tmpdir/AppListNew.tmp"
		echo -en $'\t' >> "$tmpdir/AppListNew.tmp"
		data_parse "$(request_send "$(app_scrape $iTunesURL)")" | grep "icon-175:" | tr -d $'\n'| sed 's/^.*http/http/' >> "$tmpdir/AppListNew.tmp"
		echo -e >> "$tmpdir/AppListNew.tmp"
		((i++))
	done; echo
	mv "$tmpdir/AppListNew.tmp" "$tmpdir/AppList.txt"
	
	# Downloading and writing files
	progress_current=0
	progress_full="$(cat "$tmpdir/AppList.txt" | wc -l)"
	cat "$tmpdir/AppList.txt" | while read line; do
		((progress_current++))
		progress_full="$(cat "$tmpdir/AppList.txt" | wc -l)"
		echo -en "\rDownloading and writing new icon files $progress_percent% ($progress_current/$progress_full)"
		
		fullpath="$(echo -En "$line" | cut -d $'\t' -f 1)"
		CFBundleIconFile="$(echo -En "$line" | cut -d $'\t' -f 3)"
		link="$(echo -En "$line" | cut -d $'\t' -f 6)"
		
		target="$fullpath/$CFBundleIconFile"
		touch -r "$target" "$tmpdir/timestamp.tmp"
		
		backup "$target"
		rm -rf "$tmpdir/Icon.jpg"
		wget -q --output-document="$tmpdir/Icon.jpg" "$link"
		artworkToIcon "$target" "$tmpdir/Icon.jpg"
		rm -rf "$tmpdir/Icon.jpg"
		
		[ ! -f "$target" ] && cp -p "$target.orig" "$target" # Failed
		touch --no-create -r "$tmpdir/timestamp.tmp" "$target"
	done; echo
	;;
	
	3 ) # Revert to original icons
	progress_current=0
	progress_full="$(ls -x -1 "$SANDBOX_ROOTS" | wc -l)"
	ls -x -1 "$SANDBOX_ROOTS" | while read sandbox; do	
		((progress_current++))
		progress_percent=$(($progress_current*100 / $progress_full))
		echo -en "\rReverting to original icons $progress_percent% ($progress_current/$progress_full)"

		setupvars "$SANDBOX_ROOTS/$sandbox"
		[ ! -f "$Info" ] && continue
		
		CFBundleIconFile="$(findCFBundleIconFile "$fullpath")"
		target="$fullpath/$CFBundleIconFile"
		[ ! -f "$target.orig" ] && continue
		
		touch --reference="$target.orig" "$tmpdir/timestamp.tmp"
		mv "$target.orig" "$target"
		touch --no-create --reference="$tmpdir/timestamp.tmp" "$target"
	done; echo
	;;
esac

echo "Clearing the icon cache"
rm -rf "$ICONCACHE_DIR"/*

prompt -p "Respring now"
if [ $ret == "N" ]; then
	echo "A respring/reboot should be performed later"
elif [ $ret == "Y" ]; then  
	echo "Performing respring"
	cleanup
	killall SpringBoard
fi

# Amen.
exit 0
