/// <summary>
        ///    Populates the values in the current instance by parsing
        ///    its field data in a specified version.
        /// </summary>
        /// <param name="data">
        ///    A <see cref="ByteVector" /> object containing the
        ///    extracted field data.
        /// </param>
        /// <param name="version">
        ///    A <see cref="byte" /> indicating the ID3v2 version the
        ///    field data is encoded in.
        /// </param>
        protected override void ParseFields(ByteVector data, byte version)
        {
            Events          = new List <EventTimeCode> ();
            TimestampFormat = (TimestampFormat)data.Data[0];

            var incomingEventsData = data.Mid(1);

            for (var i = 0; i < incomingEventsData.Count - 1; i++)
            {
                var eventType = (EventType)incomingEventsData.Data[i];
                i++;

                var timestampData = new ByteVector(incomingEventsData.Data[i],
                                                   incomingEventsData.Data[i + 1],
                                                   incomingEventsData.Data[i + 2],
                                                   incomingEventsData.Data[i + 3]);

                i += 3;

                var timestamp = timestampData.ToInt();

                Events.Add(new EventTimeCode(eventType, timestamp));
            }
        }
		/// <summary>
		///    Creates an IFDEntry from the given values. This method is used for
		///    every entry. Custom parsing can be hooked in by overriding the
		///    <see cref="ParseIFDEntry(ushort,ushort,uint,long,uint)"/> method.
		/// </summary>
		/// <param name="tag">
		///    A <see cref="System.UInt16"/> with the tag of the entry.
		/// </param>
		/// <param name="type">
		///    A <see cref="System.UInt16"/> with the type of the entry.
		/// </param>
		/// <param name="count">
		///    A <see cref="System.UInt32"/> with the data count of the entry.
		/// </param>
		/// <param name="base_offset">
		///    A <see cref="System.Int64"/> with the base offset which every
		///    offsets in the IFD are relative to.
		/// </param>
		/// <param name="offset_data">
		///    A <see cref="ByteVector"/> containing exactly 4 byte with the data
		///    of the offset of the entry. Since this field isn't interpreted as
		///    an offset if the data can be directly stored in the 4 byte, we
		///    pass the <see cref="ByteVector"/> to easier interpret it.
		/// </param>
		/// <param name="max_offset">
		///    A <see cref="System.UInt32"/> with the maximal offset to consider for
		///    the IFD.
		/// </param>
		/// <returns>
		///    A <see cref="IFDEntry"/> with the given parameter.
		/// </returns>
		private IFDEntry CreateIFDEntry (ushort tag, ushort type, uint count, long base_offset, ByteVector offset_data, uint max_offset)
		{
			uint offset = offset_data.ToUInt (is_bigendian);

			// Fix the type for the IPTC tag.
			// From http://www.awaresystems.be/imaging/tiff/tifftags/iptc.html
			// "Often times, the datatype is incorrectly specified as LONG. "
			if (tag == (ushort) IFDEntryTag.IPTC && type == (ushort) IFDEntryType.Long) {
				type = (ushort) IFDEntryType.Byte;
			}

			var ifd_entry = ParseIFDEntry (tag, type, count, base_offset, offset);
			if (ifd_entry != null)
				return ifd_entry;

			if (count > 0x10000000) {
				// Some Nikon files are known to exhibit this corruption (or "feature").
				file.MarkAsCorrupt ("Impossibly large item count");
				return null;
			}

			// then handle the values stored in the offset data itself
			if (count == 1) {
				if (type == (ushort) IFDEntryType.Byte)
					return new ByteIFDEntry (tag, offset_data[0]);

				if (type == (ushort) IFDEntryType.SByte)
					return new SByteIFDEntry (tag, (sbyte)offset_data[0]);

				if (type == (ushort) IFDEntryType.Short)
					return new ShortIFDEntry (tag, offset_data.Mid (0, 2).ToUShort (is_bigendian));

				if (type == (ushort) IFDEntryType.SShort)
					return new SShortIFDEntry (tag, (short) offset_data.Mid (0, 2).ToUShort (is_bigendian));

				if (type == (ushort) IFDEntryType.Long)
					return new LongIFDEntry (tag, offset_data.ToUInt (is_bigendian));

				if (type == (ushort) IFDEntryType.SLong)
					return new SLongIFDEntry (tag, offset_data.ToInt (is_bigendian));

			}

			if (count == 2) {
				if (type == (ushort) IFDEntryType.Short) {
					ushort [] data = new ushort [] {
						offset_data.Mid (0, 2).ToUShort (is_bigendian),
						offset_data.Mid (2, 2).ToUShort (is_bigendian)
					};

					return new ShortArrayIFDEntry (tag, data);
				}

				if (type == (ushort) IFDEntryType.SShort) {
					short [] data = new short [] {
						(short) offset_data.Mid (0, 2).ToUShort (is_bigendian),
						(short) offset_data.Mid (2, 2).ToUShort (is_bigendian)
					};

					return new SShortArrayIFDEntry (tag, data);
				}
			}

			if (count <= 4) {
				if (type == (ushort) IFDEntryType.Undefined)
					return new UndefinedIFDEntry (tag, offset_data.Mid (0, (int)count));

				if (type == (ushort) IFDEntryType.Ascii) {
					string data = offset_data.Mid (0, (int)count).ToString ();
					int term = data.IndexOf ('\0');

					if (term > -1)
						data = data.Substring (0, term);

					return new StringIFDEntry (tag, data);
				}

				if (type == (ushort) IFDEntryType.Byte)
					return new ByteVectorIFDEntry (tag, offset_data.Mid (0, (int)count));
			}


			// FIXME: create correct type.
			if (offset > max_offset)
				return new UndefinedIFDEntry (tag, new ByteVector ());

			// then handle data referenced by the offset
			file.Seek (base_offset + offset, SeekOrigin.Begin);

			if (count == 1) {
				if (type == (ushort) IFDEntryType.Rational)
					return new RationalIFDEntry (tag, ReadRational ());

				if (type == (ushort) IFDEntryType.SRational)
					return new SRationalIFDEntry (tag, ReadSRational ());
			}

			if (count > 1) {
				if (type == (ushort) IFDEntryType.Long) {
					uint [] data = ReadUIntArray (count);

					return new LongArrayIFDEntry (tag, data);
				}

				if (type == (ushort) IFDEntryType.SLong) {
					int [] data = ReadIntArray (count);

					return new SLongArrayIFDEntry (tag, data);
				}

				if (type == (ushort) IFDEntryType.Rational) {
					Rational[] entries = new Rational [count];

					for (int i = 0; i < count; i++)
						entries[i] = ReadRational ();

					return new RationalArrayIFDEntry (tag, entries);
				}

				if (type == (ushort) IFDEntryType.SRational) {
					SRational[] entries = new SRational [count];

					for (int i = 0; i < count; i++)
						entries[i] = ReadSRational ();

					return new SRationalArrayIFDEntry (tag, entries);
				}
			}

			if (count > 2) {
				if (type == (ushort) IFDEntryType.Short) {
					ushort [] data = ReadUShortArray (count);

					return new ShortArrayIFDEntry (tag, data);
				}

				if (type == (ushort) IFDEntryType.SShort) {
					short [] data = ReadShortArray (count);

					return new SShortArrayIFDEntry (tag, data);
				}
			}

			if (count > 4) {
				if (type == (ushort) IFDEntryType.Long) {
					uint [] data = ReadUIntArray (count);

					return new LongArrayIFDEntry (tag, data);
				}

				if (type == (ushort) IFDEntryType.Byte) {
					ByteVector data = file.ReadBlock ((int) count);

					return new ByteVectorIFDEntry (tag, data);
				}

				if (type == (ushort) IFDEntryType.Ascii) {
					string data = ReadAsciiString ((int) count);

					return new StringIFDEntry (tag, data);
				}

				if (tag == (ushort) ExifEntryTag.UserComment) {
					ByteVector data = file.ReadBlock ((int) count);

					return new UserCommentIFDEntry (tag, data, file);
				}

				if (type == (ushort) IFDEntryType.Undefined) {
					ByteVector data = file.ReadBlock ((int) count);

					return new UndefinedIFDEntry (tag, data);
				}
			}

			if (type == (ushort) IFDEntryType.Float)
				return null;

			if (type == 0 || type > 12) {
				// Invalid type
				file.MarkAsCorrupt ("Invalid item type");
				return null;
			}

			// TODO: We should ignore unreadable values, erroring for now until we have sufficient coverage.
			throw new NotImplementedException (String.Format ("Unknown type/count {0}/{1} ({2})", type, count, offset));
		}
        /// <summary>
        ///    Creates an IFDEntry from the given values. This method is used for
        ///    every entry. Custom parsing can be hooked in by overriding the
        ///    <see cref="ParseIFDEntry(ushort,ushort,uint,long,uint)"/> method.
        /// </summary>
        /// <param name="tag">
        ///    A <see cref="System.UInt16"/> with the tag of the entry.
        /// </param>
        /// <param name="type">
        ///    A <see cref="System.UInt16"/> with the type of the entry.
        /// </param>
        /// <param name="count">
        ///    A <see cref="System.UInt32"/> with the data count of the entry.
        /// </param>
        /// <param name="baseOffset">
        ///    A <see cref="System.Int64"/> with the base offset which every
        ///    offsets in the IFD are relative to.
        /// </param>
        /// <param name="offsetData">
        ///    A <see cref="ByteVector"/> containing exactly 4 byte with the data
        ///    of the offset of the entry. Since this field isn't interpreted as
        ///    an offset if the data can be directly stored in the 4 byte, we
        ///    pass the <see cref="ByteVector"/> to easier interpret it.
        /// </param>
        /// <param name="maxOffset">
        ///    A <see cref="System.UInt32"/> with the maximal offset to consider for
        ///    the IFD.
        /// </param>
        /// <returns>
        ///    A <see cref="IFDEntry"/> with the given parameter.
        /// </returns>
        IFDEntry CreateIFDEntry(ushort tag, ushort type, uint count, long baseOffset, ByteVector offsetData, uint maxOffset)
        {
            uint offset = offsetData.ToUInt(is_bigendian);

            // Fix the type for the IPTC tag.
            // From http://www.awaresystems.be/imaging/tiff/tifftags/iptc.html
            // "Often times, the datatype is incorrectly specified as LONG. "
            if (tag == (ushort)IFDEntryTag.IPTC && type == (ushort)IFDEntryType.Long)
            {
                type = (ushort)IFDEntryType.Byte;
            }

            var ifd_entry = ParseIFDEntry(tag, type, count, baseOffset, offset);

            if (ifd_entry != null)
            {
                return(ifd_entry);
            }

            if (count > 0x10000000)
            {
                // Some Nikon files are known to exhibit this corruption (or "feature").
                file.MarkAsCorrupt("Impossibly large item count");
                return(null);
            }

            // then handle the values stored in the offset data itself
            if (count == 1)
            {
                if (type == (ushort)IFDEntryType.Byte)
                {
                    return(new ByteIFDEntry(tag, offsetData[0]));
                }

                if (type == (ushort)IFDEntryType.SByte)
                {
                    return(new SByteIFDEntry(tag, (sbyte)offsetData[0]));
                }

                if (type == (ushort)IFDEntryType.Short)
                {
                    return(new ShortIFDEntry(tag, offsetData.Mid(0, 2).ToUShort(is_bigendian)));
                }

                if (type == (ushort)IFDEntryType.SShort)
                {
                    return(new SShortIFDEntry(tag, offsetData.Mid(0, 2).ToUShort(is_bigendian)));
                }

                if (type == (ushort)IFDEntryType.Long)
                {
                    return(new LongIFDEntry(tag, offsetData.ToUInt(is_bigendian)));
                }

                if (type == (ushort)IFDEntryType.SLong)
                {
                    return(new SLongIFDEntry(tag, offsetData.ToInt(is_bigendian)));
                }
            }

            if (count == 2)
            {
                if (type == (ushort)IFDEntryType.Short)
                {
                    ushort[] data = new[] {
                        offsetData.Mid(0, 2).ToUShort(is_bigendian),
                        offsetData.Mid(2, 2).ToUShort(is_bigendian)
                    };

                    return(new ShortArrayIFDEntry(tag, data));
                }

                if (type == (ushort)IFDEntryType.SShort)
                {
                    short[] data = new[] {
                        (short)offsetData.Mid(0, 2).ToUShort(is_bigendian),
                        (short)offsetData.Mid(2, 2).ToUShort(is_bigendian)
                    };

                    return(new SShortArrayIFDEntry(tag, data));
                }
            }

            if (count <= 4)
            {
                if (type == (ushort)IFDEntryType.Undefined)
                {
                    return(new UndefinedIFDEntry(tag, offsetData.Mid(0, (int)count)));
                }

                if (type == (ushort)IFDEntryType.Ascii)
                {
                    string data = offsetData.Mid(0, (int)count).ToString();
                    int    term = data.IndexOf('\0');

                    if (term > -1)
                    {
                        data = data.Substring(0, term);
                    }

                    return(new StringIFDEntry(tag, data));
                }

                if (type == (ushort)IFDEntryType.Byte)
                {
                    return(new ByteVectorIFDEntry(tag, offsetData.Mid(0, (int)count)));
                }
            }


            // FIXME: create correct type.
            if (offset > maxOffset)
            {
                return(new UndefinedIFDEntry(tag, new ByteVector()));
            }

            // then handle data referenced by the offset
            file.Seek(baseOffset + offset, SeekOrigin.Begin);

            if (count == 1)
            {
                if (type == (ushort)IFDEntryType.Rational)
                {
                    return(new RationalIFDEntry(tag, ReadRational()));
                }

                if (type == (ushort)IFDEntryType.SRational)
                {
                    return(new SRationalIFDEntry(tag, ReadSRational()));
                }
            }

            if (count > 1)
            {
                if (type == (ushort)IFDEntryType.Long)
                {
                    uint[] data = ReadUIntArray(count);

                    return(new LongArrayIFDEntry(tag, data));
                }

                if (type == (ushort)IFDEntryType.SLong)
                {
                    int[] data = ReadIntArray(count);

                    return(new SLongArrayIFDEntry(tag, data));
                }

                if (type == (ushort)IFDEntryType.Rational)
                {
                    var entries = new Rational[count];

                    for (int i = 0; i < count; i++)
                    {
                        entries[i] = ReadRational();
                    }

                    return(new RationalArrayIFDEntry(tag, entries));
                }

                if (type == (ushort)IFDEntryType.SRational)
                {
                    var entries = new SRational[count];

                    for (int i = 0; i < count; i++)
                    {
                        entries[i] = ReadSRational();
                    }

                    return(new SRationalArrayIFDEntry(tag, entries));
                }
            }

            if (count > 2)
            {
                if (type == (ushort)IFDEntryType.Short)
                {
                    ushort[] data = ReadUShortArray(count);

                    return(new ShortArrayIFDEntry(tag, data));
                }

                if (type == (ushort)IFDEntryType.SShort)
                {
                    short[] data = ReadShortArray(count);

                    return(new SShortArrayIFDEntry(tag, data));
                }
            }

            if (count > 4)
            {
                if (type == (ushort)IFDEntryType.Long)
                {
                    uint[] data = ReadUIntArray(count);

                    return(new LongArrayIFDEntry(tag, data));
                }

                if (type == (ushort)IFDEntryType.Byte)
                {
                    ByteVector data = file.ReadBlock((int)count);

                    return(new ByteVectorIFDEntry(tag, data));
                }

                if (type == (ushort)IFDEntryType.Ascii)
                {
                    string data = ReadAsciiString((int)count);

                    return(new StringIFDEntry(tag, data));
                }

                if (tag == (ushort)ExifEntryTag.UserComment)
                {
                    ByteVector data = file.ReadBlock((int)count);

                    return(new UserCommentIFDEntry(tag, data, file));
                }

                if (type == (ushort)IFDEntryType.Undefined)
                {
                    ByteVector data = file.ReadBlock((int)count);

                    return(new UndefinedIFDEntry(tag, data));
                }
            }

            if (type == (ushort)IFDEntryType.Float)
            {
                return(null);
            }

            if (type == 0 || type > 12)
            {
                // Invalid type
                file.MarkAsCorrupt("Invalid item type");
                return(null);
            }

            throw new NotImplementedException($"Unknown type/count {type}/{count} ({offset})");
        }
