/*---------------------------------------------------------------------------* Copyright 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. *---------------------------------------------------------------------------*/ // ---------------------------------------------------------------- // gfdInterface.c // ---------------------------------------------------------------- #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif // __cplusplus #define GFDPRINT(...) //#define GFDPRINT(...) OSReport(__VA_ARGS__) #define _GFD_SWAP_BYTES(x) ( (((x) >> 24) & 0xff) | (((x) >> 8) & 0xff00) | (((x) << 8) & 0xff0000) | (((x) << 24) & 0xff000000) ) // ---------------------- // // Proto Types // // ---------------------- BOOL _GFDGetHeaderVersions(u32 *pVerMajor, u32 *pVerMinor, u32 *pVerGPU, const void *pData); BOOL _GFDCheckHeaderVersions(const void *pData); BOOL _GFDCheckBlockHeaderMagicVersions(const GFDBlockHeader *pBlockHeader); u32 _GFDGetBlockCount(GFDBlockType blockType, const void *pData); u32 _GFDGetBlockDataSize(GFDBlockType blockType, u32 index, const void *pData); BOOL _GFDRelocateBlock(u32 nBytesBlock, char *pData); BOOL _GFDRelocateBlockEx(GFDBlockRelocationHeader *pTrailer, u32 fromOffset, u32 toOffset, char *pData); // ---------------------- // // Public Functions // // ---------------------- // // For shader file (gsh) // u32 GFDGetVertexShaderCount(const void *pData) { return _GFDGetBlockCount(GFD_BLOCK_TYPE_GX2_VSH_HEADER, pData); } u32 GFDGetPixelShaderCount(const void *pData) { return _GFDGetBlockCount(GFD_BLOCK_TYPE_GX2_PSH_HEADER, pData); } u32 GFDGetGeometryShaderCount(const void *pData) { return _GFDGetBlockCount(GFD_BLOCK_TYPE_GX2_GSH_HEADER, pData); } u32 GFDGetComputeShaderCount(const void *pData) { return _GFDGetBlockCount(GFD_BLOCK_TYPE_GX2_CSH_HEADER, pData); } u32 GFDGetVertexShaderHeaderSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_VSH_HEADER, index, pData); } u32 GFDGetPixelShaderHeaderSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_PSH_HEADER, index, pData); } u32 GFDGetGeometryShaderHeaderSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_GSH_HEADER, index, pData); } u32 GFDGetComputeShaderHeaderSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_CSH_HEADER, index, pData); } u32 GFDGetVertexShaderProgramSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_VSH_PROGRAM, index, pData); } u32 GFDGetPixelShaderProgramSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_PSH_PROGRAM, index, pData); } u32 GFDGetGeometryShaderProgramSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_GSH_PROGRAM, index, pData); } u32 GFDGetGeometryShaderCopyProgramSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_GSH_COPY_PROGRAM, index, pData); } u32 GFDGetComputeShaderProgramSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_CSH_PROGRAM, index, pData); } BOOL GFDGetVertexShader(GX2VertexShader *pHeader, void *pProgram, u32 index, const void *pData) { BOOL ret; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; u32 nPrograms = 0; if(pHeader == NULL || pProgram == NULL || pData == NULL) return FALSE; if(!_GFDCheckHeaderVersions(pData)) return FALSE; if( 0 != (((u32) pProgram) & (GX2_SHADER_ALIGNMENT-1))) { OSReport("Warning: Shader program buffer not aligned correctly. It needs %d byte align buffer.\n", GX2_SHADER_ALIGNMENT); return FALSE; } pDataStruct = (char*)pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *)pDataStruct; while((ret = _GFDCheckBlockHeaderMagicVersions(pBlockHeader))) { pBlockHeader = (GFDBlockHeader *)pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_VSH_HEADER: if(index == nHeaders) { memcpy(pHeader, (void *)pDataStruct, pBlockHeader->dataSize); if (!_GFDRelocateBlock(pBlockHeader->dataSize,(char *)pHeader)) { ASSERT(!"Internal offset/pointers corrupted."); return FALSE; } GFDPRINT("GFD:VSH_HEADER: %x -> %x size: %d\n", pDataStruct, pHeader, pBlockHeader->dataSize); } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_VSH_PROGRAM: if(index == nPrograms) { // Set shader program pHeader->shaderPtr = pProgram; memcpy(pHeader->shaderPtr, (char *)pDataStruct, pBlockHeader->dataSize); // This is done one layer up (in demoGfd) // GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, pDataStruct, pBlockHeader->dataSize); GFDPRINT("GFD:VSH_PROGRAM: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nPrograms++; break; default: break; } pDataStruct += pBlockHeader->dataSize; // Terminate once the full structure has been found if ( nHeaders > index && nPrograms > index ) break; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } return (ret && nHeaders >= index && nPrograms >= index); } BOOL GFDGetPixelShader(GX2PixelShader *pHeader, void *pProgram, u32 index, const void *pData) { BOOL ret; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; u32 nPrograms = 0; if(pHeader == NULL || pProgram == NULL || pData == NULL) return FALSE; if(!_GFDCheckHeaderVersions(pData)) return FALSE; if( 0 != (((u32) pProgram) & (GX2_SHADER_ALIGNMENT-1))) { OSReport("Warning: Shader program buffer not aligned correctly. It needs %d byte align buffer.\n", GX2_SHADER_ALIGNMENT); return FALSE; } pDataStruct = (char*) pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *)pDataStruct; while((ret = _GFDCheckBlockHeaderMagicVersions(pBlockHeader))) { pBlockHeader = (GFDBlockHeader *)pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_PSH_HEADER: if(index == nHeaders) { memcpy(pHeader, (void *)pDataStruct, pBlockHeader->dataSize); if (!_GFDRelocateBlock(pBlockHeader->dataSize,(char *)pHeader)) { ASSERT(!"Internal offset/pointers corrupted."); return FALSE; } GFDPRINT("GFD:PSH_HEADER: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_PSH_PROGRAM: if(index == nPrograms) { // Set shader program pHeader->shaderPtr = pProgram; memcpy(pHeader->shaderPtr, (char *)pDataStruct, pBlockHeader->dataSize); // This is done one layer up (in demoGfd) // GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, pDataStruct, pBlockHeader->dataSize); GFDPRINT("GFD:PSH_PROGRAM: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nPrograms++; break; default: break; } pDataStruct += pBlockHeader->dataSize; // Terminate once the full structure has been found if ( nHeaders > index && nPrograms > index ) break; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } return (ret && nHeaders >= index && nPrograms >= index); } BOOL GFDGetGeometryShader(GX2GeometryShader *pHeader, void *pProgram, void *pCopyProgram, u32 index, const void *pData) { BOOL ret; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; u32 nPrograms = 0; u32 nCopyPrograms = 0; if(pHeader == NULL || pProgram == NULL || pData == NULL) return FALSE; if(!_GFDCheckHeaderVersions(pData)) return FALSE; if( 0 != (((u32) pProgram) & (GX2_SHADER_ALIGNMENT-1))) { OSReport("Warning: Shader program buffer not aligned correctly. It needs %d byte align buffer.\n", GX2_SHADER_ALIGNMENT); return FALSE; } pDataStruct = (char*) pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *)pDataStruct; while((ret = _GFDCheckBlockHeaderMagicVersions(pBlockHeader))) { pBlockHeader = (GFDBlockHeader *)pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_GSH_HEADER: if(index == nHeaders) { memcpy(pHeader, (void *)pDataStruct, pBlockHeader->dataSize); if (!_GFDRelocateBlock(pBlockHeader->dataSize,(char *)pHeader)) { ASSERT(!"Internal offset/pointers corrupted."); return FALSE; } GFDPRINT("GFD:GSH_HEADER: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_GSH_COPY_PROGRAM: if(index == nCopyPrograms) { // Set copy shader program pHeader->copyShaderPtr = pCopyProgram; memcpy(pHeader->copyShaderPtr, (char *)pDataStruct, pBlockHeader->dataSize); // This is done one layer up (in demoGfd) // GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, pDataStruct, pBlockHeader->dataSize); GFDPRINT("GFD:GSH_COPY_PROGRAM: %x -> %x size: %d\n", pDataStruct, pHeader->copyShaderPtr, pBlockHeader->dataSize); } nCopyPrograms++; break; case GFD_BLOCK_TYPE_GX2_GSH_PROGRAM: if(index == nPrograms) { // Set shader program pHeader->shaderPtr = pProgram; memcpy(pHeader->shaderPtr, (char *)pDataStruct, pBlockHeader->dataSize); // This is done one layer up (in demoGfd) // GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, pDataStruct, pBlockHeader->dataSize); GFDPRINT("GFD:GSH_PROGRAM: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nPrograms++; break; default: break; } pDataStruct += pBlockHeader->dataSize; // Terminate once the full structure has been found if ( nHeaders > index && nPrograms > index && nCopyPrograms > index) break; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } return (ret && nHeaders >= index && nCopyPrograms >= index && nPrograms >= index); } BOOL GFDGetComputeShader(GX2ComputeShader *pHeader, void *pProgram, u32 index, const void *pData) { BOOL ret; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; u32 nPrograms = 0; if(pHeader == NULL || pProgram == NULL || pData == NULL) return FALSE; if(!_GFDCheckHeaderVersions(pData)) return FALSE; if( 0 != (((u32) pProgram) & (GX2_SHADER_ALIGNMENT-1))) { OSReport("Warning: Shader program buffer not aligned correctly. It needs %d byte align buffer.\n", GX2_SHADER_ALIGNMENT); return FALSE; } pDataStruct = (char*)pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *)pDataStruct; while((ret = _GFDCheckBlockHeaderMagicVersions(pBlockHeader))) { pBlockHeader = (GFDBlockHeader *)pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_CSH_HEADER: if(index == nHeaders) { memcpy(pHeader, (void *)pDataStruct, pBlockHeader->dataSize); if (!_GFDRelocateBlock(pBlockHeader->dataSize,(char *)pHeader)) { ASSERT(!"Internal offset/pointers corrupted."); return FALSE; } GFDPRINT("GFD:CSH_HEADER: %x -> %x size: %d\n", pDataStruct, pHeader, pBlockHeader->dataSize); } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_CSH_PROGRAM: if(index == nPrograms) { // Set shader program pHeader->shaderPtr = pProgram; memcpy(pHeader->shaderPtr, (char *)pDataStruct, pBlockHeader->dataSize); // This is done one layer up (in demoGfd) // GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, pDataStruct, pBlockHeader->dataSize); GFDPRINT("GFD:CSH_PROGRAM: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nPrograms++; break; default: break; } pDataStruct += pBlockHeader->dataSize; // Terminate once the full structure has been found if ( nHeaders > index && nPrograms > index ) break; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } return (ret && nHeaders >= index && nPrograms >= index); } GX2VertexShader *GFDGetVertexShaderPointer(u32 index, const void *pData) { GX2VertexShader *pHeader = NULL; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; u32 nPrograms = 0; if(!_GFDCheckHeaderVersions(pData)) return NULL; pDataStruct = (char*)pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *)pDataStruct; while(_GFDCheckBlockHeaderMagicVersions(pBlockHeader)) { pBlockHeader = (GFDBlockHeader *)pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_VSH_HEADER: if(index == nHeaders) { pHeader = (GX2VertexShader *)pDataStruct; if (!_GFDRelocateBlock(pBlockHeader->dataSize,(char *)pHeader)) { ASSERT(!"Internal offset/pointers corrupted."); return NULL; } GFDPRINT("GFD:VSH_HEADER: %x -> %x size: %d\n", pDataStruct, pHeader, pBlockHeader->dataSize); } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_VSH_PROGRAM: if(index == nPrograms) { if( 0 != (((u32) pDataStruct) & (GX2_SHADER_ALIGNMENT-1))) { OSReport("Warning: Shader program buffer not aligned correctly. It needs %d byte align buffer.\n", GX2_SHADER_ALIGNMENT); return NULL; } if(pHeader == NULL) return NULL; // Set shader program pHeader->shaderPtr = (char *)pDataStruct; GFDPRINT("GFD:VSH_PROGRAM: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nPrograms++; break; default: break; } pDataStruct += pBlockHeader->dataSize; // Terminate once the full structure has been found if ( nHeaders > index && nPrograms > index ) break; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } // Failed to find the shader program data if(pHeader && !pHeader->shaderPtr) return NULL; return pHeader; } GX2PixelShader *GFDGetPixelShaderPointer(u32 index, const void *pData) { GX2PixelShader *pHeader = NULL; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; u32 nPrograms = 0; if(!_GFDCheckHeaderVersions(pData)) return NULL; pDataStruct = (char*)pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *)pDataStruct; while(_GFDCheckBlockHeaderMagicVersions(pBlockHeader)) { pBlockHeader = (GFDBlockHeader *)pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_PSH_HEADER: if(index == nHeaders) { pHeader = (GX2PixelShader *)pDataStruct; if (!_GFDRelocateBlock(pBlockHeader->dataSize,(char *)pHeader)) { ASSERT(!"Internal offset/pointers corrupted."); return NULL; } GFDPRINT("GFD:PSH_HEADER: %x -> %x size: %d\n", pDataStruct, pHeader, pBlockHeader->dataSize); } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_PSH_PROGRAM: if(index == nPrograms) { if( 0 != (((u32) pDataStruct) & (GX2_SHADER_ALIGNMENT-1))) { OSReport("Warning: Shader program buffer not aligned correctly. It needs %d byte align buffer.\n", GX2_SHADER_ALIGNMENT); return NULL; } if(pHeader == NULL) return NULL; // Set shader program pHeader->shaderPtr = (char *)pDataStruct; GFDPRINT("GFD:PSH_PROGRAM: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nPrograms++; break; default: break; } pDataStruct += pBlockHeader->dataSize; // Terminate once the full structure has been found if ( nHeaders > index && nPrograms > index ) break; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } // Failed to find the shader program data if(pHeader && !pHeader->shaderPtr) return NULL; return pHeader; } GX2GeometryShader *GFDGetGeometryShaderPointer(u32 index, const void *pData) { GX2GeometryShader *pHeader = NULL; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; u32 nPrograms = 0; u32 nCopyPrograms = 0; if(!_GFDCheckHeaderVersions(pData)) return NULL; pDataStruct = (char*) pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *)pDataStruct; while(_GFDCheckBlockHeaderMagicVersions(pBlockHeader)) { pBlockHeader = (GFDBlockHeader *)pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_GSH_HEADER: if(index == nHeaders) { pHeader = (GX2GeometryShader *)pDataStruct; if (!_GFDRelocateBlock(pBlockHeader->dataSize,(char *)pHeader)) { ASSERT(!"Internal offset/pointers corrupted."); return NULL; } GFDPRINT("GFD:GSH_HEADER: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_GSH_COPY_PROGRAM: if(index == nCopyPrograms) { if( 0 != (((u32) pDataStruct) & (GX2_SHADER_ALIGNMENT-1))) { OSReport("Warning: Copy shader program buffer not aligned correctly. It needs %d byte align buffer.\n", GX2_SHADER_ALIGNMENT); return NULL; } if(pHeader == NULL) return NULL; // Set copy shader program pHeader->copyShaderPtr = (char *)pDataStruct; GFDPRINT("GFD:GSH_COPY_PROGRAM: %x -> %x size: %d\n", pDataStruct, pHeader->copyShaderPtr, pBlockHeader->dataSize); } nCopyPrograms++; break; case GFD_BLOCK_TYPE_GX2_GSH_PROGRAM: if(index == nPrograms) { if( 0 != (((u32) pDataStruct) & (GX2_SHADER_ALIGNMENT-1))) { OSReport("Warning: Shader program buffer not aligned correctly. It needs %d byte align buffer.\n", GX2_SHADER_ALIGNMENT); return NULL; } if(pHeader == NULL) return NULL; // Set shader program pHeader->shaderPtr = (char *)pDataStruct; GFDPRINT("GFD:GSH_PROGRAM: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nPrograms++; break; default: break; } pDataStruct += pBlockHeader->dataSize; // Terminate once the full structure has been found if (nHeaders > index && nPrograms > index && nCopyPrograms > index) break; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } // Failed to find the shader program data if(pHeader && (!pHeader->shaderPtr || !pHeader->copyShaderPtr)) return NULL; return pHeader; } GX2ComputeShader *GFDGetComputeShaderPointer(u32 index, const void *pData) { GX2ComputeShader *pHeader = NULL; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; u32 nPrograms = 0; if(!_GFDCheckHeaderVersions(pData)) return NULL; pDataStruct = (char*)pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *)pDataStruct; while(_GFDCheckBlockHeaderMagicVersions(pBlockHeader)) { pBlockHeader = (GFDBlockHeader *)pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_CSH_HEADER: if(index == nHeaders) { pHeader = (GX2ComputeShader *)pDataStruct; if (!_GFDRelocateBlock(pBlockHeader->dataSize,(char *)pHeader)) { ASSERT(!"Internal offset/pointers corrupted."); return NULL; } GFDPRINT("GFD:CSH_HEADER: %x -> %x size: %d\n", pDataStruct, pHeader, pBlockHeader->dataSize); } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_CSH_PROGRAM: if(index == nPrograms) { if( 0 != (((u32) pDataStruct) & (GX2_SHADER_ALIGNMENT-1))) { OSReport("Warning: Shader program buffer not aligned correctly. It needs %d byte align buffer.\n", GX2_SHADER_ALIGNMENT); return NULL; } if(pHeader == NULL) return NULL; // Set shader program pHeader->shaderPtr = (char *)pDataStruct; GFDPRINT("GFD:CSH_PROGRAM: %x -> %x size: %d\n", pDataStruct, pHeader->shaderPtr, pBlockHeader->dataSize); } nPrograms++; break; default: break; } pDataStruct += pBlockHeader->dataSize; // Terminate once the full structure has been found if (nHeaders > index && nPrograms > index) break; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } // Failed to find the shader program data if(pHeader && !pHeader->shaderPtr) return NULL; return pHeader; } // // For texture binary file (.gtx) // u32 GFDGetTextureCount(const void *pData) { return _GFDGetBlockCount(GFD_BLOCK_TYPE_GX2_TEX_HEADER, pData); } u32 GFDGetTextureHeaderSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_TEX_HEADER, index, pData); } u32 GFDGetTextureImageSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_TEX_IMAGE, index, pData); } u32 GFDGetTextureMipImageSize(u32 index, const void *pData) { return _GFDGetBlockDataSize(GFD_BLOCK_TYPE_GX2_TEX_MIP_IMAGE, index, pData); } u32 GFDGetTextureAlignmentSize(u32 index, const void *pData) { char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; GX2Texture pHeader; if(pData == NULL) return 0; if(!_GFDCheckHeaderVersions(pData)) return 0; pDataStruct = (char*) pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *) pDataStruct; while(_GFDCheckBlockHeaderMagicVersions(pBlockHeader)) { pBlockHeader = (GFDBlockHeader *) pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_TEX_HEADER: if(index == nHeaders) { memcpy(&pHeader, (void *)pDataStruct, pBlockHeader->dataSize); GFDPRINT("GFD:TEX_ALIGN: %x size: %d align: %d\n", pDataStruct, pBlockHeader->dataSize, pHeader.surface.alignment); return pHeader.surface.alignment; } nHeaders++; break; default: break; } pDataStruct += pBlockHeader->dataSize; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } return 0; // not found. return 0. } BOOL GFDGetTexture(GX2Texture *pHeader, void *pImage, void *pMipImage, u32 index, const void *pData) { BOOL ret; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; BOOL headerFound = FALSE; if(pHeader == NULL || pImage == NULL || pData == NULL) return FALSE; if(!_GFDCheckHeaderVersions(pData)) return FALSE; pDataStruct = (char*) pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *) pDataStruct; while((ret = _GFDCheckBlockHeaderMagicVersions(pBlockHeader))) { pBlockHeader = (GFDBlockHeader *) pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_TEX_HEADER: if(index == nHeaders) { memcpy(pHeader, (void *)pDataStruct, pBlockHeader->dataSize); GFDPRINT("GFD:TEX_HEADER: %x size: %d align: %d\n", pDataStruct, pBlockHeader->dataSize, pHeader->surface.alignment); headerFound = TRUE; } else if ( headerFound ) { // Since we've already found the header, it is assumed all blocks for the texture have been found return (pHeader->surface.imagePtr || pHeader->surface.mipPtr); } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_TEX_IMAGE: if (headerFound) { if( 0 != (((u32) pImage) & (pHeader->surface.alignment-1))) { OSReport("Warning: Texture image data buffers are not aligned correctly. It needs %d byte align buffer.\n", pHeader->surface.alignment); return FALSE; } // Set image pointer pHeader->surface.imagePtr = pImage; memcpy(pHeader->surface.imagePtr, (char *)pDataStruct, pBlockHeader->dataSize); // This is done one layer up (in demoGfd) // GX2Invalidate(GX2_INVALIDATE_CPU_TEXTURE, pDataStruct, pBlockHeader->dataSize); GFDPRINT("GFD:TEX_IMAGE: %x size: %d\n", pDataStruct, pBlockHeader->dataSize); } break; case GFD_BLOCK_TYPE_GX2_TEX_MIP_IMAGE: if (headerFound) { if( 0 != (((u32) pMipImage) & (pHeader->surface.alignment-1))) { OSReport("Warning: Texture image data buffers are not aligned correctly. It needs %d byte align buffer.\n", pHeader->surface.alignment); return FALSE; } // Set image pointer pHeader->surface.mipPtr = pMipImage; memcpy(pHeader->surface.mipPtr, (char *)pDataStruct, pBlockHeader->dataSize); // This is done one layer up (in demoGfd) // GX2Invalidate(GX2_INVALIDATE_CPU_TEXTURE, pDataStruct, pBlockHeader->dataSize); GFDPRINT("GFD:TEX_MIP_IMAGE: %x size: %d\n", pDataStruct, pBlockHeader->dataSize); } break; case GFD_BLOCK_TYPE_PAD: GFDPRINT("GFD:PAD: %x size: %d\n", pDataStruct, pBlockHeader->dataSize); break; default: break; } pDataStruct += pBlockHeader->dataSize; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } return (ret && headerFound && (pHeader->surface.imagePtr || pHeader->surface.mipPtr)); } BOOL GFDGetGX2RTexture(GX2Texture *pHeader, u32 index, const void *pData) { BOOL ret; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; BOOL headerFound = FALSE; if(pHeader == NULL || pData == NULL) return FALSE; if(!_GFDCheckHeaderVersions(pData)) return FALSE; pDataStruct = (char*) pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *) pDataStruct; while((ret = _GFDCheckBlockHeaderMagicVersions(pBlockHeader))) { pBlockHeader = (GFDBlockHeader *) pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_TEX_HEADER: if(index == nHeaders) { memcpy(pHeader, (void *)pDataStruct, pBlockHeader->dataSize); GFDPRINT("GFD:TEX_HEADER: %x size: %d align: %d\n", pDataStruct, pBlockHeader->dataSize, pHeader->surface.alignment); headerFound = TRUE; } else if ( headerFound ) { // Since we've already found the header, it is assumed all blocks for the texture have been found return (pHeader->surface.imagePtr || pHeader->surface.mipPtr); } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_TEX_IMAGE: if (headerFound) { // Allocate the surface pHeader->surface.imagePtr=pHeader->surface.mipPtr=NULL; GX2RCreateSurface(&pHeader->surface, (GX2RResourceFlags)(GX2R_BIND_TEXTURE | GX2R_USAGE_CPU_READWRITE | GX2R_USAGE_GPU_READ)); // Lock void* pImage = GX2RLockSurface(&pHeader->surface, 0); if( 0 != (((u32) pImage) & (pHeader->surface.alignment-1))) { GX2RDestroySurface(&pHeader->surface); OSReport("Warning: Texture image data buffers are not aligned correctly. It needs %d byte align buffer.\n", pHeader->surface.alignment); return FALSE; } // Copy in the data memcpy(pHeader->surface.imagePtr, (char *)pDataStruct, pBlockHeader->dataSize); // Unlock, skip invalidation until it's all loaded GX2RUnlockSurfaceEx(&pHeader->surface, 0, GX2R_OPTION_NO_INVALIDATE); GFDPRINT("GFD:TEX_IMAGE: %x size: %d\n", pDataStruct, pBlockHeader->dataSize); } break; case GFD_BLOCK_TYPE_GX2_TEX_MIP_IMAGE: if (headerFound) { ASSERT(GX2RSurfaceExists(&pHeader->surface)); GX2RLockSurface(&pHeader->surface, GX2R_SURFACE_ALL_MIPS); if( 0 != (((u32) pHeader->surface.mipPtr) & (pHeader->surface.alignment-1))) { GX2RDestroySurface(&pHeader->surface); OSReport("Warning: Texture image data buffers are not aligned correctly. It needs %d byte align buffer.\n", pHeader->surface.alignment); return FALSE; } memcpy(pHeader->surface.mipPtr, (char *)pDataStruct, pBlockHeader->dataSize); // Unlock, skip invalidation until it's all loaded GX2RUnlockSurfaceEx(&pHeader->surface, GX2R_SURFACE_ALL_MIPS, GX2R_OPTION_NO_INVALIDATE); GFDPRINT("GFD:TEX_MIP_IMAGE: %x size: %d\n", pDataStruct, pBlockHeader->dataSize); } break; case GFD_BLOCK_TYPE_PAD: GFDPRINT("GFD:PAD: %x size: %d\n", pDataStruct, pBlockHeader->dataSize); break; default: break; } pDataStruct += pBlockHeader->dataSize; // If we've started looking at a new header block ignore if (nHeaders > index + 1) break; if (GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } if (ret && headerFound && (pHeader->surface.imagePtr || pHeader->surface.mipPtr)) { if ( pHeader->surface.imagePtr ) GX2RInvalidateSurface(&pHeader->surface, 0, GX2R_OPTION_NONE); if ( pHeader->surface.mipPtr ) GX2RInvalidateSurface(&pHeader->surface, GX2R_SURFACE_ALL_MIPS, GX2R_OPTION_NONE); return TRUE; } return FALSE; } GX2Texture *GFDGetTexturePointer(u32 index, const void *pData) { GX2Texture *pHeader = NULL; char *pDataStruct; GFDBlockHeader *pBlockHeader; u32 nHeaders = 0; BOOL headerFound = FALSE; if(!_GFDCheckHeaderVersions(pData)) return NULL; pDataStruct = (char*) pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *) pDataStruct; while(_GFDCheckBlockHeaderMagicVersions(pBlockHeader)) { pBlockHeader = (GFDBlockHeader *) pDataStruct; pDataStruct += pBlockHeader->size; switch(pBlockHeader->type) { case GFD_BLOCK_TYPE_GX2_TEX_HEADER: if(index == nHeaders) { pHeader = (GX2Texture *)pDataStruct; headerFound = TRUE; GFDPRINT("GFD:TEX_HEADER: %x size: %d align: %d\n", pDataStruct, pBlockHeader->dataSize, pHeader->surface.alignment); } else if ( headerFound ) { // Since we've already found the header, it is assumed all blocks for the texture have been found return (pHeader->surface.imagePtr || pHeader->surface.mipPtr) ? pHeader : NULL; } nHeaders++; break; case GFD_BLOCK_TYPE_GX2_TEX_IMAGE: if (headerFound) { if( 0 != (((u32)pDataStruct) & (pHeader->surface.alignment-1))) { OSReport("Warning: Texture image data buffers are not aligned correctly. It needs %d byte align buffer.\n", pHeader->surface.alignment); return NULL; } // Set image pointer pHeader->surface.imagePtr = (char *)pDataStruct; GFDPRINT("GFD:TEX_IMAGE: %x size: %d\n", pDataStruct, pBlockHeader->dataSize); } break; case GFD_BLOCK_TYPE_GX2_TEX_MIP_IMAGE: if (headerFound) { if( 0 != (((u32)pDataStruct) & (pHeader->surface.alignment-1))) { OSReport("Warning: Texture image data buffers are not aligned correctly. It needs %d byte align buffer.\n", pHeader->surface.alignment); return NULL; } // Set image pointer pHeader->surface.mipPtr = (char *)pDataStruct; GFDPRINT("GFD:TEX_MIP_IMAGE: %x size: %d\n", pDataStruct, pBlockHeader->dataSize); } break; case GFD_BLOCK_TYPE_PAD: GFDPRINT("GFD:PAD: %x size: %d\n", pDataStruct, pBlockHeader->dataSize); break; default: break; } pDataStruct += pBlockHeader->dataSize; // If we've started looking at a new header block ignore if (nHeaders > index + 1) break; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } // Failed to find the level 0 or mipmap chain if(pHeader && !(pHeader->surface.imagePtr || pHeader->surface.mipPtr)) return NULL; return pHeader; } GX2Texture *GFDGetGX2RTexturePointer(u32 index, const void *pData) { GX2Texture* pHeader = GFDGetTexturePointer(index, pData); if (pHeader) { // Wrap/cast the surface to GX2R GX2RCreateSurfaceUserMemory(&pHeader->surface, pHeader->surface.imagePtr, pHeader->surface.mipPtr, (GX2RResourceFlags)(GX2R_BIND_TEXTURE | GX2R_USAGE_CPU_READWRITE | GX2R_USAGE_GPU_READ)); // Invalidate. We _probably_ don't need to invalidate CPU cache, but it's hard to 100% guarantee that, // seems safer to invalidate it anyway. if ( pHeader->surface.imagePtr ) GX2RInvalidateSurface(&pHeader->surface, 0, GX2R_OPTION_NONE); if ( pHeader->surface.mipPtr ) GX2RInvalidateSurface(&pHeader->surface, GX2R_SURFACE_ALL_MIPS, GX2R_OPTION_NONE); } return pHeader; } GFDAlignMode GFDGetAlignMode(const void *pData) { ASSERT(pData); GFDHeader *pHeader = (GFDHeader *)pData; return pHeader->alignMode; } // -------------------------------------- // // Internal functions // // -------------------------------------- BOOL _GFDCheckHeaderVersions(const void *pData) { u32 verMajor; u32 verMinor; u32 verGPU; u32 kCurrentGPU; if(pData == NULL) return FALSE; if(!_GFDGetHeaderVersions(&verMajor, &verMinor, &verGPU, pData)) return FALSE; // fails if magic numbers not match if(GFD_HEADER_MAJOR != verMajor) // major versions must match exactly return FALSE; if(GFD_HEADER_MINOR < verMinor) { OSReport("Warning: File minor version is greater than that of code."); return FALSE; } kCurrentGPU = GX2TempGetGPUVersion(); if(kCurrentGPU != verGPU) { ASSERT(!"Data is not for this version of the GPU"); return FALSE; } return TRUE; } BOOL _GFDGetHeaderVersions(u32 *pVerMajor, u32 *pVerMinor, u32 *pVerGPU, const void *pData) { u32 VerMagic; GFDHeader *pVerH = (GFDHeader *)pData; *pVerMajor = 0; *pVerMinor = 0; *pVerGPU = 0; // swap if host side VerMagic = pVerH->magic; if(GFD_HEADER_MAGIC != VerMagic) { if(GFD_HEADER_MAGIC == pVerH->magic) { ASSERT(!"Swap Byte Failed"); } return FALSE; } *pVerMajor = pVerH->majorVersion; *pVerMinor = pVerH->minorVersion; *pVerGPU = pVerH->gpuVersion; return TRUE; } BOOL _GFDCheckBlockHeaderMagicVersions(const GFDBlockHeader *pBlockHeader) { if (!(GFD_BLOCK_HEADER_MAGIC == pBlockHeader->magic )) { OSReport("Warning: Unknown block format. \n"); return FALSE; } if (!(GFD_BLOCK_HEADER_MAJOR == pBlockHeader->majorVersion)) { OSReport("Warning: Unsupported block version %d. \n", pBlockHeader->majorVersion); return FALSE; } return TRUE; } u32 _GFDGetBlockCount(GFDBlockType blockType, const void *pData) { char *pDataStruct; GFDBlockHeader *pBlockHeader; if(pData == NULL) return 0; u32 counts = 0; if(!_GFDCheckHeaderVersions(pData)) return 0; pDataStruct = (char*) pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *) pDataStruct; while(_GFDCheckBlockHeaderMagicVersions(pBlockHeader)) { pBlockHeader = (GFDBlockHeader *)pDataStruct; GFDPRINT("GFD:%x:%d: Block Type: %d, %d\n", pData, (s32)pBlockHeader, blockType, pBlockHeader->type); if (blockType == pBlockHeader->type) { counts++; } pDataStruct = pDataStruct + pBlockHeader->size + pBlockHeader->dataSize; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } GFDPRINT("GFD:%x: Total Block counts: %d\n", pData, counts); return counts; } u32 _GFDGetBlockDataSize(GFDBlockType blockType, u32 index, const void *pData) { char *pDataStruct; // jump over the header GFDBlockHeader *pBlockHeader; u32 nIndexs = 0; if(pData == NULL) return 0; // Check Header Version if(!_GFDCheckHeaderVersions(pData)) return 0; pDataStruct = (char*) pData + ((GFDHeader*)pData)->size; // jump over the header pBlockHeader = (GFDBlockHeader *) pDataStruct; while(_GFDCheckBlockHeaderMagicVersions(pBlockHeader)) { pBlockHeader = (GFDBlockHeader *)pDataStruct; if (blockType == pBlockHeader->type) { if(index == nIndexs) { GFDPRINT("GFD:%x:%x: %d Block dataSize: %d\n", pData, pBlockHeader, nIndexs, pBlockHeader->dataSize); return pBlockHeader->dataSize; } nIndexs++; } pDataStruct = pDataStruct + pBlockHeader->size + pBlockHeader->dataSize; if(GFD_BLOCK_TYPE_END == pBlockHeader->type) // terminate read, we have an end block break; } GFDPRINT("GFD:%x: %d Block size: %d\n", pData, nIndexs, pBlockHeader->dataSize); return 0; // 0 if didn't find this index } // // Main method that uses this structure _GFDRelocateBlock(), which patches addresses // in structure to new base address. It stores this base address in basePatchAddress. // Note one weird wart in the design. Bad things can happen if guts of _GFDRelocateBlock() // are called twice on the structure. To avoid this, _GFDRelocateBlock() only does real work if basePatchAddress is zero. // (This lets us move the block later if we want, simply 1) copy block to new address, 2) read basePatchAddress, 3) Call _GFDRelocateBlockEx() // to using new address(step1) and old address(step2) to move block. // // Cleans out extra debug flags attached to offset u32 GFDCleanTag(u32 Offset) {return Offset & ~GFD_TAG_MASK;} // Verifies offset stored in file is tagged with GFD_TAG_DAT BOOL GFDCheckTagDAT(u32 Offset) {return (Offset & GFD_TAG_MASK) == GFD_TAG_DAT;} // Verifies offset stored in file is tagged with GFD_TAG_STR BOOL GFDCheckTagSTR(u32 Offset) {return (Offset & GFD_TAG_MASK) == GFD_TAG_STR;} BOOL _GFDRelocateBlock(u32 nBytesBlock, char *pData) { // The design places GFDBlockRelocationHeader at the end of the // GFDBlock. As such the size is fixed. u32 size = sizeof(GFDBlockRelocationHeader); if(pData == NULL) return FALSE; GFDBlockRelocationHeader *pTrailer = (GFDBlockRelocationHeader *) (pData + nBytesBlock - size); ASSERT(GFD_BLOCK_RELOCATION_HEADER_MAGIC == pTrailer->magic && pTrailer->size == size); ASSERT(GFDCheckTagDAT(pTrailer->patchTableOffset )); // if block has already been relocated, don't relocate it again if(pTrailer-> basePatchAddress != 0) return TRUE; // finally, use the patch table to update all the pointers in the structure to // from relative to the begining of the structure, to absolute in memory return _GFDRelocateBlockEx(pTrailer, (u32)0, (u32)pData, pData); } // Relocate the structure pointed to by pData by offseting the pointers at the // Addresses in it located all the locations in the patch table. BOOL _GFDRelocateBlockEx(GFDBlockRelocationHeader *pTrailer, u32 fromOffset, u32 toOffset, char *pData) { u32 MainOffset = GFDCleanTag( pTrailer->dataOffset ); u32 PTableOffset = GFDCleanTag( pTrailer->patchTableOffset ); u32 NPatches = pTrailer->patchTableOffsetNumber; u32 i; u32 Offset; if(pData == NULL) return FALSE; ASSERT(0 == MainOffset); for(i = 0; i < NPatches; i++) { Offset = *((u32*) (pData + PTableOffset + 4*i)); // dang! Be careful with those parens.. if(Offset != 0) { u32 *pPatchLoc; ASSERT(fromOffset != 0 || GFDCheckTagDAT(Offset) || GFDCheckTagSTR(Offset)); pPatchLoc = ((u32*) (pData + GFDCleanTag(Offset)) ); GFDCheckTagSTR(*pPatchLoc); ASSERT(fromOffset != 0 || GFDCheckTagDAT(*pPatchLoc) || GFDCheckTagSTR(*pPatchLoc)); *pPatchLoc = (u32) ( GFDCleanTag(*pPatchLoc) - fromOffset + toOffset); } } pTrailer->basePatchAddress = toOffset; // store offset away so we don't step on ourselves. return TRUE; } #ifdef __cplusplus } #endif // __cplusplus