#!/bin/bash
# Fatdog64 ISO build script
# (C) James Budiono 2012, 2014, 2016, 2017, 2018, 2020, 2021, 2025
# License: GPL Version 3 or later.
#
# $1 - building sfs: base/devx/nls/base32/single/multi
# $1 - building iso: iso [std/mini/micro/nano] [uefi/xorriso-uefi/grldr/isolinux/grub2]
# $1 - building initrd: initrd [std/mini/micro/nano]
# $1 - building image:  image [std/mini/micro/nano]

### configuration
LANG=C
FATDOG_VERSION=${FATDOG_VERSION:-900}  # update for new release
KERNEL_VERSION=${KERNEL_VERSION-6.12.34} # update for new release
MICROCODE_DATE=${MICROCODE_DATE:-20250909} # update for new release
SQUASHFS_PARAM=${SQUASHFS_PARAM--comp xz -Xbcj x86} # inherit 

CHECKSUM=${CHECKSUM:-compute}  # compute, download-once, download-always, use-as-is
PKG_CACHE=${PKG_CACHE:-output}
PKG_URL=${PKG_URL:-http://distro.ibiblio.org/fatdog/packages/900}
OUTPUT_DIR=${OUTPUT_DIR:-iso}
FATDOG_INITRD=${FATDOG_INITRD:-fatdog-initrd}
FATDOG_ISO_ROOT=${FATDOG_ISO_ROOT:-fatdog-iso-root}
# default BASE_SFS as specified in BASE_SFS_DEFAULT_PATH in initrd
[ -e "$FATDOG_INITRD"/sbin/system-init ] &&
BASE_SFS=${BASE_SFS:-$(sed -n '/^BASE_SFS_DEFAULT_PATH/ {s|.*=.*\/||;p}' "$FATDOG_INITRD"/sbin/system-init)}
BASE_SFS=${BASE_SFS:-fd64.sfs}
TARGET_ISO=${TARGET_ISO:-fatdog.iso}
TARGET_IMAGE=${TARGET_IMAGE:-fatdog.img}
TARGET_ZIP=${TARGET_ZIP:-fatdog.zip}

# we support a lot of filesystems - see make_image
IMAGE_FS=${IMAGE_FS:-ext4} 
# extra free space you want to add to the image, this depends on the filesystem
# default 10MB is enough to account for inode tables etc for ext4, may need more for other fs
IMAGE_PAD=${IMAGE_PAD:-$((128*1024))} # in kilobytes
#IMAGE_COMPRESSOR= # xz, gzip, zstd, or anything

#USE_EXISTING=1  # don't build sfs-es, use existing
#DUAL_NANO=1     # create secondary nano initrd
#SGD2_LOOPBACK=1 # https://www.supergrubdisk.org/wiki/Loopback.cfg
#LOG_COMMITS_SINCE=812 # log fossil commit since 812 release

# modules required for boot, separated by spaces
BASE_MODULES='cryptoloop|nbd|cifs|aufs|usb-storage|ohci|uhci|ehci|aes|xts|dm-crypt|dm-snapshot|sg|hid-generic' 
BUILD_CONFIG=${BUILD_CONF:-./build-iso.conf}

# working dirs
WORKDIR=${WORKDIR:-/tmp/build-iso.$$}

### standard configuration items - dont change unless you know what you're doing
[ -e "$BUILD_CONFIG" ] && . "$BUILD_CONFIG"
unset BUILD_CONFIG # don't pass through to slave scripts

# pass through all other build-multi-sfs.sh options (except those overriden above)
for p in PKG_EXT PKG_URL PKG_CACHE OUTPUT_DIR INSTALLER_PKG FATDOG_VERSION \
         PKG_LIST ROOTFS_DIR \
         BASE_PKG_LIST DEVX_PKG_LIST NLS_PKG_LIST BASE32_PKG_LIST \
         BASE_SFS DEVX_SFS NLS_SFS BASE32_SFS \
         BASE_ROOTFS_DIR DEVX_ROOTFS_DIR NLS_ROOTFS_DIR BASE32_ROOTFS_DIR \
         KEEP_ROOTFS \
         SQUASHFS_PARAM GEN_EXCLUSION_LIST; do
	eval test \"\$$p\" && eval export $p
done
### end of configuration



################ helpers ################

CLEANUP_DONE=
trap 'cleanup; exit' INT TERM HUP 0
cleanup() {
	if [ -z $CLEANUP_DONE ]; then
		rm -rf "$WORKDIR"
	fi
	CLEANUP_DONE=1
}

run_in_my_dir() {
        mydir=$(readlink -f "$0")
        mydir=${mydir%/*}
        cd $mydir
}

update_checksum() {
	case $CHECKSUM in
		compute)
			(
			echo updating md5sum
			cd $PKG_CACHE
			pkg2repo MD5
			) 
			;;
		download-once)
			! [ -f "${PKG_URL}/CHECKSUMS.md5" ] &&
			wget -N -P $PKG_CACHE "${PKG_URL}/CHECKSUMS.md5"
			;;
		download-always)
			wget -N -P $PKG_CACHE "${PKG_URL}/CHECKSUMS.md5"
			;;
		use-as-is)
			;;
	esac
}

# $* combine all sfses, the last one is target
combine_sfs() {
	echo combining sfs ...
	local target TMPDIR=$WORKDIR/squashfs-root

	for target; do :; done;	rm -f $target
	for p; do
		[ -r "$p" ] && unsquashfs -f -d $TMPDIR "$p" >/dev/null
	done
	mksquashfs $TMPDIR $target -noappend $SQUASHFS_PARAM >/dev/null
	rm -rf $TMPDIR
}

# $1-source, $2-target $3-module list
extract_base_modules() {
	echo extracting kernel modules ...
	local TMPDIR=$WORKDIR/squashfs-root OLD_DEP="$BASE_MODULES" DEP="" KVER
	
	# extract, get kernel ver, update base modules list
	unsquashfs -f -d $TMPDIR "$1" > /dev/null
	KVER=$(ls $TMPDIR/lib/modules | head -n1)
	update_base_module_list $TMPDIR/lib/modules/$KVER/modules.dep

	# delete unnecessary files
	#echo $(echo /$BASE_MODULES | sed 's%|%.ko|/%g')
	find $TMPDIR/lib/modules/$KVER -type f | grep -vE $(echo /$BASE_MODULES | sed 's%|%.ko|/%g; s%$%.ko%') | xargs rm 
	find $TMPDIR/lib/modules/$KVER -type d | sort -r | xargs rmdir 2>/dev/null # remove the empty dirs
	rm -r $TMPDIR/lib/firmware # hopefully no firmware is needed
	
	# re-pack
	depmod -b $TMPDIR $KVER
	mksquashfs $TMPDIR "$2" -noappend $SQUASHFS_PARAM >/dev/null
	rm -rf $TMPDIR
}
# input: $BASE_MODULES, $1-path to modules.dep, returns: BASE_MODULES
update_base_module_list() {
	local DEP OLD_DEP
	DEP="$BASE_MODULES"; OLD_DEP=""
	[ -z "$DEP" ] && return
	while [ "$OLD_DEP" != "$DEP" ]; do	
		OLD_DEP="$DEP"
		for p in $(grep -E $(echo "/$DEP" | sed 's%|%.ko:|/%g; s%$%.ko:%') "$1" | sed 's/.*://'); do
			p=${p##*/}; p=${p%.ko}
			case $DEP in 
				*\|$p*) ;;
				*) DEP="$DEP|$p" ;;
			esac
		done
	done
	BASE_MODULES="$DEP"
}

