summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile38
-rw-r--r--src/e2e.c363
-rw-r--r--src/e2e.h3
-rw-r--r--src/main.c55
4 files changed, 459 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e98fe38
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,38 @@
+CC = gcc
+CFLAGS += -g -Wall -O3
+CPPFLAGS += -MMD
+LDFLAGS += -pthread -lm
+
+PP_BOLD := $(shell tput bold)
+PP_RESET := $(shell tput sgr0)
+PP_CC := $(PP_BOLD)$(shell tput setf 6)CC$(PP_RESET)
+PP_LD := $(PP_BOLD)$(shell tput setf 2)LD$(PP_RESET)
+PP_RM := $(PP_BOLD)$(shell tput setf 4)RM$(PP_RESET)
+
+SRC := src/e2e.c \
+ src/main.c
+
+OBJ := $(SRC:src/%.c=obj/%.o)
+OUT := e2e
+
+all: $(OUT)
+
+-include $(OBJ:.o=.d)
+
+obj/%.o : src/%.c
+ @echo "$(PP_CC) src/$*.c"
+ @mkdir -p $(@D)
+ @$(CC) $(CFLAGS) $(CPPFLAGS) -c src/$*.c -o obj/$*.o
+
+$(OUT): $(OBJ)
+ @echo "$(PP_LD) $(OUT)"
+ @$(CC) $(OBJ) -o $(OUT) $(LDFLAGS)
+
+clean:
+ @echo "${PP_RM} obj"
+ @rm -rf obj
+ @echo "${PP_RM} ${OUT}"
+ @rm -rf ${OUT}
+
+.PHONY: clean
+
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
diff --git a/src/e2e.h b/src/e2e.h
new file mode 100644
index 0000000..d7f7a13
--- /dev/null
+++ b/src/e2e.h
@@ -0,0 +1,3 @@
+#include <inttypes.h>
+
+int e2e_read(const char *start, const char *end); \ No newline at end of file
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..e51d863
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,55 @@
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "e2e.h"
+
+uint32_t u32le(const char*);
+
+int main(int argc, char **argv)
+{
+ int rv = 0;
+ char *inpath;
+ int fd;
+ struct stat st;
+ void *input;
+
+ if (argc < 2) {
+ fprintf(stderr, "%s [FILE]\n", argv[0]);
+ return 1;
+ }
+
+ inpath = argv[1];
+
+ fd = open(inpath, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ return 1;
+ }
+
+ if (fstat(fd, &st) == -1) {
+ perror("fstat");
+ rv = 1;
+ goto error_mmap;
+ }
+
+ input = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (!input) {
+ perror("mmap");
+ rv = 1;
+ goto error_mmap;
+ }
+
+ e2e_read(input, input + st.st_size);
+
+ munmap(input, st.st_size);
+error_mmap:
+ close(fd);
+ return rv;
+} \ No newline at end of file