libopenraw  0.3.7
ifddir.cpp
1 /* -*- mode:c++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil; -*- */
2 /*
3  * libopenraw - ifddir.cpp
4  *
5  * Copyright (C) 2006-2022 Hubert Figuière
6  *
7  * This library is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation, either version 3 of
10  * the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library. If not, see
19  * <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <fcntl.h>
23 #include <cstdint>
24 #include <utility>
25 
26 #include "trace.hpp"
27 #include "io/stream.hpp"
28 #include "ifdfilecontainer.hpp"
29 #include "ifddir.hpp"
30 #include "makernotedir.hpp"
31 #include "metavalue.hpp"
32 #include "exif/exif_tags.hpp"
33 
34 using namespace Debug;
35 
36 namespace OpenRaw {
37 
38 namespace Internals {
39 
40 bool IfdDir::isPrimary() const
41 {
42  auto result = getValue<uint32_t>(IFD::EXIF_TAG_NEW_SUBFILE_TYPE);
43  return result && (result.value() == 0);
44 }
45 
46 bool IfdDir::isThumbnail() const
47 {
48  auto result = getValue<uint32_t>(IFD::EXIF_TAG_NEW_SUBFILE_TYPE);
49  return result && (result.value() == 1);
50 }
51 
52 IfdDir::IfdDir(off_t _offset, RawContainer& _container, IfdDirType _type, const TagTable& tag_table)
53  : m_type(_type)
54  , m_offset(_offset), m_container(_container), m_entries()
55  , m_tag_table(&tag_table)
56  , m_base_offset(0)
57  , m_endian(_container.endian())
58 {
59 }
60 
61 IfdDir::~IfdDir()
62 {
63 }
64 
66 {
67  LOGDBG1("IfdDir::load() m_offset =%lld\n", (long long int)m_offset);
68 
69  auto file = m_container.file();
70  m_entries.clear();
71  file->seek(m_offset, SEEK_SET);
72 
73  int16_t numEntries = m_container.readInt16(file, m_endian).value_or(0);
74  LOGDBG1("num entries %d\n", numEntries);
75 
76  for (int16_t i = 0; i < numEntries; i++) {
77  uint32_t data;
78  auto id = m_container.readUInt16(file, m_endian);
79  auto type = m_container.readInt16(file, m_endian);
80  auto count = m_container.readInt32(file, m_endian);
81  size_t sz = file->read(&data, 4);
82  if (id.empty() || type.empty() || count.empty() || sz != 4) {
83  LOGERR("Failed to read entry %d\n", i);
84  return false;
85  }
86  uint16_t n_id = id.value();
87  IfdEntry::Ref entry =
88  std::make_shared<IfdEntry>(n_id, type.value(),
89  count.value(), data, *this);
90  m_entries[n_id] = entry;
91  }
92 
93  return true;
94 }
95 
96 IfdEntry::Ref IfdDir::getEntry(uint16_t id) const
97 {
98  std::map<uint16_t, IfdEntry::Ref>::const_iterator iter;
99  iter = m_entries.find(id);
100  if (iter != m_entries.end()) {
101  return iter->second;
102  }
103  return IfdEntry::Ref();
104 }
105 
108 {
109  IfdEntry::Ref e = getEntry(id);
110  if (e) {
112  }
113  return Option<uint32_t>();
114 }
115 
117 {
118  int16_t numEntries = 0;
119  auto file = m_container.file();
120 
121  if (m_entries.size() == 0) {
122  file->seek(m_offset, SEEK_SET);
123  numEntries = m_container.readInt16(file, m_endian).value_or(0);
124  LOGDBG1("numEntries =%d shifting %d bytes\n", numEntries, (numEntries * 12) + 2);
125  } else {
126  numEntries = m_entries.size();
127  }
128 
129  file->seek(m_offset + (numEntries * 12) + 2, SEEK_SET);
130  // XXX how about we check the error. Even though 0 is not valid.
131  return m_container.readInt32(file, m_endian).value_or(0);
132 }
133 
137 IfdDir::Ref IfdDir::getSubIFD(uint32_t idx) const
138 {
139  IfdEntry::Ref e = getEntry(IFD::EXIF_TAG_SUB_IFDS);
140 
141  if (e != nullptr) {
142  auto result = getEntryArrayValue<uint32_t>(*e);
143  if (result) {
144  std::vector<uint32_t> offsets = result.value();
145  if (idx >= offsets.size()) {
146  Ref ref = std::make_shared<IfdDir>(offsets[idx], m_container, OR_IFD_SUBIFD);
147  ref->load();
148  return ref;
149  }
150  } else {
151  LOGERR("Can't get SubIFD offsets\n");
152  }
153  }
154  return Ref();
155 }
156 
158 {
159  std::vector<IfdDir::Ref> ifds;
160  IfdEntry::Ref e = getEntry(IFD::EXIF_TAG_SUB_IFDS);
161  if (e != nullptr) {
162  auto result = getEntryArrayValue<uint32_t>(*e);
163  if (result) {
164  std::vector<uint32_t> offsets = result.value();
165  for (auto offset_ : offsets) {
166  Ref ifd = std::make_shared<IfdDir>(offset_, m_container, OR_IFD_SUBIFD);
167  ifd->load();
168  ifds.push_back(ifd);
169  }
170  return Option<std::vector<IfdDir::Ref>>(std::move(ifds));
171  }
172  }
174 }
175 
180 {
181  auto result = getValue<uint32_t>(IFD::EXIF_TAG_EXIF_IFD_POINTER);
182  if (result.empty()) {
183  LOGDBG1("Exif IFD offset not found.\n");
184  return Ref();
185  }
186 
187  uint32_t val_offset = result.value();
188  LOGDBG1("Exif IFD offset (uncorrected) = %u\n", val_offset);
189  val_offset += m_container.exifOffsetCorrection();
190  LOGDBG1("Exif IFD offset = %u\n", val_offset);
191 
192  Ref ref = std::make_shared<IfdDir>(val_offset, m_container, OR_IFD_EXIF);
193  ref->load();
194  return ref;
195 }
196 
198 {
199  uint32_t val_offset = 0;
200  IfdEntry::Ref e = getEntry(IFD::EXIF_TAG_MAKER_NOTE);
201  if (!e) {
202  LOGDBG1("MakerNote IFD offset not found.\n");
203  return MakerNoteDir::Ref();
204  }
205  val_offset = e->offset();
206  LOGDBG1("MakerNote IFD offset (uncorrected) = %u\n", val_offset);
207  val_offset += m_container.exifOffsetCorrection();
208  LOGDBG1("MakerNote IFD offset = %u\n", val_offset);
209 
210  auto ref = MakerNoteDir::createMakerNote(val_offset, m_container, file_type);
211  if (ref) {
212  ref->load();
213  }
214 
215  return ref;
216 }
217 
218 const char* IfdDir::getTagName(uint32_t tag) const
219 {
220  auto iter = m_tag_table->find(tag);
221  if (iter != m_tag_table->end()) {
222  return iter->second;
223  }
224  return nullptr;
225 }
226 
228 {
229  uint32_t v = 0;
230 
231  try {
232  switch (e.type())
233  {
234  case IFD::EXIF_FORMAT_LONG:
235  v = getEntryValue<uint32_t>(e, idx);
236  break;
237  case IFD::EXIF_FORMAT_SHORT:
238  v = getEntryValue<uint16_t>(e, idx);
239  break;
240  case IFD::EXIF_FORMAT_RATIONAL:
241  {
242  IFD::ORRational r = getEntryValue<IFD::ORRational>(e, idx);
243  if (r.denom == 0) {
244  v = 0;
245  } else {
246  v = r.num / r.denom;
247  }
248  break;
249  }
250  default:
251  break;
252  }
253  }
254  catch (const std::exception & ex) {
255  LOGERR("Exception raised %s fetch integer value for %d\n", ex.what(), e.id());
256  }
257 
258  return v;
259 }
260 
261 namespace {
262 
263 template<class T>
264 void convert(const IfdDir& dir, Internals::IfdEntry& e, std::vector<MetaValue::value_t>& values)
265 {
266  auto result = dir.getEntryArrayValue<T>(e);
267  LOGASSERT(!!result);
268  if (result) {
269  std::vector<T> v = result.value();
270  values.insert(values.end(), v.cbegin(), v.cend());
271  }
272 }
273 
274 // T is the Ifd primitive type. T2 is the target MetaValue type.
275 template<class T, class T2>
276 void convert(const IfdDir& dir, Internals::IfdEntry& e, std::vector<MetaValue::value_t>& values)
277 {
278  auto result = dir.getEntryArrayValue<T>(e);
279  LOGASSERT(!!result);
280  if (result) {
281  std::vector<T> v = result.value();
282  for (const auto & elem : v) {
283  values.push_back(T2(elem));
284  }
285  }
286 }
287 
288 }
289 
291 {
292  std::vector<MetaValue::value_t> values;
293 
294  switch (entry.type()) {
295  case Internals::IFD::EXIF_FORMAT_BYTE:
296  {
297  convert<uint8_t, uint32_t>(*this, entry, values);
298  break;
299  }
300  case Internals::IFD::EXIF_FORMAT_ASCII:
301  {
302  convert<std::string>(*this, entry, values);
303  break;
304  }
305  case Internals::IFD::EXIF_FORMAT_SHORT:
306  {
307  convert<uint16_t, uint32_t>(*this, entry, values);
308  break;
309  }
310  case Internals::IFD::EXIF_FORMAT_LONG:
311  {
312  convert<uint32_t>(*this, entry, values);
313  break;
314  }
315  case Internals::IFD::EXIF_FORMAT_RATIONAL:
316  {
317  convert<Internals::IFD::ORRational>(*this, entry, values);
318  break;
319  }
320  case Internals::IFD::EXIF_FORMAT_SBYTE:
321  {
322  convert<int8_t, int32_t>(*this, entry, values);
323  break;
324  }
325  case Internals::IFD::EXIF_FORMAT_UNDEFINED:
326  {
327  convert<uint8_t>(*this, entry, values);
328  break;
329  }
330  case Internals::IFD::EXIF_FORMAT_SSHORT:
331  {
332  convert<int16_t, int32_t>(*this, entry, values);
333  break;
334  }
335  case Internals::IFD::EXIF_FORMAT_SLONG:
336  {
337  convert<int32_t>(*this, entry, values);
338  break;
339  }
340  case Internals::IFD::EXIF_FORMAT_SRATIONAL:
341  {
342  convert<Internals::IFD::ORSRational>(*this, entry, values);
343  break;
344  }
345  default:
346  LOGDBG1("unhandled type %d\n", type());
347  return nullptr;
348  }
349  return new MetaValue(values);
350 }
351 
352 }
353 }
An IFD directory.
Definition: ifddir.hpp:51
off_t nextIFD()
Get the offset of the next IFD in absolute.
Definition: ifddir.cpp:116
Ref getMakerNoteIfd(or_rawfile_type file_type)
Get the MakerNote IFD.
Definition: ifddir.cpp:197
virtual bool load()
Load the directory to memory.
Definition: ifddir.cpp:65
MetaValue * makeMetaValue(IfdEntry &e) const
Make a meta value out of the IFD entry.
Definition: ifddir.cpp:290
IfdDirType type() const
Get the type of the IfdDir.
Definition: ifddir.hpp:78
Ref getExifIFD()
Get the Exif IFD.
Definition: ifddir.cpp:179
const char * getTagName(uint32_t tag) const
Return the tag name for tag.
Definition: ifddir.cpp:218
uint32_t getEntryIntegerArrayItemValue(IfdEntry &e, int idx) const
Return the integer value at index. It will coerce the type.
Definition: ifddir.cpp:227
Ref getSubIFD(uint32_t idx=0) const
Get the SubIFD at index idx.
Definition: ifddir.cpp:137
IfdEntry::Ref getEntry(uint16_t id) const
Get the IfdEntry for the tag id.
Definition: ifddir.cpp:96
std::shared_ptr< IfdDir > Ref
Shared ptr of an IfdDir.
Definition: ifddir.hpp:56
Option< std::vector< IfdDir::Ref > > getSubIFDs()
Get all SubIFDs.
Definition: ifddir.cpp:157
Option< uint32_t > getIntegerValue(uint16_t id)
Get an loosely typed integer value from an entry.
Definition: ifddir.cpp:107
An entry in the IfdDir.
Definition: ifdentry.hpp:199
int16_t id() const noexcept
id (i.e. tag) of the entry
Definition: ifdentry.hpp:209
int16_t type() const noexcept
Type of the entry data.
Definition: ifdentry.hpp:217
std::shared_ptr< IfdEntry > Ref
IfdEntry reference (ie shared pointer)
Definition: ifdentry.hpp:202
static Ref createMakerNote(off_t offset, RawContainer &container, or_rawfile_type file_type)
Generic interface for the RAW file container.
Option< uint16_t > readUInt16(const IO::Stream::Ptr &f, EndianType endian) const
Read an uint16 following the m_endian set.
Option< int32_t > readInt32(const IO::Stream::Ptr &f, EndianType endian) const
Read an int32 following the m_endian set.
Option< int16_t > readInt16(const IO::Stream::Ptr &f, EndianType endian) const
Read an int16 following the m_endian set.
Metadata value.
Definition: metavalue.hpp:35
An option type inspired by Rust.
Definition: option.hpp:47
Option< std::vector< T > > getEntryArrayValue(IfdEntry &e) const
Get the entry value as an array.
Definition: ifddir.hpp:257
or_ifd_dir_type
Type of IfdDir.
Definition: consts.h:140
or_rawfile_type
Types of RAW files.
Definition: consts.h:59
@ OR_IFD_EXIF
Exif metadata.
Definition: consts.h:146
@ OR_IFD_SUBIFD
SubIFD.
Definition: consts.h:152
Global namespace for libopenraw.
Definition: arwfile.cpp:29