#!/bin/sh
# Remaster script - take a snapshot of the currently running system and create a new iso file
# Copyright (C) James Budiono 2012, 2013, 2014, 2018, 2019, 2020
# License: GNU GPL Version 3 or later
#
# Note: this tool assumes you're running it from standard initrd.
# Running it from small/medium/nano initrd is untested and may not work very well
#
# Version 3 - minor changes to work with syslinux 6
# Version 2 - add EFI support
# Version 4 - supports "medium" initrd
# Version 5 - support "nano" initrd
# Version 6 - support output to a ZIP file with kernel + initrd only, SGD2 support, microcode support
#
#131119 L18L internationalisation

# std localisation stanza
export TEXTDOMAIN=fatdog
. gettext.sh
# performance tweak - use "C" if there is no localisation
! [ -e $TEXTDOMAINDIR/${LANG%.*}/LC_MESSAGES/$TEXTDOMAIN.mo ] &&
! [ -e $TEXTDOMAINDIR/${LANG%_*}/LC_MESSAGES/$TEXTDOMAIN.mo ] && LANG=C

### configuration variables
# hardcoded configuration: "pup_init" and "kernel-modules"
APPTITLE="$(gettext 'Remaster Fatdog64')"
ISOLINUX_PATH=/usr/share/syslinux
ISOLINUX_BINS="isolinux.bin vesamenu.c32 libcom32.c32 libutil.c32 ldlinux.c32"
LOGO_FILE=/usr/share/backgrounds/fatdog-remastered.png
OUTPUT_TYPE=ISO # default is ISO
OUTPUT=remaster-$(date +%Y-%m-%d).iso
DEFAULT_MKSQUASHFS_OPTIONS="-comp xz"
MKSQUASHFS_OPTIONS=${MKSQUASHFS_OPTIONS:-$DEFAULT_MKSQUASHFS_OPTIONS}
MICROCODE_REPO_URL="http://distro.ibiblio.org/fatdog/kernels"

. $BOOTSTATE_PATH   			# AUFS_ROOT_ID BASELINE_MOUNT MODULE_SFS_MOUNT BASE_SFS_PATH AUFS_ROOT
BASESFS=${BASE_SFS_PATH##*/}	# fd64-600.sfs

# modules required for small initrd
BASE_MODULES="cryptoloop nbd cifs aufs usb-storage ohci uhci ehci xts hid-generic \
dm-crypt dm-snapshot sg crypto_simd cryptd glue_helper algif_skcipher af_alg" 

### runtime variables
layers=				# layers chosen for remaster
tmp_aufs=			# location of temparary aufs layer 
tmpfs=				# location of temporary tmpfs layer (this becomes first layer of aufs)
req_size=			# required size to hold outputs (compensated with compression)
savelocation=		# where the ISO will be saved
kernellocation=		# where to find the kernel
efibootlocation=	# where to find efiboot.img
initrd_type=standard # standard, small, medium, nano
microcode=yes       # whether to include microcode update: yes/no

### wizard step movements
step=1
prev_action=
prev_step=

go_step() { step=$1; }
go_next() { step=$((step + 1)); }
go_prev() { step=$((step - 1)); }
go_exit() { step=exit; }
next_wizard_step() {
	local action=$?
	[ "$1" ] && action=$1
	prev_step=$step
	case $action in
		0) go_next ;;				# Xdialog ok
		3) go_prev ;;				# Xdialog previous
		1|255) go_step cancel ;;	# Xdialog Esc/window kill
	esac
	prev_action=$action
}

# parameter: $1 - use $BACKTITLE
std_wizard_flags() {
	local flags="--title \"$APPTITLE\" --wizard --stdout --separator :"
	[ "$1" ] && flags=$flags" --backtitle \"$BACKTITLE\""
	echo $flags
}

### gui helpers
#$1-msg
info() {
	Xdialog --title "$APPTITLE" --infobox "$1" 0 0 10000
}

