Ejemplo n.º 1
0
        public static void FromStream(Stream source, MetaDataIO meta, ReadTagParams readTagParams, uint chunkSize)
        {
            long   position = source.Position;
            long   initialPos = position;
            string key, value;
            int    size;

            byte[] data = new byte[chunkSize];

            while (source.Position < initialPos + chunkSize - 4) // 4 being the "INFO" purpose that belongs to the chunk
            {
                // Key
                source.Read(data, 0, 4);
                key = Utils.Latin1Encoding.GetString(data, 0, 4);
                // Size
                source.Read(data, 0, 4);
                size = StreamUtils.DecodeInt32(data);
                if (size > 0)
                {
                    source.Read(data, 0, size);
                    // Manage parasite zeroes at the end of data
                    if (source.ReadByte() != 0)
                    {
                        source.Seek(-1, SeekOrigin.Current);
                    }
                    value = Utils.Latin1Encoding.GetString(data, 0, size);
                    meta.SetMetaField("info." + key, Utils.StripEndingZeroChars(value), readTagParams.ReadAllMetaFrames);
                }
            }
        }
Ejemplo n.º 2
0
        public static void FromStream(Stream source, MetaDataIO meta, ReadTagParams readTagParams, UInt32 chunkSize)
        {
            var    position = source.Position;
            var    initialPos = position;
            String key, value;
            Int32  size;
            var    data = new Byte[256];

            while (source.Position < initialPos + chunkSize - 4) // 4 being the "INFO" purpose that belongs to the chunk
            {
                // Key
                source.Read(data, 0, 4);
                key = Utils.Latin1Encoding.GetString(data, 0, 4);
                // Size
                source.Read(data, 0, 4);
                size = StreamUtils.DecodeInt32(data);
                // Value
                value = StreamUtils.ReadNullTerminatedString(source, Utils.Latin1Encoding);

                if (value.Length > 0)
                {
                    meta.SetMetaField("info." + key, value, readTagParams.ReadAllMetaFrames);
                }

                position = source.Position;
            }
        }
Ejemplo n.º 3
0
        public static void FromStream(Stream source, MetaDataIO meta, ReadTagParams readTagParams, uint chunkSize)
        {
            long   position = source.Position;
            long   initialPos = position;
            string key, value;
            int    size;

            byte[] data   = new byte[chunkSize];
            long   maxPos = initialPos + chunkSize - 4; // 4 being the "INFO" purpose that belongs to the chunk

            while (source.Position < maxPos)
            {
                // Key
                source.Read(data, 0, 4);
                key = Utils.Latin1Encoding.GetString(data, 0, 4);
                // Size
                source.Read(data, 0, 4);
                size = StreamUtils.DecodeInt32(data);
                // Do _NOT_ use StreamUtils.ReadNullTerminatedString because non-textual fields may be found here (e.g. NITR)
                if (size > 0)
                {
                    source.Read(data, 0, size);
                    // Manage parasite zeroes at the end of data
                    if (source.Position < maxPos && source.ReadByte() != 0)
                    {
                        source.Seek(-1, SeekOrigin.Current);
                    }
                    value = Utils.Latin1Encoding.GetString(data, 0, size);
                    meta.SetMetaField("info." + key, Utils.StripEndingZeroChars(value), readTagParams.ReadAllMetaFrames);
                }
            }
        }
Ejemplo n.º 4
0
        private static int readInt32(Stream source, MetaDataIO meta, string fieldName, byte[] buffer, bool readAllMetaFrames)
        {
            source.Read(buffer, 0, 4);
            int value = StreamUtils.DecodeInt32(buffer);

            meta.SetMetaField(fieldName, value.ToString(), readAllMetaFrames);
            return(value);
        }
