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