diff --git a/CMakeLists.txt b/CMakeLists.txt index 85cf80e..179909c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ # cmake_minimum_required (VERSION 3.8) -project ("jpeg-dissect") +project ("jpeg-dissect" C) # Include sub-projects. add_subdirectory ("src") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 14554ea..dc2b8bb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,4 +3,6 @@ add_executable (jpeg-dissect "main.c" "loader.c" - "util.h") +) + +set_property(TARGET jpeg-dissect PROPERTY C_STANDARD 11) diff --git a/src/loader.c b/src/loader.c index 6800911..740786c 100644 --- a/src/loader.c +++ b/src/loader.c @@ -1,39 +1,61 @@ #include "loader.h" -#include -#include +#include +#include +#include -#include "util.h" +#define memzero(buffer, size) memset(buffer, 0, size) -static int load_segment(FILE* fp); +static int load_segment(JPEG* jpeg, FILE* fp); -static int load_rst_segment(FILE* fp, uint8_t n); -static int load_app_segment(FILE* fp, uint8_t n); +static int load_rst_segment(JPEG* jpeg, FILE* fp, uint8_t n); +static int load_app_segment(JPEG* jpeg, FILE* fp, uint8_t n); -static int load_app0_segment(FILE* fp); +static int load_app0_segment(JPEG* jpeg, FILE* fp); -int load_jpeg(const char* filename) +JPEG* load_jpeg(const char* filename) { FILE* fp = fopen(filename, "r"); if (fp == NULL) { - return 1; + return NULL; } + JPEG* jpeg = (JPEG*)malloc(sizeof(jpeg)); + memzero(jpeg, sizeof(JPEG)); + while (!feof(fp)) { - if (load_segment(fp) != 0) + if (load_segment(jpeg, fp) != 0) { fprintf(stderr, "Segment loading failed\n"); - return 1; + free_jpeg(jpeg); + + return NULL; } } fclose(fp); - return 0; + return jpeg; } -int load_segment(FILE* fp) +void free_jpeg(JPEG* jpeg) +{ + if (jpeg == NULL) + return; + + if (jpeg->app0) + { + if (jpeg->app0->thumbnail_data) + { + free(jpeg->app0->thumbnail_data); + } + + free(jpeg->app0); + } +} + +int load_segment(JPEG* jpeg, FILE* fp) { uint8_t segment_marker[2]; size_t segment_marker_size = sizeof(segment_marker); @@ -53,14 +75,14 @@ int load_segment(FILE* fp) // Handle special APPn/RSTn markers if (segment_marker[1] >= 0xD0 && segment_marker[1] <= 0xD7) { - if (load_rst_segment(fp, segment_marker[1] | 0x0F) != 0) + if (load_rst_segment(jpeg, fp, segment_marker[1] | 0x0F) != 0) { return 1; } } else if ((segment_marker[1] & 0xF0) == 0xE0) { - if (load_app_segment(fp, segment_marker[1] & 0x0F) != 0) + if (load_app_segment(jpeg, fp, segment_marker[1] & 0x0F) != 0) { return 1; } @@ -82,19 +104,19 @@ int load_segment(FILE* fp) return 0; } -int load_rst_segment(FILE* fp, uint8_t n) +int load_rst_segment(JPEG* jpeg, FILE* fp, uint8_t n) { DEBUG_LOG("RST%d marker encountered", n); return 1; } -int load_app_segment(FILE* fp, uint8_t n) +int load_app_segment(JPEG* jpeg, FILE* fp, uint8_t n) { DEBUG_LOG("APP%d marker encountered", n); switch (n) { - case 0: return load_app0_segment(); + case 0: return load_app0_segment(jpeg, fp); default: fprintf(stderr, "Unknown APP segment ID %d\n", n); @@ -104,7 +126,68 @@ int load_app_segment(FILE* fp, uint8_t n) return 0; } -int load_app0_segment(FILE* fp) +int load_app0_segment(JPEG* jpeg, FILE* fp) { + if (jpeg->app0 != NULL) + { + fprintf(stderr, "Found more than one APP0 marker\n"); + return 1; + } + assert(fp); + + jpeg->app0 = (JFIFAPP0Segment*)malloc(sizeof(JFIFAPP0Segment)); + memzero(jpeg->app0, sizeof(JFIFAPP0Segment)); + + if (jpeg->app0 == NULL) + { + fprintf(stderr, "Failed to allocate memory for APP0 header\n"); + return 1; + } + + // Extract header without thumbnail data + size_t jfif_header_size = sizeof(JFIFAPP0Segment) - sizeof(uint8_t*); + if (fread(jpeg->app0, sizeof(uint8_t), jfif_header_size, fp) != jfif_header_size) + { + fprintf(stderr, "Incomplete APP0 header\n"); + return 1; + } + + jpeg->app0->length = bswap_16(jpeg->app0->length); + jpeg->app0->density_x = bswap_16(jpeg->app0->density_x); + jpeg->app0->density_y = bswap_16(jpeg->app0->density_y); + + size_t thumbnail_data_size = jpeg->app0->thumbnail_x * jpeg->app0->thumbnail_y; + if (thumbnail_data_size > 0) + { + jpeg->app0->thumbnail_data = (uint8_t*)malloc(thumbnail_data_size * sizeof(uint8_t)); + if (jpeg->app0->thumbnail_data == NULL) + { + fprintf(stderr, "Failed to allocate memory for thumbnail data\n"); + return 1; + } + + if (fread(jpeg->app0->thumbnail_data, sizeof(uint8_t), thumbnail_data_size, fp) != thumbnail_data_size) + { + fprintf(stderr, "Incomplete thumbnail data\n"); + return 1; + } + } + + DEBUG_LOG( + "JFIFAPP0Segment\n" + "length = %u\n" + "identifier = %s\n" + "version = %u.%02u\n" + "density units = %u\n" + "density x, y = %u, %u\n" + "thumbnail x, y = %u, %u", + jpeg->app0->length, + jpeg->app0->identifier, + jpeg->app0->version.major, jpeg->app0->version.minor, + jpeg->app0->density_units, + jpeg->app0->density_x, jpeg->app0->density_y, + jpeg->app0->thumbnail_x, jpeg->app0->thumbnail_y + ); + return 0; } \ No newline at end of file diff --git a/src/loader.h b/src/loader.h index 856938d..739a1ef 100644 --- a/src/loader.h +++ b/src/loader.h @@ -1,6 +1,37 @@ #ifndef _LODAER_H #define _LOADER_H -int load_jpeg(const char* filename); +#include +#include "util.h" + +PACK ( +typedef struct JFIFAPP0Segment +{ + uint16_t length; + const char identifier[5]; + + struct { + uint8_t major; + uint8_t minor; + } version; + + uint8_t density_units; + uint16_t density_x; + uint16_t density_y; + + uint8_t thumbnail_x; + uint8_t thumbnail_y; + + uint8_t* thumbnail_data; +} JFIFAPP0Segment; +) + +typedef struct JPEG +{ + JFIFAPP0Segment* app0; +} JPEG; + +JPEG* load_jpeg(const char* filename); +void free_jpeg(JPEG* jpeg); #endif // _LOADER_H \ No newline at end of file diff --git a/src/util.h b/src/util.h index 64db194..2ac1a6b 100644 --- a/src/util.h +++ b/src/util.h @@ -4,9 +4,27 @@ #include #ifdef NDEBUG -#define DEBUG_LOG + #define DEBUG_LOG #else -#define DEBUG_LOG(msg, ...) printf("[DEBUG] " ##msg "\n", __VA_ARGS__); + #define DEBUG_LOG(...) printf("[DEBUG] "); printf(__VA_ARGS__); printf("\n") #endif +#if defined(__GNUC__) + #define PACK( data ) data __attribute((__packed__)) + + #include + #define bswap_16 bswap_16 + #define bswap_32 bswap_32 + #define bswap_64 bswap_64 +#elif defined(_MSC_VER) + #define PACK( data ) __pragma(pack(push, 1)) data __pragma(pack(pop)) + + #include + #define bswap_16 _byteswap_ushort + #define bswap_32 _byteswap_ulong + #define bswap_64 _byteswap_uint64 +#endif + + + #endif // _UTIL_H \ No newline at end of file