#!/usr/bin/env sh

# SPDX-FileCopyrightText: 2022 Caleb La Grange <thonkpeasant@protonmail.com>
# SPDX-FileCopyrightText: 2022 Ferass El Hafidi <vitali64pmemail@protonmail.com>
# SPDX-FileCopyrightText: 2023 Leah Rowe <info@minifree.org>
# SPDX-License-Identifier: GPL-3.0-only

board="${1}"
# A shorthand for each board so as not to duplicate blobs for boards of different sizes
board_short=${board%%_*mb}

mecleaner=""
blobdir="blobs"
appdir="${blobdir}/app"
dl_hash=""
dl_path=""
dl_ext=""
_7ztest="a"

Fail(){
	printf "\nERROR: $@\n"
	exit 1
}

Download_needed(){
	dl_path="${blobdir}/me.${dl_url##*.}"
	if [ "${dl_path}" = "blobs/me.dmg" ]; then
		dl_ext="dmg" # apple update file
	elif [ "${dl_path}" = "blobs/me.exe" ]; then
		dl_ext="exe" # lenovo update file
	else
		printf "Board `%s`: Download `%s` of unsupported file extension.\n" \
			"${board}" "${dl_ext}"
		printf "dl_path: %s\n" "${dl_path}"
		exit 1
	fi

	for need in ${needs}; do
		case ${need} in
			*ME*)
				Extract || _failed="${_failed} me"
				;;
			*MRC*)
				./download mrc || _failed="${_failed} mrc"
				;;
	esac
	done
	
	if [ ! -z ${_failed+x} ]; then
	Fail "failed to obtain ${_failed}\nYou may try manually extracting blobs with './blobutil extract'"
	fi
	return 0
}

Extract() {
	_me_destination=${CONFIG_ME_BIN_PATH#../../}

	if [ -f "${_me_destination}" ]; then
		printf 'me already downloaded\n'
		return 0
	fi

	if [ -z "${dl_url+x}" ]; then
		printf 'no me download available for this board\n'
		return 1
	fi

	if [ ! -d "${_me_destination%/*}" ]; then
		mkdir -p ${_me_destination%/*}
	fi

	printf "Extracting neutered me for ${board}\n"
	
	# Delete old me downloads in case user is building for multiple boards
	if [ -f "${dl_path}" ]; then
		rm -f "${dl_path}"
	fi

	if [ -d "${appdir}" ]; then
		rm -Rf ${appdir}
	fi

	curl ${dl_url} > "${dl_path}" || curl ${dl_url_bkup} > "${dl_path}"
	if [ ! -f "${dl_path}" ]; then
		printf "could not download vendor file\n"
		return 1
	fi

	if [ "$(sha1sum $dl_path | awk '{print $1}')" != "${dl_hash}" ]; then
		printf 'checksum of downloaded me did not match\ncorrupted me downloaded or wrong me for board\n'
		rm -f "${dl_path}"
		return 1
	fi

	if [ "${dl_ext}" = "dmg" ]; then # apple
		7z x "${dl_path}" -o${appdir} || Fail 'could not extract apple ME update with 7z'
		Extract_apple_me "$(pwd)/${_me_destination}" "$(pwd)/${appdir}" || return 1
	elif [ "${dl_ext}" = "exe" ]; then # lenovo
		Extract_lenovo_me "${_me_destination}" || return 1
	fi

	printf "Truncated and cleaned me output to ${_me_destination}\n"
}

# TODO: this code is comically cursed, and carcinogenic. rewrite
# ME updates in Apple's FW updates (.dmg files) are at random
# locations internally, with seemingly random file names.
# ATTACK STRATEGY: recursively extract via 7z and go systematically
# through *every* file, running me_cleaner until we get a hit
Extract_apple_me() {
	_me_destination="${1}"
	cdir="${2}" # must be an absolute path, not relative
	(
	printf "Entering %s\n" "${cdir}"
	printf "FOO\n"
	cd "${cdir}" || exit 1
	for i in *; do
		if [ -f "${_me_destination}" ]; then
			# me.bin found, so avoid needless further traversal
			break
		elif [ -L "${i}" ]; then
			# symlinks are a security risk, in this context
			continue
		elif [ -f "${i}" ]; then
			"${mecleaner}" -r -t -O "${_me_destination}" "${i}" \
				&& break # (we found me.bin)
			_7ztest="${_7ztest}a"
			7z x "${i}" -o${_7ztest} || continue
			Extract_apple_me "${_me_destination}" "${cdir}/${_7ztest}"
			cdir="${1}"
			cd "${cdir}"
		elif [ -d "$i" ]; then
			Extract_apple_me "${_me_destination}" "${cdir}/${i}"
			cdir="${1}"
			echo "BAR"
			cd "${cdir}"
		else
			printf "SKIPPING: %s\n" "${i}"
		fi
	done
	)
	if [ ! -f "${_me_destination}" ]; then
		printf "me.bin not found in Apple .dmg file\n"
		return 1
	else
		return 0
	fi
}

Extract_lenovo_me(){
	_me_destination="${1}"

	innoextract "${dl_path}" -d ${blobdir} || Fail 'could not extract lenovo ME update with innoextract' 
	./me_cleaner/me_cleaner.py -r -t -O ${_me_destination} ${appdir}/*ME*.bin \
	|| ./resources/blobs/me7_update_parser.py -O ${_me_destination} ${appdir}/ME7*.bin \
	|| return 1
}

Build_deps(){
	if [ ! -d me_cleaner ]; then
		printf "downloading me_cleaner\n"
		./download me_cleaner || Fail 'could not download me_cleaner'
	fi
	mecleaner="$(pwd)/me_cleaner/me_cleaner.py"

	if [ ! -d coreboot/default ]; then
		printf "downloading coreboot\n"
		./download coreboot default || Fail 'could not download coreboot'
	fi
	
	if [ ! -f "coreboot/default/util/ifdtool/ifdtool" ]; then
		printf "building ifdtool from coreboot\n"
		make -C coreboot/default/util/ifdtool || Fail 'could not build ifdtool'
	fi

	return 0
}

set -- "resources/coreboot/${board}/config/*"
. ${1} 2>/dev/null
. "resources/coreboot/${board}/board.cfg"

if [ "${CONFIG_HAVE_MRC}" = "y" ]; then
	printf 'haswell board detected, downloading mrc\n'
	needs="${needs} MRC"
fi

if [ "${CONFIG_HAVE_IFD_BIN}" = "y" ]; then
	printf 'board needs intel firmware descriptor\n'
	needs="${needs} IFD"
fi

if [ "${CONFIG_HAVE_ME_BIN}" = "y" ]; then
	printf 'board needs intel management engine\n'
	needs="${needs} ME"
fi

if [ "${CONFIG_HAVE_GBE_BIN}" = "y" ]; then
	printf 'board needs gigabit ethernet firmware\n'
	needs="${needs} GBE"
fi

# Quickly exit without wasting more time if there are no blobs needed (GM45)
if [ -z ${needs+x} ]; then
	printf 'No binary blobs needed for this board\n'
	exit 0
fi

Build_deps || exit 1

while read -r line ; do
	case ${line} in
		DL_hash*)
		set ${line}
		dl_hash=${2}
		;;
		DL_url*)
		set ${line}
		dl_url=${2}
		;;
		DL_url_bkup*)
		set ${line}
		dl_url_bkup=${2}
		;;
	esac
done << EOF
$(eval "awk ' /\{.*${board_short}.*}{/ {flag=1;next} /\}/{flag=0} flag { print }' resources/blobs/sources")
EOF
Download_needed
