#include #include #include #include "e2e.h" uint32_t u32le(const char *src) { uint8_t bytes[4]; memcpy(bytes, src, 4); return (uint32_t)bytes[0] + (((uint32_t)bytes[1]) << 8) + (((uint32_t)bytes[2]) << 16) + (((uint32_t)bytes[3]) << 24); } static int make_pointer(const char *start, const char *end, const char *pos, const char **out) { uint32_t ptroffs; ptroffs = u32le(pos); if (!ptroffs) *out = NULL; else { if (ptroffs > end - start) return 1; *out = start + ptroffs; } return 0; } #define HEADER_END 36 #define DIRECTORY_NUM_ENTRIES 36 #define DIRECTORY_CURRENT 40 #define DIRECTORY_NEXT 44 #define DIRECTORY_END 52 #define ENTRY_POS 0 #define ENTRY_START 4 #define ENTRY_SIZE 8 #define ENTRY_PATIENT_ID 16 #define ENTRY_STUDY_ID 20 #define ENTRY_SERIES_ID 24 #define ENTRY_SLICE_ID 28 #define ENTRY_TYPE 36 #define ENTRY_END 44 #define CHUNK_POS 20 #define CHUNK_SIZE 24 #define CHUNK_PATIENT_ID 32 #define CHUNK_STUDY_ID 36 #define CHUNK_SERIES_ID 40 #define CHUNK_SLICE_ID 44 #define CHUNK_IND 48 #define CHUNK_TYPE 52 #define CHUNK_HEADER_END 60 #define DATA_IMAGE 0x40000000 #define IMAGE_SIZE 0 #define IMAGE_TYPE 4 #define IMAGE_WIDTH 12 #define IMAGE_HEIGHT 16 #define IMAGE_IMAGE 20 #define IMAGE_FUNDUS 0x02010201 #define IMAGE_TOMOGRAM 0x02200201 #define UF16_MANTISSA_MASK ((1 << 11) - 1) // 10 most significant bits struct e2e_directory { size_t num_entries; const char *current, *next; }; struct e2e_entry { const char *start; size_t size; uint32_t patient_id, study_id, series_id, slice_id, type; }; struct e2e_chunk { const char *origin; const struct e2e_entry *entry; size_t size; uint32_t patient_id, study_id, series_id, slice_id, type; }; struct e2e_fundus { size_t size, width, height; uint32_t type; }; static int read_fundus(const char *start, const char *end, const char *cursor, size_t width, size_t height) { FILE *fp; char buffer[1024]; snprintf(buffer, sizeof(buffer), "out/fundus_%tx.py", cursor - start); fp = fopen(buffer, "w"); fprintf(fp, "data = [\n"); for (size_t y = 0; y < height; y++) { fprintf(fp, "\t["); for (size_t x = 0; x < width; x++) { uint8_t px; memcpy(&px, cursor + y * width + x, 1); fprintf(fp, "%s% 3hhu", x ? ", " : "", px); } fprintf(fp, "]%s\n", y + 1 >= height ? "" : ","); } fprintf(fp, "]\n"); fclose(fp); return 0; } static int read_tomogram(const char *start, const char *end, const char *cursor, size_t width, size_t height) { FILE *fp; char buffer[1024]; snprintf(buffer, sizeof(buffer), "out/tomogram_%tx.py", cursor - start); fp = fopen(buffer, "w"); fprintf(fp, "data = [\n"); for (size_t y = 0; y < height; y++) { fprintf(fp, "\t["); for (size_t x = 0; x < width; x++) { uint16_t raw; int exp, man; float px; memcpy(&raw, cursor + (y * width + x) * 2, 2); man = raw & UF16_MANTISSA_MASK; exp = (raw & ~UF16_MANTISSA_MASK) >> 10; px = man * exp2f(exp); fprintf(fp, "%s%e", x ? ", " : "", px); } fprintf(fp, "]%s\n", y + 1 >= height ? "" : ","); } fprintf(fp, "]\n"); fclose(fp); return 0; } static int read_image(const char *start, const char *end, const struct e2e_chunk *chunk) { const char *cursor = chunk->origin + CHUNK_HEADER_END; size_t size, width, height; uint32_t type; if (cursor + IMAGE_IMAGE > end) { fprintf(stderr, "Image data at %td ends past the end of file.\n", cursor - start); return 1; } size = u32le(cursor + IMAGE_SIZE); type = u32le(cursor + IMAGE_TYPE); width = u32le(cursor + IMAGE_WIDTH); height = u32le(cursor + IMAGE_HEIGHT); printf("# %08tx Image data: %zux%zu, size=%zu\n", cursor - start, width, height, size); printf("# Slice is %d\n", chunk->slice_id); printf("# Type is %08x\n", type); switch (type) { case IMAGE_FUNDUS: read_fundus(start, end, cursor + IMAGE_IMAGE, width, height); break; case IMAGE_TOMOGRAM: read_tomogram(start, end, cursor + IMAGE_IMAGE, width, height); break; } return 0; } static int read_chunk(const char *start, const char *end, const struct e2e_entry *entry, struct e2e_chunk *chunk) { const char *cursor = entry->start, *pos; chunk->entry = entry; chunk->origin = cursor; if (cursor + CHUNK_HEADER_END > end) { fprintf(stderr, "Chunk at %td ends past the end of file.\n", cursor - start); return 1; } if (make_pointer(start, end, cursor + CHUNK_POS, &pos)) { fprintf(stderr, "Chunk at %td has a bad 'pos' entry.\n", cursor - start); return 1; } if (pos != cursor) { fprintf(stderr, "Chunk at %td has a 'pos' entry that differs from the entry's actual position", cursor - start); return 1; } chunk->size = u32le(cursor + CHUNK_SIZE); chunk->patient_id = u32le(cursor + CHUNK_PATIENT_ID); chunk->study_id = u32le(cursor + CHUNK_STUDY_ID); chunk->series_id = u32le(cursor + CHUNK_SERIES_ID); chunk->slice_id = u32le(cursor + CHUNK_SLICE_ID); chunk->type = u32le(cursor + CHUNK_TYPE); return 0; } static int read_entry(const char *start, const char *end, const char *cursor, struct e2e_entry *entry) { const char *pos; if (cursor + ENTRY_END > end) { fprintf(stderr, "Entry at %td ends past the end of file.\n", cursor - start); return 1; } if (make_pointer(start, end, cursor + ENTRY_POS, &pos)) { fprintf(stderr, "Entry at %td has a bad 'pos' entry.\n", cursor - start); return 1; } if (pos != cursor) { fprintf(stderr, "Entry at %td has a 'pos' entry that differs from the entry's actual position", cursor - start); return 1; } if (make_pointer(start, end, cursor + ENTRY_START, &entry->start)) { fprintf(stderr, "Entry at %td has a bad 'start' entry.\n", cursor - start); return 1; } entry->size = u32le(cursor + ENTRY_SIZE); entry->patient_id = u32le(cursor + ENTRY_PATIENT_ID); entry->study_id = u32le(cursor + ENTRY_STUDY_ID); entry->series_id = u32le(cursor + ENTRY_SERIES_ID); entry->slice_id = u32le(cursor + ENTRY_SLICE_ID); entry->type = u32le(cursor + ENTRY_TYPE); return 0; } static int read_directory(const char *start, const char *end, const char *cursor, struct e2e_directory *dir) { if (cursor + DIRECTORY_END > end) { fprintf(stderr, "Directory at %td ends past the end of file.\n", cursor - start); return 1; } dir->num_entries = u32le(cursor + DIRECTORY_NUM_ENTRIES); if (make_pointer(start, end, cursor + DIRECTORY_CURRENT, &dir->current)) { fprintf(stderr, "Directory at %td contains a bad 'current' field.\n", cursor - start); return 1; } if (make_pointer(start, end, cursor + DIRECTORY_NEXT, &dir->next)) { fprintf(stderr, "Directory at %td contains a bad 'current' field.\n", cursor - start); return 1; } return 0; } int e2e_read(const char *start, const char *end) { int rv; const char *cursor; if (start + HEADER_END > end) { fprintf(stderr, "File too short to be an E2E file.\n"); return 1; } // TODO: verify the header cursor = start + HEADER_END; cursor += DIRECTORY_END; // skip the first directory (wtf?) while (cursor) { struct e2e_directory dir; rv = read_directory(start, end, cursor, &dir); if (rv) return rv; printf("# %08tx Directory: num_entries=%zu, current=%08td, next=%td\n", cursor - start, dir.num_entries, dir.current - start, dir.next ? dir.next - start : 0); for (size_t i = 0; i < dir.num_entries; i++) { const char *entry_cursor; struct e2e_entry entry; struct e2e_chunk chunk; entry_cursor = cursor + DIRECTORY_END + i * ENTRY_END; rv = read_entry(start, end, entry_cursor, &entry); if (rv) return rv; printf("# %08tx Entry %zu: start=%08tx\n", entry_cursor - start, i, entry.start ? entry.start - start : 0); if (!entry.start) continue; rv = read_chunk(start, end, &entry, &chunk); if (rv) continue; printf("# %08tx Chunk, size=%zu, type=%08x\n", entry.start - start, chunk.size, chunk.type); switch (chunk.type) { case DATA_IMAGE: read_image(start, end, &chunk); break; } } cursor = dir.next; } return 0; }