1 /*---------------------------------------------------------------------------*
2 
3   Copyright (C) 2010-2011 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