1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include <string.h>
13 
14 #include "globalincs/pstypes.h"
15 #include "tgautils/tgautils.h"
16 #include "cfile/cfile.h"
17 #include "bmpman/bmpman.h"
18 #include "palman/palman.h"
19 #include "graphics/2d.h"
20 #include "cmdline/cmdline.h"
21 
22 // -----------------
23 //
24 // Defines
25 //
26 // -----------------
27 
28 const char *Xfile_ID = "TRUEVISION-XFILE.";
29 
30 #define TARGA_FOOTER_SIZE					26  	// using sizeof(targa_footer) doesn't work unless we
31 												// pack the struct and I don't want to do that :)
32 
33 #define MAX_TARGA_RUN_LENGTH_PACKET 	128
34 #define TARGA_HEADER_LENGTH 			18
35 #define ULORIGIN						(header.image_descriptor & 0x20)
36 
37 // -----------------
38 //
39 // Structures
40 //
41 // -----------------
42 
43 typedef struct targa_header {
44 	short xoffset;
45 	short yoffset;
46 	short width;
47 	short height;
48 	short cmap_start;
49 	short cmap_length;
50 	ubyte id_length;
51 	ubyte color_map_type;
52 	ubyte image_type;
53 	ubyte cmap_depth;
54 	ubyte pixel_depth;
55 	ubyte image_descriptor;
56 } targa_header;
57 
58 typedef struct targa_footer {
59 	uint ext_offset;		// file offset to extension area (we ignore it)
60 	uint dev_offset;		// file offset to developer area (we also ignore this)
61 	char sig_string[18];	// check to see if this is new TGA format, string should be value of Xfile_ID
62 } targa_footer;
63 
64 // -----------------
65 //
66 // Internal Functions
67 //
68 // -----------------
69 
70 
71 // copy from one pixel buffer to another
72 //
73 // to - pointer to dest. buffet
74 // from - pointer to source buffer
75 // pixels - number of pixels to copy
76 // fromsize - source pixel size
77 // tosize - dest. pixel size
78 //
79 // returns - number of pixels copied to destination
80 //
targa_copy_data(char * to,char * from,int pixels,int fromsize,int tosize)81 static int targa_copy_data(char *to, char *from, int pixels, int fromsize, int tosize)
82 {
83 	if ( (fromsize == 2) && (tosize==2) )	{
84 		// Flip the alpha bit on 1555 format
85 		ushort *src, *dst;
86 
87 		src = (ushort *)from;
88 		dst = (ushort *)from;
89 		for (int i=0; i<pixels; i++ )	{
90 			*dst++ = (ushort)((*src++) ^ 0x8000);		// Flip the transparency bit
91 		}
92 		return tosize*pixels;
93 	} else if ( (fromsize == 2) && (tosize == 3) )	{
94 		ushort *src;
95 
96 		src = (ushort *)from;
97 		for (int i=0; i<pixels; i++ )	{
98 			ushort pixel = *src++;
99 
100 			*to++ = (ubyte)((pixel & 0x1f) * 8);
101 			*to++ = (ubyte)(((pixel >> 5) & 63) * 4);
102 			*to++ = (ubyte)(((pixel >> 11) & 0x1f) * 8);
103 		}
104 		return tosize*pixels;
105 	} else {
106 		Assert(fromsize == tosize);
107 		memcpy(to, from, pixels * fromsize);
108 		return tosize*pixels;
109 	}
110 }
111 
112 //	targa_pixels_equal -- Test if two pixels are identical
113 //
114 // pix1 - first pixel data
115 // pix2 - second pixel data
116 // pixbytes - number of bytes per pixel
117 //
118 // returns - 0 if No Match, else 1 if Match
targa_pixels_equal(char * pix1,char * pix2,int pixbytes)119 static int targa_pixels_equal(char *pix1, char *pix2, int pixbytes)
120 {
121 	do	{
122 		if ( *pix1++ != *pix2++ ) {
123 			return 0;
124 		}
125 	} while ( --pixbytes > 0 );
126 
127 	return 1;
128 }
129 
130 //	Perform targa RLE on the input data
131 //
132 // out - Buffer to write it out to
133 // in - Buffer to compress
134 // outsize - Number of bytes in output buffer
135 // pixsize - Number of bytes in input pixel
136 // bytecount - Number of bytes input
137 //
138 // returns -  size of compressed data
139 //
targa_compress(char * out,char * in,int outsize,int pixsize,int bytecount)140 int targa_compress(char *out, char *in, int outsize, int pixsize, int bytecount)
141 {
142 	int pixcount;		// number of pixels in the current packet
143 	char *inputpixel=NULL;	// current input pixel position
144 	char *matchpixel=NULL;	// pixel value to match for a run
145 	char *flagbyte=NULL;		// location of last flag byte to set
146 	int rlcount;		// current count in r.l. string
147 	int rlthresh;		// minimum valid run length
148 	char *copyloc;		// location to begin copying at
149 
150 	// set the threshold -- the minimum valid run length
151 
152 	if (outsize == 1) {
153 		rlthresh = 2;					// for 8bpp, require a 2 pixel span before rle'ing
154 	} else {
155 		rlthresh = 1;
156 	}
157 
158 	// set the first pixel up
159 
160 	flagbyte = out;	// place to put next flag if run
161 	inputpixel = in;
162 	pixcount = 1;
163 	rlcount = 0;
164 	copyloc = (char *)0;
165 
166 	// loop till data processing complete
167 	do	{
168 
169 		// if we have accumulated a 128-byte packet, process it
170 		if ( pixcount == 129 )	{
171 			*flagbyte = 127;
172 
173 			// set the run flag if this is a run
174 
175 			if ( rlcount >= rlthresh )	{
176 					*flagbyte |= 0x80;
177 					pixcount = 2;
178 			}
179 
180 			// copy the data into place
181 			++flagbyte;
182 			flagbyte += targa_copy_data(flagbyte, copyloc, pixcount-1, pixsize, outsize);
183 			pixcount = 1;
184 
185 			// set up for next packet
186 			continue;
187 		}
188 
189 		// if zeroth byte, handle as special case
190 		if ( pixcount == 1 )	{
191 			rlcount = 0;
192 			copyloc = inputpixel;		/* point to 1st guy in packet */
193 			matchpixel = inputpixel;	/* set pointer to pix to match */
194 			pixcount = 2;
195 			inputpixel += pixsize;
196 			continue;
197 		}
198 
199 		// assembling a packet -- look at next pixel
200 
201 		// current pixel == match pixel?
202 		if ( targa_pixels_equal(inputpixel, matchpixel, outsize) )	{
203 
204 			//	establishing a run of enough length to
205 			//	save space by doing it
206 			//		-- write the non-run length packet
207 			//		-- start run-length packet
208 
209 			if ( ++rlcount == rlthresh )	{
210 
211 				//	close a non-run packet
212 
213 				if ( pixcount > (rlcount+1) )	{
214 					// write out length and do not set run flag
215 
216 					*flagbyte++ = (char)(pixcount - 2 - rlthresh);
217 
218 					flagbyte += targa_copy_data(flagbyte, copyloc, (pixcount-1-rlcount), pixsize, outsize);
219 
220 					copyloc = inputpixel;
221 					pixcount = rlcount + 1;
222 				}
223 			}
224 		} else {
225 
226 			// no match -- either break a run or continue without one
227 			//	if a run exists break it:
228 			//		write the bytes in the string (outsize+1)
229 			//		start the next string
230 
231 			if ( rlcount >= rlthresh )	{
232 
233 				*flagbyte++ = (char)(0x80 | rlcount);
234 				flagbyte += targa_copy_data(flagbyte, copyloc, 1, pixsize, outsize);
235 				pixcount = 1;
236 				continue;
237 			} else {
238 
239 				//	not a match and currently not a run
240 				//		- save the current pixel
241 				//		- reset the run-length flag
242 				rlcount = 0;
243 				matchpixel = inputpixel;
244 			}
245 		}
246 		pixcount++;
247 		inputpixel += pixsize;
248 	} while ( inputpixel < (in + bytecount));
249 
250 	// quit this buffer without loosing any data
251 
252 	if ( --pixcount >= 1 )	{
253 		*flagbyte = (char)(pixcount - 1);
254 		if ( rlcount >= rlthresh )	{
255 			*flagbyte |= 0x80;
256 			pixcount = 1;
257 		}
258 
259 		// copy the data into place
260 		++flagbyte;
261 		flagbyte += targa_copy_data(flagbyte, copyloc, pixcount, pixsize, outsize);
262 	}
263 	return(flagbyte-out);
264 }
265 
266 // Reads a pixel of the specified bytes_per_pixel into memory and
267 // returns the number of bytes read into memory.
268 // NOTE : for FreeSpace2, this also swizzles data into the proper screen (NOT texture) format - just like
269 //        the pcxutils do.
270 //
271 // dst - A pointer to the destination.  Must be at least 4 bytes long.
272 // targa_file - The file to read from.
273 // bytes_per_pixel - The bytes per pixel of the file.
274 // dest_size - bytes per pixel in destination (1 or 2. 1 == 8 bit paletted)
275 //
276 // returns - Number of byte read into memory
277 //
targa_read_pixel(int num_pixels,ubyte ** dst,ubyte ** src,int bytes_per_pixel,int dest_size)278 static void targa_read_pixel( int num_pixels, ubyte **dst, ubyte **src, int bytes_per_pixel, int dest_size )
279 {
280 	int idx;
281 	ushort pixel;
282 	int pixel32;
283 	ubyte pal_index;
284 	ubyte r, g, b;
285 	ubyte al = 0;
286 
287 	for(idx=0; idx<num_pixels; idx++){
288 		// 24 or 32 bit
289 		if ( (bytes_per_pixel == 3) || (bytes_per_pixel == 4) ) {
290 			memset( &pixel32, 0xff, sizeof(int) );
291 			memcpy( &pixel32, *src, bytes_per_pixel );
292 
293 #if BYTE_ORDER == BIG_ENDIAN
294 			// on big-endian it will be used as ARGB so switch it back to BGRA
295 			if ( dest_size == 4 ) {
296 				pixel32 = INTEL_INT(pixel32);
297 			}
298 #endif
299 
300 			// should have it's own alpha settings so just copy it out as is
301 			memcpy( *dst, &pixel32, dest_size );
302 		}
303 		// 8 or 16 bit
304 		else {
305 			// stuff the 16 bit pixel
306 			memcpy(&pixel, *src, bytes_per_pixel);
307 
308 			pixel = INTEL_SHORT(pixel);
309 
310 			// if the pixel is transparent, make it so...
311 			if(((pixel & 0x7c00) == 0) && ((pixel & 0x03e0) == 0x03e0) && ((pixel & 0x001f) == 0)){
312 				r = b = 0;
313 				g = 255;
314 				al = 0;
315 				bm_set_components((ubyte*)&pixel, &r, &g, &b, &al);
316 			} else {
317 				// get the 8 bit r, g, and b values
318 				r = (ubyte)(((pixel & 0x7c00) >> 10) * 8);
319 				g = (ubyte)(((pixel & 0x03e0) >> 5) * 8);
320 				b = (ubyte)((pixel & 0x001f) * 8);
321 				al = 1;
322 
323 				// now stuff these back, swizzling properly
324 				pixel = 0;
325 				bm_set_components((ubyte*)&pixel, &r, &g, &b, &al);
326 			}
327 
328 			// 16 bit destination
329 			if(dest_size == 2){
330 				// stuff the final pixel
331 				memcpy( *dst, &pixel, bytes_per_pixel );
332 			}
333 			// 8 bit destination
334 			else {
335 				pal_index = (ubyte)palette_find((int)r, (int)g, (int)b);
336 				**dst = pal_index;
337 			}
338 		}
339 
340 		// next pixel
341 		(*dst) += dest_size;
342 		(*src) += bytes_per_pixel;
343 	}
344 }
345 
346 // -----------------
347 //
348 // External Functions
349 //
350 // -----------------
351 
352 // Reads header information from the targa file into the bitmap pointer
353 //
354 // filename - name of the targa bitmap file
355 // w - (output) width of the bitmap
356 // h - (output) height of the bitmap
357 // bpp - (output) bits per pixel of the bitmap
358 //
359 // returns - TARGA_ERROR_NONE if successful, otherwise error code
360 //
targa_read_header(const char * real_filename,CFILE * img_cfp,int * w,int * h,int * bpp,ubyte * palette)361 int targa_read_header(const char *real_filename, CFILE *img_cfp, int *w, int *h, int *bpp, ubyte *palette )
362 {
363 	targa_header header;
364 	CFILE *targa_file = NULL;
365 	char filename[MAX_FILENAME_LEN];
366 
367 	if (img_cfp == NULL) {
368 		strcpy_s( filename, real_filename );
369 
370 		char *p = strchr( filename, '.' );
371 
372 		if ( p )
373 			*p = 0;
374 
375 		strcat_s( filename, ".tga" );
376 
377 		targa_file = cfopen( filename , "rb" );
378 
379 		if ( !targa_file ) {
380 			return TARGA_ERROR_READING;
381 		}
382 	} else {
383 		targa_file = img_cfp;
384 	}
385 
386 	header.id_length = cfread_ubyte(targa_file);
387 	// header.id_length=targa_file.read_char();
388 
389 	header.color_map_type = cfread_ubyte(targa_file);
390 	// header.color_map_type=targa_file.read_char();
391 
392 	header.image_type = cfread_ubyte(targa_file);
393 	// header.image_type=targa_file.read_char();
394 
395 	header.cmap_start = cfread_short(targa_file);
396 	// header.cmap_start=targa_file.read_short();
397 
398 	header.cmap_length = cfread_short(targa_file);
399 	// header.cmap_length=targa_file.read_short();
400 
401 	header.cmap_depth = cfread_ubyte(targa_file);
402 	// header.cmap_depth=targa_file.read_char();
403 
404 	header.xoffset = cfread_short(targa_file);
405 	// header.xoffset=targa_file.read_short();
406 
407 	header.yoffset = cfread_short(targa_file);
408 	// header.yoffset=targa_file.read_short();
409 
410 	header.width = cfread_short(targa_file);
411 	// header.width=targa_file.read_short();
412 
413 	header.height = cfread_short(targa_file);
414 	// header.height=targa_file.read_short();
415 
416 	header.pixel_depth = cfread_ubyte(targa_file);
417 	// header.pixel_depth=targa_file.read_char();
418 
419 	header.image_descriptor = cfread_ubyte(targa_file);
420 	// header.image_descriptor=targa_file.read_char();
421 
422 	if (img_cfp == NULL) {
423 		cfclose(targa_file);
424 		targa_file = NULL;
425 	}
426 
427 	if ( (header.pixel_depth != 16) && (header.pixel_depth != 24) && (header.pixel_depth != 32) ) {
428 		Assertion( (header.pixel_depth != 16) && (header.pixel_depth != 24) && (header.pixel_depth != 32), "Invalid colour depth (%d) in header of tga file %s\n", header.pixel_depth, real_filename );
429 		return TARGA_ERROR_READING;
430 	}
431 
432 	if (w) *w = header.width;
433 	if (h) *h = header.height;
434 	if (bpp) *bpp = header.pixel_depth;
435 
436 	return TARGA_ERROR_NONE;
437 }
438 
439 // Uncompresses some RLE'd TGA data
440 //
441 // dst: pointer uncompressed destination.
442 // src: pointer to source rle'd data.
443 // bitmap_width: how many pixels to uncompress.
444 // bytes_per_pixel: bytes per pixel of the data.
445 //
446 // returns: number of input bytes processed.
447 //
targa_uncompress(ubyte * dst,ubyte * src,int bitmap_width,int bytes_per_pixel,int dest_size)448 int targa_uncompress( ubyte *dst, ubyte *src, int bitmap_width, int bytes_per_pixel, int dest_size )
449 {
450 	ubyte *pixdata = dst;
451 	ubyte *src_pixels = src;
452 
453 	int pixel_count = 0;         // Initialize pixel counter
454 
455 	// Main decoding loop
456 	while (pixel_count < bitmap_width ) {
457 
458 		// Get the pixel count
459 		int run_count = *src_pixels++;
460 
461 		// Make sure writing this next run will not overflow the buffer
462 		Assert(pixel_count + (run_count & 0x7f) + 1 <= bitmap_width );
463 
464 		// If the run is encoded...
465 		if ( run_count & 0x80 ) {
466 			run_count &= ~0x80;              // Mask off the upper bit
467 
468 			// Update total pixel count
469 			pixel_count += (run_count + 1);
470 
471 			ubyte pixel_value[4];	// temporary
472 			ubyte *tmp = pixel_value;
473 			targa_read_pixel( 1, &tmp, &src_pixels, bytes_per_pixel, dest_size );
474 
475 			// Write remainder of pixel run to buffer 'run_count' times
476 			do {
477 				memcpy( pixdata, pixel_value, dest_size );
478 				pixdata += dest_size;
479 			} while (run_count--);
480 
481 		} else {   // ...the run is unencoded (raw)
482 			// Update total pixel count
483 			pixel_count += (run_count + 1);
484 
485 			// Read run_count pixels
486 			targa_read_pixel(run_count+1, &pixdata, &src_pixels, bytes_per_pixel, dest_size );
487 		}
488 	}
489 
490 	Assert( pixel_count == bitmap_width );
491 
492 	return src_pixels - src;
493 }
494 
495 
496 // Loads a Targa bitmap
497 //
498 // filename - name of the targa file to load
499 // image_data - allocated storage for the bitmap
500 //
501 // returns - true if succesful, false otherwise
502 //
targa_read_bitmap(const char * real_filename,ubyte * image_data,ubyte * palette,int dest_size,int cf_type)503 int targa_read_bitmap(const char *real_filename, ubyte *image_data, ubyte *palette, int dest_size, int cf_type)
504 {
505 	Assert(real_filename);
506 	targa_header header;
507 	targa_footer footer;
508 	CFILE *targa_file;
509 	char filename[MAX_FILENAME_LEN];
510 	ubyte r, g, b;
511 	int xfile_offset = 0;
512 
513 	// open the file
514 	strcpy_s( filename, real_filename );
515 	char *p = strchr( filename, '.' );
516 	if ( p ) *p = 0;
517 	strcat_s( filename, ".tga" );
518 
519 	targa_file = cfopen( filename , "rb", CFILE_NORMAL, cf_type );
520 	if ( !targa_file ){
521 		return TARGA_ERROR_READING;
522 	}
523 
524 	// read the footer info first
525 	cfseek( targa_file, cfilelength(targa_file) - TARGA_FOOTER_SIZE, CF_SEEK_SET );
526 
527 	memset( &footer, 0, sizeof(targa_footer) );
528 
529 	footer.ext_offset = cfread_uint(targa_file);
530 	footer.dev_offset = cfread_uint(targa_file);
531 
532 	cfread_string(footer.sig_string, sizeof(footer.sig_string), targa_file);
533 
534 	if ( !strcmp(footer.sig_string, Xfile_ID) ) {
535 		// it's an extended file to lets be sure to skip the extra crap which comes after the
536 		// image data section, dev section comes first in the file and we only need one offset
537 		if (footer.dev_offset || footer.ext_offset) {
538 			xfile_offset = cfilelength(targa_file) - ((footer.dev_offset) ? footer.dev_offset : footer.ext_offset);
539 		}
540 	}
541 
542 	// done with the footer so jump back and do normal reading
543 	cfseek( targa_file, 0, CF_SEEK_SET );
544 
545 	header.id_length = cfread_ubyte(targa_file);
546 	// header.id_length=targa_file.read_char();
547 
548 	header.color_map_type = cfread_ubyte(targa_file);
549 	// header.color_map_type=targa_file.read_char();
550 
551 	header.image_type = cfread_ubyte(targa_file);
552 	// header.image_type=targa_file.read_char();
553 
554 	header.cmap_start = cfread_short(targa_file);
555 	// header.cmap_start=targa_file.read_short();
556 
557 	header.cmap_length = cfread_short(targa_file);
558 	// header.cmap_length=targa_file.read_short();
559 
560 	header.cmap_depth = cfread_ubyte(targa_file);
561 	// header.cmap_depth=targa_file.read_char();
562 
563 	header.xoffset = cfread_short(targa_file);
564 	// header.xoffset=targa_file.read_short();
565 
566 	header.yoffset = cfread_short(targa_file);
567 	// header.yoffset=targa_file.read_short();
568 
569 	header.width = cfread_short(targa_file);
570 	// header.width=targa_file.read_short();
571 
572 	header.height = cfread_short(targa_file);
573 	// header.height=targa_file.read_short();
574 
575 	header.pixel_depth = cfread_ubyte(targa_file);
576 	// header.pixel_depth=targa_file.read_char();
577 
578 	header.image_descriptor = cfread_ubyte(targa_file);
579 	// header.image_descriptor=targa_file.read_char();
580 
581 	int bytes_per_pixel = (header.pixel_depth>>3);
582 
583 	// we're only allowing 2 bytes per pixel (16 bit compressed), unless Cmdline_jpgtga is used
584 	Assert( (bytes_per_pixel == 2) || (bytes_per_pixel == 3) || (bytes_per_pixel == 4) );
585 
586 	if ( (bytes_per_pixel < 2) || (bytes_per_pixel > 4) ) {
587 		cfclose(targa_file);
588 		Int3();
589 
590 		return TARGA_ERROR_READING;
591 	}
592 
593 	if((header.image_type!=1)&&(header.image_type!=2)&&(header.image_type!=9)&&(header.image_type!=10)) {
594 		cfclose(targa_file);
595 		return TARGA_ERROR_READING;
596 	}
597 
598 	// skip the Image ID field -- should not be needed
599 	if(header.id_length>0) {
600 		if ( cfseek(targa_file, header.id_length, CF_SEEK_CUR) ) {
601 			cfclose(targa_file);
602 			return TARGA_ERROR_READING;
603 		}
604 	}
605 
606 	// read palette if one present.
607 
608 	if (header.color_map_type)	{		 // non-zero indicates palette in the file
609 		Int3();
610 
611 		// Determine the size of the color map
612 		Assert(header.cmap_depth==24);
613 		Assert(header.cmap_length<=256);
614 		Assert(palette);
615 
616 		// Read the color map data
617 		int i;
618 		for (i = 0; i < header.cmap_length; i++)	{
619 			r = cfread_ubyte(targa_file);
620 			g = cfread_ubyte(targa_file);
621 			b = cfread_ubyte(targa_file);
622 
623 			if(palette != NULL){
624 				palette[i*3+2] = r;
625 				palette[i*3+1] = g;
626 				palette[i*3+0] = b;
627 			}
628 		}
629 		// Fill out with black.
630 		if(palette != NULL){
631 			for (; i < 256; i++)	{
632 				palette[i*3+2] = 0;
633 				palette[i*3+1] = 0;
634 				palette[i*3+0] = 0;
635 			}
636 		}
637 	}
638 
639 	int bytes_remaining = cfilelength(targa_file) - cftell(targa_file) - xfile_offset;
640 
641 	Assert(bytes_remaining > 0);
642 
643 	ubyte *fileptr = (ubyte*)vm_malloc(bytes_remaining);
644 	Assert(fileptr);
645 	if(fileptr == NULL){
646 		return TARGA_ERROR_READING;
647 	}
648 
649 	ubyte *src_pixels = fileptr;
650 
651 	cfread(fileptr, bytes_remaining, 1, targa_file);
652 
653 	int rowsize = header.width * dest_size;
654 
655 	if ( (header.image_type == 1) || (header.image_type == 2) || (header.image_type == 3) ) {
656 		// Uncompressed read
657 
658 		for (int i = 0; i < header.height; i++)	{
659 			ubyte * pixptr;
660 
661 			if ( ULORIGIN )	{
662 				pixptr = image_data + i * rowsize;
663 			} else {
664 				pixptr = image_data + ((header.height - i - 1) * rowsize);
665 			}
666 
667 			targa_read_pixel(header.width, &pixptr, &src_pixels, bytes_per_pixel, dest_size );
668 		}
669 
670 	} else if (header.image_type == 9 || header.image_type == 10 || header.image_type == 11) {
671 		// the following handles RLE'ed targa data.
672 
673 		// targas encoded by the scanline -- loop on the height
674 		for (int i = 0; i < header.height; i++) {
675 			ubyte *pixdata;
676 
677 			if (ULORIGIN)	{
678 				pixdata = image_data + i * rowsize;
679 			} else {
680 				pixdata = image_data + ((header.height - i - 1) * rowsize);
681 			}
682 
683 			src_pixels += targa_uncompress( pixdata, src_pixels, header.width, bytes_per_pixel, dest_size );
684 		}
685 
686 	}
687 
688 	vm_free(fileptr);
689 	cfclose(targa_file);
690 	targa_file = NULL;
691 
692 	return TARGA_ERROR_NONE;
693 }
694 
695 // Write out a Targa format bitmap.  Always writes out a top-up bitmap.
696 // JAS: DOESN'T WORK WITH 8-BPP PALETTES
697 //
698 // filename: name of the Targa file, .tga extension added if not passed in
699 // data:     raw image data
700 // w:        width of the bitmap in pixels
701 // h:        height of the bitmap in pixels
702 // bpp:      bits per pixel of the bitmap
703 //
704 // returns:  0 if successful, otherwise -1
705 //
targa_write_bitmap(char * real_filename,ubyte * data,ubyte * palette,int w,int h,int bpp)706 int targa_write_bitmap(char *real_filename, ubyte *data, ubyte *palette, int w, int h, int bpp)
707 {
708 	Assert(bpp == 24);
709 	char filename[MAX_FILENAME_LEN];
710 	CFILE *f;
711 	int bytes_per_pixel = (bpp >> 3);
712 
713 	// open the file
714 	strcpy_s( filename, real_filename );
715 	char *p = strchr( filename, '.' );
716 	if ( p ) *p = 0;
717 	strcat_s( filename, ".tga" );
718 
719 	f = cfopen( filename , "wb" );
720 	if ( !f ){
721 		return TARGA_ERROR_READING;
722 	}
723 
724 	// Write the TGA header
725 	cfwrite_ubyte(0, f);
726 	// f.write_ubyte(0);				//	IDLength
727 
728 	cfwrite_ubyte(0, f);
729 	//f.write_ubyte(0);				//	ColorMapType
730 
731 	cfwrite_ubyte(10, f);
732 	// f.write_ubyte(10);			//	image_type: 2 = 24bpp, uncompressed, 10=24bpp rle compressed
733 
734 	cfwrite_ushort(0, f);
735 	// f.write_ushort(0);			// CMapStart
736 
737 	cfwrite_ushort(0, f);
738 	// f.write_ushort(0);			//	CMapLength
739 
740 	cfwrite_ubyte(0, f);
741 	// f.write_ubyte(0);				// CMapDepth
742 
743 	cfwrite_ushort(0, f);
744 	// f.write_ushort(0);			//	XOffset
745 
746 	cfwrite_ushort(0, f);
747 	// f.write_ushort(0);			//	YOffset
748 
749 	cfwrite_ushort((ushort)w, f);
750 	// f.write_ushort((ushort)w);	//	Width
751 
752 	cfwrite_ushort((ushort)h, f);
753 	// f.write_ushort((ushort)h);	//	Height
754 
755 	cfwrite_ubyte(24, f);
756 	// f.write_ubyte(24);			// pixel_depth
757 
758 	cfwrite_ubyte(0x20, f);
759 	// f.write_ubyte(0x20);				// ImageDesc  ( 0x20 = Origin at upper left )
760 
761 	ubyte *compressed_data;
762 	compressed_data = (ubyte*)vm_malloc(w * h * bytes_per_pixel);
763 	Assert(compressed_data);
764 	if(compressed_data == NULL){
765 		cfclose(f);
766 		return -1;
767 	}
768 
769 	int compressed_data_len;
770 	compressed_data_len = targa_compress((char*)compressed_data, (char*)data, 3, bytes_per_pixel, w * h * bytes_per_pixel);
771 	if (compressed_data_len < 0) {
772 		vm_free(compressed_data);
773 		cfclose(f);
774 		return -1;
775 	}
776 
777 	cfwrite(compressed_data, compressed_data_len, 1, f);
778 	cfclose(f);
779 	f = NULL;
780 
781 	return 0;
782 }
783