#!/bin/sh

#
# author: ghibo
# release: 1.0 (2022-10-14)
#
# - This script builds a newer version of the pcitable and pcitable.x86_64 files according to
#   newer nvidia drivers.
#
# Instructions:
#
# - Copy this file to your workdir and run from there.
#
# - Set the appropriate driver version number for both nvidia470 and nvidia-current in
# the variables $nvidia470_ver and $nvidiacur_ver below. The latest nvidia drivers
# are listed here: https://www.nvidia.com/en-us/drivers/unix/.
#
# - The script downloads the drivers, and extracts the pcitables. The changes for
# pcitable and pcitable.x86_64 are shown into ldetect-lst.patch, or inside the
# git tree of the local dir ldetect-lst (which is an anonymous tree). Then the changes should be
# copied manually to the real (non-anonymous) ldetect-lst tree.
#

# driver version
#
# Note that drivers nvidia470.xx and beyond are 64-bit only.
nvidia470_ver=470.256.02
nvidiacur_ver=580.119.18

GET=aria2c
GETOPT="-c"
WORKDIR="./workdir"

if [ "$(type -p ${GET})" == "" ]; then
	echo "${GET} not found, please install the ${GET} package"
	exit 1
fi

if [ "$(type -p jq)" == "" ]; then
	echo "jq not found, please install the jq package"
	exit 1
fi

if [ "$(type -p git)" == "" ]; then
	echo "git not found, please install the git package"
	exit 1
fi

nvidia470_name=NVIDIA-Linux-x86_64-${nvidia470_ver}.run
nvidia470_dir=NVIDIA-Linux-x86_64-${nvidia470_ver}
export nvidia470_card_string="Card:NVIDIA GeForce 635 to GeForce 920"

nvidiacur_name=NVIDIA-Linux-x86_64-${nvidiacur_ver}.run
nvidiacur_dir=NVIDIA-Linux-x86_64-${nvidiacur_ver}
export nvidiacur_card_string="Card:NVIDIA GeForce 745 series and later"

export nvidia390_card_string="Card:NVIDIA GeForce 420 to GeForce 630"

if [ ! -d "$WORKDIR}" ]; then
	mkdir -p ${WORKDIR}
fi

pushd ${WORKDIR} > /dev/null

# ------ nvidia 470.xx drivers ------
if [ ! -e README_${nvidia470_ver}.txt -a ! -e supported-gpus-${nvidia470_ver}.json ]; then
	if [ ! -e "${nvidia470_name}" ]; then
        	${GET} ${GETOPT} https://us.download.nvidia.com/XFree86/Linux-x86_64/${nvidia470_ver}/${nvidia470_name}
	fi
	if [ ! -d ${nvidia470_dir} ]; then
		sh ${nvidia470_name} --extract-only .
	fi
	cp -pf ${nvidia470_dir}/README.txt README_orig_${nvidia470_ver}.txt
	cp -pf ${nvidia470_dir}/supported-gpus/supported-gpus.json supported-gpus-${nvidia470_ver}.json
	# clean-up driver dir, not needed anymore
	if [ -d ./${nvidia470_dir} ]; then
		rm -rf ./${nvidia470_dir}
	fi
