stk-mkgp.sh 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #!/bin/bash
  2. usage() {
  3. (( $# > 0 )) && echo -e "$@"
  4. echo "
  5. Creates a Supertuxkart Grand Prix
  6. Without options:
  7. Create a randomized Grand Prix of ALL tracks (both addon and builtin).
  8. Dates and times refer to file timestamps, i.e. when the track
  9. was locally installed, not when it was first published.
  10. Dependencies: xml (xmlstarlet) and coreutils.
  11. -d int Pick tracks from the latest int days
  12. -n int Choose the newest int tracks.
  13. A negative number means oldest int tracks
  14. Options -d and -n refer to the installation date and also includes manually
  15. installed tracks. They also cannot be combined.
  16. -R Do NOT randomize chosen tracks
  17. (undefined result - most likely alphabetical)
  18. -g str A comma-separated list of groups to include.
  19. Defaults to \"$groups\"
  20. -a Pick only addon tracks
  21. -b Pick only bultin tracks
  22. -p str Use a prefix other than $prefix
  23. Options -a and -b cannot be combined.
  24. If not specified, the default is to pick from all tracks."
  25. exit 1
  26. }
  27. is_valid() {
  28. local track="$dir/$1/track.xml"
  29. [ -r "$track" ] \
  30. && ! [[ "$(xml sel -t -v '//@internal' "$track")" == Y ]] \
  31. && ! [[ "$(xml sel -t -v '//@soccer' "$track")" == Y ]] \
  32. && ! [[ "$(xml sel -t -v '//@arena' "$track")" == Y ]]
  33. }
  34. is_latest() {
  35. # $1 is the directory of the track
  36. [[ "$date" == all ]] && return 0
  37. local file_time="$(get_time "$1")"
  38. if ((file_time >= latest_time)); then
  39. echo "$(date -d @$file_time) - $1"
  40. return 0
  41. else
  42. return 1
  43. fi
  44. }
  45. is_groups() {
  46. local track="$dir/$1/track.xml"
  47. for group in ${groups//,/ }; do
  48. [[ "$(xml sel -t -v '//@groups' "$track")" == *"$group"* ]] && return 0
  49. done
  50. return 1
  51. }
  52. get_tracks() {
  53. # $1 is the prefix "addon_", or nothing
  54. cd "$dir" || usage "Cannot cd to $dir"
  55. for i in *; do
  56. is_valid "$i" && is_groups "$i" && is_latest "$i" && grow_array "$i" "$1"
  57. done
  58. }
  59. grow_array() {
  60. # $1 is the directory of the track
  61. # $2 is the prefix "addon_", or nothing
  62. local x i="$2$1"
  63. x="$(xml sel -t -v '//@default-number-of-laps' "$dir/$1/track.xml")" || x=3
  64. i="<track id=\"$i\" laps=\"$x\" reverse=\"false\" />"
  65. ((number!=0)) && array=( "${array[@]}" "$(get_time "$1") $i" ) || array=( "${array[@]}" "$i" )
  66. }
  67. get_time() {
  68. # $1 is the directory of the track
  69. # accumulate all possible file times
  70. local file_times=() file i file_time=0
  71. for file in scene graph quad materials track; do
  72. file="$dir/$1/$file.xml"
  73. # stat gives more choice than 'date -r', 3 different file times in seconds since epoch
  74. [ -r "$file" ] && file_times=( $(stat --printf '%W %Y %Z' "$file") )
  75. done
  76. # we simply choose the _newest_ file time
  77. # this also applies if we use "oldest" logic
  78. for i in ${file_times[@]}; do
  79. (( i > file_time )) && file_time="$i"
  80. done
  81. echo "$file_time"
  82. }
  83. echo_array() {
  84. if ((number!=0)); then
  85. IFS=$'\n'
  86. ((number>0)) && array=($(sort -nr <<<"${array[*]}")) || array=($(sort -n <<<"${array[*]}"))
  87. unset IFS
  88. for ((i=0;i<${number#-};i++)); do
  89. echo "${array[i]#* }"
  90. done
  91. else
  92. for ((i=0;i<${#array[@]};i++)); do
  93. echo "${array[i]}"
  94. done
  95. fi
  96. }
  97. # dependency checks
  98. for dep in xml date; do
  99. type -f $dep >/dev/null || usage
  100. done
  101. # default values
  102. addons=1
  103. builtins=1
  104. date=all
  105. random=1
  106. number=0
  107. oldest=0
  108. prefix=/usr/share
  109. groups=standard,Add-Ons
  110. re='^[+-]?[1-9][0-9]*$'
  111. homedir="$HOME/.local/share/supertuxkart"
  112. [ -w "$homedir/grandprix" ] || usage "Cannot access $homedir/grandprix for writing - does it exist?"
  113. while getopts "d:n:p:Rabg:h" option; do
  114. case "$option" in
  115. d)
  116. [[ "$number" != 0 ]] && usage "You cannot use both -d and -n"
  117. # regex that means positive integer but not 0
  118. [[ "$OPTARG" =~ ^[1-9][0-9]*$ ]] || usage "Not a valid number: $OPTARG"
  119. date="$OPTARG"
  120. latest_time="$(date -d "now - $date days" +%s)"
  121. ;;
  122. n)
  123. [[ "$date" != all ]] && usage "You cannot use both -n and -d"
  124. # regex that means positive or negative integer but not 0
  125. [[ "$OPTARG" =~ ^[+-]?[1-9][0-9]*$ ]] || usage "Not a valid number: $OPTARG"
  126. number="$OPTARG"
  127. ;;
  128. p)
  129. prefix="$OPTARG"
  130. [ -d "$prefix" ] || usage "The prefix $prefix is not a directory on this machine."
  131. ;;
  132. R)
  133. random=0
  134. ;;
  135. a)
  136. builtins=0
  137. [[ "$addons" == 0 ]] && usage "You cannot use both -a and -b"
  138. ;;
  139. b)
  140. addons=0
  141. [[ "$builtins" == 0 ]] && usage "You cannot use both -a and -b"
  142. ;;
  143. g)
  144. groups="$OPTARG"
  145. ;;
  146. h|*)
  147. usage
  148. ;;
  149. esac
  150. done
  151. # optional dependency checks
  152. if [[ "$random" == 1 ]]; then
  153. type -f shuf >/dev/null || usage
  154. fi
  155. if [[ "$date" != all ]]; then
  156. type -f stat >/dev/null || usage
  157. fi
  158. prompt="Creating"
  159. [[ "$random" == 0 ]] && prompt="$prompt non-randomized"
  160. prompt="$prompt GrandPrix with"
  161. if [[ "$number" == 0 ]]; then
  162. [[ "$date" == all ]] && prompt="$prompt tracks from any date" || prompt="$prompt tracks updated during the last $date days"
  163. else
  164. ((number<0)) && prompt="$prompt ${number#-} oldest tracks" || prompt="$prompt $number newest tracks"
  165. fi
  166. [[ "$builtins" == 0 ]] && prompt="$prompt (add-ons only)"
  167. [[ "$addons" == 0 ]] && prompt="$prompt (built-ins only)"
  168. [[ "$groups" != "standard,Add-Ons" ]] && prompt="${prompt}, groups: $groups"
  169. echo "${prompt}."
  170. read -p "Proceed (Enter/Ctrl-c)? "
  171. file="$homedir/grandprix/usr_gp_$(printf "%08d" $RANDOM).grandprix"
  172. [ -e "$file" ] && usage "Randomly chosen filename\n$file\nalready exists - how weird is that!"
  173. array=()
  174. if [[ "$builtins" == 1 ]]; then
  175. dir="$prefix/supertuxkart/data/tracks"
  176. get_tracks
  177. fi
  178. if [[ "$addons" == 1 ]]; then
  179. dir="$homedir/addons/tracks"
  180. get_tracks "addon_"
  181. fi
  182. if (( ${#array[@]} > 0 )); then
  183. name="tracks ($(date '+%b %-d'))"
  184. #~ [[ "$date" != all ]] && name="tracks last ${date}d $name"
  185. [[ "$addons" == 0 ]] && name="built-in $name"
  186. [[ "$builtins" == 0 ]] && name="add-on $name"
  187. #~ [[ "$random" == 1 ]] && name="random $name"
  188. ((number<0)) && name="oldest $name"
  189. ((number!=0)) && name="${number#-} $name" || name="${#array[@]} $name"
  190. start="\n<supertuxkart_grand_prix name=\"$name\">\n"
  191. end="\n</supertuxkart_grand_prix>\n"
  192. {
  193. echo -e "$start"
  194. if [[ "$random" == 0 ]]; then
  195. echo_array
  196. else
  197. echo_array | shuf
  198. fi
  199. echo -e "$end"
  200. } > "$file"
  201. echo -e "\nGenerated GrandPrix \"$name\"\nSaved to ~${file#$HOME}"
  202. else
  203. echo "No tracks, no GrandPrix!"
  204. exit 1
  205. fi