diff options
Diffstat (limited to 'src/libexif/exif-loader.c')
-rw-r--r-- | src/libexif/exif-loader.c | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/src/libexif/exif-loader.c b/src/libexif/exif-loader.c new file mode 100644 index 0000000..d6eba7d --- /dev/null +++ b/src/libexif/exif-loader.c @@ -0,0 +1,349 @@ +#include <config.h> + +#include <libexif/exif-loader.h> +#include <libexif/i18n.h> + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#undef JPEG_MARKER_SOI +#define JPEG_MARKER_SOI 0xd8 +#undef JPEG_MARKER_APP0 +#define JPEG_MARKER_APP0 0xe0 +#undef JPEG_MARKER_APP1 +#define JPEG_MARKER_APP1 0xe1 +#undef JPEG_MARKER_APP13 +#define JPEG_MARKER_APP13 0xed +#undef JPEG_MARKER_COM +#define JPEG_MARKER_COM 0xfe + +typedef enum { + EL_READ = 0, + EL_READ_SIZE_BYTE_24, + EL_READ_SIZE_BYTE_16, + EL_READ_SIZE_BYTE_08, + EL_READ_SIZE_BYTE_00, + EL_SKIP_BYTES, + EL_EXIF_FOUND, +} ExifLoaderState; + +typedef enum { + EL_DATA_FORMAT_UNKNOWN, + EL_DATA_FORMAT_EXIF, + EL_DATA_FORMAT_JPEG, + EL_DATA_FORMAT_FUJI_RAW +} ExifLoaderDataFormat; + +struct _ExifLoader { + ExifLoaderState state; + ExifLoaderDataFormat data_format; + + /* Small buffer used for detection of format */ + unsigned char b[12]; + unsigned char b_len; + + unsigned int size; + unsigned char *buf; + unsigned int bytes_read; + + unsigned int ref_count; + + ExifLog *log; + ExifMem *mem; +}; + +static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; + +static void * +exif_loader_alloc (ExifLoader *l, unsigned int i) +{ + void *d; + + if (!l || !i) return NULL; + + d = exif_mem_alloc (l->mem, i); + if (d) return d; + + EXIF_LOG_NO_MEMORY (l->log, "ExifLog", i); + return NULL; +} + +#undef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +void +exif_loader_write_file (ExifLoader *l, const char *path) +{ + FILE *f; + int size; + unsigned char data[1024]; + + if (!l) return; + + f = fopen (path, "rb"); + if (!f) { + exif_log (l->log, EXIF_LOG_CODE_NONE, "ExifLoader", + _("The file '%s' could not be opened."), path); + return; + } + while (1) { + size = fread (data, 1, sizeof (data), f); + if (size <= 0) break; + if (!exif_loader_write (l, data, size)) break; + } + fclose (f); +} + +static unsigned int +exif_loader_copy (ExifLoader *eld, unsigned char *buf, unsigned int len) +{ + if (!eld || (len && !buf) || (eld->bytes_read >= eld->size)) return 0; + + /* If needed, allocate the buffer. */ + if (!eld->buf) eld->buf = exif_loader_alloc (eld, eld->size); + if (!eld->buf) return 0; + + /* Copy memory */ + len = MIN (len, eld->size - eld->bytes_read); + memcpy (eld->buf + eld->bytes_read, buf, len); + eld->bytes_read += len; + + return (eld->bytes_read >= eld->size) ? 0 : 1; +} + +unsigned char +exif_loader_write (ExifLoader *eld, unsigned char *buf, unsigned int len) +{ + unsigned int i; + + if (!eld || (len && !buf)) return 0; + + switch (eld->state) { + case EL_EXIF_FOUND: + return exif_loader_copy (eld, buf, len); + case EL_SKIP_BYTES: + if (eld->size > len) { eld->size -= len; return 1; } + len -= eld->size; + buf += eld->size; + eld->size = 0; + eld->b_len = 0; + switch (eld->data_format) { + case EL_DATA_FORMAT_FUJI_RAW: + eld->state = EL_READ_SIZE_BYTE_24; + break; + default: + eld->state = EL_READ; + break; + } + break; + default: + break; + } + + exif_log (eld->log, EXIF_LOG_CODE_DEBUG, "ExifLoader", + "Scanning %i byte(s) of data...", len); + + /* + * First fill the small buffer. Only continue if the buffer + * is filled. Note that EXIF data contains at least 12 bytes. + */ + i = MIN (len, sizeof (eld->b) - eld->b_len); + if (i) { + memcpy (&eld->b[eld->b_len], buf, i); + eld->b_len += i; + if (eld->b_len < sizeof (eld->b)) return 1; + buf += i; + len -= i; + } + + switch (eld->data_format) { + case EL_DATA_FORMAT_UNKNOWN: + + /* Check the small buffer against known formats. */ + if (!memcmp (eld->b, "FUJIFILM", 8)) { + + /* Skip to byte 84. There is another offset there. */ + eld->data_format = EL_DATA_FORMAT_FUJI_RAW; + eld->size = 84; + eld->state = EL_SKIP_BYTES; + eld->size = 84; + + } else if (!memcmp (eld->b + 2, ExifHeader, sizeof (ExifHeader))) { + + /* Read the size (2 bytes). */ + eld->data_format = EL_DATA_FORMAT_EXIF; + eld->state = EL_READ_SIZE_BYTE_08; + } + default: + break; + } + + for (i = 0; i < sizeof (eld->b); i++) + switch (eld->state) { + case EL_EXIF_FOUND: + if (!exif_loader_copy (eld, eld->b + i, + sizeof (eld->b) - i)) return 0; + return exif_loader_copy (eld, buf, len); + case EL_SKIP_BYTES: + eld->size--; + if (!eld->size) eld->state = EL_READ; + break; + + case EL_READ_SIZE_BYTE_24: + eld->size |= eld->b[i] << 24; + eld->state = EL_READ_SIZE_BYTE_16; + break; + case EL_READ_SIZE_BYTE_16: + eld->size |= eld->b[i] << 16; + eld->state = EL_READ_SIZE_BYTE_08; + break; + case EL_READ_SIZE_BYTE_08: + eld->size |= eld->b[i] << 8; + eld->state = EL_READ_SIZE_BYTE_00; + break; + case EL_READ_SIZE_BYTE_00: + eld->size |= eld->b[i] << 0; + switch (eld->data_format) { + case EL_DATA_FORMAT_JPEG: + eld->state = EL_SKIP_BYTES; + eld->size -= 2; + break; + case EL_DATA_FORMAT_FUJI_RAW: + eld->data_format = EL_DATA_FORMAT_EXIF; + eld->state = EL_SKIP_BYTES; + eld->size -= 86; + break; + case EL_DATA_FORMAT_EXIF: + eld->state = EL_EXIF_FOUND; + break; + default: + break; + } + break; + + default: + switch (eld->b[i]) { + case JPEG_MARKER_APP1: + eld->data_format = EL_DATA_FORMAT_EXIF; + eld->size = 0; + eld->state = EL_READ_SIZE_BYTE_08; + break; + case JPEG_MARKER_APP0: + case JPEG_MARKER_APP13: + case JPEG_MARKER_COM: + eld->data_format = EL_DATA_FORMAT_JPEG; + eld->size = 0; + eld->state = EL_READ_SIZE_BYTE_08; + break; + case 0xff: + case JPEG_MARKER_SOI: + break; + default: + exif_log (eld->log, + EXIF_LOG_CODE_CORRUPT_DATA, + "ExifLoader", _("The data supplied " + "does not seem to contain " + "EXIF data.")); + exif_loader_reset (eld); + return 0; + } + } + + /* + * If we reach this point, the buffer has not been big enough + * to read all data we need. Fill it with new data. + */ + eld->b_len = 0; + return exif_loader_write (eld, buf, len); +} + +ExifLoader * +exif_loader_new (void) +{ + ExifMem *mem = exif_mem_new_default (); + ExifLoader *l = exif_loader_new_mem (mem); + + exif_mem_unref (mem); + + return l; +} + +ExifLoader * +exif_loader_new_mem (ExifMem *mem) +{ + ExifLoader *loader; + + if (!mem) return NULL; + + loader = exif_mem_alloc (mem, sizeof (ExifLoader)); + if (!loader) return NULL; + loader->ref_count = 1; + + loader->mem = mem; + exif_mem_ref (mem); + + return loader; +} + +void +exif_loader_ref (ExifLoader *loader) +{ + if (loader) loader->ref_count++; +} + +static void +exif_loader_free (ExifLoader *loader) +{ + ExifMem *mem; + + if (!loader) return; + + mem = loader->mem; + exif_loader_reset (loader); + exif_mem_free (mem, loader); + exif_mem_unref (mem); +} + +void +exif_loader_unref (ExifLoader *loader) +{ + if (!loader) return; + if (!--loader->ref_count) + exif_loader_free (loader); +} + +void +exif_loader_reset (ExifLoader *loader) +{ + if (!loader) return; + exif_mem_free (loader->mem, loader->buf); loader->buf = NULL; + loader->size = 0; + loader->bytes_read = 0; + loader->state = 0; + loader->b_len = 0; + loader->data_format = EL_DATA_FORMAT_UNKNOWN; +} + +ExifData * +exif_loader_get_data (ExifLoader *loader) +{ + ExifData *ed; + + if (!loader) return NULL; + + ed = exif_data_new_mem (loader->mem); + exif_data_log (ed, loader->log); + exif_data_load_data (ed, loader->buf, loader->bytes_read); + + return ed; +} + +void +exif_loader_log (ExifLoader *loader, ExifLog *log) +{ + if (!loader) return; + exif_log_unref (loader->log); + loader->log = log; + exif_log_ref (log); +} |