/// <summary> /// Reads an IFD from file at position <paramref name="offset"/> relative /// to <paramref name="baseOffset"/>. /// </summary> /// <param name="baseOffset"> /// A <see cref="System.Int64"/> with the base offset which every offset /// in IFD is relative to. /// </param> /// <param name="offset"> /// A <see cref="System.UInt32"/> with the offset of the IFD relative to /// <paramref name="baseOffset"/> /// </param> /// <param name="maxOffset"> /// A <see cref="System.UInt32"/> with the maximal offset to consider for /// the IFD. /// </param> /// <returns> /// A <see cref="System.UInt32"/> with the offset of the next IFD, the /// offset is also relative to <paramref name="baseOffset"/> /// </returns> uint ReadIFD(long baseOffset, uint offset, uint maxOffset) { long length = 0; try { length = file.Length; } catch (Exception) { // Use a safety-value of 4 gigabyte. length = 1073741824L * 4; } if (baseOffset + offset > length) { file.MarkAsCorrupt("Invalid IFD offset"); return(0); } var directory = new IFDDirectory(); file.Seek(baseOffset + offset, SeekOrigin.Begin); ushort entry_count = ReadUShort(); if (file.Tell + 12 * entry_count > baseOffset + maxOffset) { file.MarkAsCorrupt("Size of entries exceeds possible data size"); return(0); } ByteVector entry_datas = file.ReadBlock(12 * entry_count); uint next_offset = ReadUInt(); for (int i = 0; i < entry_count; i++) { ByteVector entry_data = entry_datas.Mid(i * 12, 12); ushort entry_tag = entry_data.Mid(0, 2).ToUShort(is_bigendian); ushort type = entry_data.Mid(2, 2).ToUShort(is_bigendian); uint value_count = entry_data.Mid(4, 4).ToUInt(is_bigendian); ByteVector offset_data = entry_data.Mid(8, 4); IFDEntry entry = CreateIFDEntry(entry_tag, type, value_count, baseOffset, offset_data, maxOffset); if (entry == null) { continue; } if (directory.ContainsKey(entry.Tag)) { directory.Remove(entry.Tag); } directory.Add(entry.Tag, entry); } FixupDirectory(baseOffset, directory); structure.directories.Add(directory); return(next_offset); }
/// <summary> /// Renders the IFD to an ByteVector where the offset of the IFD /// itself is <paramref name="ifd_offset"/> and all offsets /// contained in the IFD are adjusted accroding it. /// </summary> /// <param name="directory"> /// A <see cref="IFDDirectory"/> with the directory to render. /// </param> /// <param name="ifd_offset"> /// A <see cref="System.UInt32"/> with the offset of the IFD /// </param> /// <param name="last"> /// A <see cref="System.Boolean"/> which is true, if the IFD is /// the last one, i.e. the offset to the next IFD, which is /// stored inside the IFD, is 0. If the value is false, the /// offset to the next IFD is set that it starts directly after /// the current one. /// </param> /// <returns> /// A <see cref="ByteVector"/> with the rendered IFD. /// </returns> private ByteVector RenderIFD(IFDDirectory directory, uint ifd_offset, bool last) { if (directory.Count > (int)UInt16.MaxValue) { throw new Exception(String.Format("Directory has too much entries: {0}", directory.Count)); } // Remove empty SUB ifds. var tags = new List <ushort>(directory.Keys); foreach (var tag in tags) { var entry = directory[tag]; if (entry is SubIFDEntry && (entry as SubIFDEntry).ChildCount == 0) { directory.Remove(tag); } } ushort entry_count = (ushort)directory.Count; // ifd_offset + size of entry_count + entries + next ifd offset uint data_offset = ifd_offset + 2 + 12 * (uint)entry_count + 4; // store the entries itself ByteVector entry_data = new ByteVector(); // store the data referenced by the entries ByteVector offset_data = new ByteVector(); entry_data.Add(ByteVector.FromUShort(entry_count, is_bigendian)); foreach (IFDEntry entry in directory.Values) { RenderEntryData(entry, entry_data, offset_data, data_offset); } if (last) { entry_data.Add("\0\0\0\0"); } else { entry_data.Add(ByteVector.FromUInt((uint)(data_offset + offset_data.Count), is_bigendian)); } if (data_offset - ifd_offset != entry_data.Count) { throw new Exception(String.Format("Expected IFD data size was {0} but is {1}", data_offset - ifd_offset, entry_data.Count)); } entry_data.Add(offset_data); return(entry_data); }
/// <summary> /// Performs some fixups to a read <see cref="IFDDirectory"/>. For some /// special cases multiple <see cref="IFDEntry"/> instances contained /// in the directory are needed. Therfore, we do the fixups after reading the /// whole directory to be sure, all entries are present. /// </summary> /// <param name="base_offset"> /// A <see cref="System.Int64"/> value with the base offset, all offsets in the /// directory refers to. /// </param> /// <param name="directory"> /// A <see cref="IFDDirectory"/> instance which was read and needs fixes. /// </param> private void FixupDirectory (long base_offset, IFDDirectory directory) { // The following two entries refer to thumbnail data, where one is the offset // to the data and the other is the length. Unnaturally both are used to describe // the data. So it is needed to keep both entries in sync and keep the thumbnail data // for writing it back. // We determine the position of the data, read it and store it in an ThumbnailDataIFDEntry // which replaces the offset-entry to thumbnail data. ushort offset_tag = (ushort) IFDEntryTag.JPEGInterchangeFormat; ushort length_tag = (ushort) IFDEntryTag.JPEGInterchangeFormatLength; if (directory.ContainsKey (offset_tag) && directory.ContainsKey (length_tag)) { var offset_entry = directory [offset_tag] as LongIFDEntry; var length_entry = directory [length_tag] as LongIFDEntry; if (offset_entry != null && length_entry != null) { uint offset = offset_entry.Value; uint length = length_entry.Value; file.Seek (base_offset + offset, SeekOrigin.Begin); ByteVector data = file.ReadBlock ((int) length); directory.Remove (offset_tag); directory.Add (offset_tag, new ThumbnailDataIFDEntry (offset_tag, data)); } } // create a StripOffsetIFDEntry if necessary ushort strip_offsets_tag = (ushort) IFDEntryTag.StripOffsets; ushort strip_byte_counts_tag = (ushort) IFDEntryTag.StripByteCounts; if (directory.ContainsKey (strip_offsets_tag) && directory.ContainsKey (strip_byte_counts_tag)) { uint [] strip_offsets = null; uint [] strip_byte_counts = null; var strip_offsets_entry = directory [strip_offsets_tag]; var strip_byte_counts_entry = directory [strip_byte_counts_tag]; if (strip_offsets_entry is LongIFDEntry) strip_offsets = new uint[] {(strip_offsets_entry as LongIFDEntry).Value}; else if (strip_offsets_entry is LongArrayIFDEntry) strip_offsets = (strip_offsets_entry as LongArrayIFDEntry).Values; if (strip_offsets == null) return; if (strip_byte_counts_entry is LongIFDEntry) strip_byte_counts = new uint[] {(strip_byte_counts_entry as LongIFDEntry).Value}; else if (strip_byte_counts_entry is LongArrayIFDEntry) strip_byte_counts = (strip_byte_counts_entry as LongArrayIFDEntry).Values; if (strip_byte_counts == null) return; directory.Remove (strip_offsets_tag); directory.Add (strip_offsets_tag, new StripOffsetsIFDEntry (strip_offsets_tag, strip_offsets, strip_byte_counts, file)); } }
/// <summary> /// Reads an IFD from file at position <paramref name="offset"/> relative /// to <paramref name="base_offset"/>. /// </summary> /// <param name="base_offset"> /// A <see cref="System.Int64"/> with the base offset which every offset /// in IFD is relative to. /// </param> /// <param name="offset"> /// A <see cref="System.UInt32"/> with the offset of the IFD relative to /// <paramref name="base_offset"/> /// </param> /// <param name="max_offset"> /// A <see cref="System.UInt32"/> with the maximal offset to consider for /// the IFD. /// </param> /// <returns> /// A <see cref="System.UInt32"/> with the offset of the next IFD, the /// offset is also relative to <paramref name="base_offset"/> /// </returns> private uint ReadIFD (long base_offset, uint offset, uint max_offset) { long length = 0; try { length = file.Length; } catch (Exception) { // Use a safety-value of 4 gigabyte. length = 1073741824L * 4; } if (base_offset + offset > length) { file.MarkAsCorrupt ("Invalid IFD offset"); return 0; } var directory = new IFDDirectory (); file.Seek (base_offset + offset, SeekOrigin.Begin); ushort entry_count = ReadUShort (); if (file.Tell + 12 * entry_count > base_offset + max_offset) { file.MarkAsCorrupt ("Size of entries exceeds possible data size"); return 0; } ByteVector entry_datas = file.ReadBlock (12 * entry_count); uint next_offset = ReadUInt (); for (int i = 0; i < entry_count; i++) { ByteVector entry_data = entry_datas.Mid (i * 12, 12); ushort entry_tag = entry_data.Mid (0, 2).ToUShort (is_bigendian); ushort type = entry_data.Mid (2, 2).ToUShort (is_bigendian); uint value_count = entry_data.Mid (4, 4).ToUInt (is_bigendian); ByteVector offset_data = entry_data.Mid (8, 4); IFDEntry entry = CreateIFDEntry (entry_tag, type, value_count, base_offset, offset_data, max_offset); if (entry == null) continue; if (directory.ContainsKey (entry.Tag)) directory.Remove (entry.Tag); directory.Add (entry.Tag, entry); } FixupDirectory (base_offset, directory); structure.directories.Add (directory); return next_offset; }
/// <summary> /// Performs some fixups to a read <see cref="IFDDirectory"/>. For some /// special cases multiple <see cref="IFDEntry"/> instances contained /// in the directory are needed. Therfore, we do the fixups after reading the /// whole directory to be sure, all entries are present. /// </summary> /// <param name="baseOffset"> /// A <see cref="System.Int64"/> value with the base offset, all offsets in the /// directory refers to. /// </param> /// <param name="directory"> /// A <see cref="IFDDirectory"/> instance which was read and needs fixes. /// </param> void FixupDirectory(long baseOffset, IFDDirectory directory) { // The following two entries refer to thumbnail data, where one is the offset // to the data and the other is the length. Unnaturally both are used to describe // the data. So it is needed to keep both entries in sync and keep the thumbnail data // for writing it back. // We determine the position of the data, read it and store it in an ThumbnailDataIFDEntry // which replaces the offset-entry to thumbnail data. ushort offset_tag = (ushort)IFDEntryTag.JPEGInterchangeFormat; ushort length_tag = (ushort)IFDEntryTag.JPEGInterchangeFormatLength; if (directory.ContainsKey(offset_tag) && directory.ContainsKey(length_tag)) { if (directory[offset_tag] is LongIFDEntry offset_entry && directory[length_tag] is LongIFDEntry length_entry) { uint offset = offset_entry.Value; uint length = length_entry.Value; file.Seek(baseOffset + offset, SeekOrigin.Begin); var data = file.ReadBlock((int)length); directory.Remove(offset_tag); directory.Add(offset_tag, new ThumbnailDataIFDEntry(offset_tag, data)); } } // create a StripOffsetIFDEntry if necessary ushort strip_offsets_tag = (ushort)IFDEntryTag.StripOffsets; ushort strip_byte_counts_tag = (ushort)IFDEntryTag.StripByteCounts; if (directory.ContainsKey(strip_offsets_tag) && directory.ContainsKey(strip_byte_counts_tag)) { uint[] strip_offsets = null; uint[] strip_byte_counts = null; var strip_offsets_entry = directory[strip_offsets_tag]; var strip_byte_counts_entry = directory[strip_byte_counts_tag]; if (strip_offsets_entry is LongIFDEntry) { strip_offsets = new[] { (strip_offsets_entry as LongIFDEntry).Value } } ; else if (strip_offsets_entry is LongArrayIFDEntry) { strip_offsets = (strip_offsets_entry as LongArrayIFDEntry).Values; } if (strip_offsets == null) { return; } if (strip_byte_counts_entry is LongIFDEntry) { strip_byte_counts = new[] { (strip_byte_counts_entry as LongIFDEntry).Value } } ; else if (strip_byte_counts_entry is LongArrayIFDEntry) { strip_byte_counts = (strip_byte_counts_entry as LongArrayIFDEntry).Values; } if (strip_byte_counts == null) { return; } directory.Remove(strip_offsets_tag); directory.Add(strip_offsets_tag, new StripOffsetsIFDEntry(strip_offsets_tag, strip_offsets, strip_byte_counts, file)); } }
/// <summary> /// Renders the IFD to an ByteVector where the offset of the IFD /// itself is <paramref name="ifd_offset"/> and all offsets /// contained in the IFD are adjusted accroding it. /// </summary> /// <param name="directory"> /// A <see cref="IFDDirectory"/> with the directory to render. /// </param> /// <param name="ifd_offset"> /// A <see cref="System.UInt32"/> with the offset of the IFD /// </param> /// <param name="last"> /// A <see cref="System.Boolean"/> which is true, if the IFD is /// the last one, i.e. the offset to the next IFD, which is /// stored inside the IFD, is 0. If the value is false, the /// offset to the next IFD is set that it starts directly after /// the current one. /// </param> /// <returns> /// A <see cref="ByteVector"/> with the rendered IFD. /// </returns> private ByteVector RenderIFD(IFDDirectory directory, uint ifd_offset, bool last) { if (directory.Count > (int)UInt16.MaxValue) throw new Exception (String.Format ("Directory has too much entries: {0}", directory.Count)); // Remove empty SUB ifds. var tags = new List<ushort> (directory.Keys); foreach (var tag in tags) { var entry = directory [tag]; if (entry is SubIFDEntry && (entry as SubIFDEntry).ChildCount == 0) { directory.Remove (tag); } } ushort entry_count = (ushort) directory.Count; // ifd_offset + size of entry_count + entries + next ifd offset uint data_offset = ifd_offset + 2 + 12 * (uint) entry_count + 4; // store the entries itself ByteVector entry_data = new ByteVector (); // store the data referenced by the entries ByteVector offset_data = new ByteVector (); entry_data.Add (ByteVector.FromUShort (entry_count, is_bigendian)); foreach (IFDEntry entry in directory.Values) RenderEntryData (entry, entry_data, offset_data, data_offset); if (last) entry_data.Add ("\0\0\0\0"); else entry_data.Add (ByteVector.FromUInt ((uint) (data_offset + offset_data.Count), is_bigendian)); if (data_offset - ifd_offset != entry_data.Count) throw new Exception (String.Format ("Expected IFD data size was {0} but is {1}", data_offset - ifd_offset, entry_data.Count)); entry_data.Add (offset_data); return entry_data; }
/// <summary> /// Reads an IFD from file at position <paramref name="offset"/> relative /// to <paramref name="base_offset"/>. /// </summary> /// <param name="base_offset"> /// A <see cref="System.Int64"/> with the base offset which every offset /// in IFD is relative to. /// </param> /// <param name="offset"> /// A <see cref="System.UInt32"/> with the offset of the IFD relative to /// <paramref name="base_offset"/> /// </param> /// <param name="max_offset"> /// A <see cref="System.UInt32"/> with the maximal offset to consider for /// the IFD. /// </param> /// <returns> /// A <see cref="System.UInt32"/> with the offset of the next IFD, the /// offset is also relative to <paramref name="base_offset"/> /// </returns> private uint ReadIFD(long base_offset, uint offset, uint max_offset) { long length = 0; try { length = file.Length; } catch (Exception) { // Use a safety-value of 4 gigabyte. length = 1073741824L * 4; } if (base_offset + offset > length) { file.MarkAsCorrupt("Invalid IFD offset"); return(0); } var directory = new IFDDirectory(); file.Seek(base_offset + offset, SeekOrigin.Begin); ushort entry_count = ReadUShort(); if (file.Tell + 12 * entry_count > base_offset + max_offset) { file.MarkAsCorrupt("Size of entries exceeds possible data size"); return(0); } ByteVector entry_datas = file.ReadBlock(12 * entry_count); uint next_offset = ReadUInt(); for (int i = 0; i < entry_count; i++) { ByteVector entry_data = entry_datas.Mid(i * 12, 12); ushort entry_tag = entry_data.Mid(0, 2).ToUShort(is_bigendian); ushort type = entry_data.Mid(2, 2).ToUShort(is_bigendian); uint value_count = entry_data.Mid(4, 4).ToUInt(is_bigendian); ByteVector offset_data = entry_data.Mid(8, 4); // Even if the value count represents a single bytes it's impossible // for the count plus the base offset to be bigger than the entire file, // ignore this entry and keep going if (value_count + base_offset > max_offset) { file.MarkAsCorrupt("Value count + base offset is greater than the maximum possible offset"); continue; } IFDEntry entry = CreateIFDEntry(entry_tag, type, value_count, base_offset, offset_data, max_offset); if (entry == null) { continue; } if (directory.ContainsKey(entry.Tag)) { directory.Remove(entry.Tag); } directory.Add(entry.Tag, entry); } FixupDirectory(base_offset, directory); structure.directories.Add(directory); return(next_offset); }
/// <summary> /// Performs some fixups to a read <see cref="IFDDirectory"/>. For some /// special cases multiple <see cref="IFDEntry"/> instances contained /// in the directory are needed. Therfore, we do the fixups after reading the /// whole directory to be sure, all entries are present. /// </summary> /// <param name="base_offset"> /// A <see cref="System.Int64"/> value with the base offset, all offsets in the /// directory refers to. /// </param> /// <param name="directory"> /// A <see cref="IFDDirectory"/> instance which was read and needs fixes. /// </param> private void FixupDirectory (long base_offset, IFDDirectory directory) { // The following two entries refer to thumbnail data, where one is the offset // to the data and the other is the length. Unnaturally both are used to describe // the data. So it is needed to keep both entries in sync and keep the thumbnail data // for writing it back. // We determine the position of the data, read it and store it in an ThumbnailDataIFDEntry // which replaces the offset-entry to thumbnail data. ushort offset_tag = (ushort) IFDEntryTag.JPEGInterchangeFormat; ushort length_tag = (ushort) IFDEntryTag.JPEGInterchangeFormatLength; if (directory.ContainsKey (offset_tag) && directory.ContainsKey (length_tag)) { var offset_entry = directory [offset_tag] as LongIFDEntry; var length_entry = directory [length_tag] as LongIFDEntry; if (offset_entry != null && length_entry != null) { uint offset = offset_entry.Value; uint length = length_entry.Value; file.Seek (base_offset + offset, SeekOrigin.Begin); ByteVector data = file.ReadBlock ((int) length); directory.Remove (offset_tag); directory.Add (offset_tag, new ThumbnailDataIFDEntry (offset_tag, data)); } } }
/// <summary> /// Reads an IFD from file at position <paramref name="offset"/> relative /// to <paramref name="base_offset"/>. /// </summary> /// <param name="base_offset"> /// A <see cref="System.Int64"/> with the base offset which every offset /// in IFD is relative to. /// </param> /// <param name="offset"> /// A <see cref="System.UInt32"/> with the offset of the IFD relative to /// <paramref name="base_offset"/> /// </param> /// <param name="max_offset"> /// A <see cref="System.UInt32"/> with the maximal offset to consider for /// the IFD. /// </param> /// <returns> /// A <see cref="System.UInt32"/> with the offset of the next IFD, the /// offset is also relative to <paramref name="base_offset"/> /// </returns> private uint ReadIFD (long base_offset, uint offset, uint max_offset) { if (base_offset + offset > file.Length) throw new Exception (String.Format ("Invalid IFD offset {0}, length: {1}", offset, file.Length)); var directory = new IFDDirectory (); file.Seek (base_offset + offset, SeekOrigin.Begin); ushort entry_count = ReadUShort (); if (file.Tell + 12 * entry_count > base_offset + max_offset) throw new Exception (String.Format ("Size of entries exceeds possible data size")); ByteVector entry_datas = file.ReadBlock (12 * entry_count); uint next_offset = ReadUInt (); for (int i = 0; i < entry_count; i++) { ByteVector entry_data = entry_datas.Mid (i * 12, 12); ushort entry_tag = entry_data.Mid (0, 2).ToUShort (is_bigendian); ushort type = entry_data.Mid (2, 2).ToUShort (is_bigendian); uint value_count = entry_data.Mid (4, 4).ToUInt (is_bigendian); ByteVector offset_data = entry_data.Mid (8, 4); IFDEntry entry = CreateIFDEntry (entry_tag, type, value_count, base_offset, offset_data, max_offset); if (entry == null) continue; if (directory.ContainsKey (entry.Tag)) directory.Remove (entry.Tag); directory.Add (entry.Tag, entry); } FixupDirectory (base_offset, directory); structure.directories.Add (directory); return next_offset; }
private void FixupDirectory(long base_offset, IFDDirectory directory) { ushort offset_tag = (ushort)IFDEntryTag.JPEGInterchangeFormat; ushort length_tag = (ushort)IFDEntryTag.JPEGInterchangeFormatLength; if (directory.ContainsKey(offset_tag) && directory.ContainsKey(length_tag)) { var offset_entry = directory[offset_tag] as LongIFDEntry; var length_entry = directory[length_tag] as LongIFDEntry; if (offset_entry != null && length_entry != null) { uint offset = offset_entry.Value; uint length = length_entry.Value; file.Seek(base_offset + offset, SeekOrigin.Begin); ByteVector data = file.ReadBlock((int)length); directory.Remove(offset_tag); directory.Add(offset_tag, new ThumbnailDataIFDEntry(offset_tag, data)); } } ushort strip_offsets_tag = (ushort)IFDEntryTag.StripOffsets; ushort strip_byte_counts_tag = (ushort)IFDEntryTag.StripByteCounts; if (directory.ContainsKey(strip_offsets_tag) && directory.ContainsKey(strip_byte_counts_tag)) { uint[] strip_offsets = null; uint[] strip_byte_counts = null; var strip_offsets_entry = directory[strip_offsets_tag]; var strip_byte_counts_entry = directory[strip_byte_counts_tag]; if (strip_offsets_entry is LongIFDEntry) { strip_offsets = new uint[] { (strip_offsets_entry as LongIFDEntry).Value }; } else if (strip_offsets_entry is LongArrayIFDEntry) { strip_offsets = (strip_offsets_entry as LongArrayIFDEntry).Values; } if (strip_offsets == null) { return; } if (strip_byte_counts_entry is LongIFDEntry) { strip_byte_counts = new uint[] { (strip_byte_counts_entry as LongIFDEntry).Value }; } else if (strip_byte_counts_entry is LongArrayIFDEntry) { strip_byte_counts = (strip_byte_counts_entry as LongArrayIFDEntry).Values; } if (strip_byte_counts == null) { return; } directory.Remove(strip_offsets_tag); directory.Add(strip_offsets_tag, new StripOffsetsIFDEntry(strip_offsets_tag, strip_offsets, strip_byte_counts, file)); } }