diff options
-rw-r--r-- | Makefile | 38 | ||||
-rw-r--r-- | src/e2e.c | 363 | ||||
-rw-r--r-- | src/e2e.h | 3 | ||||
-rw-r--r-- | src/main.c | 55 |
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 |