libopenraw
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-2017 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
32using namespace Debug;
33
34namespace OpenRaw {
35
36namespace Internals {
37
38bool IfdDir::isPrimary() const
39{
40 auto result = getValue<uint32_t>(IFD::EXIF_TAG_NEW_SUBFILE_TYPE);
41 return result && (result.value() == 0);
42}
43
44bool IfdDir::isThumbnail() const
45{
46 auto result = getValue<uint32_t>(IFD::EXIF_TAG_NEW_SUBFILE_TYPE);
47 return result && (result.value() == 1);
48}
49
50IfdDir::IfdDir(off_t _offset, IfdFileContainer &_container)
51 : m_offset(_offset), m_container(_container), m_entries()
52{
53}
54
55IfdDir::~IfdDir()
56{
57}
58
60{
61 LOGDBG1("IfdDir::load() m_offset =%lld\n", (long long int)m_offset);
62
63 auto file = m_container.file();
64 m_entries.clear();
65 file->seek(m_offset, SEEK_SET);
66
67 int16_t numEntries = m_container.readInt16(file).value_or(0);
68 LOGDBG1("num entries %d\n", numEntries);
69
70 for (int16_t i = 0; i < numEntries; i++) {
71 uint32_t data;
72 auto id = m_container.readUInt16(file);
73 auto type = m_container.readInt16(file);
74 auto count = m_container.readInt32(file);
75 size_t sz = file->read(&data, 4);
76 if (id.empty() || type.empty() || count.empty() || sz != 4) {
77 LOGERR("Failed to read entry %d\n", i);
78 return false;
79 }
80 uint16_t n_id = id.value();
81 IfdEntry::Ref entry =
82 std::make_shared<IfdEntry>(n_id, type.value(),
83 count.value(), data, m_container);
84 m_entries[n_id] = entry;
85 }
86
87 return true;
88}
89
90IfdEntry::Ref IfdDir::getEntry(uint16_t id) const
91{
92 std::map<uint16_t, IfdEntry::Ref>::const_iterator iter;
93 iter = m_entries.find(id);
94 if (iter != m_entries.end()) {
95 return iter->second;
96 }
97 return IfdEntry::Ref();
98}
99
102{
103 IfdEntry::Ref e = getEntry(id);
104 if (e != nullptr) {
105 return Option<uint32_t>(e->getIntegerArrayItem(0));
106 }
107 return Option<uint32_t>();
108}
109
111{
112 int16_t numEntries = 0;
113 auto file = m_container.file();
114
115 if (m_entries.size() == 0) {
116 file->seek(m_offset, SEEK_SET);
117 numEntries = m_container.readInt16(file).value_or(0);
118 LOGDBG1("numEntries =%d shifting %d bytes\n", numEntries, (numEntries * 12) + 2);
119 } else {
120 numEntries = m_entries.size();
121 }
122
123 file->seek(m_offset + (numEntries * 12) + 2, SEEK_SET);
124 // XXX how about we check the error. Even though 0 is not valid.
125 return m_container.readInt32(file).value_or(0);
126}
127
131IfdDir::Ref IfdDir::getSubIFD(uint32_t idx) const
132{
133 IfdEntry::Ref e = getEntry(IFD::EXIF_TAG_SUB_IFDS);
134
135 if (e != nullptr) {
136 auto result = e->getArray<uint32_t>();
137 if (result) {
138 std::vector<uint32_t> offsets = result.value();
139 if (idx >= offsets.size()) {
140 Ref ref = std::make_shared<IfdDir>(offsets[idx], m_container);
141 ref->load();
142 return ref;
143 }
144 } else {
145 LOGERR("Can't get SubIFD offsets\n");
146 }
147 }
148 return Ref();
149}
150
152{
153 std::vector<IfdDir::Ref> ifds;
154 IfdEntry::Ref e = getEntry(IFD::EXIF_TAG_SUB_IFDS);
155 if (e != nullptr) {
156 auto result = e->getArray<uint32_t>();
157 if (result) {
158 std::vector<uint32_t> offsets = result.value();
159 for (auto offset_ : offsets) {
160 Ref ifd = std::make_shared<IfdDir>(offset_, m_container);
161 ifd->load();
162 ifds.push_back(ifd);
163 }
164 return Option<std::vector<IfdDir::Ref>>(std::move(ifds));
165 }
166 }
168}
169
174{
175 auto result = getValue<uint32_t>(IFD::EXIF_TAG_EXIF_IFD_POINTER);
176 if (result.empty()) {
177 LOGDBG1("Exif IFD offset not found.\n");
178 return Ref();
179 }
180
181 uint32_t val_offset = result.value();
182 LOGDBG1("Exif IFD offset (uncorrected) = %u\n", val_offset);
183 val_offset += m_container.exifOffsetCorrection();
184 LOGDBG1("Exif IFD offset = %u\n", val_offset);
185
186 Ref ref = std::make_shared<IfdDir>(val_offset, m_container);
187 ref->load();
188 return ref;
189}
190
192{
193 uint32_t val_offset = 0;
194 IfdEntry::Ref e = getEntry(IFD::EXIF_TAG_MAKER_NOTE);
195 if (!e) {
196 LOGDBG1("MakerNote IFD offset not found.\n");
197 return MakerNoteDir::Ref();
198 }
199 val_offset = e->offset();
200 LOGDBG1("MakerNote IFD offset (uncorrected) = %u\n", val_offset);
201 val_offset += m_container.exifOffsetCorrection();
202 LOGDBG1("MakerNote IFD offset = %u\n", val_offset);
203
204 auto ref = MakerNoteDir::createMakerNote(val_offset, m_container);
205 if (ref) {
206 ref->load();
207 }
208
209 return ref;
210}
211
212}
213}
Option< T > getValue(uint16_t id) const
Definition ifddir.hpp:70
Ref getSubIFD(uint32_t idx=0) const
Definition ifddir.cpp:131
Option< std::vector< IfdDir::Ref > > getSubIFDs()
Definition ifddir.cpp:151
Option< uint32_t > getIntegerValue(uint16_t id)
Definition ifddir.cpp:101
std::shared_ptr< IfdEntry > Ref
Definition ifdentry.hpp:165
static Ref createMakerNote(off_t offset, IfdFileContainer &container)
CIFF is the container for CRW files. It is an attempt from Canon to make this a standard....
Definition arwfile.cpp:30