# Copyright 1999-2026 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

EAPI=8

inherit desktop udev xdg

# Upstream names the archive after a TWO-component version ("21.1"), while the
# API that .autoupdate probes reports three ("21.1.0").  The ${PV%.0} strip is
# what reconciles them, and it is only correct while the third component is 0 --
# a hypothetical 21.1.1 would keep all three, which is also what upstream names
# such a release.  A bump that changes this shape must re-check the file name
# before trusting the Manifest.

# Blackmagic RESPINS a release under the SAME file name: "21.1" was build 0014
# on 7 Sep 2026 and build 0017 on 10 Sep 2026, and only the second one is still
# served.  The zip is RESTRICT=fetch, so the mismatch surfaces as a digest
# failure on the user's machine and nowhere else -- there is no fetch here that
# could have caught it.  The build the Manifest describes is readable from the
# payload itself:
#
#   unzip -o DaVinci_Resolve_21.1_Linux.zip
#   chmod u+x DaVinci_Resolve_21.1_Linux.run
#   unsquashfs -o "$(./DaVinci_Resolve_21.1_Linux.run --appimage-offset)" \
#       -d out DaVinci_Resolve_21.1_Linux.run bin/resolve
#   strings -a out/bin/resolve | grep -oE '21\.1\.0\.[0-9]{4}'
#
# A digest failure reported by a user is therefore a respin until proven
# otherwise, NOT a corrupt download.
ZIP_NAME="DaVinci_Resolve_${PV%.0}_Linux"
RUN_NAME="${ZIP_NAME}.run"

DESCRIPTION="Professional video editing, color, effects and audio post-processing"
HOMEPAGE="https://www.blackmagicdesign.com/support/family/davinci-resolve-and-fusion"
SRC_URI="${ZIP_NAME}.zip"
S="${WORKDIR}"

LICENSE="all-rights-reserved"
SLOT="0"
# ~amd64 only, and this is the one case where that is not a shortcut: Blackmagic
# publishes a single Linux build and it is x86_64.  ARM64 exists for macOS on
# Apple silicon, not for Linux, so there is no arm64 payload to key.
KEYWORDS="~amd64"

IUSE="video_cards_amdgpu video_cards_intel video_cards_nvidia"
RESTRICT="fetch mirror bindist strip"

QA_PREBUILT="*"

BDEPEND="
	app-arch/unzip
	dev-util/patchelf
"

# RDEPEND was not inherited on trust: it is the DT_NEEDED closure of every ELF
# surviving src_prepare (scanelf -RyBF), minus the ~470 sonames the bundle ships
# itself.  Two results are worth recording, because both are invisible in the
# ebuild text:
#
#  * media-libs/libpulse is NOT here, and its absence is deliberate.  The string
#    "libpulse" does not occur anywhere in the 7 GiB payload -- not as DT_NEEDED,
#    not as a dlopen literal.  Resolve talks to ALSA (libasound.so.2 is a real
#    DT_NEEDED); PulseAudio reaches it through ALSA like any other client.  The
#    upstream overlays list it anyway, which forces PulseAudio onto anyone
#    running plain ALSA or PipeWire.
#  * the whole CUDA stack (libcudart, libcublas, libcudnn, libcufft) is BUNDLED
#    under libs/, so video_cards_nvidia only has to supply libcuda.so.1 -- that
#    is the driver, and nothing from dev-util/nvidia-cuda-toolkit is needed.
#
# The libs that only ever appeared through files src_prepare deletes are the
# reason those deletions exist: bin/sqlite3 was the sole consumer of ncurses 5
# and readline 6, and "DaVinci Control Panels Setup/libk5crypto.so.3" the sole
# consumer of OpenSSL 1.1.  Neither version exists in the tree any more.
RDEPEND="
	app-arch/bzip2
	app-arch/xz-utils
	app-crypt/mit-krb5
	app-misc/ca-certificates
	dev-libs/expat
	dev-libs/glib:2
	dev-libs/nspr
	dev-libs/nss
	media-libs/alsa-lib
	media-libs/fontconfig
	media-libs/freetype
	media-libs/glu
	media-libs/libglvnd
	sys-apps/dbus
	sys-apps/util-linux
	sys-devel/gcc:*[openmp]
	virtual/libcrypt:=
	virtual/libudev
	virtual/opencl
	virtual/udev
	virtual/zlib:=
	sys-libs/mtdev
	video_cards_amdgpu? ( dev-libs/rocm-opencl-runtime )
	video_cards_intel? ( dev-libs/intel-compute-runtime )
	video_cards_nvidia? ( x11-drivers/nvidia-drivers )
	x11-libs/libdrm
	x11-libs/libICE
	x11-libs/libSM
	x11-libs/libX11
	x11-libs/libxcb
	x11-libs/libXext
	x11-libs/libXi
	x11-libs/libXrender
	x11-libs/libXt
	x11-libs/libXtst
	x11-libs/libXxf86vm
	x11-libs/libxkbcommon[X]
	x11-libs/libxkbfile
	x11-libs/xcb-util
	x11-libs/xcb-util-cursor
	x11-libs/xcb-util-image
	x11-libs/xcb-util-keysyms
	x11-libs/xcb-util-renderutil
	x11-libs/xcb-util-wm
