protected virtual void ReadSubFiles(BinaryReader binaryReader, SffSpriteV2 sprite)
        {
            // Go to first sub file
            binaryReader.BaseStream.Seek(sprite.SpriteOffset, SeekOrigin.Begin);

            // Read all sprites
            for (var i = 0; i < sprite.SpriteCount; ++i)
            {
                var subFile = new SpriteSubFileSffV2
                {
                    SpriteGroup       = binaryReader.ReadUInt16(),
                    SpriteNumber      = binaryReader.ReadUInt16(),
                    SpriteImageWidth  = binaryReader.ReadUInt16(),
                    SpriteImageHeight = binaryReader.ReadUInt16(),
                    SpriteImageXAxis  = binaryReader.ReadUInt16(),
                    SpriteImageYAxis  = binaryReader.ReadUInt16(),
                    SpriteLinkedIndex = binaryReader.ReadUInt16(),
                    CompressionMethod = binaryReader.ReadByte(),
                    ColorDepth        = binaryReader.ReadByte(),
                    DataOffset        = binaryReader.ReadUInt32(), // Offset to data
                    DataLength        = binaryReader.ReadUInt32(),
                    PaletteIndex      = binaryReader.ReadUInt16(),
                    LoadMode          = binaryReader.ReadUInt16()
                };

                sprite.AddSubFile(subFile);
            }
        }
        public override async Task <ISprite> ReadAsync(BinaryReader binaryReader, string signature, string version)
        {
            var sprite = new SffSpriteV2
            {
                Signature        = signature,                  // 12 bytes
                Version          = version,                    // 4 bytes
                Unknown1         = binaryReader.ReadBytes(10), // TODO: ???
                PaletteMapOffset = binaryReader.ReadUInt32(),
                Unknown2         = binaryReader.ReadBytes(6),  // TODO: ???
                SpriteOffset     = binaryReader.ReadUInt32(),  // First sprite offset
                SpriteCount      = binaryReader.ReadUInt32(),  // Number of sprites
                PaletteOffset    = binaryReader.ReadUInt32(),  // First palette offset
                PaletteCount     = binaryReader.ReadUInt32(),  // Number of palettes
                LDataOffset      = binaryReader.ReadUInt32(),  // Literal Block Data Information
                LDataLength      = binaryReader.ReadUInt32(),  // Palettes + SpritesData(OnDemand)
                TDataOffset      = binaryReader.ReadUInt32(),  // Translated Data Block Information
                TDataLength      = binaryReader.ReadUInt32(),  // SpritesData(OnLoad)
                Comment          = binaryReader.ReadBytes(444) // Space for comment
            };

            this.ReadSubFiles(binaryReader, sprite);

            this.ReadPalettesInfo(binaryReader, sprite);

            this.ReadPalettesColors(binaryReader, sprite);

            await this.ReadSubFilesDataAsync(binaryReader, sprite);

            return(sprite);
        }
        protected virtual void ReadPalettesColors(BinaryReader binaryReader, SffSpriteV2 sprite)
        {
            var paletteReader = NegumContainer.Resolve <IPaletteReader>();

            foreach (var palette in sprite.Palettes)
            {
                // Go to palette data
                binaryReader.BaseStream.Seek(sprite.LDataOffset + palette.LDataOffset, SeekOrigin.Begin);

                var paletteData = binaryReader.ReadBytes((int)palette.LDataLength);

                // Read colors of palette - create temporary palette
                var paletteWithColors = paletteReader.ReadExact(paletteData, palette.ColorNumber, true);

                // Copy colors to destination palette
                paletteWithColors.CopyTo(palette);
            }
        }
        protected virtual void ReadPalettesInfo(BinaryReader binaryReader, SffSpriteV2 sprite)
        {
            // Go to first palette
            binaryReader.BaseStream.Seek(sprite.PaletteOffset, SeekOrigin.Begin);

            // Read all palettes
            for (var i = 0; i < sprite.PaletteCount; ++i)
            {
                var palette = new Palette
                {
                    GroupNumber = binaryReader.ReadUInt16(),
                    ItemNumber  = binaryReader.ReadUInt16(),
                    ColorNumber = binaryReader.ReadUInt16(),
                    LinkedIndex = binaryReader.ReadUInt16(),
                    LDataOffset = binaryReader.ReadUInt32(),
                    LDataLength = binaryReader.ReadUInt32()
                };

                sprite.AddPalette(palette);
            }
        }
        protected virtual async Task <IEnumerable <byte> > ParsePngAsync(ISffPngReader sffPngReader, SffSpriteV2 sprite,
                                                                         ISpriteSubFileSffV2 subFile,
                                                                         int pngFormat)
        {
            var ctx = new SffPngReaderContext
            {
                PngFormat = pngFormat,
                RawImage  = subFile.RawImage
            };

            if (sprite.Palettes.Count() > subFile.PaletteIndex)
            {
                ctx.Palette = sprite.Palettes.ElementAt(subFile.PaletteIndex);
            }

            var image = await sffPngReader.ReadAsync(ctx);

            return(image);
        }
        protected virtual async Task ReadSubFilesDataAsync(BinaryReader binaryReader, SffSpriteV2 sprite)
        {
            var sffRle8Reader = NegumContainer.Resolve <ISffRle8Reader>();
            var sffRle5Reader = NegumContainer.Resolve <ISffRle5Reader>();
            var sffLz5Reader  = NegumContainer.Resolve <ISffLz5Reader>();
            var sffPngReader  = NegumContainer.Resolve <ISffPngReader>();

            // Read all sprites pixels
            foreach (var subFileSffV2 in sprite.SpriteSubFiles)
            {
                var subFile = subFileSffV2 as SpriteSubFileSffV2;

                binaryReader.BaseStream.Seek(
                    (subFile.LoadMode == 1 ? sprite.TDataOffset : sprite.LDataOffset) + subFile.DataOffset,
                    SeekOrigin.Begin);

                if (subFile.CompressionMethod == 0) // No conversion
                {
                    subFile.ImageSize = 0;          // Indicate that there was no compression
                    subFile.RawImage  = binaryReader.ReadBytes((int)subFile.DataLength);
                }
                else
                {
                    const byte totalBytesPerImageSize = 4;

                    subFile.ImageSize = binaryReader.ReadUInt32();

                    if (subFile.DataLength >= totalBytesPerImageSize)
                    {
                        subFile.RawImage = binaryReader.ReadBytes((int)(subFile.DataLength - totalBytesPerImageSize));
                    }
                    else
                    {
                        continue;
                    }
                }

                if (subFile.DataLength == 0)
                {
                    // TODO: Process sprite from linked index -> subFile.SpriteLinkedIndex
                    continue;
                }

                switch (subFile.CompressionMethod)
                {
                case 0:     // Raw Data
                    subFile.Image = subFile.RawImage;
                    break;

                case 1:     // Invalid
                    break;

                case 2:     // RLE8 (Run-Length Encoding at 8 bits-per-pixel pixmap)
                    subFile.Image = await sffRle8Reader.ReadAsync(subFile.RawImage);

                    break;

                case 3:     // RLE5 (Run-Length Encoding at 5 bits-per-pixel pixmap)
                    subFile.Image = await sffRle5Reader.ReadAsync(subFile.RawImage);

                    break;

                case 4:     // LZ5
                    subFile.Image = await sffLz5Reader.ReadAsync(subFile.RawImage);

                    break;

                case 10:     // PNG8
                    subFile.Image = await ParsePngAsync(sffPngReader, sprite, subFile, 8);

                    break;

                case 11:     // PNG24
                    subFile.Image = await ParsePngAsync(sffPngReader, sprite, subFile, 24);

                    break;

                case 12:     // PNG32
                    subFile.Image = await ParsePngAsync(sffPngReader, sprite, subFile, 32);

                    break;
                }
            }
        }