/// <summary>
        ///  Extracts the <see cref="HG3IMG"/> from the HG-3 file.
        /// </summary>
        /// <param name="reader">The binary reader for the file.</param>
        /// <param name="std">The HG3STDINFO containing image dimensions, etc.</param>
        /// <param name="img">The image header used to process the image.</param>
        /// <param name="expand">True if the image should be expanded to its full size.</param>
        /// <param name="pngFile">The path to the PNG file to save to.</param>
        private static void ExtractHg3ImageStandard(BinaryReader reader, Hg3FrameInfo frameInfo, HgxOptions options,
                                                    string pngFile)
        {
            HG3STDINFO std = frameInfo.StdInfo;
            HG3IMG     img = frameInfo.Img.Data;

            reader.BaseStream.Position = frameInfo.Img.Offset;

            byte[] pixelBuffer = ProcessImage(reader, std.Width, std.Height, std.DepthBits, img.Data);

            if (!CatDebug.SpeedTestHgx)
            {
                // This image type is normally flipped, so reverse the option
                options ^= HgxOptions.Flip;
                WritePng(pixelBuffer, std, options, pngFile);
            }
        }
Beispiel #2
0
        /// <summary>
        ///  Extracts the bitmap from the HG-3 file.
        /// </summary>
        /// <param name="reader">The binary reader for the file.</param>
        /// <param name="std">The HG3STDINFO containing image dimensions, etc.</param>
        /// <param name="img">The image header used to process the image.</param>
        /// <param name="expand">True if the image should be expanded to its full size.</param>
        /// <param name="pngFile">The path to the PNG file to save to.</param>
        private static void ExtractBitmap(BinaryReader reader, HG3STDINFO std, HG3IMG img, bool expand, string pngFile)
        {
            int depthBytes = (std.DepthBits + 7) / 8;
            int stride     = (std.Width * depthBytes + 3) / 4 * 4;

            byte[] bufferTmp    = reader.ReadBytes(img.DataLength);
            byte[] cmdBufferTmp = reader.ReadBytes(img.CmdLength);
            byte[] rgbaBuffer;

            // Perform heavy processing that's faster in native code
            ProcessImageNative(
                bufferTmp,
                img.DataLength,
                img.OriginalDataLength,
                cmdBufferTmp,
                img.CmdLength,
                img.OriginalCmdLength,
                out IntPtr pRgbaBuffer,
                out int rgbaLength,
                std.Width,
                std.Height,
                depthBytes);

            try {
                // Vertically flip the buffer so its in the correct setup to load into Bitmap
                rgbaBuffer = new byte[rgbaLength];
                for (int y = 0; y < std.Height; y++)
                {
                    int src = y * stride;
                    int dst = (std.Height - (y + 1)) * stride;
                    Marshal.Copy(pRgbaBuffer + src, rgbaBuffer, dst, stride);
                }
            } finally {
                Marshal.FreeHGlobal(pRgbaBuffer);
            }

            WriteBitmap(rgbaBuffer, std, expand, pngFile);
        }
Beispiel #3
0
        private static byte[] ProcessImageInternal(BinaryReader reader, HG3STDINFO std, HG3IMG img,
                                                   out byte[] rgbaBuffer)
        {
            int depthBytes = (std.DepthBits + 7) / 8;

            byte[] bufferTmp    = reader.ReadBytes(img.DataLength);
            byte[] cmdBufferTmp = reader.ReadBytes(img.CmdLength);

            rgbaBuffer = new byte[std.Height * std.Width * 4];
            ProcessImage(
                bufferTmp,
                img.DataLength,
                img.OriginalDataLength,
                cmdBufferTmp,
                img.CmdLength,
                img.OriginalCmdLength,
                out IntPtr pRgbaBuffer,
                out int rgbaLength,
                std.Width,
                std.Height,
                depthBytes);

            rgbaBuffer = new byte[rgbaLength];
            Marshal.Copy(pRgbaBuffer, rgbaBuffer, 0, rgbaLength);
            Marshal.FreeHGlobal(pRgbaBuffer);
            return(rgbaBuffer);
        }
