#!/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
      /usr/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
      if [ "$(uname -m)" = "i686" ]; then
        if grep -o -w 'lm' /proc/cpuinfo; then
          eerror "Your processor supports amd64 but you are using an x86 iso.  Please use the amd64 iso instead."
        fi
        eerror "NVIDIA binary driver requested but doesn't support x86 hardware, trying nouveau"
        switch_to_nouveau
        return
      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 $3}' | sed -e 's/\x1b\[[0-9;]*m//g')"

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

        #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
}
