diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a76e217..654864d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,3 +6,7 @@ add_executable (jpeg-dissect ) set_property(TARGET jpeg-dissect PROPERTY C_STANDARD 11) + +if (UNIX) + target_link_libraries(jpeg-dissect m) +endif() \ No newline at end of file diff --git a/src/loader.c b/src/loader.c index 48a1705..1626a32 100644 --- a/src/loader.c +++ b/src/loader.c @@ -3,6 +3,7 @@ #include #include #include +#include #define MAX_QUANTIZATION_TABLES 255 #define MAX_HUFFMAN_TABLES 255 @@ -14,6 +15,9 @@ static int load_segment(JPEG* jpeg, FILE* fp); static int load_quantization_table(JPEG* jpeg, FILE* fp); static int load_huffman_table(JPEG* jpeg, FILE* fp); static int load_start_of_frame(JPEG* jpeg, FILE* fp, uint8_t type); +static int load_start_of_scan(JPEG* jpeg, FILE* fp); + +static int load_scan_data(JPEG* jpeg, FILE* fp, struct ScanComponent* scan_component); static int load_rst_segment(JPEG* jpeg, FILE* fp, uint8_t n); static int load_app_segment(JPEG* jpeg, FILE* fp, uint8_t n); @@ -74,6 +78,44 @@ void free_jpeg(JPEG* jpeg) free(jpeg->quantization_tables); jpeg->quantization_tables = NULL; } + + if (jpeg->huffman_tables) + { + for (size_t i = 0; i < jpeg->num_huffman_tables; i++) + { + for (size_t j = 0; j < 16; j++) + { + free(jpeg->huffman_tables[i].codes[j]); + } + } + + free(jpeg->huffman_tables); + jpeg->huffman_tables = NULL; + } + + if (jpeg->frame_header) + { + if (jpeg->frame_header->components) + { + free(jpeg->frame_header->components); + jpeg->frame_header->components = NULL; + } + + free(jpeg->frame_header); + jpeg->frame_header = NULL; + } + + if (jpeg->scan_header) + { + if (jpeg->scan_header->components) + { + free(jpeg->scan_header->components); + jpeg->scan_header->components = NULL; + } + + free(jpeg->scan_header); + jpeg->scan_header = NULL; + } } int load_segment(JPEG* jpeg, FILE* fp) @@ -125,6 +167,9 @@ int load_segment(JPEG* jpeg, FILE* fp) DEBUG_LOG("SOI marker encountered"); break; + case 0xDA: + return load_start_of_scan(jpeg, fp); + case 0xDB: // Quantization table return load_quantization_table(jpeg, fp); @@ -146,7 +191,7 @@ int load_quantization_table(JPEG* jpeg, FILE* fp) if (jpeg->quantization_tables == NULL) { - jpeg->quantization_tables = (QuantizationTable*)malloc(sizeof(QuantizationTable) * MAX_QUANTIZATION_TABLES); + jpeg->quantization_tables = (struct QuantizationTable*)malloc(sizeof(struct QuantizationTable) * MAX_QUANTIZATION_TABLES); if (jpeg->quantization_tables == NULL) { ERROR_LOG("Failed to allocate memory for quantization tables"); @@ -154,6 +199,7 @@ int load_quantization_table(JPEG* jpeg, FILE* fp) } } + uint16_t total_length; if (fread(&total_length, sizeof(uint8_t), sizeof(uint16_t), fp) != sizeof(uint16_t)) { @@ -166,12 +212,12 @@ int load_quantization_table(JPEG* jpeg, FILE* fp) while (read_length < total_length) { - QuantizationTable* current_table = jpeg->quantization_tables + jpeg->num_quantization_tables; + struct QuantizationTable* current_table = jpeg->quantization_tables + jpeg->num_quantization_tables; uint8_t meta_info; if (fread(&meta_info, sizeof(uint8_t), sizeof(uint8_t), fp) != sizeof(uint8_t)) { - ERROR_LOG("Failed to read quantization table #%d meta info", jpeg->num_quantization_tables); + ERROR_LOG("Failed to read quantization table #%zu meta info", jpeg->num_quantization_tables); return 1; } @@ -183,7 +229,7 @@ int load_quantization_table(JPEG* jpeg, FILE* fp) size_t table_length = current_table->precision * 64; DEBUG_LOG( - "Quantization table #%d\n" + "Quantization table #%zu\n" "\tprecision = %d bit\n" "\tdestination = %d", @@ -195,13 +241,13 @@ int load_quantization_table(JPEG* jpeg, FILE* fp) current_table->data = (uint8_t*)malloc(table_length); if (current_table->data == NULL) { - ERROR_LOG("Failed to allocate memory for quantization table #%d data", jpeg->num_quantization_tables); + ERROR_LOG("Failed to allocate memory for quantization table #%zu data", jpeg->num_quantization_tables); return 1; } if (fread(current_table->data, sizeof(uint8_t), table_length, fp) != table_length) { - ERROR_LOG("Failed to read quantization table #%d data", jpeg->num_quantization_tables); + ERROR_LOG("Failed to read quantization table #%zu data", jpeg->num_quantization_tables); return 1; } @@ -221,7 +267,7 @@ int load_huffman_table(JPEG* jpeg, FILE* fp) if (jpeg->huffman_tables == NULL) { - jpeg->huffman_tables = (HuffmanTable*)malloc(sizeof(HuffmanTable) * MAX_HUFFMAN_TABLES); + jpeg->huffman_tables = (struct HuffmanTable*)malloc(sizeof(struct HuffmanTable) * MAX_HUFFMAN_TABLES); if (jpeg->huffman_tables == NULL) { ERROR_LOG("Failed to allocate memory for huffman tables"); @@ -241,12 +287,12 @@ int load_huffman_table(JPEG* jpeg, FILE* fp) while (read_length < total_length) { - HuffmanTable* current_table = jpeg->huffman_tables + jpeg->num_huffman_tables; + struct HuffmanTable* current_table = jpeg->huffman_tables + jpeg->num_huffman_tables; uint8_t meta_info; if (fread(&meta_info, sizeof(uint8_t), sizeof(uint8_t), fp) != sizeof(uint8_t)) { - ERROR_LOG("Failed to read huffman table #%d meta info", jpeg->num_huffman_tables); + ERROR_LOG("Failed to read huffman table #%zu meta info", jpeg->num_huffman_tables); return 1; } @@ -257,14 +303,14 @@ int load_huffman_table(JPEG* jpeg, FILE* fp) if (fread(current_table->num_codes, sizeof(uint8_t), 16, fp) != 16) { - ERROR_LOG("Failed to read huffman code lengths for table #%d", jpeg->num_huffman_tables); + ERROR_LOG("Failed to read huffman code lengths for table #%zu", jpeg->num_huffman_tables); return 1; } read_length += 16; DEBUG_LOG( - "Huffman table #%d\n" + "Huffman table #%zu\n" "\tclass = %s\n" "\tdestination = %d\n" "\tcode lengths = %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", @@ -289,13 +335,13 @@ int load_huffman_table(JPEG* jpeg, FILE* fp) current_table->codes[i] = (uint8_t*)malloc(sizeof(uint8_t) * current_table->num_codes[i]); if (current_table->codes[i] == NULL) { - ERROR_LOG("Failed to allocate memory for huffman table #%d list of codes of length %d", jpeg->num_huffman_tables, i); + ERROR_LOG("Failed to allocate memory for huffman table #%zu list of codes of length %zu", jpeg->num_huffman_tables, i); return 1; } if (fread(current_table->codes[i], sizeof(uint8_t), current_table->num_codes[i], fp) != current_table->num_codes[i]) { - ERROR_LOG("Failed to read huffman codes of length %d for table #%d", i, jpeg->num_huffman_tables); + ERROR_LOG("Failed to read huffman codes of length %zu for table #%zu", i, jpeg->num_huffman_tables); return 1; } @@ -321,13 +367,15 @@ int load_start_of_frame(JPEG* jpeg, FILE* fp, uint8_t type) return 1; } - jpeg->frame_header = (FrameHeader*)malloc(sizeof(FrameHeader)); + jpeg->frame_header = (struct FrameHeader*)malloc(sizeof(struct FrameHeader)); if (jpeg->frame_header == NULL) { ERROR_LOG("Failed to allocate memory for frame header"); return 1; } + memzero(jpeg->frame_header, sizeof(struct FrameHeader)); + if (fread(jpeg->frame_header, sizeof(uint8_t), FRAME_HEADER_SIZE, fp) != FRAME_HEADER_SIZE) { ERROR_LOG("Failed to read data from frame header"); @@ -340,7 +388,7 @@ int load_start_of_frame(JPEG* jpeg, FILE* fp, uint8_t type) jpeg->frame_header->encoding = type; - jpeg->frame_header->components = (FrameComponent*)malloc(sizeof(FrameComponent) * jpeg->frame_header->num_components); + jpeg->frame_header->components = (struct FrameComponent*)malloc(sizeof(struct FrameComponent) * jpeg->frame_header->num_components); if (jpeg->frame_header->components == NULL) { ERROR_LOG("Failed to allocate memory for frame components"); @@ -349,12 +397,15 @@ int load_start_of_frame(JPEG* jpeg, FILE* fp, uint8_t type) for (size_t c = 0; c < jpeg->frame_header->num_components; c++) { - FrameComponent* current_component = jpeg->frame_header->components + c; - if (fread(current_component, sizeof(uint8_t), sizeof(FrameComponent), fp) != sizeof(FrameComponent)) + struct FrameComponent* current_component = jpeg->frame_header->components + c; + if (fread(current_component, sizeof(uint8_t), sizeof(struct FrameComponent), fp) != sizeof(struct FrameComponent)) { - ERROR_LOG("Failed to read component #%u", c); + ERROR_LOG("Failed to read component #%zu", c); return 1; } + + jpeg->frame_header->max_sampling_factor.v = fmaxl(jpeg->frame_header->max_sampling_factor.v, current_component->sampling_factor.v); + jpeg->frame_header->max_sampling_factor.h = fmaxl(jpeg->frame_header->max_sampling_factor.h, current_component->sampling_factor.h); } DEBUG_LOG( @@ -363,7 +414,8 @@ int load_start_of_frame(JPEG* jpeg, FILE* fp, uint8_t type) "\tprecision = %d\n" "\tlines, samples = %d, %d\n" "\tcomponents = %d\n" - "\tencoding = %s %s, %s", + "\tencoding = %s %s, %s\n" + "\tmax sampling factor v/h = %d/%d", jpeg->frame_header->length, jpeg->frame_header->precision, @@ -375,9 +427,176 @@ int load_start_of_frame(JPEG* jpeg, FILE* fp, uint8_t type) (jpeg->frame_header->encoding & ENCODING_PROCESS_MASK) == Extended ? "extended sequential DCT" : (jpeg->frame_header->encoding & ENCODING_PROCESS_MASK) == Progressive ? "progressive DCT" : "Lossless (sequential)", - (jpeg->frame_header->encoding & ENCODING_CODING_MASK) == Huffman ? "huffman coding" : "arithmetic coding" + (jpeg->frame_header->encoding & ENCODING_CODING_MASK) == Huffman ? "huffman coding" : "arithmetic coding", + + jpeg->frame_header->max_sampling_factor.v, jpeg->frame_header->max_sampling_factor.h ); + for (size_t i = 0; i < jpeg->frame_header->num_components; i++) + { + DEBUG_LOG( + "Frame header component #%zu\n" + "\tid = %d\n" + "\tsampling factor v/h = %d/%d\n" + "\ttable = %d", + + i, + jpeg->frame_header->components[i].identifier, + jpeg->frame_header->components[i].sampling_factor.v, jpeg->frame_header->components[i].sampling_factor.h, + jpeg->frame_header->components[i].quantization_table + ); + } + + return 0; +} + +int load_start_of_scan(JPEG* jpeg, FILE* fp) +{ + DEBUG_LOG("SOS encountered"); + + assert(jpeg); + assert(fp); + + if (jpeg->scan_header != NULL) + { + ERROR_LOG("Found multiple scans"); + return 1; + } + + jpeg->scan_header = (struct ScanHeader*)malloc(sizeof(struct ScanHeader)); + if (jpeg->scan_header == NULL) + { + ERROR_LOG("Failed to allocate memory for scan header"); + return 1; + } + + if (fread(jpeg->scan_header, sizeof(uint8_t), SCAN_HEADER_PRE_SIZE, fp) != SCAN_HEADER_PRE_SIZE) + { + ERROR_LOG("Failed to read length of scan header or scan header components"); + return 1; + } + + jpeg->scan_header->length = bswap_16(jpeg->scan_header->length); + + jpeg->scan_header->components = (struct ScanComponent*)malloc(sizeof(struct ScanComponent) * jpeg->scan_header->num_components); + if (jpeg->scan_header == NULL) + { + ERROR_LOG("Failed to allocate memory for scan header components"); + return 1; + } + + for (size_t i = 0; i < jpeg->scan_header->num_components; i++) + { + struct ScanComponent* current_component = jpeg->scan_header->components + i; + + if (fread(current_component, sizeof(uint8_t), sizeof(struct ScanComponent), fp) != sizeof(struct ScanComponent)) + { + ERROR_LOG("Failed to load component #%zu of scan header", i); + return 1; + } + } + + if (fread(&jpeg->scan_header->spectral_select_start, sizeof(uint8_t), SCAN_HEADER_POST_SIZE, fp) != SCAN_HEADER_POST_SIZE) + { + ERROR_LOG("Failed to read spectral selection info"); + return 1; + } + + DEBUG_LOG( + "Scan header\n" + "\tcomponents = %d\n" + "\tspectral select s/e = %d/%d\n" + "\tapprox bit pos h/l = %d/%d", + + jpeg->scan_header->num_components, + jpeg->scan_header->spectral_select_start, jpeg->scan_header->spectral_select_end, + jpeg->scan_header->approx_bit_pos.high, jpeg->scan_header->approx_bit_pos.low + ); + + jpeg->scans = (struct Scan*)malloc(sizeof(struct Scan) * jpeg->scan_header->num_components); + if (jpeg->scans == NULL) + { + ERROR_LOG("Failed to allocate memory for scan data"); + return 1; + } + + for (size_t i = 0; i < jpeg->scan_header->num_components; i++) + { + struct ScanComponent* scan_component = jpeg->scan_header->components + i; + + DEBUG_LOG( + "Scan header component #%zu\n" + "\tid = %d\n" + "\tdestination dc/ac = %d/%d", + + i, + scan_component->identifier, + scan_component->table_destination.dc, scan_component->table_destination.ac + ); + + if (load_scan_data(jpeg, fp, scan_component) != 0) + { + return 1; + } + } + + return 0; +} + +int load_scan_data(JPEG* jpeg, FILE* fp, struct ScanComponent* scan_component) +{ + DEBUG_LOG("Loading scan data (component #%d)", scan_component->identifier); + + assert(jpeg); + assert(fp); + + struct Scan* scan = jpeg->scans + jpeg->num_scans; + + struct FrameComponent* frame_component = NULL; + for (size_t i = 0; i < jpeg->frame_header->num_components; i++) + { + if (jpeg->frame_header->components[i].identifier == scan_component->identifier) + { + frame_component = jpeg->frame_header->components + i; + break; + } + } + + if (frame_component == NULL) + { + ERROR_LOG("Couldn't find matching frame component for scan component (%d)", scan_component->identifier); + return 1; + } + + scan->scan_component = scan_component; + scan->frame_component = frame_component; + + uint16_t lines = jpeg->frame_header->num_lines; + uint16_t samples = jpeg->frame_header->num_samples; + + scan->width = ceill(samples * (float)frame_component->sampling_factor.h / (float)jpeg->frame_header->max_sampling_factor.h); + scan->height = ceill(lines * (float)frame_component->sampling_factor.v / (float)jpeg->frame_header->max_sampling_factor.v); + + DEBUG_LOG("size of scan (w,h) = %d,%d", scan->width, scan->height); + + scan->length = (size_t)scan->width * scan->height; + + scan->data = (uint8_t*)malloc(sizeof(uint8_t) * scan->length); + if (scan->data == NULL) + { + ERROR_LOG("Failed to allocate memory for scan data"); + return 1; + } + + size_t tmp = fread(scan->data, sizeof(uint8_t), scan->length, fp); + if (tmp != scan->length) + { + ERROR_LOG("Failed to load scan data from file"); + return 1; + } + + + jpeg->num_scans++; return 0; } @@ -414,8 +633,8 @@ int load_app0_segment(JPEG* jpeg, FILE* fp) assert(jpeg); assert(fp); - jpeg->app0 = (JFIFAPP0Segment*)malloc(sizeof(JFIFAPP0Segment)); - memzero(jpeg->app0, sizeof(JFIFAPP0Segment)); + jpeg->app0 = (struct JFIFAPP0Segment*)malloc(sizeof(struct JFIFAPP0Segment)); + memzero(jpeg->app0, sizeof(struct JFIFAPP0Segment)); if (jpeg->app0 == NULL) { diff --git a/src/loader.h b/src/loader.h index 8734cbc..b73a76e 100644 --- a/src/loader.h +++ b/src/loader.h @@ -1,4 +1,4 @@ -#ifndef _LODAER_H +#ifndef _LOADER_H #define _LOADER_H #include @@ -8,55 +8,50 @@ #define ENCODING_DCT_MASK 4 #define ENCODING_CODING_MASK 8 -typedef enum EncodingProcess +enum EncodingProcess { Baseline = 0, Extended = 1, Progressive = 2, Lossless = 3, -} EncodingProcess; +}; -typedef enum Coding +enum Coding { Huffman = 0, Arithmetic = (1 << 3) -} Coding; +}; -typedef enum DCTType +enum DCTType { NonDifferential = 0, Differential = (1 << 2) -} DCTType; +}; -typedef enum TableClass +enum TableClass { DCTable, ACTable -} TableClass; +}; -PACK( -typedef struct QuantizationTable +PACK(struct QuantizationTable { uint8_t precision; uint8_t destination; uint8_t* data; -} QuantizationTable; -) +}); -PACK( -typedef struct HuffmanTable +PACK(struct HuffmanTable { - TableClass class; + enum TableClass class; uint8_t destination; uint8_t num_codes[16]; uint8_t* codes[16]; -} HuffmanTable; -) +}); -#define QUANTIZATION_TABLE_SIZE sizeof(QuantizationTable) - sizeof(uint8_t*) +#define QUANTIZATION_TABLE_SIZE sizeof(struct QuantizationTable) - sizeof(uint8_t*) -PACK( -typedef struct FrameComponent +PACK(struct FrameComponent { uint8_t identifier; struct @@ -65,11 +60,9 @@ typedef struct FrameComponent uint8_t h : 4; } sampling_factor; uint8_t quantization_table; -} FrameComponent; -) +}); -PACK( -typedef struct FrameHeader +PACK(struct FrameHeader { uint16_t length; uint8_t precision; @@ -77,15 +70,59 @@ typedef struct FrameHeader uint16_t num_samples; uint8_t num_components; + struct + { + uint8_t v : 4; + uint8_t h : 4; + } max_sampling_factor; + uint8_t encoding; - FrameComponent* components; -} FrameHeader; -) + struct FrameComponent* components; +}); -#define FRAME_HEADER_SIZE sizeof(FrameHeader) - (sizeof(FrameComponent*) + sizeof(uint8_t)) +#define FRAME_HEADER_SIZE sizeof(struct FrameHeader) - (sizeof(struct FrameComponent*) + sizeof(uint8_t) + sizeof(uint8_t)) -PACK ( -typedef struct JFIFAPP0Segment +PACK(struct ScanComponent +{ + uint8_t identifier; + struct + { + uint8_t dc : 4; + uint8_t ac : 4; + } table_destination; +}); + +PACK(struct ScanHeader +{ + uint16_t length; + uint8_t num_components; + struct ScanComponent* components; + + uint8_t spectral_select_start; + uint8_t spectral_select_end; + + struct + { + uint8_t high : 4; + uint8_t low : 4; + } approx_bit_pos; +}); + +struct Scan +{ + size_t length; + struct ScanComponent* scan_component; + struct FrameComponent* frame_component; + + uint16_t width; + uint16_t height; + uint8_t* data; +}; + +#define SCAN_HEADER_PRE_SIZE sizeof(uint16_t) + sizeof(uint8_t) +#define SCAN_HEADER_POST_SIZE sizeof(uint8_t) * 3 + +PACK(struct JFIFAPP0Segment { uint16_t length; const char identifier[5]; @@ -103,22 +140,25 @@ typedef struct JFIFAPP0Segment uint8_t thumbnail_y; uint8_t* thumbnail_data; -} JFIFAPP0Segment; -) +}); -#define JFIF_APP0_SIZE sizeof(JFIFAPP0Segment) - sizeof(uint8_t*) +#define JFIF_APP0_SIZE sizeof(struct JFIFAPP0Segment) - sizeof(uint8_t*) typedef struct JPEG { - JFIFAPP0Segment* app0; + struct JFIFAPP0Segment* app0; size_t num_quantization_tables; - QuantizationTable* quantization_tables; + struct QuantizationTable* quantization_tables; size_t num_huffman_tables; - HuffmanTable* huffman_tables; + struct HuffmanTable* huffman_tables; - FrameHeader* frame_header; + struct FrameHeader* frame_header; + struct ScanHeader* scan_header; + + size_t num_scans; + struct Scan* scans; } JPEG; JPEG* load_jpeg(const char* filename); diff --git a/src/util.h b/src/util.h index 1024f42..cc775d2 100644 --- a/src/util.h +++ b/src/util.h @@ -12,12 +12,9 @@ #endif #if defined(__GNUC__) - #define PACK( data ) data __attribute((__packed__)) + #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))