generate_version_info() {
	cat << EOF
Fatdog version : $FATDOG_VERSION
Kernel version : $KERNEL_VERSION
Microcode      : $MICROCODE_DATE
Build date     : $(date -I)
EOF
}

generate_commit_logs() {
	[ -e CHANGELOG ] && cat CHANGELOG
}


############ sfs builders #################

# $1-all,multi,base,devx,nls,base32 (default "multi")
build_multi_sfs() {
	update_checksum
	SFS_TO_BUILD=$1 ./build-multi-sfs.sh
}

build_single_sfs() {
	update_checksum
	SFS_OUTPUT=$BASE_SFS ./build-sfs.sh
}




############# initrd builders ###############

# $1-std,mini,micro,nano $2-basesfs $3-output dir (=iso root)
build_initrd() {
	INITRD_DIR="$WORKDIR/initrd.$$"
	cp -a "$FATDOG_INITRD" $INITRD_DIR

	# static nodes, needed for embedded initrd in kernel
	mknod -m 600 $INITRD_DIR/dev/console c 5 1 # otherwise won't boot
	mknod -m 666 $INITRD_DIR/dev/null c 1 3    # nice to have
	
	# kernel modules
	[ -e kernels/kernel-modules.sfs-$KERNEL_VERSION ] &&
	cp kernels/kernel-modules.sfs-$KERNEL_VERSION "$INITRD_DIR"/kernel-modules.sfs
	
	# microcode update
	[ -e kernels/microcode-update-${MICROCODE_DATE}.cpio ] &&
	cpio -i -d -D "$INITRD_DIR" < kernels/microcode-update-${MICROCODE_DATE}.cpio 
	
	# basesfs
	echo building initrd $1
	case $1 in
		std|"") # std is default
			# basesfs inside  initrd
			cp "$2" "$INITRD_DIR"/$BASE_SFS  
			;; 
			
		mini|small) # small is alias for mini
			# basesfs outside initrd
			cp "$2" "$3"/$BASE_SFS
			set_external_initrd ram:local /$BASE_SFS
			;;
			
		micro)
			# combine base + kernel modules, keep only important modules in initrd
			combine_sfs "$2" kernels/kernel-modules.sfs-$KERNEL_VERSION "$3"/$BASE_SFS
			extract_base_modules kernels/kernel-modules.sfs-$KERNEL_VERSION $INITRD_DIR/kernel-modules.sfs "$BASE_MODULES"
			set_external_initrd ram:local /$BASE_SFS
			DUAL_NANO= # disable 2nd nano initrd if our initrd is already small
			;;

		nano)
			cp "$2" "$3"/$BASE_SFS
			mv "$INITRD_DIR"/kernel-modules.sfs "$3"
			set_external_initrd ram:local /$BASE_SFS
			set_external_kernel_modules "$3" ram:local /kernel-modules.sfs 
			DUAL_NANO= # disable 2nd nano initrd if our initrd is already small
			;;
	esac
	
	# cleanup
	find $INITRD_DIR -type f -name 'placeholder_for_fossil' -delete
	
	# build initrd
	INITRD=$(readlink -f "$3")/initrd
	( cd $INITRD_DIR; find . | cpio -o -H newc > $INITRD )

	# Create loopback.cfg to support SGD2
	[ $SGD2_LOOPBACK ] &&
	mkdir -p "$3"/boot/grub &&
	echo "source /grub.cfg" > "$3"/boot/grub/loopback.cfg
	
	# build 2nd nano initrd if asked
	if [ $DUAL_NANO ]; then
		rm -f "$INITRD_DIR"/$BASE_SFS "$INITRD_DIR"/kernel-modules.sfs
		( cd $INITRD_DIR; find . | cpio -o -H newc > ${INITRD}-nano )
		add_dual_nano_menu_entries "$3"
	fi
}
# change default base_sfs from "initrd" to "local", increase waitdev delay to 3 seconds
# $1-basesfs-default-search $2-basesfs 
set_external_initrd() {
	sed -i -e "
	s|^BASE_SFS_DEFAULT=.*|BASE_SFS_DEFAULT=$1|	
	s|^BASE_SFS_DEFAULT_PATH=.*|BASE_SFS_DEFAULT_PATH=$2|
	s|^DEFAULT_DEVICE_DELAY=.*|DEFAULT_DEVICE_DELAY=3|
	" "$INITRD_DIR"/sbin/system-init 
}
# $1-output path, $2-prefix $3-kernel modules path
set_external_kernel_modules() {
	sed -i -e '/^append/ s|$| extrasfs='"$2:$3"'|' "$1"/isolinux.cfg
	sed -i -e '/vmlinuz/ s|$| extrasfs='"$2:$3"'|' "$1"/grub.cfg "$1"/menu.lst
}
# $1-output path
add_dual_nano_menu_entries() {
	>> "$1"/isolinux.cfg cat << EOF

label fatdog-nano
linux vmlinuz
initrd initrd-nano
append rootfstype=ramfs mergeinitrd1=local:/initrd waitdev=3
menu label For machines with slow BIOS or bootloaders
text help
Start Fatdog using nano-initrd - the smallest possible initrd.
This is meant for old machines with slow BIOS or bootloaders.
Be aware that nano-initrd lacks many drivers to support advanced
hardware; so it may fail to boot altogether.
endtext

label fatdog-nano-iso-boot
linux vmlinuz
initrd initrd-nano
append rootfstype=ramfs mergeinitrd1=findimg:fatdog.iso:/initrd waitdev=3
menu label For machines with slow BIOS or bootloaders (ISO Boot)
text help
Start Fatdog using nano-initrd - the smallest possible initrd.
This is meant for old machines with slow BIOS or bootloaders.
Be aware that nano-initrd lacks many drivers to support advanced
hardware; so it may fail to boot altogether.
This option is for ISO booting. ISO must be named fatdog.iso
and be located in the root directory.
endtext

EOF
	>> "$1"/grub.cfg cat << EOF

menuentry "Fatdog64 for slow BIOS and bootloaders" {
	echo Loading ...
	linux /vmlinuz rootfstype=ramfs mergeinitrd1=local:/initrd waitdev=3
	initrd /initrd-nano
	echo Booting ...
}
menuentry "Fatdog64 for slow BIOS and bootloaders (ISO Boot)" {
	echo Loading ...
	linux /vmlinuz rootfstype=ramfs mergeinitrd1=findimg:fatdog.iso:/initrd waitdev=3
	initrd /initrd-nano
	echo Booting ...
}
EOF
	>> "$1"/menu.lst cat << EOF

title Fatdog64 for slow BIOS and bootloaders
kernel /vmlinuz rootfstype=ramfs mergeinitrd1=local:/initrd waitdev=3
initrd /initrd-nano

title Fatdog64 for slow BIOS and bootloaders (ISO Boot)
kernel /vmlinuz rootfstype=ramfs mergeinitrd1=findimg:fatdog.iso:/initrd waitdev=3
initrd /initrd-nano
EOF
}