Beispiel #4
0
        // This encoding tries to optimize for lots of zeros. I think. :)

        /*private static byte UnpackVal(byte c) {
         *      byte z = (byte) ((c & 1) != 0 ? 0xFF : 0);
         *      return (byte) ((c >> 1) ^ z);
         * }
         *
         * private static unsafe void UndeltaFilter(byte[] buffer,
         *      //int length,
         *      byte[] outBuffer,
         *      int width,
         *      int height,
         *      int depthBytes)
         * {
         *      uint[] table1 = new uint[256];
         *      uint[] table2 = new uint[256];
         *      uint[] table3 = new uint[256];
         *      uint[] table4 = new uint[256];
         *
         *      for (uint i = 0; i < 256; i++) {
         *              uint val = i & 0xC0;
         *
         *              val <<= 6;
         *              val |= i & 0x30;
         *
         *              val <<= 6;
         *              val |= i & 0x0C;
         *
         *              val <<= 6;
         *              val |= i & 0x03;
         *
         *              table4[i] = val;
         *              table3[i] = val << 2;
         *              table2[i] = val << 4;
         *              table1[i] = val << 6;
         *      }
         *
         *      uint sectLength = (uint) buffer.Length / 4;
         *
         *      fixed (byte* pOutBuffer = outBuffer)
         *      fixed (byte* pBuffer = buffer) {
         *              byte* sect1 = pBuffer;
         *              byte* sect2 = sect1 + sectLength;
         *              byte* sect3 = sect2 + sectLength;
         *              byte* sect4 = sect3 + sectLength;
         *
         *              byte* outP = pOutBuffer;
         *              byte* outEnd = pOutBuffer + buffer.Length;
         *
         *              while (outP < outEnd) {
         *                      uint val = table1[*sect1++] | table2[*sect2++] | table3[*sect3++] | table4[*sect4++];
         *
         * outP++ = UnpackVal((byte) (val >> 0));
         * outP++ = UnpackVal((byte) (val >> 8));
         * outP++ = UnpackVal((byte) (val >> 16));
         * outP++ = UnpackVal((byte) (val >> 24));
         *              }
         *
         *              int stride = width * depthBytes;
         *
         *              for (int x = depthBytes; x < stride; x++) {
         *                      outBuffer[x] += outBuffer[x - depthBytes];
         *              }
         *
         *              for (uint y = 1; y < height; y++) {
         *                      byte* line = pOutBuffer + y * stride;
         *                      byte* prev = pOutBuffer + (y - 1) * stride;
         *
         *                      for (uint x = 0; x < stride; x++) {
         *                              line[x] += prev[x];
         *                      }
         *              }
         *      }
         * }
         *
         * private static void Unrle(byte[] buffer,
         *              byte[] cmdBuffer,
         *              out byte[] outBuffer)
         * {
         *      BitBuffer cmdBits = new BitBuffer(cmdBuffer);
         *
         *      bool copyFlag = cmdBits.GetBit();
         *
         *      int outLength = (int) cmdBits.GetEliasGammaValue();
         *      outBuffer = new byte[outLength];
         *
         *      int n = 0;
         *      int index = 0;
         *      for (int i = 0; i < outLength; i += n) {
         *              n = (int) cmdBits.GetEliasGammaValue();
         *
         *              if (copyFlag) {
         *                      Array.Copy(buffer, index, outBuffer, i, n);
         *                      index += n;
         *              }
         *              else {
         *                      Array.Clear(outBuffer, i, n);
         *              }
         *
         *              copyFlag = !copyFlag;
         *      }
         * }*/
        #endregion

        private static void ProcessImage(BinaryReader reader,
                                         string file, HG3STDINFO std, HG3IMG img, bool expand = true)
        {
            int depthBytes = (std.DepthBits + 7) / 8;
            int stride     = (std.Width * depthBytes + 3) / 4 * 4;

            #region Old Code

            /*byte[] buffer = new byte[img.OriginalDataLength];
             * {
             *      byte[] temp = reader.ReadBytes(img.DataLength);
             *      Uncompress(buffer, ref img.OriginalDataLength,
             *              temp, img.DataLength);
             * }
             *
             * byte[] cmdBuffer = new byte[img.OriginalCmdLength];
             * {
             *      byte[] temp = reader.ReadBytes(img.CmdLength);
             *      Uncompress(cmdBuffer, ref img.OriginalCmdLength,
             *              temp, img.CmdLength);
             * }
             *
             * Unrle(buffer, cmdBuffer, out byte[] outBuffer);//, ref outLength);
             *
             * byte[] rgbaBuffer = new byte[outBuffer.Length];
             * UndeltaFilter(outBuffer, rgbaBuffer, std.Width, std.Height, depthBytes);
             * UnrleDeltaFilter(
             *      buffer, buffer.Length,
             *      cmdBuffer, cmdBuffer.Length,
             *      out IntPtr pRgbaBuffer, out int rgbaLength,
             *      std.Width,
             *      std.Height,
             *      depthBytes);*/

            /*byte[] bufferTmp = reader.ReadBytes(img.DataLength);
             * byte[] cmdBufferTmp = reader.ReadBytes(img.CmdLength);
             *
             * ProcessImage(
             *      bufferTmp,
             *      img.DataLength,
             *      img.OriginalDataLength,
             *      cmdBufferTmp,
             *      img.CmdLength,
             *      img.OriginalCmdLength,
             *      out IntPtr pRgbaBuffer,
             *      out int rgbaLength,
             *      std.Width,
             *      std.Height,
             *      depthBytes);
             *
             * byte[] rgbaBuffer = new byte[rgbaLength];
             * Marshal.Copy(pRgbaBuffer, rgbaBuffer, 0, rgbaLength);
             * Marshal.FreeCoTaskMem(pRgbaBuffer);*/
            #endregion

            ProcessImageInternal(reader, std, img, out byte[] rgbaBuffer);
            //int depthBytes = std.DepthBits / 8;

            /*byte[] bufferTmp = reader.ReadBytes(img.DataLength);
             * byte[] cmdBufferTmp = reader.ReadBytes(img.CmdLength);
             *
             * int rgbaLength;
             * byte[] rgbaBuffer = new byte[std.Height * std.Width * 4];
             * ProcessImage(
             *      bufferTmp,
             *      img.DataLength,
             *      img.OriginalDataLength,
             *      cmdBufferTmp,
             *      img.CmdLength,
             *      img.OriginalCmdLength,
             *      rgbaBuffer,
             *      //out IntPtr pRgbaBuffer,
             *      out rgbaLength,
             *      std.Width,
             *      std.Height,
             *      depthBytes);
             * if (rgbaBuffer.Length != rgbaLength) {
             *      //Console.WriteLine();
             *      Console.WriteLine($"\n{rgbaLength - rgbaBuffer.Length}                ");
             *      Console.Beep(500, 2000);
             *      return;
             * }*/

            if (expand)
            {
                int offsetXBytes = std.OffsetX * depthBytes;
                //int offsetYVert = std.TotalHeight - std.OffsetY - std.Height;

                int    expStride = (std.TotalWidth * depthBytes + 3) / 4 * 4;
                byte[] expBuffer = new byte[std.TotalHeight * expStride];

                for (int y = 0; y < std.Height; y++)
                {
                    int src = y * stride;
                    //int dst = (y + offsetYVert) * expStride + offsetXBytes;
                    int dst = (std.Height - (y + 1) + std.OffsetY) * expStride + offsetXBytes;
                    Array.Copy(rgbaBuffer, src, expBuffer, dst, stride);
                }

                std.Width  = std.TotalWidth;
                std.Height = std.TotalHeight;
                rgbaBuffer = expBuffer;
            }
            else
            {
                byte[] flipBuffer = new byte[rgbaBuffer.Length];
                for (int y = 0; y < std.Height; y++)
                {
                    int src = y * stride;
                    int dst = (std.Height - (y + 1)) * stride;
                    Array.Copy(rgbaBuffer, src, flipBuffer, dst, stride);
                }
                rgbaBuffer = flipBuffer;
            }

            WritePng(file, rgbaBuffer, std.Width, std.Height, std.DepthBits);
        }
