commit 041d3dd55e0d7d5a0f01a15b1bcfc3e63a70b661
Author: Louis Burda <quent.burda@gmail.com>
Date: Sun, 12 Feb 2023 04:28:49 +0100
Initial prototype with raylib
Diffstat:
A | .gitignore | | | 2 | ++ |
A | Makefile | | | 11 | +++++++++++ |
A | ntrop.c | | | 337 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 350 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+ntrop
+.gdb_history
diff --git a/Makefile b/Makefile
@@ -0,0 +1,11 @@
+CFLAGS = -Wunused-variable -Wunused-function
+LDLIBS = -lraylib -lm
+
+all: ntrop
+
+clean:
+ rm -f ntrop
+
+ntrop: ntrop.c
+
+.PHONY: all clean
diff --git a/ntrop.c b/ntrop.c
@@ -0,0 +1,337 @@
+#include "raylib.h"
+
+#include <err.h>
+#include <math.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define CHUNK_SIZE 4096
+#define ZOOM_MAX 64
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) > (b) ? (b) : (a))
+
+const char *file_path;
+uint8_t *file_data;
+size_t data_len;
+size_t data_width;
+size_t data_height;
+
+Color *value_colors;
+Color *entropy_colors;
+
+int entropy_ctx;
+
+double zoom;
+double zoom_x, zoom_y;
+
+int window_width;
+int window_height;
+char *window_title;
+bool show_pos;
+bool show_value;
+
+int mouse_x, mouse_y;
+
+int drag_mouse_x, drag_mouse_y;
+double drag_zoom_x, drag_zoom_y;
+bool drag;
+
+char fmtbuf[256];
+
+void
+usage(void)
+{
+ printf("Usage: ntrop FILE\n");
+ exit(0);
+}
+
+uint8_t *
+readfile(const char *path, size_t *len)
+{
+ FILE *file;
+ char *chunk;
+ uint8_t *data;
+ size_t nread;
+ size_t cap;
+
+ chunk = malloc(CHUNK_SIZE);
+ if (!chunk) err(1, "malloc");
+
+ file = fopen(file_path, "r");
+ if (!file) err(1, "fopen");
+
+ cap = 16 * 1024;
+ data = malloc(cap);
+ if (!data) err(1, "malloc");
+
+ *len = 0;
+ while ((nread = fread(chunk, 1, CHUNK_SIZE, file))) {
+ if (*len + nread > cap) {
+ cap *= 2;
+ data = realloc(data, cap);
+ if (!data) err(1, "realloc");
+ }
+ memcpy(data + *len, chunk, nread);
+ *len += nread;
+ }
+
+ fclose(file);
+
+ return data;
+}
+
+void
+init_value_colors(Color *data_colors)
+{
+ size_t pos;
+ Color c;
+
+ c.a = 255;
+ for (pos = 0; pos < data_len; pos++) {
+ c.r = file_data[pos];
+ c.g = file_data[pos];
+ c.b = file_data[pos];
+ data_colors[pos] = c;
+ }
+}
+
+void
+init_entropy_colors(Color *data_colors)
+{
+ ssize_t i, k, pos, uniq;
+ uint8_t *vals;
+ size_t *counts;
+ size_t ctx_count;
+ double entropy;
+ Color c;
+
+ vals = malloc(entropy_ctx * 2 + 1);
+ if (!vals) err(1, "malloc");
+
+ counts = malloc(sizeof(size_t) * (entropy_ctx * 2 + 1));
+ if (!counts) err(1, "malloc");
+
+ c.a = 255;
+ for (pos = 0; pos < data_len; pos++) {
+ uniq = 0;
+ ctx_count = entropy_ctx * 2 + 1;
+ for (i = -entropy_ctx; i <= entropy_ctx; i++) {
+ if (pos + i < 0 || pos + i > data_len) {
+ ctx_count--;
+ continue;
+ }
+ for (k = 0; k < uniq; k++) {
+ if (vals[k] == file_data[pos + i]) {
+ counts[k] += 1;
+ break;
+ }
+ }
+ if (k == uniq) {
+ counts[uniq] = 1;
+ vals[uniq] = file_data[pos + i];
+ uniq += 1;
+ }
+ }
+ entropy = 0;
+ for (k = 0; k < uniq; k++) {
+ entropy += - 1.F * counts[k] / ctx_count
+ * log2(1.F * counts[k] / ctx_count);
+ }
+ entropy /= - 1.F * log2(1.F / ctx_count);
+ entropy *= 255;
+ c.r = entropy;
+ c.g = entropy;
+ c.b = entropy;
+ data_colors[pos] = c;
+ }
+
+ free(vals);
+ free(counts);
+}
+
+void
+vis(void)
+{
+ Color *data_colors;
+ size_t pos, len;
+ double mouse_move;
+ size_t data_x, data_y;
+ size_t data_pos;
+ size_t x, y;
+
+ data_colors = value_colors;
+ show_value = true;
+
+ window_title = malloc(1024);
+ if (!window_title) err(1, "malloc");
+ snprintf(window_title, 1024, "ntrop %s", file_path);
+
+ SetConfigFlags(FLAG_WINDOW_RESIZABLE);
+ SetTargetFPS(60);
+
+ SetTraceLogLevel(LOG_NONE);
+
+ window_width = 800;
+ window_height = 600;
+ InitWindow(window_width, window_height, window_title);
+
+ data_width = window_width / 2;
+ data_height = data_len / data_width + !!(data_len % data_width);
+ zoom_x = 0;
+ zoom_y = 0;
+ zoom = 1;
+
+ show_pos = false;
+ while (!WindowShouldClose()) {
+ if (IsKeyPressed(KEY_Q))
+ break;
+
+ if (IsWindowResized()) {
+ window_width = GetScreenWidth();
+ window_height = GetScreenHeight();
+ data_width = MIN(window_width / 2, 400);
+ data_height = data_len / data_width + !!(data_len % data_width);
+ zoom_x = (data_width - window_width / zoom) / 2.F;
+ zoom_y = (data_height - window_height / zoom) / 2.F;
+ }
+
+ mouse_x = MIN(MAX(0, GetMouseX()), window_width);
+ mouse_y = MIN(MAX(0, GetMouseY()), window_height);
+
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
+ drag_mouse_x = mouse_x;
+ drag_mouse_y = mouse_y;
+ drag_zoom_x = zoom_x;
+ drag_zoom_y = zoom_y;
+ drag = true;
+ }
+ if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
+ zoom_x = drag_zoom_x - (mouse_x - drag_mouse_x) * 1.F / zoom;
+ zoom_y = drag_zoom_y - (mouse_y - drag_mouse_y) * 1.F / zoom;
+ } else {
+ drag = false;
+ }
+
+ mouse_move = GetMouseWheelMove();
+ if (mouse_move > 0 && zoom < ZOOM_MAX) {
+ zoom_x += (1.F * window_width / zoom / 2)
+ * (1.F * mouse_x / window_width);
+ zoom_y += (1.F * window_height / zoom / 2)
+ * (1.F * mouse_y / window_height);
+ zoom *= 2;
+ } else if (mouse_move < 0 && zoom > 1) {
+ zoom_x -= (1.F * window_width / zoom)
+ * (1.F * mouse_x / window_width);
+ zoom_y -= (1.F * window_height / zoom)
+ * (1.F * mouse_y / window_height);
+ zoom /= 2;
+ }
+
+ show_pos ^= IsKeyPressed(KEY_T);
+
+ if (IsKeyDown(KEY_LEFT))
+ zoom_x -= 20.F / zoom;
+ else if (IsKeyDown(KEY_RIGHT))
+ zoom_x += 20.F / zoom;
+
+ if (IsKeyDown(KEY_UP))
+ zoom_y -= 20.F / zoom;
+ else if (IsKeyDown(KEY_DOWN))
+ zoom_y += 20.F / zoom;
+
+ if (IsKeyPressed(KEY_P)) {
+ show_value = !show_value;
+ if (show_value)
+ data_colors = value_colors;
+ else
+ data_colors = entropy_colors;
+ }
+
+ BeginDrawing();
+
+ ClearBackground(BLACK);
+
+ for (y = 0; y < window_height; y++) {
+ data_y = (size_t) (zoom_y + y / zoom);
+ if (data_y < 0 || data_y >= data_height)
+ continue;
+
+ for (x = 0; x < window_width; x++) {
+ data_x = (size_t) (zoom_x + x / zoom);
+ if (data_x < 0 || data_x >= data_width)
+ continue;
+
+ data_pos = data_y * data_width + data_x;
+ if (data_pos < data_len)
+ DrawPixel(x, y, data_colors[data_pos]);
+ }
+ }
+
+ if (show_pos) {
+ data_x = zoom_x + mouse_x / zoom;
+ data_y = zoom_y + mouse_y / zoom;
+ if (data_x >= 0 && data_x < data_width
+ && data_y >= 0 && data_y < data_height) {
+ pos = data_y * data_width + data_x;
+ snprintf(fmtbuf, sizeof(fmtbuf), "0x%08lx", pos);
+ len = MeasureText(fmtbuf, 20);
+ DrawRectangle(window_width - len - 6, 0,
+ len + 6, 19, WHITE);
+ DrawText(fmtbuf, window_width - len - 3,
+ 0, 20, BLACK);
+ }
+ }
+
+ EndDrawing();
+ }
+
+ CloseWindow();
+ free(window_title);
+}
+
+int
+main(int argc, const char **argv)
+{
+ const char **arg;
+
+ file_path = NULL;
+ entropy_ctx = 1;
+ for (arg = argv + 1; *arg; arg++) {
+ if (!strcmp("-c", *arg)) {
+ arg += 1;
+ if (!arg) usage();
+ entropy_ctx = atoi(*arg);
+ if (entropy_ctx <= 0) usage();
+ } else if (!file_path) {
+ file_path = *arg;
+ } else {
+ usage();
+ }
+ }
+
+ if (!file_path)
+ usage();
+
+ if (!strcmp(file_path, "-"))
+ file_path = "/dev/stdin";
+
+ file_data = readfile(file_path, &data_len);
+
+ value_colors = malloc(sizeof(Color) * data_len);
+ if (!value_colors) err(1, "malloc");
+ init_value_colors(value_colors);
+
+ entropy_colors = malloc(sizeof(Color) * data_len);
+ if (!entropy_colors) err(1, "malloc");
+ init_entropy_colors(entropy_colors);
+
+ vis();
+
+ free(value_colors);
+ free(entropy_colors);
+
+ free(file_data);
+}