"

# The manual route comes FIRST and stays complete on its own: bentoolkit is not a
# dependency of this package, and whoever does not have it installed may not be
# left without a way to get the archive.
pkg_nofetch() {
	einfo
	einfo "  ${ZIP_NAME}.zip is behind a registration form, so Portage cannot"
	einfo "  fetch it. Download it from:"
	einfo
	einfo "    ${HOMEPAGE}"
	einfo
	einfo "  place it in your DISTDIR directory, and re-run emerge."
	einfo
	einfo "  With app-portage/bentoolkit installed, one command does the same --"
	einfo "  it submits the form and writes the archive under the exact name this"
	einfo "  package's Manifest expects:"
	einfo
	einfo "    bentoo distfile fetch ${CATEGORY}/${PN} --version ${PV%.0}"
	einfo
	einfo "  The version there is the TWO-component one, matching the file name."
	einfo
}

src_unpack() {
	unpack "${ZIP_NAME}.zip" || die

	chmod u+x "${RUN_NAME}" || die
	"${S}/${RUN_NAME}" --appimage-extract || die
}

_is_elf() {
	[[ -f ${1} ]] || return 1
	[[ $(LC_ALL=C od -An -tx1 -N4 "${1}" 2>/dev/null) == *"7f 45 4c 46"* ]]
}

_set_rpath() {
	local rpath=${1}
	shift

	local f
	for f; do
		[[ -e ${f} ]] || continue
		_is_elf "${f}" || continue
		patchelf --force-rpath --set-rpath "${rpath}" "${f}" ||
			die "patchelf failed on ${f}"
	done
}

_has_bad_rpath() {
	local rpath
	rpath=$(patchelf --print-rpath "${1}" 2>/dev/null) || return 1

	[[ ${rpath} == *"/home/"* ||
		${rpath} == *"/persistent/"* ||
		${rpath} == *"/var/lib/jenkins/"* ||
		${rpath} == *"/xxxxxxxx"* ||
		${rpath} == *@loader_path* ||
		${rpath} == *"/lib/qt-"* ]]
}

_sanitize_bad_rpaths() {
	local root=${1}
	local rpath=${2}
	local f

	[[ -d ${root} ]] || return

	while IFS= read -r -d '' f; do
		_is_elf "${f}" || continue
		_has_bad_rpath "${f}" || continue
		_set_rpath "${rpath}" "${f}"
	done < <(find "${root}" -type f -print0)
}

_patch_desktop_file() {
	local file=${1}
	local exec=${2}
	local icon=${3}

	sed -i \
		-e "s|^Exec=.*|Exec=${exec}|" \
		-e "s|^Icon=.*|Icon=${icon}|" \
		-e "/^Path=/d" \
		"${file}" || die
}

_fperms_image() {
	local mode=${1}
	shift

	local f
	for f; do
		[[ -e ${f} ]] || continue
		fperms "${mode}" "${f#${ED}}" || die
	done
}