Ejemplo n.º 5
0
        // ---------- SUPPORT METHODS

        private bool readWAV(Stream source, ReadTagParams readTagParams)
        {
            bool result = true;
            uint riffChunkSize;
            long riffChunkSizePos;

            byte[] data = new byte[4];

            source.Seek(0, SeekOrigin.Begin);

            // Read header
            source.Read(data, 0, 4);
            string str = Utils.Latin1Encoding.GetString(data);

            if (str.Equals(HEADER_RIFF))
            {
                _isLittleEndian = true;
            }
            else if (str.Equals(HEADER_RIFX))
            {
                _isLittleEndian = false;
            }
            else
            {
                return(false);
            }

            // Force creation of FileStructureHelper with detected endianness
            structureHelper      = new FileStructureHelper(isLittleEndian);
            id3v2StructureHelper = new FileStructureHelper(isLittleEndian);

            riffChunkSizePos = source.Position;
            source.Read(data, 0, 4);
            if (isLittleEndian)
            {
                riffChunkSize = StreamUtils.DecodeUInt32(data);
            }
            else
            {
                riffChunkSize = StreamUtils.DecodeBEUInt32(data);
            }

            // Format code
            source.Read(data, 0, 4);
            str = Utils.Latin1Encoding.GetString(data);
            if (!str.Equals(FORMAT_WAVE))
            {
                return(false);
            }


            string subChunkId;
            uint   chunkSize;
            long   chunkDataPos;
            bool   foundSample = false;
            bool   foundBext   = false;
            bool   foundInfo   = false;
            bool   foundIXml   = false;

            // Sub-chunks loop
            while (source.Position < riffChunkSize + 8)
            {
                // Chunk ID
                source.Read(data, 0, 4);
                if (0 == data[0]) // Sometimes data segment ends with a parasite null byte
                {
                    source.Seek(-3, SeekOrigin.Current);
                    source.Read(data, 0, 4);
                }

                subChunkId = Utils.Latin1Encoding.GetString(data);

                // Chunk size
                source.Read(data, 0, 4);
                if (isLittleEndian)
                {
                    chunkSize = StreamUtils.DecodeUInt32(data);
                }
                else
                {
                    chunkSize = StreamUtils.DecodeBEUInt32(data);
                }

                chunkDataPos = source.Position;

                if (subChunkId.Equals(CHUNK_FORMAT))
                {
                    source.Read(data, 0, 2);
                    if (isLittleEndian)
                    {
                        formatId = StreamUtils.DecodeUInt16(data);
                    }
                    else
                    {
                        formatId = StreamUtils.DecodeBEUInt16(data);
                    }

                    source.Read(data, 0, 2);
                    if (isLittleEndian)
                    {
                        channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(StreamUtils.DecodeUInt16(data));
                    }
                    else
                    {
                        channelsArrangement = ChannelsArrangements.GuessFromChannelNumber(StreamUtils.DecodeBEUInt16(data));
                    }

                    source.Read(data, 0, 4);
                    if (isLittleEndian)
                    {
                        sampleRate = StreamUtils.DecodeUInt32(data);
                    }
                    else
                    {
                        sampleRate = StreamUtils.DecodeBEUInt32(data);
                    }

                    source.Read(data, 0, 4);
                    if (isLittleEndian)
                    {
                        bytesPerSecond = StreamUtils.DecodeUInt32(data);
                    }
                    else
                    {
                        bytesPerSecond = StreamUtils.DecodeBEUInt32(data);
                    }

                    source.Seek(2, SeekOrigin.Current); // BlockAlign

                    source.Read(data, 0, 2);
                    if (isLittleEndian)
                    {
                        bitsPerSample = StreamUtils.DecodeUInt16(data);
                    }
                    else
                    {
                        bitsPerSample = StreamUtils.DecodeBEUInt16(data);
                    }
                }
                else if (subChunkId.Equals(CHUNK_DATA))
                {
                    headerSize = riffChunkSize - chunkSize;
                }
                else if (subChunkId.Equals(CHUNK_FACT))
                {
                    source.Read(data, 0, 4);
                    if (isLittleEndian)
                    {
                        sampleNumber = StreamUtils.DecodeInt32(data);
                    }
                    else
                    {
                        sampleNumber = StreamUtils.DecodeBEInt32(data);
                    }
                }
                else if (subChunkId.Equals(CHUNK_SAMPLE))
                {
                    structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId);
                    structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId);

                    foundSample = true;
                    tagExists   = true;

                    SampleTag.FromStream(source, this, readTagParams);
                }
                else if (subChunkId.Equals(CHUNK_BEXT))
                {
                    structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId);
                    structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId);

                    foundBext = true;
                    tagExists = true;

                    BextTag.FromStream(source, this, readTagParams);
                }
                else if (subChunkId.Equals(CHUNK_INFO))
                {
                    // Purpose of the list should be INFO
                    source.Read(data, 0, 4);
                    string purpose = Utils.Latin1Encoding.GetString(data, 0, 4);
                    if (purpose.Equals(InfoTag.PURPOSE_INFO))
                    {
                        structureHelper.AddZone(source.Position - 12, (int)(chunkSize + 8), subChunkId);
                        structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId);

                        foundInfo = true;
                        tagExists = true;

                        InfoTag.FromStream(source, this, readTagParams, chunkSize);
                    }
                }
                else if (subChunkId.Equals(CHUNK_IXML))
                {
                    structureHelper.AddZone(source.Position - 8, (int)(chunkSize + 8), subChunkId);
                    structureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId);

                    foundIXml = true;
                    tagExists = true;

                    IXmlTag.FromStream(source, this, readTagParams, chunkSize);
                }
                else if (subChunkId.Equals(CHUNK_ID3))
                {
                    id3v2Offset = source.Position;

                    // Zone is already added by Id3v2.Read
                    id3v2StructureHelper.AddZone(id3v2Offset - 8, (int)(chunkSize + 8), subChunkId);
                    id3v2StructureHelper.AddSize(riffChunkSizePos, riffChunkSize, subChunkId);
                }

                source.Seek(chunkDataPos + chunkSize, SeekOrigin.Begin);
            }

            // Add zone placeholders for future tag writing
            if (readTagParams.PrepareForWriting)
            {
                if (!foundSample)
                {
                    structureHelper.AddZone(source.Position, 0, CHUNK_SAMPLE);
                    structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_SAMPLE);
                }
                if (!foundBext)
                {
                    structureHelper.AddZone(source.Position, 0, CHUNK_BEXT);
                    structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_BEXT);
                }
                if (!foundInfo)
                {
                    structureHelper.AddZone(source.Position, 0, CHUNK_INFO);
                    structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_INFO);
                }
                if (!foundIXml)
                {
                    structureHelper.AddZone(source.Position, 0, CHUNK_IXML);
                    structureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_IXML);
                }
            }

            // ID3 zone should be set as the very last one for Windows to be able to read the LIST INFO zone properly
            if (-1 == id3v2Offset)
            {
                id3v2Offset = 0; // Switch status to "tried to read, but nothing found"

                if (readTagParams.PrepareForWriting)
                {
                    id3v2StructureHelper.AddZone(source.Position, 0, CHUNK_ID3);
                    id3v2StructureHelper.AddSize(riffChunkSizePos, riffChunkSize, CHUNK_ID3);
                }
            }

            return(result);
        }
