Commit 2b4ec849 authored by Mario's avatar Mario

Merge branch 'gamepack-manager' into 'master'

new gamepack-manager script to download and install game packs

See merge request xonotic/netradiant!98
parents 2e64b0b9 d8d8fd49
......@@ -251,25 +251,16 @@ endif ()
#-----------------------------------------------------------------------
option(DOWNLOAD_GAMEPACKS "Download game packs" ON)
add_custom_target(game_packs_free
COMMAND ${CMAKE_COMMAND} -E make_directory games
COMMAND DOWNLOAD_GAMEPACKS=yes SOURCE_DIR="${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/install-gamepacks.sh" "${PROJECT_BINARY_DIR}"
COMMENT "Downloading free game packs"
)
add_custom_target(game_packs_all
COMMAND ${CMAKE_COMMAND} -E make_directory games
COMMAND DOWNLOAD_GAMEPACKS=all SOURCE_DIR="${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/install-gamepacks.sh" "${PROJECT_BINARY_DIR}"
COMMENT "Downloading all game packs"
)
set(GAMEPACKS_LICENSE_LIST free CACHE STRING "Download game packs by license")
set(GAMEPACKS_NAME_LIST none CACHE STRING "Download game packs by name")
if (DOWNLOAD_GAMEPACKS)
set(DOWNLOAD_GAMEPACKS "all")
add_custom_target(game_packs ALL
COMMAND "${PROJECT_SOURCE_DIR}/gamepack-manager" --license ${GAMEPACKS_LICENSE_LIST} --name ${GAMEPACKS_NAME_LIST} --download-dir "${PROJECT_BINARY_DIR}/download" --install-dir "${PROJECT_BINARY_DIR}" --download --install
COMMENT "Downloading ${GAMEPACKS_LICENSE_LIST} game packs"
)
endif()
if ("${DOWNLOAD_GAMEPACKS}" STREQUAL "free")
add_custom_target(game_packs_go ALL DEPENDS game_packs_free)
elseif ("${DOWNLOAD_GAMEPACKS}" STREQUAL "all")
add_custom_target(game_packs_go ALL DEPENDS game_packs_all)
endif ()
#-----------------------------------------------------------------------
# Install
......@@ -290,35 +281,7 @@ install(
DESTINATION .
)
set(GAME_FILES
DarkPlaces
Nexuiz
OpenArena
Osirion
Q3
Quake2
Quake
Quetoo
Tremulous
UFOAI
Unvanquished
Warsow
Xonotic
)
unset(_tmp)
foreach (it ${GAME_FILES})
set(dir "${PROJECT_BINARY_DIR}/games/${it}Pack")
string(TOLOWER "${it}" it)
list(APPEND _tmp "${dir}/games")
list(APPEND _tmp "${dir}/${it}.game")
endforeach ()
set(GAME_FILES ${_tmp})
install(
DIRECTORY
${GAME_FILES}
DESTINATION .
OPTIONAL
install(CODE "execute_process(COMMAND \"${PROJECT_SOURCE_DIR}/gamepack-manager\" --license ${GAMEPACKS_LICENSE_LIST} --name ${GAMEPACKS_NAME_LIST} --download-dir \"${PROJECT_BINARY_DIR}/download\" --install-dir \"${CMAKE_INSTALL_PREFIX}\" --install)"
)
include(cmake/scripts/package.cmake)
......@@ -18,6 +18,7 @@ RADIANT_ABOUTMSG ?= Custom build
# warning: this directory may NOT contain any files other than the ones written by this Makefile!
# NEVER SET THIS TO A SYSTEM WIDE "bin" DIRECTORY!
INSTALLDIR ?= install
DOWNLOADDIR ?= build/download
CC ?= gcc
CXX ?= g++
......@@ -36,7 +37,7 @@ ECHO_NOLF ?= echo -n
CAT ?= cat
MKDIR ?= mkdir -p
CP ?= cp
CP_R ?= $(CP) -r
CP_R ?= $(CP) -r --preserve=timestamps
LN ?= ln
LN_SNF ?= $(LN) -snf
RM ?= rm
......@@ -448,6 +449,7 @@ binaries-q3map2: \
.PHONY: clean
clean:
$(RM_R) $(INSTALLDIR_BASE)/
$(RM_R) $(DOWNLOADDIR)/
$(FIND) . \( -name \*.o -o -name \*.d -o -name \*.$(DLL) -o -name \*.$(A) -o -name \*.$(EXE) \) -exec $(RM) {} \;
$(RM) icons/*.rc
......@@ -1043,15 +1045,13 @@ $(INSTALLDIR)/heretic2/h2data.$(EXE): \
.PHONY: install-data
install-data: binaries
$(MKDIR) $(INSTALLDIR)/games
$(FIND) $(INSTALLDIR_BASE)/ -name .svn -exec $(RM_R) {} \; -prune
DOWNLOAD_GAMEPACKS="$(DOWNLOAD_GAMEPACKS)" GIT="$(GIT)" SVN="$(SVN)" WGET="$(WGET)" RM_R="$(RM_R)" MV="$(MV)" UNZIPPER="$(UNZIPPER)" ECHO="$(ECHO)" SH="$(SH)" CP="$(CP)" CP_R="$(CP_R)" $(SH) install-gamepacks.sh "$(INSTALLDIR)"
DOWNLOAD_GAMEPACKS="$(DOWNLOAD_GAMEPACKS)" DOWNLOADDIR="$(DOWNLOADDIR)" INSTALLDIR="$(INSTALLDIR)" GIT="$(GIT)" SVN="$(SVN)" WGET="$(WGET)" RM_R="$(RM_R)" MV="$(MV)" UNZIPPER="$(UNZIPPER)" ECHO="$(ECHO)" SH="$(SH)" CP="$(CP)" CP_R="$(CP_R)" $(SH) gamepack-manager
$(ECHO) $(RADIANT_MAJOR_VERSION) > $(INSTALLDIR)/RADIANT_MAJOR
$(ECHO) $(RADIANT_MINOR_VERSION) > $(INSTALLDIR)/RADIANT_MINOR
$(ECHO) $(RADIANT_PATCH_VERSION) > $(INSTALLDIR)/RADIANT_PATCH
$(CP_R) setup/data/tools/* $(INSTALLDIR)/
$(MKDIR) $(INSTALLDIR)/docs
$(CP_R) docs/* $(INSTALLDIR)/docs/
$(FIND) $(INSTALLDIR_BASE)/ -name .svn -exec $(RM_R) {} \; -prune
.PHONY: install-dll
ifeq ($(OS),Win32)
......
#!/bin/sh
# Usage:
# sh download-gamepack.sh
# LICENSEFILTER=GPL,BSD BATCH=1 sh download-gamepack.sh
: ${GIT:=git}
: ${SVN:=svn}
: ${WGET:=wget}
: ${ECHO:=echo}
: ${MKDIR:=mkdir}
: ${RM_R:=rm -f -r}
: ${MV:=mv}
: ${UNZIPPER:=unzip}
set -e
extra_urls()
{
if [ -f "$1/extra-urls.txt" ]; then
while IFS=" " read -r FILE URL; do
$WGET -O "$1/$FILE" "$URL"
done < "$1/extra-urls.txt"
fi
}
pack()
{
pack=$1; shift
license=$1; shift
sourcetype=$1; shift
source=$1; shift
if [ -d "games/$pack" ]; then
$ECHO "Updating $pack..."
case "$sourcetype" in
svn)
$SVN update "games/$pack" "$@" || true
;;
zip1)
$RM_R zipdownload
$MKDIR zipdownload
cd zipdownload
$WGET "$source" "$@" || true
$UNZIPPER *.zip || true
cd ..
$RM_R "games/$pack"
$MKDIR "games/$pack"
$MV zipdownload/*/* "games/$pack/" || true
$RM_R zipdownload
;;
gitdir)
$RM_R "games/$pack"
cd games
$GIT archive --remote="$source" --prefix="$pack/" "$2":"$1" | tar xvf - || true
cd ..
;;
git)
cd "games/$pack"
$GIT pull || true
cd ../..
;;
esac
extra_urls "games/$pack"
return
fi
$ECHO
$ECHO "Available pack: $pack"
$ECHO " License: $license"
$ECHO " Download via $sourcetype from $source"
$ECHO
case " $PACKFILTER " in
" ")
;;
*" $pack "*)
;;
*)
$ECHO "Pack $pack rejected because it is not in PACKFILTER."
return
;;
esac
case " $LICENSEFILTER " in
" ")
;;
*)
if ! echo "$LICENSEFILTER" | tr ',' '\n' | grep -F -q -x "$license"
then
$ECHO "Pack $pack rejected because its license is not in LICENSEFILTER."
return
fi
;;
esac
case "$BATCH" in
'')
while :; do
$ECHO "Download this pack? (y/n)"
read -r P
case "$P" in
y*)
break
;;
n*)
return
;;
esac
done
;;
*)
;;
esac
$ECHO "Downloading $pack..."
case "$sourcetype" in
svn)
$SVN checkout "$source" "games/$pack" "$@" || true
;;
zip1)
$RM_R zipdownload
$MKDIR zipdownload
cd zipdownload
$WGET "$source" "$@" || true
$UNZIPPER *.zip || true
cd ..
$MKDIR "games/$pack"
$MV zipdownload/*/* "games/$pack/" || true
$RM_R zipdownload
;;
gitdir)
cd games
$GIT archive --remote="$source" --prefix="$pack/" "$2":"$1" | tar xvf - || true
cd ..
;;
git)
cd games
$GIT clone "$source" "$pack" || true
cd ..
;;
esac
extra_urls "games/$pack"
good=false
for D in "games/$pack"/*.game; do
if [ -d "$D" ]; then
good=true
fi
done
$good || rm -rf "$D"
}
mkdir -p games
pack DarkPlacesPack GPL svn svn://svn.icculus.org/gtkradiant-gamepacks/DarkPlacesPack/branches/1.5/
pack Doom3Pack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Doom3Pack/branches/1.5/
pack ETPack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/ETPack/branches/1.5/
pack Heretic2Pack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Her2Pack/branches/1.5/
pack JediAcademyPack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/JAPack/branches/1.5/
pack NeverballPack proprietary zip1 http://ingar.intranifty.net/files/netradiant/gamepacks/NeverballPack.zip
pack NexuizPack GPL gitdir git://git.icculus.org/divverent/nexuiz.git misc/netradiant-NexuizPack master
#pack OpenArenaPack unknown zip1 http://ingar.satgnu.net/files/netradiant/gamepacks/OpenArenaPack.zip
pack OpenArenaPack GPL git https://github.com/NeonKnightOA/oagamepack.git
pack OsirionPack GPL zip1 http://ingar.intranifty.net/files/netradiant/gamepacks/OsirionPack.zip
pack PreyPack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/PreyPack/trunk/
pack Q3Pack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Q3Pack/trunk/ -r29
pack Quake2Pack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Q2Pack/branches/1.5/
pack Quake4Pack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Q4Pack/branches/1.5/
#pack QuakePack proprietary zip1 http://ingar.intranifty.net/files/netradiant/gamepacks/QuakePack.zip
pack QuakePack GPL zip1 http://ingar.intranifty.net/files/netradiant/gamepacks/Quake1Pack.zip
#pack Quake2WorldPack GPL svn svn://jdolan.dyndns.org/quake2world/trunk/gtkradiant
pack QuetooPack GPL svn svn://svn.icculus.org/gtkradiant-gamepacks/QuetooPack/branches/1.5/
#pack TremulousPack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/TremulousPack/branches/1.5/
pack TremulousPack proprietary zip1 http://ingar.intranifty.net/files/netradiant/gamepacks/TremulousPack.zip
pack TurtleArenaPack proprietary git https://github.com/Turtle-Arena/turtle-arena-radiant-pack.git
pack UFOAIPack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/UFOAIPack/branches/1.5/
#pack UnvanquishedPack unknown zip1 http://ingar.intranifty.net/gtkradiant/files/gamepacks/UnvanquishedPack.zip
pack UnvanquishedPack BSD svn https://github.com/Unvanquished/unvanquished-mapeditor-support.git/trunk/build/netradiant
#pack WarsowPack GPL svn https://svn.bountysource.com/wswpack/trunk/netradiant/games/WarsowPack/
#pack WarsowPack GPL zip1 http://ingar.intranifty.net/files/netradiant/gamepacks/WarsowPack.zip
pack WarsowPack GPL git https://github.com/Warsow/NetRadiantPack.git
pack WolfPack proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/WolfPack/branches/1.5/
pack XonoticPack GPL git https://gitlab.com/xonotic/netradiant-xonoticpack.git
#! /usr/bin/env bash
# get usage help this way:
# ./gamepack_manager -h
: "${CP:=cp -v}"
: "${CP_R:=cp -r --preserve=timestamps}"
: "${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
# Other gamepacks have better version available
# OpenArena unknown zip http://ingar.intranifty.net/files/netradiant/gamepacks/OpenArenaPack.zip
# Quake proprietary zip http://ingar.intranifty.net/files/netradiant/gamepacks/QuakePack.zip
# Quake2World GPL svn svn://jdolan.dyndns.org/quake2world/trunk/gtkradiant
# Tremulous proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/TremulousPack/branches/1.5/
# Unvanquished unknown zip http://ingar.intranifty.net/gtkradiant/files/gamepacks/UnvanquishedPack.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 #
#######################################################
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 svn svn://svn.icculus.org/gtkradiant-gamepacks/JAPack/branches/1.5/
Neverball proprietary zip http://ingar.intranifty.net/files/netradiant/gamepacks/NeverballPack.zip
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 svn svn://svn.icculus.org/gtkradiant-gamepacks/PreyPack/trunk/
Q3 proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Q3Pack/trunk/ 29
Quake2 proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Q2Pack/branches/1.5/
Quake4 proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/Q4Pack/branches/1.5/
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/
Tremulous proprietary zip http://ingar.intranifty.net/files/netradiant/gamepacks/TremulousPack.zip
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 svn https://github.com/Unvanquished/unvanquished-mapeditor-support.git/trunk/build/netradiant
Warsow GPL git https://github.com/Warsow/NetRadiantPack.git
Wolf proprietary svn svn://svn.icculus.org/gtkradiant-gamepacks/WolfPack/branches/1.5/
Xonotic GPL git https://gitlab.com/xonotic/netradiant-xonoticpack.git
EOF
}
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
}
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')
local name_list
name_list="$(printNameList)"
break
;;
*)
if printNameList | inList "${name}"
then
local name_list
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 ()
{
local dir
dir="${1}"
if [ -f "${dir}/extra-urls.txt" ]
then
while IFS=' ' read -r FILE URL
do
(
${WGET} -O "${dir}/${FILE}" "${URL}"
) </dev/null
done < "${dir}/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 "${download_dir}/${pack}" ]
then
${SVN} update -r"${reference}" "${pack}"
else
${SVN} checkout -r"${reference}" "${source_url}" "${pack}"
fi
;;
'zip')
${RM_R} 'zipdownload'
${MKDIR} 'zipdownload'
(
cd 'zipdownload'
${WGET} "${source_url}"
${UNZIPPER} './'*.zip
)
${RM_R} "${pack}"
${MKDIR} "${pack}"
${MV} 'zipdownload/'*'/'* "${pack}/"
${RM_R} 'zipdownload'
;;
'gitdir')
subdir="$(getValue "${name}" '5')"
branch="$(getValue "${name}" '6')"
${RM_R} "${pack}"
${GIT} archive --remote="${source_url}" --prefix="${pack}/" "${branch}":"${subdir}" \
| ${TAR} xvf -
;;
'git')
if [ -d "${download_dir}/${pack}" ]
then
(
cd "${pack}"
${GIT} pull
)
else
${GIT} clone "${source_url}" "${pack}"
fi
;;
esac
if [ -d "${download_dir}/${pack}" ]
then
(
cd "${download_dir}/${pack}"
downloadExtraUrls "${pack}"
)
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} "${real_install_dir}/${games_dir}"
# Some per-game workaround for malformed gamepack
case "${name}" in
'JediAcademy')
pack="${pack}/Tools"
;;
'Prey'|'Q3')
pack="${pack}/tools"
;;
'Wolf')
pack="${pack}/bin"
;;
esac
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="$(basename "$(readlink -f "${0}")")"
cat <<-EOF
Usage: ${prog_name} [OPTION] [SELECTION <ARGUMENTS>] [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