Beispiel #1
0
        /// <summary>
        /// Injects a dds stream to Meta Data.
        /// </summary>
        /// <param name="m">Bitmap Meta</param>
        /// <param name="pm">ParsedBitmap Data</param>
        /// <param name="br">Binary Reader Stream</param>
        /// <param name="index">The index # of the ParsedBitmap Data</param>
        /// <param name="bi">BitmapInfo Data</param>
        /// <remarks></remarks>
        public static void InjectDDS(
            Meta m, ParsedBitmap pm, ref BinaryReader br, int index, ParsedBitmap.BitmapInfo bi)
        {
            DDS_HEADER_STRUCTURE dds = new DDS_HEADER_STRUCTURE();
            ParsedBitmap.BitmapInfo pmProp = pm.Properties[index];

            // ' Read dds header
            dds.ReadStruct(ref br);

            // Check padded values as we can allow bitmaps of different size if they have the same padding
            int pmPropPaddedWidth = pmProp.width + (pmProp.width % 16 == 0 ? 0 : (16 - pmProp.width % 16));
            int ddsPaddedWidth = dds.ddsd.width + (dds.ddsd.width % 16 == 0 ? 0 : (16 - dds.ddsd.width % 16));
            if (ddsPaddedWidth != pmPropPaddedWidth || pmProp.height != dds.ddsd.height)
            {
                MessageBox.Show("ERROR (Bitmap #" + index + "): Images have different padded dimensions!");
                return;
            }
            // Set new image width
            pmProp.width = (ushort)dds.ddsd.width;

            #region Discover if we are doing a bitmap, 3D or cubemap
            ParsedBitmap.BitmapType type = ParsedBitmap.BitmapType.BITM_TYPE_2D;
            if ((dds.ddsd.ddsCaps.caps2 & (int)DDSEnum.DDSCAPS2_VOLUME) > 0)
                type = ParsedBitmap.BitmapType.BITM_TYPE_3D;
            else if ((dds.ddsd.ddsCaps.caps2 & (int)DDSEnum.DDSCAPS2_CUBEMAP) > 0)
                type = ParsedBitmap.BitmapType.BITM_TYPE_CUBEMAP;
            #endregion

            // If they are different types
            if (pmProp.typename != type)
            {
                MessageBox.Show("ERROR: Images are different types!" +
                    "\nInjected image = " + type +
                    "\nInternal image = " + pmProp.typename);
                return;
            }
            if (bi.formatname.ToString().ToUpper().Contains("DXT"))
                bi.bitsPerPixel = 32; // Some programs don't save these values, so to be sure

            int rawsize = (int)br.BaseStream.Length - 128;
            br.BaseStream.Position = 128;
            byte[] bChunk = br.ReadBytes(rawsize);

            int cubemap = ((dds.ddsd.ddsCaps.caps2 & (int)DDSEnum.DDSCAPS2_CUBEMAP) > 0 ? 6 : 1);
            byte[][][] mipStreams = new byte[cubemap][][];
            for (int i = 0; i < mipStreams.Length; i++)
                mipStreams[i] = new byte[Math.Max(dds.ddsd.MipMapCount, 1)][];

            // 6 loops for cubemaps, 1 for all else
            for (int c = 0; c < cubemap; c++)
            {
                // the starting offset of each cubemap side
                int mipOffset = c * (bChunk.Length / cubemap);
                int inOffset = mipOffset;

                // Loop for each mipmap within each cube face (if it is a cubemap)
                for (int i = 0; i < mipStreams[c].Length; i++)
                {
                    #region calculate width, height & stream length of mipmap
                    // each mipmap is 1/2 width & height
                    int tWidth = ddsPaddedWidth / (1 << i);
                    int tHeight = dds.ddsd.height / (1 << i);

                    // Contains the size of the current MipMap
                    int mipStreamLength;
                    switch (bi.formatname)
                    {
                        case ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT1:
                            mipStreamLength = Math.Max(1, tWidth / 4) * Math.Max(1, tHeight / 4) * 8;
                            break;
                        case ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT2AND3:
                        case ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT4AND5:
                            mipStreamLength = Math.Max(1, tWidth / 4) * Math.Max(1, tHeight / 4) * 16;
                            break;
                        default:
                            mipStreamLength = Math.Max(1, tWidth) * Math.Max(1, tHeight) * (dds.ddsd.ddfPixelFormat.RGBBitCount >> 3);
                            break;
                    }
                    #endregion

                    mipStreams[c][i] = new byte[mipStreamLength];
                    if (dds.ddsd.width == ddsPaddedWidth)
                        Array.Copy(bChunk, mipOffset, mipStreams[c][i], 0, mipStreams[c][i].Length);
                    else
                    {
                        int byteStep = (dds.ddsd.ddfPixelFormat.RGBBitCount >> 3);
                        Array.Clear(mipStreams[c][i], 0, mipStreams[c][i].Length);
                        // Copy each line, adding padding to the output
                        for (int h = 0; h < (dds.ddsd.height); h++)
                        {
                            Array.Copy(
                                bChunk,
                                mipOffset + h * dds.ddsd.width * byteStep,
                                mipStreams[c][i],
                                h * ddsPaddedWidth * byteStep,
                                dds.ddsd.width * byteStep);
                        }
                    }
                    mipOffset += mipStreams[c][i].Length;
                }


                #region conversion routine
                // Temporary. Remove after conversion routine verified / corrected
                    // Don't allow different BPP injections
                if ((pmProp.bitsPerPixel != bi.bitsPerPixel) ||
                    // Don't allow compressed types to be converted to
                    ((pmProp.formatname != bi.formatname) && pmProp.formatname.ToString().Contains("DXT")))
                {
                    string tempInfo =
                        "\nBPP: " + pmProp.bitsPerPixel.ToString() + ", " + bi.bitsPerPixel.ToString() +
                        "\nDXT: " + pmProp.formatname.ToString() + ", " + bi.formatname.ToString() + " (DXT = " + pmProp.formatname.ToString().Contains("DXT") + ")";
                    MessageBox.Show("Incompatible format types\n Check Bits Per Pixel & Format type\n *NOTE* No conversions to any DXT (compressed) format supported!\n" + tempInfo);
                    return;
                }

                // H2 pads to a 128 boundry (not cubemaps)
                if (cubemap <= 1 && mipOffset % 128 != 0)
                    mipOffset += (128 - (mipOffset % 128));

                /*
                if (pmProp.formatname != bi.formatname)
                {
                    byte[] temp = new byte[mipStreamLength];

                    // Check if we are at the end of Data, such as less mipmaps injected than the original
                    if (inOffset + mipStreamLength > bAll.Length)
                    {
                        return;
                    }

                    Array.Copy(bAll, inOffset, temp, 0, mipStreamLength);
                    Bitmap ba = DDS_Convert.DecodeDDS(temp, bi);

                    /***** This testes to make sure it was being decoded properly
                    Form1 f = new Form1();
                    System.Windows.Forms.PictureBox p1 = new System.Windows.Forms.PictureBox();
                    p1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
                    p1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
                    p1.Location = new System.Drawing.Point(300, 10);
                    p1.Size = new System.Drawing.Size(70, 70);
                    p1.Image = ba;
                    f.Controls.Add(p1);
                    f.ShowDialog();
                    *******/

                /*// We need to make sure that it doesn't swizzle it here as it does it later!
                bool tb = pmProp.swizzle;
                pmProp.swizzle = false;
                bChunk = DDS_Convert.EncodeDDS(ba, ref pmProp);
                pmProp.swizzle = tb;
                ba.Dispose();
                bi.width = (ushort)(bi.width / 2);
                bi.height = (ushort)(bi.height / 2);
                tWidth = pmProp.width;
                tHeight = pmProp.height;
                pmProp.width = (ushort)origWidth;
                pmProp.height = (ushort)origHeight;
            }
            */
                #endregion


                #region Add padding, byte changes, etc                
                for (int mi = 0; mi < mipStreams[c].Length; mi++)
                {
                    int mipWidth = pmProp.width >> mi;

                    // We don't need to pad the saved data as we pad any data read in
                    /*
                    if (pmProp.width % 16 != 0)
                        //if (mipWidth % 16 != 0)
                    {
                        int paddedWidth = 16 - (mipWidth % 16);
                        int byteStep = pmProp.bitsPerPixel / 8;
                        byte[] paddedChunk = new byte[(mipWidth + paddedWidth) * pmProp.height * byteStep];

                        Array.Clear(paddedChunk, 0, paddedChunk.Length); // Make sure all bytes are zero to start!                        

                        // Copy each line, adding padding to the output
                        for (int h = 0; h < (pmProp.height >> mi); h++)
                        {
                            Array.Copy(
                                mipStreams[c][mi],
                                h * mipWidth * byteStep,
                                paddedChunk,
                                h * (mipWidth + paddedWidth) * byteStep,
                                mipWidth * byteStep);
                        }
                        mipStreams[c][mi] = paddedChunk;
                    }
                    */

                    // G8B8 is based on a 128 value for 0 and adds -/+, so adjust values to 128
                    if (pmProp.formatname == ParsedBitmap.BitmapFormat.BITM_FORMAT_G8B8)
                    {
                        for (int mii = 0; mii < mipStreams[c][mi].Length; mii++)
                        {
                            mipStreams[c][mi][mii] += 128;
                        }
                    }
                }
                #endregion

                int lodNumber = 0;
                // First one is the main pic, not mipmap
                for (int j = 0; j < m.raw.rawChunks.Count; j++)
                {
                    // If the selected index is the desired index and the LOD # starts at the mipmap #
                    if (((BitmapRawDataChunk)m.raw.rawChunks[j]).inchunk == index &&
                        ((BitmapRawDataChunk)m.raw.rawChunks[j]).num == lodNumber)
                    {

                        m.Map.OpenMap(m.raw.rawChunks[j].rawLocation, false);
                        if (!m.Map.isOpen)
                        {
                            return;
                        }

                        // Recaculate the total length (may have changed from above with conversion code)
                        int totalLength = 0;
                        for (int temp = lodNumber; temp < mipStreams[c].Length; temp++)
                            totalLength += mipStreams[c][temp].Length;

                        // H2 pads to a 128 boundry for each Bpp (not on cubemaps)
                        switch (pmProp.formatname)
                        {                               
                            case ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT1:
                                if (totalLength % (256) != 0)
                                    totalLength += 256 - (totalLength % (256));
                                break;
                            case ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT2AND3:
                            case ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT4AND5:
                                if (totalLength % (128) != 0)
                                    totalLength += 128 - (totalLength % (128));
                                break;
                            default:
                                if (totalLength % (128 * pmProp.bitsPerPixel >> 3) != 0)
                                    totalLength += (128 * (pmProp.bitsPerPixel >> 3) - (totalLength % (128 * pmProp.bitsPerPixel >> 3)));
                                break;
                        }
                        byte[] tempChunk = new byte[totalLength];
                        int outOffset = 0;
                        for (int temp = lodNumber; temp < mipStreams[c].Length; temp++)
                        {
                            if (pmProp.swizzle)
                            {
                                Array.Copy(
                                    Swizzler.Swizzle(
                                        mipStreams[c][temp],
                                        dds.ddsd.width >> temp,
                                        dds.ddsd.height >> temp,
                                        -1,
                                        dds.ddsd.ddfPixelFormat.RGBBitCount,
                                        false),
                                    0,
                                    tempChunk,
                                    outOffset,
                                    mipStreams[c][temp].Length);

                            }
                            else
                                Array.Copy(mipStreams[c][temp], 0, tempChunk, outOffset, mipStreams[c][temp].Length);
                            outOffset += mipStreams[c][temp].Length;
                        }

                        // Append onto existing data for cubemaps
                        m.Map.BW.BaseStream.Position = m.raw.rawChunks[j].offset + totalLength * c;
                        m.Map.BW.Write(tempChunk, 0, Math.Min(tempChunk.Length, m.raw.rawChunks[j].size - (totalLength * c)));


                        if (c == cubemap - 1)
                        {
                            m.Map.OpenMap(MapTypes.Internal);
                            if (((BitmapRawDataChunk)m.raw.rawChunks[j]).num == 0)
                            {
                                m.Map.BW.BaseStream.Position = m.offset + m.raw.rawChunks[j].pointerMetaOffset - 28;
                                pm.Properties[index].Write(ref m.Map.BW);
                            }
                            
                            m.Map.BW.BaseStream.Position = m.offset + m.raw.rawChunks[j].pointerMetaOffset + 24;
                            m.Map.BW.Write(Math.Min(tempChunk.Length * cubemap, m.raw.rawChunks[j].size));
                            m.Map.CloseMap();
                        }
                        
                        lodNumber++;
                    }
                }
                inOffset += mipOffset;
            }
        }