Ejemplo n.º 6
0
        public static ImageProperties GetImageProperties(byte[] imageData, ImageFormat format = ImageFormat.Undefined)
        {
            ImageProperties props = new ImageProperties();

            if (ImageFormat.Undefined.Equals(format))
            {
                format = GetImageFormatFromPictureHeader(imageData);
            }

            if (format.Equals(ImageFormat.Unsupported))
            {
                return(props);
            }

            props.NumColorsInPalette = 0;
            props.Format             = format;

            using (MemoryStream s = new MemoryStream(imageData))
                using (BinaryReader r = new BinaryReader(s))
                {
                    long limit = (long)Math.Round(s.Length * 0.25); // TODO - test and adjust limit

                    switch (format)
                    {
                    case (ImageFormat.Tiff):
                        bool isBigEndian = (0x4D == r.ReadByte());
                        s.Seek(3, SeekOrigin.Current); // Skip the rest of the signature
                        long IFDOffset = readInt32(r, isBigEndian);

                        s.Seek(IFDOffset, SeekOrigin.Begin);

                        int nbIFDEntries = readInt16(r, isBigEndian);

                        long   initialPos = s.Position;
                        int    IFDtag, IFDFieldType, IFDNbValues, IFDValue32, IFDValue16;
                        byte[] IFDValueBinary;
                        int    photometricInterpretation = 0;
                        int    bitsPerSample             = 0;
                        int    samplesPerPixel           = 0;

                        for (int i = 0; i < nbIFDEntries; i++)
                        {
                            IFDtag         = readInt16(r, isBigEndian);
                            IFDFieldType   = readInt16(r, isBigEndian);
                            IFDNbValues    = readInt32(r, isBigEndian);
                            IFDValueBinary = r.ReadBytes(4);
                            IFDValue32     = isBigEndian? StreamUtils.DecodeBEInt32(IFDValueBinary) : StreamUtils.DecodeInt32(IFDValueBinary);
                            IFDValue16     = isBigEndian ? StreamUtils.DecodeBEInt16(IFDValueBinary) : StreamUtils.DecodeInt16(IFDValueBinary);

                            switch (IFDtag)
                            {
                            // Common properties
                            case (0x0100):
                                props.Width = IFDValue32;
                                // Specs say "SHORT or LONG" but the implementation actually takes up 4 bytes anyway -> we'll assume it's a SHORT if the last two bytes are null
                                if (0 == IFDValueBinary[2] + IFDValueBinary[3])
                                {
                                    props.Width = IFDValue16;
                                }
                                break;

                            case (0x0101):
                                props.Height = IFDValue32;
                                if (0 == IFDValueBinary[2] + IFDValueBinary[3])
                                {
                                    props.Height = IFDValue16;
                                }
                                break;

                            // Specific properties
                            case (0x0106):                      // PhotometricInterpretation
                                photometricInterpretation = IFDValue32;
                                if (IFDValue32 < 2)
                                {
                                    props.ColorDepth = 1;                             // Bilevel or greyscale image
                                }
                                else if (2 == IFDValue32)
                                {
                                    props.ColorDepth = 24;                            // RGB full color image
                                }
                                // NB : A value of 3 would indicate a palette-color image, but has no effect here
                                break;

                            case (0x0102):                      // BitsPerSample
                                bitsPerSample = IFDValue16;
                                break;

                            case (0x0115):                      // SamplesPerPixel
                                samplesPerPixel = IFDValue16;
                                break;
                            }
                        }

                        if (photometricInterpretation < 2) // Bilevel or greyscale
                        {
                            props.ColorDepth = bitsPerSample;
                        }
                        else if (2 == photometricInterpretation) // RGB
                        {
                            props.ColorDepth = 8 * samplesPerPixel;
                        }
                        else if (3 == photometricInterpretation) // Palette
                        {
                            props.ColorDepth         = 8 * samplesPerPixel;
                            props.NumColorsInPalette = bitsPerSample;
                        }


                        break;

                    case (ImageFormat.Gif):
                        byte[] GraphicControlExtensionBlockSignature = new byte[2] {
                            0x21, 0xf9
                        };

                        props.ColorDepth = 24;         // 1 byte for each component

                        s.Seek(3, SeekOrigin.Current); // Skip GIF signature

                        string version = Utils.Latin1Encoding.GetString(r.ReadBytes(3));

                        s.Seek(4, SeekOrigin.Current); // Skip logical screen descriptors

                        byte globalPaletteUse = r.ReadByte();
                        if (((globalPaletteUse & 0x80) >> 7) > 0) // File uses a global color palette
                        {
                            props.NumColorsInPalette = 2 << (globalPaletteUse & 0x07);
                        }

                        /*
                         * v89a means that the first image block should follow the first graphic control extension block
                         * (which may in turn be located after an application extension block if the GIF is animated)
                         *
                         * => The simplest way to get to the 1st image block is to look for the 1st
                         * graphic control extension block, and to skip it
                         */
                        if ("89a".Equals(version))
                        {
                            initialPos = s.Position;
                            if (StreamUtils.FindSequence(s, GraphicControlExtensionBlockSignature))
                            {
                                s.Seek(6, SeekOrigin.Current);
                            }
                            else
                            {
                                LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Invalid v89a GIF file; no graphic control extension block found");
                                // GIF is malformed; trying to find the image block directly
                                s.Seek(initialPos, SeekOrigin.Begin);
                                if (StreamUtils.FindSequence(s, new byte[1] {
                                    0x2c
                                }))
                                {
                                    s.Seek(-1, SeekOrigin.Current);
                                }
                            }
                        }

                        // At this point, we should be at the very beginning of the first image block
                        if (0x2c == r.ReadByte())
                        {
                            s.Seek(4, SeekOrigin.Current); // Skip image position descriptors
                            props.Width  = r.ReadInt16();
                            props.Height = r.ReadInt16();

                            // No global palette is set => try and find information in the local palette of the 1st image block
                            if (0 == props.NumColorsInPalette)
                            {
                                props.NumColorsInPalette = (int)Math.Pow(2, ((globalPaletteUse & 0x0F) << 4) + 1);
                            }
                        }
                        else
                        {
                            LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Error parsing GIF file; image block not found");
                        }

                        break;

                    case (ImageFormat.Bmp):

                        // Skip useless information
                        s.Seek(18, SeekOrigin.Begin);

                        props.Width  = r.ReadInt32();
                        props.Height = r.ReadInt32();
                        s.Seek(2, SeekOrigin.Current); // Planes
                        props.ColorDepth = r.ReadInt16();

                        // No support for BMP color palettes, as they seem to be exotic (and ATL has no use of this information)

                        break;

                    case (ImageFormat.Png):
                        byte[] intData               = new byte[4];
                        byte[] IHDRChunkSignature    = Utils.Latin1Encoding.GetBytes("IHDR");
                        byte[] PaletteChunkSignature = Utils.Latin1Encoding.GetBytes("PLTE");

                        // Skip header
                        s.Seek(8, SeekOrigin.Begin);

                        // Scroll chunks until we find IHDR (that should be the first one to appear, but who knows...)
                        if (0 == findPngChunk(s, IHDRChunkSignature, limit))
                        {
                            LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Invalid PNG file; no IHDR chunk found");
                        }
                        else
                        {
                            // Read IHDR chunk
                            s.Read(intData, 0, 4);
                            props.Width = StreamUtils.DecodeBEInt32(intData);
                            s.Read(intData, 0, 4);
                            props.Height     = StreamUtils.DecodeBEInt32(intData);
                            props.ColorDepth = r.ReadByte();
                            int colorType = r.ReadByte();
                            if (3 == colorType)                // PNG file uses a palette
                            {
                                s.Seek(7, SeekOrigin.Current); // 3 last useful data + ending chunk CRC
                                uint paletteChunkSize = findPngChunk(s, PaletteChunkSignature, limit);
                                if (0 == paletteChunkSize)
                                {
                                    LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Invalid PNG file; palette declared, but no PLTE chunk found");
                                }
                                else
                                {
                                    props.NumColorsInPalette = (int)Math.Floor(paletteChunkSize / 3.0);
                                }
                            }
                            else
                            {
                                props.NumColorsInPalette = 0;
                            }
                        }

                        break;

                    case (ImageFormat.Jpeg):
                        byte[] shortData          = new byte[2];
                        byte[] SOF0FrameSignature = new byte[2] {
                            0xFF, 0xC0
                        };

                        /*
                         * We just need to reach the SOF0 frame descripting the actual picture
                         *
                         * In order to handle JPEG files that contain multiple SOF0 frames (see test suite),
                         * the simplest way of proceeding is to look for all SOF0 frames in the first 25% of the file,
                         * and then read the very last one
                         */
                        long lastPos = 0;

                        while (StreamUtils.FindSequence(s, SOF0FrameSignature, limit))
                        {
                            lastPos = s.Position;
                        }

                        if (0 == lastPos)
                        {
                            LogDelegator.GetLogDelegate()(Log.LV_WARNING, "Invalid JPEG file; no SOF0 frame found");
                        }
                        else
                        {
                            // Skip frame length
                            s.Seek(2, SeekOrigin.Current);
                            bitsPerSample = r.ReadByte();
                            s.Read(shortData, 0, 2);
                            props.Height = StreamUtils.DecodeBEUInt16(shortData);
                            s.Read(shortData, 0, 2);
                            props.Width = StreamUtils.DecodeBEUInt16(shortData);
                            byte nbComponents = r.ReadByte();
                            props.ColorDepth = bitsPerSample * nbComponents;
                        }

                        break;
                    }
                }

            return(props);
        }