_newlib_ldscript() {
	local lib=${1}
	local target=${2}

	insinto "/usr/$(get_libdir)"
	newins - "${lib}" <<-EOF
		/* GNU ld script */
		GROUP ( ${EPREFIX}${target} )
	EOF
	fperms a+x "/usr/$(get_libdir)/${lib}" || die
}

src_prepare() {
	default

	local squashfs="squashfs-root"
	local install_dir="/opt/${PN}"

	chmod -R u+rwX "${squashfs}" || die

	mkdir -p "${squashfs}/panel-framework" || die
	tar -xzf "${squashfs}/share/panels/dvpanel-framework-linux-x86_64.tgz" \
		-C "${squashfs}/panel-framework" || die
	chmod -R u+rwX "${squashfs}/panel-framework" || die
	rm -f "${squashfs}/share/panels/dvpanel-framework-linux-x86_64.tgz" || die

	rm -rf "${squashfs}"/{installer*,AppRun*,CentOSUpdate} || die
	rm -f "${squashfs}/DaVinci Control Panels Setup/libk5crypto.so.3" || die
	rm -f "${squashfs}/share/DaVinciResolveInstaller.desktop" || die
	rm -f "${squashfs}/scripts/"{pre_install.sh,post_install.sh,uninstall.sh} || die
	rm -f "${squashfs}/LUT/GenLut" "${squashfs}/LUT/GenOutputLut" || die
	rm -f "${squashfs}/bin/sqlite3" || die
	if ! use video_cards_nvidia; then
		rm -f \
			"${squashfs}/BlackmagicRAWPlayer/BlackmagicRawAPI/libDecoderCUDA.so" \
			"${squashfs}/BlackmagicRAWSpeedTest/BlackmagicRawAPI/libDecoderCUDA.so" \
			"${squashfs}/libs/libDecoderCUDA.so" || die
	fi
	rm -rf \
		"${squashfs}/Onboarding/qml/Qt/labs/lottieqt" \
		"${squashfs}/Onboarding/qml/QtQml/RemoteObjects" \
		"${squashfs}/Onboarding/qml/QtQuick/Particles.2" \
		"${squashfs}/Onboarding/qml/QtQuick/Shapes" \
		"${squashfs}/Onboarding/qml/QtQuick/VirtualKeyboard" || die

	_set_rpath "\$ORIGIN/../libs:\$ORIGIN/../libs/Fusion" \
		"${squashfs}/bin/resolve"
	_set_rpath "\$ORIGIN/../libs:\$ORIGIN/../libs/Fusion" \
		"${squashfs}/Onboarding/DaVinci_Resolve_Welcome"
	_set_rpath "\$ORIGIN/lib" \
		"${squashfs}/panel-framework/libDaVinciPanelAPI.so" \
		"${squashfs}/panel-framework/libFairlightPanelAPI.so"

	patchelf --set-soname libsonyxavcenc.so \
		"${squashfs}/libs/libsonyxavcenc.so.1.1.11.68" || die

	while IFS= read -r -d '' f; do
		_set_rpath "\$ORIGIN" "${f}"
	done < <(find "${squashfs}" -type f \
		\( -name "libc++abi.so*" \
		-o -name "libgcc_s.so.1" \
		-o -name "libCrmSdk.so.*" \
		-o -name "libcrypto.so.*" \
		-o -name "libcurl.so" \
		-o -name "libsharpyuv.so.0.1.1" \
		-o -name "libssl.so.*" \
		-o -name "libwebpdecoder.so.3.1.10" \
		-o -name "libxmlsec1-openssl.so" \) -print0)

	_sanitize_bad_rpaths "${squashfs}/libs" "\$ORIGIN:\$ORIGIN/Fusion"
	_sanitize_bad_rpaths "${squashfs}/plugins" "\$ORIGIN:\$ORIGIN/../libs"
	_sanitize_bad_rpaths "${squashfs}/Onboarding" \
		"\$ORIGIN:${install_dir}/libs:${install_dir}/libs/Fusion"

	ln -s "../BlackmagicRAWPlayer/BlackmagicRawAPI" \
		"${squashfs}/bin/BlackmagicRawAPI" || die

	while IFS= read -r -d '' f; do
		sed -i "s|RESOLVE_INSTALL_LOCATION|${install_dir}|g" "${f}" || die
	done < <(find "${squashfs}/share" -type f \
		\( -name "*.desktop" -o -name "*.directory" -o -name "*.menu" \) -print0)

	_patch_desktop_file "${squashfs}/share/DaVinciResolve.desktop" \
		"davinci-resolve %u" "davinci-resolve"
	_patch_desktop_file "${squashfs}/share/DaVinciResolveCaptureLogs.desktop" \
		"davinci-resolve-capture-logs" "davinci-resolve"
	_patch_desktop_file "${squashfs}/share/DaVinciControlPanelsSetup.desktop" \
		"davinci-control-panels-setup" "davinci-resolve-panels-setup"
	_patch_desktop_file "${squashfs}/share/blackmagicraw-player.desktop" \
		"blackmagicraw-player %f" "blackmagicraw-player"
	_patch_desktop_file "${squashfs}/share/blackmagicraw-speedtest.desktop" \
		"blackmagicraw-speedtest %f" "blackmagicraw-speedtest"

	sed -i 's#Categories=Video#Categories=AudioVideo;Video;#' \
		"${squashfs}/share/blackmagicraw-player.desktop" \
		"${squashfs}/share/blackmagicraw-speedtest.desktop" || die
	echo "StartupWMClass=resolve" >> "${squashfs}/share/DaVinciResolve.desktop" || die

	cat > "${squashfs}/share/etc/udev/rules.d/75-sdx.rules" <<-EOF || die
	SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="096e", MODE="0666"
	EOF
}

