#!/bin/sh

# Run while inside a directory containing the [name]_[number].bin files
# extracted from the .dz or .kdz ROM file

Usage()
{
	printf 'Usage: %s <filename>\n' "$prgnam" >&2
	printf '\tSome of the partition binaries in the .kdz or .dz files\n' >&2
	printf '\t  come split in multiple files, this script correctly\n' >&2
	printf '\t  reassembles them to one dd(1)-able image.\n' >&2
	printf '\tFor <filename> specify one of the filenames partici-\n' >&2
	printf '\t  pating in such a group, it does not matter which one.\n' >&2
	printf '\tA partition name will also do (E.g. "system" instead of\n' >&2
	printf '\t  "system_123456.bin")\n\n' >&2
	printf '2015, Timo Buhrmester <fstd.lkml@gmail.com>\n' >&2
	exit 1
}

# Trace if we're run with -x
if [ "x$1" = 'x-x' ]; then
	set -x
	shift
fi

if [ "x$1" = 'x-h' ]; then
	Usage
fi

prgnam="$(basename "$0")"

Chat()
{
	printf '%s: %s\n' "$prgnam" "$1" >&2
}

# Complain loudly, remove incomplete output file (unless asked not to) and exit
Bomb()
{
	printf '%s: ERROR: %s\n' "$prgnam" "$1" >&2
	printf 'Re-run with -x (i.e. "%s -x %s") and email me the COMPLETE\n"' \
	    "$prgnam" "$in" >&2
	printf ' output (fstd.lkml@gmail.com) if this is a bug.\n' >&2

	if [ -z "$2" ]; then
		printf '%s: Removing incomplete output file\n' "$prgnam" >&2
		rm -f "$out"
	fi
	exit 1
}


if [ -z "$1" ]; then
	Usage
fi

# Allow to give either a base name or a full filename
# For the images comprised of multiple parts, any filename will do
in="$(printf '%s' "$1" | sed 's/_[0-9]*\.bin$//')"

if ! ls ${in}_[0-9]*.bin >/dev/null 2>/dev/null; then
	Bomb "There's no file named '${in}_[number].bin' around here."
fi

stat=
# Find out how to stat(1) on this platform
case $(uname -s) in
	Linux) stat='stat -c %s' ;;
	*BSD) stat='stat -f %z' ;;
	*) Bomb "Don't know how to stat(1) on this platform, pls fix" ;;
esac

Getsize()
{
	printf '%s/512\n' "$($stat "$1")" | bc
}

# Create tempfile with sorted list of ${in}_*.bin files id'ed by their offsets
# Note that this works well for the case when there's just one file around.
tmp=$(mktemp /tmp/glue.XXXXXXXX)
trap 'rm -f "$tmp"' EXIT
ls "${in}"_[0-9]*.bin | sed -e 's/^.*_//' -e 's/\.bin$//' | sort -n >$tmp

# Calculate size of resulting image
first_off=$(head -n1 $tmp)
last_off=$(tail -n1 $tmp)
last_sz=$(Getsize ${in}_${last_off}.bin)
img_sz=$((last_off + last_sz - first_off))

num_files=$(wc -l <$tmp)

# This is the name of the file we're going to produce
out="${in}_${first_off}.img"

# Check we're not accidentally going to overwrite an existing image"
if [ -f "$out" ]; then
	Bomb "not touching already existing file '$out', remove manually" keep
fi


Chat "Creating preliminary zero image"
if ! dd if=/dev/zero of="$out" bs=512 count=$img_sz; then
	ret=$?
	Bomb "Couldn't create preliminary zero-image (dd: $ret)"
fi

# Now walk over the ${in} images and write them at their respective offsets,
# accounting for the fact that they're relative to the beginning of the device,
# not to the beginning of the ${in} partition.
export i=1
cat $tmp | while read -r offset; do
	f=${in}_${offset}.bin

	Chat "Writing '$f' ($i/$num_files)'"
	if ! dd if=$f of="$out" bs=512 seek=$((offset-first_off)) \
	    conv=notrunc count=$(Getsize $f); then
		ret=$?
		Bomb "Failed to write ${in}_${offset}.bin into image (dd: $ret)"
	fi

	i=$((i+1))
done

Chat "Success! Output file: '$out'"
exit 0

# 2015, Timo Buhrmester <fstd.lkml@gmail.com>
