1 /*---------------------------------------------------------------------------*
2
3 Copyright (C) Nintendo. All rights reserved.
4
5 These coded instructions, statements, and computer programs contain
6 proprietary information of Nintendo of America Inc. and/or Nintendo
7 Company Ltd., and are protected by Federal copyright law. They may
8 not be disclosed to third parties or copied or duplicated in any form,
9 in whole or in part, without the prior written consent of Nintendo.
10
11 *---------------------------------------------------------------------------*/
12 // ------------------------------------------------------------------
13 // tgaReader.cpp
14 // ------------------------------------------------------------------
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <assert.h>
18 #include "tgaReader.h"
19
20 #pragma pack(push,x1) // Byte alignment (8-bit)
21 #pragma pack(1)
22
23 namespace TGAReader
24 {
25 #define INVERTED_BIT (1 << 5)
26
27 typedef struct
28 {
29 unsigned char idSize;
30 unsigned char mapType;
31 unsigned char imageType;
32 unsigned short paletteStart;
33 unsigned short paletteSize;
34 unsigned char paletteEntryDepth;
35 unsigned short x;
36 unsigned short y;
37 unsigned short dwWidth;
38 unsigned short dwHeight;
39 unsigned char colorDepth;
40 unsigned char descriptor;
41 } TGA_HEADER;
42
43 typedef struct
44 {
45 TGA_HEADER hdr;
46 u8 id[256];
47 u8 map[256*4]; // always BGRA8 format
48 u32 mapSize; // # of entries; typically 256
49 } TGA_INFO;
50
51 #pragma pack(pop,x1)
52
53 typedef TU_Error (LoopFunction)(FILE*& pFile, TGA_INFO*& pTGAD, GX2Surface*& pSurface, u32 nMipLevel, u32 nFaceOrSlice, u32 dwWidth, u32 dwHeight);
54 TU_Error GenericLoadFunction(FILE*& pFile, TGA_INFO*& pTGAD, GX2Surface*& pSurface, ChannelFormat channelFormat, TextureDataType textureDataType, LoopFunction fnLoop);
55 bool GetGX2TextureInfo(const TGA_INFO* pTGAD, GX2Surface* pSurface, ChannelFormat channelFormat,
56 TextureDataType textureDataType,MS_CubeFace* pCubeFaceMask);
57 TU_Error SwapFromBottomToTop(u8* pImg, u32 width, u32 height);
58 TU_Error SwapFromRightToLeft(u8* pImg, u32 width, u32 height);
59 TU_Error DecodeRLEImage(FILE*& pFile, TGA_INFO* pTGAD, u8* pImg, u32 width, u32 height, u32 bbp);
60 TU_Error ReadRawImage(FILE*& pFile, TGA_INFO* pTGAD, u8* pImg, u32 width, u32 height, u32 bbp);
61
62 //
63 // Image swap function
64 //
65
SwapFromBottomToTop(u8 * pImg,u32 width,u32 height)66 TU_Error SwapFromBottomToTop(u8* pImg, u32 width, u32 height)
67 {
68 u32 x,y,c;
69 u32 *temp = (u32 *)pImg;
70
71 for(y = 0; y < height/2; y++)
72 {
73 for(x = 0; x < width; x++)
74 {
75 u32 id0 = (width * y + x);
76 u32 id1 = (width * (height-1 - y) + x);
77 c = temp[id0];
78 temp[id0] = temp[id1];
79 temp[id1] = c;
80 }
81 }
82 return PE_OK;
83 }
84
SwapFromRightToLeft(u8 * pImg,u32 width,u32 height)85 TU_Error SwapFromRightToLeft(u8* pImg, u32 width, u32 height)
86 {
87 u32 x,y,c;
88 u32 *temp = (u32 *)pImg;
89
90 assert(temp);
91
92 for(y = 0; y < height; y++)
93 {
94 for(x = 0; x < width/2; x++)
95 {
96 u32 id0 = width * y + x;
97 u32 id1 = width * y + (width-1 - x);
98 c = temp[id0];
99 temp[id0] = temp[id1];
100 temp[id1] = c;
101 }
102 }
103 return PE_OK;
104 }
105
106 //
107 // Read Functions
108 //
109
DecodeRLEImage(FILE * & pFile,TGA_INFO * pTGAD,u8 * pImg,u32 width,u32 height,u32 bpp)110 TU_Error DecodeRLEImage(FILE*& pFile, TGA_INFO* pTGAD, u8* pImg, u32 width, u32 height, u32 bpp)
111 {
112 u32 x = 0;
113 u32 y = 0;
114 u32 offset = 4; // r,g,b,a
115 u32 size = width*height*offset;
116 u32 chunkheader;
117 u32 id;
118 u32 dataType;
119 u32 pixelCounts;
120 u8 red, green, blue, alpha;
121
122 while(y < height)
123 {
124 // Read chunk header data
125 chunkheader = fgetc(pFile);
126 if (chunkheader == EOF) break;
127
128 dataType = chunkheader & 0x80;
129 pixelCounts = chunkheader - dataType + 1;
130
131 // RLE Data
132 if (dataType == 0x80)
133 {
134 u32 data;
135 switch (bpp)
136 {
137 // 8bit
138 case 8:
139 blue = fgetc(pFile);
140 if ((pTGAD->hdr.imageType & 0x03) == 1) {
141 green = pTGAD->map[blue*4+1];
142 red = pTGAD->map[blue*4+2];
143 alpha = pTGAD->map[blue*4+3];
144 blue = pTGAD->map[blue*4+0];
145 } else {
146 green = blue;
147 red = blue;
148 alpha = 0xff;
149 }
150 break;
151 // 16bit
152 case 16:
153 data = (fgetc(pFile)<<8)|fgetc(pFile);
154 blue = (data >> 0) & 0x1f; // B
155 green = (data >> 5) & 0x1f; // G
156 red = (data >> 10) & 0x1f; // R
157 alpha = 255;
158 break;
159 // 24bit
160 case 24:
161 blue = fgetc(pFile);
162 green = fgetc(pFile);
163 red = fgetc(pFile);
164 alpha = 0xff;
165 break;
166 // 32 bit
167 case 32:
168 blue = fgetc(pFile);
169 green = fgetc(pFile);
170 red = fgetc(pFile);
171 alpha = fgetc(pFile);
172 break;
173 default:
174 return PE_Unknown;
175 }
176
177 // pixelCounts same data
178 for (u32 i = 0; i < pixelCounts; i++)
179 {
180 id = (width * y + x)*offset;
181
182 // RGB(A)
183 pImg[id] = red;
184 pImg[id + 1] = green;
185 pImg[id + 2] = blue;
186 pImg[id + 3] = alpha;
187
188 x ++;
189 if(x == width)
190 {
191 x = 0;
192 y++;
193 }
194
195 // For ignoring external footer
196 if (id + 3 >= size - 1) break;
197 }
198 }
199 // Raw Data
200 else
201 {
202 // pixelCounts raw data
203 for (u32 i = 0; i < pixelCounts; i++)
204 {
205 u32 data;
206 switch (bpp)
207 {
208 // 8bit
209 case 8:
210 blue = fgetc(pFile);
211 if ((pTGAD->hdr.imageType & 0x03) == 1) {
212 green = pTGAD->map[blue*4+1];
213 red = pTGAD->map[blue*4+2];
214 alpha = pTGAD->map[blue*4+3];
215 blue = pTGAD->map[blue*4+0];
216 } else {
217 green = blue;
218 red = blue;
219 alpha = 0xff;
220 }
221 break;
222 // 16bit
223 case 16:
224 data = (fgetc(pFile)<<8)|fgetc(pFile);
225 blue = (data >> 0) & 0x1f; // B
226 green = (data >> 5) & 0x1f; // G
227 red = (data >> 10) & 0x1f; // R
228 alpha = 255;
229 break;
230 // 24bit
231 case 24:
232 blue = fgetc(pFile);
233 green = fgetc(pFile);
234 red = fgetc(pFile);
235 alpha = 0xff;
236 break;
237 // 32 bit
238 case 32:
239 blue = fgetc(pFile);
240 green = fgetc(pFile);
241 red = fgetc(pFile);
242 alpha = fgetc(pFile);
243 break;
244 default:
245 return PE_Unknown;
246 }
247
248 id = (width * y + x) * offset;
249
250 // RGB(A)
251 pImg[id] = red;
252 pImg[id + 1] = green;
253 pImg[id + 2] = blue;
254 pImg[id + 3] = alpha;
255
256 x ++;
257 if(x == width)
258 {
259 x = 0;
260 y++;
261 }
262
263 // For ignoring external footer
264 if (id + 3 >= size - 1) break;
265 }
266 }
267 }
268 return PE_OK;
269 }
270
ReadRawImage(FILE * & pFile,TGA_INFO * pTGAD,u8 * pImg,u32 width,u32 height,u32 bpp)271 TU_Error ReadRawImage(FILE*& pFile, TGA_INFO* pTGAD, u8* pImg, u32 width, u32 height, u32 bpp)
272 {
273 u32 x = 0;
274 u32 y = 0;
275 u32 offset = 4; // r,g,b,a
276 u32 size = width*height*offset;
277 u32 id;
278 u8 red, green, blue, alpha;
279
280 while(y < height)
281 {
282 u32 data;
283 switch (bpp)
284 {
285 // 8bit
286 case 8:
287 blue = fgetc(pFile);
288 if ((pTGAD->hdr.imageType & 0x03) == 1) {
289 green = pTGAD->map[blue*4+1];
290 red = pTGAD->map[blue*4+2];
291 alpha = pTGAD->map[blue*4+3];
292 blue = pTGAD->map[blue*4+0];
293 } else {
294 green = blue;
295 red = blue;
296 alpha = 0xff;
297 }
298 break;
299 // 16bit
300 case 16:
301 data = (fgetc(pFile)<<8)|fgetc(pFile);
302 blue = (data >> 0) & 0x1f; // B
303 green = (data >> 5) & 0x1f; // G
304 red = (data >> 10) & 0x1f; // R
305 alpha = 255;
306 break;
307 // 24bit
308 case 24:
309 blue = fgetc(pFile);
310 green = fgetc(pFile);
311 red = fgetc(pFile);
312 alpha = 0xff;
313 break;
314 // 32 bit
315 case 32:
316 blue = fgetc(pFile);
317 green = fgetc(pFile);
318 red = fgetc(pFile);
319 alpha = fgetc(pFile);
320 break;
321 default:
322 return PE_Unknown;
323 }
324
325 id = (width * y + x) * offset;
326
327 // RGBA
328 pImg[id] = red;
329 pImg[id + 1] = green;
330 pImg[id + 2] = blue;
331 pImg[id + 3] = alpha;
332
333 x ++;
334 if(x == width)
335 {
336 x = 0;
337 y++;
338 }
339 }
340 return PE_OK;
341 }
342
GetGX2TextureInfo(const TGA_INFO * pTGAD,GX2Surface * pSurface,ChannelFormat channelFormat,TextureDataType textureDataType,MS_CubeFace * pCubeFaceMask)343 bool GetGX2TextureInfo(const TGA_INFO* pTGAD, GX2Surface* pSurface, ChannelFormat channelFormat,
344 TextureDataType textureDataType,MS_CubeFace* pCubeFaceMask)
345 {
346 u32 cubeFaceMask = 0;
347 pSurface->depth = 1;
348 pSurface->dim = GX2_SURFACE_DIM_2D;
349 pSurface->numMips = 1;
350 pSurface->width = pTGAD->hdr.dwWidth;
351 pSurface->height = pTGAD->hdr.dwHeight;
352 pSurface->tileMode = GX2_TILE_MODE_LINEAR_SPECIAL;
353 pSurface->use = GX2_SURFACE_USE_TEXTURE;
354 pSurface->alignment = 1;
355
356 HMODULE hTexUtilDLL = LoadLibrary(LIB_DLL_TEXUTILS);
357 if ( !hTexUtilDLL )
358 {
359 printf("Failed to lode DLL \"%s\". Exiting.", LIB_DLL_TEXUTILS);
360 FreeLibrary(hTexUtilDLL);
361 exit(1);
362 }
363 PTC2GetSourceSurfaceSize fpTC2GetSourceSurfaceSize = reinterpret_cast<PTC2GetSourceSurfaceSize>(GetProcAddress(hTexUtilDLL, "TC2GetSourceSurfaceSize"));
364
365 switch (channelFormat)
366 {
367 case CF_8bit:
368 switch (textureDataType)
369 {
370 case TDT_XRGB:
371 case TDT_ARGB:
372 pSurface->format = GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM;
373 break;
374 default:
375 FreeLibrary(hTexUtilDLL);
376 return false;
377 }
378 break;
379 default:
380 FreeLibrary(hTexUtilDLL);
381 return false;
382 }
383
384 if (!fpTC2GetSourceSurfaceSize(pSurface))
385 {
386 FreeLibrary(hTexUtilDLL);
387 return false;
388 }
389
390 FreeLibrary(hTexUtilDLL);
391 return true;
392 }
393
GenericLoadFunction(FILE * & pFile,TGA_INFO * & pTGAD,GX2Surface * & pSurface,ChannelFormat channelFormat,TextureDataType textureDataType,LoopFunction fnLoop)394 TU_Error GenericLoadFunction(FILE*& pFile, TGA_INFO*& pTGAD, GX2Surface*& pSurface, ChannelFormat channelFormat,
395 TextureDataType textureDataType, LoopFunction fnLoop)
396 {
397 u32 dwWidth, dwHeight;
398 TU_Error err;
399 MS_CubeFace cubeFaceMask = MS_CF_None;
400
401 memset(pSurface, 0, sizeof(*pSurface));
402 if (!GetGX2TextureInfo(pTGAD, pSurface, channelFormat, textureDataType, &cubeFaceMask))
403 {
404 return PE_Unknown;
405 }
406
407 pSurface->imagePtr = malloc(pSurface->imageSize + pSurface->mipSize);
408 assert(pSurface->imagePtr);
409
410 if (pSurface->mipSize)
411 {
412 pSurface->mipPtr = (void*)((u8*)pSurface->imagePtr + pSurface->imageSize);
413 }
414
415 for(u32 nFace = 0; nFace < pSurface->depth; nFace++)
416 {
417 dwWidth = pTGAD->hdr.dwWidth;
418 dwHeight = pTGAD->hdr.dwHeight;
419 for(u32 nMipLevel = 0; nMipLevel < pSurface->numMips; nMipLevel++)
420 {
421 err = fnLoop(pFile, pTGAD, pSurface, nMipLevel, nFace, dwWidth, dwHeight);
422 if(err != PE_OK)
423 return err;
424 dwWidth = (dwWidth>1) ? (dwWidth>>1) : 1;
425 dwHeight = (dwHeight>1) ? (dwHeight>>1) : 1;
426 }
427 }
428
429 return PE_OK;
430 }
431
GenericFreeFunction(GX2Surface * & pSurface)432 TU_Error GenericFreeFunction(GX2Surface*& pSurface)
433 {
434 if(pSurface == NULL)
435 {
436 assert(pSurface);
437 return PE_Unknown;
438 }
439
440 if(pSurface->imagePtr != NULL)
441 {
442 free(pSurface->imagePtr);
443 pSurface->imagePtr = NULL;
444 pSurface->mipPtr = NULL;
445 }
446
447 return PE_OK;
448 }
449
450 //
451 // RGBA8888 (32bit) & RGB888 (24bit) & R8 (8bit)
452 //
453
LoopRGBA(FILE * & pFile,TGA_INFO * & pTGAD,GX2Surface * & pSurface,u32 nMipLevel,u32 nFaceOrSlice,u32 dwWidth,u32 dwHeight)454 TU_Error LoopRGBA(FILE*& pFile, TGA_INFO*& pTGAD, GX2Surface*& pSurface,
455 u32 nMipLevel, u32 nFaceOrSlice, u32 dwWidth, u32 dwHeight)
456 {
457 MipLevel mipLevel;
458 size_t size = (size_t)(dwWidth * dwHeight * pTGAD->hdr.colorDepth / 8);
459 bool isRLE = (pTGAD->hdr.imageType & 0x08) != 0;
460 bool fromTop = (pTGAD->hdr.descriptor & 0x20) != 0;
461 bool fromLeft = (pTGAD->hdr.descriptor & 0x10) == 0;
462
463 if (!GetMipLevel(pSurface, nMipLevel, nFaceOrSlice, &mipLevel))
464 {
465 return PE_Unknown;
466 }
467
468 // Allocate the permanent buffer and unpack the bitmap data into it
469 if(!mipLevel.pData)
470 {
471 return PE_Unknown;
472 }
473
474 u8* pData = mipLevel.pData;
475
476 // Run-Length Comp Format
477 if(isRLE)
478 {
479 if(DecodeRLEImage(pFile, pTGAD, pData, dwWidth, dwHeight, pTGAD->hdr.colorDepth))
480 {
481 return PE_Unknown;
482 }
483 }
484 // Raw Format
485 else
486 {
487 if(ReadRawImage(pFile, pTGAD, pData, dwWidth, dwHeight, pTGAD->hdr.colorDepth))
488 {
489 return PE_Unknown;
490 }
491 }
492
493 // Swap image
494 if(!fromTop && fromLeft)
495 {
496 SwapFromBottomToTop(pData, dwWidth, dwHeight);
497 }
498 else if(fromTop && !fromLeft)
499 {
500 SwapFromRightToLeft(pData, dwWidth, dwHeight);
501 }
502 else if(!fromTop && !fromLeft)
503 {
504 SwapFromBottomToTop(pData, dwWidth, dwHeight);
505 SwapFromRightToLeft(pData, dwWidth, dwHeight);
506 }
507
508 return PE_OK;
509 }
510
LoadRGBA(FILE * pFile,TGA_INFO * pTGAD,GX2Surface * pSurface)511 TU_Error LoadRGBA(FILE* pFile, TGA_INFO* pTGAD, GX2Surface* pSurface)
512 {
513 TU_Error err = GenericLoadFunction(pFile, pTGAD, pSurface, CF_8bit, TDT_ARGB, LoopRGBA);
514 fclose(pFile);
515 return err;
516 }
517
518 //
519 // Public functions
520 //
521
TGALoadFile(const TCHAR * pszFilename,GX2Surface * pSurface)522 bool TGALoadFile(const TCHAR* pszFilename, GX2Surface* pSurface)
523 {
524 FILE* pFile = NULL;
525 TGA_INFO tgad;
526 u32 i;
527
528 if(_tfopen_s(&pFile, pszFilename, _T("rb")))
529 {
530 return false;
531 }
532
533 // Read header
534 if(fread(&tgad.hdr, sizeof(TGA_HEADER), 1, pFile) != 1)
535 {
536 fclose(pFile);
537 return false;
538 }
539
540 // Check image type
541 if(!tgad.hdr.imageType)
542 {
543 fclose(pFile);
544 return false;
545 }
546
547 // Check id field
548 if(tgad.hdr.idSize > 0)
549 {
550 if(fread(tgad.id, tgad.hdr.idSize, 1, pFile) != 1)
551 {
552 fclose(pFile);
553 return false;
554 }
555 }
556
557 // Check color map
558 // We only handle "true color, max 8 bpc" maps.
559 tgad.mapSize = tgad.hdr.paletteSize;
560 if (tgad.mapSize > 256) tgad.mapSize = 256;
561 // Always set up default map; BGRA format
562 for(i=0; i<256; i++)
563 {
564 tgad.map[i*4+0] = i;
565 tgad.map[i*4+1] = i;
566 tgad.map[i*4+2] = i;
567 tgad.map[i*4+3] = 255;
568 }
569 if(tgad.hdr.mapType)
570 {
571 assert(tgad.hdr.paletteSize);
572 // If present in file, read it in & convert it to 8888
573 u8 *tempMap = (u8*)malloc(tgad.hdr.paletteSize * 4);
574 assert(tempMap);
575 // handle 8, 15, 16, 24, 32 depths
576 u32 byteDepth = (tgad.hdr.paletteEntryDepth+1)/8;
577 if(fread(tempMap, tgad.hdr.paletteSize*byteDepth, 1, pFile) != 1)
578 {
579 free(tempMap);
580 fclose(pFile);
581 return false;
582 }
583 for(i=0; i<tgad.mapSize; i++)
584 {
585 u32 data;
586 if (tgad.hdr.paletteStart+i > 255) break;
587 // order in file is "ARGB" mapped to little endian; ie, "BGRA" for 32-bit
588 // color map order is BGRA
589 switch(byteDepth) {
590 case 1:
591 tgad.map[(tgad.hdr.paletteStart+i)*4+0] = tempMap[i*1];
592 tgad.map[(tgad.hdr.paletteStart+i)*4+1] = tempMap[i*1];
593 tgad.map[(tgad.hdr.paletteStart+i)*4+2] = tempMap[i*1];
594 tgad.map[(tgad.hdr.paletteStart+i)*4+3] = 255;
595 break;
596 case 2:
597 data = (tempMap[i*2+1] << 8) | tempMap[i*2+0];
598 tgad.map[(tgad.hdr.paletteStart+i)*4+0] = (data >> 0) & 0x1f; // B
599 tgad.map[(tgad.hdr.paletteStart+i)*4+1] = (data >> 5) & 0x1f; // G
600 tgad.map[(tgad.hdr.paletteStart+i)*4+2] = (data >> 10) & 0x1f; // R
601 tgad.map[(tgad.hdr.paletteStart+i)*4+3] = 255;
602 break;
603 case 3:
604 tgad.map[(tgad.hdr.paletteStart+i)*4+0] = tempMap[i*3+0];
605 tgad.map[(tgad.hdr.paletteStart+i)*4+1] = tempMap[i*3+1];
606 tgad.map[(tgad.hdr.paletteStart+i)*4+2] = tempMap[i*3+2];
607 tgad.map[(tgad.hdr.paletteStart+i)*4+3] = 255;
608 break;
609 case 4:
610 tgad.map[(tgad.hdr.paletteStart+i)*4+0] = tempMap[i*4+0];
611 tgad.map[(tgad.hdr.paletteStart+i)*4+1] = tempMap[i*4+1];
612 tgad.map[(tgad.hdr.paletteStart+i)*4+2] = tempMap[i*4+2];
613 tgad.map[(tgad.hdr.paletteStart+i)*4+3] = tempMap[i*4+3];
614 break;
615 default:
616 assert(!"bad palette byte depth in tga header");
617 }
618 }
619 free(tempMap);
620 }
621
622 return LoadRGBA(pFile, &tgad, pSurface) == PE_OK;
623 }
624
TGAFree(GX2Surface * pSurface)625 bool TGAFree(GX2Surface* pSurface)
626 {
627 return GenericFreeFunction(pSurface) == PE_OK;
628 }
629
630 } //namespace TGAReader
631