Ejemplo n.º 7
0
        public void StreamUtils_Exceptions()
        {
            Assert.IsFalse(StreamUtils.ArrEqualsArr(new byte[1], new byte[2]));
            Assert.IsFalse(StreamUtils.StringEqualsArr(".", new char[2]));

            try
            {
                StreamUtils.DecodeBEUInt16(new byte[1]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.DecodeUInt16(new byte[1]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.DecodeInt16(new byte[1]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.DecodeBEInt16(new byte[1]);
                Assert.Fail();
            }
            catch { }


            try
            {
                StreamUtils.DecodeBEInt24(new byte[2]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.DecodeBEUInt24(new byte[2]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.EncodeBEUInt24(0x01FFFFFF);
                Assert.Fail();
            }
            catch { }


            try
            {
                StreamUtils.DecodeBEUInt32(new byte[3]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.DecodeUInt32(new byte[3]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.DecodeBEInt32(new byte[3]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.DecodeInt32(new byte[3]);
                Assert.Fail();
            }
            catch { }


            try
            {
                StreamUtils.DecodeUInt64(new byte[7]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.DecodeBEInt64(new byte[7]);
                Assert.Fail();
            }
            catch { }


            try
            {
                StreamUtils.DecodeSynchSafeInt(new byte[6]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.DecodeSynchSafeInt32(new byte[6]);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.EncodeSynchSafeInt(1, 0);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.EncodeSynchSafeInt(1, 6);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.ReadBits(new BinaryReader(new MemoryStream()), 0, 0);
                Assert.Fail();
            }
            catch { }

            try
            {
                StreamUtils.ReadBits(new BinaryReader(new MemoryStream()), 0, 33);
                Assert.Fail();
            }
            catch { }
        }