/*---------------------------------------------------------------------------* Copyright (C) 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. *---------------------------------------------------------------------------*/ #include "types.h" #include #include #include #include #include #include #include "windows/gx2.h" #include "cafe/gfd.h" #include "gfdFile.h" #define GTX_DEFAULT_FILENAME "out.gtx" // ------------------------------------------------------------ // GFD specific Surface structures to repack structure between 32 bit and 64 bit typedef struct _GFDSurface { GX2SurfaceDim dim; u32 width; u32 height; u32 depth; u32 numMips; GX2SurfaceFormat format; GX2AAMode aa; GX2SurfaceUse use; u32 imageSize; u32 imagePtr; u32 mipSize; u32 mipPtr; GX2TileMode tileMode; u32 swizzle; u32 alignment; u32 pitch; u32 mipOffset[ 13 ]; } GFDSurface; // GFD specific Texture structures to repack structure between 32 bit and 64 bit typedef struct _GFDTexture { GFDSurface surface; u32 viewFirstMip; u32 viewNumMips; u32 viewFirstSlice; u32 viewNumSlices; GX2CompSel compSel; u32 _regs[GX2_NUM_TEXTURE_REGISTERS]; } GFDTexture; /// Repack a texture from a (possibly) 64-bit structure to a 32-bit structure. /// We output a GX2Texture * for convenience, but it is not valid for 64-bit. /// All pointers are cast to 32-bit integers. It is therefore 2*4 bytes shorter. /// The return value is the resulting 32-bit structure size. u32 GFDRepackTexture32Bit(GX2Texture *pTXin64, GFDTexture *pTXout32) { // First, the enclosed surface structure: pTXout32->surface.dim = pTXin64->surface.dim; pTXout32->surface.width = pTXin64->surface.width; pTXout32->surface.height = pTXin64->surface.height; pTXout32->surface.depth = pTXin64->surface.depth; pTXout32->surface.numMips = pTXin64->surface.numMips; pTXout32->surface.format = pTXin64->surface.format; pTXout32->surface.aa = pTXin64->surface.aa; pTXout32->surface.use = pTXin64->surface.use; pTXout32->surface.imageSize = pTXin64->surface.imageSize; pTXout32->surface.imagePtr = (u32) pTXin64->surface.imagePtr; pTXout32->surface.mipSize = pTXin64->surface.mipSize; pTXout32->surface.mipPtr = (u32) pTXin64->surface.mipPtr; pTXout32->surface.tileMode = pTXin64->surface.tileMode; pTXout32->surface.swizzle = pTXin64->surface.swizzle; pTXout32->surface.alignment = pTXin64->surface.alignment; pTXout32->surface.pitch = pTXin64->surface.pitch; for(u32 i=0; i<13; i++) { pTXout32->surface.mipOffset[i] = pTXin64->surface.mipOffset[i]; } // Then, the rest of the texture structure: pTXout32->viewFirstMip = pTXin64->viewFirstMip; pTXout32->viewNumMips = pTXin64->viewNumMips; pTXout32->viewFirstSlice = pTXin64->viewFirstSlice; pTXout32->viewNumSlices = pTXin64->viewNumSlices; pTXout32->compSel = pTXin64->compSel; for(u32 i=0; i_regs[i] = pTXin64->_regs[i]; } return sizeof(GFDTexture); } /// Repack a texture from a 32-bit structure to a 64-bit structure. u32 GFDRepackTexture64Bit(GFDTexture *pTXin32, GX2Texture *pTXout64) { // First, the enclosed surface structure: pTXout64->surface.dim = pTXin32->surface.dim; pTXout64->surface.width = pTXin32->surface.width; pTXout64->surface.height = pTXin32->surface.height; pTXout64->surface.depth = pTXin32->surface.depth; pTXout64->surface.numMips = pTXin32->surface.numMips; pTXout64->surface.format = pTXin32->surface.format; pTXout64->surface.aa = pTXin32->surface.aa; pTXout64->surface.use = pTXin32->surface.use; pTXout64->surface.imageSize = pTXin32->surface.imageSize; pTXout64->surface.imagePtr = (void*)pTXin32->surface.imagePtr; pTXout64->surface.mipSize = pTXin32->surface.mipSize; pTXout64->surface.mipPtr = (void*)pTXin32->surface.mipPtr; pTXout64->surface.tileMode = pTXin32->surface.tileMode; pTXout64->surface.swizzle = pTXin32->surface.swizzle; pTXout64->surface.alignment = pTXin32->surface.alignment; pTXout64->surface.pitch = pTXin32->surface.pitch; for(u32 i=0; i<13; i++) { pTXout64->surface.mipOffset[i] = pTXin32->surface.mipOffset[i]; } // Then, the rest of the texture structure: pTXout64->viewFirstMip = pTXin32->viewFirstMip; pTXout64->viewNumMips = pTXin32->viewNumMips; pTXout64->viewFirstSlice = pTXin32->viewFirstSlice; pTXout64->viewNumSlices = pTXin32->viewNumSlices; pTXout64->compSel = pTXin32->compSel; for(u32 i=0; i_regs[i] = pTXin32->_regs[i]; } return sizeof(GX2Texture); } // ------------------------------------------------------------ bool GFDWriteFileTextureBlock(FILE *fp, GFDEndianSwapMode swapMode, GFDAlignMode alignMode, GX2Texture *pTexture) { if(pTexture == NULL) return false; // get info about the actual bitmap and mipmap u32 nBytesTex = GFD_ROUND_UP(pTexture->surface.imageSize, sizeof(u32)); void* pDataTex = pTexture->surface.imagePtr; u32 nBytesMip = GFD_ROUND_UP(pTexture->surface.mipSize, sizeof(u32)); void* pDataMip = pTexture->surface.mipPtr; u32 alignment = pTexture->surface.alignment; u32 padSize = 0; // zero out these pointers, so they don't get stored in file // (Not actually read in, but they aren't real, so let's zap them) // they are restored later on before we return pTexture->surface.imagePtr = NULL; pTexture->surface.mipPtr = NULL; // add GX2Surface struct header GFDTexture tex32; u32 size = GFDRepackTexture32Bit(pTexture, &tex32); // write header for Texture header if(!GFDWriteFileBlockHeader(fp, GFD_BLOCK_TYPE_GX2_TEX_HEADER, size)) return false; // write the texture data (not image data) if(!GFDWriteFilePPCData(fp, size/sizeof(u32), GFD_ELEMENT_SIZE_32, (u32 *) &tex32)) return false; // calc padding size for texture align // 2 more block headers will be written for pad + texture image data u32 currentSize = (u32)ftell(fp) + 2 * GFD_BLOCK_HEADER_SIZE; padSize = (alignment - (currentSize % alignment)) % alignment; // add pad block if(alignMode) if(!GFDWriteFilePadBlock(fp, padSize)) return false; // write out Header for texture block if(!GFDWriteFileBlockHeader(fp, GFD_BLOCK_TYPE_GX2_TEX_IMAGE, nBytesTex)) return false; // printf("%d <- %d \n", (u32)ftell(fp) & (pTexture->surface.alignment-1), pTexture->surface.alignment); // write out the image data for the texture if(!GFDWriteFileGPUData(fp, nBytesTex/sizeof(u32), GFD_ELEMENT_SIZE_32, swapMode, (u32 *) pDataTex)) return false; // zero if there is no mipmap if(nBytesMip > 0) { // calc padding size for texture align // 2 more block headers will be written for pad + texture mip data currentSize = (u32)ftell(fp) + 2 * GFD_BLOCK_HEADER_SIZE; padSize = (alignment - (currentSize % alignment)) % alignment; // add pad block if(alignMode) if(!GFDWriteFilePadBlock(fp, padSize)) return false; // write out Header for texture mips if(!GFDWriteFileBlockHeader(fp, GFD_BLOCK_TYPE_GX2_TEX_MIP_IMAGE, nBytesMip)) return false; // write out the mip image data for the texture if(!GFDWriteFileGPUData(fp, nBytesMip/sizeof(u32), GFD_ELEMENT_SIZE_32, swapMode, (u32 *) pDataMip)) return false; } // restore the pointers so our structure is still useable pTexture->surface.imagePtr = pDataTex; pTexture->surface.mipPtr = pDataMip; return true; } bool GFD_API GFDWriteFileTexture(char* pFilename, GFDGPUVersion gpuVer, GFDEndianSwapMode swapMode, GFDAlignMode alignMode, u32 numTexture, GX2Texture* pTexture) { FILE *fpout = NULL; u32 count = 0; if (!pFilename) { pFilename = GTX_DEFAULT_FILENAME; } // open file if(GFDOpenFile(&fpout, pFilename, "wb") != 0) { printf("Error: Can't open %s\n", pFilename); return false; } // check gpu version switch(gpuVer) { case GFD_GPU_VERSION_0: break; case GFD_GPU_VERSION_1: break; case GFD_GPU_VERSION_GPU7: break; default: printf("Warning: Unsupported GPU %d, using default.\n", gpuVer); gpuVer = GFD_GPU_VERSION_GPU7; break; } // writes the file header if(!GFDWriteFileHeader(fpout, gpuVer, alignMode)) { printf("Error: Can't write file header\n"); GFDCloseFile(fpout); return false; } // writes multiple texture blocks for (count = 0; count < numTexture; count++) { if(NULL != &pTexture[count]) { // write the texture header, registers, then the texture data if(!GFDWriteFileTextureBlock(fpout, swapMode, alignMode, &pTexture[count])) { printf("Error: Can't write texture block %d.\n", count); GFDCloseFile(fpout); return false; } } } // writes an 'End' block to the file if(!GFDWriteFileBlockHeader(fpout, GFD_BLOCK_TYPE_END, 0)) { printf("Error: Can't write end block header\n"); GFDCloseFile(fpout); return false; } GFDCloseFile(fpout); return true; } bool GFD_API GFDAppendWriteFileTexture(char* pFilename, GFDGPUVersion gpuVer, GFDEndianSwapMode swapMode, GFDAlignMode alignMode, u32 numTexture, GX2Texture* pTexture) { FILE *fpout = NULL; u32 count = 0; GFDHeader fileHeader; // open file if(GFDOpenFile(&fpout, pFilename, "rb+") != 0) { printf("Error: Can't open %s\n", pFilename); return false; } // Read File Header if(!GFDReadFilePPCData(&fileHeader, (GFD_HEADER_SIZE + 3) / 4, GFD_ELEMENT_SIZE_32, fpout)) { GFDCloseFile(fpout); printf("Error: Can't read file header.\n"); return false; } // check gpu version if(fileHeader.gpuVersion != gpuVer) { GFDCloseFile(fpout); printf("Error: GPU version is different.\n"); return false; } // check header version if(!GFDCheckHeaderMagicVersions(&fileHeader)) { GFDCloseFile(fpout); printf("Error: Format version is different.\n"); return false; } // seeks to beginning of 'End' block fseek(fpout, -(s32)GFD_BLOCK_HEADER_SIZE, SEEK_END); // appened writes multiple texture blocks for (count = 0; count < numTexture; count++) { if(NULL != &pTexture[count]) { // write the texture header, registers, then the texture data if(!GFDWriteFileTextureBlock(fpout, swapMode, alignMode, &pTexture[count])) { printf("Error: Can't write texture block %d.\n", count); GFDCloseFile(fpout); return false; } } } // writes an 'End' block to the file if(!GFDWriteFileBlockHeader(fpout, GFD_BLOCK_TYPE_END, 0)) { printf("Error: Can't write end block header\n"); GFDCloseFile(fpout); return false; } GFDCloseFile(fpout); return true; } GFD_DECLSPEC bool GFD_API GFDReadFileTexture(GX2Texture* pTexture, GFDGPUVersion gpuVer, GFDEndianSwapMode swapMode, const char* pFilename) { FILE *pFile = NULL; GFDHeader gfdHeader; GFDBlockHeader gfdBlockHeader; u32 *imageData; u32 *mipData; int id = 0; if(GFDOpenFile(&pFile, pFilename, "rb")) { printf("Error: loading GTX file. Exiting.\n"); return false; } // check gpu version switch(gpuVer) { case GFD_GPU_VERSION_0: break; case GFD_GPU_VERSION_1: break; case GFD_GPU_VERSION_GPU7: break; default: printf("Warning: Unsupported GPU %d, using default.\n", gpuVer); gpuVer = GFD_GPU_VERSION_GPU7; break; } // Read File Header if(!GFDReadFilePPCData(&gfdHeader, (GFD_HEADER_SIZE + 3) / 4, GFD_ELEMENT_SIZE_32, pFile)) { GFDCloseFile(pFile); printf("Error: reading file header. Exiting.\n"); return false; } // Read Block Header if(!GFDReadFilePPCData(&gfdBlockHeader, (GFD_BLOCK_HEADER_SIZE + 3) / 4, GFD_ELEMENT_SIZE_32, pFile)) { GFDCloseFile(pFile); printf("Error: reading block header. Exiting.\n"); return false; } while(GFDCheckBlockHeaderMagicVersions(&gfdBlockHeader)) { switch(gfdBlockHeader.type) { case GFD_BLOCK_TYPE_GX2_TEX_HEADER: GFDTexture outTexture; // Read GX2 Header if(!GFDReadFilePPCData(&outTexture, (gfdBlockHeader.dataSize + 3) / 4, GFD_ELEMENT_SIZE_32, pFile)) { GFDCloseFile(pFile); printf("Error reading gx2 header. Exiting.\n"); if(pTexture->surface.imagePtr != NULL) free(pTexture->surface.imagePtr); if(pTexture->surface.mipPtr != NULL) free(pTexture->surface.mipPtr); return false; } GFDRepackTexture64Bit(&outTexture, pTexture); // Print Information //PrintTextureSurfaceInfo(config.printinfo, id, pTexture); // Inc id id++; break; case GFD_BLOCK_TYPE_GX2_TEX_IMAGE: // malloc image data imageData = (u32*)malloc(((gfdBlockHeader.dataSize + 3) / 4)*GFD_ELEMENT_SIZE_32); // Read Texture Image Data if(!GFDReadFileGPUData(imageData, (gfdBlockHeader.dataSize + 3) / 4, GFD_ELEMENT_SIZE_32, swapMode, pFile)) { GFDCloseFile(pFile); printf("Error: reading gx2 header. Exiting.\n"); if(imageData != NULL) free(imageData); if(pTexture->surface.mipPtr != NULL) free(pTexture->surface.mipPtr); return false; } // set pointer pTexture->surface.imagePtr = imageData; break; case GFD_BLOCK_TYPE_GX2_TEX_MIP_IMAGE: // malloc mip data mipData = (u32*)malloc(((gfdBlockHeader.dataSize + 3) / 4)*GFD_ELEMENT_SIZE_32); // Read Texture Image Data if(!GFDReadFileGPUData(mipData, (gfdBlockHeader.dataSize + 3) / 4, GFD_ELEMENT_SIZE_32, swapMode, pFile)) { GFDCloseFile(pFile); printf("Error: reading gx2 header. Exiting.\n"); if(pTexture->surface.imagePtr != NULL) free(pTexture->surface.imagePtr); if(mipData != NULL) free(mipData); return false; } // set pointer pTexture->surface.mipPtr = mipData; break; case GFD_BLOCK_TYPE_PAD: fseek(pFile, gfdBlockHeader.dataSize, SEEK_CUR); break; default: break; } memset(&gfdBlockHeader, 0, sizeof(gfdBlockHeader)); // Read Block Header if(!GFDReadFilePPCData(&gfdBlockHeader, (GFD_BLOCK_HEADER_SIZE + 3) / 4, GFD_ELEMENT_SIZE_32, pFile)) { GFDCloseFile(pFile); printf("Error: reading block header. Exiting.\n"); if(pTexture->surface.imagePtr != NULL) free(pTexture->surface.imagePtr); if(pTexture->surface.mipPtr != NULL) free(pTexture->surface.mipPtr); return false; } // End Block if(GFD_BLOCK_TYPE_END == gfdBlockHeader.type) { GFDCloseFile(pFile); // terminate read, we have an end block break; } } return true; } GFD_DECLSPEC void GFD_API GFDFreeFileTexture(GX2Texture* pTexture) { if(pTexture->surface.imagePtr != NULL) free(pTexture->surface.imagePtr); if(pTexture->surface.mipPtr != NULL) free(pTexture->surface.mipPtr); }