#!/bin/sh

# Replicant GTA04 installer
#
# Copyright (C) 2012-2014 Paul Kocialkowski, GPLv2
#
# Based on mkcard.sh v0.5
# Copyright (C) 2009 Graeme Gregory <dp@xora.org.uk>, GPLv2
# Parts of the procudure base on the work of Denys Dmytriyenko
# http://wiki.omap.com/index.php/MMC_Boot_Format
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

#
# Environment
#

export LC_ALL=C

#
# Globals
#

DRIVE=""
DRIVE_NAME=""
DRIVE_SIZE=""
DRIVE_CYLINDERS=""
DRIVE_PART=""

SYSTEM_ZIP="replicant-4.2-gta04.zip"
BOOTABLE_ZIP="bootable.zip"

FILES_BASE="."
MOUNT_BASE="/media"

#
# Functions
#

# Display

display_help() {
	echo "Usage: $0 [COMMAND] [DRIVE]"
	echo ""
	echo "It is expected that the following files are located in $FILES_BASE:"
	echo "  $BOOTABLE_ZIP for setup"
	echo "  $SYSTEM_ZIP for install"
	echo ""
	echo "Commands:"
	echo "  setup   - setup the drive and copy the base files"
	echo "  install - install the system on the drive"
	echo ""
	echo "Optional arguments:"
	echo "  [DRIVE] - drive node to use"
}

display_banner() {
	echo "Replicant GTA04 installer"
	echo ""
}

display_complete() {
	echo ""
	echo "Process completed, you can now remove the card!"
}

# Drive

drive_select_list() {
	drives_dir="/dev/disk/by-id/"
	count=0

	list=$( ls "$drives_dir" )

	for drive in $list
	do
		drive_nopart=$( echo "$drive" | sed "s/-part[0-9]*$//g" )
		if [ "$drive" = "$drive_nopart" ]
		then
			ls $drives_dir/$drive-part* 2> /dev/null > /dev/null

			if [ $? -eq 0 ]
			then
				count=$(( $count + 1 ))
				name=$( echo "$drive" | sed "s/^[^-]*-\(.*\)$/\1/g" )

				case "$1" in
					"show")
						echo "$count - $name"
					;;
					"store")
						if [ $count -eq $2 ]
						then
							DRIVE=$( readlink -f "$drives_dir/$drive" )
							DRIVE_NAME="$name"
						fi
					;;
				esac
			fi
		fi
	done
}

drive_select() {
	echo "Available devices:"
	drive_select_list "show"

	echo -n "Choice: "
	read choice

	drive_select_list "store" $choice
	echo ""
}

drive_umount() {
	list=$( mount | grep $DRIVE | sed "s|$DRIVE[0-9]* on \([^ ]*\) .*|\1|g" )

	for mount_point in $list
	do
		echo "Unmounting $mount_point"

		umount "$mount_point"
		if [ $? != 0 ]
		then
			echo "Unmounting $mount_point failed, arborting!"
			exit 1
		fi
	done
}

drive_part() {
	if [ -e "${DRIVE}p1" ]
	then
		DRIVE_PART="${DRIVE}p"
	else
		DRIVE_PART="${DRIVE}"
	fi
}

# Setup

setup_drive_confirm() {
	if [ "$DRIVE" = "" ]
	then
		echo "Invalid drive block"
		exit 1
	fi

	list=$( mount | grep $DRIVE | sed "s|$DRIVE[0-9]* on \([^ ]*\) .*|\1|g" )

	echo "This is going to erase the following drive:"

	if [ "$DRIVE_NAME" != "" ]
	then
		echo "- $DRIVE_NAME ($DRIVE)"
	else
		echo "- $DRIVE"
	fi

	for mount_point in $list
	do
		mount_point_check=$( dirname $mount_point | grep -P "^/mnt|mount" )
		if [ "$mount_point_check" != "" ]
		then
			echo ""
			echo "Warning: the drive is mounted as $mount_point!"
			echo "This is probably not the drive you want to use!"
		fi
	done

	echo ""
	echo -n "Are you sure? [Y/N] "
	read confirm

	if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]
	then
		echo ""
		return
	else
		exit 0
	fi
}