fi	
echo "\"DeviceId\",\"SubVendorId\",\"SubDeviceId\",\"Card Name\"" > pcitable-nvidia470-from-json-extended.csv
cat supported-gpus-${nvidia470_ver}.json | \
	jq -r '.chips[] | select (.legacybranch == null) | [.devid,.subvendorid,.subdevid,.name] | @text'| \
	sed -e 's@^\[@@g' -e 's@\]$@@g' | \
	awk -F, '{      devid=tolower($1); gsub(/"/, "", devid);
	          subvendorid=tolower($2); gsub(/"/, "", subvendorid);
	             subdevid=tolower($3); gsub(/"/, "", subdevid);
	             print(devid "," subvendorid "," subdevid "," $4)}' | \
	sort -u >> pcitable-nvidia470-from-json-extended.csv

cat supported-gpus-${nvidia470_ver}.json | \
	jq -r '.chips[] | select (.legacybranch == null) | [.devid] | @text'| \
	sed -e 's@^\[@@g' -e 's@\]$@@g' -e 's@"@@g' | \
	awk -F, '{print("0x10de" "\t" tolower($1) "\t" "\"" ENVIRON["nvidia470_card_string"] "\"")}' | \
	sort -u > pcitable-nvidia470-from-json.txt

cat supported-gpus-${nvidia470_ver}.json | \
	jq -r '.chips[] | select (.legacybranch == null) | [.devid,.subvendorid,.subdevid] | @text'| \
	sed -e 's@^\[@@g' -e 's@\]$@@g' | \
	awk -F, '{      devid=tolower($1); gsub(/"/, "", devid);
	          subvendorid=tolower($2); gsub(/"/, "", subvendorid);
	             subdevid=tolower($3); gsub(/"/, "", subdevid);
	             print("0x10de" "\t" devid "\t" subvendorid "\t" subdevid "\t" "\"" ENVIRON["nvidia470_card_string"] "\"")}' | \
	sed -e 's@\tnull@@g' | \
	sort -u > pcitable-nvidia470-from-json-withsubdev.txt
# ------------------------------------

# ------ nvidia current drivers ------
if [ ! -e README_${nvidiacur_ver}.txt -a ! -e supported-gpus-${nvidiacur_ver}.json ]; then
	if [ ! -e "${nvidiacur_name}" ]; then
		${GET} ${GETOPT} https://us.download.nvidia.com/XFree86/Linux-x86_64/${nvidiacur_ver}/${nvidiacur_name}
	fi 
	if [ ! -d ${nvidiacur_dir} ]; then
		sh ${nvidiacur_name} --extract-only .
	fi
	cp -pf ${nvidiacur_dir}/README.txt README_orig_${nvidiacur_ver}.txt
	cp -pf ${nvidiacur_dir}/supported-gpus/supported-gpus.json supported-gpus-${nvidiacur_ver}.json
	# clean-up driver dir, not needed anymore
	if [ -d ./${nvidiacur_dir} ]; then
		rm -rf ./${nvidiacur_dir}
	fi
fi
echo "\"DeviceId\",\"SubVendorId\",\"SubDeviceId\",\"Card Name\"" > pcitable-nvidiacurrent-from-json-extended.csv
cat supported-gpus-${nvidiacur_ver}.json | \
	jq -r '.chips[] | select (.legacybranch == null) | [.devid,.subvendorid,.subdevid,.name] | @text'| \
	sed -e 's@^\[@@g' -e 's@\]$@@g' | \
	awk -F, '{      devid=tolower($1); gsub(/"/, "", devid);
	          subvendorid=tolower($2); gsub(/"/, "", subvendorid);
	             subdevid=tolower($3); gsub(/"/, "", subdevid);
	             print(devid "," subvendorid "," subdevid "," $4)}' | \
	sort -u >> pcitable-nvidiacurrent-from-json-extended.csv

cat supported-gpus-${nvidiacur_ver}.json | \
	jq -r '.chips[] | select (.legacybranch == null) | [.devid] | @text'| \
	sed -e 's@^\[@@g' -e 's@\]$@@g' -e 's@"@@g' | \
	awk -F, '{print("0x10de" "\t" tolower($1) "\t" "\"" ENVIRON["nvidiacur_card_string"] "\"")}' | \
	sort -u > pcitable-nvidiacurrent-from-json.txt

cat supported-gpus-${nvidiacur_ver}.json | \
	jq -r '.chips[] | select (.legacybranch == null) | [.devid,.subvendorid,.subdevid] | @text'| \
	sed -e 's@^\[@@g' -e 's@\]$@@g' | \
	awk -F, '{      devid=tolower($1); gsub(/"/, "", devid);
	          subvendorid=tolower($2); gsub(/"/, "", subvendorid);
	             subdevid=tolower($3); gsub(/"/, "", subdevid);
	             print("0x10de" "\t" devid "\t" subvendorid "\t" subdevid "\t" "\"" ENVIRON["nvidiacur_card_string"] "\"")}' | \
	sed -e 's@\tnull@@g' | \
	sort -u > pcitable-nvidiacurrent-from-json-withsubdev.txt

# -----------------------------------------------------

cp pcitable-nvidiacurrent-from-json.txt pcitable-nvidiacurrent-nvidia470-from-json-temp.txt
perl -pi -e "s@${nvidiacur_card_string}@${nvidia470_card_string}@g" pcitable-nvidiacurrent-nvidia470-from-json-temp.txt

# pcitable-nvidia470-from-json-residual.txt contains the list of the cards supported by driver nvidia470, which are
# not already supported by driver nvidia-current.
comm -1 -3 pcitable-nvidiacurrent-nvidia470-from-json-temp.txt \
	pcitable-nvidia470-from-json.txt > pcitable-nvidia470-from-json-residual.txt
rm -f pcitable-nvidiacurrent-nvidia470-from-json-temp.txt

popd > /dev/null

# download ldetect-lst from git
if [ ! -e ldetect-lst ]; then
	git clone git://git.mageia.org/software/ldetect-lst
else
	(cd ldetect-lst
	 git pull --quiet
	)
fi

cp -pf ldetect-lst/lst/pcitable ${WORKDIR}/
cp -pf ldetect-lst/lst/pcitable.x86_64 ${WORKDIR}/

pushd ${WORKDIR} > /dev/null

cat pcitable | grep -E "${nvidia390_card_string}" | sort -u > pcitable-nvidia390.txt
cp -p pcitable-nvidia470-from-json-residual.txt pcitable-nvidia470-nvidia390-temp.txt
perl -pi -e "s@${nvidia470_card_string}@${nvidia390_card_string}@g" pcitable-nvidia470-nvidia390-temp.txt

# pcitable-nvidia390-residual.txt contains the list of the cards supported by the driver for
# ${nvidia390_card_string} (now supposed to be nouveau) which are not already supported by
# the driver nvidia470.
comm -1 -3 pcitable-nvidia470-nvidia390-temp.txt \
	pcitable-nvidia390.txt > pcitable-nvidia390-residual.txt
rm -f pcitable-nvidia470-nvidia390-temp.txt

cat pcitable.x86_64 | \
	grep -E -v "${nvidia470_card_string}|${nvidiacur_card_string}" > pcitable.x86_64.skel
cat pcitable-nvidiacurrent-from-json.txt >> pcitable.x86_64.skel

# $nvidia470_card_string is listed on pcitable (though nvidia470 is x86_64 only). Is that the right place for it
# or should it be moved to pcitable.x86_64? To review in ldetect-lst.
cat pcitable | \
	grep -E -v "${nvidia470_card_string}" > pcitable.skel
# append cards for nvidia470 not supported by nvidia-current.
cat pcitable-nvidia470-from-json-residual.txt >> pcitable.skel

# cleanup pcitables against older $nvidia390_card_string entries, to avoid "multiple entry" errors during
# ldetect-lst package building
cat pcitable.x86_64.skel | \
	grep -E -v "$nvidia390_card_string" > pcitable_cleaned.x86_64.skel
	
cat pcitable.skel | \
	grep -E -v "$nvidia390_card_string" > pcitable_cleaned.skel

# append older nvidia390's residual pcitable (now used by nouveau) to pcitable
cat pcitable_cleaned.skel pcitable-nvidia390-residual.txt | sort > pcitable
cat pcitable_cleaned.x86_64.skel | sort > pcitable.x86_64

popd > /dev/null

cp -pf ${WORKDIR}/{pcitable,pcitable.x86_64} ldetect-lst/lst/

(cd ldetect-lst
 git diff > ../ldetect-lst.patch
)

echo "-----------------------------"
echo "ldetect-lst diffs:"
cat ldetect-lst.patch

echo "-----------------------------"
pushd ${WORKDIR} > /dev/null
	echo "new card entries added or moved (approximate list)"
	newcards=$(cat ../ldetect-lst.patch | grep ^+0 | awk '{print $2}')
	for i in ${newcards}; do \
		grep -F -i $i pcitable-nvidiacurrent-from-json-extended.csv pcitable-nvidia470-from-json-extended.csv
	done
	echo
	echo "older entries removed or moved (approximate list)"
	oldcards=$(cat ../ldetect-lst.patch | grep ^\-0 | awk '{print $2}')
	for i in ${oldcards}; do \
		grep -F -i $i pcitable-nvidiacurrent-from-json-extended.csv pcitable-nvidia470-from-json-extended.csv
	done
popd > /dev/null
