1 /* -*- Mode: C++; c-basic-offset:4; tab-width:4; indent-tabs-mode:nil; -*- */
2 /*
3  * libopenraw - ciffifd.hpp
4  *
5  * Copyright (C) 2020 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 "endianutils.hpp"
23 #include "ifd.hpp"
24 #include "ciffifd.hpp"
25 #include "ciffcontainer.hpp"
26 #include "crwfile.hpp"
27 
28 namespace OpenRaw {
29 namespace Internals {
30 namespace CIFF {
31 
CiffIfd(CRWFile & ciff,RawContainer & container,IfdDirType _type)32 CiffIfd::CiffIfd(CRWFile& ciff, RawContainer& container, IfdDirType _type)
33     : IfdDir(0, container, _type)
34     , m_file(ciff)
35 {
36 }
37 
entryForString(uint16_t id,const std::string & str) const38 IfdEntry::Ref CiffIfd::entryForString(uint16_t id, const std::string& str) const
39 {
40     // We include the terminating NUL.
41     // std::string::c_str() returns it.
42     auto entry = std::make_shared<IfdEntry>(id, IFD::EXIF_FORMAT_ASCII,
43                                             str.size() + 1, 0, *this, true);
44     entry->setData(reinterpret_cast<const uint8_t*>(str.c_str()), str.size() + 1);
45     return entry;
46 }
47 
CiffMainIfd(CRWFile & ciff,RawContainer & container)48 CiffMainIfd::CiffMainIfd(CRWFile& ciff, RawContainer& container)
49     : CiffIfd(ciff, container, OR_IFD_MAIN)
50 {
51 }
52 
load()53 bool CiffMainIfd::load()
54 {
55     auto img_spec = static_cast<CIFFContainer*>(m_file.getContainer())->getImageSpec();
56     if (img_spec) {
57         auto w = img_spec->imageWidth;
58         auto h = img_spec->imageHeight;
59         auto bpc = img_spec->componentBitDepth;
60 
61         // The data field in ifdentry is in container endian
62         if (endian() == RawContainer::ENDIAN_BIG) {
63             w = htobe16(w);
64             h = htobe16(h);
65             bpc = htobe16(bpc);
66         } else {
67             w = htole16(w);
68             h = htole16(h);
69             bpc = htole16(bpc);
70         }
71         auto entry = std::make_shared<IfdEntry>(EXIF_TAG_IMAGE_WIDTH, EXIF_FORMAT_SHORT,
72                                                 1, w, *this, true);
73         m_entries[EXIF_TAG_IMAGE_WIDTH] = entry;
74         entry = std::make_shared<IfdEntry>(EXIF_TAG_IMAGE_LENGTH, EXIF_FORMAT_SHORT,
75                                            1, h, *this, true);
76         m_entries[EXIF_TAG_IMAGE_LENGTH] = entry;
77         entry = std::make_shared<IfdEntry>(EXIF_TAG_BITS_PER_SAMPLE, EXIF_FORMAT_SHORT,
78                                            1, bpc, *this, true);
79         m_entries[EXIF_TAG_BITS_PER_SAMPLE] = entry;
80     }
81 
82     auto val = m_file.getOrientation();
83     if (val) {
84         auto entry = std::make_shared<IfdEntry>(EXIF_TAG_ORIENTATION, EXIF_FORMAT_SHORT,
85                                                 1, val.value(), *this, true);
86         m_entries[EXIF_TAG_ORIENTATION] = entry;
87     }
88     auto val_str = m_file.getMakeOrModel(EXIF_TAG_MAKE);
89     if (val_str) {
90         auto entry = entryForString(EXIF_TAG_MAKE, val_str.value());
91         m_entries[EXIF_TAG_MAKE] = entry;
92     }
93     val_str = m_file.getMakeOrModel(EXIF_TAG_MODEL);
94     if (val_str) {
95         auto entry = entryForString(EXIF_TAG_MODEL, val_str.value());
96         m_entries[EXIF_TAG_MODEL] = entry;
97     }
98     return true;
99 }
100 
CiffExifIfd(CRWFile & ciff,RawContainer & container)101 CiffExifIfd::CiffExifIfd(CRWFile& ciff, RawContainer& container)
102     : CiffIfd(ciff, container, OR_IFD_EXIF)
103 {
104 }
105 
106 namespace {
107 
108 typedef std::vector<IfdEntry::Ref> (*Converter)(const RecordEntry& e, Heap& heap, CiffIfd& ifd, uint16_t exifTag);
109 
110 struct Ciff2Exif {
111     uint16_t exifTag;
112     or_ifd_dir_type dest;
113     Option<Converter> converter;
114 };
115 
116 // TAG_FOCALLENGTH to Exif
translateFocalLength(const RecordEntry & e,Heap &,CiffIfd & ifd,uint16_t exifTag)117 std::vector<IfdEntry::Ref> translateFocalLength(const RecordEntry& e, Heap&, CiffIfd& ifd, uint16_t exifTag)
118 {
119     LOGASSERT(e.inRecord());
120     uint32_t fl;
121     uint32_t fu = 0;
122     auto data = boost::get<RecordEntry::InRec>(e.data);
123     if (ifd.container().endian() == RawContainer::ENDIAN_LITTLE) {
124         fl = IfdTypeTrait<uint16_t>::EL(data.c_array() + 2, sizeof(uint16_t));
125     } else {
126         fl = IfdTypeTrait<uint16_t>::BE(data.c_array() + 2, sizeof(uint16_t));
127     }
128 
129     CIFFContainer* ciffc = dynamic_cast<CIFFContainer*>(&ifd.container());
130     auto csettings = ciffc->getCameraSettings();
131     if (csettings.size() >= 26) {
132         fu = csettings[25];
133     }
134 
135     uint32_t r[] = { fl, fu };
136     auto ifdentry = std::make_shared<IfdEntry>(exifTag, EXIF_FORMAT_RATIONAL, 1, 0, ifd, true);
137     ifdentry->setData(reinterpret_cast<uint8_t*>(&r), 8);
138     return { ifdentry };
139 }
140 
translateDate(const RecordEntry & e,Heap & heap,CiffIfd & ifd,uint16_t)141 std::vector<IfdEntry::Ref> translateDate(const RecordEntry& e, Heap& heap, CiffIfd& ifd, uint16_t)
142 {
143     struct tm d;
144     uint32_t data[3];
145     e.fetchData(&heap, &data, 12);
146     time_t t = data[0];
147     char date[] = "0000:00:00 00:00:00";
148     auto d2 = gmtime_r(&t, &d);
149     if (d2) {
150         strftime(date, 20, "%Y:%m:%d %H:%M:%S", d2);
151     }
152     return {
153         ifd.entryForString(EXIF_TAG_DATE_TIME_ORIGINAL, date),
154         ifd.entryForString(EXIF_TAG_DATE_TIME_DIGITIZED, date),
155     };
156 }
157 
translateSerial(const RecordEntry & e,Heap &,CiffIfd & ifd,uint16_t exifTag)158 std::vector<IfdEntry::Ref> translateSerial(const RecordEntry& e, Heap& , CiffIfd& ifd, uint16_t exifTag)
159 {
160     uint32_t serial_v;
161     LOGASSERT(e.inRecord());
162     auto data = boost::get<RecordEntry::InRec>(e.data);
163     if (ifd.container().endian() == RawContainer::ENDIAN_LITTLE) {
164         serial_v = IfdTypeTrait<uint32_t>::EL(data.c_array(), sizeof(uint32_t));
165     } else {
166         serial_v = IfdTypeTrait<uint32_t>::BE(data.c_array() + 2, sizeof(uint32_t));
167     }
168     char serial[10];
169     snprintf(serial, 10, "%X", serial_v);
170     return { ifd.entryForString(exifTag, serial) };
171 }
172 
translateString(const RecordEntry & e,Heap & heap,CiffIfd & ifd,uint16_t exifTag)173 std::vector<IfdEntry::Ref> translateString(const RecordEntry& e, Heap& heap, CiffIfd& ifd, uint16_t exifTag)
174 {
175     std::string val_str = e.getString(heap);
176     return { ifd.entryForString(exifTag, val_str) };
177 }
178 
translateMakeModel(const RecordEntry & e,Heap & heap,CiffIfd & ifd,uint16_t exifTag)179 std::vector<IfdEntry::Ref> translateMakeModel(const RecordEntry& e, Heap& heap, CiffIfd& ifd, uint16_t exifTag)
180 {
181     return { ifd.entryForString(exifTag, e.getString(heap)) };
182 }
183 
translateCameraSettings(const RecordEntry & e,Heap & heap,CiffIfd & ifd,uint16_t)184 std::vector<IfdEntry::Ref> translateCameraSettings(const RecordEntry& e, Heap& heap, CiffIfd& ifd, uint16_t /*exifTag*/)
185 {
186     std::vector<IfdEntry::Ref> entries;
187     auto count = e.count();
188     CIFF::CameraSettings settings;
189     auto file = ifd.container().file();
190     file->seek(heap.offset() + e.offset(), SEEK_SET);
191     size_t countRead = ifd.container().readUInt16Array(file, settings, count);
192     if (count != countRead) {
193         LOGERR("Not enough data for camera settings\n");
194     } else {
195         for (uint32_t i = 0; i < count; i++) {
196             switch (i) {
197             case 1: // Macro Mode
198                 if (settings[i] == 1) {
199                     auto ifdentry = std::make_shared<IfdEntry>(
200                         EXIF_TAG_SUBJECT_DISTANCE_RANGE, EXIF_FORMAT_SHORT, 1,
201                         1, ifd, true);
202                     entries.push_back(ifdentry);
203                 }
204                 break;
205             case 4: { // Flash mode
206                 uint16_t flash = 0;
207                 switch (settings[i]) {
208                 case 0:
209                     // off
210                     break;
211                 case 1:
212                     // Auto
213                     flash = 0x19;
214                     break;
215                 case 2:
216                     // on
217                     flash = 0x01;
218                     break;
219                 case 3:
220                 case 5:
221                     // red-eye
222                     flash = 0x41;
223                     break;
224                 }
225                 auto ifdentry = std::make_shared<IfdEntry>(
226                     EXIF_TAG_FLASH, EXIF_FORMAT_SHORT, 1, flash, ifd, true);
227                 entries.push_back(ifdentry);
228                 break;
229             }
230             case 17: { // Metering mode
231                 uint16_t metering = 0;
232                 switch (settings[i]) {
233                 case 0: // Default
234                     break;
235                 case 1: // Spot
236                     metering = 3;
237                     break;
238                 case 2: // Average
239                     metering = 1;
240                     break;
241                 case 3: // Evaluative
242                     metering = 5;
243                     break;
244                 case 4: // Partial
245                     metering = 6;
246                     break;
247                 case 5: // Center-weigthed average
248                     metering = 2;
249                     break;
250                 default:
251                     break;
252                 }
253                 auto ifdentry = std::make_shared<IfdEntry>(
254                     EXIF_TAG_METERING_MODE, EXIF_FORMAT_SHORT, 1, metering, ifd, true);
255                 entries.push_back(ifdentry);
256                 break;
257             }
258             case 20: { // Exposure mode
259                 uint16_t exposure = 0;
260                 switch (settings[i]) {
261                 case 0: // Easy
262                     break;
263                 case 1: // Program AE
264                     exposure = 2;
265                     break;
266                 case 2: // Shutter prio
267                     exposure = 4;
268                     break;
269                 case 3: // Aperture prio
270                     exposure = 3;
271                     break;
272                 case 4: // Manual
273                     exposure = 1;
274                     break;
275                 case 5: // DoF
276                     exposure = 5;
277                     break;
278                 case 6: // M-Dep
279                 case 7: // Bulb
280                 case 8: // Flexible
281                 default:
282                     break;
283                 }
284                 auto ifdentry = std::make_shared<IfdEntry>(
285                     EXIF_TAG_METERING_MODE, EXIF_FORMAT_SHORT, 1, exposure, ifd, true);
286                 entries.push_back(ifdentry);
287                 break;
288             }
289             default:
290                 break;
291             }
292         }
293     }
294 
295     return entries;
296 }
297 
298 static const std::multimap<uint16_t, Ciff2Exif> ciff_exif_map = {
299     { TAG_FOCALLENGTH, { EXIF_TAG_FOCAL_LENGTH, OR_IFD_EXIF, &translateFocalLength } },
300     { TAG_FILEDESCRIPTION, { EXIF_TAG_IMAGE_DESCRIPTION, OR_IFD_MAIN, OptionNone() } },
301     { TAG_ORIGINALFILENAME, { EXIF_TAG_DOCUMENT_NAME, OR_IFD_MAIN, OptionNone() } },
302     { TAG_TARGETDISTANCESETTING, { EXIF_TAG_SUBJECT_DISTANCE, OR_IFD_EXIF, OptionNone() } },
303     { TAG_RAWMAKEMODEL, { EXIF_TAG_MAKE, OR_IFD_MAIN, &translateMakeModel } },
304     { TAG_RAWMAKEMODEL, { EXIF_TAG_MODEL, OR_IFD_MAIN, &translateMakeModel } },
305     { TAG_OWNERNAME, { EXIF_TAG_CAMERA_OWNER_NAME, OR_IFD_EXIF, &translateString } },
306     { TAG_SERIALNUMBER, { EXIF_TAG_BODY_SERIAL_NUMBER, OR_IFD_EXIF, &translateSerial } },
307     { TAG_CAPTUREDTIME, { 0, OR_IFD_EXIF, &translateDate } },
308     { TAG_CAMERASETTINGS, { 0, OR_IFD_EXIF, &translateCameraSettings } }
309 };
310 
translateRecordEntry(const RecordEntry & e,Heap & heap,CiffIfd & ifd)311 std::vector<IfdEntry::Ref> translateRecordEntry(const RecordEntry& e, Heap& heap, CiffIfd& ifd)
312 {
313     std::vector<IfdEntry::Ref> vec;
314     if (e.isHeap()) {
315         const CIFFContainer* ciffc = dynamic_cast<const CIFFContainer*>(&ifd.container());
316         LOGASSERT(ciffc);
317         Heap h = e.heap(heap, ciffc);
318         for (const auto& rec : h.records()) {
319             auto r = translateRecordEntry(rec.second, h, ifd);
320             vec.insert(vec.begin(), r.begin(), r.end());
321         }
322         return vec;
323     }
324     auto iter = ciff_exif_map.find(TAGCODE(e.typeCode));
325     if (iter != ciff_exif_map.end()) {
326         do {
327             if (iter->first != (TAGCODE(e.typeCode))) {
328                 break;
329             }
330             // printf("tag 0x%x mapped to 0x%x in %d\n", iter->first, iter->second.exifTag,
331             //       iter->second.dest);
332             if (iter->second.dest == ifd.type()) {
333                 if (iter->second.converter) {
334                     auto values =
335                         iter->second.converter.value_ref()(e, heap, ifd, iter->second.exifTag);
336                     vec.insert(vec.end(), values.begin(), values.end());
337                 } else {
338                     vec.push_back(
339                         std::make_shared<IfdEntry>(
340                             iter->second.exifTag, e.exifType(), e.count(), 0, ifd));
341                 }
342             }
343             iter++;
344         } while(iter != ciff_exif_map.end());
345     } else {
346         // printf("No mapping for 0x%x\n", e.typeCode & TAGCODE_MASK);
347     }
348 
349     return vec;
350 }
351 
352 }
353 
load()354 bool CiffExifIfd::load()
355 {
356     auto container = static_cast<CIFFContainer*>(m_file.getContainer());
357     HeapRef props = container->getImageProps();
358     if (props) {
359         const RecordEntries& propsRecs = props->records();
360         for (const auto& rec : propsRecs) {
361             auto ifdentries = translateRecordEntry(rec.second, *props, *this);
362             if (!ifdentries.empty()) {
363                 for (auto entry2 : ifdentries) {
364                     m_entries[entry2->id()] = entry2;
365                 }
366             }
367         }
368 
369         HeapRef exifProps = container->getExifInfo();
370         if (exifProps) {
371             for (const auto& rec : exifProps->records()) {
372                 auto ifdentries = translateRecordEntry(rec.second, *exifProps, *this);
373                 if (!ifdentries.empty()) {
374                     for (auto entry2 : ifdentries) {
375                         m_entries[entry2->id()] = entry2;
376                     }
377                 }
378             }
379         }
380     }
381     return true;
382 }
383 
384 }
385 }
386 }
387