Beispiel #5
0
        public static string[] Run(Stream stream, string hg3File, string outputDir,
                                   string fileName, bool expand)
        {
            BinaryReader reader = new BinaryReader(stream);
            HG3HDR       hdr    = reader.ReadStruct <HG3HDR>();

            if (hdr.Signature != "HG-3")
            {
                throw new InvalidFileException(Path.GetFileName(hg3File), "HG3");
            }

            List <string> files     = new List <string>();
            int           backtrack = Marshal.SizeOf <HG3TAG>() - 1;

            for (int i = 0; true; i++)
            {
                HG3TAG tag = reader.ReadStruct <HG3TAG>();

                // NEW METHOD: Keep searching for the next stdinfo
                // This way we don't miss any images
                while (!tag.Signature.StartsWith("stdinfo"))
                {
                    if (stream.IsEndOfStream())
                    {
                        break;
                    }
                    stream.Position -= backtrack;
                    tag              = reader.ReadStruct <HG3TAG>();
                }
                if (stream.IsEndOfStream())
                {
                    break;
                }

                // OLD METHOD: Missed entries in a few files
                //if (!tag.signature.StartsWith(StdInfoSignature))
                //	break;

                HG3STDINFO stdInfo = reader.ReadStruct <HG3STDINFO>();

                int imgIndex = 0;

                while (tag.OffsetNext != 0)
                {
                    tag = reader.ReadStruct <HG3TAG>();

                    string pngFile = Path.Combine(outputDir, MakeFileName(
                                                      fileName, i > 0 || hdr.EntryCount > 0, i, imgIndex++));

                    /*if (tag.Signature.StartsWith("img_al")) {
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     *      HG3IMGAL imghdr = reader.ReadStruct<HG3IMGAL>();
                     *
                     *      int length = imghdr.length;
                     *      byte[] buffer = reader.ReadBytes(length);
                     *
                     *      int outLength = imghdr.original_length;
                     *      byte[] outBuffer = reader.ReadBytes(outLength);
                     *
                     *      Uncompress(outBuffer, ref outLength, buffer, length);
                     *
                     *      files.Add(pngFile);
                     *      WritePng(pngFile,
                     *              outBuffer,
                     *              stdInfo.width,
                     *              stdInfo.height,
                     *              1,
                     *              true);
                     * }
                     * else if (tag.Signature.StartsWith("img_jpg")) {
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else if (tag.Signature == "imgmode") {
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else */if (Regex.IsMatch(tag.Signature, @"img\d+"))
                    {
                        HG3IMG imghdr = reader.ReadStruct <HG3IMG>();

                        files.Add(pngFile);
                        ProcessImage(reader,
                                     pngFile,
                                     stdInfo,
                                     imghdr,
                                     expand);

                        /*stdInfo.Width,
                         * stdInfo.Height,
                         * stdInfo.DepthBits / 8,
                         * stdInfo.TotalWidth,
                         * stdInfo.TotalHeight,
                         * stdInfo.OffsetX,
                         * stdInfo.OffsetY,
                         * imghdr.DataLength,
                         * imghdr.OriginalDataLength,
                         * imghdr.CmdLength,
                         * imghdr.OriginalCmdLength);*/
                    }
                    else
                    {
                        // Skip this tag
                        stream.Position += tag.Length;
                    }
                }

                stream.Position += 8;
            }

            return(files.ToArray());
        }
        private static Hg3FrameInfo ReadHg3FrameInfo(BinaryReader reader, HGXHDR hdr, bool frameOnly)
        {
            Stream stream      = reader.BaseStream;
            long   frameOffset = stream.Position;

            HG3FRAMEHDR frameHdr = reader.ReadUnmanaged <HG3FRAMEHDR>();

            long   tagStartPosition = stream.Position;
            HG3TAG tag = reader.ReadUnmanaged <HG3TAG>();

            if (!HG3STDINFO.HasTagSignature(tag.Signature))
            {
                throw new Exception("Expected \"stdinfo\" tag!");
            }

            HG3STDINFO   stdInfo   = reader.ReadUnmanaged <HG3STDINFO>();
            Hg3FrameInfo frameInfo = new Hg3FrameInfo(frameHdr, stdInfo, frameOffset);

            while (tag.OffsetNext != 0)
            {
                stream.Position  = tagStartPosition + tag.OffsetNext;
                tagStartPosition = stream.Position;
                tag = reader.ReadUnmanaged <HG3TAG>();

                string signature = tag.Signature;

                if (HG3IMG.HasTagSignature(signature, out int imdId))                   // "img####"
                {
                    HG3IMG img = reader.ReadUnmanaged <HG3IMG>();
                    frameInfo.AddTagImg(stream, tag, img, imdId);
                }
                else if (HG3IMG_AL.HasTagSignature(signature))                   // "img_al"
                {
                    HG3IMG_AL img = reader.ReadUnmanaged <HG3IMG_AL>();
                    frameInfo.AddTagImg(stream, tag, img, 0);
                }
                else if (HG3IMG_JPG.HasTagSignature(signature))                   // "img_jpg"
                // There is no image info, reading it would increment by one byte which we don't want
                {
                    frameInfo.AddTagImg(stream, tag, new HG3IMG_JPG(), 0);
                }
                else if (frameOnly)
                {
                    if (frameInfo.Type != Hg3ImageType.None)
                    {
                        return(frameInfo);
                    }
                }
                else if (HG3ATS.HasTagSignature(signature, out int atsId))                   // "ats####"
                {
                    HG3ATS ats = reader.ReadUnmanaged <HG3ATS>();
                    frameInfo.Ats.Add(atsId, ats);
                }
                else if (HG3CPTYPE.HasTagSignature(signature))                   // "cptype"
                {
                    HG3CPTYPE cpType = reader.ReadUnmanaged <HG3CPTYPE>();
                    frameInfo.CpType = cpType;
                }
                else if (HG3IMGMODE.HasTagSignature(signature))                   // "imgmode"
                {
                    HG3IMGMODE imgMode = reader.ReadUnmanaged <HG3IMGMODE>();
                    frameInfo.ImgMode = imgMode;
                }
                else
                {
                    Trace.WriteLine($"UNKNOWN TAG: \"{signature}\"");
                }
            }
            return(frameInfo);
        }
