#! /usr/bin/env bash # get usage help this way: # ./gamepack_manager -h : "${CP:=cp -va}" : "${CP_R:=cp -Rva}" : "${GIT:=git}" : "${SVN:=svn}" : "${WGET:=wget}" : "${ECHO:=echo}" : "${MKDIR:=mkdir -v}" : "${MKDIR_P:=mkdir -vp}" : "${RM_R:=rm -vrf}" : "${MV:=mv -v}" : "${TAR:=tar}" : "${UNZIPPER:=unzip}" set -e default_download_dir='build/download' default_install_dir='build' games_dir='games' pack_suffix='Pack' free_license_list='BSD GPL' printRawDB () { cat <<\EOF ####################################################### # # # IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT # # # # Use two whitespaces or more as column separator # # # ####################################################### ####################################################### # Obsolete packs # ####################################################### # Quake2World was renamed as Quetoo. # JediAcademy and JediOutcast gamepacks are unusable. # Other gamepacks have better version available. # JediAcademy proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/JAPack/branches/1.5/ # JediOutcast proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/JK2Pack/trunk/ # Kingpin unknown zip http://download.kingpin.info/kingpin/editing/maps/map_editors/NetRadiant/addon/Kingpinpack.zip # Neverball proprietary zip http://ingar.intranifty.net/files/netradiant/gamepacks/NeverballPack.zip # OpenArena unknown zip http://ingar.intranifty.net/files/netradiant/gamepacks/OpenArenaPack.zip # Quake2World GPL svn svn://jdolan.dyndns.org/quake2world/trunk/gtkradiant # Quake3 proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Q3Pack/trunk/ 29 # Quake proprietary zip http://ingar.intranifty.net/files/netradiant/gamepacks/QuakePack.zip # Tremulous proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/TremulousPack/branches/1.5/ # Tremulous proprietary zip http://ingar.intranifty.net/files/netradiant/gamepacks/TremulousPack.zip # Unvanquished unknown zip http://ingar.intranifty.net/gtkradiant/files/gamepacks/UnvanquishedPack.zip # Warfork GPL zip https://cdn.discordapp.com/attachments/611741789237411850/659512520553267201/netradiant_warfork_gamepack.zip # Warsow GPL svn https://svn.bountysource.com/wswpack/trunk/netradiant/games/WarsowPack/ # Warsow GPL zip http://ingar.intranifty.net/files/netradiant/gamepacks/WarsowPack.zip ####################################################### # Usable packs # ####################################################### AlienArena GPL svn https://svn.code.sf.net/p/alienarena-cc/code/trunk/tools/netradiant_gamepack/AlienArenaPack DarkPlaces GPL svn svn://svn.icculus.org/gtkradiant-gamepacks/DarkPlacesPack/branches/1.5/ Doom3 proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Doom3Pack/branches/1.5/ ET proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/ETPack/branches/1.5/ Heretic2 proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Her2Pack/branches/1.5/ JediAcademy proprietary git https://gitlab.com/netradiant/gamepacks/jediacademy-mapeditor-support.git JediOutcast proprietary git https://gitlab.com/netradiant/gamepacks/jedioutcast-mapeditor-support.git Kingpin unknown git https://gitlab.com/netradiant/gamepacks/kingpin-mapeditor-support.git Neverball proprietary git https://gitlab.com/netradiant/gamepacks/neverball-mapeditor-support.git Nexuiz GPL gitdir git://git.icculus.org/divverent/nexuiz.git misc/netradiant-NexuizPack master OpenArena GPL git https://github.com/NeonKnightOA/oagamepack.git Osirion GPL zip http://ingar.intranifty.net/files/netradiant/gamepacks/OsirionPack.zip Prey proprietary git https://gitlab.com/netradiant/gamepacks/prey-mapeditor-support.git Q3Rally proprietary svn https://svn.code.sf.net/p/q3rallysa/code/tools/radiant-config/radiant15-netradiant/ Quake2 proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Q2Pack/branches/1.5/ Quake3 proprietary git https://gitlab.com/netradiant/gamepacks/quake3-mapeditor-support.git Quake4 proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Q4Pack/branches/1.5/ QuakeLive proprietary git https://gitlab.com/netradiant/gamepacks/quakelive-mapeditor-support.git Quake GPL zip http://ingar.intranifty.net/files/netradiant/gamepacks/Quake1Pack.zip Quetoo GPL svn svn://svn.icculus.org/gtkradiant-gamepacks/QuetooPack/branches/1.5/ SmokinGuns unknown git https://github.com/smokin-guns/smokinguns-mapeditor-support.git SoF2 unknown git https://gitlab.com/netradiant/gamepacks/sof2-mapeditor-support.git STVEF unknown git https://gitlab.com/netradiant/gamepacks/stvef-mapeditor-support.git Tremulous proprietary git https://gitlab.com/netradiant/gamepacks/tremulous-mapeditor-support.git TurtleArena proprietary git https://github.com/Turtle-Arena/turtle-arena-radiant-pack.git UFOAI proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/UFOAIPack/branches/1.5/ Unvanquished BSD git https://github.com/Unvanquished/unvanquished-mapeditor-support.git UrbanTerror unknown git https://gitlab.com/netradiant/gamepacks/urbanterror-mapeditor-support.git Warfork GPL git https://gitlab.com/netradiant/gamepacks/warfork-mapeditor-support.git Warsow GPL git https://github.com/Warsow/NetRadiantPack.git Wolf proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/WolfPack/branches/1.5/ WoP proprietary git https://github.com/PadWorld-Entertainment/wop-mapeditor-support.git Wrath GPL git https://gitlab.com/netradiant/gamepacks/wrath-mapeditor-support.git Xonotic GPL git https://gitlab.com/xonotic/netradiant-xonoticpack.git ZEQ2Lite unknown git https://gitlab.com/netradiant/gamepacks/zeq2lite-mapeditor-support.git EOF } if command -v gsed >/dev/null then SED=gsed elif sed --help >/dev/null 2>&1 then SED=sed else printf 'ERROR: GNU sed is missing\n' >&2 exit 1 fi printRealPath () { if command -v grealpath >/dev/null then grealpath "${1}" elif command -v realpath >/dev/null then realpath "${1}" elif command -v greadlink >/dev/null then # test greadlink first as greadlink has the feature on macos # but readlink only has it on linux, note that it's probably # the same on bsd # note: (g)readlink requires the file to be create first greadlink -f "${1}" elif command -v readlink >/dev/null then # --help and -f options are GNU readlink things if readlink --help >/dev/null 2>&1 then readlink -f "${1}" else if ! python -c "import os; print(os.path.realpath('${1}'))" then printf 'ERROR: GNU realpath or other way to compute real path of a file is missing\n' >&2 exit 1 fi fi fi } sanitizeDB () { ${SED} -e 's/#.*//;s/[ \t][ \t][ \t]*/\t/g;s/^[ \t]*//;s/[ \t]*$//' \ | grep -v '^$' } inList () { [ "$(grep "^${1}$")" = "${1}" ] } printList () { echo "${1}" \ | tr ' ' '\n' \ | grep -v '^$' \ | sort -u } dedupeList () { printList "${1}" \ | tr '\n' ' ' \ | ${SED} -e 's/ $//' } printGamePackDB () { printRawDB \ | sanitizeDB } printLicenseList () { printGamePackDB \ | awk '{ print $2 }' \ | sort -u } printNameList () { printGamePackDB \ | awk '{ print $1 }' \ | sort -u } printNameListByLicense () { local arg_license_list local license_list local license arg_license_list="${1}" license_list='' for license in ${arg_license_list} do case "${license}" in 'none') break ;; 'all') license_list="$(printLicenseList)" break ;; 'free') license_list="${license_list} ${free_license_list}" ;; *) if printLicenseList | inList "${license}" then license_list="${license_list} ${license}" else printError "unknown license: ${license}" fi ;; esac done license_list="$(dedupeList "${license_list}")" for license in ${license_list} do printGamePackDB \ | awk '$2 == "'"${license}"'"' \ | awk '{ print $1 }' done | sort -u } printNameListByName () { local argname_list local name_list local name argname_list="${1}" name_list='' for name in ${argname_list} do case "${name}" in 'none') break ;; 'all') name_list="$(printNameList)" break ;; *) if printNameList | inList "${name}" then name_list="${name_list} ${name}" else printError "unknown name: ${name}" fi ;; esac done name_list="$(dedupeList "${name_list}")" for name in ${name_list} do printGamePackDB \ | awk '$1 == "'"${name}"'"' \ | awk '{ print $1 }' done } printPackLine () { local name name="${1}" printGamePackDB \ | awk '$1 == "'"${name}"'"' } getValue () { local name local key name="${1}" key="${2}" printPackLine "${name}" \ | awk '{ print $'"${key}"' }' } downloadExtraUrls () { if [ -f 'extra-urls.txt' ] then local line while read line do local extra_file="$(echo "${line}" | cut -f1 -d$'\t')" local extra_url="$(echo "${line}" | cut -f2 -d$'\t')" ${WGET} -O "${extra_file}" "${extra_url}" < /dev/null done < 'extra-urls.txt' fi } downloadPack () { local download_dir local name local license local source_type local source_url local pack local reference local subdir local branch download_dir="${1}" name="${2}" license="$(getValue "${name}" 2)" source_type="$(getValue "${name}" 3)" source_url="$(getValue "${name}" 4)" pack="${name}${pack_suffix}" ${MKDIR_P} "${download_dir}" ( cd "${download_dir}" ${ECHO} '' ${ECHO} "Available pack: ${pack}" ${ECHO} " License: ${license}" ${ECHO} " Download via ${source_type} from ${source_url}" ${ECHO} '' if [ -d "${download_dir}/${pack}" ] then ${ECHO} "Updating ${name}…" else ${ECHO} "Downloading ${pack}…" fi case "${source_type}" in 'svn') reference="$(getValue "${name}" 5)" if [ -z "${reference}" ] then reference='HEAD' fi if [ -d "${pack}" ] then if [ -d "${pack}/.git" ] then ( cd "${pack}" ${GIT} svn fetch ) else ${SVN} update -r"${reference}" "${pack}" fi else ${SVN} checkout -r"${reference}" "${source_url}" "${pack}" \ || ${GIT} svn clone "${source_url}" "${pack}" fi ;; 'zip') ${RM_R} 'zipdownload' ${MKDIR} 'zipdownload' ( cd 'zipdownload' ${WGET} "${source_url}" ${UNZIPPER} './'*.zip ) ${RM_R} "${pack}" ${MKDIR} "${pack}" if [ -d 'zipdownload/games' ] then ${MV} 'zipdownload/'* "${pack}/" else ${MV} 'zipdownload/'*'/'* "${pack}/" fi ${RM_R} 'zipdownload' ;; 'gitdir') local subdir="$(getValue "${name}" 5)" local branch="$(getValue "${name}" 6)" ${RM_R} "${pack}" ${GIT} archive --remote="${source_url}" --prefix="${pack}/" "${branch}":"${subdir}" \ | ${TAR} xvf - ;; 'git') if [ -d "${pack}" ] then ( cd "${pack}" ${GIT} pull ) else ${GIT} clone "${source_url}" "${pack}" fi ;; esac if [ -d "${pack}" ] then ( cd "${pack}" downloadExtraUrls ) fi ) } downloadPackList () { local download_dir local name_list download_dir="${1}" name_list="${2}" for name in ${name_list} do if printNameList | inList "${name}" then downloadPack "${download_dir}" "${name}" else printError "unknown name: ${name}" fi done } installPack () { local download_dir local install_dir local name local pack local path local game_file local game_dir download_dir="${1}" install_dir="${2}" name="${3}" pack="${name}${pack_suffix}" ${MKDIR_P} "${install_dir}/${games_dir}" # Some per-game workaround for malformed gamepack case "${name}" in 'Wolf') pack="${pack}/bin" ;; esac # Game packs built with mkeditorpacks if [ -d "${download_dir}/${pack}/build/netradiant" ] then pack="${pack}/build/netradiant" elif [ -d "${download_dir}/${pack}/netradiant" ] # Other known layout then pack="${pack}/netradiant" fi path="${download_dir}/${pack}" for game_file in "${path}/${games_dir}/"*'.game' do if [ x"${game_file}" != x"${path}/"*'.game' ] then ${CP} "${game_file}" "${real_install_dir}/${games_dir}/" fi done for game_dir in "${path}/"*'.game' do if [ x"${game_dir}" != x"${path}/"*'.game' ] then ${CP_R} "${game_dir}" "${real_install_dir}/" fi done } installPackList () { local download_dir local install_dir local name_list download_dir="${1}" install_dir="${2}" name_list="${3}" for name in ${name_list} do if printNameList | inList "${name}" then installPack "${download_dir}" "${install_dir}" "${name}" else printError "unknown name: ${name}" fi done } printError () { printf 'ERROR: %s\n' "${1}" >&2 exit 1 } printHelp () { local tab local prog_name tab="$(printf '\t')" prog_name='gamepack-manager' cat <<-EOF Usage: ${prog_name} [OPTION] [SELECTION ] [ACTION] OPTIONS: ${tab}-dd, --download-dir DIRNAME ${tab}${tab}store downloaded games to DIRNAME (default: ${default_download_dir}) ${tab}-id, --install-dir DIRNAME ${tab}${tab}store installed games to DIRNAME (default: ${default_install_dir}) SELECTIONS: ${tab}-n, --name NAMES… ${tab}${tab}select games by name (default: none) ${tab}${tab}special keyword: all, none ${tab}${tab}available games: $(printNameList | ${SED} -e 's/^/\t\t\t/') ${tab}-l, --license LICENSES… ${tab}${tab}select games by license (default: none) ${tab}${tab}special keyword: free, all, none ${tab}${tab}available licenses: $(printLicenseList | ${SED} -e 's/^/\t\t\t/') ACTIONS: ${tab}-ln, --list-names ${tab}${tab}list all game names ${tab}-ll, --list-licenses ${tab}${tab}list all game licenses ${tab}-ls, --list-selected ${tab}${tab}list selected games ${tab}-d, --download ${tab}${tab}download selected games ${tab}-i, --install ${tab}${tab}install selected games ${tab}-h, --help ${tab}${tab}print this help Examples: ${tab}${prog_name} --license GPL BSD --list-selected ${tab}${prog_name} --license GPL BSD --download --install ${tab}${prog_name} --name all --list-selected ${tab}${prog_name} --name all --download --install EOF exit } option_list='' list_selected='false' list_licenses='false' list_names='false' download_packs='false' install_packs='false' mkdir_download='false' mkdir_install='false' by_license='false' by_name='false' arg_type='' selected_list='' license_list='' name_list='' install_dir='' while ! [ -z "${1}" ] do if printList "${option_list}" | inList "${1}" then printError "option called more than once: ${1}" fi if echo "${@}" | tr ' ' '\n' | inList '--help' then printHelp elif echo "${@}" | tr ' ' '\n' | inList '-h' then printHelp fi case "${1}" in '--list-names'|'-ln') arg_type='' list_names='true' option_list="${option_list} ${1}" ;; '--list-licenses'|'-ll') arg_type='' list_licenses='true' option_list="${option_list} ${1}" ;; '--list-selected'|'-ls') arg_type='' list_selected='true' option_list="${option_list} ${1}" ;; '--download'|'-d') arg_type='' download_packs='true' mkdir_download='true' option_list="${option_list} ${1}" ;; '--install'|'-i') arg_type='' install_packs='true' mkdir_download='true' mkdir_install='true' option_list="${option_list} ${1}" ;; '--license'|'-l') by_license='true' arg_type='pack-license' option_list="${option_list} ${1}" ;; '--name'|'-n') by_name='true' arg_type='pack-name' option_list="${option_list} ${1}" ;; '--download-dir'|'-dd') arg_type='download-dir' option_list="${option_list} ${1}" ;; '--install-dir'|'-id') arg_type='install-dir' option_list="${option_list} ${1}" ;; '-'*) printError "unknown option: ${1}" ;; *) case "${arg_type}" in 'pack-name') name_list="${name_list} ${1}" ;; 'pack-license') license_list="${license_list} ${1}" ;; 'download-dir') if [ -z "${download_dir}" ] then download_dir="${1}" else printError "more than one download dir: ${1}" fi ;; 'install-dir') if [ -z "${install_dir}" ] then install_dir="${1}" else printError "more than one install dir: ${1}" fi ;; *) printError "misplaced argument: ${1}" ;; esac ;; esac shift done # compatibility with legacy Makefile if [ "${DOWNLOAD_GAMEPACKS}" = 'yes' ] then if ! [ -z "${DOWNLOADDIR}" ] then download_dir="${DOWNLOADDIR}" fi if ! [ -z "${INSTALLDIR}" ] then install_dir="${INSTALLDIR}" fi license_list='free' by_license='true' download_packs='true' mkdir_download='true' install_packs='true' mkdir_install='true' fi if [ -z "${download_dir}" ] then download_dir="${default_download_dir}" fi if [ -z "${install_dir}" ] then install_dir="${default_install_dir}" fi if "${by_name}" then selected_list="${selected_list} $(printNameListByName "${name_list}")" fi if "${by_license}" then selected_list="${selected_list} $(printNameListByLicense "${license_list}")" fi selected_list="$(dedupeList "${selected_list}")" if "${mkdir_download}" then ${MKDIR_P} "${download_dir}" real_download_dir="$(printRealPath "${download_dir}")" fi if "${mkdir_install}" then ${MKDIR_P} "${install_dir}" real_install_dir="$(printRealPath "${install_dir}")" fi if "${list_names}" then printNameList fi if "${list_licenses}" then printLicenseList fi if "${list_selected}" then printList "${selected_list}" fi if "${download_packs}" then downloadPackList "${real_download_dir}" "${selected_list}" fi if "${install_packs}" then installPackList "${real_download_dir}" "${real_install_dir}" "${selected_list}" fi #EOF