########### iso builder #################

# $1-uefi,xorriso-uefi,grldr,isolinux,grub2 $2-iso-root $3-output dir
make_iso() {
	local mode=$1
	
	# if mode is uefi, check if efiboot.img exist
	# otherwise build bios iso
	case $mode in
		*uefi*|"")
			! [ -f "$2"/efiboot.img ] && 
			echo "efiboot.img missing, building BIOS isohybrid." &&
			mode=isolinux ;;
	esac
	
	case $mode in
		uefi|"") # uefi is default
			# standard uefi build using cdrtools' mkisofs
			rm -f "$2"/grldr "$2"/menu.lst "$3/$TARGET_ISO"
			mkisofs -o "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-iso-level 4 -D -R \
			-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 \
			"$2"/
			isohybrid -u "$3/$TARGET_ISO"
			echo Standard UEFI ISO created.
			;;

		uefi-only)
			# non-hybrid uefi-only build using cdrtools' mkisofs
			rm -f "$3/$TARGET_ISO"
			( cd "$2"; rm -rf isolinux.* vesamenu.c32 help menu.lst grldr )
			mkisofs -o "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-iso-level 4 -D -R \
			-eltorito-platform efi -b efiboot.img -no-emul-boot \
			"$2"/
			echo Non-hybrid UEFI-only ISO created.
			;;

		xorriso-uefi) # for recent xorriso (tested with 1.4.0)
			# hybrid uefi build - Windows XP had problems interpreting iso:1999 spec
			type xorriso || return
			rm -f "$2"/grldr "$2"/menu.lst "$3/$TARGET_ISO"
			xorriso -dev "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-compliance "iso_9660_level=3:iso_9660_1999" \
			-map "$2" / \
			-boot_image isolinux dir=/	 \
			-boot_image isolinux system_area=/usr/share/syslinux/isohdpfx.bin \
			-boot_image isolinux partition_table=on \
			-boot_image isolinux next \
			-boot_image any efi_path=efiboot.img \
			-boot_image isolinux partition_entry=gpt_basdat
			echo Xorriso hybrid UEFI ISO created.
			;;

		
		xorriso-uefi-only) # for recent xorriso (tested with 1.5.2)
			# hybrid uefi-only build, probably same problem in Windows as above
			type xorriso || return
			rm -f "$3/$TARGET_ISO"
			( cd "$2"; rm -rf isolinux.* vesamenu.c32 help menu.lst grldr )
			rm -f "$2"/grldr "$2"/menu.lst "$3/$TARGET_ISO"
			xorriso -dev "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-compliance "iso_9660_level=3:iso_9660_1999" \
			-map "$2" / \
			-boot_image any efi_path=efiboot.img \
			-boot_image any cat_hidden=on        \
			-boot_image any efi_boot_part=--efi-boot-image # this makes it isohybrid
			echo Xorriso hybrid UEFI-only ISO created.
			;;

		xorriso-uefi-old)
			# hybrid uefi build for older xorriso (requires post-processing with isohybrid)
			type xorriso || return
			rm -f "$2"/grldr "$2"/menu.lst "$3/$TARGET_ISO"
			xorriso -dev "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-compliance "iso_9660_level=3:iso_9660_1999" \
			-map "$2" / \
			-boot_image isolinux dir=/	 \
			-boot_image isolinux next \
			-boot_image any efi_path=efiboot.img	 
			isohybrid -u "$3/$TARGET_ISO"
			echo Xorriso hybrid UEFI ISO created.
			;;

		grldr)
			# grub build - non-hybrid ISO
			rm -f "$3/$TARGET_ISO"
			( cd "$2"; rm -rf efiboot.img grub.cfg isolinux.* vesamenu.c32 help )
			mkisofs -o "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-iso-level 4 -D -R  \
			-b grldr -no-emul-boot -boot-load-size 4 -boot-info-table \
			"$2"/
			echo Grub4dos non-hybrid BIOS ISO created.
			;;

		grub2)
			# grub2 build - non-hybrid ISO
			rm -f "$3/$TARGET_ISO"
			( cd "$2"; rm -rf efiboot.img menu.lst grldr isolinux.* vesamenu.c32 help;
			fatdog-create-grub2-bootloader cdboot )
			mkisofs -o "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-iso-level 4 -D -R  \
			-b grub2.cdboot -no-emul-boot -boot-load-size 4 -boot-info-table \
			"$2"/
			echo Grub2 non-hybrid BIOS ISO created.
			;;

		isolinux)
			# standard bios build
			rm -f "$2"/efiboot.img "$2"/grub.cfg "$2"/grldr "$2"/menu.lst "$3/$TARGET_ISO"
			mkisofs -o "$3/$TARGET_ISO" \
			-volid "FATDOG_LIVE" \
			-iso-level 4 -D -R  \
			-b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table \
			"$2"/
			isohybrid -o 64 "$3/$TARGET_ISO"
			echo Standard BIOS ISO created.	
			;;
	esac
}

