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