/// <summary>
        /// TEMP: Creates super-atlas populated with all archives in array.
        /// Currently does not support animated textures, normal map, or emission map.
        /// TODO: Integrate this feature fully with material system.
        /// </summary>
        /// <param name="archives">Archive array.</param>
        /// <param name="borderSize">Number of pixels border to add around image.</param>
        /// <param name="dilate">Blend texture into surrounding empty pixels. Requires border.</param>
        /// <param name="maxAtlasSize">Maximum atlas size.</param>
        /// <param name="alphaTextureFormat">Alpha TextureFormat.</param>
        /// <param name="nonAlphaFormat">Non-alpha TextureFormat.</param>
        /// <returns>TextureAtlasBuilder.</returns>
        public TextureAtlasBuilder CreateTextureAtlasBuilder(
            int[] archives,
            int borderSize = 0,
            bool dilate = false,
            int maxAtlasSize = 2048,
            SupportedAlphaTextureFormats alphaTextureFormat = SupportedAlphaTextureFormats.RGBA32,
            SupportedNonAlphaTextureFormats nonAlphaFormat = SupportedNonAlphaTextureFormats.RGB24)
        {
            // Iterate archives
            TextureFile textureFile = new TextureFile();
            TextureAtlasBuilder builder = new TextureAtlasBuilder();
            GetTextureSettings settings = TextureReader.CreateTextureSettings(0, 0, 0, 0, borderSize, dilate, maxAtlasSize);
            settings.stayReadable = true;
            for (int i = 0; i < archives.Length; i++)
            {
                // Load texture file
                settings.archive = archives[i];
                textureFile.Load(Path.Combine(Arena2Path, TextureFile.IndexToFileName(settings.archive)), FileUsage.UseMemory, true);

                // Add all records for this archive - single frame only
                for (int record = 0; record < textureFile.RecordCount; record++)
                {
                    settings.record = record;
                    GetTextureResults results = GetTexture2D(settings, alphaTextureFormat, nonAlphaFormat);
                    DFSize size = textureFile.GetSize(record);
                    DFSize scale = textureFile.GetScale(record);
                    builder.AddTextureItem(
                        results.albedoMap,
                        settings.archive,
                        settings.record,
                        0, 1,
                        new Vector2(size.Width, size.Height),
                        new Vector2(scale.Width, scale.Height));
                }
            }

            // Apply the builder
            builder.Rebuild(borderSize);

            return builder;
        }
        /// <summary>
        /// Reads any Daggerfall image file to ImageData package.
        /// </summary>
        /// <param name="filename">Name of standalone file as it appears in arena2 folder.</param>
        /// <param name="record">Which image record to read for multi-image files.</param>
        /// <param name="frame">Which frame to read for multi-frame images.</param>
        /// <param name="hasAlpha">Enable this for image cutouts.</param>
        /// <param name="createTexture">Create a Texture2D.</param>
        /// <returns>ImageData. If result.type == ImageTypes.None then read failed.</returns>
        public static ImageData GetImageData(string filename, int record = 0, int frame = 0, bool hasAlpha = false, bool createTexture = true)
        {
            // Check API ready
            DaggerfallUnity dfUnity = DaggerfallUnity.Instance;
            if (!dfUnity.IsReady)
                return new ImageData();

            // Parse image file type
            ImageTypes fileType;
            try
            {
                fileType = ParseFileType(filename);
            }
            catch
            {
                return new ImageData();
            }

            // Create base image data
            ImageData imageData = new ImageData();
            imageData.type = fileType;
            imageData.filename = filename;
            imageData.record = record;
            imageData.frame = frame;
            imageData.hasAlpha = hasAlpha;

            // Read supported image files
            DFBitmap dfBitmap = null;
            switch (fileType)
            {
                case ImageTypes.TEXTURE:
                    TextureFile textureFile = new TextureFile(Path.Combine(dfUnity.Arena2Path, filename), FileUsage.UseMemory, true);
                    textureFile.LoadPalette(Path.Combine(dfUnity.Arena2Path, textureFile.PaletteName));
                    dfBitmap = textureFile.GetDFBitmap(record, frame);
                    imageData.offset = textureFile.GetOffset(record);
                    imageData.scale = textureFile.GetScale(record);
                    imageData.size = textureFile.GetSize(record);
                    break;

                case ImageTypes.IMG:
                    ImgFile imgFile = new ImgFile(Path.Combine(dfUnity.Arena2Path, filename), FileUsage.UseMemory, true);
                    imgFile.LoadPalette(Path.Combine(dfUnity.Arena2Path, imgFile.PaletteName));
                    dfBitmap = imgFile.GetDFBitmap();
                    imageData.offset = imgFile.ImageOffset;
                    imageData.scale = new DFSize();
                    imageData.size = imgFile.GetSize(0);
                    break;

                case ImageTypes.CIF:
                case ImageTypes.RCI:
                    CifRciFile cifFile = new CifRciFile(Path.Combine(dfUnity.Arena2Path, filename), FileUsage.UseMemory, true);
                    cifFile.LoadPalette(Path.Combine(dfUnity.Arena2Path, cifFile.PaletteName));
                    dfBitmap = cifFile.GetDFBitmap(record, frame);
                    imageData.offset = cifFile.GetOffset(record);
                    imageData.scale = new DFSize();
                    imageData.size = cifFile.GetSize(record);
                    break;

                default:
                    return new ImageData();
            }

            // Store bitmap
            imageData.dfBitmap = dfBitmap;
            imageData.width = dfBitmap.Width;
            imageData.height = dfBitmap.Height;

            // Create Texture2D
            if (createTexture)
            {
                // Get colors array
                Color32[] colors = GetColors(imageData);
                if (colors == null)
                    return new ImageData();

                // Create new Texture2D
                imageData.texture = GetTexture(colors, imageData.width, imageData.height);
            }

            return imageData;
        }
        /// <summary>
        /// Precalculate and cache billboard scale for every record.
        /// This will change based on animation state and orientation.
        /// Cache this to array so it only needs to be calculated once.
        /// Also store number of frames for state animations.
        /// </summary>
        /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param>
        /// <param name="archive">Texture archive index derived from type and gender.</param>
        private void CacheRecordSizesAndFrames(DaggerfallUnity dfUnity, int archive)
        {
            // Open texture file
            string path = Path.Combine(dfUnity.Arena2Path, TextureFile.IndexToFileName(archive));
            TextureFile textureFile = new TextureFile(path, FileUsage.UseMemory, true);

            // Cache size and scale for each record
            summary.RecordSizes = new Vector2[textureFile.RecordCount];
            summary.RecordFrames = new int[textureFile.RecordCount];
            for (int i = 0; i < textureFile.RecordCount; i++)
            {
                // Get size and scale of this texture
                DFSize size = textureFile.GetSize(i);
                DFSize scale = textureFile.GetScale(i);

                // Set start size
                Vector2 startSize;
                startSize.x = size.Width;
                startSize.y = size.Height;

                // Apply scale
                Vector2 finalSize;
                int xChange = (int)(size.Width * (scale.Width / BlocksFile.ScaleDivisor));
                int yChange = (int)(size.Height * (scale.Height / BlocksFile.ScaleDivisor));
                finalSize.x = (size.Width + xChange);
                finalSize.y = (size.Height + yChange);

                // Store final size and frame count
                summary.RecordSizes[i] = finalSize * MeshReader.GlobalScale;
                summary.RecordFrames[i] = textureFile.GetFrameCount(i);
            }
        }