#$1-msg
splash() {
	close_splash
	Xdialog --title "$APPTITLE" --no-buttons --infobox "$1" 3 60 1000000 &	
	XPID=$!
}
close_splash() {
	[ $XPID ] && kill $XPID
	unset XPID
}

#$1-msg $2-backtitle 
msg() {
	if [ "$2" ]; then
		Xdialog --title "$APPTITLE" --backtitle "$2" --msgbox "$1" 0 0
	else
		Xdialog --title "$APPTITLE" --msgbox "$1" 0 0
	fi
}

#$1-msg or "--fill", $2-msg
yesno() {
	if [ "$1" = "--fill" ]; then
		Xdialog --title "$APPTITLE" --fill --yesno "$2" 0 0
	else
		Xdialog --title "$APPTITLE" --yesno "$1" 0 0
	fi
}

### helper - taken from fatdog-split-initrd
### get BASE_MODULES with list of required modules and their dependencies
# $1-BASE_MODULES $2-MODULES_DEP output: returns BASE_MODULES
get_modules_dep() {
	local OLD_MODULES GREP_PARAM MODULES_DEP
	BASE_MODULES=$1 MODULES_DEP=$2
	while [ ! "$OLD_MODULES" = "$BASE_MODULES" ]; do
		OLD_MODULES=$BASE_MODULES
		get_grep_param $BASE_MODULES
		set -- $(grep -E "$GREP_PARAM" $MODULES_DEP)
		while [ $1 ]; do
			case $1 in
				*:) ;;
				*)	new=${1##*/}; new=${new%.ko}; 
					case $BASE_MODULES in 
						*$new*) ;;
						*) BASE_MODULES="$BASE_MODULES $new"
					esac ;;
			esac
			shift
		done
	done
}
# $*-modules, returns GREP_PARAM
get_grep_param() {
	GREP_PARAM=
	while [ $1 ]; do
		GREP_PARAM="$GREP_PARAM|/$1.ko:"
		shift
	done
	GREP_PARAM=${GREP_PARAM#|}
}

### helper functions
cleanup() {
	if [ "$tmp_aufs" ]; then
		umount $tmp_aufs
		umount $tmpfs
		rmdir $tmpfs $tmp_aufs
		tmpfs= && tmp_aufs=
	fi
}

### create a temporary aufs layers for remaster purpose
# parameter: $1 - all aufs layers to include
create_tmp_aufs() {
	local OIFS branches
	
	OIFS=$IFS; IFS=:
	set -- $1
	IFS=$OIFS
	
	tmpfs=$(mktemp -dp /tmp remaster-tmpfs.XXXXXX)
	tmp_aufs=$(mktemp -dp /tmp remaster-aufs.XXXXXX)
	mount -t tmpfs tmpfs $tmpfs 
	chmod 0755 $tmpfs $tmp_aufs
	while [ $1 ];do
		branches=$branches:$1=ro+wh
		shift
	done
	branches=$tmpfs=rw$branches
	if ! mount -t aufs -o br=$branches aufs $tmp_aufs; then
		info "$(gettext 'Cannot create temporary aufs mount, bailing out.')"
		go_step exit
		return
	fi
}

### map layers to locations - for display purposes
# parameter: $layer
map_layers_to_locations() {
	{ echo ==mount==; mount; 
	  echo ==losetup==; losetup-FULL -a; 
	  echo ==layers==; echo "$layers" | sed 's/:/\n/g'; } | awk '
	  /==mount==/ { mode=1 }
	  /==losetup==/ { mode=2 }
	  /==layers==/ { mode=3 }
	  {
		if (mode == 1) {
			# get list of mount points, types, and devices - index is $3 (mount points)
			mountdev[$3]=$1
			mounttypes[$3]=$5
		} else if (mode == 2) {
			# get list of loop devices and files - index is $1 (loop devs)
			sub(/:/,"",$1)
			sub(/.*\//,"",$3); sub(/)/,"",$3)
			loopdev[$1]=$3
		} else if (mode == 3) {
			# map mount types to loop files if mount devices is a loop
			for (m in mountdev) {
				if ( loopdev[mountdev[m]] != "" ) mounttypes[m]=loopdev[mountdev[m]]
			}
			# for (m in mountdev) print m " on " mountdev[m] " type " mounttypes[m]
			mode=4
		} else if (mode=4) {
			# print the branches and its mappings - except pup_init, which we hide
			printf("- %s - %s\n", $0, mounttypes[$0])
		}
	  }'
}

### kilobytes to human-friendly size
# $1-kb
kb_to_human() {
	awk -v val=$1 'BEGIN { suffix = "K"
if (val > 1024) { val = val / 1024; suffix = "M"; }
if (val > 1024) { val = val / 1024; suffix = "G"; }
if (val > 1024) { val = val / 1024; suffix = "T"; }
printf "%0.1f%s",val,suffix;
exit; }'
}

### download latest microcode and extract to current dir
download_microcode() {
	local repover microcode

	# get repo version
	read repover < /etc/fatdog-version
	repover=${repover%%[!0-9]*}
	repover=${repover%?}0
	#echo $repover

	# get latest microcode version
	latest=$(wget -qO - $MICROCODE_REPO_URL/$repover | grep -o '"microcode.*cpio"' | sort -r | head -n1)
	latest=${latest%?}; latest=${latest#?}; # get rid of the quotes
	echo $latest

	# now get the stuff
	wget -qO - $MICROCODE_REPO_URL/$repover/$latest | cpio -id
}


### wizard actions here
check_prerequisites() {
	if ! which mkisofs > /dev/null; then
		info "$(gettext 'Missing mkisofs, cannot continue.')"
		go_step exit
		return
	fi

	for p in $ISOLINUX_BINS; do
		if ! [ -f $ISOLINUX_PATH/$p ]; then
			info "$(eval_gettext 'Missing $p, cannot continue.')"
			go_step exit
			return
		fi
	done

	if ! [ "$MODULE_SFS_MOUNT" -a -e "$MODULE_SFS_MOUNT" ]; then
		info "$(gettext 'Missing MODULE_SFS_MOUNT, cannot continue.')"
		go_step exit
		return
	fi

	if ! [ "$BASELINE_MOUNT" -a -e "$BASELINE_MOUNT" ]; then
		info "$(gettext 'Missing BASELINE_MOUNT, cannot continue.')"
		go_step exit
		return
	fi
	go_next	
}

### blurb
introduction() {
	local msg=$(cat << EOF
$(gettext 'This application will create a new CD image that is a snapshot of your currently running system.') \
$(gettext 'It can capture everything --- your installed applications and your customised settings. \
You will have an option of what to include/exclude later on.') 

$(gettext 'The created CD image is an isohybrid, you can burn it to CD/DVD using Pburn \
or copy directly to USB flash drive using dd.')

$(gettext 'Press Yes to continue.')
EOF
)
	yesno --fill "$msg"
	next_wizard_step
}

### confirm that user want to cancel
confirm_cancel() {
	local msg=$(cat <<EOF
$(gettext 'Are you sure you want cancel the remaster process?\n
Click Yes to really cancel and exit the program.')
EOF
)
	if yesno "$msg"; then go_exit
	else go_step $prev_step
	fi
}

### choose output type
choose_output_type() {
msg=$(cat << EOF
$(gettext 'Please select the output type.')
- $(gettext 'ISO: ISO image which can be burned to a CD/DVD and dd-ed to a USB stick')
- $(gettext 'Zip: Contains kernel and initrd/base SFS, for systems with existing bootloader')
EOF
)
	items="\
ISO '$(gettext 'Bootable ISO image')' on \
Zip '$(gettext 'Non-bootable kernel + initrd in a Zip file')' off \
"
	
	OUTPUT_TYPE=$(eval "Xdialog $(std_wizard_flags) --left --radiolist \"$msg\" 0 0 7 $items")
	next_wizard_step
}

### get vmlinuz location
get_vmlinuz() {
	local BACKTITLE
	
	BACKTITLE="$(gettext 'Please tell me where I can find the kernel (vmlinuz).')\n\
$(gettext 'If you do not know where vmlinuz is, there is one on Live CD.')"
	kernellocation=$(eval "Xdialog $(std_wizard_flags usebk) --no-buttons --fselect '' 0 0")
	next_wizard_step	
}

## get efiboot location
get_efiboot() {
	if [ $OUTPUT_TYPE != "ISO" ]; then
		next_wizard_step
		return
	fi
	
	local BACKTITLE
	
	BACKTITLE="\
$(gettext 'If you do not need to support booting on EFI systems, click Next to continue.')\n\n
$(gettext 'Or else specify the location of EFI boot image (efiboot.img),\n
and tick the checkbox at the bottom.')\n\n
$(gettext 'If you do not know where efiboot.img is, there is one on Live CD.')"

	efibootlocation=$(eval "Xdialog $(std_wizard_flags usebk) --check \"$(gettext 'EFI boot support')\" off --no-buttons --fselect '' 0 0")
	next_wizard_step
	efibootlocation=$(echo "$efibootlocation" | tr '\n' '|')
	[ "${efibootlocation%|unchecked|}" == "$efibootlocation" ] && efibootlocation=${efibootlocation%|checked|} || efibootlocation=
	#echo $efibootlocation
}

### get microcode update
get_microcode() {
	if yesno "\
$(gettext "\
MICROCODE UPDATE
----------------
Do you want include microcode update in initrd?
This will require Internet access to pull the update from the internet.\n
")"
	then
		microcode=yes
	else
		microcode=no
	fi
	next_wizard_step
}

### choose which layers to include
choose_layers() {
	local items msg 
	
	# 1. get aufs system-id for the root filesystem
	[ -z "$AUFS_ROOT_ID" ] && AUFS_ROOT_ID=$(
	awk '{ if ($2 == "/" && $3 == "aufs") { match($4,/si=[0-9a-f]*/); print "si_" substr($4,RSTART+3,RLENGTH-3) } }' /proc/mounts
	)

	# 2. get branches, then map branches to mount types or loop devices 
	items=$(
	{ echo ==mount==; cat /proc/mounts; 
	  echo ==losetup==; losetup-FULL -a; 
	  echo ==branches==; ls -v /sys/fs/aufs/$AUFS_ROOT_ID/br[0-9]* | xargs sed 's/=.*//'; } | awk '
	  /==mount==/ { mode=1 }
	  /==losetup==/ { mode=2 }
	  /==branches==/ { mode=3 }
	  {
		if (mode == 1) {
			# get list of mount points, types, and devices - index is $2 (mount points)
			mountdev[$2]=$1
			mounttypes[$2]=$3
		} else if (mode == 2) {
			# get list of loop devices and files - index is $1 (loop devs)
			sub(/:/,"",$1)
			sub(/.*\//,"",$3); sub(/)/,"",$3)
			loopdev[$1]=$3
		} else if (mode == 3) {
			# map mount types to loop files if mount devices is a loop
			for (m in mountdev) {
				if ( loopdev[mountdev[m]] != "" ) mounttypes[m]=loopdev[mountdev[m]]
			}
			# for (m in mountdev) print m " on " mountdev[m] " type " mounttypes[m]
			mode=4
		} else if (mode == 4) {
			# print the branches and its mappings - except pup_init & kernel modules, which we hide
			if ($0 !~ /pup_init/ && $0 !~ /kernel-modules/) printf("%s %s on ", $0, mounttypes[$0])
		}
	  }  
	'
	)
	# ' this line to make Geany happy
	msg=$(cat << EOF
$(gettext 'Choose layers you want to include in the remastered image.')
- pup_rw $(gettext '(if any) - is the RAM layer.')
- pup_save / devsave $(gettext '(if any) - is your currently active save file / device')
- pup_ro / devbase $(gettext '(if any) - is the base sfs file / device')
- $(gettext 'others') - $(gettext 'your currently used SFS')
$(gettext 'pup_rw and pup_save contain your customised settings and applications.')
EOF
)
	if layers=$(eval "Xdialog $(std_wizard_flags) --left --checklist \"$msg\" 0 0 7 $items"); then
		next_wizard_step
		cleanup
		create_tmp_aufs $layers
	else
		next_wizard_step
	fi
}

### choose whether we want to split the initrd
choose_split() {
	local msg
	
	msg="$(gettext 'Choose the type of initrd you want to use.')
- $(gettext 'Standard boots faster on harddisk frugal install. Required for network boot.')
- $(gettext 'Medium has all drivers included.')
- $(gettext 'Small boots faster on flash drives and CD/DVD.')
- $(gettext 'Nano has no drivers.')
"
	initrd_type=$(eval "Xdialog $(std_wizard_flags) --left --no-tags --radiolist \"$msg\" 0 0 5 \
standard \"$(gettext 'Standard (humongous) initrd')\" on \
medium \"$(gettext 'Medium initrd')\" off" \
small  \"$(gettext 'Small initrd')\" off \
nano \"$(gettext 'Nano initrd')\" off)
	next_wizard_step
}

### choose whether we want to split the initrd
choose_compression() {
	local msg
	
	msg="$(gettext 'Choose the compression you want to use.')
$(gettext 'In order of size: xz, gzip, lzo lz4 (biggest).')
$(gettext 'In order of processing time: lz4, lzo, gzip, xz (longest).')"
	MKSQUASHFS_OPTIONS=$(eval "Xdialog $(std_wizard_flags) --inputbox \"$msg\" 0 0 \"$MKSQUASHFS_OPTIONS\"")
	next_wizard_step	
	MKSQUASHFS_OPTIONS=${MKSQUASHFS_OPTIONS:-$DEFAULT_MKSQUASHFS_OPTIONS}
}

### choose where to save to
choose_save_location() {
	local BACKTITLE tmp_free_space tmp_size

	# get uncompressed filesystem size
	echo $tmp_aufs
	splash "$(gettext 'Calculating size requirement, please wait ...')"
	tmp_size=$(du -sk $tmp_aufs | sed 's/\t.*//') # in kilobytes
	close_splash

	# compute required size
	case "$MKSQUASHFS_OPTIONS" in
		*xz*)   req_size=$tmp_size; break ;; # no adjustment needed
		*gzip*) req_size=$(( (tmp_size*110)/100 )); break ;;
		*lzo*)  req_size=$(( (tmp_size*120)/100 )); break ;;
		*lz4*)  req_size=$(( (tmp_size*130)/100 )); break ;;
	esac
	req_size=$(( (req_size*110)/100 )) # overall fudge factor
	req_size=$(kb_to_human $req_size)

	# get free size of output location
	tmp_free_space=$(df -h /tmp | awk 'NR==2 {print $4}')

	BACKTITLE="$(gettext 'Choose where to save the CD/DVD image output file (default is /tmp)')\n\n\
$(eval_gettext 'Output requires $req_size disk space. /tmp has $tmp_free_space free.')\n\
$(gettext 'If output can fit in /tmp, it is recommended to use it.')\n\
$(gettext 'If you use another location, make sure it is on a Linux filesystem.')"
	#eval echo -e \""$BACKTITLE"\"
	savelocation=$(eval "Xdialog $(std_wizard_flags usebk) --dselect '/tmp' 0 0")
	next_wizard_step	
}

### confirm settings
confirmation() {
	local msg BACKTITLE savelocation_freespace efibootwarning kernelwarning
	
	savelocation_freespace=$(df -h $savelocation | awk 'NR==2 {print $4}')
	[ -f "$(realpath "$kernellocation")" ] || kernelwarning=" *** $(gettext 'Warning: file not found') ***"
	[ "$efibootlocation" ] && ! [ -f "$(realpath "$efibootlocation")" ] && efibootwarning=" *** $(gettext 'Warning: file not found') ***"
	msg=$(cat <<EOF
$(eval_gettext 'Output type: $OUTPUT_TYPE')
$(eval_gettext 'Where the output image will be saved: $savelocation')
$(eval_gettext 'Free space available in $savelocation: $savelocation_freespace')
$(eval_gettext 'Required disk space for output: $req_size')
$(eval_gettext 'Mksquashfs compression option: $MKSQUASHFS_OPTIONS')
$(eval_gettext 'Initrd type: $initrd_type')
$(eval_gettext 'Include microcode update: $microcode')

$(eval_gettext 'Kernel file path: $kernellocation$kernelwarning')
$([ "$efibootlocation" ] && echo "$(eval_gettext 'EFI bootimage file path: $efibootlocation$efibootwarning')\n")
$(gettext 'Source layers:')
$(map_layers_to_locations)

$(gettext 'Make sure you get everything correct.')
$(gettext 'If you click "Next", the remaster will start immediately.')
$(gettext 'If some of the above are wrong, click "Previous" now and correct them.')
EOF
)
	BACKTITLE="$(gettext 'Confirm Selected Settings')"
	eval "Xdialog $(std_wizard_flags usebk) --left --yesno \"$msg\" 0 0"	
	next_wizard_step
}

customise() {
	local p pp

	# remove stuff we don't want to have in the remaster
	rm -rf $tmp_aufs/$AUFS_ROOT $tmp_aufs/etc/BOOTSTATE
	for p in cache lock locks run; do # code copied from system-init
		for pp in $tmp_aufs; do
			# delete everything except dirs and symlinks
			[ -e $pp/var/$p ] && find $pp/var/$p ! \( -type d -o -type l \) -delete
			[ -e $pp/usr/local/var/$p ] && find $pp/usr/local/var/$p ! \( -type d -o -type l \) -delete
		done
	done

	# open folder and ask use to add/remove/edit files
	rox -d $tmp_aufs
	msg "$(gettext 'A folder window has been opened.
This folder contains all the files that will be part of the remaster.
You can add/remove files into/from this folder, or edit them as necessary.
Anything you do here will not affect or change the running system.')
$(gettext 'When done, click OK to continue; Or just click OK now if you do not want to change anything.')" \
"$(gettext 'Add/remove/edit files for remastered copy')"
	rox -D $tmp_aufs
	go_next
}

### go and do it - the heavylifting here
create_remaster() {
	local tmp_initrd tmp_isoroot EDITOR_PID iso_size free_size extrasfs
	
	# create temporary directories
	tmp_initrd=$(mktemp -dp "$savelocation" tmp-initrd-XXXXXX)
	tmp_isoroot=$(mktemp -dp "$savelocation" tmp-isoroot-XXXXXX)
	chmod 0755 $tmp_isoroot $tmp_initrd
		
	# re-create the initrd from our baseline
	cp -a $BASELINE_MOUNT/* "$tmp_initrd"
	
	# create the missing /init symlink
	cd "$tmp_initrd"
	ln -s sbin/system-init init

	# download microcode if needed
	[ $microcode = "yes" ] && {
		splash "Downloading microcode, please wait ..."
		download_microcode
	}

	# now tell user to be patient, the time consuming process is about to begin
	# change output filename based on output type
	case $OUTPUT_TYPE in
		Zip) OUTPUT=${OUTPUT%.iso}.zip ;;
		ISO) ;;
	esac
	splash "$(eval_gettext 'Creating $OUTPUT on $savelocation, please wait  ...')"

	# re-create kernel-modules.sfs
	case $initrd_type in
	small)
		# copy contents of kernel-modules to base sfs
		cp -a $MODULE_SFS_MOUNT/* "$tmp_aufs"
		depmod -a -b "$tmp_aufs"
		
		# generate minimum set of modules - this is the same procedure as in fatdog-split-initrd
		tmp_modules=$(mktemp -dp /tmp remaster-modules.XXXXXXXX)
		chmod 0755 $tmp_modules
		cp -a $MODULE_SFS_MOUNT/* $tmp_modules
		get_modules_dep "$BASE_MODULES" $(echo $tmp_modules/lib/modules/*/modules.dep)
		
		# convert this to find params
		FIND_PARAM=""
		for p in $BASE_MODULES; do
			FIND_PARAM="$FIND_PARAM -a ! -name $p.ko"
		done
		FIND_PARAM=${FIND_PARAM#*-a}

		# remove all files that we don't need
		find $tmp_modules/* \( -type f $FIND_PARAM -o -type l \) -delete
		find $tmp_modules/* -type d -empty -delete

		# depmod then we're finished
		depmod -a -b $tmp_modules
		mksquashfs $tmp_modules kernel-modules.sfs $MKSQUASHFS_OPTIONS
		rm -rf $tmp_modules 
		;;

	standard|medium)
		# copy directly from loop device
		dd if=$(grep -m 1 $MODULE_SFS_MOUNT /proc/mounts | awk '{print $1}') of=kernel-modules.sfs bs=1M
		;;
	nano)
		# copy directly from loop device to iso root
		dd if=$(grep -m 1 $MODULE_SFS_MOUNT /proc/mounts | awk '{print $1}') of="$tmp_isoroot"/kernel-modules.sfs bs=1M
		extrasfs="extrasfs=local:kernel-modules.sfs"
		;;
	esac

	# change default base_sfs from "initrd" to "local" as needed
	case $initrd_type in
	small|medium|nano)
		# change default base_sfs from "initrd" to "local" so that it will search basesfs on local drives
		# while we're at it, increase the default delay to 5 seconds too
		sed -i -e '
s/BASE_SFS_DEFAULT=initrd/BASE_SFS_DEFAULT=local/
s/DEFAULT_DEVICE_DELAY=0/DEFAULT_DEVICE_DELAY=5/
' sbin/system-init 
		;;
	esac
	
	# create the base sfs - this is the main remaster process, really.
	mksquashfs "$tmp_aufs" $BASESFS -noappend $MKSQUASHFS_OPTIONS
	case $initrd_type in
	small|medium|nano)
		mv $BASESFS "$tmp_isoroot" # if small initrd, move it outside
	esac
	
	# build initrd, put it to iso-root
	find . | cpio -o -H newc > "$tmp_isoroot/initrd"

	# copy kernel - if given
	[ "$kernellocation" ] && cp "$kernellocation" "$tmp_isoroot"/vmlinuz

	# ============ remaster completed, prepare OUTPUT now ==============

	case $OUTPUT_TYPE in
	
		Zip) # ----------- make kernel + initrd Zip -------------
			cd "$tmp_isoroot"
			zip "$savelocation/$OUTPUT" *
			cd "$savelocation"
			;;
	
		ISO) # ----------- make isohybrid ISO ----------------
			
			# populate iso-root with other stuff - isolinux.bin and isolinux.cfg
			cd "$tmp_isoroot"
			for p in $ISOLINUX_BINS; do
				cp $ISOLINUX_PATH/$p $p
			done

			# check if size is still enough, if not, bail out
			iso_size=$(du -sk $tmp_isoroot | sed 's/\t.*//') # in kilobytes
			free_size=$(du -sk $savelocation | sed 's/\t.*//') # in kilobytes
			iso_size=$(( iso_size + 1024)) # 1MB fudge factor for additional config files etc
			if [ $iso_size -gt $free_size ]; then
				free_size=$(kb_to_human $(( iso_size - free_size)) )
				info "\
$(eval_gettext 'Not enough space in $savelocation to make ISO, need $free_size more.')
$(gettext 'Operation cancelled.')"
				rm -rf "$tmp_initrd" "$tmp_isoroot"
				close_splash
				go_step exit
				return
			fi

			# the rest are just small files
			cp $LOGO_FILE fatdog.png

			# isolinux.cfg is for BIOS
			cat > isolinux.cfg << EOF
default fatdog
prompt 1
timeout 100

ui vesamenu.c32
menu resolution 800 600
menu title Fatdog64 Remastered
menu background fatdog.png
menu tabmsg $(gettext 'Press Tab to edit entry, Esc for boot prompt')
menu color border 37;40  #80ffffff #00000000 std
menu color sel 7;37;40 #80ffffff #20ff8000 all
menu margin 1
menu rows 20
menu tabmsgrow 26
menu cmdlinerow -2
menu passwordrow 19
menu timeoutrow 28
menu helpmsgrow 30

label fatdog
linux vmlinuz
initrd initrd
append rootfstype=ramfs $extrasfs
menu label Fatdog64 Remastered
text help
Start Fatdog64.
endtext

EOF
			# SGD2 loopback support
			# https://www.supergrubdisk.org/wiki/Loopback.cfg
			mkdir -p boot/grub
			echo "source /grub.cfg" > boot/grub/loopback.cfg

			# grub.cfg is for UEFI, but also used for SGD2 loopback
			cat > grub.cfg << EOF
insmod png
background_image /fatdog.png
set timeout=10
menuentry "Fatdog64 Remastered" {
	echo Loading ...
	linux /vmlinuz rootfstype=ramfs $extrasfs
	initrd /initrd
	echo Booting ...
}
EOF

			# efi boot support
			if [ "$efibootlocation" ]; then
				cp "$efibootlocation" efiboot.img
			fi
			
			# customise isolinux.cfg & grub.cfg
			rox -d .
			geany -i grub.cfg isolinux.cfg &
			EDITOR_PID=$!; sleep 1
			msg "$(gettext \
'A text editor has been opened with a copy of the default isolinux.cfg and grub.cfg in it.
This file contains the default instructions for the CD/DVD bootloader.
You can modify this file to change the default boot parameters.

A ROX folder has been opened too. This will become the root of the remastered ISO.
It mainly contains bootloader files. You can add/remove/modify them as needed.
Please __DO NOT__ touch it unless you know what you are doing.

Once you are finished, save the file (use "Save", not "Save As"), then click OK to continue.
Or just click OK now if you do not want to change anything.')

$(gettext 'Note: isolinux.cfg is used for BIOS, grub.cfg is used for EFI and SGD2 loopback')" \
"$(gettext 'Customise isolinux.cfg and grub.cfg')"

			kill -CONT $EDITOR_PID; kill -HUP $EDITOR_PID
			rox -D .
			
			# build the iso
			cd "$savelocation"
			rm $OUTPUT	# mkisofs here must be the same as in build-iso.sh and rc.shutdown
			
			if ! [ "$efibootlocation" ]; then
				# standard iso
				mkisofs -iso-level 4 -D -R -o $OUTPUT -b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table "$tmp_isoroot"
				isohybrid -o 64 $OUTPUT
			else
				# EFI iso
				mkisofs -iso-level 4 -D -R -o $OUTPUT -b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table \
				-eltorito-alt-boot -eltorito-platform efi -b efiboot.img -no-emul-boot "$tmp_isoroot"		
				isohybrid -u $OUTPUT
			fi
			;;
	esac
	
	# done, cleanup
	rm -rf "$tmp_initrd" "$tmp_isoroot"
	close_splash
	
	info "$(eval_gettext 'Done! $OUTPUT is now be found at $savelocation.') \
$(gettext 'I will open the folder for you.')
$(gettext 'If you chose ISO output,')
$(gettext 'You can now use Pburn (under Multimedia menu) to burn it to CD/DVD.')
$(gettext 'Otherwise you can configure your bootloader to boot the kernel and initrd in the Zip file.')
"
	su $USER -c "rox -d \"$savelocation\""
	go_next
}


################## main ###################
[ "$NOT_USING_AUFS" ] && exit 1
[ $(id -u) -ne 0 ] && exec gtksu "$APPTITLE" "$0"
while true; do 
	case $step in 
		1) check_prerequisites ;;	
		2) introduction ;;
		3) choose_output_type ;;
		4) get_vmlinuz ;;
		5) get_efiboot ;;
		6) get_microcode ;;
		7) choose_layers ;;
		8) choose_split ;;		
		9) choose_compression ;;
		10) choose_save_location ;;
		11) confirmation ;;
		12) customise ;;
		13) create_remaster ;;
		14|exit) cleanup; break ;;
		cancel) confirm_cancel ;;
	esac
done