setup_drive_empty() {
	drive_umount

	# Backup
	dd if="$DRIVE" of=".drive_start_backup" bs=1024 count=1024

	dd if=/dev/zero of="$DRIVE" bs=1024 count=1024
	if [ $? != 0 ]
	then
		echo "Emptying the drive failed, aborting!"
		exit 1
	fi
}

setup_drive_rescue() {
	if [ -f ".drive_start_backup" ]
	then
		echo -n "Something went wrong, do you want to restore drive start backup? [Y/N] "
		if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]
		then
			dd if=".drive_start_backup" of="$DRIVE" bs=1024 count=1024
		fi
	fi
}

setup_drive_infos() {
	DRIVE_SIZE=$( fdisk -l "$DRIVE" | grep Disk | grep bytes | awk '{print $5}' )
	DRIVE_CYLINDERS=$( echo "$DRIVE_SIZE/255/63/512" | bc )
}

setup_drive_partition() {
	boot_size=$( echo "(50 * 1024 * 1024) / ($DRIVE_SIZE/$DRIVE_CYLINDERS)" | bc )
	system_size=$( echo "(350 * 1024 * 1024) / ($DRIVE_SIZE/$DRIVE_CYLINDERS)" | bc )
	cache_size=$( echo "(100 * 1024 * 1024) / ($DRIVE_SIZE/$DRIVE_CYLINDERS)" | bc )

	{
		echo ",$boot_size,c,*"
		echo ",$system_size,83,-"
		echo ",$cache_size,83,-"
		echo ",,83,-"
	} | sfdisk -D -H 255 -S 63 -C "$DRIVE_CYLINDERS" "$DRIVE"
	if [ $? != 0 ]
	then
		drive_rescue
		exit 1
	fi

	sleep 1

	drive_part

	mkfs.vfat -F 32 -n "boot" "${DRIVE_PART}1"
	mkfs.ext4 -L "system" "${DRIVE_PART}2"
	mkfs.ext4 -L "cache" "${DRIVE_PART}3"
	mkfs.ext4 -L "data" "${DRIVE_PART}4"

	sleep 1
}

setup_boot_install() {
	echo "Installing boot files"

	drive_part

	mkdir -p "$MOUNT_BASE/boot"
	mount "${DRIVE_PART}1" "$MOUNT_BASE/boot"

	unzip -o "$FILES_BASE/$BOOTABLE_ZIP" "**" -d "$MOUNT_BASE/boot/"

	if [ -f "$MOUNT_BASE/boot/splash.rgb16z" ]
	then
		gunzip < "$MOUNT_BASE/boot/splash.rgb16z" > "$MOUNT_BASE/boot/splash.rgb16"
	fi

	dir=$( pwd )
	echo "Syncing boot files"
	cd "$MOUNT_BASE/boot"
	sync
	cd "$dir"

	umount "$MOUNT_BASE/boot"
	rmdir "$MOUNT_BASE/boot"
}

setup_drive_eject() {
	rm -rf ".drive_start_backup"
	eject "$DRIVE"
}

# Install

