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 ); }