summaryrefslogtreecommitdiff
path: root/src/e2e.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/e2e.c')
-rw-r--r--src/e2e.c363
1 files changed, 363 insertions, 0 deletions
diff --git a/src/e2e.c b/src/e2e.c
new file mode 100644
index 0000000..1ab1449
--- /dev/null
+++ b/src/e2e.c
@@ -0,0 +1,363 @@
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#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;
+} \ No newline at end of file