src_install() {
	local install_dir="/opt/${PN}"
	local app_dir="${ED}${install_dir}"
	local iconsrc="${app_dir}/graphics"

	dodir "${install_dir}"
	cp -a squashfs-root/. "${app_dir}/" || die

	local f
	while IFS= read -r -d '' f; do
		_fperms_image 0755 "${f}"
	done < <(find "${app_dir}" -type d -print0)

	while IFS= read -r -d '' f; do
		_is_elf "${f}" || continue
		_fperms_image 0755 "${f}"
	done < <(find "${app_dir}" -type f -print0)

	_fperms_image 0755 \
		"${app_dir}/bin/run_bmdpaneld" \
		"${app_dir}/libs/libc++.so" \
		"${app_dir}/scripts/script.checkfirmware" \
		"${app_dir}/scripts/script.getlogs.v4" \
		"${app_dir}/scripts/script.halt" \
		"${app_dir}/scripts/script.kill" \
		"${app_dir}/scripts/script.reboot" \
		"${app_dir}/scripts/script.start" \
		"${app_dir}/scripts/script.update"

	local runtime_dir
	for runtime_dir in \
		"Apple Immersive/Calibration" \
		.crashreport \
		.LUT \
		configs \
		DolbyVision \
		GPUCache \
		logs
	do
		keepdir "${install_dir}/${runtime_dir}"
	done

	# Directories resolve writes to at RUNTIME, as an ordinary user.  The install
	# image is root-owned 0755, so each one has to be made writable here or it is
	# simply not writable at all.  Four independent sources, no overlap:
	#
	#  * "Immersive" is the one that made 21.1 unusable.  bin/resolve mkdir()s
	#    ${install_dir}/Immersive/Canon/STMap during startup and ABORTS with
	#    "Failed to create application support directories" when that returns
	#    EACCES.  Measured, not guessed: an LD_PRELOAD mkdir(2) tracer over the
	#    installed image names that mkdir and no other as the failing one, and the
	#    abort disappears once the directory exists writable.  Note the name -- it
	#    is NOT the "Apple Immersive" below, which is a different directory.
	#  * easyDCP, LUT and .license are what the vendor scripts/post_install.sh
	#    creates 0775 and chowns to the installing user (create_config_files), and
	#    "Apple Immersive" is its set_folder_permissions "chmod a+w".  src_prepare
	#    deletes that script, which is why those permissions are reproduced here.
	#  * /var/BlackmagicDesign/DaVinci Resolve is the installer's "Common Data
	#    Dir", shared by every user of the machine.  The path is hardcoded in
	#    bin/resolve, which creates ".migrated" inside it, so it cannot be moved
	#    under /opt.
	#  * Extras and Fairlight are absent from the payload AND from post_install.sh,
	#    so nothing above predicts them -- they were read off a running 21.1.0.0017
	#    on 2026-09-19.  Extras is the Download Manager's package store: failing to
	#    create it is not degraded, it aborts the whole subsystem
	#    ("DDM: failed to create storage dir ... (Permission denied)" ->
	#    "DDM init failed"), so Blackmagic Cloud and downloadable extras are gone
	#    for the session.  Fairlight logs "mkdir failed ... (errno 13)" three times
	#    and then comes up anyway, which is exactly why it went unnoticed.
	#
	# 0777 rather than a group: upstream chowns to the single user running the
	# installer, which a package cannot do -- there is no such user at merge time,
	# and the machine may well have several.  Upstream itself uses 0777 for the
	# /var directory.
	local writable_dir
	for writable_dir in \
		"Apple Immersive" \
		.license \
		easyDCP \
		Extras \
		Fairlight \
		Immersive
	do
		keepdir "${install_dir}/${writable_dir}"
		fperms 0777 "${install_dir}/${writable_dir}"
	done

	# LUT ships in the payload, so it needs the mode and not the directory.
	fperms 0777 "${install_dir}/LUT"

	keepdir "/var/BlackmagicDesign/DaVinci Resolve"
	fperms 0777 "/var/BlackmagicDesign/DaVinci Resolve"

	cp "${app_dir}/share/"{default-config.dat,log-conf.xml} "${app_dir}/configs/" || die
	cp "${app_dir}/share/default_cm_config.bin" "${app_dir}/DolbyVision/" || die

	dosym "${install_dir}/bin/resolve" "/usr/bin/${PN}"
	dosym "${install_dir}/scripts/script.getlogs.v4" "/usr/bin/davinci-resolve-capture-logs"
	dosym "${install_dir}/DaVinci Control Panels Setup/DaVinci Control Panels Setup" \
		"/usr/bin/davinci-control-panels-setup"
	dosym "${install_dir}/BlackmagicRAWPlayer/BlackmagicRAWPlayer" \
		"/usr/bin/blackmagicraw-player"
	dosym "${install_dir}/BlackmagicRAWSpeedTest/BlackmagicRAWSpeedTest" \
		"/usr/bin/blackmagicraw-speedtest"
	_newlib_ldscript libDaVinciPanelAPI.so \
		"${install_dir}/panel-framework/libDaVinciPanelAPI.so"
	_newlib_ldscript libFairlightPanelAPI.so \
		"${install_dir}/panel-framework/libFairlightPanelAPI.so"

	newmenu "${app_dir}/share/DaVinciResolve.desktop" \
		com.blackmagicdesign.resolve.desktop
	newmenu "${app_dir}/share/DaVinciResolveCaptureLogs.desktop" \
		com.blackmagicdesign.resolve-CaptureLogs.desktop
	newmenu "${app_dir}/share/DaVinciControlPanelsSetup.desktop" \
		com.blackmagicdesign.resolve-Panels.desktop
	newmenu "${app_dir}/share/blackmagicraw-player.desktop" \
		com.blackmagicdesign.rawplayer.desktop
	newmenu "${app_dir}/share/blackmagicraw-speedtest.desktop" \
		com.blackmagicdesign.rawspeedtest.desktop

	newicon -s 64 "${iconsrc}/DV_Resolve.png" davinci-resolve.png
	newicon -s 64 "${iconsrc}/DV_Panels.png" davinci-resolve-panels-setup.png

	newicon -s 48 "${iconsrc}/blackmagicraw-player_48x48_apps.png" blackmagicraw-player.png
	newicon -s 48 "${iconsrc}/blackmagicraw-speedtest_48x48_apps.png" blackmagicraw-speedtest.png

	newicon -s 256 "${iconsrc}/blackmagicraw-player_256x256_apps.png" blackmagicraw-player.png
	newicon -s 256 "${iconsrc}/blackmagicraw-speedtest_256x256_apps.png" blackmagicraw-speedtest.png

	# -c mimetypes is load-bearing, not tidiness.  These nine names are the seven
	# MIME types resolve.xml and blackmagicraw.xml declare, with "/" rewritten as
	# "-" per the icon-naming spec, and a mimetype icon is only ever looked up in
	# the mimetypes context.  newicon defaults to apps/, where nothing consults
	# them -- verified on the installed image: without this the files land in
	# hicolor/*/apps/ and no file manager shows an icon for a .braw or .drp.
	# Upstream already names the braw sources "..._mimetypes.png".
	newicon -s 64 -c mimetypes "${iconsrc}/DV_ResolveBin.png" application-x-resolvebin.png
	newicon -s 64 -c mimetypes "${iconsrc}/DV_ResolveProj.png" application-x-resolveproj.png
	newicon -s 64 -c mimetypes "${iconsrc}/DV_ResolveTimeline.png" application-x-resolvetimeline.png
	newicon -s 64 -c mimetypes "${iconsrc}/DV_ServerAccess.png" application-x-resolvedbkey.png
	newicon -s 64 -c mimetypes "${iconsrc}/DV_TemplateBundle.png" application-x-resolvetemplatebundle.png

	newicon -s 48 -c mimetypes "${iconsrc}/application-x-braw-clip_48x48_mimetypes.png" \
		application-x-braw-clip.png
	newicon -s 48 -c mimetypes "${iconsrc}/application-x-braw-sidecar_48x48_mimetypes.png" \
		application-x-braw-sidecar.png

	newicon -s 256 -c mimetypes "${iconsrc}/application-x-braw-clip_256x256_mimetypes.png" \
		application-x-braw-clip.png
	newicon -s 256 -c mimetypes "${iconsrc}/application-x-braw-sidecar_256x256_mimetypes.png" \
		application-x-braw-sidecar.png

	insinto /usr/share/mime/packages
	doins "${app_dir}/share/resolve.xml" "${app_dir}/share/blackmagicraw.xml"

	udev_dorules "${app_dir}/share/etc/udev/rules.d/"*.rules

	dodir "/usr/share/licenses/${PN}"
	dosym "${install_dir}/docs/License.html" "/usr/share/licenses/${PN}/License.html"
}

