1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 03/07/2009
6 //
7 // Filename: src-IL/src/il_tpl.c
8 //
9 // Description: Reads from a Gamecube Texture Palette (.tpl).
10 //                Specifications were found at
11 //                http://pabut.homeip.net:8000/yagcd/chap14.html.
12 //
13 //-----------------------------------------------------------------------------
14 
15 
16 #include "il_internal.h"
17 #ifndef IL_NO_TPL
18 #include "il_dds.h"
19 
20 
21 typedef struct TPLHEAD
22 {
23 	ILuint	Magic;
24 	ILuint	nTextures;
25 	ILuint	HeaderSize;
26 } TPLHEAD;
27 
28 // Data formats
29 #define TPL_I4		0
30 #define TPL_I8		1
31 #define TPL_IA4		2
32 #define TPL_IA8		3
33 #define TPL_RGB565	4
34 #define TPL_RGB5A3	5
35 #define TPL_RGBA8	6
36 #define TPL_CI4		8
37 #define TPL_CI8		9
38 #define TPL_CI14X2	10
39 #define TPL_CMP		14
40 
41 // Wrapping
42 #define TPL_CLAMP	0
43 #define	TPL_REPEAT	1
44 #define TPL_MIRROR	2
45 
46 // Palette entries
47 #define TPL_PAL_IA8		0
48 #define TPL_PAL_RGB565	1
49 #define TPL_PAL_RGB5A3	2
50 
51 
52 ILboolean iIsValidTpl(void);
53 ILboolean iCheckTpl(TPLHEAD *Header);
54 ILboolean iLoadTplInternal(void);
55 ILboolean TplGetIndexImage(ILimage *Image, ILuint TexOff, ILuint DataFormat);
56 
57 
58 //! Checks if the file specified in FileName is a valid TPL file.
ilIsValidTpl(ILconst_string FileName)59 ILboolean ilIsValidTpl(ILconst_string FileName)
60 {
61 	ILHANDLE	TplFile;
62 	ILboolean	bTpl = IL_FALSE;
63 
64 	if (!iCheckExtension(FileName, IL_TEXT("tpl"))) {
65 		ilSetError(IL_INVALID_EXTENSION);
66 		return bTpl;
67 	}
68 
69 	TplFile = iopenr(FileName);
70 	if (TplFile == NULL) {
71 		ilSetError(IL_COULD_NOT_OPEN_FILE);
72 		return bTpl;
73 	}
74 
75 	bTpl = ilIsValidTplF(TplFile);
76 	icloser(TplFile);
77 
78 	return bTpl;
79 }
80 
81 
82 //! Checks if the ILHANDLE contains a valid TPL file at the current position.
ilIsValidTplF(ILHANDLE File)83 ILboolean ilIsValidTplF(ILHANDLE File)
84 {
85 	ILuint		FirstPos;
86 	ILboolean	bRet;
87 
88 	iSetInputFile(File);
89 	FirstPos = itell();
90 	bRet = iIsValidTpl();
91 	iseek(FirstPos, IL_SEEK_SET);
92 
93 	return bRet;
94 }
95 
96 
97 //! Checks if Lump is a valid TPL lump.
ilIsValidTplL(const void * Lump,ILuint Size)98 ILboolean ilIsValidTplL(const void *Lump, ILuint Size)
99 {
100 	iSetInputLump(Lump, Size);
101 	return iIsValidTpl();
102 }
103 
104 
105 // Internal function used to get the TPL header from the current file.
iGetTplHead(TPLHEAD * Header)106 ILboolean iGetTplHead(TPLHEAD *Header)
107 {
108 	Header->Magic = GetBigUInt();
109 	Header->nTextures = GetBigUInt();
110 	Header->HeaderSize = GetBigUInt();
111 	return IL_TRUE;
112 }
113 
114 
115 // Internal function to get the header and check it.
iIsValidTpl(void)116 ILboolean iIsValidTpl(void)
117 {
118 	TPLHEAD Header;
119 
120 	if (!iGetTplHead(&Header))
121 		return IL_FALSE;
122 	iseek(-12, IL_SEEK_CUR);
123 
124 	return iCheckTpl(&Header);
125 }
126 
127 
128 // Internal function used to check if the HEADER is a valid TPL header.
iCheckTpl(TPLHEAD * Header)129 ILboolean iCheckTpl(TPLHEAD *Header)
130 {
131 	// The file signature is 0x0020AF30.
132 	if (Header->Magic != 0x0020AF30)
133 		return IL_FALSE;
134 	// Only valid header size is 0x0C.
135 	if (Header->HeaderSize != 0x0C)
136 		return IL_FALSE;
137 	// We have to have at least 1 texture.
138 	if (Header->nTextures == 0)
139 		return IL_FALSE;
140 
141 	return IL_TRUE;
142 }
143 
144 
145 //! Reads a TPL file
ilLoadTpl(ILconst_string FileName)146 ILboolean ilLoadTpl(ILconst_string FileName)
147 {
148 	ILHANDLE	TplFile;
149 	ILboolean	bTpl = IL_FALSE;
150 
151 	TplFile = iopenr(FileName);
152 	if (TplFile == NULL) {
153 		ilSetError(IL_COULD_NOT_OPEN_FILE);
154 		return bTpl;
155 	}
156 
157 	bTpl = ilLoadTplF(TplFile);
158 	icloser(TplFile);
159 
160 	return bTpl;
161 }
162 
163 
164 //! Reads an already-opened TPL file
ilLoadTplF(ILHANDLE File)165 ILboolean ilLoadTplF(ILHANDLE File)
166 {
167 	ILuint		FirstPos;
168 	ILboolean	bRet;
169 
170 	iSetInputFile(File);
171 	FirstPos = itell();
172 	bRet = iLoadTplInternal();
173 	iseek(FirstPos, IL_SEEK_SET);
174 
175 	return bRet;
176 }
177 
178 
179 //! Reads from a memory "lump" that contains a TPL
ilLoadTplL(const void * Lump,ILuint Size)180 ILboolean ilLoadTplL(const void *Lump, ILuint Size)
181 {
182 	iSetInputLump(Lump, Size);
183 	return iLoadTplInternal();
184 }
185 
186 
187 // Internal function used to load the TPL.
iLoadTplInternal(void)188 ILboolean iLoadTplInternal(void)
189 {
190 	TPLHEAD		Header;
191 	ILimage		*Image/*, *BaseImage*/;
192 	ILuint		Pos, TexOff, PalOff, DataFormat, Bpp, DataOff, WrapS, WrapT;
193 	ILuint		x, y, xBlock, yBlock, i, j, k, n;
194 	ILenum		Format;
195 	ILushort	Width, Height, ShortPixel;
196 	ILubyte		BytePixel, CompData[8];
197 	Color8888	colours[4], *col;
198 	ILushort	color_0, color_1;
199 	ILuint		bitmask, Select;
200 
201 	if (iCurImage == NULL) {
202 		ilSetError(IL_ILLEGAL_OPERATION);
203 		return IL_FALSE;
204 	}
205 	Image = iCurImage;  // Top-level image
206 
207 	if (!iGetTplHead(&Header))
208 		return IL_FALSE;
209 	if (!iCheckTpl(&Header)) {
210 		ilSetError(IL_INVALID_FILE_HEADER);
211 		return IL_FALSE;
212 	}
213 
214 	// Points to the beginning of the texture header directory.
215 	Pos = itell();
216 
217 	for (n = 0; n < Header.nTextures; n++) {
218 		// Go back to the texture header directory for texture number n+1.
219 		iseek(Pos + n * 8, IL_SEEK_SET);
220 		TexOff = GetBigUInt();
221 		PalOff = GetBigUInt();
222 		// Go to the texture header.
223 		if (iseek(TexOff, IL_SEEK_SET))
224 			return IL_FALSE;
225 
226 		Height = GetBigUShort();
227 		Width = GetBigUShort();
228 		// It looks like files actually have n-1 images, with the nth one having 0 height and width.
229 		if (Width == 0 || Height == 0) {
230 			// If this is our first image, however, we error out.
231 			if (Image == iCurImage) {
232 				ilSetError(IL_ILLEGAL_FILE_VALUE);
233 				return IL_FALSE;
234 			}
235 			// Break out of our for loop and run ilFixImage on the images.
236 			break;
237 		}
238 
239 		DataFormat = GetBigUInt();
240 		TexOff = GetBigUInt();
241 		WrapS = GetBigUInt();
242 		WrapT = GetBigUInt();
243 		if (WrapS == TPL_REPEAT || WrapS == TPL_MIRROR) {
244 			// By the specs, repeated and mirrored textures must have dimensions of power of 2.
245 			if ((Width != ilNextPower2(Width)) || (Height != ilNextPower2(Height))) {
246 				ilSetError(IL_ILLEGAL_FILE_VALUE);
247 				return IL_FALSE;
248 			}
249 		}
250 
251 		// Go to the actual texture data.
252 		if (iseek(TexOff, IL_SEEK_SET))
253 			return IL_FALSE;
254 
255 		switch (DataFormat)
256 		{
257 			case TPL_I4:
258 			case TPL_I8:
259 				Format = IL_LUMINANCE;
260 				Bpp = 1;
261 				break;
262 			case TPL_IA4:
263 			case TPL_IA8:
264 				Format = IL_LUMINANCE_ALPHA;
265 				Bpp = 1;
266 				break;
267 			case TPL_RGB565:
268 				Format = IL_RGB;
269 				Bpp = 3;
270 				break;
271 			case TPL_RGB5A3:
272 				Format = IL_RGBA;
273 				Bpp = 4;
274 				break;
275 			case TPL_RGBA8:
276 				Format = IL_RGBA;
277 				Bpp = 4;
278 				break;
279 			case TPL_CI4:
280 			case TPL_CI8:
281 				Format = IL_COLOR_INDEX;
282 				Bpp = 1;
283 				break;
284 			case TPL_CI14X2:
285 				Format = IL_RGBA;
286 				Bpp = 3;
287 				break;
288 			case TPL_CMP:
289 				Format = IL_RGBA;
290 				Bpp = 4;
291 				break;
292 
293 			default:
294 				ilSetError(IL_FORMAT_NOT_SUPPORTED);
295 				return IL_FALSE;
296 		}
297 
298 		if (Image == iCurImage) {  // This is our first image.
299 			if (!ilTexImage(Width, Height, 1, Bpp, Format, IL_UNSIGNED_BYTE, NULL))
300 				return IL_FALSE;
301 		}
302 		else {
303 			Image->Next = ilNewImageFull(Width, Height, 1, Bpp, Format, IL_UNSIGNED_BYTE, NULL);
304 			if (Image->Next == NULL)
305 				return IL_FALSE;
306 			Image = Image->Next;
307 		}
308 		Image->Origin = IL_ORIGIN_UPPER_LEFT;  // Origins are always fixed here.
309 
310 		switch (DataFormat)
311 		{
312 			case TPL_I4:
313 				// 8x8 tiles of 4-bit intensity values
314 				for (y = 0; y < Image->Height; y += 8) {
315 					for (x = 0; x < Image->Width; x += 8) {
316 						for (yBlock = 0; yBlock < 8; yBlock++) {
317 							if ((y + yBlock) >= Image->Height) {
318 								iseek(8, IL_SEEK_CUR);  // Entire row of pad bytes skipped.
319 								continue;
320 							}
321 							DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
322 							for (xBlock = 0; xBlock < 8; xBlock += 2) {
323 								BytePixel = igetc();
324 								if ((x + xBlock) >= Image->Width)
325 									continue;  // Already read the pad byte.
326 								Image->Data[DataOff] = (BytePixel & 0xF0) | (BytePixel & 0xF0) >> 4;
327 								DataOff++;
328 								// We have to do this check again, so we do not go past the last pixel in the image (ex. 1 pixel wide image).
329 								if ((x + xBlock) >= Image->Width)
330 									continue;  // Already read the pad byte.
331 								Image->Data[DataOff+1] = (BytePixel & 0x0F) << 4 | (BytePixel & 0x0F);
332 								DataOff++;
333 							}
334 						}
335 					}
336 				}
337 				break;
338 
339 			case TPL_I8:
340 				// 8x4 tiles of 8-bit intensity values
341 				for (y = 0; y < Image->Height; y += 4) {
342 					for (x = 0; x < Image->Width; x += 8) {
343 						for (yBlock = 0; yBlock < 4; yBlock++) {
344 							if ((y + yBlock) >= Image->Height) {
345 								iseek(8, IL_SEEK_CUR);  // Entire row of pad bytes skipped.
346 								continue;
347 							}
348 							DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
349 							for (xBlock = 0; xBlock < 8; xBlock++) {
350 								if ((x + xBlock) >= Image->Width) {
351 									igetc();  // Skip the pad byte.
352 									continue;
353 								}
354 								Image->Data[DataOff] = igetc();  // Luminance value
355 								DataOff++;
356 							}
357 						}
358 					}
359 				}
360 				break;
361 
362 			case TPL_IA4:
363 				// 8x4 tiles of 4-bit intensity and 4-bit alpha values
364 				for (y = 0; y < Image->Height; y += 4) {
365 					for (x = 0; x < Image->Width; x += 8) {
366 						for (yBlock = 0; yBlock < 4; yBlock++) {
367 							if ((y + yBlock) >= Image->Height) {
368 								iseek(8, IL_SEEK_CUR);  // Entire row of pad bytes skipped.
369 								continue;
370 							}
371 							DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
372 							for (xBlock = 0; xBlock < 8; xBlock += 2) {
373 								BytePixel = igetc();
374 								if ((x + xBlock) >= Image->Width)
375 									continue;  // Already read the pad byte.
376 								Image->Data[DataOff] = (BytePixel & 0xF0) | (BytePixel & 0xF0) >> 4;
377 								Image->Data[DataOff+1] = (BytePixel & 0x0F) << 4 | (BytePixel & 0x0F);
378 								DataOff += 2;
379 							}
380 						}
381 					}
382 				}
383 				break;
384 
385 			case TPL_IA8:
386 				// 4x4 tiles of 8-bit intensity and 8-bit alpha values
387 				for (y = 0; y < Image->Height; y += 4) {
388 					for (x = 0; x < Image->Width; x += 4) {
389 						for (yBlock = 0; yBlock < 4; yBlock++) {
390 							if ((y + yBlock) >= Image->Height) {
391 								iseek(8, IL_SEEK_CUR);  // Entire row of pad bytes skipped.
392 								continue;
393 							}
394 							DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
395 							for (xBlock = 0; xBlock < 4; xBlock += 2) {
396 								if ((x + xBlock) >= Image->Width) {
397 									iseek(2, IL_SEEK_CUR);  // Skip the pad bytes.
398 									continue;
399 								}
400 								Image->Data[DataOff] = igetc();
401 								Image->Data[DataOff+1] = igetc();
402 								DataOff += 2;
403 							}
404 						}
405 					}
406 				}
407 				break;
408 
409 			case TPL_RGB565:
410 				// 4x4 tiles of RGB565 data
411 				for (y = 0; y < Image->Height; y += 4) {
412 					for (x = 0; x < Image->Width; x += 4) {
413 						for (yBlock = 0; yBlock < 4; yBlock++) {
414 							if ((y + yBlock) >= Image->Height) {
415 								iseek(8, IL_SEEK_CUR);  // Entire row of pad bytes skipped.
416 								continue;
417 							}
418 							DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
419 							for (xBlock = 0; xBlock < 4; xBlock++) {
420 								ShortPixel = GetBigUShort();
421 								if ((x + xBlock) >= Image->Width)
422 									continue;  // Already read the pad byte.
423 								Image->Data[DataOff] = ((ShortPixel & 0xF800) >> 8) | ((ShortPixel & 0xE000) >> 13); // Red
424 								Image->Data[DataOff+1] = ((ShortPixel & 0x7E0) >> 3) | ((ShortPixel & 0x600) >> 9); // Green
425 								Image->Data[DataOff+2] = ((ShortPixel & 0x1f) << 3) | ((ShortPixel & 0x1C) >> 2); // Blue
426 								DataOff += 3;
427 							}
428 						}
429 					}
430 				}
431 				break;
432 
433 			case TPL_RGB5A3:
434 				// 4x4 tiles of either RGB5 or RGB4A3 depending on the MSB. 0x80
435 				for (y = 0; y < Image->Height; y += 4) {
436 					for (x = 0; x < Image->Width; x += 4) {
437 						for (yBlock = 0; yBlock < 4; yBlock++) {
438 							if ((y + yBlock) >= Image->Height) {
439 								iseek(8, IL_SEEK_CUR);  // Entire row of pad bytes skipped.
440 								continue;
441 							}
442 							DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
443 							for (xBlock = 0; xBlock < 4; xBlock++) {
444 								ShortPixel = GetBigUShort();
445 								if ((x + xBlock) >= Image->Width)
446 									continue;  // Already read the pad byte.
447 
448 								if (ShortPixel & 0x8000) {  // Check MSB.
449 									// We have RGB5.
450 									Image->Data[DataOff] = ((ShortPixel & 0x7C00) >> 7) | ((ShortPixel & 0x7000) >> 12); // Red
451 									Image->Data[DataOff+1] = ((ShortPixel & 0x3E0) >> 2) | ((ShortPixel & 0x380) >> 7); // Green
452 									Image->Data[DataOff+2] = ((ShortPixel & 0x1F) << 3) | ((ShortPixel & 0x1C) >> 2); // Blue
453 									Image->Data[DataOff+3] = 0xFF;  // I am just assuming that it is opaque.
454 								}
455 								else {
456 									// We have RGB4A3.
457 									Image->Data[DataOff] = ((ShortPixel & 0x7800) >> 7) | ((ShortPixel & 0x7800) >> 11); // Red
458 									Image->Data[DataOff+1] = ((ShortPixel & 0x0780) >> 3) | ((ShortPixel & 0x0780) >> 7); // Green
459 									Image->Data[DataOff+2] = ((ShortPixel & 0x0078) << 1) | ((ShortPixel & 0x0078) >> 3); // Blue
460 									Image->Data[DataOff+3] = ((ShortPixel & 0x07) << 5) | ((ShortPixel & 0x07) << 2) | (ShortPixel >> 1); // Alpha
461 								}
462 								DataOff += 3;
463 							}
464 						}
465 					}
466 				}
467 				break;
468 
469 			case TPL_RGBA8:
470 				// 4x4 tiles of RGBA data
471 				for (y = 0; y < Image->Height; y += 4) {
472 					for (x = 0; x < Image->Width; x += 4) {
473 						for (yBlock = 0; yBlock < 4; yBlock++) {
474 							// Skip pad bytes at the bottom of the tile if any.
475 							if ((y + yBlock) >= Image->Height) {
476 								iseek(16, IL_SEEK_CUR);  // Entire row of pad bytes skipped
477 								continue;
478 							}
479 
480 							// First it has the AR data.
481 							DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
482 							for (xBlock = 0; xBlock < 4; xBlock++) {
483 								if ((x + xBlock) >= Image->Width) {
484 									iseek(2, IL_SEEK_CUR);  // Skip pad bytes.
485 									continue;
486 								}
487 								Image->Data[DataOff+3] = igetc();  // Alpha
488 								Image->Data[DataOff] = igetc();  // Red
489 								DataOff += 3;
490 							}
491 
492 							// Then it has the GB data.
493 							DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
494 							for (xBlock = 0; xBlock < 4 && x + xBlock < Image->Width; xBlock++) {
495 								if ((x + xBlock) >= Image->Width) {
496 									iseek(2, IL_SEEK_CUR);  // Skip pad bytes.
497 									continue;
498 								}
499 								Image->Data[DataOff+1] = igetc();  // Green
500 								Image->Data[DataOff+2] = igetc();  // Blue
501 								DataOff += 3;
502 							}
503 						}
504 					}
505 				}
506 				break;
507 
508 			case TPL_CI4:
509 			case TPL_CI8:
510 			case TPL_CI14X2:
511 				// Seek to the palette header.
512 				if (iseek(PalOff, IL_SEEK_SET))
513 					return IL_FALSE;
514 				if (!TplGetIndexImage(Image, TexOff, DataFormat))
515 					return IL_FALSE;
516 				break;
517 
518 			case TPL_CMP:
519 				// S3TC 2x2 blocks of 4x4 tiles.  I am assuming that this is DXT1, since it is not specified in the specs.
520 				//  Most of this ended up being copied from il_dds.c, from the DecompressDXT1 function.
521 				//@TODO: Make this/that code a bit more modular.
522 				for (y = 0; y < Image->Height; y += 8) {
523 					for (x = 0; x < Image->Width; x += 8) {
524 						for (yBlock = 0; yBlock < 8 && (y + yBlock) < Image->Height; yBlock += 4) {
525 							for (xBlock = 0; xBlock < 8 && (x + xBlock) < Image->Width; xBlock += 4) {
526 								if (iread(CompData, 1, 8) != 8)
527 									return IL_FALSE;  //@TODO: Need to do any cleanup here?
528 								color_0 = *((ILushort*)CompData);
529 								UShort(&color_0);
530 								color_1 = *((ILushort*)(CompData + 2));
531 								UShort(&color_1);
532 								DxtcReadColor(color_0, colours);
533 								DxtcReadColor(color_1, colours + 1);
534 								bitmask = ((ILuint*)CompData)[1];
535 								UInt(&bitmask);
536 
537 								if (color_0 > color_1) {
538 									// Four-color block: derive the other two colors.
539 									// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
540 									// These 2-bit codes correspond to the 2-bit fields
541 									// stored in the 64-bit block.
542 									colours[2].b = (2 * colours[0].b + colours[1].b + 1) / 3;
543 									colours[2].g = (2 * colours[0].g + colours[1].g + 1) / 3;
544 									colours[2].r = (2 * colours[0].r + colours[1].r + 1) / 3;
545 									//colours[2].a = 0xFF;
546 
547 									colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
548 									colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
549 									colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
550 									colours[3].a = 0xFF;
551 								}
552 								else {
553 									// Three-color block: derive the other color.
554 									// 00 = color_0,  01 = color_1,  10 = color_2,
555 									// 11 = transparent.
556 									// These 2-bit codes correspond to the 2-bit fields
557 									// stored in the 64-bit block.
558 									colours[2].b = (colours[0].b + colours[1].b) / 2;
559 									colours[2].g = (colours[0].g + colours[1].g) / 2;
560 									colours[2].r = (colours[0].r + colours[1].r) / 2;
561 									//colours[2].a = 0xFF;
562 
563 									colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
564 									colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
565 									colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
566 									colours[3].a = 0x00;
567 								}
568 
569 								for (j = 0, k = 0; j < 4; j++) {
570 									for (i = 0; i < 4; i++, k++) {
571 										Select = (bitmask & (0x03 << k*2)) >> k*2;
572 										col = &colours[Select];
573 
574 										if (((x + xBlock + i) < Image->Width) && ((y + yBlock + j) < Image->Height)) {
575 											DataOff = (y + yBlock + j) * Image->Bps + (x + xBlock + i) * Image->Bpp;
576 											Image->Data[DataOff + 0] = col->r;
577 											Image->Data[DataOff + 1] = col->g;
578 											Image->Data[DataOff + 2] = col->b;
579 											Image->Data[DataOff + 3] = col->a;
580 										}
581 									}
582 								}
583 							}
584 						}
585 					}
586 				}
587 				break;
588 		}
589 	}
590 
591 	return ilFixImage();
592 }
593 
594 
TplGetIndexImage(ILimage * Image,ILuint TexOff,ILuint DataFormat)595 ILboolean TplGetIndexImage(ILimage *Image, ILuint TexOff, ILuint DataFormat)
596 {
597 	ILushort	NumPal, ShortPixel;
598 	ILubyte		LumVal, BytePixel;
599 	ILuint		PalFormat, PalOff, PalBpp, DataOff;
600 	ILuint		x, y, xBlock, yBlock, i;
601 
602 	NumPal = GetBigUShort();
603 	iseek(2, IL_SEEK_CUR);  // Do we need to do anything with the 'unpacked' entry?  I see nothing in the specs about it.
604 	PalFormat = GetBigUInt();
605 
606 	// Now we have to find out where the actual palette data is stored.
607 	//@TODO: Do we need to set any errors here?
608 	PalOff = GetBigUInt();
609 	if (iseek(PalOff, IL_SEEK_SET))
610 		return IL_FALSE;
611 
612 	switch (PalFormat)
613 	{
614 		case TPL_PAL_IA8:
615 			Image->Pal.Palette = (ILubyte*)ialloc(NumPal * 4);
616 			if (Image->Pal.Palette == NULL)
617 				return IL_FALSE;
618 			Image->Pal.PalType = IL_PAL_RGBA32;  //@TODO: Use this format natively.
619 			Image->Pal.PalSize = NumPal * 4;
620 			PalBpp = 4;
621 
622 			for (i = 0; i < NumPal; i++) {
623 				LumVal = igetc();
624 				//@TODO: Do proper conversion of luminance, or support this format natively.
625 				Image->Pal.Palette[i * 4] = LumVal;  // Assign the luminance value.
626 				Image->Pal.Palette[i * 4 + 1] = LumVal;
627 				Image->Pal.Palette[i * 4 + 2] = LumVal;
628 				Image->Pal.Palette[i * 4 + 3] = igetc();  // Get alpha value.
629 			}
630 			break;
631 
632 		case TPL_PAL_RGB565:
633 			Image->Pal.Palette = (ILubyte*)ialloc(NumPal * 3);
634 			if (Image->Pal.Palette == NULL)
635 				return IL_FALSE;
636 			Image->Pal.PalType = IL_PAL_RGB24;
637 			Image->Pal.PalSize = NumPal * 3;
638 			PalBpp = 3;
639 
640 			for (i = 0; i < NumPal; i++) {
641 				ShortPixel = GetBigUShort();
642 				// This is mostly the same code as in the TPL_RGB565 case.
643 				Image->Pal.Palette[i*3] = ((ShortPixel & 0xF800) >> 8) | ((ShortPixel & 0xE000) >> 13); // Red
644 				Image->Pal.Palette[i*3+1] = ((ShortPixel & 0x7E0) >> 3) | ((ShortPixel & 0x600) >> 9); // Green
645 				Image->Pal.Palette[i*3+2] = ((ShortPixel & 0x1f) << 3) | ((ShortPixel & 0x1C) >> 2); // Blue
646 			}
647 			break;
648 
649 		case TPL_PAL_RGB5A3:
650 			Image->Pal.Palette = (ILubyte*)ialloc(NumPal * 4);
651 			if (Image->Pal.Palette == NULL)
652 				return IL_FALSE;
653 			Image->Pal.PalType = IL_PAL_RGBA32;
654 			Image->Pal.PalSize = NumPal * 4;
655 			PalBpp = 4;
656 
657 			for (i = 0; i < NumPal; i++) {
658 				ShortPixel = GetBigUShort();
659 				// This is mostly the same code as in the TPL_RGB565 case.
660 				if (ShortPixel & 0x8000) {  // Check MSB.
661 					// We have RGB5.
662 					Image->Pal.Palette[i*4] = ((ShortPixel & 0x7C00) >> 7) | ((ShortPixel & 0x7000) >> 12); // Red
663 					Image->Pal.Palette[i*4+1] = ((ShortPixel & 0x3E0) >> 2) | ((ShortPixel & 0x380) >> 7); // Green
664 					Image->Pal.Palette[i*4+2] = ((ShortPixel & 0x1F) << 3) | ((ShortPixel & 0x1C) >> 2); // Blue
665 					Image->Pal.Palette[i*4+3] = 0xFF;  // I am just assuming that it is opaque.
666 				}
667 				else {
668 					// We have RGB4A3.
669 					Image->Pal.Palette[i*4] = ((ShortPixel & 0x7800) >> 7) | ((ShortPixel & 0x7800) >> 11); // Red
670 					Image->Pal.Palette[i*4+1] = ((ShortPixel & 0x0780) >> 3) | ((ShortPixel & 0x0780) >> 7); // Green
671 					Image->Pal.Palette[i*4+2] = ((ShortPixel & 0x0078) << 1) | ((ShortPixel & 0x0078) >> 3); // Blue
672 					Image->Pal.Palette[i*4+3] = ((ShortPixel & 0x07) << 5) | ((ShortPixel & 0x07) << 2) | (ShortPixel >> 1); // Alpha
673 				}
674 			}
675 			break;
676 
677 		default:
678 			ilSetError(IL_ILLEGAL_FILE_VALUE);
679 			return IL_FALSE;
680 	}
681 
682 	// Go back to the texture data.
683 	if (iseek(TexOff, IL_SEEK_SET))
684 		return IL_FALSE;
685 
686 	switch (DataFormat)
687 	{
688 		case TPL_CI4:
689 			// 8x8 tiles of 4-bit color indices
690 			//  This is the exact same code as the TPL_I4 case.
691 			for (y = 0; y < Image->Height; y += 8) {
692 				for (x = 0; x < Image->Width; x += 8) {
693 					for (yBlock = 0; yBlock < 8; yBlock++) {
694 						if ((y + yBlock) >= Image->Height) {
695 							iseek(8, IL_SEEK_CUR);  // Entire row of pad bytes skipped.
696 							continue;
697 						}
698 						DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
699 						for (xBlock = 0; xBlock < 8; xBlock += 2) {
700 							BytePixel = igetc();
701 							if ((x + xBlock) >= Image->Width)
702 								continue;  // Already read the pad byte.
703 							Image->Data[DataOff] = (BytePixel & 0xF0) | (BytePixel & 0xF0) >> 4;
704 							DataOff++;
705 							// We have to do this check again, so we do not go past the last pixel in the image (ex. 1 pixel wide image).
706 							if ((x + xBlock) >= Image->Width)
707 								continue;  // Already read the pad byte.
708 							Image->Data[DataOff+1] = (BytePixel & 0x0F) << 4 | (BytePixel & 0x0F);
709 							DataOff++;
710 						}
711 					}
712 				}
713 			}
714 			break;
715 
716 		case TPL_CI8:
717 			// 8x4 tiles of 8-bit intensity values
718 			//  This is the exact same code as the TPL_I8 case.
719 			for (y = 0; y < Image->Height; y += 4) {
720 				for (x = 0; x < Image->Width; x += 8) {
721 					for (yBlock = 0; yBlock < 4; yBlock++) {
722 						if ((y + yBlock) >= Image->Height) {
723 							iseek(8, IL_SEEK_CUR);  // Entire row of pad bytes skipped.
724 							continue;
725 						}
726 						DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
727 						for (xBlock = 0; xBlock < 8; xBlock++) {
728 							if ((x + xBlock) >= Image->Width) {
729 								igetc();  // Skip the pad byte.
730 								continue;
731 							}
732 							Image->Data[DataOff] = igetc();  // Color index
733 							DataOff++;
734 						}
735 					}
736 				}
737 			}
738 			break;
739 
740 
741 		//@TODO: Convert to more formats than just RGBA.
742 		case TPL_CI14X2:
743 			// 4x4 tiles of 14-bit indices into a palette.  Not supported at all in DevIL, since
744 			//  it has a huge palette (> 256 colors).  Convert to RGBA.
745 			for (y = 0; y < Image->Height; y += 4) {
746 				for (x = 0; x < Image->Width; x += 4) {
747 					for (yBlock = 0; yBlock < 4; yBlock++) {
748 						if ((y + yBlock) >= Image->Height) {
749 							iseek(8, IL_SEEK_CUR);  // Entire row of pad bytes skipped.
750 							continue;
751 						}
752 						DataOff = Image->Bps * (y + yBlock) + Image->Bpp * x;
753 						for (xBlock = 0; xBlock < 4; xBlock++) {
754 							if ((x + xBlock) >= Image->Width) {
755 								GetBigUShort();  // Skip the pad short.
756 								continue;
757 							}
758 							ShortPixel = GetBigUShort();
759 							ShortPixel >>= 2;  // Lower 2 bits are padding bits.
760 							Image->Data[DataOff] = Image->Pal.Palette[ShortPixel * PalBpp];
761 							Image->Data[DataOff+1] = Image->Pal.Palette[ShortPixel * PalBpp + 1];
762 							Image->Data[DataOff+2] = Image->Pal.Palette[ShortPixel * PalBpp + 2];
763 							if (PalFormat == TPL_PAL_RGB565)
764 								Image->Data[DataOff+3] = 0xFF;
765 							else
766 								Image->Data[DataOff+3] = Image->Pal.Palette[ShortPixel * PalBpp + 3];
767 							DataOff++;
768 						}
769 					}
770 				}
771 			}
772 			// Get rid of the palette, since we no longer need it.
773 			ifree(Image->Pal.Palette);
774 			Image->Pal.PalType = IL_PAL_NONE;
775 			Image->Pal.PalSize = 0;
776 			break;
777 
778 	}
779 
780 	return IL_TRUE;
781 }
782 #endif//IL_NO_TPL
783