void Raster::compressCustom(eCompressionType targetCompressionType) { scoped_rwlock_writer <rwlock> rasterConsistency( GetRasterLock( this ) ); // Make sure we are mutable. NativeCheckRasterMutable( this ); PlatformTexture *platformTex = this->platformData; if ( !platformTex ) { throw RwException( "no native data" ); } Interface *engineInterface = this->engineInterface; texNativeTypeProvider *texProvider = GetNativeTextureTypeProvider( engineInterface, platformTex ); if ( !texProvider ) { throw RwException( "invalid native data" ); } // Avoid recompression. if ( IsNativeTextureCompressed( engineInterface, texProvider, platformTex ) ) return; CompressNativeTexture( engineInterface, texProvider, platformTex, targetCompressionType ); }
void readStringChunkANSI( Interface *engineInterface, BlockProvider& inputProvider, std::string& stringOut ) { BlockProvider stringBlock( &inputProvider ); stringBlock.EnterContext(); try { if ( stringBlock.getBlockID() == CHUNK_STRING ) { int64 chunkLength = stringBlock.getBlockLength(); if ( chunkLength < 0x80000000L ) { size_t strLen = (size_t)chunkLength; char *buffer = new char[ strLen + 1 ]; if ( buffer == NULL ) { throw RwException( "failed to allocate memory for string chunk" ); } try { stringBlock.read(buffer, strLen); buffer[strLen] = '\0'; stringOut = buffer; } catch( ... ) { delete[] buffer; throw; } delete[] buffer; } else { engineInterface->PushWarning( "too long string in string chunk" ); } } else { engineInterface->PushWarning( "could not find string chunk" ); } } catch( ... ) { stringBlock.LeaveContext(); throw; } stringBlock.LeaveContext(); }
void MagnifyFiltering( const resizeColorPipeline& srcBmp, uint32 magX, uint32 magY, uint32 magScaleX, uint32 magScaleY, resizeColorPipeline& dstBmp, uint32 dstX, uint32 dstY ) const override { // TODO. throw RwException( "not supported yet" ); }
void d3d12DriverInterface::RasterInstance( Interface *engineInterface, void *driverObjMem, void *objMem, Raster *sysRaster ) { if ( sysRaster->hasNativeDataOfType( "Direct3D9" ) == false ) { throw RwException( "unsupported raster type for Direct3D 12 native raster instancing" ); } // TODO. }
eCompressionType Raster::getCompressionFormat( void ) const { scoped_rwlock_reader <rwlock> rasterConsistency( GetRasterLock( this ) ); PlatformTexture *platformTex = this->platformData; if ( !platformTex ) { throw RwException( "no native data" ); } Interface *engineInterface = this->engineInterface; texNativeTypeProvider *texProvider = GetNativeTextureTypeProvider( engineInterface, platformTex ); if ( !texProvider ) { throw RwException( "invalid native data" ); } return texProvider->GetTextureCompressionFormat( platformTex ); }
bool ConvertMipmapLayerNative( Interface *engineInterface, uint32 mipWidth, uint32 mipHeight, uint32 layerWidth, uint32 layerHeight, void *srcTexels, uint32 srcDataSize, eRasterFormat srcRasterFormat, uint32 srcDepth, eColorOrdering srcColorOrder, ePaletteType srcPaletteType, const void *srcPaletteData, uint32 srcPaletteSize, eCompressionType srcCompressionType, eRasterFormat dstRasterFormat, uint32 dstDepth, eColorOrdering dstColorOrder, ePaletteType dstPaletteType, const void *dstPaletteData, uint32 dstPaletteSize, eCompressionType dstCompressionType, bool copyAnyway, uint32& dstPlaneWidthOut, uint32& dstPlaneHeightOut, void*& dstTexelsOut, uint32& dstDataSizeOut ) { bool isMipLayerTexels = true; // Perform this like a pipeline with multiple stages. uint32 srcDXTType; uint32 dstDXTType; bool isSrcDXTType = IsDXTCompressionType( srcCompressionType, srcDXTType ); bool isDstDXTType = IsDXTCompressionType( dstCompressionType, dstDXTType ); bool requiresCompression = ( srcCompressionType != dstCompressionType ); if ( requiresCompression ) { if ( isSrcDXTType ) { assert( srcPaletteType == PALETTE_NONE ); // Decompress stuff. eDXTCompressionMethod dxtMethod = engineInterface->GetDXTRuntime(); void *decompressedTexels; uint32 decompressedSize; bool success = decompressTexelsUsingDXT( engineInterface, srcDXTType, dxtMethod, mipWidth, mipHeight, layerWidth, layerHeight, srcTexels, dstRasterFormat, dstColorOrder, dstDepth, decompressedTexels, decompressedSize ); assert( success == true ); // Update with raw raster data. srcRasterFormat = dstRasterFormat; srcColorOrder = dstColorOrder; srcDepth = dstDepth; srcTexels = decompressedTexels; srcDataSize = decompressedSize; mipWidth = layerWidth; mipHeight = layerHeight; srcCompressionType = RWCOMPRESS_NONE; isMipLayerTexels = false; } } if ( srcCompressionType == RWCOMPRESS_NONE && dstCompressionType == RWCOMPRESS_NONE ) { uint32 texUnitCount = ( mipWidth * mipHeight ); void *newtexels = NULL; uint32 dstDataSize = 0; if ( dstPaletteType == PALETTE_NONE ) { dstDataSize = getRasterDataSize( texUnitCount, dstDepth ); newtexels = engineInterface->PixelAllocate( dstDataSize ); // Do the conversion. for ( uint32 n = 0; n < texUnitCount; n++ ) { uint8 r, g, b, a; bool gotColor = browsetexelcolor( srcTexels, srcPaletteType, srcPaletteData, srcPaletteSize, n, srcRasterFormat, srcColorOrder, srcDepth, r, g, b, a ); if ( !gotColor ) { r = 0; g = 0; b = 0; a = 0; } puttexelcolor( newtexels, n, dstRasterFormat, dstColorOrder, dstDepth, r, g, b, a ); } } else if ( srcPaletteType != PALETTE_NONE ) { if ( srcPaletteData == dstPaletteData ) { // Fix the indice, if necessary. if ( srcDepth != dstDepth ) { dstDataSize = getRasterDataSize( texUnitCount, dstDepth ); newtexels = engineInterface->PixelAllocate( dstDataSize ); // Convert the depth. ConvertPaletteDepth( srcTexels, newtexels, texUnitCount, srcPaletteType, srcPaletteSize, srcDepth, dstDepth ); } } else { // Remap texels. RemapMipmapLayer( engineInterface, dstRasterFormat, dstColorOrder, srcTexels, texUnitCount, srcRasterFormat, srcColorOrder, srcDepth, srcPaletteType, srcPaletteData, srcPaletteSize, dstPaletteData, dstPaletteSize, dstDepth, dstPaletteType, newtexels, dstDataSize ); } } else { // There must be a destination palette already allocated, as we cannot create one. assert( dstPaletteData != NULL ); // Remap. RemapMipmapLayer( engineInterface, dstRasterFormat, dstColorOrder, srcTexels, texUnitCount, srcRasterFormat, srcColorOrder, srcDepth, srcPaletteType, srcPaletteData, srcPaletteSize, dstPaletteData, dstPaletteSize, dstDepth, dstPaletteType, newtexels, dstDataSize ); } if ( newtexels != NULL ) { if ( isMipLayerTexels == false ) { // If we have temporary texels, remove them. engineInterface->PixelFree( srcTexels ); } // Store the new texels. srcTexels = newtexels; srcDataSize = dstDataSize; // Update raster properties. srcRasterFormat = dstRasterFormat; srcColorOrder = dstColorOrder; srcDepth = dstDepth; isMipLayerTexels = false; } } if ( requiresCompression ) { // Perform compression now. if ( isDstDXTType ) { if ( dstDXTType == 2 || dstDXTType == 4 ) { throw RwException( "unsupported DXT target in pixel conversion routine (sorry)" ); } void *dstTexels; uint32 dstDataSize; uint32 newWidth, newHeight; compressTexelsUsingDXT( engineInterface, dstDXTType, srcTexels, mipWidth, mipHeight, srcRasterFormat, srcPaletteData, srcPaletteType, srcPaletteSize, srcColorOrder, srcDepth, dstTexels, dstDataSize, newWidth, newHeight ); // Delete old texels (if necessary). if ( isMipLayerTexels == false ) { engineInterface->PixelFree( srcTexels ); } // Update parameters. srcTexels = dstTexels; srcDataSize = dstDataSize; // Clear raster format (for safety). srcRasterFormat = RASTER_DEFAULT; srcColorOrder = COLOR_BGRA; srcDepth = 16; mipWidth = newWidth; mipHeight = newHeight; srcCompressionType = dstCompressionType; isMipLayerTexels = false; } } // Output parameters. if ( copyAnyway == true || isMipLayerTexels == false ) { dstPlaneWidthOut = mipWidth; dstPlaneHeightOut = mipHeight; dstTexelsOut = srcTexels; dstDataSizeOut = srcDataSize; return true; } return false; }
void genericCompressDXTNative( Interface *engineInterface, pixelDataTraversal& pixelData, uint32 dxtType ) { // We must get data in raw format. if ( pixelData.compressionType != RWCOMPRESS_NONE ) { throw RwException( "runtime fault: attempting to compress an already compressed texture" ); } if (dxtType != 1 && dxtType != 3 && dxtType != 5) { throw RwException( "cannot compress Direct3D textures using unsupported DXTn format" ); } // We must have stand-alone pixel data. // Otherwise we could mess up pretty badly! assert( pixelData.isNewlyAllocated == true ); // Compress it now. uint32 mipmapCount = pixelData.mipmaps.size(); uint32 itemDepth = pixelData.depth; eRasterFormat rasterFormat = pixelData.rasterFormat; ePaletteType paletteType = pixelData.paletteType; eColorOrdering colorOrder = pixelData.colorOrder; uint32 maxpalette = pixelData.paletteSize; void *paletteData = pixelData.paletteData; for ( uint32 n = 0; n < mipmapCount; n++ ) { pixelDataTraversal::mipmapResource& mipLayer = pixelData.mipmaps[ n ]; uint32 mipWidth = mipLayer.width; uint32 mipHeight = mipLayer.height; void *texelSource = mipLayer.texels; void *dxtArray = NULL; size_t dxtDataSize = 0; // Create the new DXT array. uint32 realMipWidth, realMipHeight; compressTexelsUsingDXT( engineInterface, dxtType, texelSource, mipWidth, mipHeight, rasterFormat, paletteData, paletteType, maxpalette, colorOrder, itemDepth, dxtArray, dxtDataSize, realMipWidth, realMipHeight ); // Delete the raw texels. engineInterface->PixelFree( texelSource ); if ( mipWidth != realMipWidth ) { mipLayer.width = realMipWidth; } if ( mipHeight != realMipHeight ) { mipLayer.height = realMipHeight; } // Put in the new DXTn texels. mipLayer.texels = dxtArray; // Update fields. mipLayer.dataSize = dxtDataSize; } // We are finished compressing. // If we were palettized, unset that. if ( paletteType != PALETTE_NONE ) { // Free the palette colors. engineInterface->PixelFree( paletteData ); // Reset the fields. pixelData.paletteType = PALETTE_NONE; pixelData.paletteData = NULL; pixelData.paletteSize = 0; } // Set a virtual raster format. // This is what is done by the R* DXT output system. { uint32 newDepth = itemDepth; eRasterFormat virtualRasterFormat = RASTER_8888; if ( dxtType == 1 ) { newDepth = 16; if ( pixelData.hasAlpha ) { virtualRasterFormat = RASTER_1555; } else { virtualRasterFormat = RASTER_565; } } else if ( dxtType == 2 || dxtType == 3 || dxtType == 4 || dxtType == 5 ) { newDepth = 16; virtualRasterFormat = RASTER_4444; } if ( rasterFormat != virtualRasterFormat ) { pixelData.rasterFormat = virtualRasterFormat; } if ( newDepth != itemDepth ) { pixelData.depth = newDepth; } } // We are now successfully compressed, so set the correct compression type. eCompressionType targetCompressionType = RWCOMPRESS_NONE; if ( dxtType == 1 ) { targetCompressionType = RWCOMPRESS_DXT1; } else if ( dxtType == 2 ) { targetCompressionType = RWCOMPRESS_DXT2; } else if ( dxtType == 3 ) { targetCompressionType = RWCOMPRESS_DXT3; } else if ( dxtType == 4 ) { targetCompressionType = RWCOMPRESS_DXT4; } else if ( dxtType == 5 ) { targetCompressionType = RWCOMPRESS_DXT5; } else { throw RwException( "runtime fault: unknown compression type request in DXT compressor" ); } pixelData.compressionType = targetCompressionType; }
void Raster::optimizeForLowEnd(float quality) { PlatformTexture *platformTex = this->platformData; if ( !platformTex ) { throw RwException( "no native data" ); } Interface *engineInterface = this->engineInterface; texNativeTypeProvider *texProvider = GetNativeTextureTypeProvider( engineInterface, platformTex ); if ( !texProvider ) { throw RwException( "invalid native data" ); } // To make good decisions, we need the storage capabilities of the texture. storageCapabilities storeCaps; texProvider->GetStorageCapabilities( storeCaps ); // Textures that should run on low end hardware should not be too HD. // This routine takes the PlayStation 2 as reference hardware. const uint32 maxTexWidth = 256; const uint32 maxTexHeight = 256; while ( true ) { // Get properties of this texture first. uint32 texWidth, texHeight; nativeTextureBatchedInfo nativeInfo; texProvider->GetTextureInfo( engineInterface, platformTex, nativeInfo ); texWidth = nativeInfo.baseWidth; texHeight = nativeInfo.baseHeight; // Optimize this texture. bool hasTakenStep = false; if ( !hasTakenStep && quality < 1.0f ) { // We first must make sure that the texture is not unnecessaringly huge. if ( texWidth > maxTexWidth || texHeight > maxTexHeight ) { // Half the texture size until we are at a suitable size. uint32 targetWidth = texWidth; uint32 targetHeight = texHeight; do { targetWidth /= 2; targetHeight /= 2; } while ( targetWidth > maxTexWidth || targetHeight > maxTexHeight ); // The texture dimensions are too wide. // We half the texture in size. this->resize( targetWidth, targetHeight ); hasTakenStep = true; } } if ( !hasTakenStep ) { // Can we store palette data? if ( storeCaps.pixelCaps.supportsPalette == true ) { // We should decrease the texture size by palettization. ePaletteType currentPaletteType = texProvider->GetTexturePaletteType( platformTex ); if (currentPaletteType == PALETTE_NONE) { ePaletteType targetPalette = ( quality > 0.0f ) ? ( PALETTE_8BIT ) : ( PALETTE_4BIT ); // TODO: per mipmap alpha checking. if (texProvider->DoesTextureHaveAlpha( platformTex ) == false) { // 4bit palette is only feasible for non-alpha textures (at higher quality settings). // Otherwise counting the colors is too complicated. // TODO: still allow 8bit textures. targetPalette = PALETTE_4BIT; } // The texture should be palettized for good measure. this->convertToPalette( targetPalette ); // TODO: decide whether 4bit or 8bit palette. hasTakenStep = true; } } } if ( !hasTakenStep ) { // The texture dimension is important for renderer performance. That is why we need to scale the texture according // to quality settings aswell. } if ( !hasTakenStep ) { break; } } }
void Raster::compress( float quality ) { scoped_rwlock_writer <rwlock> rasterConsistency( GetRasterLock( this ) ); // Make sure we are mutable. NativeCheckRasterMutable( this ); // A pretty complicated algorithm that can be used to optimally compress rasters. // Currently this only supports DXT. PlatformTexture *platformTex = this->platformData; if ( !platformTex ) { throw RwException( "no native data" ); } Interface *engineInterface = this->engineInterface; texNativeTypeProvider *texProvider = GetNativeTextureTypeProvider( engineInterface, platformTex ); if ( !texProvider ) { throw RwException( "invalid native data" ); } // We should not continue if we are compressed already. storageCapabilities storeCaps; if ( IsNativeTextureCompressed( engineInterface, texProvider, platformTex, &storeCaps ) ) return; // We want to check what kind of compression the target architecture supports. // If we have any compression support, we proceed to next stage. bool supportsDXT1 = false; bool supportsDXT2 = false; bool supportsDXT3 = false; bool supportsDXT4 = false; bool supportsDXT5 = false; pixelCapabilities inputTransferCaps; texProvider->GetPixelCapabilities( inputTransferCaps ); // Now decide upon things. if ( inputTransferCaps.supportsDXT1 && storeCaps.pixelCaps.supportsDXT1 ) { supportsDXT1 = true; } if ( inputTransferCaps.supportsDXT2 && storeCaps.pixelCaps.supportsDXT2 ) { supportsDXT2 = true; } if ( inputTransferCaps.supportsDXT3 && storeCaps.pixelCaps.supportsDXT3 ) { supportsDXT3 = true; } if ( inputTransferCaps.supportsDXT4 && storeCaps.pixelCaps.supportsDXT4 ) { supportsDXT4 = true; } if ( inputTransferCaps.supportsDXT5 && storeCaps.pixelCaps.supportsDXT5 ) { supportsDXT5 = true; } if ( supportsDXT1 == false && supportsDXT2 == false && supportsDXT3 == false && supportsDXT4 == false && supportsDXT5 == false ) { throw RwException( "attempted to compress a raster that does not support compression" ); } // We need to know about the alpha status of the texture to make a good decision. bool texHasAlpha = texProvider->DoesTextureHaveAlpha( platformTex ); // Decide now what compression we want. eCompressionType targetCompressionType = RWCOMPRESS_NONE; bool couldDecide = DecideBestDXTCompressionFormat( engineInterface, texHasAlpha, supportsDXT1, supportsDXT2, supportsDXT3, supportsDXT4, supportsDXT5, quality, targetCompressionType ); if ( !couldDecide ) { throw RwException( "could not decide on an optimal DXT compression type" ); } CompressNativeTexture( engineInterface, texProvider, platformTex, targetCompressionType ); }
static AINLINE void CompressNativeTexture( Interface *engineInterface, texNativeTypeProvider *texProvider, PlatformTexture *platformTex, eCompressionType targetCompressionType ) { // Since we now know about everything, we can take the pixels and perform the compression. pixelDataTraversal pixelData; texProvider->GetPixelDataFromTexture( engineInterface, platformTex, pixelData ); bool hasDirectlyAcquired = false; try { // Unset the pixels from the texture and make them standalone. texProvider->UnsetPixelDataFromTexture( engineInterface, platformTex, pixelData.isNewlyAllocated == true ); pixelData.SetStandalone(); // Compress things. pixelFormat targetPixelFormat; targetPixelFormat.rasterFormat = pixelData.rasterFormat; targetPixelFormat.depth = pixelData.depth; targetPixelFormat.rowAlignment = 0; targetPixelFormat.colorOrder = pixelData.colorOrder; targetPixelFormat.paletteType = PALETTE_NONE; targetPixelFormat.compressionType = targetCompressionType; bool hasCompressed = ConvertPixelData( engineInterface, pixelData, targetPixelFormat ); if ( !hasCompressed ) { throw RwException( "failed to compress raster" ); } AdjustPixelDataDimensionsByFormat( engineInterface, texProvider, pixelData ); // Put the new pixels into the texture. texNativeTypeProvider::acquireFeedback_t acquireFeedback; texProvider->SetPixelDataToTexture( engineInterface, platformTex, pixelData, acquireFeedback ); hasDirectlyAcquired = acquireFeedback.hasDirectlyAcquired; } catch( ... ) { pixelData.FreePixels( engineInterface ); throw; } if ( hasDirectlyAcquired == false ) { // Should never happen. pixelData.FreePixels( engineInterface ); } else { pixelData.DetachPixels(); } }
void uncNativeTextureTypeProvider::DeserializeTexture( TextureBase *theTexture, PlatformTexture *nativeTex, BlockProvider& inputProvider ) const { Interface *engineInterface = theTexture->engineInterface; // Read the texture native block. { BlockProvider texImageDataBlock( &inputProvider ); texImageDataBlock.EnterContext(); try { if ( texImageDataBlock.getBlockID() == CHUNK_STRUCT ) { // Read the meta header first. mobile_unc::textureNativeGenericHeader metaHeader; texImageDataBlock.read( &metaHeader, sizeof( metaHeader ) ); // Make sure we got the right platform descriptor. if ( metaHeader.platformDescriptor != PLATFORMDESC_UNC_MOBILE ) { throw RwException( "invalid platform descriptor in uncompressed mobile texture native" ); } // Cast out native texture type. NativeTextureMobileUNC *platformTex = (NativeTextureMobileUNC*)nativeTex; // Read the format info. metaHeader.formatInfo.parse( *theTexture ); // Read the texture names. { char tmpbuf[ sizeof( metaHeader.name ) + 1 ]; // Make sure the name buffer is zero terminted. tmpbuf[ sizeof( metaHeader.name ) ] = '\0'; // Move over the texture name. memcpy( tmpbuf, metaHeader.name, sizeof( metaHeader.name ) ); theTexture->SetName( tmpbuf ); // Move over the texture mask name. memcpy( tmpbuf, metaHeader.maskName, sizeof( metaHeader.maskName ) ); theTexture->SetMaskName( tmpbuf ); } // Read some advanced properties. bool hasAlpha = metaHeader.hasAlpha; platformTex->hasAlpha = hasAlpha; platformTex->unk2 = metaHeader.unk2; platformTex->unk3 = metaHeader.unk3; assert( metaHeader.unk1 == false ); assert( metaHeader.unk2 == 0 ); // This texture format is very primitive. // It supports only RASTER_4444 textures with 16 depth. // Everything that this format stores is already storable in the Direct3D 8/9 platform. eRasterFormat rasterFormat; uint32 depth; eColorOrdering colorOrder; getUNCRasterFormat( hasAlpha, rasterFormat, colorOrder, depth ); // Parse all mipmaps. // This format is pretty simple. uint32 maybeMipmapCount = metaHeader.mipmapCount; mipGenLevelGenerator mipLevelGen( metaHeader.width, metaHeader.height ); if ( !mipLevelGen.isValidLevel() ) { throw RwException( "texture " + theTexture->GetName() + " has invalid dimensions" ); } uint32 mipmap_index = 0; uint32 remainingTexImageDataSize = metaHeader.imageDataSectionSize; while ( true ) { if ( remainingTexImageDataSize == 0 ) { break; } if ( mipmap_index >= maybeMipmapCount ) { break; } bool couldEstablishLevel = true; if ( mipmap_index > 0 ) { couldEstablishLevel = mipLevelGen.incrementLevel(); } if ( !couldEstablishLevel ) { break; } // Create the new mipmap layer. NativeTextureMobileUNC::mipmapLayer newLayer; uint32 width = mipLevelGen.getLevelWidth(); uint32 height = mipLevelGen.getLevelHeight(); newLayer.layerWidth = width; newLayer.layerHeight = height; // Since we are an uncompressed texture, the layer dimensions equal the raw dimensions. newLayer.width = width; newLayer.height = height; // Calculate the size of this layer. uint32 texRowSize = getUNCRasterDataRowSize( width, depth ); uint32 texDataSize = getRasterDataSizeByRowSize( texRowSize, height ); // Reduce the texture image data section remainder. if ( remainingTexImageDataSize < texDataSize ) { throw RwException( "texture " + theTexture->GetName() + " has an invalid image data stream section size" ); } remainingTexImageDataSize -= texDataSize; // Store the texels. texImageDataBlock.check_read_ahead( texDataSize ); void *texels = engineInterface->PixelAllocate( texDataSize ); try { texImageDataBlock.read( texels, texDataSize ); } catch( ... ) { engineInterface->PixelFree( texels ); throw; } newLayer.texels = texels; newLayer.dataSize = texDataSize; // Store this layer. platformTex->mipmaps.push_back( newLayer ); // Increase the mipmap index. mipmap_index++; } // We do not want no empty textures. if ( mipmap_index == 0 ) { throw RwException( "texture " + theTexture->GetName() + " is empty" ); } // Fix filtering mode. fixFilteringMode( *theTexture, mipmap_index ); int warningLevel = engineInterface->GetWarningLevel(); // Check whether we have any remaining texture image data. if ( remainingTexImageDataSize != 0 ) { if ( warningLevel >= 3 ) { engineInterface->PushWarning( "texture " + theTexture->GetName() + " has image data section meta-data" ); } // Skip the meta-data. texImageDataBlock.skip( remainingTexImageDataSize ); } // We are done! } else { throw RwException( "could not find tex image data block in uncompressed mobile texture native" ); } } catch( ... ) { texImageDataBlock.LeaveContext(); throw; } texImageDataBlock.LeaveContext(); } // Deserialize extensions. engineInterface->DeserializeExtensions( theTexture, inputProvider ); }
void d3d8NativeTextureTypeProvider::DeserializeTexture( TextureBase *theTexture, PlatformTexture *nativeTex, BlockProvider& inputProvider ) const { Interface *engineInterface = theTexture->engineInterface; { BlockProvider texNativeImageStruct( &inputProvider ); texNativeImageStruct.EnterContext(); try { if ( texNativeImageStruct.getBlockID() == CHUNK_STRUCT ) { d3d8::textureMetaHeaderStructGeneric metaHeader; texNativeImageStruct.read( &metaHeader, sizeof(metaHeader) ); uint32 platform = metaHeader.platformDescriptor; if (platform != PLATFORM_D3D8) { throw RwException( "invalid platform type in Direct3D 8 texture reading" ); } // Recast the texture to our native type. NativeTextureD3D8 *platformTex = (NativeTextureD3D8*)nativeTex; int engineWarningLevel = engineInterface->GetWarningLevel(); bool engineIgnoreSecureWarnings = engineInterface->GetIgnoreSecureWarnings(); // Read the texture names. { char tmpbuf[ sizeof( metaHeader.name ) + 1 ]; // Make sure the name buffer is zero terminted. tmpbuf[ sizeof( metaHeader.name ) ] = '\0'; // Move over the texture name. memcpy( tmpbuf, metaHeader.name, sizeof( metaHeader.name ) ); theTexture->SetName( tmpbuf ); // Move over the texture mask name. memcpy( tmpbuf, metaHeader.maskName, sizeof( metaHeader.maskName ) ); theTexture->SetMaskName( tmpbuf ); } // Read texture format. texFormatInfo formatInfo = metaHeader.texFormat; formatInfo.parse( *theTexture ); // Deconstruct the format flags. bool hasMipmaps = false; // TODO: actually use this flag. readRasterFormatFlags( metaHeader.rasterFormat, platformTex->rasterFormat, platformTex->paletteType, hasMipmaps, platformTex->autoMipmaps ); platformTex->hasAlpha = ( metaHeader.hasAlpha != 0 ? true : false ); uint32 depth = metaHeader.depth; uint32 maybeMipmapCount = metaHeader.mipmapCount; platformTex->depth = depth; platformTex->rasterType = metaHeader.rasterType; // Decide about the color order. { eColorOrdering colorOrder = COLOR_BGRA; if ( platformTex->paletteType != PALETTE_NONE ) { colorOrder = COLOR_RGBA; } platformTex->colorOrdering = colorOrder; } // Read compression information. { uint32 dxtInfo = metaHeader.dxtCompression; platformTex->dxtCompression = dxtInfo; // If we are compressed, it must be a compression we know about. if ( dxtInfo != 0 ) { if ( dxtInfo != 1 && dxtInfo != 2 && dxtInfo != 3 && dxtInfo != 4 && dxtInfo != 5 ) { throw RwException( "invalid Direct3D texture compression format" ); } } } // Verify raster properties and attempt to fix broken textures. // Broken textures travel with mods like San Andreas Retextured. // - Verify depth. { bool hasInvalidDepth = false; if (platformTex->paletteType == PALETTE_4BIT) { if (depth != 4 && depth != 8) { hasInvalidDepth = true; } } else if (platformTex->paletteType == PALETTE_8BIT) { if (depth != 8) { hasInvalidDepth = true; } } if (hasInvalidDepth == true) { throw RwException( "texture " + theTexture->GetName() + " has an invalid depth" ); // We cannot fix an invalid depth. } } if (platformTex->paletteType != PALETTE_NONE) { uint32 reqPalItemCount = getD3DPaletteCount( platformTex->paletteType ); uint32 palDepth = Bitmap::getRasterFormatDepth( platformTex->rasterFormat ); assert( palDepth != 0 ); size_t paletteDataSize = getPaletteDataSize( reqPalItemCount, palDepth ); // Check whether we have palette data in the stream. texNativeImageStruct.check_read_ahead( paletteDataSize ); void *palData = engineInterface->PixelAllocate( paletteDataSize ); try { texNativeImageStruct.read( palData, paletteDataSize ); } catch( ... ) { engineInterface->PixelFree( palData ); throw; } // Store the palette. platformTex->palette = palData; platformTex->paletteSize = reqPalItemCount; } mipGenLevelGenerator mipLevelGen( metaHeader.width, metaHeader.height ); if ( !mipLevelGen.isValidLevel() ) { throw RwException( "texture " + theTexture->GetName() + " has invalid dimensions" ); } uint32 mipmapCount = 0; uint32 processedMipmapCount = 0; uint32 dxtCompression = platformTex->dxtCompression; bool hasDamagedMipmaps = false; for (uint32 i = 0; i < maybeMipmapCount; i++) { bool couldEstablishLevel = true; if (i > 0) { couldEstablishLevel = mipLevelGen.incrementLevel(); } if (!couldEstablishLevel) { break; } // Create a new mipmap layer. NativeTextureD3D8::mipmapLayer newLayer; newLayer.layerWidth = mipLevelGen.getLevelWidth(); newLayer.layerHeight = mipLevelGen.getLevelHeight(); // Process dimensions. uint32 texWidth = newLayer.layerWidth; uint32 texHeight = newLayer.layerHeight; { // DXT compression works on 4x4 blocks, // no smaller values allowed if (dxtCompression != 0) { texWidth = ALIGN_SIZE( texWidth, 4u ); texHeight = ALIGN_SIZE( texHeight, 4u ); } } newLayer.width = texWidth; newLayer.height = texHeight; uint32 texDataSize = texNativeImageStruct.readUInt32(); // We started processing this mipmap. processedMipmapCount++; // Verify the data size. bool isValidMipmap = true; { uint32 actualDataSize = 0; if (dxtCompression != 0) { uint32 texItemCount = ( texWidth * texHeight ); actualDataSize = getDXTRasterDataSize(dxtCompression, texItemCount); } else { uint32 rowSize = getD3DRasterDataRowSize( texWidth, depth ); actualDataSize = getRasterDataSizeByRowSize( rowSize, texHeight ); } if (actualDataSize != texDataSize) { isValidMipmap = false; } } if ( !isValidMipmap ) { // Even the Rockstar games texture generator appeared to have problems with mipmap generation. // This is why textures appear to have the size of zero. if (texDataSize != 0) { if ( !engineIgnoreSecureWarnings ) { engineInterface->PushWarning( "texture " + theTexture->GetName() + " has damaged mipmaps (ignoring)" ); } hasDamagedMipmaps = true; } // Skip the damaged bytes. if (texDataSize != 0) { texNativeImageStruct.skip( texDataSize ); } break; } // We first have to check whether there is enough data in the stream. // Otherwise we would just flood the memory in case of an error; // that could be abused by exploiters. texNativeImageStruct.check_read_ahead( texDataSize ); void *texelData = engineInterface->PixelAllocate( texDataSize ); try { texNativeImageStruct.read( texelData, texDataSize ); } catch( ... ) { engineInterface->PixelFree( texelData ); throw; } // Store mipmap properties. newLayer.dataSize = texDataSize; // Store the image data pointer. newLayer.texels = texelData; // Put the layer. platformTex->mipmaps.push_back( newLayer ); mipmapCount++; } if ( mipmapCount == 0 ) { throw RwException( "texture " + theTexture->GetName() + " is empty" ); } // mipmapCount can only be smaller than maybeMipmapCount. // This is logically true and would be absurd to assert here. if ( processedMipmapCount < maybeMipmapCount ) { // Skip the remaining mipmaps (most likely zero-sized). bool hasSkippedNonZeroSized = false; for ( uint32 n = processedMipmapCount; n < maybeMipmapCount; n++ ) { uint32 mipSize = texNativeImageStruct.readUInt32(); if ( mipSize != 0 ) { hasSkippedNonZeroSized = true; // Skip the section. texNativeImageStruct.skip( mipSize ); } } if ( !engineIgnoreSecureWarnings && !hasDamagedMipmaps ) { // Print the debug message. if ( !hasSkippedNonZeroSized ) { engineInterface->PushWarning( "texture " + theTexture->GetName() + " has zero sized mipmaps" ); } else { engineInterface->PushWarning( "texture " + theTexture->GetName() + " violates mipmap rules" ); } } } // Fix filtering mode. fixFilteringMode( *theTexture, mipmapCount ); // - Verify auto mipmap { bool hasAutoMipmaps = platformTex->autoMipmaps; if ( hasAutoMipmaps ) { bool canHaveAutoMipmaps = ( mipmapCount == 1 ); if ( !canHaveAutoMipmaps ) { engineInterface->PushWarning( "texture " + theTexture->GetName() + " has an invalid auto-mipmap flag (fixing)" ); platformTex->autoMipmaps = false; } } } } else { engineInterface->PushWarning( "failed to find texture native image struct in D3D texture native" ); } } catch( ... ) { texNativeImageStruct.LeaveContext(); throw; } texNativeImageStruct.LeaveContext(); } // Read extensions. engineInterface->DeserializeExtensions( theTexture, inputProvider ); }
void xboxNativeTextureTypeProvider::SerializeTexture( TextureBase *theTexture, PlatformTexture *nativeTex, BlockProvider& outputProvider ) const { Interface *engineInterface = theTexture->engineInterface; // Cast the texture to our native type. NativeTextureXBOX *platformTex = (NativeTextureXBOX*)nativeTex; // Debug some essentials. ePaletteType paletteType = platformTex->paletteType; uint32 compressionType = platformTex->dxtCompression; // If we are not compressed, then the color order matters. if ( compressionType == 0 ) { // XBOX textures are always BGRA. eColorOrdering requiredColorOrder = COLOR_BGRA; if ( platformTex->colorOrder != requiredColorOrder ) { throw RwException( "texture " + theTexture->GetName() + " has an invalid color ordering for writing" ); } } // Write the struct. { BlockProvider texImageDataBlock( &outputProvider ); texImageDataBlock.EnterContext(); try { // First comes the platform id. texImageDataBlock.writeUInt32( NATIVE_TEXTURE_XBOX ); // Write the header. size_t mipmapCount = platformTex->mipmaps.size(); { textureMetaHeaderStructXbox metaInfo; // Write addressing information. texFormatInfo infoOut; infoOut.set( *theTexture ); metaInfo.formatInfo = infoOut; // Write texture names. // These need to be written securely. writeStringIntoBufferSafe( engineInterface, theTexture->GetName(), metaInfo.name, sizeof( metaInfo.name ), theTexture->GetName(), "name" ); writeStringIntoBufferSafe( engineInterface, theTexture->GetMaskName(), metaInfo.maskName, sizeof( metaInfo.maskName ), theTexture->GetName(), "mask name" ); // Construct raster flags. uint32 rasterFlags = generateRasterFormatFlags(platformTex->rasterFormat, paletteType, mipmapCount > 1, platformTex->autoMipmaps); // Store the flags. metaInfo.rasterFormat = rasterFlags; metaInfo.hasAlpha = ( platformTex->hasAlpha ? 1 : 0 ); metaInfo.isCubeMap = ( platformTex->isCubeMap ? 1 : 0 ); metaInfo.mipmapCount = (uint8)mipmapCount; metaInfo.rasterType = platformTex->rasterType; metaInfo.dxtCompression = compressionType; // Write the dimensions. metaInfo.width = platformTex->mipmaps[ 0 ].layerWidth; metaInfo.height = platformTex->mipmaps[ 0 ].layerHeight; metaInfo.depth = platformTex->depth; // Calculate the size of all the texture data combined. uint32 imageDataSectionSize = 0; for (size_t n = 0; n < mipmapCount; n++) { imageDataSectionSize += platformTex->mipmaps[ n ].dataSize; } metaInfo.imageDataSectionSize = imageDataSectionSize; // Write the generic header. texImageDataBlock.write( &metaInfo, sizeof( metaInfo ) ); } // Write palette data (if available). if (paletteType != PALETTE_NONE) { // Make sure we write as much data as the system expects. uint32 reqPalCount = getD3DPaletteCount(paletteType); uint32 palItemCount = platformTex->paletteSize; // Get the real data size of the palette. uint32 palRasterDepth = Bitmap::getRasterFormatDepth(platformTex->rasterFormat); uint32 paletteDataSize = getPaletteDataSize( palItemCount, palRasterDepth ); uint32 palByteWriteCount = writePartialBlockSafe(texImageDataBlock, platformTex->palette, paletteDataSize, getPaletteDataSize(reqPalCount, palRasterDepth)); assert( palByteWriteCount * 8 / palRasterDepth == reqPalCount ); } // Write mipmap data. for ( size_t n = 0; n < mipmapCount; n++ ) { const NativeTextureXBOX::mipmapLayer& mipLayer = platformTex->mipmaps[ n ]; uint32 texDataSize = mipLayer.dataSize; const void *texelData = mipLayer.texels; texImageDataBlock.write( texelData, texDataSize ); } } catch( ... ) { texImageDataBlock.LeaveContext(); throw; } texImageDataBlock.LeaveContext(); } // Extension engineInterface->SerializeExtensions( theTexture, outputProvider ); }