/// <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})"); }
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)); }