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