jpegoptim is hereby FIRED (causes chroma subsampling)
[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_jpeg2()
97 {
98         i=$1; shift
99         ia=$1; shift
100         o=$1; shift
101         oa=$1; shift
102         if convert "$i" TGA:- | cjpeg -targa -quality "$1" -optimize -sample 1x1,1x1,1x1 > "$o"; then
103                 if [ "`stat -c %s "$i"`" -lt "`stat -c %s "$o"`" ]; then
104                         cp "$i" "$o"
105                 fi
106         else
107                 return 0
108         fi
109         if convert "$ia" TGA:- | cjpeg -targa -quality "$2" -optimize -sample 1x1,1x1,1x1 > "$oa"; then
110                 if [ "`stat -c %s "$ia"`" -lt "`stat -c %s "$oa"`" ]; then
111                         cp "$ia" "$oa"
112                 fi
113         else
114                 return 1
115         fi
116 }
117
118 reduce_jpeg_jpeg()
119 {
120         i=$1; shift; shift
121         o=$1; shift; shift
122         if convert "$i" TGA:- | cjpeg -targa -quality "$1" -optimize -sample 1x1,1x1,1x1 > "$o"; then
123                 if [ "`stat -c %s "$i"`" -lt "`stat -c %s "$o"`" ]; then
124                         cp "$i" "$o"
125                 fi
126         else
127                 return 0
128         fi
129 }
130
131 reduce_ogg_ogg()
132 {
133         i=$1; shift; shift
134         o=$1; shift; shift
135         tags=`vorbiscomment -R -l "$i" || true`
136         oggdec -o "$tmpdir/x.wav" "$i" && \
137         oggenc -q"$1" -o "$o" "$tmpdir/x.wav"
138         echo "$tags" | vorbiscomment -R -w "$o" || true
139 }
140
141 reduce_wav_ogg()
142 {
143         i=$1; shift; shift
144         o=$1; shift; shift
145         oggenc -q"$1" -o "$o" "$i"
146 }
147
148 reduce_rgba_dds()
149 {
150         i=$1; shift; shift
151         o=$1; shift; shift
152         convert "$i" "$tmpdir/x.tga" && \
153         "$meprefix"compress-texture "$dds_tool" dxt5 "$tmpdir/x.tga" "$o" $1
154 }
155
156 reduce_rgba_jpeg2()
157 {
158         i=$1; shift; shift
159         o=$1; shift
160         oa=$1; shift
161         if convert "$i" TGA:- | cjpeg -targa -quality "$1" -optimize -sample 1x1,1x1,1x1 > "$o"; then
162                 :
163         else
164                 return 0
165         fi
166         if convert "$ia" TGA:- | cjpeg -targa -quality "$2" -optimize -sample 1x1,1x1,1x1 > "$oa"; then
167                 :
168         else
169                 return 1
170         fi
171 }
172
173 reduce_rgb_dds()
174 {
175         i=$1; shift; shift
176         o=$1; shift; shift
177         convert "$i" "$tmpdir/x.tga" && \
178         "$meprefix"compress-texture "$dds_tool" dxt1 "$tmpdir/x.tga" "$o" $1
179 }
180
181 reduce_rgb_jpeg()
182 {
183         i=$1; shift; shift
184         o=$1; shift; shift
185         if convert "$i" TGA:- | cjpeg -targa -quality "$1" -optimize -sample 1x1,1x1,1x1 > "$o"; then
186                 :
187         else
188                 return 0
189         fi
190 }
191
192 has_alpha()
193 {
194         i=$1; shift; shift
195         o=$1; shift; shift
196         if convert "$F" -depth 16 RGBA:- | perl -e 'while(read STDIN, $_, 8) { substr($_, 6, 2) eq "\xFF\xFF" or exit 1; } exit 0;'; then
197                 # no alpha
198                 : > "$o"
199         else
200                 # has alpha
201                 echo yes > "$o"
202         fi
203 }
204
205 to_delete=
206 for F in "$@"; do
207         f=${F%.*}
208
209         echo >&2 "Handling $F..."
210         conv=false
211         keep=false
212         jqual_rgb=$jpeg_qual_rgb
213         jqual_a=$jpeg_qual_a
214
215         will_jpeg=$do_jpeg
216         will_dds=$do_dds
217         case "$f" in
218                 *_bump) will_dds=false ;;
219                 ./models/player/*) will_dds=false ;;
220                 ./models/sprites/*) will_dds=false ;;
221                 ./textures/*) ;;
222                 ./models/*) ;;
223                 ./particles/*) ;;
224                 ./progs/*) ;;
225                 *)
226                         # we can't DDS compress the 2D textures, sorry
227                         # but JPEG is still fine
228                         will_dds=false
229                         ;;
230         esac
231
232         # for deluxemaps, lightmaps and normalmaps, enforce high jpeg quality (like on alpha channels)
233         if [ "$jqual_a" -gt "$jqual_rgb" ]; then
234                 case "$f" in
235                         ./maps/*/lm_[0-9][0-9][0-9][13579]) # deluxemap
236                                 jqual_rgb=$jqual_a
237                                 ;;
238                         ./maps/*/lm_[0-9][0-9][0-9][02468]) # lightmap
239                                 jqual_rgb=$jqual_a
240                                 ;;
241                         *_norm) # normalmap
242                                 jqual_rgb=$jqual_a
243                                 ;;
244                 esac
245         fi
246
247         if $do_jpeg_if_not_dds; then
248                 if $will_dds; then
249                         will_jpeg=false
250                 else
251                         will_jpeg=true
252                 fi
253         fi
254
255         case "$F" in
256                 *_alpha.jpg)
257                         # handle in *.jpg case
258
259                         # they always got converted, I assume
260                         if $will_dds || $will_jpeg; then
261                                 conv=true
262                         fi
263                         keep=$will_jpeg
264                         ;;
265                 *.jpg)
266                         if [ -f "${f}_alpha.jpg" ]; then
267                                 cached "$will_dds"  reduce_jpeg2_dds   "$F" "${f}_alpha.jpg" "dds/${f}.dds" ""               "$dds_flags"
268                                 cached "$will_jpeg" reduce_jpeg2_jpeg2 "$F" "${f}_alpha.jpg" "$F"           "${f}_alpha.jpg" "$jqual_rgb" "$jqual_a"
269                         else                                   
270                                 cached "$will_dds"  reduce_rgb_dds     "$F" ""               "dds/${f}.dds" ""               "$dds_flags"
271                                 cached "$will_jpeg" reduce_jpeg_jpeg   "$F" ""               "$F"           ""               "$jqual_rgb"
272                         fi
273                         ;;
274                 *.png|*.tga)
275                         cached true has_alpha "$F" "" "$F.hasalpha" ""
276                         conv=false
277                         if [ -s "$F.hasalpha" ]; then
278                                 cached "$will_dds"  reduce_rgba_dds    "$F" ""               "dds/${f}.dds" ""               "$dds_flags"
279                                 cached "$will_jpeg" reduce_rgba_jpeg2  "$F" ""               "${f}.jpg"     "${f}_alpha.jpg" "$jqual_rgb" "$jqual_a"
280                         else                                                             
281                                 cached "$will_dds"  reduce_rgb_dds     "$F" ""               "dds/${f}.dds" ""               "$dds_flags"
282                                 cached "$will_jpeg" reduce_rgb_jpeg    "$F" ""               "${f}.jpg"     ""               "$jqual_rgb"
283                         fi
284                         rm -f "$F.hasalpha"
285                         ;;
286                 *.ogg)
287                         cached "$do_ogg" reduce_ogg_ogg "$F" "" "$F" "" "$ogg_qual"
288                         ;;
289                 ./sound/misc/null.wav)
290                         # never convert this one
291                         ;;
292                 *.wav)
293                         cached "$do_ogg" reduce_wav_ogg "$F" "" "${f}.ogg" "" "$ogg_qual"
294                         ;;
295         esac
296         if $del_src; then
297                 if $conv; then
298                         if ! $keep; then
299                                 # FIXME can't have spaces in filenames that way
300                                 to_delete="$to_delete $F"
301                         fi
302                 fi
303         fi
304         # fix up DDS paths by a symbolic link
305         if [ -f "dds/${f}.dds" ]; then
306                 if [ -z "${f##./textures/*}" ]; then
307                         if [ -n "${f##./textures/*/*}" ]; then
308                                 ln -snf "textures/${f#./textures/}.dds" "dds/${f#./textures/}.dds"
309                         fi
310                 fi
311         fi
312 done
313 for F in $to_delete; do
314         rm -f "$F"
315 done