libopenraw  0.3.7
ifdfilecontainer.cpp
1 /*
2  * libopenraw - ifdfilecontainer.cpp
3  *
4  * Copyright (C) 2006-2020 Hubert Figuière
5  *
6  * This library is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation, either version 3 of
9  * the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <fcntl.h>
22 #include <sys/types.h>
23 #include <memory>
24 
25 #include <vector>
26 
27 #include <libopenraw/debug.h>
28 
29 #include "trace.hpp"
30 #include "ifdfilecontainer.hpp"
31 
32 using namespace Debug;
33 
34 namespace OpenRaw {
35 
36 namespace Internals {
37 
38 IfdFileContainer::IfdFileContainer(const IO::Stream::Ptr &_file, off_t _offset)
39  : RawContainer(_file, _offset)
40  , m_error(0)
41  , m_exif_offset_correction(0)
42  , m_current_dir()
43  , m_dirs()
44 {
45 }
46 
48 {
49  m_dirs.clear();
50 }
51 
53  int len)
54 {
55  if (len < 4) {
56  // we need at least 4 bytes to check
57  return ENDIAN_NULL;
58  }
59  if ((p[0] == 0x49) && (p[1] == 0x49) && (p[2] == 0x2a) && (p[3] == 0x00)) {
60  return ENDIAN_LITTLE;
61  } else if ((p[0] == 0x4d) && (p[1] == 0x4d) && (p[2] == 0x00) &&
62  (p[3] == 0x2a)) {
63  return ENDIAN_BIG;
64  }
65  return ENDIAN_NULL;
66 }
67 
69 {
70  if (m_dirs.size() == 0) {
71  // FIXME check result
72  bool ret = _locateDirs();
73  if (!ret) {
74  return -1;
75  }
76  }
77  return m_dirs.size();
78 }
79 
80 std::vector<IfdDir::Ref> &IfdFileContainer::directories()
81 {
82  if (m_dirs.size() == 0) {
84  }
85  return m_dirs;
86 }
87 
89 {
90  if (dir < 0) {
91  // FIXME set error
92  return IfdDir::Ref();
93  }
94  // FIXME handle negative values
95  int n = countDirectories();
96  if (n <= 0) {
97  // FIXME set error
98  return IfdDir::Ref();
99  }
100  // dir is signed here because we can pass negative
101  // value for specific Exif IFDs.
102  if (dir > (int)m_dirs.size()) {
103  // FIXME set error
104  return IfdDir::Ref();
105  }
106  m_current_dir = m_dirs[dir];
107  m_current_dir->load();
108  return m_current_dir;
109 }
110 
112 {
113  // TODO move to IFDirectory
114  LOGDBG1("getDirectoryDataSize()\n");
115  off_t dir_offset = m_current_dir->offset();
116  // FIXME check error
117  LOGDBG1("offset = %lld m_numTags = %d\n", (long long int)dir_offset,
118  m_current_dir->numTags());
119  off_t begin = dir_offset + 2 + (m_current_dir->numTags() * 12);
120 
121  LOGDBG1("begin = %lld\n", (long long int)begin);
122 
123  m_file->seek(begin, SEEK_SET);
124  begin += 2;
125 
126  int32_t nextIFD = readInt32(m_file, m_endian).value_or(0);
127  LOGDBG1("nextIFD = %d\n", nextIFD);
128  if (nextIFD == 0) {
129  // FIXME not good
130  // XXX we should check the Option<> from readInt32().
131  }
132  return nextIFD - begin;
133 }
134 
136 {
137  return true;
138 }
139 
140 bool IfdFileContainer::_locateDirs(void)
141 {
142  if (!locateDirsPreHook()) {
143  return false;
144  }
145  LOGDBG1("_locateDirs()\n");
146  if (m_endian == ENDIAN_NULL) {
147  char buf[4];
148  m_file->seek(m_offset, SEEK_SET);
149  m_file->read(buf, 4);
150  m_endian = isMagicHeader(buf, 4);
151  if (m_endian == ENDIAN_NULL) {
152  // FIXME set error code
153  return false;
154  }
155  }
156  m_file->seek(m_offset + 4, SEEK_SET);
157  int32_t dir_offset = readInt32(m_file, m_endian).value_or(0);
158  m_dirs.clear();
159  do {
160  if (dir_offset != 0) {
161  LOGDBG1("push offset =0x%x\n", dir_offset);
162 
163  // we assume the offset is relative to the begining of
164  // the IFD.
165  IfdDir::Ref dir(
166  std::make_shared<IfdDir>(m_offset + dir_offset, *this, OR_IFD_OTHER));
167  m_dirs.push_back(dir);
168 
169  dir_offset = dir->nextIFD();
170  }
171  } while (dir_offset != 0);
172 
173  LOGDBG1("# dir found = %lu\n", (LSIZE)m_dirs.size());
174  return (m_dirs.size() != 0);
175 }
176 }
177 }
std::shared_ptr< Stream > Ptr
Definition: stream.hpp:47
std::shared_ptr< IfdDir > Ref
Shared ptr of an IfdDir.
Definition: ifddir.hpp:56
std::vector< IfdDir::Ref > & directories()
Get the directories, loading them if necessary.
virtual EndianType isMagicHeader(const char *p, int len)
Check the IFD magic header.
IfdDir::Ref setDirectory(int dir)
Set the current directory.
size_t getDirectoryDataSize()
get the extra data size chunk associated to the current image directory
int countDirectories(void)
Count the number of image file directories, not including EXIF, GPS and INTEROP.
virtual bool locateDirsPreHook()
hook to be called at the start of _locateDirs()
Generic interface for the RAW file container.
IO::Stream::Ptr m_file
The file handle.
EndianType
Define the endian of the container.
Option< int32_t > readInt32(const IO::Stream::Ptr &f, EndianType endian) const
Read an int32 following the m_endian set.
off_t m_offset
The offset from the beginning of the file.
@ OR_IFD_OTHER
Generic.
Definition: consts.h:142
Global namespace for libopenraw.
Definition: arwfile.cpp:29