Beispiel #2
0
        /// <summary>
        /// The get bitmap format.
        /// </summary>
        /// <param name="dds">The dds.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public static ParsedBitmap.BitmapFormat getBitmapFormat(DDS_HEADER_STRUCTURE dds)
        {
            if (dds.ddsd.ddfPixelFormat.FourCC.StartsWith("DXT"))
            {
                switch (dds.ddsd.ddfPixelFormat.FourCC)
                {
                    case "DXT1":
                        return ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT1;
                    case "DXT2":
                    case "DXT3":
                        return ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT2AND3;
                    case "DXT4":
                    case "DXT5":
                    default:
                        return ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT4AND5;
                }
            }
            else if (dds.ddsd.ddfPixelFormat.RGBAlphaBitMask != 0)
            {
                if ((dds.ddsd.ddfPixelFormat.RGBAlphaBitMask == 0x8000) && (dds.ddsd.ddfPixelFormat.RBitMask == 0x7C00) &&
                    (dds.ddsd.ddfPixelFormat.GBitMask == 0x03E0) && (dds.ddsd.ddfPixelFormat.BBitMask == 0x001F))
                {
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_A1R5G5B5;
                }
                else if ((dds.ddsd.ddfPixelFormat.RGBAlphaBitMask == 0xF000) &&
                         (dds.ddsd.ddfPixelFormat.RBitMask == 0x0F00) && (dds.ddsd.ddfPixelFormat.GBitMask == 0x00F0) &&
                         (dds.ddsd.ddfPixelFormat.BBitMask == 0x000F))
                {
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_A4R4G4B4;
                }
                else if ((dds.ddsd.ddfPixelFormat.RGBAlphaBitMask == 0x00000000) &&
                         (dds.ddsd.ddfPixelFormat.RBitMask == 0x00FF0000) &&
                         (dds.ddsd.ddfPixelFormat.GBitMask == 0x0000FF00) &&
                         (dds.ddsd.ddfPixelFormat.BBitMask == 0x000000FF))
                {
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_X8R8G8B8;
                }
                else if ((dds.ddsd.ddfPixelFormat.RGBAlphaBitMask == 0xFF000000) &&
                         (dds.ddsd.ddfPixelFormat.RBitMask == 0x00FF0000) &&
                         (dds.ddsd.ddfPixelFormat.GBitMask == 0x0000FF00) &&
                         (dds.ddsd.ddfPixelFormat.BBitMask == 0x000000FF))
                {
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_A8R8G8B8;
                }
                else if ((dds.ddsd.ddfPixelFormat.RGBAlphaBitMask == 0xFF00) &&
                         (dds.ddsd.ddfPixelFormat.RBitMask == 0x00FF) && 
                         (dds.ddsd.ddfPixelFormat.GBitMask == 0x0000) &&
                         (dds.ddsd.ddfPixelFormat.BBitMask == 0x0000))
                {
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_G8B8;
                }
                else if ((dds.ddsd.ddfPixelFormat.RGBAlphaBitMask == 0xFF) &&
                         (dds.ddsd.ddfPixelFormat.RBitMask == 0x0000) &&
                         (dds.ddsd.ddfPixelFormat.GBitMask == 0x0000) &&
                         (dds.ddsd.ddfPixelFormat.BBitMask == 0x0000))
                {
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_A8;
                }
                else if ((dds.ddsd.ddfPixelFormat.RGBAlphaBitMask == 0x00FF) &&
                         (dds.ddsd.ddfPixelFormat.RBitMask == 0xFF00) && (dds.ddsd.ddfPixelFormat.GBitMask == 0x0000) &&
                         (dds.ddsd.ddfPixelFormat.BBitMask == 0x0000))
                {
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_A8Y8;
                }

                    /*
                    else // Same as above, so we need to post process to figure out which one as A8Y8 is byte & G8B8 is sbyte
                    if ((dds.ddsd.ddfPixelFormat.RGBAlphaBitMask == 0x00FF) &&
                        (dds.ddsd.ddfPixelFormat.RBitMask == 0xFF00) &&
                        (dds.ddsd.ddfPixelFormat.GBitMask == 0x0000) &&
                        (dds.ddsd.ddfPixelFormat.BBitMask == 0x0000))
                        return Raw.ParsedBitmap.BitmapFormat.BITM_FORMAT_G8B8;
                    */
                else
                {
                    ////
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_A8;
                }
            }
            else
            {
                if ((dds.ddsd.ddfPixelFormat.RGBAlphaBitMask == 0x00000000) &&
                    (dds.ddsd.ddfPixelFormat.RBitMask == 0x00FF0000) && (dds.ddsd.ddfPixelFormat.GBitMask == 0x0000FF00) &&
                    (dds.ddsd.ddfPixelFormat.BBitMask == 0x000000FF))
                {
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_X8R8G8B8;
                }
                else if ((dds.ddsd.ddfPixelFormat.RBitMask == 0xF800) && (dds.ddsd.ddfPixelFormat.GBitMask == 0x07E0) &&
                         (dds.ddsd.ddfPixelFormat.BBitMask == 0x001F))
                {
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_R5G6B5;
                }
                else
                {
                    ////
                    return ParsedBitmap.BitmapFormat.BITM_FORMAT_A8;
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Extracts meta data to a dds stream.
        /// </summary>
        /// <param name="m">Bitmap Meta</param>
        /// <param name="pm">ParsedBitmap Data</param>
        /// <param name="bw">Binary Writer Stream</param>
        /// <param name="index">The index # of the ParsedBitmap Data</param>
        /// <param name="bi">BitmapInfo Data</param>
        /// <returns>BitmapInfo containing the format of the extracted DDS</returns>
        /// <remarks></remarks>
        public static ParsedBitmap.BitmapInfo ExtractDDS(
            Meta m, ParsedBitmap pm, ref BinaryWriter bw, int index, ParsedBitmap.BitmapInfo bi)
        {
            DDS_HEADER_STRUCTURE dds = new DDS_HEADER_STRUCTURE();
            ParsedBitmap.BitmapInfo pmProp = pm.Properties[index];
            dds.generate(ref pmProp);

            // Get our raw data from the Meta, LOD 0 of our selected index
            int tempNum = -1;
            for (int i = 0; i < m.raw.rawChunks.Count; i++)
                if (((BitmapRawDataChunk)m.raw.rawChunks[i]).inchunk == index)
                {
                    tempNum = i;
                    break;
                }
            if (tempNum == -1)
                return null;


            byte[] tempChunk = m.raw.rawChunks[tempNum].MS.ToArray();
            //int bytesToWrite = 0;
            int inOffset = 0;
            
            // If the original size needs padding, then use it, but not otherwise
            bool useWidthPad = pmProp.width % 16 != 0 ? true : false;

            bw.BaseStream.SetLength(128);
            bw.BaseStream.Position = 0;

            for (int cubemap = 0; cubemap < (pmProp.typename == ParsedBitmap.BitmapType.BITM_TYPE_CUBEMAP ? 6 : 1); cubemap++)
            {
                inOffset = cubemap * (tempChunk.Length / 6);
                int outOffset = 0;
                int tWidth = pmProp.width;
                int tHeight = pmProp.height;
                int tDepth = pmProp.depth;
                int mipMapCount = 0;

                // Halo 2 only uses mipmaps down to 2x__, instead of 1x1 (could be 2x1, 2x2, 2x4, etc),
                // but is padded enough we should be able to get a 1x1 extracted from the padded data
                while (tWidth != 0 && tHeight != 0)
                {

                    int mipSize;

                    // Any compressed images must have each mimap at least 16 bytes (4x4 image)
                    // This concerns mipmaps such as 2x4, 2x2, 1x1, etc
                    switch (pmProp.formatname)
                    {
                        case ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT1:
                            mipSize = Math.Max(Math.Max(1, tWidth) * Math.Max(1, tHeight) * Math.Max(1, tDepth) * (pmProp.bitsPerPixel >> 3) / 8, 8);
                            //mipSize = Math.Max(1, tWidth / 4) * Math.Max(1, tHeight / 4) * 8;
                            if (dds.ddsd.PitchOrLinearSize == 0)
                                dds.ddsd.PitchOrLinearSize = mipSize;
                            //dds.ddsd.PitchOrLinearSize = Math.Max(1, ((tWidth + 3) / 4)) * 8;
                            break;
                        case ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT2AND3:
                        case ParsedBitmap.BitmapFormat.BITM_FORMAT_DXT4AND5:
                            mipSize = Math.Max(Math.Max(1, tWidth) * Math.Max(1, tHeight) * Math.Max(1, tDepth), 16) * (pmProp.bitsPerPixel >> 3) / 4;
                            //mipSize = Math.Max(1, tWidth / 4) * Math.Max(1, tHeight / 4) * 16;
                            if (dds.ddsd.PitchOrLinearSize == 0)
                                dds.ddsd.PitchOrLinearSize = mipSize;
                            break;
                        default:
                            mipSize = Math.Max(Math.Max(1, tWidth) * Math.Max(1, tHeight) * Math.Max(1, tDepth), 8) * (pmProp.bitsPerPixel >> 3);
                            break;
                    }
                    
                    #region Decode swizzled images
                    if (pmProp.swizzle)
                    {
                        try
                        {
                            byte[] tempSwizzle = new byte[mipSize];
                            Array.Copy(tempChunk, inOffset + outOffset, tempSwizzle, 0, tempSwizzle.Length);
                            tempSwizzle = Swizzler.Swizzle(tempSwizzle, tWidth, tHeight, tDepth > 1 ? tDepth : -1, pmProp.bitsPerPixel, true);
                            Array.Copy(tempSwizzle, 0, tempChunk, inOffset + outOffset, tempSwizzle.Length);
                        }
                        catch
                        {
                            // We cannot generate all mipmaps, so just continue with the amount we have
                            break;
                        }
                    }
                    #endregion

                    // H2 Bitmap Data is padded to a minimum of:
                    //      1 BPP/DXT2/3/4/5    = 128 bytes
                    //      2 BPP/DXT1          = 256 bytes
                    //      4 BPP               = 512 bytes
                    // We need to remove the extra padding when 
                    // extracting to a DDS. DDS pads DXT1 to 8 bytes minimum & DXT2/3/4/5 to 16 bytes minimum
                    // bytesToWrite is the width * height size. tempChunk2 will contain a stripped version
                    #region Remove padding

                    int widthPad = 0;
                    //if (pmProp.typename != ParsedBitmap.BitmapType.BITM_TYPE_3D && tWidth % 16 != 0)
                    if (useWidthPad && tWidth % 16 != 0)
                        widthPad = 16 - tWidth % 16;

                    if (!pmProp.formatname.ToString().Contains("DXT") && widthPad != 0)
                    {
                        //byte[] tempChunk2 = new byte[tWidth * tHeight * tDepth * byteStep];
                        byte[] tempChunk2 = new byte[mipSize];
                        try
                        {
                            // tempPadSize = Padded scanline length
                            int tempPadSize = (tWidth + widthPad) * (pmProp.bitsPerPixel >> 3);
                            int paddedSize = tHeight * tempPadSize;

                            // Copy each line without the padding
                            for (int d = 0; d < tDepth; d++)
                                for (int h = 0; h < tHeight; h++)
                                {
                                    Array.Copy(
                                        tempChunk,
                                        inOffset + outOffset + (d * paddedSize) + (h * tempPadSize),
                                        tempChunk2,
                                        (d * mipSize) + (h * tWidth * (pmProp.bitsPerPixel >> 3)),
                                        tWidth * (pmProp.bitsPerPixel >> 3));
                                }
                            // Make a copy of the bytes beyond our current mipmap
                            paddedSize *= tDepth;
                            byte[] endBytes = new byte[tempChunk.Length - paddedSize - (inOffset + outOffset)];
                            Array.Copy(tempChunk, inOffset + outOffset + paddedSize, endBytes, 0, endBytes.Length);
                            Array.Resize(
                                ref tempChunk,
                                inOffset + outOffset + tempChunk2.Length + endBytes.Length
                                );
                            Array.Copy(tempChunk2, 0, tempChunk, inOffset + outOffset, tempChunk2.Length);
                            Array.Copy(endBytes, 0, tempChunk, inOffset + outOffset + tempChunk2.Length, endBytes.Length);
                        }
                        catch
                        {
                            // We cannot generate all mipmaps, so just continue with the amount we have
                            break;
                        }
                    }
                    #endregion

                    outOffset += mipSize;
                    if ((pmProp.mipMapCount == 0) ||
                       (outOffset > tempChunk.Length))
                        break;
                    tWidth >>= 1;
                    tHeight >>= 1;
                    tDepth = Math.Max(tDepth >> 1, 1);
                    mipMapCount++;
                }
                // For exporting, use mipmaps right down to 1x1, instead of Halo's 2x2
                dds.ddsd.MipMapCount = mipMapCount;

                // G8B8 is based on a 128 value for 0 and adds -/+, so adjust values to 128
                if (pmProp.formatname == ParsedBitmap.BitmapFormat.BITM_FORMAT_G8B8)
                {
                    for (int ii = 0; ii < tempChunk.Length; ii++)
                    {
                        tempChunk[ii] += 128;
                    }
                }

                byte[] bChunk = new byte[outOffset];
                Array.Copy(tempChunk, inOffset, bChunk, 0, Math.Min(outOffset, tempChunk.Length));

                if (cubemap == 0)
                {
                    dds.WriteStruct(ref bw);
                    bw.BaseStream.Position = 128;
                }
                bw.Write(bChunk);
            }
            
            return pmProp;
        }