1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Last modified: 03/0/2009
5 //
6 // Filename: src-IL/src/il_iff.c
7 //
8 // Description: Reads from an .iff file.  Contribution from GAIA:
9 //				http://gaia.fdi.ucm.es/grupo/projects/javy/devzone.html#DevILNotes.
10 //
11 //-----------------------------------------------------------------------------
12 
13 #include "il_internal.h"
14 
15 #ifndef IL_NO_IFF
16 
17 // Chunk type, data and functions:
18 typedef struct _iff_chunk {
19 	ILuint	tag;
20 	ILuint	start;
21 	ILuint	size;
22 	ILuint	chunkType;
23 } iff_chunk;
24 
25 #define CHUNK_STACK_SIZE (32)
26 static iff_chunk chunkStack[CHUNK_STACK_SIZE];
27 static int chunkDepth = -1;
28 
29 iff_chunk iff_begin_read_chunk();
30 void iff_end_read_chunk();
31 char *iff_read_data(int size);
32 ILboolean iLoadIffInternal(void);
33 
34 
35 /* Define the IFF tags we are looking for in the file. */
36 const ILuint IFF_TAG_CIMG = ('C' << 24) | ('I' << 16) | ('M' << 8) | ('G');
37 const ILuint IFF_TAG_FOR4 = ('F' << 24) | ('O' << 16) | ('R' << 8) | ('4');
38 const ILuint IFF_TAG_TBHD = ('T' << 24) | ('B' << 16) | ('H' << 8) | ('D');
39 const ILuint IFF_TAG_TBMP = ('T' << 24) | ('B' << 16) | ('M' << 8) | ('P');
40 const ILuint IFF_TAG_RGBA = ('R' << 24) | ('G' << 16) | ('B' << 8) | ('A');
41 const ILuint IFF_TAG_CLPZ = ('C' << 24) | ('L' << 16) | ('P' << 8) | ('Z');
42 const ILuint IFF_TAG_ESXY = ('E' << 24) | ('S' << 16) | ('X' << 8) | ('Y');
43 const ILuint IFF_TAG_ZBUF = ('Z' << 24) | ('B' << 16) | ('U' << 8) | ('F');
44 const ILuint IFF_TAG_BLUR = ('B' << 24) | ('L' << 16) | ('U' << 8) | ('R');
45 const ILuint IFF_TAG_BLRT = ('B' << 24) | ('L' << 16) | ('R' << 8) | ('T');
46 const ILuint IFF_TAG_HIST = ('H' << 24) | ('I' << 16) | ('S' << 8) | ('T');
47 
48 // Flags
49 #define RGB_FLAG     (1)
50 #define ALPHA_FLAG   (2)
51 #define ZBUFFER_FLAG (4)
52 
53 // Function for decompress the file.
54 char *iff_decompress_rle(ILuint numBytes, char *compressedData,
55 							ILuint compressedDataSize,
56 							ILuint *compressedStartIndex);
57 
58 char *iffReadUncompressedTile(ILushort width, ILushort height, ILbyte depth);
59 char *iff_decompress_tile_rle(ILushort width, ILushort height, ILushort depth,
60 				 char *compressedData, ILuint compressedDataSize);
61 
62 
63 //! Reads an IFF file
ilLoadIff(const ILstring FileName)64 ILboolean ilLoadIff(const ILstring FileName)
65 {
66 	ILHANDLE iffFile;
67 	ILboolean ret = IL_FALSE;
68 
69 	iffFile = iopenr(FileName);
70 	if (iffFile == NULL) {
71 		ilSetError(IL_COULD_NOT_OPEN_FILE);
72 		return ret;
73 	}
74 	ret = ilLoadIffF(iffFile);
75 	icloser(iffFile);
76 	return ret;
77 }
78 
79 
80 //! Reads an already-opened IFF file
ilLoadIffF(ILHANDLE File)81 ILboolean ilLoadIffF(ILHANDLE File)
82 {
83 	ILuint		FirstPos;
84 	ILboolean	bRet;
85 
86 	iSetInputFile(File);
87 	FirstPos = itell();
88 	bRet = iLoadIffInternal();
89 	iseek(FirstPos, IL_SEEK_SET);
90 
91 	// Lbm files can have the .iff extension as well, so if Iff-loading failed,
92 	//  try to load it as a Lbm.
93 	if (bRet == IL_FALSE)
94 		return ilLoadIlbmF(File);
95 
96 	return bRet;
97 }
98 
99 
100 //! Reads from a memory "lump" that contains an IFF
ilLoadIffL(const void * Lump,ILuint Size)101 ILboolean ilLoadIffL(const void *Lump, ILuint Size)
102 {
103 	ILuint		FirstPos;
104 	ILboolean	bRet;
105 
106 	iSetInputLump(Lump, Size);
107 	FirstPos = itell();
108 	bRet = iLoadIffInternal();
109 	iseek(FirstPos, IL_SEEK_SET);
110 
111 	// Lbm files can have the .iff extension as well, so if Iff-loading failed,
112 	//  try to load it as a Lbm.
113 	if (bRet == IL_FALSE)
114 		return ilLoadIlbmL(Lump, Size);
115 
116 	return IL_TRUE;
117 }
118 
iLoadIffInternal(void)119 ILboolean iLoadIffInternal(void)
120 {
121 	iff_chunk chunkInfo;
122 
123 	// -- Header info.
124 	ILuint width, height;
125 	ILuint flags, compress;
126 	ILushort tiles;
127 
128 	ILenum	format;
129 	ILubyte bpp;
130 
131 	ILboolean tileImageDataFound;
132 
133 	// -- Initialize the top of the chunk stack.
134 	chunkDepth = -1;
135 
136 	// -- File should begin with a FOR4 chunk of type CIMG
137 	chunkInfo = iff_begin_read_chunk();
138 	if (chunkInfo.chunkType != IFF_TAG_CIMG) {
139 		ilSetError(IL_ILLEGAL_FILE_VALUE);
140 		return IL_FALSE;
141 	}
142 
143 
144 	/*
145 	* Read the image header
146 	* OK, we have a FOR4 of type CIMG, look for the following tags
147 	*		FVER
148 	*		TBHD	bitmap header, definition of size, etc.
149 	*		AUTH
150 	*		DATE
151 	*/
152 	while (1) {
153 
154 		chunkInfo = iff_begin_read_chunk();
155 
156 		// -- Right now, the only info we need about the image is in TBHD
157 		// -- so search this level until we find it.
158 		if( chunkInfo.tag == IFF_TAG_TBHD ) {
159 			// -- Header chunk found
160 			width = GetBigUInt();
161 			height = GetBigUInt();
162 			GetBigShort(); // -- Don't support
163 			GetBigShort(); // -- Don't support
164 			flags = GetBigUInt();
165 			GetBigShort(); // -- Don't support
166 			tiles = GetBigUShort();
167 			compress	= GetBigUInt();
168 
169 			iff_end_read_chunk();
170 
171 			if( compress > 1 ) {
172 				ilSetError(IL_ILLEGAL_FILE_VALUE);
173 				return	IL_FALSE;
174 			}
175    				break;
176 		} else
177 			iff_end_read_chunk();
178 	} /* END find TBHD while loop */
179 
180 	if (!(flags & RGB_FLAG)) {
181 		ilSetError(IL_ILLEGAL_FILE_VALUE);
182 		return	IL_FALSE;
183 	}
184 
185 	if (flags & ALPHA_FLAG) {
186 		format = IL_RGBA; bpp = 4;
187 	} else {
188 		format = IL_RGB; bpp = 3;
189 	}
190 
191 	if (!ilTexImage(width, height, 1, bpp, format, IL_UNSIGNED_BYTE, NULL))
192 		return IL_FALSE;
193 
194 	iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
195 
196 	tileImageDataFound = IL_FALSE;
197 
198 	while (!tileImageDataFound) {
199 		ILuint tileImage;
200 		ILuint tileZ;
201 
202 		chunkInfo = iff_begin_read_chunk();
203 
204 		/*
205 		 * OK, we have a FOR4 of type TBMP, (embedded FOR4)
206 		 * look for the following tags
207 		 *		RGBA	color data,	RLE compressed tiles of 32 bbp data
208 		 *		ZBUF	z-buffer data, 32 bit float values
209 		 *		CLPZ	depth map specific, clipping planes, 2 float values
210 		 *		ESXY	depth map specific, eye x-y ratios, 2 float values
211 		 *		HIST
212 		 *		VERS
213 		 *		FOR4 <size>	BLUR (twice embedded FOR4)
214 		 */
215 		if (chunkInfo.chunkType != IFF_TAG_TBMP) {
216 			iff_end_read_chunk();
217 			continue;
218 		}
219 		tileImageDataFound = IL_TRUE;
220 		tileImage = 0;	// Si no RGBA, tileImage = tiles...
221 		if (flags & ZBUFFER_FLAG)
222 			tileZ = 0;
223 		else
224 			tileZ = tiles;
225 
226 		// Read tiles
227 		while ( (tileImage < tiles) || (tileZ < tiles)) {
228 			char	 *tileData;
229 			ILushort x1, x2, y1, y2, tile_width, tile_height;
230 			ILuint remainingDataSize;
231 			ILushort	tile_area;
232 			ILuint	tileCompressed;
233 
234 			chunkInfo = iff_begin_read_chunk();
235 			if ((chunkInfo.tag != IFF_TAG_RGBA) && (chunkInfo.tag != IFF_TAG_ZBUF)) {
236 				ilSetError(IL_ILLEGAL_FILE_VALUE);
237 				return IL_FALSE;
238 			}
239 			x1 = GetBigUShort();	y1 = GetBigUShort();
240 			x2 = GetBigUShort();	y2 = GetBigUShort();
241 
242 			remainingDataSize = chunkInfo.size - 4*sizeof(ILushort);
243 			tile_width = x2 - x1 + 1;
244 			tile_height = y2 - y1 + 1;
245 			tile_area = tile_width * tile_height;
246 
247 			if ((ILint)remainingDataSize >= (tile_width * tile_height * bpp))
248 				tileCompressed = 0;
249 			else
250 				tileCompressed = 1;
251 
252 			if (chunkInfo.tag == IFF_TAG_RGBA) {
253 				if (tileCompressed) {
254 					char	*data = iff_read_data(remainingDataSize);
255 					if (data) {
256 						tileData = iff_decompress_tile_rle(tile_width, tile_height,
257 															bpp, data, remainingDataSize);
258 						ifree(data);
259 					}
260 				} else {
261 					tileData = iffReadUncompressedTile(tile_width, tile_height, bpp);
262 				}
263 
264 				if (tileData) {
265 					// Dump RGBA data to our data structure
266 					ILushort	i;
267 					ILuint		base;
268 					base = bpp*(width * y1 + x1);
269 					for (i = 0; i < tile_height; i++) {
270 						memcpy(&iCurImage->Data[base + bpp*i*width],
271 								&tileData[bpp*i*tile_width],
272 								tile_width*bpp*sizeof(char));
273 					}
274 					ifree(tileData);
275 					tileData = NULL;
276 
277 					iff_end_read_chunk();
278 					tileImage++;
279 				} else
280 					return IL_FALSE;
281 			} else if (chunkInfo.tag == IFF_TAG_ZBUF) {
282 				tileZ++;
283 				iff_end_read_chunk();
284 			}
285 
286 		}
287 	}
288 	//ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE);  // Why was this here?
289 	return ilFixImage();
290 }
291 
292 /*
293  * IFF Chunking Routines.
294  *
295  */
296 
iff_begin_read_chunk()297 iff_chunk iff_begin_read_chunk()
298 {
299 	chunkDepth++;
300 	if (chunkDepth >= CHUNK_STACK_SIZE){
301 		ilSetError(IL_STACK_OVERFLOW);
302 		return chunkStack[0];
303 	}
304 	if (chunkDepth < 0) {
305 		ilSetError(IL_STACK_UNDERFLOW);
306 		return chunkStack[0];
307 	}
308 
309 	chunkStack[chunkDepth].start = itell();
310 	chunkStack[chunkDepth].tag = GetBigInt();
311 	chunkStack[chunkDepth].size = GetBigInt();
312 
313 	if (chunkStack[chunkDepth].tag == IFF_TAG_FOR4) {
314 		// -- We have a form, so read the form type tag as well.
315 		chunkStack[chunkDepth].chunkType = GetBigInt();
316 	} else {
317 		chunkStack[chunkDepth].chunkType = 0;
318 	}
319 
320 	return chunkStack[chunkDepth];
321 }
322 
iff_end_read_chunk()323 void iff_end_read_chunk()
324 {
325 	ILuint	end;
326 	int		part;
327 
328 	end = chunkStack[chunkDepth].start + chunkStack[chunkDepth].size + 8;
329 
330 	if (chunkStack[chunkDepth].chunkType != 0) {
331 		end += 4;
332 	}
333 	// Add padding
334 	part = end % 4;
335 	if (part != 0) {
336 	    end += 4 - part;
337 	}
338 
339 	iseek(end, IL_SEEK_SET);
340 
341 	chunkDepth--;
342 }
343 
iff_read_data(int size)344 char * iff_read_data(int size)
345 {
346 	char *buffer = ialloc(size * sizeof(char));
347 	if (buffer == NULL)
348 		return NULL;
349 
350 	if (iread(buffer, size*sizeof(char), 1) != 1) {
351 		ifree(buffer);
352 		return NULL;
353 	}
354 
355 	return buffer;
356 }
357 
358 /*
359 	IFF decompress functions
360 */
361 
iffReadUncompressedTile(ILushort width,ILushort height,ILbyte depth)362 char *iffReadUncompressedTile(ILushort width, ILushort height, ILbyte depth)
363 {
364 
365 	char	*data = NULL;
366 	char	*iniPixel;
367 	char	*finPixel;
368 	int		i, j;
369 	int		tam = width* height * depth * sizeof(char);
370 
371 	data = ialloc(tam);
372 	if (data == NULL)
373 		return NULL;
374 
375 	if (iread(data, tam, 1) != 1) {
376 		ifree(data);
377 		return NULL;
378 	}
379 
380 	iniPixel = data;
381 	for (i = 0; i < tam / depth; i++) {
382 		finPixel = iniPixel + depth;
383 		for (j = 0; j < (depth /2); j++) {
384 			char aux;
385 			aux = *iniPixel;
386 			*(finPixel--) = *iniPixel;
387 			*(iniPixel++) = aux;
388 		}
389 	}
390 	return data;
391 }
392 
393 
iff_decompress_tile_rle(ILushort width,ILushort height,ILushort depth,char * compressedData,ILuint compressedDataSize)394 char *iff_decompress_tile_rle(ILushort width, ILushort height, ILushort depth,
395 							  char *compressedData, ILuint compressedDataSize)
396 {
397 
398 	char	*channels[4];
399 	char	*data;
400 	int		i, k, row, column;
401 	ILuint	compressedStart = 0;
402 
403 	// Decompress only in RGBA.
404 	if (depth != 4) {
405 		ilSetError(IL_ILLEGAL_FILE_VALUE);
406 		return NULL;
407 	}
408 
409 	for (i = depth-1; i >= 0; --i) {
410 		channels[i] = iff_decompress_rle(width * height, compressedData,
411 				      compressedDataSize, &compressedStart);
412 		if (channels[i] == NULL)
413 			return NULL;
414 	}
415 
416     // Build all the channels from the decompression into an RGBA array.
417 	data = ialloc(width * height * depth * sizeof(char));
418 	if (data == NULL)
419 		return NULL;
420 
421 	for (row = 0; row < height; row++)
422 		for (column = 0; column < width; column++)
423 			for (k = 0; k < depth; k++)
424 				data[depth*(row*width + column) + k] =
425 					channels[k][row*width + column];
426 
427 	ifree(channels[0]); ifree(channels[1]);
428 	ifree(channels[2]); ifree(channels[3]);
429 
430 	return data;
431 }
432 
iff_decompress_rle(ILuint numBytes,char * compressedData,ILuint compressedDataSize,ILuint * compressedStartIndex)433 char *iff_decompress_rle(ILuint numBytes, char *compressedData,
434 							ILuint compressedDataSize,
435 							ILuint *compressedStartIndex)
436 {
437 
438 	char	*data = ialloc(numBytes * sizeof(char));
439 	unsigned char	nextChar, count;
440 	int		i;
441 	ILuint	byteCount = 0;
442 
443 	if (data == NULL)
444 		return NULL;
445 
446 	memset(data, 0, numBytes*sizeof(char));
447 
448 	while (byteCount < numBytes) {
449 		if (*compressedStartIndex >= compressedDataSize)
450 			break;
451 		nextChar = compressedData[*compressedStartIndex];
452 		(*compressedStartIndex)++;
453 		count = (nextChar & 0x7f) + 1;
454 		if ((byteCount + count) > numBytes) break;
455 		if (nextChar & 0x80) {
456 			// Duplication run
457 			nextChar = compressedData[*compressedStartIndex];
458 			(*compressedStartIndex)++;
459 			// assert ((byteCount + count) <= numBytes);
460 			for (i = 0; i < count; i++) {
461 				data[byteCount]= nextChar;
462 				byteCount++;
463 			}
464 		} else {
465 			// Verbatim run
466 			for (i = 0; i < count; i++) {
467 				data[byteCount] = compressedData[*compressedStartIndex];
468 				(*compressedStartIndex)++;
469 				byteCount++;
470 			}
471 		}
472 		//assert(byteCount <= numBytes);
473 	}
474 
475 	return data;
476 
477 }
478 
479 #endif //IL_NO_IFF
480