]> git.xonotic.org Git - xonotic/netradiant.git/blob - gamepack-manager
gamepacks: avoid readlink for useless things
[xonotic/netradiant.git] / gamepack-manager
1 #! /usr/bin/env bash
2
3 # get usage help this way:
4 # ./gamepack_manager -h
5
6 : "${CP:=cp -va}"
7 : "${CP_R:=cp -Rva}"
8 : "${GIT:=git}"
9 : "${SVN:=svn}"
10 : "${WGET:=wget}"
11 : "${ECHO:=echo}"
12 : "${MKDIR:=mkdir -v}"
13 : "${MKDIR_P:=mkdir -vp}"
14 : "${RM_R:=rm -vrf}"
15 : "${MV:=mv -v}"
16 : "${TAR:=tar}"
17 : "${UNZIPPER:=unzip}"
18
19 set -e
20
21 default_download_dir='build/download'
22 default_install_dir='build'
23
24 games_dir='games'
25 pack_suffix='Pack'
26
27 free_license_list='BSD GPL'
28
29 printRawDB () {
30 cat <<\EOF
31 #######################################################
32 #                                                     #
33 #  IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT  #
34 #                                                     #
35 #   Use two whitespaces or more as column separator   #
36 #                                                     #
37 #######################################################
38
39 #######################################################
40 # Obsolete packs                                      #
41 #######################################################
42
43 # Quake2World was renamed as Quetoo
44 # Other gamepacks have better version available
45
46 # OpenArena     unknown      zip     http://ingar.intranifty.net/files/netradiant/gamepacks/OpenArenaPack.zip
47 # Quake         proprietary  zip     http://ingar.intranifty.net/files/netradiant/gamepacks/QuakePack.zip
48 # Quake2World   GPL          svn     svn://jdolan.dyndns.org/quake2world/trunk/gtkradiant
49 # Tremulous     proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/TremulousPack/branches/1.5/
50 # Unvanquished  unknown      zip     http://ingar.intranifty.net/gtkradiant/files/gamepacks/UnvanquishedPack.zip
51 # Warsow        GPL          svn     https://svn.bountysource.com/wswpack/trunk/netradiant/games/WarsowPack/
52 # Warsow        GPL          zip     http://ingar.intranifty.net/files/netradiant/gamepacks/WarsowPack.zip
53
54 #######################################################
55 # Usable packs                                        #
56 #######################################################
57
58 DarkPlaces      GPL          svn     svn://svn.icculus.org/gtkradiant-gamepacks/DarkPlacesPack/branches/1.5/
59 Doom3           proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/Doom3Pack/branches/1.5/
60 ET              proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/ETPack/branches/1.5/
61 Heretic2        proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/Her2Pack/branches/1.5/
62 JediAcademy     proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/JAPack/branches/1.5/
63 Neverball       proprietary  zip     http://ingar.intranifty.net/files/netradiant/gamepacks/NeverballPack.zip
64 Nexuiz          GPL          gitdir  git://git.icculus.org/divverent/nexuiz.git misc/netradiant-NexuizPack master
65 OpenArena       GPL          git     https://github.com/NeonKnightOA/oagamepack.git
66 Osirion         GPL          zip     http://ingar.intranifty.net/files/netradiant/gamepacks/OsirionPack.zip
67 Prey            proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/PreyPack/trunk/
68 Q3              proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/Q3Pack/trunk/ 29
69 Q3Rally         proprietary  svn     https://svn.code.sf.net/p/q3rallysa/code/tools/radiant-config/radiant15-netradiant/
70 Quake2          proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/Q2Pack/branches/1.5/
71 Quake4          proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/Q4Pack/branches/1.5/
72 Quake           GPL          zip     http://ingar.intranifty.net/files/netradiant/gamepacks/Quake1Pack.zip
73 Quetoo          GPL          svn     svn://svn.icculus.org/gtkradiant-gamepacks/QuetooPack/branches/1.5/
74 Tremulous       proprietary  zip     http://ingar.intranifty.net/files/netradiant/gamepacks/TremulousPack.zip
75 TurtleArena     proprietary  git     https://github.com/Turtle-Arena/turtle-arena-radiant-pack.git
76 UFOAI           proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/UFOAIPack/branches/1.5/
77 Unvanquished    BSD          git     https://github.com/Unvanquished/unvanquished-mapeditor-support.git
78 Warsow          GPL          git     https://github.com/Warsow/NetRadiantPack.git
79 Wolf            proprietary  svn     svn://svn.icculus.org/gtkradiant-gamepacks/WolfPack/branches/1.5/
80 Xonotic         GPL          git     https://gitlab.com/xonotic/netradiant-xonoticpack.git
81 EOF
82 }
83
84 if command -v gsed >/dev/null
85 then
86         SED=gsed
87 elif sed --help >/dev/null 2>&1
88 then
89         SED=sed
90 else
91         printf 'ERROR: GNU sed is missing\n' >&2
92         exit 1
93 fi
94
95 printRealPath ()
96 {
97         if command -v grealpath >/dev/null
98         then
99                 grealpath "${1}"
100         elif command -v realpath >/dev/null
101         then
102                 realpath "${1}"
103         elif command -v greadlink >/dev/null
104         then
105                 # test greadlink first as greadlink has the feature on macos
106                 # but readlink only has it on linux, note that it's probably
107                 # the same on bsd
108                 # note: (g)readlink requires the file to be create first
109                 greadlink -f "${1}"
110         elif command -v readlink >/dev/null
111         then
112                 # --help and -f options are GNU readlink things
113                 if readlink --help >/dev/null 2>&1
114                 then
115                         readlink -f "${1}"
116                 else
117                         if ! python -c "import os; print(os.path.realpath('${1}'))"
118                         then
119                                 printf 'ERROR: GNU realpath or other way to compute real path of a file is missing\n' >&2
120                                 exit 1
121                         fi
122                 fi
123         fi
124 }
125
126 sanitizeDB () {
127         ${SED} -e 's/#.*//;s/[ \t][ \t][ \t]*/\t/g;s/^[ \t]*//;s/[ \t]*$//' \
128         | grep -v '^$'
129 }
130
131 inList () {
132         [ "$(grep "^${1}$")" = "${1}" ]
133 }
134
135 printList () {
136         echo "${1}" \
137         | tr ' ' '\n' \
138         | grep -v '^$' \
139         | sort -u
140 }
141
142 dedupeList () {
143         printList "${1}" \
144         | tr '\n' ' ' \
145         | ${SED} -e 's/ $//'
146 }
147
148 printGamePackDB () {
149         printRawDB \
150         | sanitizeDB
151 }
152
153 printLicenseList () {
154         printGamePackDB \
155         | awk '{ print $2 }' \
156         | sort -u
157 }
158
159 printNameList () {
160         printGamePackDB \
161         | awk '{ print $1 }' \
162         | sort -u
163 }
164
165 printNameListByLicense () {
166         local arg_license_list
167         local license_list
168         local license
169
170         arg_license_list="${1}"
171         license_list=''
172
173         for license in ${arg_license_list}
174         do
175                 case "${license}" in
176                         'none')
177                                 break
178                                 ;;
179                         'all')
180                                 license_list="$(printLicenseList)"
181                                 break
182                                 ;;
183                         'free')
184                                 license_list="${license_list} ${free_license_list}"
185                                 ;;
186                         *)
187                                 if printLicenseList | inList "${license}"
188                                 then
189                                         license_list="${license_list} ${license}"
190                                 else
191                                         printError "unknown license: ${license}"
192                                 fi
193                                 ;;
194                 esac
195         done
196
197         license_list="$(dedupeList "${license_list}")"
198
199         for license in ${license_list}
200         do
201                 printGamePackDB \
202                 | awk '$2 == "'"${license}"'"' \
203                 | awk '{ print $1 }'
204         done
205 }
206
207 printNameListByName () {
208         local argname_list
209         local name_list
210         local name
211
212         argname_list="${1}"
213         name_list=''
214
215         for name in ${argname_list}
216         do
217                 case "${name}" in
218                         'none')
219                                 break
220                                 ;;
221                         'all')
222                                 local name_list
223                                 name_list="$(printNameList)"
224                                 break
225                                 ;;
226                         *)
227                                 if printNameList | inList "${name}"
228                                 then
229                                         local name_list
230                                         name_list="${name_list} ${name}"
231                                 else
232                                         printError "unknown name: ${name}"
233                                 fi
234                                 ;;
235                 esac
236         done
237
238         name_list="$(dedupeList "${name_list}")"
239
240         for name in ${name_list}
241         do
242                 printGamePackDB \
243                 | awk '$1 == "'"${name}"'"' \
244                 | awk '{ print $1 }'
245         done
246 }
247
248 printPackLine () {
249         local name
250
251         name="${1}"
252
253         printGamePackDB \
254         | awk '$1 == "'"${name}"'"'
255 }
256
257 getValue () {
258         local name
259         local key
260
261         name="${1}"
262         key="${2}"
263
264         printPackLine "${name}" \
265         | awk '{ print $'"${key}"' }'
266 }
267
268 downloadExtraUrls ()
269 {
270         if [ -f 'extra-urls.txt' ]
271         then
272                 while IFS='     ' read -r extra_file extra_url
273                 do
274                         (
275                                 ${WGET} -O "${extra_file}" "${extra_url}"
276                         ) </dev/null
277                 done < 'extra-urls.txt'
278         fi
279 }
280
281 downloadPack () {
282         local download_dir
283         local name
284         local license
285         local source_type
286         local source_url
287         local pack
288         local reference
289         local subdir
290         local branch
291
292         download_dir="${1}"
293         name="${2}"
294
295         license="$(getValue "${name}" '2')"
296         source_type="$(getValue "${name}" '3')"
297         source_url="$(getValue "${name}" '4')"
298
299         pack="${name}${pack_suffix}"
300
301         ${MKDIR_P} "${download_dir}"
302
303         (
304                 cd "${download_dir}"
305
306                 ${ECHO} ''
307                 ${ECHO} "Available pack: ${pack}"
308                 ${ECHO} "  License: ${license}"
309                 ${ECHO} "  Download via ${source_type} from ${source_url}"
310                 ${ECHO} ''
311
312                 if [ -d "${download_dir}/${pack}" ]
313                 then
314                         ${ECHO} "Updating ${name}…"
315                 else
316                         ${ECHO} "Downloading ${pack}…"
317                 fi
318
319                 case "${source_type}" in
320                         'svn')
321                                 reference="$(getValue "${name}" '5')"
322                                 if [ -z "${reference}" ]
323                                 then
324                                         reference='HEAD'
325                                 fi
326
327                                 if [ -d "${pack}" ]
328                                 then
329                                         if [ -d "${pack}/.git" ]
330                                         then
331                                                 (
332                                                         cd "${pack}"
333                                                         ${GIT} svn fetch
334                                                 )
335                                         else
336                                                 ${SVN} update -r"${reference}" "${pack}"
337                                         fi
338                                 else
339                                         ${SVN} checkout -r"${reference}" "${source_url}" "${pack}" \
340                                         || ${GIT} svn clone "${source_url}" "${pack}"
341                                 fi
342                                 ;;
343                         'zip')
344                                 ${RM_R} 'zipdownload'
345                                 ${MKDIR} 'zipdownload'
346                                 (
347                                         cd 'zipdownload'
348                                         ${WGET} "${source_url}"
349                                         ${UNZIPPER} './'*.zip
350                                 )
351                                 ${RM_R} "${pack}"
352                                 ${MKDIR} "${pack}"
353                                 ${MV} 'zipdownload/'*'/'* "${pack}/"
354                                 ${RM_R} 'zipdownload'
355                                 ;;
356                         'gitdir')
357                                 local subdir="$(getValue "${name}" '5')"
358                                 local branch="$(getValue "${name}" '6')"
359                                 ${RM_R} "${pack}"
360                                 ${GIT} archive --remote="${source_url}" --prefix="${pack}/" "${branch}":"${subdir}" \
361                                 | ${TAR} xvf -
362                                 ;;
363                         'git')
364                                 if [ -d "${pack}" ]
365                                 then
366                                         (
367                                                 cd "${pack}"
368                                                 ${GIT} pull
369                                         )
370                                 else
371                                         ${GIT} clone "${source_url}" "${pack}"
372                                 fi
373                                 ;;
374                 esac
375
376                 if [ -d "${pack}" ]
377                 then
378                         (
379                                 cd "${pack}"
380                                 downloadExtraUrls
381                         )
382                 fi
383
384         )
385 }
386
387 downloadPackList () {
388         local download_dir
389         local name_list
390
391         download_dir="${1}"
392         name_list="${2}"
393
394         for name in ${name_list}
395         do
396                 if printNameList | inList "${name}"
397                 then
398                         downloadPack "${download_dir}" "${name}"
399                 else
400                         printError "unknown name: ${name}"
401                 fi
402         done
403 }
404
405 installPack () {
406         local download_dir
407         local install_dir
408         local name
409         local pack
410         local path
411         local game_file
412         local game_dir
413
414         download_dir="${1}"
415         install_dir="${2}"
416         name="${3}"
417
418         pack="${name}${pack_suffix}"
419
420         ${MKDIR_P} "${install_dir}/${games_dir}"
421
422         # Some per-game workaround for malformed gamepack
423         case "${name}" in
424                 'JediAcademy')
425                         pack="${pack}/Tools"
426                         ;;
427                 'Prey'|'Q3')
428                         pack="${pack}/tools"
429                         ;;
430                 'Wolf')
431                         pack="${pack}/bin"
432                         ;;
433         esac
434
435         # mkeditorpacks-based gamepack
436         if [ -d "${download_dir}/${pack}/build/netradiant" ]
437         then
438                 pack="${pack}/build/netradiant"
439         fi
440
441         path="${download_dir}/${pack}"
442
443         for game_file in "${path}/${games_dir}/"*'.game'
444         do
445                 if [ x"${game_file}" != x"${path}/"*'.game' ]
446                 then
447                         ${CP} "${game_file}" "${real_install_dir}/${games_dir}/"
448                 fi
449         done
450
451         for game_dir in "${path}/"*'.game'
452         do
453                 if [ x"${game_dir}" != x"${path}/"*'.game' ]
454                 then
455                         ${CP_R} "${game_dir}" "${real_install_dir}/"
456                 fi
457         done
458 }
459
460 installPackList () {
461         local download_dir
462         local install_dir
463         local name_list
464
465         download_dir="${1}"
466         install_dir="${2}"
467         name_list="${3}"
468
469         for name in ${name_list}
470         do
471                 if printNameList | inList "${name}"
472                 then
473                         installPack "${download_dir}" "${install_dir}" "${name}"
474                 else
475                         printError "unknown name: ${name}"
476                 fi
477         done
478 }
479
480 printError () {
481         printf 'ERROR: %s\n' "${1}" >&2
482         exit 1
483 }
484
485 printHelp () {
486         local tab
487         local prog_name
488
489         tab="$(printf '\t')"
490         prog_name='gamepack-manager'
491
492         cat <<-EOF
493         Usage: ${prog_name} [OPTION] [SELECTION <ARGUMENTS>] [ACTION]
494
495         OPTIONS:
496         ${tab}-dd, --download-dir DIRNAME
497         ${tab}${tab}store downloaded games to DIRNAME (default: ${default_download_dir})
498
499         ${tab}-id, --install-dir DIRNAME
500         ${tab}${tab}store installed games to DIRNAME (default: ${default_install_dir})
501
502         SELECTIONS:
503         ${tab}-n, --name NAMES…
504         ${tab}${tab}select games by name (default: none)
505         ${tab}${tab}special keyword: all, none
506         ${tab}${tab}available games:
507         $(printNameList | ${SED} -e 's/^/\t\t\t/')
508
509         ${tab}-l, --license LICENSES…
510         ${tab}${tab}select games by license (default: none)
511         ${tab}${tab}special keyword: free, all, none
512         ${tab}${tab}available licenses:
513         $(printLicenseList | ${SED} -e 's/^/\t\t\t/')
514
515         ACTIONS:
516         ${tab}-ln, --list-names
517         ${tab}${tab}list all game names
518
519         ${tab}-ll, --list-licenses
520         ${tab}${tab}list all game licenses
521
522         ${tab}-ls, --list
523         ${tab}${tab}list selected games
524
525         ${tab}-d, --download
526         ${tab}${tab}download selected games
527
528         ${tab}-i, --install
529         ${tab}${tab}install selected games
530
531         ${tab}-h, --help
532         ${tab}${tab}print this help
533
534         Example:
535         ${tab}${prog_name} --license GPL BSD --download --install
536
537         EOF
538
539         exit
540 }
541
542 option_list=''
543
544 list_selected='false'
545 list_licenses='false'
546 list_names='false'
547
548 download_packs='false'
549 install_packs='false'
550
551 mkdir_download='false'
552 mkdir_install='false'
553
554 by_license='false'
555 by_name='false'
556
557 arg_type=''
558 selected_list=''
559 license_list=''
560 name_list=''
561 install_dir=''
562
563 while ! [ -z "${1}" ]
564 do
565
566         if printList "${option_list}" | inList "${1}"
567         then
568                 printError "option called more than once: ${1}"
569         fi
570
571         if echo "${@}" | tr ' ' '\n' | inList '--help'
572         then
573                 printHelp
574         elif echo "${@}" | tr ' ' '\n' | inList '-h'
575         then
576                 printHelp
577         fi
578
579         case "${1}" in
580                 '--list-licenses'|'-ll')
581                         arg_type=''
582                         list_licenses='true'
583                         option_list="${option_list} ${1}"
584                         ;;
585                 '--list-names'|'-ln')
586                         arg_type=''
587                         list_names='true'
588                         option_list="${option_list} ${1}"
589                         ;;
590                 '--list-selected'|'-ls')
591                         arg_type=''
592                         list_selected='true'
593                         option_list="${option_list} ${1}"
594                         ;;
595                 '--download'|'-d')
596                         arg_type=''
597                         download_packs='true'
598                         mkdir_download='true'
599                         option_list="${option_list} ${1}"
600                         ;;
601                 '--install'|'-i')
602                         arg_type=''
603                         install_packs='true'
604                         mkdir_download='true'
605                         mkdir_install='true'
606                         option_list="${option_list} ${1}"
607                         ;;
608                 '--license'|'-l')
609                         by_license='true'
610                         arg_type='pack-license'
611                         option_list="${option_list} ${1}"
612                         ;;
613                 '--name'|'-n')
614                         by_name='true'
615                         arg_type='pack-name'
616                         option_list="${option_list} ${1}"
617                         ;;
618                 '--download-dir'|'-dd')
619                         arg_type='download-dir'
620                         option_list="${option_list} ${1}"
621                         ;;
622                 '--install-dir'|'-id')
623                         arg_type='install-dir'
624                         option_list="${option_list} ${1}"
625                         ;;
626                 '-'*)
627                         printError "unknown option: ${1}"
628                         ;;
629                 *)
630                         case "${arg_type}" in
631                                 'pack-license')
632                                         license_list="${license_list} ${1}"
633                                         ;;
634                                 'pack-name')
635                                         name_list="${name_list} ${1}"
636                                         ;;
637                                 'download-dir')
638                                         if [ -z "${download_dir}" ]
639                                         then
640                                                 download_dir="${1}"
641                                         else
642                                                 printError "more than one download dir: ${1}"
643                                         fi
644                                         ;;
645                                 'install-dir')
646                                         if [ -z "${install_dir}" ]
647                                         then
648                                                 install_dir="${1}"
649                                         else
650                                                 printError "more than one install dir: ${1}"
651                                         fi
652                                         ;;
653                                 *)
654                                         printError "misplaced argument: ${1}"
655                                         ;;
656                         esac
657                         ;;
658         esac
659
660         shift
661 done
662
663 # compatibility with legacy Makefile
664 if [ "${DOWNLOAD_GAMEPACKS}" = 'yes' ]
665 then
666         if ! [ -z "${DOWNLOADDIR}" ]
667         then
668                 download_dir="${DOWNLOADDIR}"
669         fi
670
671         if ! [ -z "${INSTALLDIR}" ]
672         then
673                 install_dir="${INSTALLDIR}"
674         fi
675
676         license_list='free'
677         by_license='true'
678
679         download_packs='true'
680         mkdir_download='true'
681
682         install_packs='true'
683         mkdir_install='true'
684 fi
685
686 if [ -z "${download_dir}" ]
687 then
688         download_dir="${default_download_dir}"
689 fi
690
691 if [ -z "${install_dir}" ]
692 then
693         install_dir="${default_install_dir}"
694 fi
695
696 if "${by_license}"
697 then
698         selected_list="${selected_list} $(printNameListByLicense "${license_list}")"
699 fi
700
701 if "${by_name}"
702 then
703         selected_list="${selected_list} $(printNameListByName "${name_list}")"
704 fi
705
706 selected_list="$(dedupeList "${selected_list}")"
707
708 if "${mkdir_download}"
709 then
710         ${MKDIR_P} "${download_dir}"
711         real_download_dir="$(printRealPath "${download_dir}")"
712 fi
713
714 if "${mkdir_install}"
715 then
716         ${MKDIR_P} "${install_dir}"
717         real_install_dir="$(printRealPath "${install_dir}")"
718 fi
719
720 if "${list_licenses}"
721 then
722         printLicenseList
723 fi
724
725 if "${list_names}"
726 then
727         printNameList
728 fi
729 if "${list_selected}"
730 then
731         printList "${selected_list}"
732 fi
733
734 if "${download_packs}"
735 then
736         downloadPackList "${real_download_dir}" "${selected_list}"
737 fi
738
739 if "${install_packs}"
740 then
741         installPackList "${real_download_dir}" "${real_install_dir}" "${selected_list}"
742 fi
743
744 #EOF