/// <summary>
        /// Installs the specified r2d to the specified export. Specify an asset name if you wish to use a specific asset in the RTexture2D object.
        /// </summary>
        /// <param name="r2d"></param>
        /// <param name="export"></param>
        /// <param name="asset"></param>
        public static void InstallTexture(RTexture2D r2d, ExportEntry export, string asset = null)
        {
            // If no asset was specified pick a random asset
            asset ??= r2d.FetchRandomTextureAsset();

            // 2. Is this asset already parsed?
            if (r2d.InstantiatedItems.TryGetValue(asset, out var instantiated))
            {
                // It's already been instantiated. Just use this data instead
                MERLog.Information($@"Writing out cached asset {asset} for {export.InstancedFullPath}");
                export.WritePropertiesAndBinary(instantiated.props, instantiated.texData);
            }
            else
            {
                MERLog.Information($@"Installing texture asset {asset} for {export.InstancedFullPath}");

                // 3. Asset has not been setup yet. Write out the precomputed data.
                export.WriteBinary(GetTextureAssetBinary(asset));

                // 4. Read in the data so it's in the correct context.
                var ut2d = ObjectBinary.From <UTexture2D>(export);

                // 5. Move compressed mips to TFC
                foreach (var mip in ut2d.Mips)
                {
                    if (mip.IsCompressed)
                    {
                        mip.DataOffset = TFCBuilder.WriteMip(r2d, mip.Mip); // If it's compressed it needs to be stored externally.
                        if (mip.StorageType == StorageTypes.pccLZO)
                        {
                            mip.StorageType = StorageTypes.extLZO;
                        }
                        if (mip.StorageType == StorageTypes.pccZlib)
                        {
                            mip.StorageType = StorageTypes.extZlib;
                        }
                    }
                }

                // 6. Setup the properties
                var props = export.GetProperties();
                props.AddOrReplaceProp(new IntProperty(ut2d.Mips.Count - 1, @"MipTailBaseIdx"));
                props.AddOrReplaceProp(new IntProperty(ut2d.Mips[0].SizeX, @"SizeX"));
                props.AddOrReplaceProp(new IntProperty(ut2d.Mips[0].SizeY, @"SizeY"));
                props.AddOrReplaceProp(r2d.PreMountTexture ? ActiveBuilder.BGTFCNameProp : ActiveBuilder.DLCTFCNameProp);
                props.AddOrReplaceProp(r2d.PreMountTexture ? ActiveBuilder.BGTFCGuidProp : ActiveBuilder.DLCTFCGuidProp);
                if (r2d.LODGroup != null)
                {
                    // Write a new LOD property
                    props.AddOrReplaceProp(r2d.LODGroup);
                }


                // 7. Commit the export
                export.WritePropertiesAndBinary(props, ut2d);

                // 8. Cache the work that's been done so we don't need to it again.
                r2d.InstantiatedItems[asset] = (props, ut2d);
            }
        }
 private static int WriteMip(RTexture2D tex, byte[] mipBytes)
 {
     // Only one thread is allowed to write a mip at a time.
     lock (syncLock)
     {
         if (tex.PreMountTexture)
         {
             var startOffset = (int)ActiveBuilder.BGTfcStream.Position;
             ActiveBuilder.BGTfcStream.Write(mipBytes);
             return(startOffset);
         }
         else
         {
             var startOffset = (int)ActiveBuilder.DLCTfcStream.Position;
             ActiveBuilder.DLCTfcStream.Write(mipBytes);
             return(startOffset);
         }
     }
 }