jpeg-mess/main.c

196 lines
4.7 KiB
C

// x-run: gcc % -o main -g && ./main /home/hkc/images/wallpapers/photo_2020-09-18_10-05-14.jpg
#include <endian.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#define HEXDUMP_RAINBOW
#define HEXDUMP_PRINTABLE
#define HEXDUMP_IMPLEMENTATION
#include "./hexdump.h"
typedef uint16_t marker_t;
typedef enum {
SOF0 = 0xFFC0, // Baseline DCT
SOF1 = 0xFFC1, // Extended seq DCT
SOF2 = 0xFFC2, // Progressive DCT
SOF3 = 0xFFC3, // Lossless
DHT = 0xFFC4, // Define Huffman Table
SOF5 = 0xFFC5, //
SOF6 = 0xFFC6,
SOF7 = 0xFFC7,
JPG = 0xFFC8,
SOF9 = 0xFFC9,
SOF10 = 0xFFCA,
SOF11 = 0xFFCB,
DAC = 0xFFCC,
SOF13 = 0xFFCD,
SOF14 = 0xFFCE,
SOF15 = 0xFFCF,
} marker_type_t;
struct JFIF_SOF0_Component {
uint8_t id;
uint8_t TODO_0 : 4;
uint8_t TODO_1 : 4;
} __attribute__((packed));
struct JFIF_SOF0 {
uint16_t bit_depth;
uint16_t height;
uint8_t width;
uint8_t n_components;
struct JFIF_SOF0_Component *components;
} __attribute__((packed));
#define panic(...) { \
fprintf(stderr, "!PANIC! at %s:%d\n", __FILE__, __LINE__);\
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\nerrno=%d (%s)\n", errno, strerror(errno)); \
abort(); \
}
marker_t read_and_print_block(FILE *fp);
const char *marker_name(int marker);
int main(int argc, char **argv) {
static uint8_t tmp[8192];
FILE *fp = fopen(argv[1], "rb");
fread(tmp, 1, 2, fp);
if (memcmp(tmp, "\xFF\xD8", 2)) {
panic("Invalid header!");
}
marker_t last_marker;
do {
last_marker = read_and_print_block(fp);
} while (last_marker != 0xFFD9);
}
marker_t read_and_print_block(FILE *fp) {
static uint8_t buffer[65536];
marker_t marker;
uint16_t length;
long int pos = ftell(fp);
fread(&marker, sizeof(marker_t), 1, fp);
marker = be16toh(marker);
if ((marker & 0xFF00) != 0xFF00) {
printf("!!!!!!!!\n");
fseek(fp, pos & ~0xF, SEEK_SET);
fread(buffer, 1, 32, fp);
hexdump(buffer, 32);
panic("Invalid marker %04x at %ld (0x%lx)", marker, pos, pos);
}
if (marker == 0xFFD9) return marker;
fread(&length, sizeof(uint16_t), 1, fp);
length = be16toh(length) - 2;
printf("marker: %04x %8s (%d long) at %ld %lx\n", marker, marker_name(marker), length, pos, pos);
fread(buffer, 1, length, fp);
switch (marker) {
case SOF0:
printf("\033[91mSTART OF FRAME\033[0m\n");
hexdump(buffer, length);
break;
case 0xFFDA: // Start Of Scan
{
uint8_t n_components = buffer[0];
uint8_t *cur = &buffer[1];
printf("components: %d\n", n_components);
for (uint8_t i = 0; i < n_components; i++) {
uint8_t selector = *cur++,
tables = *cur++;
printf("\t%d: %d in %d/%d\n", i, selector, tables >> 4, tables & 0xf);
}
uint8_t spectral_selector_start = *cur++,
spectral_selector_end = *cur++,
successive_approx = *cur++;
printf("spectral selector: %d..%d\n", spectral_selector_start, spectral_selector_end);
int n_read = -1;
do {
n_read++;
fread(&buffer[n_read], 1, 1, fp);
} while (!(buffer[n_read - 1] == 0xFF && (buffer[n_read] & 0xF0) == 0xF0));
printf("stopped after %d bytes\n", n_read);
hexdump(buffer, n_read);
fseek(fp, ftell(fp) - 1, SEEK_SET);
}
break;
case 0xFFDB: // Quantization table
{
uint8_t destination = buffer[0];
printf("\tdestination: %d\n", destination);
for (int y = 0; y < 8; y++) {
printf("\t");
for (int x = 0; x < 8; x++) {
printf("%3d ", buffer[1 + x + y * 8]);
}
printf("\n");
}
}
break;
case 0xFFE0: // APP*
case 0xFFE1:
case 0xFFE2:
case 0xFFE3:
case 0xFFE4:
case 0xFFE5:
case 0xFFE6:
case 0xFFE7:
case 0xFFE8:
case 0xFFE9:
case 0xFFEA:
case 0xFFEB:
case 0xFFEC:
case 0xFFED:
case 0xFFEE:
case 0xFFEF:
break;
default:
hexdump(buffer, length);
break;
}
return marker;
}
const char *marker_name(int marker) {
switch (marker) {
case SOF0: return " SOF0";
case SOF1: return " SOF1";
case SOF2: return " SOF2";
case SOF3: return " SOF3";
case DHT: return " DHT";
case SOF5: return " SOF5";
case SOF6: return " SOF6";
case SOF7: return " SOF7";
case JPG: return " JPG";
case SOF9: return " SOF9";
case SOF10: return "SOF10";
case SOF11: return "SOF11";
case DAC: return " DAC";
case SOF13: return "SOF13";
case SOF14: return "SOF14";
case SOF15: return "SOF15";
default: return "N/A";
}
}