////////////////////////////////////////////////////////////////
//
// 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 ();
}
/////////////////////////////////////////////////////////////////////////////
//
// 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::ModelInfoTXDAddTextures
//
// Adds texture into the TXD of a model.
// Returns true if model was affected.
//
////////////////////////////////////////////////////////////////
bool CRenderWareSA::ModelInfoTXDAddTextures ( SReplacementTextures* pReplacementTextures, ushort usModelId )
{
    // Already done for this modelid?
    if ( ListContains ( pReplacementTextures->usedInModelIds, usModelId ) )
        return false;

    // Get valid textures info for this model
    CModelTexturesInfo* pInfo = GetModelTexturesInfo ( usModelId );
    if ( !pInfo )
        return false;

    // Remember which models this set has been applied to
    pReplacementTextures->usedInModelIds.push_back ( usModelId );

    // Already done for this txd?
    if ( ListContains ( pReplacementTextures->usedInTxdIds, pInfo->usTxdId ) )
        return true;    // Return true as model may need restreaming

    //
    // Add section for this txd
    //
    pReplacementTextures->perTxdList.push_back ( SReplacementTextures::SPerTxd () );
    SReplacementTextures::SPerTxd& perTxdInfo = pReplacementTextures->perTxdList.back ();

    perTxdInfo.usTxdId = pInfo->usTxdId;
    perTxdInfo.bTexturesAreCopies = ( pReplacementTextures->usedInTxdIds.size () > 0 );

    // Copy / clone textures
    for ( std::vector < RwTexture* >::iterator iter = pReplacementTextures->textures.begin () ; iter != pReplacementTextures->textures.end () ; iter++ )
    {
        RwTexture* pNewTexture = *iter;

        // Use a copy if not first txd
        if ( perTxdInfo.bTexturesAreCopies )
        {
            // Reuse the given texture's raster
            RwTexture* pCopyTex = RwTextureCreate ( pNewTexture->raster );

            // Copy over additional properties
            MemCpyFast ( &pCopyTex->name, &pNewTexture->name, RW_TEXTURE_NAME_LENGTH );
            MemCpyFast ( &pCopyTex->mask, &pNewTexture->mask, RW_TEXTURE_NAME_LENGTH );
            pCopyTex->flags = pNewTexture->flags;
            
            pNewTexture = pCopyTex;
        }
        perTxdInfo.usingTextures.push_back ( pNewTexture );
    }

    //
    // Add each texture to the target txd
    //
    for ( std::vector < RwTexture* >::iterator iter = perTxdInfo.usingTextures.begin () ; iter != perTxdInfo.usingTextures.end () ; iter++ )
    {
        RwTexture* pNewTexture = *iter;

        // If there is a name clash with an existing texture, replace it
        RwTexture* pExistingTexture = RwTexDictionaryFindNamedTexture ( pInfo->pTxd, pNewTexture->name );
        if ( pExistingTexture )
        {
            RwTexDictionaryRemoveTexture ( pInfo->pTxd, pExistingTexture );
        }

        // Add the texture
        dassert ( !RwTexDictionaryContainsTexture ( pInfo->pTxd, pNewTexture ) );
        RwTexDictionaryAddTexture ( pInfo->pTxd, pNewTexture );
    }

    // Remember which txds this set has been applied to
    pReplacementTextures->usedInTxdIds.push_back ( pInfo->usTxdId );
    dassert ( !ListContains ( pInfo->usedByReplacements, pReplacementTextures ) );
    pInfo->usedByReplacements.push_back ( pReplacementTextures );
    return true;
}