########### image builder #################

# $1-uefi,xorriso-uefi,grldr,isolinux,grub2 $2-iso-root $3-output dir
make_image() {
	local outdir="$3" isoroot="$2" loopdev= size= helper=

	# extract usb-boot-mbr.img
	echo "Extracting boot image, please wait ..."
	tar -C "$outdir" -xf $isoroot/usb-boot/boot-images.tar.xz usb-boot-mbr.img
	mv "$outdir/usb-boot-mbr.img" "$outdir/$TARGET_IMAGE"

	(
		# keep only the files we need
		cd $isoroot
		rm -r boot help
		rm -f efiboot.img fix-usb.sh grldr isolinux.* menu.lst *.c32

		# move the files to the temp directory, and create the mountpoint
		mkdir zzz; mv * # so our dir is always last
		mkdir img       # our temporary mount point for the image
	)

	# extend the image file as needed
	size=$(du -s "$isoroot" | awk '{print $1}') # in kilobytes
	size=$((size + IMAGE_PAD))
	extend_image_file $size

	# find the loop device - the second line (second partition)
	loopdev=$(kpartx -l "$outdir/$TARGET_IMAGE"  | sed -n '2 {s/ .*//;p}')
	#echo $loopdev
	if [ "$loopdev" ]; then
		# (re-)create the filesystem, if unknown, treat as ext4
		kpartx -a "$outdir/$TARGET_IMAGE"
		case $IMAGE_FS in
			f2fs)  mkfs.f2fs -f -l "FATDOG_LIVE" /dev/mapper/$loopdev  ;;
			btrfs) mkfs.btrfs -f -L "FATDOG_LIVE" /dev/mapper/$loopdev ;;
			btrfs-small) mkfs.btrfs -f -L "FATDOG_LIVE" --mixed /dev/mapper/$loopdev    ;;
			ntfs)  mkfs.ntfs -F -L "FATDOG_LIVE" /dev/mapper/$loopdev; helper=.ntfs-3g  ;;
			xfs)   mkfs.xfs -f -L "FATDOG_LIVE" /dev/mapper/$loopdev   ;;
			fat|vfat) mkfs.vfat -n "FATDOG_LIVE" /dev/mapper/$loopdev  ;;
			# exfat note: initrd-nano doesn't support exfat (kernel driver missing)
			exfat) mkfs.exfat -n "FATDOG_LIVE" /dev/mapper/$loopdev    ;;
			# jfs note: grub4does doesn't support jfs, not bootable under BIOS
			# jfs note: initrd-nano doesn't support jfs (kernel module not built-in)
			jfs) mkfs.jfs -L "FATDOG_LIVE" /dev/mapper/$loopdev      ;;
			udf) mkfs.udf --utf8 --blocksize="$(blockdev --getss "/dev/mapper/$loopdev")" --media-type=hd --udfrev=0x0201 --lvid="FATDOG_LIVE" --vid="FATDOG_LIVE" /dev/mapper/$loopdev ;;
			ext2|ext3|ext4) mkfs.$IMAGE_FS -F -L "FATDOG_LIVE" /dev/mapper/$loopdev ;;
			ext4-nojournal) mkfs.ext4 -F -L "FATDOG_LIVE" -O ^has_journal /dev/mapper/$loopdev ;;
			*) mkfs.ext4 -F -L "FATDOG_LIVE" /dev/mapper/$loopdev ;; # fallback - use ext4
		esac

		# mount the image & copy the files
		mount$helper /dev/mapper/$loopdev $isoroot/img
		mv $isoroot/zzz/* $isoroot/img
		sync
		umount $isoroot/img
		
		# and we're done
		kpartx -d "$outdir/$TARGET_IMAGE"

		# optionally compress the image if a compressor is supplied
		[ "$IMAGE_COMPRESSOR" ] && "$IMAGE_COMPRESSOR" "$outdir/$TARGET_IMAGE"
		ls -hs "$outdir/$TARGET_IMAGE"*
	fi
}
# input: $1-min required size, in kilobytes, $outdir, $TARGET_IMAGE
extend_image_file() {
	local size="$1" sectors partsize sizediff fsize extra_sectors
	
	sectors=$(sfdisk -l "$outdir/$TARGET_IMAGE" | awk -v part2=${TARGET_IMAGE}2 '$1 ~ part2 { print $4 }')
	partsize=$(( sectors / 2 ))             # convert to kilobytes

	# if partition size less than the required file size
	if [ $partsize -lt $size ]; then
		echo "Extending image, please wait ..."
		
		# extend the image file
		sizediff=$(( (size - partsize) * 1024 ))    # size difference, in bytes
		#echo $sizediff 
		fsize=$(stat -c %s "$outdir/$TARGET_IMAGE") # extend the image file
		truncate -s $(( sizediff + fsize )) "$outdir/$TARGET_IMAGE"

		# now extend the partition
		extra_sectors=$(( sizediff / 512 )) # convert bytes to sectors
		#echo $extra_sectors
		echo "size=$(( sectors + extra_sectors ))" | sfdisk -N2 "$outdir/$TARGET_IMAGE"		
	fi
}

########### zip builder #################

# $1-std,mini,micro,nano (default std) $2-iso-root $3-output dir
make_zip() {
	local outdir="$3" isoroot="$2" loopdev= size= helper=
	local targetzip=$(readlink -f "$outdir/$TARGET_ZIP")
	local use_initrdnano= use_basesfs= use_kernelmodules=

	# NOTE: ZIP IS NOT BOOTABLE AT THE MOMENT !
	#       We will fix it later
	(
		cd $isoroot
		[ -e initrd-nano ] && use_initrdnano=yes
		[ -e $BASE_SFS ]   && use_basesfs="basesfs=ram:local:/fatdog64/$BASE_SFS"
		[ -e kernel-modules.sfs ] && use_kernelmodules="extrasfs=ram:local:/fatdog64/kernel-modules.sfs"

		# extract EFI bootloader if it exist
		if [ -e efiboot.img ]; then
			mkdir img 
			mount -o loop efiboot.img img
			cp -a img/* .
			umount -f img
			rmdir img

			(
				cd EFI/boot

				# driver_x64 usage notes
				mkdir -p drivers_x64
				echo "Copy driver files you need to /EFI/boot/drivers_x64 to use them." > ../../drivers_x64/README
				echo "Put driver files here to use them." > drivers_x64/README
				
				# remove fatdog boot stanza for ISO
				sed -i -e '/Fatdog64/,/}/ d' refind.conf

				# create new boot stanza
				{
					if [ $use_initrdnano ]; then
						cat << EOF

# nano initrd first as it is the fastest
menuentry "Fatdog64 Linux Fast" {
	icon /EFI/boot/icons/os_fatdog.png
	loader /fatdog64/vmlinuz
	initrd /fatdog64/initrd-nano
	options "rootfstype=ramfs mergeinitrd1=local:/fatdog64/initrd waitdev=3 $use_basesfs $use_kernelmodules"
}
EOF
					fi
					[ $use_basesfs ] && use_basesfs="waitdev=3 $use_basesfs"
					cat << EOF

# if fail then use standard initrd
menuentry "Fatdog64 Linux Standard" {
	icon /EFI/boot/icons/os_fatdog.png
	loader /fatdog64/vmlinuz
	initrd /fatdog64/initrd
	options "rootfstype=ramfs $use_kernelmodules $use_basesfs"
}
EOF
				} >> refind.conf
			)

			# remove irrelevant files
			rm -rf EFI/grub2						
		fi
		
		# remove irrelevant files
		rm -r boot help
		rm -f efiboot.img fix-usb.sh grldr isolinux.* menu.lst *.c32
		rm -f fatdog.* *.pf2 Shellx64.efi grub.cfg

		# reorganize fatdog files to a folder
		echo "Copy entire content of this zip file to a FAT32 USB Flash drive." > INSTALL		
		mkdir fatdog64
		mv initrd* vmlinuz fatdog64
		[ $use_basesfs ] && mv $BASE_SFS fatdog64
		[ $use_kernelmodules ] && mv kernel-modules.sfs fatdog64

		# always remove previous zip before creating a new one
		rm -fv "$targetzip"
		echo "Fatdog $FATDOG_VERSION build on $(date -I)." | zip -0 -r -q -z "$targetzip" .
		ls -hs "$targetzip"
	)
}


########### main distro builder (iso/image/zip) #################

# $1-std,mini,micro,nano (default std) $2-uefi,xorriso-uefi,grldr,isolinux,grub2 (default uefi), or initrd or image
build_distro() {
	ISO_ROOT="$WORKDIR/iso-root.$$"
	cp -a "$FATDOG_ISO_ROOT" $ISO_ROOT
	# Do we want or need to label efiboot.img as FATDOG_LIVE?
	# Does it work for all situation (iso/image)?
	#[ -e $ISO_ROOT/efiboot.img ] && dosfslabel $ISO_ROOT/efiboot.img FATDOG_LIVE

	# version info
	generate_version_info > $ISO_ROOT/VERSION
	[ "$LOG_COMMITS_SINCE" ] && generate_commit_logs > $ISO_ROOT/CHANGES

	# kernel
	KERNEL_VERSION=$KERNEL_VERSION ./get-kernel.sh 
	! [ -e kernels/vmlinuz-$KERNEL_VERSION ] && echo "Can't download kernel $KERNEL_VERSION, exiting..." && exit
	cp kernels/vmlinuz-$KERNEL_VERSION $ISO_ROOT/vmlinuz
	
	# sfs, initrd, image, and iso
	[ -z "$USE_EXISTING" ] && build_multi_sfs base
	case "$2" in
		initrd)
			DUAL_NANO= # don't bother with this if we aren't going to use it
			build_initrd "$1" "$OUTPUT_DIR/$BASE_SFS" "$ISO_ROOT"
			mv "$ISO_ROOT"/initrd "$OUTPUT_DIR"
			;;
		image)
			# Note: we use kpartx because not all versions of losetup supports partition -P flag.
			! type kpartx > /dev/null && echo "Missing kpartx, cannot build image." && exit
			! [ -e "$ISO_ROOT"/usb-boot/boot-images.tar.xz ] && echo "Missing boot-image, cannot build image." && exit
			build_initrd "$1" "$OUTPUT_DIR/$BASE_SFS" "$ISO_ROOT"
			make_image "$2" "$ISO_ROOT" "$OUTPUT_DIR"	
			;;
		zip)
			build_initrd "$1" "$OUTPUT_DIR/$BASE_SFS" "$ISO_ROOT"
			make_zip "$1" "$ISO_ROOT" "$OUTPUT_DIR"	
			;;
		*)
			build_initrd "$1" "$OUTPUT_DIR/$BASE_SFS" "$ISO_ROOT"
			make_iso "$2" "$ISO_ROOT" "$OUTPUT_DIR"	
			;;
	esac
}



#### main ####
run_in_my_dir
mkdir -p "$WORKDIR" "$OUTPUT_DIR"
case $1 in
	single)	 build_single_sfs      ;;
	
	all)     build_multi_sfs all   ;;
	multi)   build_multi_sfs multi ;;
	base)    build_multi_sfs base  ;;
	devx)    build_multi_sfs devx  ;;
	nls)     build_multi_sfs nls   ;;
	base32)  build_multi_sfs base32 ;;
	
	iso)     build_distro "$2" "$3"  ;; # $2-initrd-type $3-iso-type
	initrd)  build_distro "$2" initrd "$WORKDIR" ;; # $2-initrd-type
	image)   build_distro "$2" image "$WORKDIR"  ;; # $2-initrd-type
	zip)     build_distro "$2" zip "$WORKDIR"    ;; # $2-initrd-type

	*) echo "Usage: ${0##*/} [iso|initrd|image|zip|all|multi|single|base|devx|nls|base32]"
	   echo "  iso    options: [std|mini|micro|nano] [uefi|xorriso-uefi|grldr|isolinux|grub2]"
	   echo "  initrd options: [std|mini|micro|nano]"
	   echo "  image  options: [std|mini|micro|nano]"
	   echo "  zip    options: [std|mini|micro|nano]"
	   ;;
esac