pkg_postinst() {
	xdg_pkg_postinst
	udev_reload

	elog "DaVinci Resolve requires a working OpenCL runtime and GPU driver."
	elog "Install the vendor GPU stack matching your hardware if Resolve cannot detect OpenCL."

	# Measured on 2026-09-19, NVIDIA-only host with four ICDs installed: GPUDetect
	# calls clGetPlatformIDs, the ICD loader dlopen()s EVERY file in
	# /etc/OpenCL/vendors, and the static constructor of libhsa-runtime64.so.1
	# (pulled in by libamdocl64.so) binds std::filesystem::path::_M_split_cmpts()
	# to the copy exported by the bundled libs/libProResRAW.so -- an old statically
	# linked libstdc++ that is already in the global scope.  SIGSEGV, milliseconds
	# in, with no error message of any kind.
	#
	# There is no ebuild-side fix: the rpaths are correct and the colliding symbol
	# comes out of a proprietary blob we cannot relink.  Narrowing the ICD set is
	# the user's call because it is hardware-dependent -- a machine with both an
	# AMD and an NVIDIA GPU genuinely needs both ICDs -- so this is documented
	# rather than wrapped.
	elog
	elog "If Resolve dies with no message right after startup, an unrelated OpenCL"
	elog "ICD is likely crashing inside it. The loader opens every file in"
	elog "/etc/OpenCL/vendors, and a runtime for a GPU you do not have is enough."
	elog "Confirm it by restricting the set to the ICD of your own GPU:"
	elog
	elog "    mkdir -p ~/.config/resolve-icd"
	elog "    cp /etc/OpenCL/vendors/<your-vendor>.icd ~/.config/resolve-icd/"
	elog "    OCL_ICD_VENDORS=~/.config/resolve-icd davinci-resolve"
	elog
}

pkg_postrm() {
	xdg_pkg_postrm
	udev_reload
}