#!/sbin/openrc-run
#seriously, fuck you AMD and Nvidia. Write a sane license that permits redistribution, we just want your shitty hardware to work

extra_commands="aufs_module"

depend() {
	before xdm
}

start() {
	BLACKLIST_FILE="/etc/modprobe.d/blacklist.conf"

	blacklist() {
		grep -q "${1}" "${BLACKLIST_FILE}" || \
			echo "blacklist ${1}" >> "${BLACKLIST_FILE}"
	}

	unblacklist() {
		sed -i "/${1}/d" "${BLACKLIST_FILE}"
	}

	handle_pax() {
		if [ -x "$(command -v paxctl-ng 2> /dev/null)" ]; then
			paxctl-ng -m /usr/bin/X || eerror "Unable to pax-mark /usr/bin/X"
		fi
  }

	switch_to_nvidia() {
		#remove oss
		if lsmod | grep -iq nouveau ; then
			modprobe -r nouveau || eerror "Unable to unload nouveau driver."
		fi

		if [ ! -f "/lib/modules/$(uname -r)/video/nvidia.ko" ]; then
			einfo "Building evil binary nvidia driver..."
			if ! eval FEATURES="-getbinpkg" ${run_merge} nvidia-drivers; then
				eerror "Failed to build nvidia driver, very sad..."
				return
			fi
		fi

		modprobe nvidia || NVIDIA_FAILURE=yes
		if [ "${NVIDIA_FAILURE}" = "yes" ]; then
			eerror "Nvidia binary driver failed to load, something went wrong."
			if lspci -d8086: | grep -q VGA; then
				eerror "Intel detected, crossing fingers and continuing without a working nvidia driver"
			else
				eerror "Falling back to open source nouveau driver..."
				switch_to_nouveau
			fi
		else
			einfo "Enabling evil binary nvidia driver..."
			#blacklist the open drivers
			#check for nvidia in the blacklist and remove it
			unblacklist nvidia
			#check for nouveau in the blacklist and add it
			blacklist nouveau
			#fix pax flags for hardened
			handle_pax
			if [ "$(uname -i)" = "AuthenticAMD" ] && ! grep -q 'mem_encrypt=off' /proc/cmdline; then
				ewarn "This setup is extremely unlikely to work without adding 'mem_encrypt=off' to the kernel line"
			fi

		fi

		#check for intel gpu
		if lspci -d8086: | grep -q VGA; then
			ewarn "nvidia and intel are both detected, using intel for video"
			eend 0
		elif [ "${NVIDIA_FAILURE}" != "yes" ]; then
			/opt/bin/nvidia-xconfig
			einfo "Enabled evil NVIDIA binary GPU driver"
			eend 0
		fi
	}

	switch_to_nouveau() {
		modprobe nouveau || NOUVEAU_FAILURE=yes
		if [ "${NOUVEAU_FAILURE}" = "yes" ] && [ "${NVIDIA_FAILURE}" = "yes" ]; then
			eerror "Both nvidia binary driver and open source nouveau both failed to load, something went wrong. Please report this"
			eend 1
		elif [ "${NOUVEAU_FAILURE}" = "yes" ]; then
			eerror "Nouveau driver failed to load, something went wrong. Please try the binary nvidia driver."
			eend 1
		else
			einfo "Enabling open source nouveau driver..."
			#blacklist the binary drivers
			#check for nouveau in the blacklist and remove it
			unblacklist nouveau
			#check for nvidia in the blacklist and add it
			blacklist nvidia
			eend 0
		fi
	}

	handle_nvidia() {
		einfo "Detected: nvidia gpu, please stand by..."
		if [ "${NOBINDRIVERS}" = 1 ]; then

			#remove the binary driver and insert oss
			if lsmod | grep -iq nvidia ; then
				modprobe -r nvidia || eerror "Unable to unload nvidia driver."
			fi
			if lspci -d8086: | grep -q VGA; then
				einfo "Using Intel GPU."
				eend 0
			else
				switch_to_nouveau
			fi
		else
			if [ "$(uname -i)" = "AuthenticAMD" ] && ! grep -q 'mem_encrypt=off' /proc/cmdline; then
				ewarn "This setup is extremely unlikely to work without adding 'mem_encrypt=off' to the kernel line"
			fi
			#first we detect if the nvidia binary driver can actually support the hardware
			NV_TARBALL="$(FETCHCOMMAND="false" RESUMECOMMAND="false" FEATURES="-getbinpkg" emerge --getbinpkg=n --color=n -f nvidia-drivers --nodeps 2> /dev/zero | \
			grep "run BLAKE2B SHA512 size ;-) ..." | awk '{print $2}')"

			if [ -n "${NV_TARBALL}" ]; then
				cd "$(mktemp -d -t nvidia-detectXXXX)"

				#stolen from unpacker.eclass
				exe="tail -n +$(grep -a '^skip=' "$(portageq envvar DISTDIR)/${NV_TARBALL}" | cut -d= -f2) '$(portageq envvar DISTDIR)/${NV_TARBALL}'"
				eval ${exe} | unxz -d | tar --no-same-owner -xf - --wildcards --no-anchored 'README.txt'

				if [ -f README.txt ]; then
					#stolen from debian's nvidia-detect rules file
					#https://packages.debian.org/source/sid/nvidia-graphics-drivers
					sed -e '0,/A. Supported\|APPENDIX A: SUPPORTED/d' \
						-e '0,/Appendix A. Supported\|APPENDIX A: SUPPORTED/d' \
						-e '0,/^Below\|APPENDIX B/{/ 0x/s/.*  0x\([0-9a-fA-F]\{4\}\).*/10de\1/p; /^.\{41\} [0-9a-fA-F]\{4\} /s/^.\{41\} \([0-9a-fA-F]\{4\}\) .*/10de\1/p};d' \
						README.txt \
						| tr a-f A-F | sort -u > list.txt

					if [ -f list.txt ]; then
						#stolen from nvidia-detect code
						NVIDIA_VGA=$(sudo lspci -mmn -d 10de: | awk '{ gsub("\"",""); print $3 $4 }')

						for pciid in ${NVIDIA_VGA^^}
						do
							if grep -q "${pciid}" list.txt ; then
								NVIDIA_SUPPORTED=1
							fi
						done
						if [ "${NVIDIA_SUPPORTED}" = "1" ]; then
							switch_to_nvidia
						else
							eerror "NVIDIA binary driver requested but doesn't support this hardware, trying nouveau"
							switch_to_nouveau
						fi
					else
						eerror  "Failed to extract or sort PCIIDs from NVIDIA README.txt, please report this."
						eend 1
					fi
				else
					eerror "Failed to unpack README from NVIDIA drivers, please report this."
					eend 1
				fi

			else
				eerror "Missing nvidia-drivers distfile, please download it first or run \"/etc/init.d/binary-driver-handler aufs-module\""
				eerror "before making the livecd to create an aufs-module which contains the needed distfiles."
				eend 1
			fi
		fi
	}

	switch_to_fglrx() {
		#remove oss
		if lsmod | grep -iq radeon ; then
			modprobe -r radeon || eerror "Unable to unload radeon"
		fi

		if [ ! -f "/lib/modules/$(uname -r)/video/fglrx.ko" ]; then
			einfo "Building evil binary AMD driver..."
			if ! eval FEATURES="-getbinpkg" ${run_merge} ati-drivers; then
				eerror "Failed to build AMD driver, very sad..."
				return
			fi
		fi
		modprobe fglrx || FGLRX_FAILURE=yes
		if [ "${FGLRX_FAILURE}" = "yes" ]; then
			eerror "FGLRX binary driver failed to load, something went wrong. Falling back to open source radeon driver."
			switch_to_radeon
		else
			einfo "Enabling evil binary ati driver..."
			#blacklist the open drivers
			#check for fglrx in the blacklist and remove it
			unblacklist fglrx
			#check for radeon in the blacklist and add it
			blacklist radeon
			#fix pax flags for hardened
			handle_pax

			/opt/bin/aticonfig --initial
			einfo "Successfully switched to AMD binary GPU driver"
			eend 0
		fi
	}

	switch_to_radeon() {
		#remove binary and insert oss
		if lsmod | grep -iq radeon ; then
			modprobe -r fglrx || eerror "Unable to unload fglrx."
		fi
		modprobe radeon || RADEON_FAILURE=yes
		if [ "${RADEON_FAILURE}" = "yes" ] && [ "${FGLRX_FAILURE}" = "yes" ]; then
			eerror "Both FGLRX binary driver and open source radeon driver failed. Please report this."
			eend 1
		elif [ "${RADEON_FAILURE}" = "yes" ]; then
			eerror "Radeon driver failed to load, something went wrong. Please try the binary drivers."
			eend 1
		else
			einfo "Enabling open source radeon driver..."
			#blacklist the binary driver
			#check for radeon in the blacklist and remove it
			unblacklist radeon
			#check for fglrx in the blacklist and add it
			blacklist fglrx
			eend 0
		fi
	}

	handle_amd() {
		einfo "Detected: amd gpu, please stand by..."
		if [ "${NOBINDRIVERS}" = 1 ]; then
			switch_to_radeon
		else
			if [ "$(uname -m)" = "x86_64" ]; then
				amd_dcm="amd_dcm64"
			elif [ "$(uname -m)" = "x86" ]; then
				amd_dcm="amd_dcm32"
			else
				eerror "Unsupported arch."
			fi

			ATI_TARBALL="$(FETCHCOMMAND="false" RESUMECOMMAND="false" FEATURES="-getbinpkg" emerge --getbinpkg=n --color=n -f ati-drivers --nodeps 2> /dev/zero | \
				grep "BLAKE2B SHA512 size ;-) ..." | awk '{print $2}' | head -n1)"

			if [ -n "${ATI_TARBALL}" ]; then
				cd "$(mktemp -d -t ati-detectXXXX)"

				unzip -p "$(portageq envvar DISTDIR)/${ATI_TARBALL}" fglrx*/amd-driver-installer-*.run > amd-driver-installer.run
				chmod +x amd-driver-installer.run
				./amd-driver-installer.run --extract ati-drivers

				if [ -f ati-drivers/"${amd_dcm}" ]; then
					cd ati-drivers
					AMD_DCM_OUTPUT="$(./${amd_dcm} 2>&1)"
					if [ "${AMD_DCM_OUTPUT}" = "Supported adapter detected." ]; then
						switch_to_fglrx
					elif [ "${AMD_DCM_OUTPUT}" = "./${amd_dcm}: No supported adapters detected" ]; then
						eerror "User requested binary fglrx but there is no supported hardware present."
						eerror "Falling back to OSS radeon driver."
						switch_to_radeon
					else
						eerror "${amd_dcm} output is not as expected, please report this."
					fi
				else
					eerror "Failed to unpack ${amd_dcm} from ati-drivers, please report this."
					eend 1
				fi

			else
				eerror "Missing ati-drivers distfile, please download it first or run \"/etc/init.d/binary-driver-handler aufs-module\""
				eerror "before making the livecd to create an aufs-module which contains the needed distfiles."
				eend 1
			fi

		fi
	}

	handle_broadcom() {
    if [ "${NOBINDRIVERS}" = 1 ]; then
      einfo "Broadcom wifi devices detected but nobindrivers on command line prevents installing firmware automatically."
      return
    fi
    BROADCOM_READY=""
    for mod in ${BROADCOM_DETECTED}; do
      einfo "Detected: ${mod} hardware, please stand by..."
      TEST_FILE=""
      TEST_FILE="$(FETCHCOMMAND="false" RESUMECOMMAND="false" FEATURES="-getbinpkg" emerge --getbinpkg=n --color=n -f ${mod}-firmware --nodeps 2> /dev/zero | \
         grep "BLAKE2B SHA512 size ;-) ..." | awk '{print $2}' | head -n1)"
      if [ -n "${TEST_FILE}" ]; then
        BROADCOM_READY="${BROADCOM_READY} ${mod}-firmware"
      else
        eerror "Unable to determine distfile for ${mod}-firmware"
      fi
    done
    if eval FEATURES="-getbinpkg" ${run_merge} ${BROADCOM_READY}; then
      return 0
    else
      return 1
    fi
	}

	#parse kernel commandline to see what we are doing
	if grep -q nobindrivers /proc/cmdline; then
		NOBINDRIVERS=1
	else
		NOBINDRIVERS=0
	fi

	#check environment to see if we can make modules or not
	if [ -w /mnt/cdrom/modules ] && [ -x /usr/sbin/makemo ]; then
		run_merge="echo y | makemo"
	else
		run_merge="emerge --nodeps --getbinpkg=n"
	fi

  ebegin "Setting up broadcom firmware if needed..."
  BROADCOM_DETECTED=""
  for mod in b43 b43legacy; do
    if grep -Eq "^${mod} " /proc/modules; then
      if [ ! -d /lib/firmware/${mod} ]; then
        BROADCOM_DETECTED="${BROADCOM_DETECTED} ${mod}"
      else
        einfo "${mod}-firmware appears to already be installed, skipping"
      fi
    fi
  done
  if [ -n "${BROADCOM_DETECTED}" ]; then
    if handle_broadcom; then
      for mod in b43 b43legacy; do
        if grep -Eq "^${mod} " /proc/modules; then
          if [ ! -d /lib/firmware/${mod} ]; then
            eend "Something went wrong installing ${mod}-firmware."
          else
            einfo "${mod}-firmware is now available, reloading ${mod}"
            modprobe -r ${mod}
            sleep 1
            modprobe ${mod}
          fi
        fi
      done
      eend 0
    else
      eend "Something went wrong installing broadcom drivers."
    fi
  fi

  if grep -q notouchyvideo /proc/cmdline; then
    ebegin "Skipping video drivers by request"
    eend 0
  else
    ebegin "Setting up GPU drivers requested by user, this may take a few minutes"

    #check for nvidia GPU
    #lspci | grep -iq "VGA.*NVIDIA"
    if lspci -d10de: | grep -qE 'VGA|3D controller'; then
      handle_nvidia
    #check for AMD GPU
    #lspci | grep -iq "VGA.*Radeon"
    #elif $(lspci -d1002: | grep -q VGA); then
    #	handle_amd
    fi
    eend 0
  fi
}

aufs_module() {
	ebegin "Making aufs module with needed distfiles for binary drivers"
	#temp dir
	T="/dev/shm"
	#target dir
	DEST=/usr/src/pentoo/livecd/trunk/isoroot/modules

	mkdir -p "${T}"/distfiles/rootfs/usr/portage/distfiles/
	DISTDIR="${T}"/distfiles/ emerge -FO ati-drivers
	DISTDIR="${T}"/dev/shm/distfiles/ emerge -FO nvidia-drivers
	cp "${T}"/distfiles/{*[Ll]inux*,xvba*} "${T}"/distfiles/rootfs/usr/portage/distfiles
	chown portage.portage -R "${T}"/distfiles/rootfs/usr/portage
  mksquashfs "${T}/distfiles/rootfs/" "${DEST}/zdistfiles-$(date '+%Y%m%d').lzm" -comp xz -b 1048576 -Xdict-size 1048576 -no-recovery -noappend

	eend 0
}