Beispiel #7
0
        private static Hg3 Extract(Stream stream, string fileName, string directory, bool saveFrames, bool expand)
        {
            BinaryReader reader = new BinaryReader(stream);
            HG3HDR       hdr    = reader.ReadUnmanaged <HG3HDR>();

            if (hdr.Signature != "HG-3")
            {
                throw new UnexpectedFileTypeException(fileName, "HG-3");
            }

            //int backtrack = Marshal.SizeOf<HG3TAG>() - 1;
            List <KeyValuePair <HG3STDINFO, List <long> > > imageOffsets = new List <KeyValuePair <HG3STDINFO, List <long> > >();

            for (int i = 0; ; i++)
            {
                // NEW-NEW METHOD: We now know the next offset ahead
                // of time from the HG3OFFSET we're going to read.
                // Usually skips 0 bytes, otherwise usually 1-7 bytes.
                long      startPosition = stream.Position;
                HG3OFFSET offset        = reader.ReadUnmanaged <HG3OFFSET>();

                HG3TAG tag = reader.ReadUnmanaged <HG3TAG>();
                if (!HG3STDINFO.HasTagSignature(tag.Signature))
                {
                    throw new Exception("Expected \"stdinfo\" tag!");
                }

                // NEW METHOD: Keep searching for the next stdinfo
                // This way we don't miss any images

                /*int offset = 0;
                 * while (!tag.Signature.StartsWith("stdinfo")) {
                 *      if (stream.IsEndOfStream())
                 *              break;
                 *      stream.Position -= backtrack;
                 *      tag = reader.ReadStruct<HG3TAG>();
                 *      offset++;
                 * }
                 * if (stream.IsEndOfStream())
                 *      break;*/

                // OLD METHOD: Missed entries in a few files
                //if (!tag.signature.StartsWith(StdInfoSignature))
                //	break;

                HG3STDINFO stdInfo = reader.ReadUnmanaged <HG3STDINFO>();

                List <long> frameOffsets = new List <long>();
                imageOffsets.Add(new KeyValuePair <HG3STDINFO, List <long> >(stdInfo, frameOffsets));

                while (tag.OffsetNext != 0)
                {
                    tag = reader.ReadUnmanaged <HG3TAG>();

                    string signature = tag.Signature;
                    if (HG3IMG.HasTagSignature(signature))                       // "img####"
                    {
                        frameOffsets.Add(stream.Position);
                        // Skip this tag
                        stream.Position += tag.Length;
                    }

                    /*else if (HG3ATS.HasTagSignature(signature)) { // "ats####"
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else if (HG3CPTYPE.HasTagSignature(signature)) { // "cptype"
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else if (HG3IMG_AL.HasTagSignature(signature)) { // "img_al"
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else if (HG3IMG_JPG.HasTagSignature(signature)) { // "img_jpg"
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }
                     * else if (HG3IMGMODE.HasTagSignature(signature)) { // "imgmode"
                     *      // Skip this tag
                     *      stream.Position += tag.Length;
                     * }*/
                    else
                    {
                        // Skip this unknown tag
                        stream.Position += tag.Length;
                    }
                }

                if (offset.OffsetNext == 0)
                {
                    break;                     // End of stream
                }
                stream.Position = startPosition + offset.OffsetNext;
            }

            HG3STDINFO[] stdInfos        = imageOffsets.Select(p => p.Key).ToArray();
            long[][]     allFrameOffsets = imageOffsets.Select(p => p.Value.ToArray()).ToArray();
            Hg3          hg3             = new Hg3(Path.GetFileName(fileName), hdr, stdInfos, allFrameOffsets, saveFrames && expand);

            // Save any frames after we've located them all.
            // This way we truely know if something is an animation.
            if (saveFrames)
            {
                for (int imgIndex = 0; imgIndex < hg3.Count; imgIndex++)
                {
                    HG3STDINFO stdInfo  = stdInfos[imgIndex];
                    Hg3Image   hg3Image = hg3.Images[imgIndex];
                    for (int frmIndex = 0; frmIndex < hg3Image.FrameCount; frmIndex++)
                    {
                        stream.Position = hg3Image.FrameOffsets[frmIndex];
                        HG3IMG imghdr  = reader.ReadUnmanaged <HG3IMG>();
                        string pngFile = hg3.GetFrameFilePath(directory, imgIndex, frmIndex);
                        ExtractBitmap(reader, stdInfo, imghdr, expand, pngFile);
                    }
                }
            }

            return(hg3);
        }