/*---------------------------------------------------------------------------* Copyright (C) 2010-2011 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. *---------------------------------------------------------------------------*/ // ------------------------------------------------------------------ // tgaReader.cpp // ------------------------------------------------------------------ #include #include #include #include "tgaReader.h" #pragma pack(push,x1) // Byte alignment (8-bit) #pragma pack(1) namespace TGAReader { #define INVERTED_BIT (1 << 5) typedef struct { unsigned char idSize; unsigned char mapType; unsigned char imageType; unsigned short paletteStart; unsigned short paletteSize; unsigned char paletteEntryDepth; unsigned short x; unsigned short y; unsigned short dwWidth; unsigned short dwHeight; unsigned char colorDepth; unsigned char descriptor; } TGA_HEADER; typedef struct { TGA_HEADER hdr; u8 id[256]; u8 map[256*4]; // always BGRA8 format u32 mapSize; // # of entries; typically 256 } TGA_INFO; #pragma pack(pop,x1) typedef TU_Error (LoopFunction)(FILE*& pFile, TGA_INFO*& pTGAD, GX2Surface*& pSurface, u32 nMipLevel, u32 nFaceOrSlice, u32 dwWidth, u32 dwHeight); TU_Error GenericLoadFunction(FILE*& pFile, TGA_INFO*& pTGAD, GX2Surface*& pSurface, ChannelFormat channelFormat, TextureDataType textureDataType, LoopFunction fnLoop); bool GetGX2TextureInfo(const TGA_INFO* pTGAD, GX2Surface* pSurface, ChannelFormat channelFormat, TextureDataType textureDataType,MS_CubeFace* pCubeFaceMask); TU_Error SwapFromBottomToTop(u8* pImg, u32 width, u32 height); TU_Error SwapFromRightToLeft(u8* pImg, u32 width, u32 height); TU_Error DecodeRLEImage(FILE*& pFile, TGA_INFO* pTGAD, u8* pImg, u32 width, u32 height, u32 bbp); TU_Error ReadRawImage(FILE*& pFile, TGA_INFO* pTGAD, u8* pImg, u32 width, u32 height, u32 bbp); // // Image swap function // TU_Error SwapFromBottomToTop(u8* pImg, u32 width, u32 height) { u32 x,y,c; u32 *temp = (u32 *)pImg; for(y = 0; y < height/2; y++) { for(x = 0; x < width; x++) { u32 id0 = (width * y + x); u32 id1 = (width * (height-1 - y) + x); c = temp[id0]; temp[id0] = temp[id1]; temp[id1] = c; } } return PE_OK; } TU_Error SwapFromRightToLeft(u8* pImg, u32 width, u32 height) { u32 x,y,c; u32 *temp = (u32 *)pImg; assert(temp); for(y = 0; y < height; y++) { for(x = 0; x < width/2; x++) { u32 id0 = width * y + x; u32 id1 = width * y + (width-1 - x); c = temp[id0]; temp[id0] = temp[id1]; temp[id1] = c; } } return PE_OK; } // // Read Functions // TU_Error DecodeRLEImage(FILE*& pFile, TGA_INFO* pTGAD, u8* pImg, u32 width, u32 height, u32 bpp) { u32 x = 0; u32 y = 0; u32 offset = 4; // r,g,b,a u32 size = width*height*offset; u32 chunkheader; u32 id; u32 dataType; u32 pixelCounts; u8 red, green, blue, alpha; while(y < height) { // Read chunk header data chunkheader = fgetc(pFile); if (chunkheader == EOF) break; dataType = chunkheader & 0x80; pixelCounts = chunkheader - dataType + 1; // RLE Data if (dataType == 0x80) { u32 data; switch (bpp) { // 8bit case 8: blue = fgetc(pFile); if ((pTGAD->hdr.imageType & 0x03) == 1) { green = pTGAD->map[blue*4+1]; red = pTGAD->map[blue*4+2]; alpha = pTGAD->map[blue*4+3]; blue = pTGAD->map[blue*4+0]; } else { green = blue; red = blue; alpha = 0xff; } break; // 16bit case 16: data = (fgetc(pFile)<<8)|fgetc(pFile); blue = (data >> 0) & 0x1f; // B green = (data >> 5) & 0x1f; // G red = (data >> 10) & 0x1f; // R alpha = 255; break; // 24bit case 24: blue = fgetc(pFile); green = fgetc(pFile); red = fgetc(pFile); alpha = 0xff; break; // 32 bit case 32: blue = fgetc(pFile); green = fgetc(pFile); red = fgetc(pFile); alpha = fgetc(pFile); break; default: return PE_Unknown; } // pixelCounts same data for (u32 i = 0; i < pixelCounts; i++) { id = (width * y + x)*offset; // RGB(A) pImg[id] = red; pImg[id + 1] = green; pImg[id + 2] = blue; pImg[id + 3] = alpha; x ++; if(x == width) { x = 0; y++; } // For ignoring external footer if (id + 3 >= size - 1) break; } } // Raw Data else { // pixelCounts raw data for (u32 i = 0; i < pixelCounts; i++) { u32 data; switch (bpp) { // 8bit case 8: blue = fgetc(pFile); if ((pTGAD->hdr.imageType & 0x03) == 1) { green = pTGAD->map[blue*4+1]; red = pTGAD->map[blue*4+2]; alpha = pTGAD->map[blue*4+3]; blue = pTGAD->map[blue*4+0]; } else { green = blue; red = blue; alpha = 0xff; } break; // 16bit case 16: data = (fgetc(pFile)<<8)|fgetc(pFile); blue = (data >> 0) & 0x1f; // B green = (data >> 5) & 0x1f; // G red = (data >> 10) & 0x1f; // R alpha = 255; break; // 24bit case 24: blue = fgetc(pFile); green = fgetc(pFile); red = fgetc(pFile); alpha = 0xff; break; // 32 bit case 32: blue = fgetc(pFile); green = fgetc(pFile); red = fgetc(pFile); alpha = fgetc(pFile); break; default: return PE_Unknown; } id = (width * y + x) * offset; // RGB(A) pImg[id] = red; pImg[id + 1] = green; pImg[id + 2] = blue; pImg[id + 3] = alpha; x ++; if(x == width) { x = 0; y++; } // For ignoring external footer if (id + 3 >= size - 1) break; } } } return PE_OK; } TU_Error ReadRawImage(FILE*& pFile, TGA_INFO* pTGAD, u8* pImg, u32 width, u32 height, u32 bpp) { u32 x = 0; u32 y = 0; u32 offset = 4; // r,g,b,a u32 size = width*height*offset; u32 id; u8 red, green, blue, alpha; while(y < height) { u32 data; switch (bpp) { // 8bit case 8: blue = fgetc(pFile); if ((pTGAD->hdr.imageType & 0x03) == 1) { green = pTGAD->map[blue*4+1]; red = pTGAD->map[blue*4+2]; alpha = pTGAD->map[blue*4+3]; blue = pTGAD->map[blue*4+0]; } else { green = blue; red = blue; alpha = 0xff; } break; // 16bit case 16: data = (fgetc(pFile)<<8)|fgetc(pFile); blue = (data >> 0) & 0x1f; // B green = (data >> 5) & 0x1f; // G red = (data >> 10) & 0x1f; // R alpha = 255; break; // 24bit case 24: blue = fgetc(pFile); green = fgetc(pFile); red = fgetc(pFile); alpha = 0xff; break; // 32 bit case 32: blue = fgetc(pFile); green = fgetc(pFile); red = fgetc(pFile); alpha = fgetc(pFile); break; default: return PE_Unknown; } id = (width * y + x) * offset; // RGBA pImg[id] = red; pImg[id + 1] = green; pImg[id + 2] = blue; pImg[id + 3] = alpha; x ++; if(x == width) { x = 0; y++; } } return PE_OK; } bool GetGX2TextureInfo(const TGA_INFO* pTGAD, GX2Surface* pSurface, ChannelFormat channelFormat, TextureDataType textureDataType,MS_CubeFace* pCubeFaceMask) { u32 cubeFaceMask = 0; pSurface->depth = 1; pSurface->dim = GX2_SURFACE_DIM_2D; pSurface->numMips = 1; pSurface->width = pTGAD->hdr.dwWidth; pSurface->height = pTGAD->hdr.dwHeight; pSurface->tileMode = GX2_TILE_MODE_LINEAR_SPECIAL; pSurface->use = GX2_SURFACE_USE_TEXTURE; pSurface->alignment = 1; HMODULE hTexUtilDLL = LoadLibrary(LIB_DLL_TEXUTILS); if ( !hTexUtilDLL ) { printf("Failed to lode DLL \"%s\". Exiting.", LIB_DLL_TEXUTILS); FreeLibrary(hTexUtilDLL); exit(1); } PTC2GetSourceSurfaceSize fpTC2GetSourceSurfaceSize = reinterpret_cast(GetProcAddress(hTexUtilDLL, "TC2GetSourceSurfaceSize")); switch (channelFormat) { case CF_8bit: switch (textureDataType) { case TDT_XRGB: case TDT_ARGB: pSurface->format = GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM; break; default: FreeLibrary(hTexUtilDLL); return false; } break; default: FreeLibrary(hTexUtilDLL); return false; } if (!fpTC2GetSourceSurfaceSize(pSurface)) { FreeLibrary(hTexUtilDLL); return false; } FreeLibrary(hTexUtilDLL); return true; } TU_Error GenericLoadFunction(FILE*& pFile, TGA_INFO*& pTGAD, GX2Surface*& pSurface, ChannelFormat channelFormat, TextureDataType textureDataType, LoopFunction fnLoop) { u32 dwWidth, dwHeight; TU_Error err; MS_CubeFace cubeFaceMask = MS_CF_None; memset(pSurface, 0, sizeof(*pSurface)); if (!GetGX2TextureInfo(pTGAD, pSurface, channelFormat, textureDataType, &cubeFaceMask)) { return PE_Unknown; } pSurface->imagePtr = malloc(pSurface->imageSize + pSurface->mipSize); assert(pSurface->imagePtr); if (pSurface->mipSize) { pSurface->mipPtr = (void*)((u8*)pSurface->imagePtr + pSurface->imageSize); } for(u32 nFace = 0; nFace < pSurface->depth; nFace++) { dwWidth = pTGAD->hdr.dwWidth; dwHeight = pTGAD->hdr.dwHeight; for(u32 nMipLevel = 0; nMipLevel < pSurface->numMips; nMipLevel++) { err = fnLoop(pFile, pTGAD, pSurface, nMipLevel, nFace, dwWidth, dwHeight); if(err != PE_OK) return err; dwWidth = (dwWidth>1) ? (dwWidth>>1) : 1; dwHeight = (dwHeight>1) ? (dwHeight>>1) : 1; } } return PE_OK; } TU_Error GenericFreeFunction(GX2Surface*& pSurface) { if(pSurface == NULL) { assert(pSurface); return PE_Unknown; } if(pSurface->imagePtr != NULL) { free(pSurface->imagePtr); pSurface->imagePtr = NULL; pSurface->mipPtr = NULL; } return PE_OK; } // // RGBA8888 (32bit) & RGB888 (24bit) & R8 (8bit) // TU_Error LoopRGBA(FILE*& pFile, TGA_INFO*& pTGAD, GX2Surface*& pSurface, u32 nMipLevel, u32 nFaceOrSlice, u32 dwWidth, u32 dwHeight) { MipLevel mipLevel; size_t size = (size_t)(dwWidth * dwHeight * pTGAD->hdr.colorDepth / 8); bool isRLE = (pTGAD->hdr.imageType & 0x08) != 0; bool fromTop = (pTGAD->hdr.descriptor & 0x20) != 0; bool fromLeft = (pTGAD->hdr.descriptor & 0x10) == 0; if (!GetMipLevel(pSurface, nMipLevel, nFaceOrSlice, &mipLevel)) { return PE_Unknown; } // Allocate the permanent buffer and unpack the bitmap data into it if(!mipLevel.pData) { return PE_Unknown; } u8* pData = mipLevel.pData; // Run-Length Comp Format if(isRLE) { if(DecodeRLEImage(pFile, pTGAD, pData, dwWidth, dwHeight, pTGAD->hdr.colorDepth)) { return PE_Unknown; } } // Raw Format else { if(ReadRawImage(pFile, pTGAD, pData, dwWidth, dwHeight, pTGAD->hdr.colorDepth)) { return PE_Unknown; } } // Swap image if(!fromTop && fromLeft) { SwapFromBottomToTop(pData, dwWidth, dwHeight); } else if(fromTop && !fromLeft) { SwapFromRightToLeft(pData, dwWidth, dwHeight); } else if(!fromTop && !fromLeft) { SwapFromBottomToTop(pData, dwWidth, dwHeight); SwapFromRightToLeft(pData, dwWidth, dwHeight); } return PE_OK; } TU_Error LoadRGBA(FILE* pFile, TGA_INFO* pTGAD, GX2Surface* pSurface) { TU_Error err = GenericLoadFunction(pFile, pTGAD, pSurface, CF_8bit, TDT_ARGB, LoopRGBA); fclose(pFile); return err; } // // Public functions // bool TGALoadFile(const TCHAR* pszFilename, GX2Surface* pSurface) { FILE* pFile = NULL; TGA_INFO tgad; u32 i; if(_tfopen_s(&pFile, pszFilename, _T("rb"))) { return false; } // Read header if(fread(&tgad.hdr, sizeof(TGA_HEADER), 1, pFile) != 1) { fclose(pFile); return false; } // Check image type if(!tgad.hdr.imageType) { fclose(pFile); return false; } // Check id field if(tgad.hdr.idSize > 0) { if(fread(tgad.id, tgad.hdr.idSize, 1, pFile) != 1) { fclose(pFile); return false; } } // Check color map // We only handle "true color, max 8 bpc" maps. tgad.mapSize = tgad.hdr.paletteSize; if (tgad.mapSize > 256) tgad.mapSize = 256; // Always set up default map; BGRA format for(i=0; i<256; i++) { tgad.map[i*4+0] = i; tgad.map[i*4+1] = i; tgad.map[i*4+2] = i; tgad.map[i*4+3] = 255; } if(tgad.hdr.mapType) { assert(tgad.hdr.paletteSize); // If present in file, read it in & convert it to 8888 u8 *tempMap = (u8*)malloc(tgad.hdr.paletteSize * 4); assert(tempMap); // handle 8, 15, 16, 24, 32 depths u32 byteDepth = (tgad.hdr.paletteEntryDepth+1)/8; if(fread(tempMap, tgad.hdr.paletteSize*byteDepth, 1, pFile) != 1) { free(tempMap); fclose(pFile); return false; } for(i=0; i 255) break; // order in file is "ARGB" mapped to little endian; ie, "BGRA" for 32-bit // color map order is BGRA switch(byteDepth) { case 1: tgad.map[(tgad.hdr.paletteStart+i)*4+0] = tempMap[i*1]; tgad.map[(tgad.hdr.paletteStart+i)*4+1] = tempMap[i*1]; tgad.map[(tgad.hdr.paletteStart+i)*4+2] = tempMap[i*1]; tgad.map[(tgad.hdr.paletteStart+i)*4+3] = 255; break; case 2: data = (tempMap[i*2+1] << 8) | tempMap[i*2+0]; tgad.map[(tgad.hdr.paletteStart+i)*4+0] = (data >> 0) & 0x1f; // B tgad.map[(tgad.hdr.paletteStart+i)*4+1] = (data >> 5) & 0x1f; // G tgad.map[(tgad.hdr.paletteStart+i)*4+2] = (data >> 10) & 0x1f; // R tgad.map[(tgad.hdr.paletteStart+i)*4+3] = 255; break; case 3: tgad.map[(tgad.hdr.paletteStart+i)*4+0] = tempMap[i*3+0]; tgad.map[(tgad.hdr.paletteStart+i)*4+1] = tempMap[i*3+1]; tgad.map[(tgad.hdr.paletteStart+i)*4+2] = tempMap[i*3+2]; tgad.map[(tgad.hdr.paletteStart+i)*4+3] = 255; break; case 4: tgad.map[(tgad.hdr.paletteStart+i)*4+0] = tempMap[i*4+0]; tgad.map[(tgad.hdr.paletteStart+i)*4+1] = tempMap[i*4+1]; tgad.map[(tgad.hdr.paletteStart+i)*4+2] = tempMap[i*4+2]; tgad.map[(tgad.hdr.paletteStart+i)*4+3] = tempMap[i*4+3]; break; default: assert(!"bad palette byte depth in tga header"); } } free(tempMap); } return LoadRGBA(pFile, &tgad, pSurface) == PE_OK; } bool TGAFree(GX2Surface* pSurface) { return GenericFreeFunction(pSurface) == PE_OK; } } //namespace TGAReader