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