install_package_extract_dir() {
	destination="$MOUNT_BASE"$( dirname "$2")

	if [ $# -lt 2 ]
	then
		return
	fi

	unzip -o "$FILES_BASE/$SYSTEM_ZIP" "$1/**" -d "$destination"
}

install_package_extract_file() {
	destination="$MOUNT_BASE"$( dirname "$2")

	if [ $# -lt 2 ]
	then
		return
	fi

	unzip -o "$FILES_BASE/$SYSTEM_ZIP" "$1" -d "$destination"
}

install_symlink() {
	source=""

	if [ $# -lt 2 ]
	then
		return
	fi

	for path in $@
	do
		if [ "$source" = "" ]
		then
			source="$path"
			continue
		fi

		unlink "$MOUNT_BASE$path"
		ln -s "$source" "$MOUNT_BASE$path"
	done
}

install_set_perm() {
	uid=""
	gid=""
	mode=""

	if [ $# -lt 4 ]
	then
		return
	fi

	for value in $@
	do
		if [ "$uid" = "" ]
		then
			uid="$value"
			continue
		fi

		if [ "$gid" = "" ]
		then
			gid="$value"
			continue
		fi

		if [ "$mode" = "" ]
		then
			mode="$value"
			continue
		fi

		chown "$uid:$gid" "$MOUNT_BASE$value"
		chmod "$mode" "$MOUNT_BASE$value"
	done
}

install_set_perm_recursive() {
	uid=""
	gid=""
	dir_mode=""
	file_mode=""

	if [ $# -lt 4 ]
	then
		return
	fi

	for value in $@
	do
		if [ "$uid" = "" ]
		then
			uid="$value"
			continue
		fi

		if [ "$gid" = "" ]
		then
			gid="$value"
			continue
		fi

		if [ "$dir_mode" = "" ]
		then
			dir_mode="$value"
			continue
		fi

		if [ "$file_mode" = "" ]
		then
			file_mode="$value"
			continue
		fi

		find "$MOUNT_BASE$value" -type d -exec chown "$uid:$gid" {} \; -exec chmod "$dir_mode" {} \;
		find "$MOUNT_BASE$value" -type f -exec chown "$uid:$gid" {} \; -exec chmod "$file_mode" {} \;
	done
}

install_command() {
	command=$( echo "$1" | sed "s/[ \t]*\([^(]*\)(.*/\1/g" )
	arguments=$( echo "$1" | sed -e "s/[^(]*(\([^)]*\));.*/\1/g" -e "s/[ \t]*,[ \t]*/ /g" | tr -d '"')

	case "$command" in
		"package_extract_dir")
			install_package_extract_dir $arguments
		;;
		"package_extract_file")
			install_package_extract_file $arguments
		;;
		"symlink")
			install_symlink $arguments
		;;
		"set_perm")
			install_set_perm $arguments
		;;
		"set_perm_recursive")
			install_set_perm_recursive $arguments
		;;
	esac
}

install_script() {
	unzip -p "$FILES_BASE/$SYSTEM_ZIP" "META-INF/com/google/android/updater-script" | while read line
	do
		end_test=$( echo "$line" | grep -P "\);$" )

		COMMAND="$COMMAND$line"

		if [ "$end_test" != "" ]
		then
			install_command "$COMMAND"

			COMMAND=""
		fi
	done
}

install_drive_mount() {
	drive_part

	drive_umount

	mkdir -p "$MOUNT_BASE/boot"
	mount "${DRIVE_PART}1" "$MOUNT_BASE/boot"

	mkdir -p "$MOUNT_BASE/system"
	mount "${DRIVE_PART}2" "$MOUNT_BASE/system"
}

install_drive_umount() {
	drive_part

	dir=$( pwd )
	echo "Syncing files"

	cd "$MOUNT_BASE/boot"
	sync
	cd "$dir"

	cd "$MOUNT_BASE/system"
	sync
	cd "$dir"

	umount "$MOUNT_BASE/boot"
	rmdir "$MOUNT_BASE/boot"

	umount "$MOUNT_BASE/system"
	rmdir "$MOUNT_BASE/system"
}

install_drive_eject() {
	eject "$DRIVE"
}

#
# Main
#

if [ $# -lt 1 ] || [ $# -gt 2 ]
then
	display_help
	exit 1
fi

if [ $# -gt 1 ]
then
	DRIVE=$2
fi

display_banner

if [ "$DRIVE" = "" ]
then
	drive_select
fi

case $1 in
	"setup")
		setup_drive_confirm
		drive_umount
		setup_drive_empty
		setup_drive_infos
		setup_drive_partition
		setup_boot_install
		setup_drive_eject

		display_complete
	;;
	"install")
		install_drive_mount
		install_script
		install_drive_umount
		install_drive_eject

		display_complete
	;;
	*)
		display_help
		exit 1
	;;
esac
