1 /*
2  * Copyright (C) 2011  Rudolf Polzer   All Rights Reserved.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * RUDOLF POLZER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20  */
21 #define S2TC_LICENSE_IDENTIFIER s2tc_compress_license
22 #include "s2tc_license.h"
23 
24 #include <getopt.h>
25 #include <stdbool.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 
32 #ifdef ENABLE_RUNTIME_LINKING
33 #include <dlfcn.h>
34 #include <GL/gl.h>
35 typedef void (tx_compress_dxtn_t)(GLint srccomps, GLint width, GLint height,
36 		      const GLubyte *srcPixData, GLenum destformat,
37 		      GLubyte *dest, GLint dstRowStride);
38 tx_compress_dxtn_t *tx_compress_dxtn = NULL;
load_libraries(const char * n)39 bool load_libraries(const char *n)
40 {
41 	void *l = dlopen(n, RTLD_NOW);
42 	if(!l)
43 	{
44 		fprintf(stderr, "Cannot load library: %s\n", dlerror());
45 		return false;
46 	}
47 	tx_compress_dxtn = (tx_compress_dxtn_t *) dlsym(l, "tx_compress_dxtn");
48 	if(!tx_compress_dxtn)
49 	{
50 		fprintf(stderr, "The selected libtxc_dxtn.so does not contain all required symbols.");
51 		dlclose(l);
52 		return false;
53 	}
54 	return true;
55 }
56 #else
57 #include "txc_dxtn.h"
58 #endif
59 
60 /* START stuff that originates from image.c in DarkPlaces */
61 /*
62 	Thu Jul 14 21:58:02 CEST 2011
63 	21:25:25        @divVerent | LordHavoc: http://paste.pocoo.org/show/438804/
64 	21:25:31        @divVerent | can I have this code under a MIT-style license?
65 	21:59:58        @LordHavoc | divVerent: yeah, have them under any license you want
66 	22:00:11        @LordHavoc | divVerent: my attitude toward licenses is generally "WTFPL would be preferably if I could use it" :P
67 	22:04:01        @divVerent | LordHavoc: okay, thanks
68 */
69 
70 int image_width, image_height;
71 
72 typedef struct _TargaHeader
73 {
74 	unsigned char 	id_length, colormap_type, image_type;
75 	unsigned short	colormap_index, colormap_length;
76 	unsigned char	colormap_size;
77 	unsigned short	x_origin, y_origin, width, height;
78 	unsigned char	pixel_size, attributes;
79 }
80 TargaHeader;
81 
LoadTGA_BGRA(const unsigned char * f,int filesize)82 unsigned char *LoadTGA_BGRA (const unsigned char *f, int filesize)
83 {
84 	int x, y, pix_inc, row_inci, runlen, alphabits;
85 	unsigned char *image_buffer;
86 	unsigned int *pixbufi;
87 	const unsigned char *fin, *enddata;
88 	TargaHeader targa_header;
89 	unsigned int palettei[256];
90 	union
91 	{
92 		unsigned int i;
93 		unsigned char b[4];
94 	}
95 	bgra;
96 
97 	if (filesize < 19)
98 		return NULL;
99 
100 	enddata = f + filesize;
101 
102 	targa_header.id_length = f[0];
103 	targa_header.colormap_type = f[1];
104 	targa_header.image_type = f[2];
105 
106 	targa_header.colormap_index = f[3] + f[4] * 256;
107 	targa_header.colormap_length = f[5] + f[6] * 256;
108 	targa_header.colormap_size = f[7];
109 	targa_header.x_origin = f[8] + f[9] * 256;
110 	targa_header.y_origin = f[10] + f[11] * 256;
111 	targa_header.width = image_width = f[12] + f[13] * 256;
112 	targa_header.height = image_height = f[14] + f[15] * 256;
113 	targa_header.pixel_size = f[16];
114 	targa_header.attributes = f[17];
115 
116 	if (image_width > 32768 || image_height > 32768 || image_width <= 0 || image_height <= 0)
117 	{
118 		printf("LoadTGA: invalid size\n");
119 		return NULL;
120 	}
121 
122 	/* advance to end of header */
123 	fin = f + 18;
124 
125 	/* skip TARGA image comment (usually 0 bytes) */
126 	fin += targa_header.id_length;
127 
128 	/* read/skip the colormap if present (note: according to the TARGA spec it */
129 	/* can be present even on 1color or greyscale images, just not used by */
130 	/* the image data) */
131 	if (targa_header.colormap_type)
132 	{
133 		if (targa_header.colormap_length > 256)
134 		{
135 			printf("LoadTGA: only up to 256 colormap_length supported\n");
136 			return NULL;
137 		}
138 		if (targa_header.colormap_index)
139 		{
140 			printf("LoadTGA: colormap_index not supported\n");
141 			return NULL;
142 		}
143 		if (targa_header.colormap_size == 24)
144 		{
145 			for (x = 0;x < targa_header.colormap_length;x++)
146 			{
147 				bgra.b[0] = *fin++;
148 				bgra.b[1] = *fin++;
149 				bgra.b[2] = *fin++;
150 				bgra.b[3] = 255;
151 				palettei[x] = bgra.i;
152 			}
153 		}
154 		else if (targa_header.colormap_size == 32)
155 		{
156 			memcpy(palettei, fin, targa_header.colormap_length*4);
157 			fin += targa_header.colormap_length * 4;
158 		}
159 		else
160 		{
161 			printf("LoadTGA: Only 32 and 24 bit colormap_size supported\n");
162 			return NULL;
163 		}
164 	}
165 
166 	/* check our pixel_size restrictions according to image_type */
167 	switch (targa_header.image_type & ~8)
168 	{
169 	case 2:
170 		if (targa_header.pixel_size != 24 && targa_header.pixel_size != 32)
171 		{
172 			printf("LoadTGA: only 24bit and 32bit pixel sizes supported for type 2 and type 10 images\n");
173 			return NULL;
174 		}
175 		break;
176 	case 3:
177 		/* set up a palette to make the loader easier */
178 		for (x = 0;x < 256;x++)
179 		{
180 			bgra.b[0] = bgra.b[1] = bgra.b[2] = x;
181 			bgra.b[3] = 255;
182 			palettei[x] = bgra.i;
183 		}
184 		/* fall through to colormap case */
185 	case 1:
186 		if (targa_header.pixel_size != 8)
187 		{
188 			printf("LoadTGA: only 8bit pixel size for type 1, 3, 9, and 11 images supported\n");
189 			return NULL;
190 		}
191 		break;
192 	default:
193 		printf("LoadTGA: Only type 1, 2, 3, 9, 10, and 11 targa RGB images supported, image_type = %i\n", targa_header.image_type);
194 		return NULL;
195 	}
196 
197 	if (targa_header.attributes & 0x10)
198 	{
199 		printf("LoadTGA: origin must be in top left or bottom left, top right and bottom right are not supported\n");
200 		return NULL;
201 	}
202 
203 	/* number of attribute bits per pixel, we only support 0 or 8 */
204 	alphabits = targa_header.attributes & 0x0F;
205 	if (alphabits != 8 && alphabits != 0)
206 	{
207 		printf("LoadTGA: only 0 or 8 attribute (alpha) bits supported\n");
208 		return NULL;
209 	}
210 
211 	image_buffer = (unsigned char *)malloc(image_width * image_height * 4);
212 	if (!image_buffer)
213 	{
214 		printf("LoadTGA: not enough memory for %i by %i image\n", image_width, image_height);
215 		return NULL;
216 	}
217 
218 	/* If bit 5 of attributes isn't set, the image has been stored from bottom to top */
219 	if ((targa_header.attributes & 0x20) == 0)
220 	{
221 		pixbufi = (unsigned int*)image_buffer + (image_height - 1)*image_width;
222 		row_inci = -image_width*2;
223 	}
224 	else
225 	{
226 		pixbufi = (unsigned int*)image_buffer;
227 		row_inci = 0;
228 	}
229 
230 	pix_inc = 1;
231 	if ((targa_header.image_type & ~8) == 2)
232 		pix_inc = (targa_header.pixel_size + 7) / 8;
233 	switch (targa_header.image_type)
234 	{
235 	case 1: /* colormapped, uncompressed */
236 	case 3: /* greyscale, uncompressed */
237 		if (fin + image_width * image_height * pix_inc > enddata)
238 			break;
239 		for (y = 0;y < image_height;y++, pixbufi += row_inci)
240 			for (x = 0;x < image_width;x++)
241 				*pixbufi++ = palettei[*fin++];
242 		break;
243 	case 2:
244 		/* BGR or BGRA, uncompressed */
245 		if (fin + image_width * image_height * pix_inc > enddata)
246 			break;
247 		if (targa_header.pixel_size == 32 && alphabits)
248 		{
249 			for (y = 0;y < image_height;y++)
250 				memcpy(pixbufi + y * (image_width + row_inci), fin + y * image_width * pix_inc, image_width*4);
251 		}
252 		else
253 		{
254 			for (y = 0;y < image_height;y++, pixbufi += row_inci)
255 			{
256 				for (x = 0;x < image_width;x++, fin += pix_inc)
257 				{
258 					bgra.b[0] = fin[0];
259 					bgra.b[1] = fin[1];
260 					bgra.b[2] = fin[2];
261 					bgra.b[3] = 255;
262 					*pixbufi++ = bgra.i;
263 				}
264 			}
265 		}
266 		break;
267 	case 9: /* colormapped, RLE */
268 	case 11: /* greyscale, RLE */
269 		for (y = 0;y < image_height;y++, pixbufi += row_inci)
270 		{
271 			for (x = 0;x < image_width;)
272 			{
273 				if (fin >= enddata)
274 					break; /* error - truncated file */
275 				runlen = *fin++;
276 				if (runlen & 0x80)
277 				{
278 					/* RLE - all pixels the same color */
279 					runlen += 1 - 0x80;
280 					if (fin + pix_inc > enddata)
281 						break; /* error - truncated file */
282 					if (x + runlen > image_width)
283 						break; /* error - line exceeds width */
284 					bgra.i = palettei[*fin++];
285 					for (;runlen--;x++)
286 						*pixbufi++ = bgra.i;
287 				}
288 				else
289 				{
290 					/* uncompressed - all pixels different color */
291 					runlen++;
292 					if (fin + pix_inc * runlen > enddata)
293 						break; /* error - truncated file */
294 					if (x + runlen > image_width)
295 						break; /* error - line exceeds width */
296 					for (;runlen--;x++)
297 						*pixbufi++ = palettei[*fin++];
298 				}
299 			}
300 
301 			if (x != image_width)
302 			{
303 				/* pixbufi is useless now */
304 				printf("LoadTGA: corrupt file\n");
305 				break;
306 			}
307 		}
308 		break;
309 	case 10:
310 		/* BGR or BGRA, RLE */
311 		if (targa_header.pixel_size == 32 && alphabits)
312 		{
313 			for (y = 0;y < image_height;y++, pixbufi += row_inci)
314 			{
315 				for (x = 0;x < image_width;)
316 				{
317 					if (fin >= enddata)
318 						break; /* error - truncated file */
319 					runlen = *fin++;
320 					if (runlen & 0x80)
321 					{
322 						/* RLE - all pixels the same color */
323 						runlen += 1 - 0x80;
324 						if (fin + pix_inc > enddata)
325 							break; /* error - truncated file */
326 						if (x + runlen > image_width)
327 							break; /* error - line exceeds width */
328 						bgra.b[0] = fin[0];
329 						bgra.b[1] = fin[1];
330 						bgra.b[2] = fin[2];
331 						bgra.b[3] = fin[3];
332 						fin += pix_inc;
333 						for (;runlen--;x++)
334 							*pixbufi++ = bgra.i;
335 					}
336 					else
337 					{
338 						/* uncompressed - all pixels different color */
339 						runlen++;
340 						if (fin + pix_inc * runlen > enddata)
341 							break; /* error - truncated file */
342 						if (x + runlen > image_width)
343 							break; /* error - line exceeds width */
344 						for (;runlen--;x++)
345 						{
346 							bgra.b[0] = fin[0];
347 							bgra.b[1] = fin[1];
348 							bgra.b[2] = fin[2];
349 							bgra.b[3] = fin[3];
350 							fin += pix_inc;
351 							*pixbufi++ = bgra.i;
352 						}
353 					}
354 				}
355 
356 				if (x != image_width)
357 				{
358 					/* pixbufi is useless now */
359 					printf("LoadTGA: corrupt file\n");
360 					break;
361 				}
362 			}
363 		}
364 		else
365 		{
366 			for (y = 0;y < image_height;y++, pixbufi += row_inci)
367 			{
368 				for (x = 0;x < image_width;)
369 				{
370 					if (fin >= enddata)
371 						break; /* error - truncated file */
372 					runlen = *fin++;
373 					if (runlen & 0x80)
374 					{
375 						/* RLE - all pixels the same color */
376 						runlen += 1 - 0x80;
377 						if (fin + pix_inc > enddata)
378 							break; /* error - truncated file */
379 						if (x + runlen > image_width)
380 							break; /* error - line exceeds width */
381 						bgra.b[0] = fin[0];
382 						bgra.b[1] = fin[1];
383 						bgra.b[2] = fin[2];
384 						bgra.b[3] = 255;
385 						fin += pix_inc;
386 						for (;runlen--;x++)
387 							*pixbufi++ = bgra.i;
388 					}
389 					else
390 					{
391 						/* uncompressed - all pixels different color */
392 						runlen++;
393 						if (fin + pix_inc * runlen > enddata)
394 							break; /* error - truncated file */
395 						if (x + runlen > image_width)
396 							break; /* error - line exceeds width */
397 						for (;runlen--;x++)
398 						{
399 							bgra.b[0] = fin[0];
400 							bgra.b[1] = fin[1];
401 							bgra.b[2] = fin[2];
402 							bgra.b[3] = 255;
403 							fin += pix_inc;
404 							*pixbufi++ = bgra.i;
405 						}
406 					}
407 				}
408 
409 				if (x != image_width)
410 				{
411 					/* pixbufi is useless now */
412 					printf("LoadTGA: corrupt file\n");
413 					break;
414 				}
415 			}
416 		}
417 		break;
418 	default:
419 		/* unknown image_type */
420 		break;
421 	}
422 
423 	return image_buffer;
424 }
425 
426 /* in can be the same as out */
Image_MipReduce32(const unsigned char * in,unsigned char * out,int * width,int * height,int destwidth,int destheight)427 void Image_MipReduce32(const unsigned char *in, unsigned char *out, int *width, int *height, int destwidth, int destheight)
428 {
429 	const unsigned char *inrow;
430 	int x, y, nextrow;
431 	/* note: if given odd width/height this discards the last row/column of
432 	 * pixels, rather than doing a proper box-filter scale down
433 	 */
434 	inrow = in;
435 	nextrow = *width * 4;
436 	if (*width > destwidth)
437 	{
438 		*width >>= 1;
439 		if (*height > destheight)
440 		{
441 			/* reduce both */
442 			*height >>= 1;
443 			for (y = 0;y < *height;y++, inrow += nextrow * 2)
444 			{
445 				for (in = inrow, x = 0;x < *width;x++)
446 				{
447 					out[0] = (unsigned char) ((in[0] + in[4] + in[nextrow  ] + in[nextrow+4]) >> 2);
448 					out[1] = (unsigned char) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
449 					out[2] = (unsigned char) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
450 					out[3] = (unsigned char) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
451 					out += 4;
452 					in += 8;
453 				}
454 			}
455 		}
456 		else
457 		{
458 			/* reduce width */
459 			for (y = 0;y < *height;y++, inrow += nextrow)
460 			{
461 				for (in = inrow, x = 0;x < *width;x++)
462 				{
463 					out[0] = (unsigned char) ((in[0] + in[4]) >> 1);
464 					out[1] = (unsigned char) ((in[1] + in[5]) >> 1);
465 					out[2] = (unsigned char) ((in[2] + in[6]) >> 1);
466 					out[3] = (unsigned char) ((in[3] + in[7]) >> 1);
467 					out += 4;
468 					in += 8;
469 				}
470 			}
471 		}
472 	}
473 	else
474 	{
475 		if (*height > destheight)
476 		{
477 			/* reduce height */
478 			*height >>= 1;
479 			for (y = 0;y < *height;y++, inrow += nextrow * 2)
480 			{
481 				for (in = inrow, x = 0;x < *width;x++)
482 				{
483 					out[0] = (unsigned char) ((in[0] + in[nextrow  ]) >> 1);
484 					out[1] = (unsigned char) ((in[1] + in[nextrow+1]) >> 1);
485 					out[2] = (unsigned char) ((in[2] + in[nextrow+2]) >> 1);
486 					out[3] = (unsigned char) ((in[3] + in[nextrow+3]) >> 1);
487 					out += 4;
488 					in += 4;
489 				}
490 			}
491 		}
492 	}
493 }
FS_LoadFile(const char * fn,int * len)494 unsigned char *FS_LoadFile(const char *fn, int *len)
495 {
496 	unsigned char *buf = NULL;
497 	int n;
498 	FILE *f = fn ? fopen(fn, "rb") : stdin;
499 	*len = 0;
500 	if(!f)
501 		return NULL;
502 	for(;;)
503 	{
504 		unsigned char *newbuf = (unsigned char *) realloc(buf, *len + 65536);
505 		if(newbuf)
506 		{
507 			buf = newbuf;
508 		}
509 		else
510 		{
511 			if(fn)
512 				fclose(f);
513 			free(buf);
514 			*len = 0;
515 			return NULL;
516 		}
517 		n = fread(buf + *len, 1, 65536, f);
518 		if(n < 0)
519 		{
520 			if(fn)
521 				fclose(f);
522 			free(buf);
523 			*len = 0;
524 			return NULL;
525 		}
526 		*len += n;
527 		if(n < 65536)
528 			break;
529 	}
530 	if(fn)
531 		fclose(f);
532 	return buf;
533 }
534 /* END of darkplaces stuff */
535 
LittleLong(uint32_t w)536 uint32_t LittleLong(uint32_t w)
537 {
538 	union
539 	{
540 		unsigned char c[4];
541 		uint32_t u;
542 	}
543 	un;
544 	un.c[0] = w;
545 	un.c[1] = w >> 8;
546 	un.c[2] = w >> 16;
547 	un.c[3] = w >> 24;
548 	return un.u;
549 }
550 
usage(const char * me)551 int usage(const char *me)
552 {
553 	fprintf(stderr, "usage:\n"
554 			"%s \n"
555 			"    [-i infile.tga]\n"
556 			"    [-o outfile.dds]\n"
557 			"    [-t {DXT1|DXT3|DXT5}]\n"
558 #ifdef ENABLE_RUNTIME_LINKING
559 			"    [-l path_to_libtxc_dxtn.so]\n"
560 #endif
561 			,
562 			me);
563 	return 1;
564 }
565 
main(int argc,char ** argv)566 int main(int argc, char **argv)
567 {
568 	const char *infile = NULL, *outfile = NULL;
569 	FILE *outfh;
570 	unsigned char *picdata, *pic;
571 	int x, mipcount, piclen;
572 	const char *fourcc;
573 	int blocksize;
574 	GLenum dxt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
575 
576 #ifdef ENABLE_RUNTIME_LINKING
577 	const char *library = "libtxc_dxtn.so";
578 #endif
579 
580 	int opt;
581 	while((opt = getopt(argc, argv, "i:o:t:"
582 #ifdef ENABLE_RUNTIME_LINKING
583 					"l:"
584 #endif
585 					)) != -1)
586 	{
587 		switch(opt)
588 		{
589 			case 'i':
590 				infile = optarg;
591 				break;
592 			case 'o':
593 				outfile = optarg;
594 				break;
595 			case 't':
596 				if(!strcasecmp(optarg, "DXT1"))
597 					dxt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
598 				else if(!strcasecmp(optarg, "DXT3"))
599 					dxt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
600 				else if(!strcasecmp(optarg, "DXT5"))
601 					dxt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
602 				else
603 					return usage(argv[0]);
604 				break;
605 #ifdef ENABLE_RUNTIME_LINKING
606 			case 'l':
607 				library = optarg;
608 				break;
609 #endif
610 			default:
611 				return usage(argv[0]);
612 				break;
613 		}
614 	}
615 #ifdef ENABLE_RUNTIME_LINKING
616 	if(!load_libraries(library))
617 		return 1;
618 #endif
619 
620 	outfh = outfile ? fopen(outfile, "wb") : stdout;
621 	if(!outfh)
622 	{
623 		printf("opening output failed\n");
624 		return 2;
625 	}
626 
627 	picdata = FS_LoadFile(infile, &piclen);
628 	if(!picdata)
629 	{
630 		printf("FS_LoadFile failed\n");
631 		return 2;
632 	}
633 
634 	pic = LoadTGA_BGRA(picdata, piclen);
635 	for(x = 0; x < image_width*image_height; ++x) {
636 		unsigned char h = pic[4*x];
637 		pic[4*x] = pic[4*x+2];
638 		pic[4*x+2] = h;
639 	}
640 	mipcount = 0;
641 	while(image_width >= (1 << mipcount) || image_height >= (1 << mipcount))
642 		++mipcount;
643 	/* now, (1 << mipcount) >= width, height */
644 
645 	switch(dxt)
646 	{
647 		case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
648 			blocksize = 8;
649 			fourcc = "DXT1";
650 			break;
651 		case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
652 			blocksize = 16;
653 			fourcc = "DXT3";
654 			break;
655 		default:
656 		case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
657 			blocksize = 16;
658 			fourcc = "DXT5";
659 			break;
660 	}
661 
662 	{
663 		uint32_t zero = LittleLong(0);
664 		bool alphapixels = false;
665 		int x, y;
666 		uint32_t dds_picsize, dds_mipcount, dds_width, dds_height;
667 
668 		for(y = 0; y < image_height; ++y)
669 			for(x = 0; x < image_width; ++x)
670 				if(pic[(y*image_width+x)*4+3] != 255)
671 				{
672 					alphapixels = true;
673 					break;
674 				}
675 
676 		dds_picsize = LittleLong(((image_width+3)/4) * ((image_height+3)/4) * blocksize);
677 		dds_mipcount = LittleLong(mipcount);
678 		dds_width = LittleLong(image_width);
679 		dds_height = LittleLong(image_height);
680 
681 		/* 0 */
682 		fwrite("DDS ", 4, 1, outfh);
683 		fwrite("\x7c\x00\x00\x00", 4, 1, outfh);
684 		fwrite("\x07\x10\x0a\x00", 4, 1, outfh);
685 		fwrite(&dds_height, 4, 1, outfh);
686 		fwrite(&dds_width, 4, 1, outfh);
687 		fwrite(&dds_picsize, 4, 1, outfh);
688 		fwrite(&zero, 4, 1, outfh);
689 		fwrite(&dds_mipcount, 4, 1, outfh);
690 
691 		/* 32 */
692 		fwrite(&zero, 4, 1, outfh);
693 		fwrite(&zero, 4, 1, outfh);
694 		fwrite(&zero, 4, 1, outfh);
695 		fwrite(&zero, 4, 1, outfh);
696 		fwrite(&zero, 4, 1, outfh);
697 		fwrite(&zero, 4, 1, outfh);
698 		fwrite(&zero, 4, 1, outfh);
699 		fwrite(&zero, 4, 1, outfh);
700 
701 		/* 64 */
702 		fwrite(&zero, 4, 1, outfh);
703 		fwrite(&zero, 4, 1, outfh);
704 		fwrite(&zero, 4, 1, outfh);
705 		fwrite("\x20\x00\x00\x00", 4, 1, outfh);
706 		fwrite(alphapixels ? "\x05\x00\x00\x00" : "\x04\x00\x00\x00", 4, 1, outfh);
707 		fwrite(fourcc, 4, 1, outfh);
708 		fwrite(&zero, 4, 1, outfh);
709 		fwrite(&zero, 4, 1, outfh);
710 
711 		/* 96 */
712 		fwrite(&zero, 4, 1, outfh);
713 		fwrite(&zero, 4, 1, outfh);
714 		fwrite(&zero, 4, 1, outfh);
715 		fwrite("\x08\x10\x40\x00", 4, 1, outfh);
716 		fwrite(&zero, 4, 1, outfh);
717 		fwrite(&zero, 4, 1, outfh);
718 		fwrite(&zero, 4, 1, outfh);
719 		fwrite(&zero, 4, 1, outfh);
720 	}
721 
722 	for(;;)
723 	{
724 		int blocks_w = (image_width + 3) / 4;
725 		int blocks_h = (image_height + 3) / 4;
726 		GLubyte *obuf = (GLubyte *) malloc(blocksize * blocks_w * blocks_h);
727 		tx_compress_dxtn(4, image_width, image_height, pic, dxt, obuf, blocks_w * blocksize);
728 		fwrite(obuf, blocksize * blocks_w * blocks_h, 1, outfh);
729 		free(obuf);
730 		if(image_width == 1 && image_height == 1)
731 			break;
732 		Image_MipReduce32(pic, pic, &image_width, &image_height, 1, 1);
733 	}
734 
735 	if(outfile)
736 		fclose(outfh);
737 
738 	return 0;
739 }
740