void* CCustomCarEnvMapPipeline::pluginSpecMatDestructorCB(void* object, RwInt32 offset, RwInt32 size)
{
    CustomSpecMapPipeMaterialData* data = PLUGIN_SPECMAP(object, data);
    if(data)
    {
        if(data->texture)
        {
            RwTextureDestroy(data->texture);
            data->texture = NULL;
        }
        m_gSpecMapPipeMatDataPool->Delete(data);
        PLUGIN_SPECMAP(object, data) = NULL;

    }
    return object;
}
/////////////////////////////////////////////////////////////////////////////
//
// CRenderWareSA::RightSizeTxd
//
// Check textures in a txd and shrink if required.
// Returns true if shrunk file was written
//
/////////////////////////////////////////////////////////////////////////////
bool CRenderWareSA::RightSizeTxd( const SString& strInTxdFilename, const SString& strOutTxdFilename, uint uiSizeLimit )
{
    //
    // Read txd from memory
    //
    RwStream* pStream = RwStreamOpen( STREAM_TYPE_FILENAME, STREAM_MODE_READ, *strInTxdFilename );
    if ( pStream == NULL )
        return false;

    // Find our txd chunk 
    if ( RwStreamFindChunk( pStream, 0x16, NULL, NULL ) == false )
    {
        RwStreamClose( pStream, NULL );
        return false;
    }

    // read the txd
    RwTexDictionary* pTxd = RwTexDictionaryGtaStreamRead( pStream );
    RwStreamClose( pStream, NULL );
    if ( !pTxd )
        return false;

    //
    // Process each texture in the txd
    //
    std::vector < RwTexture* > textureList;
    pGame->GetRenderWareSA()->GetTxdTextures( textureList, pTxd );

    bool bChanged = false;
    for ( std::vector < RwTexture* > ::iterator iter = textureList.begin() ; iter != textureList.end() ; iter++ )
    {
        RwTexture* pTexture = *iter;
        RwTexture* pNewRwTexture = RightSizeTexture( pTexture, uiSizeLimit );
        if ( pNewRwTexture && pNewRwTexture != pTexture )
        {
            // Replace texture in txd if changed
            RwTextureDestroy( pTexture );
            RwTexDictionaryAddTexture( pTxd, pNewRwTexture );
            bChanged = true;
        }
        else
        {
            // Keep texture (Reinsert to preserve order for easier debugging)
            RwTexDictionaryRemoveTexture( pTxd, pTexture );
            RwTexDictionaryAddTexture( pTxd, pTexture );
        }
    }


    //
    // Save shrunked txd if changed
    //
    if ( bChanged )
    {
        pStream = RwStreamOpen( STREAM_TYPE_FILENAME, STREAM_MODE_WRITE, *strOutTxdFilename );
        if ( pStream )
        {
            RwTexDictionaryStreamWrite( pTxd, pStream );
            RwStreamClose( pStream, NULL );
            RwTexDictionaryDestroy( pTxd );
            return true;
        }
    }

    RwTexDictionaryDestroy( pTxd );
    return false;
}
////////////////////////////////////////////////////////////////
//
// CRenderWareSA::ModelInfoTXDRemoveTextures
//
// Remove the textures from the txds that are using them.
//
////////////////////////////////////////////////////////////////
void CRenderWareSA::ModelInfoTXDRemoveTextures ( SReplacementTextures* pReplacementTextures )
{
    // For each using txd
    for ( uint i = 0 ; i < pReplacementTextures->perTxdList.size () ; i++ )
    {
        SReplacementTextures::SPerTxd& perTxdInfo = pReplacementTextures->perTxdList[i];

        // Get textures info
        ushort usTxdId = perTxdInfo.usTxdId;
        CModelTexturesInfo* pInfo = MapFind ( ms_ModelTexturesInfoMap, usTxdId );

        // Validate
        dassert ( MapFind ( ms_ModelTexturesInfoMap, usTxdId ) );
        dassert ( ListContains ( pInfo->usedByReplacements, pReplacementTextures ) );

        // Remove replacement textures
        for ( uint i = 0 ; i < perTxdInfo.usingTextures.size () ; i++ )
        {
            RwTexture* pOldTexture = perTxdInfo.usingTextures[i];
            RwTexDictionaryRemoveTexture ( pInfo->pTxd, pOldTexture );
            dassert ( !RwTexDictionaryContainsTexture ( pInfo->pTxd, pOldTexture ) );
            if ( perTxdInfo.bTexturesAreCopies )
            {
                // Destroy the copy (but not the raster as that was not copied)
                pOldTexture->raster = NULL;
                RwTextureDestroy ( pOldTexture );
            }
        }

        // Ensure there are original named textures in the txd
        for ( uint i = 0 ; i < pInfo->originalTextures.size () ; i++ )
        {
            RwTexture* pOriginalTexture = pInfo->originalTextures[i];
            if ( !RwTexDictionaryFindNamedTexture ( pInfo->pTxd, pOriginalTexture->name ) )
                RwTexDictionaryAddTexture ( pInfo->pTxd, pOriginalTexture );
        }

        // Remove refs
        ListRemove ( pInfo->usedByReplacements, pReplacementTextures );

        // If no refs left, check original state and then remove info
        if ( pInfo->usedByReplacements.empty () )
        {
            // txd should now contain the same textures as 'originalTextures'
        #ifdef MTA_DEBUG
            std::vector < RwTexture* > currentTextures;
            GetTxdTextures ( currentTextures, pInfo->pTxd );
            assert ( currentTextures.size () == pInfo->originalTextures.size () );
            for ( uint i = 0 ; i < pInfo->originalTextures.size () ; i++ )
            {
                RwTexture* pOriginalTexture = pInfo->originalTextures[i];
                assert ( ListContains ( currentTextures, pOriginalTexture ) );
                ListRemove ( currentTextures, pOriginalTexture );
            }
            assert ( currentTextures.empty () );
        #endif

            // Remove info
            CTxdStore_RemoveRef ( pInfo->usTxdId );
            MapRemove ( ms_ModelTexturesInfoMap, usTxdId );
        }
    }

    // Destroy replacement textures
    for ( uint i = 0 ; i < pReplacementTextures->textures.size () ; i++ )
    {
        RwTexture* pOldTexture = pReplacementTextures->textures[i];
        DestroyTexture ( pOldTexture );
    }
    pReplacementTextures->textures.clear ();
}