Merge branch 'master' of git://git.xonotic.org/xonotic/xonotic
[xonotic/xonotic.git] / misc / tools / cached-converter.sh
1 #!/bin/sh
2
3 set -e
4
5 : ${CACHEDIR:=$HOME/.xonotic-cached-converter}
6 : ${do_jpeg:=true}
7 : ${do_jpeg_if_not_dds:=false}
8 : ${jpeg_qual_rgb:=95}
9 : ${jpeg_qual_a:=99}
10 : ${do_dds:=true}
11 : ${dds_tool:=compressonator-dxtc}
12 : ${do_ogg:=false}
13 : ${ogg_qual:=1}
14 : ${del_src:=false}
15 : ${git_src_repo:=}
16
17 me=$0
18 case "$me" in
19         */*)
20                 meprefix=${me%/*}/
21                 ;;
22         *)
23                 meprefix=
24                 ;;
25 esac
26
27 tmpdir=`mktemp -d -t cached-converter.XXXXXX`
28 trap 'exit 1' INT
29 trap 'rm -rf "$tmpdir"' EXIT
30
31 lastinfiles=
32 lastinfileshash=
33 cached()
34 {
35         flag=$1; shift
36         method=$1; shift
37         infile1=$1; shift
38         infile2=$1; shift
39         outfile1=$1; shift
40         outfile2=$1; shift
41         if ! $flag; then
42                 return 0
43         fi
44         #sleep 0.25
45         if [ x"$infile1" = x"$outfile1" ]; then
46                 keep=true
47         fi
48         options=`echo "$*" | git hash-object --stdin`
49         if [ x"$infile1/../$infile2" = x"$lastinfiles" ]; then
50                 sum=$lastinfileshash
51         else
52                 if [ -n "$git_src_repo" ]; then
53                         sum=`( cd "$git_src_repo"; git rev-parse --revs-only HEAD:"${infile1#./}" | grep . ) || git hash-object "$infile1"`
54                         if [ -n "$infile2" ]; then
55                                 sum=$sum`( cd "$git_src_repo"; git rev-parse --revs-only HEAD:"${infile2#./}" | grep . ) || git hash-object "$infile2"`
56                         fi
57                 else
58                         sum=`git hash-object "$infile1"`
59                         if [ -n "$infile2" ]; then
60                                 sum=$sum`git hash-object "$infile2"`
61                         fi
62                 fi
63                 lastinfileshash=$sum
64         fi
65         mkdir -p "$CACHEDIR/$method-$options"
66         name1="$CACHEDIR/$method-$options/$sum-1.${outfile1##*.}"
67         [ -z "$outfile2" ] || name2="$CACHEDIR/$method-$options/$sum-2.${outfile2##*.}"
68         tempfile1="${name1%/*}/new-${name1##*/}"
69         [ -z "$outfile2" ] || tempfile2="${name2%/*}/new-${name2##*/}"
70         if [ -f "$name1" ] && { [ -z "$outfile2" ] || [ -f "$name2" ]; }; then
71                 case "$outfile1" in */*) mkdir -p "${outfile1%/*}"; esac && { ln "$name1" "$outfile1" 2>/dev/null || cp "$name1" "$outfile1"; }
72                 [ -z "$outfile2" ] || { case "$outfile2" in */*) mkdir -p "${outfile2%/*}"; esac && { ln "$name2" "$outfile2" 2>/dev/null || cp "$name2" "$outfile2"; }; }
73                 conv=true
74         elif "$method" "$infile1" "$infile2" "$tempfile1" "$tempfile2" "$@"; then
75                 mv "$tempfile1" "$name1"
76                 [ -z "$outfile2" ] || mv "$tempfile2" "$name2"
77                 case "$outfile1" in */*) mkdir -p "${outfile1%/*}"; esac && { ln "$name1" "$outfile1" 2>/dev/null || cp "$name1" "$outfile1"; }
78                 [ -z "$outfile2" ] || { case "$outfile2" in */*) mkdir -p "${outfile2%/*}"; esac && { ln "$name2" "$outfile2" 2>/dev/null || cp "$name2" "$outfile2"; }; }
79                 conv=true
80         else
81                 rm -f "$tempfile1"
82                 rm -f "$tempfile2"
83                 exit 1
84         fi
85 }
86
87 reduce_jpeg2_dds()
88 {
89         i=$1; shift
90         ia=$1; shift
91         o=$1; shift; shift 
92         convert "$i" "$ia" -compose CopyOpacity -composite "$tmpdir/x.tga" && \
93         "$meprefix"compress-texture "$dds_tool" dxt5 "$tmpdir/x.tga" "$o" $1
94 }
95
96 reduce_jpeg2_dds_premul()
97 {
98         i=$1; shift
99         ia=$1; shift
100         o=$1; shift; shift 
101         convert "$i" "$ia" -compose CopyOpacity -composite "$tmpdir/x.tga" && \
102         "$meprefix"compress-texture "$dds_tool" dxt4 "$tmpdir/x.tga" "$o" $1
103 }
104
105 reduce_jpeg2_jpeg2()
106 {
107         i=$1; shift
108         ia=$1; shift
109         o=$1; shift
110         oa=$1; shift
111         if convert "$i" TGA:- | cjpeg -targa -quality "$1" -optimize -sample 1x1,1x1,1x1 > "$o"; then
112                 if [ "`stat -c %s "$i"`" -lt "`stat -c %s "$o"`" ]; then
113                         cp "$i" "$o"
114                 fi
115         else
116                 return 1
117         fi
118         if convert "$ia" TGA:- | cjpeg -targa -quality "$2" -optimize -sample 1x1,1x1,1x1 > "$oa"; then
119                 if [ "`stat -c %s "$ia"`" -lt "`stat -c %s "$oa"`" ]; then
120                         cp "$ia" "$oa"
121                 fi
122         else
123                 return 1
124         fi
125 }
126
127 reduce_jpeg_jpeg()
128 {
129         i=$1; shift; shift
130         o=$1; shift; shift
131         if convert "$i" TGA:- | cjpeg -targa -quality "$1" -optimize -sample 1x1,1x1,1x1 > "$o"; then
132                 if [ "`stat -c %s "$i"`" -lt "`stat -c %s "$o"`" ]; then
133                         cp "$i" "$o"
134                 fi
135         else
136                 return 1
137         fi
138 }
139
140 reduce_ogg_ogg()
141 {
142         i=$1; shift; shift
143         o=$1; shift; shift
144         tags=`vorbiscomment -R -l "$i" || true`
145         oggdec -o "$tmpdir/x.wav" "$i" && \
146         oggenc -q"$1" -o "$o" "$tmpdir/x.wav"
147         echo "$tags" | vorbiscomment -R -w "$o" || true
148 }
149
150 reduce_wav_ogg()
151 {
152         i=$1; shift; shift
153         o=$1; shift; shift
154         oggenc -q"$1" -o "$o" "$i"
155 }
156
157 reduce_rgba_dds()
158 {
159         i=$1; shift; shift
160         o=$1; shift; shift
161         convert "$i" "$tmpdir/x.tga" && \
162         "$meprefix"compress-texture "$dds_tool" dxt5 "$tmpdir/x.tga" "$o" $1
163 }
164
165 reduce_rgba_dds_premul()
166 {
167         i=$1; shift; shift
168         o=$1; shift; shift
169         convert "$i" "$tmpdir/x.tga" && \
170         "$meprefix"compress-texture "$dds_tool" dxt4 "$tmpdir/x.tga" "$o" $1
171 }
172
173 reduce_rgba_jpeg2()
174 {
175         i=$1; shift; shift
176         o=$1; shift
177         oa=$1; shift
178         if convert "$i" -alpha off TGA:- | cjpeg -targa -quality "$1" -optimize -sample 1x1,1x1,1x1 > "$o"; then
179                 :
180         else
181                 return 1
182         fi
183         if convert "$i" -alpha extract TGA:- | cjpeg -targa -quality "$2" -optimize -sample 1x1,1x1,1x1 > "$oa"; then
184                 :
185         else
186                 return 1
187         fi
188 }
189
190 reduce_rgb_dds()
191 {
192         i=$1; shift; shift
193         o=$1; shift; shift
194         convert "$i" "$tmpdir/x.tga" && \
195         "$meprefix"compress-texture "$dds_tool" dxt1 "$tmpdir/x.tga" "$o" $1
196 }
197
198 reduce_rgb_jpeg()
199 {
200         i=$1; shift; shift
201         o=$1; shift; shift
202         if convert "$i" TGA:- | cjpeg -targa -quality "$1" -optimize -sample 1x1,1x1,1x1 > "$o"; then
203                 :
204         else
205                 return 1
206         fi
207 }
208
209 has_alpha()
210 {
211         i=$1; shift; shift
212         o=$1; shift; shift
213         if convert "$i" -depth 16 RGBA:- | perl -e 'while(read STDIN, $_, 8) { substr($_, 6, 2) eq "\xFF\xFF" or exit 1; } exit 0;'; then
214                 # no alpha
215                 : > "$o"
216         else
217                 # has alpha
218                 echo yes > "$o"
219         fi
220 }
221
222 to_delete=
223 for F in "$@"; do
224         f=${F%.*}
225
226         echo >&2 "Handling $F..."
227         conv=false
228         keep=false
229         jqual_rgb=$jpeg_qual_rgb
230         jqual_a=$jpeg_qual_a
231
232         will_jpeg=$do_jpeg
233         will_dds=$do_dds
234         case "$f" in
235                 *_bump) will_dds=false ;;
236                 ./models/player/*) will_dds=false ;;
237                 ./models/sprites/*) will_dds=false ;;
238                 ./textures/*) ;;
239                 ./models/*) ;;
240                 ./particles/*) ;;
241                 ./progs/*) ;;
242                 *)
243                         # we can't DDS compress the 2D textures, sorry
244                         # but JPEG is still fine
245                         will_dds=false
246                         ;;
247         esac
248
249         # for deluxemaps, lightmaps and normalmaps, enforce high jpeg quality (like on alpha channels)
250         if [ "$jqual_a" -gt "$jqual_rgb" ]; then
251                 case "$f" in
252                         ./maps/*/lm_[0-9][0-9][0-9][13579]) # deluxemap
253                                 jqual_rgb=$jqual_a
254                                 ;;
255                         ./maps/*/lm_[0-9][0-9][0-9][02468]) # lightmap
256                                 jqual_rgb=$jqual_a
257                                 ;;
258                         *_norm) # normalmap
259                                 jqual_rgb=$jqual_a
260                                 ;;
261                 esac
262         fi
263
264         pm=
265         case "$f" in
266                 ./particles/*) # deluxemap
267                         pm=_premul
268                         ;;
269         esac
270
271         if $do_jpeg_if_not_dds; then
272                 if $will_dds; then
273                         will_jpeg=false
274                 else
275                         will_jpeg=true
276                 fi
277         fi
278
279         case "$F" in
280                 *_alpha.jpg)
281                         # handle in *.jpg case
282
283                         # they always got converted, I assume
284                         if $will_dds || $will_jpeg; then
285                                 conv=true
286                         fi
287                         keep=$will_jpeg
288                         ;;
289                 *.jpg)
290                         if [ -f "${f}_alpha.jpg" ]; then
291                                 cached "$will_dds"  reduce_jpeg2_dds$pm "$F" "${f}_alpha.jpg" "dds/${f}.dds" ""               "$dds_flags"
292                                 cached "$will_jpeg" reduce_jpeg2_jpeg2  "$F" "${f}_alpha.jpg" "$F"           "${f}_alpha.jpg" "$jqual_rgb" "$jqual_a"
293                         else                                   
294                                 cached "$will_dds"  reduce_rgb_dds      "$F" ""               "dds/${f}.dds" ""               "$dds_flags"
295                                 cached "$will_jpeg" reduce_jpeg_jpeg    "$F" ""               "$F"           ""               "$jqual_rgb"
296                         fi
297                         ;;
298                 *.png|*.tga)
299                         cached true has_alpha "$F" "" "$F.hasalpha" ""
300                         conv=false
301                         if [ -s "$F.hasalpha" ]; then
302                                 cached "$will_dds"  reduce_rgba_dds$pm  "$F" ""               "dds/${f}.dds" ""               "$dds_flags"
303                                 cached "$will_jpeg" reduce_rgba_jpeg2   "$F" ""               "${f}.jpg"     "${f}_alpha.jpg" "$jqual_rgb" "$jqual_a"
304                         else                                                             
305                                 cached "$will_dds"  reduce_rgb_dds      "$F" ""               "dds/${f}.dds" ""               "$dds_flags"
306                                 cached "$will_jpeg" reduce_rgb_jpeg     "$F" ""               "${f}.jpg"     ""               "$jqual_rgb"
307                         fi
308                         rm -f "$F.hasalpha"
309                         ;;
310                 *.ogg)
311                         cached "$do_ogg" reduce_ogg_ogg "$F" "" "$F" "" "$ogg_qual"
312                         ;;
313                 ./sound/misc/null.wav)
314                         # never convert this one
315                         ;;
316                 *.wav)
317                         cached "$do_ogg" reduce_wav_ogg "$F" "" "${f}.ogg" "" "$ogg_qual"
318                         ;;
319         esac
320         if $del_src; then
321                 if $conv; then
322                         if ! $keep; then
323                                 # FIXME can't have spaces in filenames that way
324                                 to_delete="$to_delete $F"
325                         fi
326                 fi
327         fi
328         # fix up DDS paths by a symbolic link
329         if [ -f "dds/${f}.dds" ]; then
330                 if [ -z "${f##./textures/*}" ]; then
331                         if [ -n "${f##./textures/*/*}" ]; then
332                                 ln -snf "textures/${f#./textures/}.dds" "dds/${f#./textures/}.dds"
333                         fi
334                 fi
335         fi
336 done
337 for F in $to_delete; do
338         rm -f "$F"
339 done