| /* exif-loader.c |
| * |
| * Copyright (c) 2002 Lutz Mueller <lutz@users.sourceforge.net> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301 USA. |
| */ |
| |
| #include <config.h> |
| |
| #include <libexif/exif-loader.h> |
| #include <libexif/exif-utils.h> |
| #include <libexif/i18n.h> |
| |
| #include <sys/types.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdio.h> |
| |
| #undef JPEG_MARKER_DHT |
| #define JPEG_MARKER_DHT 0xc4 |
| #undef JPEG_MARKER_SOI |
| #define JPEG_MARKER_SOI 0xd8 |
| #undef JPEG_MARKER_DQT |
| #define JPEG_MARKER_DQT 0xdb |
| #undef JPEG_MARKER_APP0 |
| #define JPEG_MARKER_APP0 0xe0 |
| #undef JPEG_MARKER_APP1 |
| #define JPEG_MARKER_APP1 0xe1 |
| #undef JPEG_MARKER_APP2 |
| #define JPEG_MARKER_APP2 0xe2 |
| #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; |
| |
| /*! \internal */ |
| struct _ExifLoader { |
| ExifLoaderState state; |
| ExifLoaderDataFormat data_format; |
| |
| /*! Small buffer used for detection of format */ |
| unsigned char b[12]; |
| |
| /*! Number of bytes in the small buffer \c b */ |
| unsigned char b_len; |
| |
| unsigned int size; |
| unsigned char *buf; |
| unsigned int bytes_read; |
| |
| unsigned int ref_count; |
| |
| ExifLog *log; |
| ExifMem *mem; |
| }; |
| |
| /*! Magic number for EXIF header */ |
| 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; |
| } |
| |
| 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; |
| |
| case EL_READ: |
| default: |
| break; |
| } |
| |
| if (!len) |
| return 1; |
| 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: |
| switch (eld->size) { |
| case 0: |
| eld->state = EL_READ; |
| i--; // reprocess this byte |
| break; |
| case 1: |
| eld->size = 0; |
| eld->state = EL_READ; |
| break; |
| default: |
| eld->size--; |
| break; |
| } |
| 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; |
| if (eld->size < 2) { |
| // Actually it's malformed... |
| eld->size = 0; |
| } else |
| eld->size -= 2; |
| break; |
| case EL_DATA_FORMAT_FUJI_RAW: |
| eld->data_format = EL_DATA_FORMAT_EXIF; |
| eld->state = EL_SKIP_BYTES; |
| if (eld->size < 86) { |
| // Actually it's malformed... |
| eld->size = 0; |
| } else |
| eld->size -= 86; // and put this in an else |
| break; |
| case EL_DATA_FORMAT_EXIF: |
| eld->state = EL_EXIF_FOUND; |
| break; |
| default: |
| break; |
| } |
| break; |
| |
| default: |
| switch (eld->b[i]) { |
| case JPEG_MARKER_APP1: |
| if (!memcmp (eld->b + i + 3, ExifHeader, MIN((ssize_t)(sizeof(ExifHeader)), MAX(0, ((ssize_t)(sizeof(eld->b))) - ((ssize_t)i) - 3)))) { |
| eld->data_format = EL_DATA_FORMAT_EXIF; |
| } else { |
| eld->data_format = EL_DATA_FORMAT_JPEG; /* Probably JFIF - keep searching for APP1 EXIF*/ |
| } |
| eld->size = 0; |
| eld->state = EL_READ_SIZE_BYTE_08; |
| break; |
| case JPEG_MARKER_DHT: |
| case JPEG_MARKER_DQT: |
| case JPEG_MARKER_APP0: |
| case JPEG_MARKER_APP2: |
| 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_log_unref (loader->log); |
| 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 || (loader->data_format == EL_DATA_FORMAT_UNKNOWN) || |
| !loader->bytes_read) |
| 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_get_buf (ExifLoader *loader, const unsigned char **buf, |
| unsigned int *buf_size) |
| { |
| const unsigned char* b = NULL; |
| unsigned int s = 0; |
| |
| if (!loader || (loader->data_format == EL_DATA_FORMAT_UNKNOWN)) { |
| exif_log (loader->log, EXIF_LOG_CODE_DEBUG, "ExifLoader", |
| "Loader format unknown"); |
| } else { |
| b = loader->buf; |
| s = loader->bytes_read; |
| } |
| if (buf) |
| *buf = b; |
| if (buf_size) |
| *buf_size = s; |
| } |
| |
| void |
| exif_loader_log (ExifLoader *loader, ExifLog *log) |
| { |
| if (!loader) |
| return; |
| exif_log_unref (loader->log); |
| loader->log = log; |
| exif_log_ref (log); |
| } |