libopenraw
ifdfilecontainer.cpp
1 /*
2  * libopenraw - ifdfilecontainer.cpp
3  *
4  * Copyright (C) 2006-2017 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 
88 IfdDir::Ref IfdFileContainer::setDirectory(int dir)
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).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).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));
167  m_dirs.push_back(dir);
168 
169  dir_offset = dir->nextIFD();
170  }
171  } while (dir_offset != 0);
172 
173  LOGDBG1("# dir found = %ld\n", m_dirs.size());
174  return (m_dirs.size() != 0);
175 }
176 }
177 }
OpenRaw::Internals::IfdFileContainer::countDirectories
int countDirectories(void)
Definition: ifdfilecontainer.cpp:68
OpenRaw::Internals::RawContainer::m_file
IO::Stream::Ptr m_file
Definition: rawcontainer.hpp:100
OpenRaw::Internals::RawContainer
Definition: rawcontainer.hpp:41
OpenRaw::Internals::RawContainer::readInt32
Option< int32_t > readInt32(const IO::Stream::Ptr &f)
Definition: rawcontainer.cpp:138
OpenRaw::Internals::IfdFileContainer::locateDirsPreHook
virtual bool locateDirsPreHook()
Definition: ifdfilecontainer.cpp:135
OpenRaw::Internals::RawContainer::ENDIAN_LITTLE
@ ENDIAN_LITTLE
Definition: rawcontainer.hpp:47
OpenRaw
CIFF is the container for CRW files. It is an attempt from Canon to make this a standard....
Definition: arwfile.cpp:30
OpenRaw::Internals::IfdFileContainer::getDirectoryDataSize
size_t getDirectoryDataSize()
Definition: ifdfilecontainer.cpp:111
OpenRaw::Internals::IfdFileContainer::directories
std::vector< IfdDir::Ref > & directories()
Definition: ifdfilecontainer.cpp:80
OpenRaw::Internals::RawContainer::ENDIAN_BIG
@ ENDIAN_BIG
Definition: rawcontainer.hpp:46
OpenRaw::Internals::IfdFileContainer::setDirectory
IfdDir::Ref setDirectory(int dir)
Definition: ifdfilecontainer.cpp:88
OpenRaw::Internals::RawContainer::m_offset
off_t m_offset
Definition: rawcontainer.hpp:102
OpenRaw::Internals::IfdFileContainer::isMagicHeader
virtual EndianType isMagicHeader(const char *p, int len)
Definition: ifdfilecontainer.cpp:52
OpenRaw::Internals::IfdFileContainer::~IfdFileContainer
virtual ~IfdFileContainer()
Definition: ifdfilecontainer.cpp:47
OpenRaw::Internals::RawContainer::EndianType
EndianType
Definition: rawcontainer.hpp:44