aboutsummaryrefslogtreecommitdiff
path: root/tools/info.h
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2023-05-23 02:09:15 +0200
committerPřemysl Eric Janouch <p@janouch.name>2023-05-26 15:32:34 +0200
commitbb4b895cb5938712bd09fbd2b5f49bea811d7551 (patch)
tree586902190b9fe52b1c6cb14309b852267fc9ee1e /tools/info.h
parent0f1c61ae3325dda14be8f98ee7047ac5eda02108 (diff)
downloadfiv-bb4b895cb5938712bd09fbd2b5f49bea811d7551.tar.gz
fiv-bb4b895cb5938712bd09fbd2b5f49bea811d7551.tar.xz
fiv-bb4b895cb5938712bd09fbd2b5f49bea811d7551.zip
Extract some full-size raw previews without LibRaw
Not all image/x-nikon-nef will work like this, so don't claim their MIME type.
Diffstat (limited to 'tools/info.h')
-rw-r--r--tools/info.h363
1 files changed, 23 insertions, 340 deletions
diff --git a/tools/info.h b/tools/info.h
index 28cfb36..8dcd3d2 100644
--- a/tools/info.h
+++ b/tools/info.h
@@ -21,348 +21,10 @@
#include <stdlib.h>
#include <string.h>
-// --- Utilities ---------------------------------------------------------------
-
-static char *
-binhex(const uint8_t *data, size_t len)
-{
- static const char *alphabet = "0123456789abcdef";
- char *buf = calloc(1, len * 2 + 1), *p = buf;
- for (size_t i = 0; i < len; i++) {
- *p++ = alphabet[data[i] >> 4];
- *p++ = alphabet[data[i] & 0xF];
- }
- return buf;
-}
-
-static uint64_t
-u64be(const uint8_t *p)
-{
- return (uint64_t) p[0] << 56 | (uint64_t) p[1] << 48 |
- (uint64_t) p[2] << 40 | (uint64_t) p[3] << 32 |
- (uint64_t) p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7];
-}
-
-static uint32_t
-u32be(const uint8_t *p)
-{
- return (uint32_t) p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
-}
-
-static uint16_t
-u16be(const uint8_t *p)
-{
- return (uint16_t) p[0] << 8 | p[1];
-}
-
-static uint64_t
-u64le(const uint8_t *p)
-{
- return (uint64_t) p[7] << 56 | (uint64_t) p[6] << 48 |
- (uint64_t) p[5] << 40 | (uint64_t) p[4] << 32 |
- (uint64_t) p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
-}
-
-static uint32_t
-u32le(const uint8_t *p)
-{
- return (uint32_t) p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
-}
-
-static uint16_t
-u16le(const uint8_t *p)
-{
- return (uint16_t) p[1] << 8 | p[0];
-}
-
-// --- TIFF --------------------------------------------------------------------
-// libtiff is a mess, and the format is not particularly complicated.
-// Exiv2 is senselessly copylefted, and cannot do much.
-// libexif is only marginally better.
-// ExifTool is too user-oriented.
-
-static struct un {
- uint64_t (*u64) (const uint8_t *);
- uint32_t (*u32) (const uint8_t *);
- uint16_t (*u16) (const uint8_t *);
-} unbe = {u64be, u32be, u16be}, unle = {u64le, u32le, u16le};
-
-struct tiffer {
- struct un *un;
- const uint8_t *begin, *p, *end;
- uint16_t remaining_fields;
-};
-
-static bool
-tiffer_u32(struct tiffer *self, uint32_t *u)
-{
- if (self->p < self->begin || self->p + 4 > self->end)
- return false;
-
- *u = self->un->u32(self->p);
- self->p += 4;
- return true;
-}
-
-static bool
-tiffer_u16(struct tiffer *self, uint16_t *u)
-{
- if (self->p < self->begin || self->p + 2 > self->end)
- return false;
-
- *u = self->un->u16(self->p);
- self->p += 2;
- return true;
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-static bool
-tiffer_init(struct tiffer *self, const uint8_t *tiff, size_t len)
-{
- self->un = NULL;
- self->begin = self->p = tiff;
- self->end = tiff + len;
- self->remaining_fields = 0;
-
- const uint8_t
- le[4] = {'I', 'I', 42, 0},
- be[4] = {'M', 'M', 0, 42};
-
- if (tiff + 8 > self->end)
- return false;
- else if (!memcmp(tiff, le, sizeof le))
- self->un = &unle;
- else if (!memcmp(tiff, be, sizeof be))
- self->un = &unbe;
- else
- return false;
-
- self->p = tiff + 4;
- // The first IFD needs to be read by caller explicitly,
- // even though it's required to be present by TIFF 6.0.
- return true;
-}
-
-/// Read the next IFD in a sequence.
-static bool
-tiffer_next_ifd(struct tiffer *self)
-{
- // All fields from any previous IFD need to be read first.
- if (self->remaining_fields)
- return false;
-
- uint32_t ifd_offset = 0;
- if (!tiffer_u32(self, &ifd_offset))
- return false;
-
- // There is nothing more to read, this chain has terminated.
- if (!ifd_offset)
- return false;
-
- // Note that TIFF 6.0 requires there to be at least one entry,
- // but there is no need for us to check it.
- self->p = self->begin + ifd_offset;
- return tiffer_u16(self, &self->remaining_fields);
-}
-
-/// Initialize a derived TIFF reader for a subIFD at the given location.
-static bool
-tiffer_subifd(struct tiffer *self, uint32_t offset, struct tiffer *subreader)
-{
- *subreader = *self;
- subreader->p = subreader->begin + offset;
- return tiffer_u16(subreader, &subreader->remaining_fields);
-}
-
-enum tiffer_type {
- BYTE = 1, ASCII, SHORT, LONG, RATIONAL,
- SBYTE, UNDEFINED, SSHORT, SLONG, SRATIONAL, FLOAT, DOUBLE,
- IFD // This last type from TIFF Technical Note 1 isn't really used much.
-};
-
-static size_t
-tiffer_value_size(enum tiffer_type type)
-{
- switch (type) {
- case BYTE:
- case SBYTE:
- case ASCII:
- case UNDEFINED:
- return 1;
- case SHORT:
- case SSHORT:
- return 2;
- case LONG:
- case SLONG:
- case FLOAT:
- case IFD:
- return 4;
- case RATIONAL:
- case SRATIONAL:
- case DOUBLE:
- return 8;
- default:
- return 0;
- }
-}
-
-/// A lean iterator for values within entries.
-struct tiffer_entry {
- uint16_t tag;
- enum tiffer_type type;
- // For {S,}BYTE, ASCII, UNDEFINED, use these fields directly.
- const uint8_t *p;
- uint32_t remaining_count;
-};
-
-static bool
-tiffer_next_value(struct tiffer_entry *entry)
-{
- if (!entry->remaining_count)
- return false;
-
- entry->p += tiffer_value_size(entry->type);
- entry->remaining_count--;
- return true;
-}
-
-static bool
-tiffer_integer(
- const struct tiffer *self, const struct tiffer_entry *entry, int64_t *out)
-{
- if (!entry->remaining_count)
- return false;
-
- // Somewhat excessively lenient, intended for display.
- // TIFF 6.0 only directly suggests that a reader is should accept
- // any of BYTE/SHORT/LONG for unsigned integers.
- switch (entry->type) {
- case BYTE:
- case ASCII:
- case UNDEFINED:
- *out = *entry->p;
- return true;
- case SBYTE:
- *out = (int8_t) *entry->p;
- return true;
- case SHORT:
- *out = self->un->u16(entry->p);
- return true;
- case SSHORT:
- *out = (int16_t) self->un->u16(entry->p);
- return true;
- case LONG:
- case IFD:
- *out = self->un->u32(entry->p);
- return true;
- case SLONG:
- *out = (int32_t) self->un->u32(entry->p);
- return true;
- default:
- return false;
- }
-}
-
-static bool
-tiffer_rational(const struct tiffer *self, const struct tiffer_entry *entry,
- int64_t *numerator, int64_t *denominator)
-{
- if (!entry->remaining_count)
- return false;
-
- // Somewhat excessively lenient, intended for display.
- switch (entry->type) {
- case RATIONAL:
- *numerator = self->un->u32(entry->p);
- *denominator = self->un->u32(entry->p + 4);
- return true;
- case SRATIONAL:
- *numerator = (int32_t) self->un->u32(entry->p);
- *denominator = (int32_t) self->un->u32(entry->p + 4);
- return true;
- default:
- if (tiffer_integer(self, entry, numerator)) {
- *denominator = 1;
- return true;
- }
- return false;
- }
-}
-
-static bool
-tiffer_real(
- const struct tiffer *self, const struct tiffer_entry *entry, double *out)
-{
- if (!entry->remaining_count)
- return false;
-
- // Somewhat excessively lenient, intended for display.
- // Assuming the host architecture uses IEEE 754.
- switch (entry->type) {
- int64_t numerator, denominator;
- case FLOAT:
- *out = *(float *) entry->p;
- return true;
- case DOUBLE:
- *out = *(double *) entry->p;
- return true;
- default:
- if (tiffer_rational(self, entry, &numerator, &denominator)) {
- *out = (double) numerator / denominator;
- return true;
- }
- return false;
- }
-}
-
-static bool
-tiffer_next_entry(struct tiffer *self, struct tiffer_entry *entry)
-{
- if (!self->remaining_fields)
- return false;
-
- uint16_t type = entry->type = 0xFFFF;
- if (!tiffer_u16(self, &entry->tag) || !tiffer_u16(self, &type) ||
- !tiffer_u32(self, &entry->remaining_count))
- return false;
-
- // Short values may and will be inlined, rather than pointed to.
- size_t values_size = tiffer_value_size(type) * entry->remaining_count;
- uint32_t offset = 0;
- if (values_size <= sizeof offset) {
- entry->p = self->p;
- self->p += sizeof offset;
- } else if (tiffer_u32(self, &offset)) {
- entry->p = self->begin + offset;
- } else {
- return false;
- }
-
- // All entries are pre-checked not to overflow.
- if (entry->p + values_size > self->end)
- return false;
-
- // Setting it at the end may provide an indication while debugging.
- entry->type = type;
- self->remaining_fields--;
- return true;
-}
-
-// --- TIFF/Exif tags ----------------------------------------------------------
-
-struct tiff_value {
- const char *name;
- uint16_t value;
-};
-
-struct tiff_entry {
- const char *name;
- uint16_t tag;
- struct tiff_value *values;
-};
+// --- TIFF/Exif ---------------------------------------------------------------
#include "tiff-tables.h"
+#include "tiffer.h"
// TODO(p): Consider if these can't be inlined into `tiff_entries`.
static struct {
@@ -376,6 +38,27 @@ static struct {
{}
};
+// --- Utilities ---------------------------------------------------------------
+
+#define u64be tiffer_u64be
+#define u32be tiffer_u32be
+#define u16be tiffer_u16be
+#define u64le tiffer_u64le
+#define u32le tiffer_u32le
+#define u16le tiffer_u16le
+
+static char *
+binhex(const uint8_t *data, size_t len)
+{
+ static const char *alphabet = "0123456789abcdef";
+ char *buf = calloc(1, len * 2 + 1), *p = buf;
+ for (size_t i = 0; i < len; i++) {
+ *p++ = alphabet[data[i] >> 4];
+ *p++ = alphabet[data[i] & 0xF];
+ }
+ return buf;
+}
+
// --- Analysis ----------------------------------------------------------------
static jv