Exemple #4
0
        private IFDEntry CreateIFDEntry(ushort tag, ushort type, uint count, long base_offset, ByteVector offset_data, uint max_offset)
        {
            uint offset = offset_data.ToUInt(is_bigendian);

            if (tag == (ushort)IFDEntryTag.IPTC && type == (ushort)IFDEntryType.Long)
            {
                type = (ushort)IFDEntryType.Byte;
            }
            var ifd_entry = ParseIFDEntry(tag, type, count, base_offset, offset);

            if (ifd_entry != null)
            {
                return(ifd_entry);
            }
            if (count > 0x10000000)
            {
                file.MarkAsCorrupt("Impossibly large item count");
                return(null);
            }
            if (count == 1)
            {
                if (type == (ushort)IFDEntryType.Byte)
                {
                    return(new ByteIFDEntry(tag, offset_data[0]));
                }
                if (type == (ushort)IFDEntryType.SByte)
                {
                    return(new SByteIFDEntry(tag, (sbyte)offset_data[0]));
                }
                if (type == (ushort)IFDEntryType.Short)
                {
                    return(new ShortIFDEntry(tag, offset_data.Mid(0, 2).ToUShort(is_bigendian)));
                }
                if (type == (ushort)IFDEntryType.SShort)
                {
                    return(new SShortIFDEntry(tag, (ushort)offset_data.Mid(0, 2).ToUShort(is_bigendian)));
                }
                if (type == (ushort)IFDEntryType.Long)
                {
                    return(new LongIFDEntry(tag, offset_data.ToUInt(is_bigendian)));
                }
                if (type == (ushort)IFDEntryType.SLong)
                {
                    return(new SLongIFDEntry(tag, offset_data.ToInt(is_bigendian)));
                }
            }
            if (count == 2)
            {
                if (type == (ushort)IFDEntryType.Short)
                {
                    ushort[] data = new ushort[]
                    {
                        offset_data.Mid(0, 2).ToUShort(is_bigendian), offset_data.Mid(2, 2).ToUShort(is_bigendian)
                    };
                    return(new ShortArrayIFDEntry(tag, data));
                }
                if (type == (ushort)IFDEntryType.SShort)
                {
                    short[] data = new short[]
                    {
                        (short)offset_data.Mid(0, 2).ToUShort(is_bigendian), (short)offset_data.Mid(2, 2).ToUShort(is_bigendian)
                    };
                    return(new SShortArrayIFDEntry(tag, data));
                }
            }
            if (count <= 4)
            {
                if (type == (ushort)IFDEntryType.Undefined)
                {
                    return(new UndefinedIFDEntry(tag, offset_data.Mid(0, (int)count)));
                }
                if (type == (ushort)IFDEntryType.Ascii)
                {
                    string data = offset_data.Mid(0, (int)count).ToString();
                    int    term = data.IndexOf('\0');
                    if (term > -1)
                    {
                        data = data.Substring(0, term);
                    }
                    return(new StringIFDEntry(tag, data));
                }
                if (type == (ushort)IFDEntryType.Byte)
                {
                    return(new ByteVectorIFDEntry(tag, offset_data.Mid(0, (int)count)));
                }
            }
            if (offset > max_offset)
            {
                return(new UndefinedIFDEntry(tag, new ByteVector()));
            }
            file.Seek(base_offset + offset, SeekOrigin.Begin);
            if (count == 1)
            {
                if (type == (ushort)IFDEntryType.Rational)
                {
                    return(new RationalIFDEntry(tag, ReadRational()));
                }
                if (type == (ushort)IFDEntryType.SRational)
                {
                    return(new SRationalIFDEntry(tag, ReadSRational()));
                }
            }
            if (count > 1)
            {
                if (type == (ushort)IFDEntryType.Long)
                {
                    uint[] data = ReadUIntArray(count);
                    return(new LongArrayIFDEntry(tag, data));
                }
                if (type == (ushort)IFDEntryType.SLong)
                {
                    int[] data = ReadIntArray(count);
                    return(new SLongArrayIFDEntry(tag, data));
                }
                if (type == (ushort)IFDEntryType.Rational)
                {
                    Rational[] entries = new Rational[count];
                    for (int i = 0; i < count; i++)
                    {
                        entries[i] = ReadRational();
                    }
                    return(new RationalArrayIFDEntry(tag, entries));
                }
                if (type == (ushort)IFDEntryType.SRational)
                {
                    SRational[] entries = new SRational[count];
                    for (int i = 0; i < count; i++)
                    {
                        entries[i] = ReadSRational();
                    }
                    return(new SRationalArrayIFDEntry(tag, entries));
                }
            }
            if (count > 2)
            {
                if (type == (ushort)IFDEntryType.Short)
                {
                    ushort[] data = ReadUShortArray(count);
                    return(new ShortArrayIFDEntry(tag, data));
                }
                if (type == (ushort)IFDEntryType.SShort)
                {
                    short[] data = ReadShortArray(count);
                    return(new SShortArrayIFDEntry(tag, data));
                }
            }
            if (count > 4)
            {
                if (type == (ushort)IFDEntryType.Long)
                {
                    uint[] data = ReadUIntArray(count);
                    return(new LongArrayIFDEntry(tag, data));
                }
                if (type == (ushort)IFDEntryType.Byte)
                {
                    ByteVector data = file.ReadBlock((int)count);
                    return(new ByteVectorIFDEntry(tag, data));
                }
                if (type == (ushort)IFDEntryType.Ascii)
                {
                    string data = ReadAsciiString((int)count);
                    return(new StringIFDEntry(tag, data));
                }
                if (tag == (ushort)ExifEntryTag.UserComment)
                {
                    ByteVector data = file.ReadBlock((int)count);
                    return(new UserCommentIFDEntry(tag, data, file));
                }
                if (type == (ushort)IFDEntryType.Undefined)
                {
                    ByteVector data = file.ReadBlock((int)count);
                    return(new UndefinedIFDEntry(tag, data));
                }
            }
            if (type == (ushort)IFDEntryType.Float)
            {
                return(null);
            }
            if (type == 0 || type > 12)
            {
                file.MarkAsCorrupt("Invalid item type");
                return(null);
            }
            throw new NotImplementedException(String.Format("Unknown type/count {0}/{1} ({2})", type, count, offset));
        }