// x-run: gcc % -o main -g && ./main /home/hkc/images/wallpapers/photo_2020-09-18_10-05-14.jpg #include #include #include #include #include #include #include #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"; } }