Example #1
0
        /// <summary>
        /// Returns a byte[] containing the converted image data from the cache.
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="bitmapTag"></param>
        /// <param name="index"></param>
        /// <param name="version"></param>
        /// <returns></returns>
        public static BaseBitmap ConvertGen3Bitmap(CacheFile cache, Bitmap bitmapTag, int index, CacheVersion version)
        {
            if (cache.ResourceLayoutTable == null || cache.ResourceGestalt == null)
            {
                cache.LoadResourceTags();
            }

            byte[]     imageData  = null;
            byte[]     mipMapData = null;
            XboxBitmap xboxBitmap = null;
            int        bitmapSize = 0;
            int        mipMapSize = 0;

            var image  = bitmapTag.Images[index];
            var handle = GetBitmapResourceHandle(bitmapTag, index, version);

            if (!ResourceEntryValid(cache, handle) || (!HasPrimaryResource(cache, handle) && !HasSecondaryResource(cache, handle)))
            {
                Console.WriteLine($"Invalid resource entry at {handle}. No data to convert.");
                return(null);
            }


            // interleaved means two images are inside a single resource along with the mipmaps.
            if (image.XboxFlags.HasFlag(BitmapFlagsXbox.UseInterleavedTextures))
            {
                var resourceDef = GetInterleavedResourceDefinition(cache, handle);
                xboxBitmap = new XboxBitmap(resourceDef, image.InterleavedTextureIndex2, image);
                bitmapSize = BitmapUtils.GetXboxImageSize(xboxBitmap);
                mipMapSize = 0;

                xboxBitmap.Offset = 0;

                if (!xboxBitmap.InTile)
                {
                    var offset = image.InterleavedTextureIndex2 * (int)(xboxBitmap.VirtualHeight * xboxBitmap.VirtualWidth / xboxBitmap.CompressionFactor);
                    imageData  = cache.GetSecondaryResource(handle, bitmapSize, offset, true);
                    mipMapData = null;
                }
                else
                {
                    if (xboxBitmap.Type == BitmapType.CubeMap && image.Flags.HasFlag(BitmapFlags.Compressed) && xboxBitmap.Width <= 16)
                    {
                        xboxBitmap.Offset = (int)(16 * 4 / xboxBitmap.CompressionFactor);   // account for the mipmaps
                    }
                    imageData  = cache.GetPrimaryResource(handle, bitmapSize, 0, true);
                    mipMapData = null;
                }

                if (image.InterleavedTextureIndex2 == 1 && xboxBitmap.InTile)
                {
                    byte[] totalData = null;
                    var    tileSize  = (int)(xboxBitmap.MinimalBitmapSize * xboxBitmap.MinimalBitmapSize / xboxBitmap.CompressionFactor);
                    var    subCount  = 0;
                    switch (xboxBitmap.Type)
                    {
                    case BitmapType.Texture2D:
                        subCount = 1;
                        break;

                    case BitmapType.Texture3D:
                    case BitmapType.Array:
                        subCount = xboxBitmap.Depth;
                        break;

                    case BitmapType.CubeMap:
                        subCount = 6;
                        break;
                    }
                    if (mipMapData != null)
                    {
                        totalData = new byte[bitmapSize + mipMapSize];
                        Array.Copy(imageData, 0, totalData, 0, bitmapSize);
                        Array.Copy(mipMapData, 0, totalData, bitmapSize, mipMapSize);
                    }
                    else
                    {
                        totalData = imageData;
                    }

                    for (int i = 0; i < subCount; i++)
                    {
                        // make sure to copy the right amount of data
                        var copySize = tileSize;
                        if (copySize > totalData.Length - ((tileSize * i) + tileSize / 2))
                        {
                            copySize = totalData.Length - ((tileSize * i) + tileSize / 2);
                        }
                        Array.Copy(totalData, (tileSize * i) + tileSize / 2, imageData, (tileSize * i), copySize);
                    }
                }
            }
            else
            {
                var resourceDef = GetResourceDefinition(cache, handle);
                xboxBitmap = new XboxBitmap(resourceDef, image);
                bitmapSize = BitmapUtils.GetXboxImageSize(xboxBitmap);

                if (HasSecondaryResource(cache, handle))
                {
                    imageData = cache.GetSecondaryResource(handle, bitmapSize, 0, true);

                    if (xboxBitmap.MipMapCount > 0)
                    {
                        if (HasPrimaryResource(cache, handle))
                        {
                            // dedicated resource for mipmaps
                            mipMapData = cache.GetPrimaryResource(handle, mipMapSize, 0, true);
                        }
                        else
                        {
                            //throw new Exception($"Unsupported layout. Compute bitmap offset for weird bitmap.");
                            mipMapData = null;
                        }
                    }
                    else
                    {
                        mipMapData = null;
                    }
                }
                else
                {
                    // Bitmap doesn't have a secondary resource means either no mipmaps or everything is packed in the primary resource.
                    if (xboxBitmap.MipMapCount > 0)
                    {
                        imageData  = cache.GetPrimaryResource(handle, 2 * bitmapSize, 0, true);
                        mipMapData = cache.GetPrimaryResource(handle, mipMapSize, 0, true);

                        // Formula seems quite complex, small hack to make it work
                        if (xboxBitmap.BlockDimension == 4)
                        {
                            if (xboxBitmap.Width > xboxBitmap.Height)
                            {
                                xboxBitmap.Offset = 4 * (int)(BitmapUtils.RoundSize(xboxBitmap.Height, 4) * xboxBitmap.VirtualWidth / xboxBitmap.CompressionFactor);
                            }
                            else if (xboxBitmap.Width == xboxBitmap.Height)
                            {
                                var width = xboxBitmap.Width;
                                if (xboxBitmap.Width >= 4)
                                {
                                    width = 4;
                                }
                                xboxBitmap.Offset = 4 * (int)(width * 4 / xboxBitmap.CompressionFactor);
                            }
                            else
                            {
                                var width = xboxBitmap.Width / 2;
                                if (width == 0)
                                {
                                    width = 1;
                                }
                                xboxBitmap.Offset = 4 * (int)(BitmapUtils.RoundSize(width, 4) * xboxBitmap.BlockDimension / xboxBitmap.CompressionFactor);
                            }
                        }
                        else
                        {
                            xboxBitmap.Offset = (int)(xboxBitmap.Width * 4 / xboxBitmap.CompressionFactor);
                            Console.WriteLine("WEIRD BITMAP");
                        }
                    }
                    else
                    {
                        imageData  = cache.GetPrimaryResource(handle, bitmapSize, 0, true);
                        mipMapData = null;
                    }
                }
            }

            //
            // Convert main bitmap
            //

            List <XboxBitmap> xboxBitmaps = ParseImages(xboxBitmap, image, imageData, bitmapSize);
            bool flipImage = true;

            // rearrange cubemaps order
            if (xboxBitmap.Type == BitmapType.CubeMap)
            {
                XboxBitmap temp = xboxBitmaps[1];
                xboxBitmaps[1] = xboxBitmaps[2];
                xboxBitmaps[2] = temp;
            }

            List <BaseBitmap> finalBitmaps = new List <BaseBitmap>();

            foreach (var bitmap in xboxBitmaps)
            {
                // extract bitmap from padded image
                BaseBitmap finalBitmap = ExtractImage(bitmap);
                // convert to PC format
                flipImage = ConvertImage(finalBitmap);
                // flip data if required
                if (flipImage)
                {
                    FlipImage(finalBitmap, image);
                }
                // until I write code to move mipmaps at the end of the file, remove cubemap mipmaps
                if (xboxBitmap.Type == BitmapType.CubeMap)
                {
                    finalBitmap.MipMapCount = 0;
                }
                // generate mipmaps for uncompressed textures
                if (!finalBitmap.Flags.HasFlag(BitmapFlags.Compressed) && finalBitmap.MipMapCount > 0)
                {
                    GenerateUncompressedMipMaps(finalBitmap);
                }

                finalBitmaps.Add(finalBitmap);
            }
            // build and return the final bitmap
            return(RebuildBitmap(finalBitmaps));
        }