////////////////////////////////////////////////////////////////
//
// CRenderWareSA::ModelInfoTXDLoadTextures
//
// Load textures from a TXD file
//
////////////////////////////////////////////////////////////////
bool CRenderWareSA::ModelInfoTXDLoadTextures ( SReplacementTextures* pReplacementTextures, const SString& strFilename, bool bFilteringEnabled )
{
    // Are we already loaded?
    if ( !pReplacementTextures->textures.empty () )
        return false;

    if ( !g_pCore->GetNetwork ()->CheckFile ( "txd", strFilename ) )
        return false;

    // Try to load it
    RwTexDictionary* pTxd = ReadTXD ( strFilename );
    if ( pTxd )
    {
        // Get the list of textures into our own list
        GetTxdTextures ( pReplacementTextures->textures, pTxd );

        for ( uint i = 0 ; i < pReplacementTextures->textures.size () ; i++ )
        {
            pReplacementTextures->textures[i]->txd = NULL;
            if ( bFilteringEnabled )
                pReplacementTextures->textures[i]->flags = 0x1102;       // Enable filtering (otherwise textures are pixely)
        }

        // Make the txd forget it has any textures and destroy it
        pTxd->textures.root.next = &pTxd->textures.root;
        pTxd->textures.root.prev = &pTxd->textures.root;
        RwTexDictionaryDestroy ( pTxd );
        pTxd = NULL;

        // We succeeded if we got any textures
        return pReplacementTextures->textures.size () > 0;
    }

    return false;
}
////////////////////////////////////////////////////////////////
//
// CRenderWareSA::PulseWorldTextureWatch
//
// Process ms_txdStreamEventList
//
////////////////////////////////////////////////////////////////
void CRenderWareSA::PulseWorldTextureWatch ( void )
{
    FlushPendingAssociations ();

    // Go through ms_txdStreamEventList
    for ( std::vector < STxdStreamEvent >::const_iterator iter = ms_txdStreamEventList.begin () ; iter != ms_txdStreamEventList.end () ; ++iter )
    {
        const STxdStreamEvent& action = *iter;
        if ( action.bAdded )
        {
            //
            // New txd has been loaded
            //

            // Get list of texture names and data to add

            // Note: If txd has been unloaded since, textureList will be empty
            std::vector < RwTexture* > textureList;
            GetTxdTextures ( textureList, action.usTxdId );

            for ( std::vector < RwTexture* > ::iterator iter = textureList.begin () ; iter != textureList.end () ; iter++ )
            {
                RwTexture* texture = *iter;
                const char* szTextureName = texture->name;
                CD3DDUMMY* pD3DData = texture->raster ? (CD3DDUMMY*)texture->raster->renderResource : NULL;
                StreamingAddedTexture ( action.usTxdId, szTextureName, pD3DData );
            }
        }
        else
        {
            //
            // Txd has been unloaded
            //

            StreamingRemovedTxd ( action.usTxdId );
        }
    }

    ms_txdStreamEventList.clear ();
}
////////////////////////////////////////////////////////////////
//
// CRenderWareSA::GetModelTexturesInfo
//
// Find/create texture info for a modelid
//
////////////////////////////////////////////////////////////////
CModelTexturesInfo* CRenderWareSA::GetModelTexturesInfo ( ushort usModelId )
{
    if ( !pGame->GetModelInfo ( usModelId ) )
        return NULL;

    ushort usTxdId = pGame->GetModelInfo ( usModelId )->GetTextureDictionaryID ();

    CModelTexturesInfo* pInfo = MapFind ( ms_ModelTexturesInfoMap, usTxdId );
    if ( !pInfo )
    {
        // Get txd
        RwTexDictionary* pTxd = CTxdStore_GetTxd ( usTxdId );

        if ( !pTxd )
        {
            pGame->GetModelInfo ( usModelId )->Request ( BLOCKING, "CRenderWareSA::GetModelTexturesInfo" );
            CTxdStore_AddRef ( usTxdId );
            ( (void (__cdecl *)(unsigned short))FUNC_RemoveModel )( usModelId );
            pTxd = CTxdStore_GetTxd ( usTxdId );
        }
        else
        {
            CTxdStore_AddRef ( usTxdId );
        }

        if ( !pTxd )
            return NULL;

        // Add new info
        MapSet ( ms_ModelTexturesInfoMap, usTxdId, CModelTexturesInfo () );
        pInfo = MapFind ( ms_ModelTexturesInfoMap, usTxdId );
        pInfo->usTxdId = usTxdId;
        pInfo->pTxd = pTxd;

        // Save original textures
        GetTxdTextures ( pInfo->originalTextures, pInfo->pTxd );
    }

    return pInfo;
}
////////////////////////////////////////////////////////////////
//
// 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 ();
}