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