ntrop.c (11911B)
1#include "raylib.h" 2 3#include <err.h> 4#include <math.h> 5#include <string.h> 6#include <stdint.h> 7#include <stdio.h> 8#include <stdlib.h> 9 10#define CHUNK_SIZE 4096 11#define ZOOM_MAX 64 12 13#define MAX(a, b) ((a) > (b) ? (a) : (b)) 14#define MIN(a, b) ((a) > (b) ? (b) : (a)) 15 16enum { PRESS = 1, HOLD }; 17 18const char *file_path; 19uint8_t *file_data; 20ssize_t data_len; 21ssize_t data_width; 22ssize_t data_height; 23 24ssize_t data_window_start; 25ssize_t data_window_len; 26 27Color *value_colors; 28Color *entropy_colors; 29 30int entropy_ctx; 31 32int zoom; 33double zoom_x, zoom_y; 34 35int bar_width; 36int bar_zoom; 37ssize_t bar_start; 38bool show_bar; 39 40int window_width; 41int window_height; 42char *window_title; 43bool show_pos; 44bool show_value; 45 46int mouse_x, mouse_y; 47 48int drag_mouse_x, drag_mouse_y; 49double drag_zoom_x, drag_zoom_y; 50bool drag; 51 52uint64_t hold_times[337] = { 0 }; 53 54char fmtbuf[256]; 55 56void 57usage(void) 58{ 59 printf("Usage: ntrop [-c CTX] FILE\n"); 60 exit(0); 61} 62 63uint8_t * 64read_file(const char *path, ssize_t *len) 65{ 66 FILE *file; 67 char *chunk; 68 uint8_t *data; 69 ssize_t nread; 70 size_t cap; 71 72 chunk = malloc(CHUNK_SIZE); 73 if (!chunk) err(1, "malloc"); 74 75 file = fopen(file_path, "r"); 76 if (!file) err(1, "fopen"); 77 78 cap = 16 * 1024; 79 data = malloc(cap); 80 if (!data) err(1, "malloc"); 81 82 *len = 0; 83 while ((nread = fread(chunk, 1, CHUNK_SIZE, file)) > 0) { 84 if (*len + nread > cap) { 85 cap *= 2; 86 data = realloc(data, cap); 87 if (!data) err(1, "realloc"); 88 } 89 memcpy(data + *len, chunk, nread); 90 *len += nread; 91 } 92 93 fclose(file); 94 95 return data; 96} 97 98void 99init_value_colors(Color *data_colors) 100{ 101 size_t pos; 102 Color c; 103 104 c.a = 255; 105 for (pos = 0; pos < data_len; pos++) { 106 c.r = file_data[pos]; 107 c.g = file_data[pos]; 108 c.b = file_data[pos]; 109 data_colors[pos] = c; 110 } 111} 112 113void 114init_entropy_colors(Color *data_colors) 115{ 116 ssize_t i, k, pos, uniq; 117 uint8_t *vals; 118 size_t *counts; 119 size_t ctx_count; 120 double entropy; 121 Color c; 122 123 vals = malloc(entropy_ctx * 2 + 1); 124 if (!vals) err(1, "malloc"); 125 126 counts = malloc(sizeof(size_t) * (entropy_ctx * 2 + 1)); 127 if (!counts) err(1, "malloc"); 128 129 c.a = 255; 130 for (pos = 0; pos < data_len; pos++) { 131 uniq = 0; 132 ctx_count = 0; 133 for (i = -entropy_ctx; i <= entropy_ctx; i++) { 134 if (pos + i < 0 || pos + i > data_len) 135 continue; 136 for (k = 0; k < uniq; k++) { 137 if (vals[k] == file_data[pos + i]) { 138 counts[k] += 1; 139 break; 140 } 141 } 142 if (k == uniq) { 143 counts[uniq] = 1; 144 vals[uniq] = file_data[pos + i]; 145 uniq += 1; 146 } 147 ctx_count++; 148 } 149 entropy = 0; 150 for (k = 0; k < uniq; k++) { 151 entropy += - 1.F * counts[k] / ctx_count 152 * log2(1.F * counts[k] / ctx_count); 153 } 154 entropy /= - 1.F * log2(1.F / ctx_count); 155 entropy *= 255; 156 c.r = file_data[pos]; 157 c.g = 0; 158 c.b = entropy; 159 data_colors[pos] = c; 160 } 161 162 free(vals); 163 free(counts); 164} 165 166int 167key_press_hold(int cnt, ...) 168{ 169 va_list ap; 170 int i, key; 171 172 va_start(ap, cnt); 173 for (i = 0; i < cnt; i++) { 174 key = va_arg(ap, int); 175 176 if (!IsKeyDown(key)) { 177 hold_times[key] = 0; 178 continue; 179 } 180 181 if (IsKeyPressed(key)) 182 return PRESS; 183 184 hold_times[key] += 1; 185 if (hold_times[key] > 10) 186 return HOLD; 187 } 188 189 return 0; 190} 191 192void 193update_data_window_start(void) 194{ 195 ssize_t len; 196 197 len = data_len - data_window_len; 198 if (len % data_width) 199 len += data_width - (len % data_width); 200 data_window_start = MAX(0, MIN(len, data_window_start)); 201} 202 203void 204update_data_window_len(void) 205{ 206 update_data_window_start(); 207 data_window_len = MIN(data_len - data_window_start, data_window_len); 208 data_window_len = MAX(0, data_window_len); 209 data_height = data_window_len / data_width; 210 211 zoom_y = (data_height - 1.F * window_height / zoom) / 2.F; 212} 213 214void 215update_data_width(void) 216{ 217 218 data_width = MAX(1, data_width); 219 update_data_window_len(); 220 221 if (show_bar) { 222 zoom_x = - 1.F * bar_width / zoom 223 - (1.F * (window_width - bar_width) / zoom - data_width) / 2.F; 224 } else { 225 zoom_x = -(1.F * window_width / zoom - data_width) / 2.F; 226 } 227} 228 229void 230vis(void) 231{ 232 Color *data_colors; 233 Image window_image; 234 int window_init_frames; 235 ssize_t pos, len; 236 double mouse_move; 237 ssize_t data_x, data_y; 238 ssize_t data_pos; 239 ssize_t x, y; 240 int b; 241 242 data_colors = value_colors; 243 show_value = true; 244 245 window_title = malloc(1024); 246 if (!window_title) err(1, "malloc"); 247 snprintf(window_title, 1024, "ntrop %s", file_path); 248 249 SetConfigFlags(FLAG_WINDOW_RESIZABLE); 250 SetTargetFPS(60); 251 252 SetTraceLogLevel(LOG_NONE); 253 254 InitWindow(800, 600, window_title); 255 256 window_image = GenImageColor(800, 600, (Color) { 0 }); 257 258 zoom = 2; 259 bar_zoom = 4; 260 bar_start = 0; 261 bar_width = MAX(80, window_width / 7); 262 show_bar = true; 263 264 data_window_start = 0; 265 data_window_len = data_len; 266 267 show_pos = false; 268 269 window_init_frames = 2; 270 while (!WindowShouldClose()) { 271 if (IsKeyPressed(KEY_Q)) 272 break; 273 274 if (window_init_frames || IsWindowResized() || IsKeyPressed(KEY_G)) { 275 window_width = GetScreenWidth(); 276 window_height = GetScreenHeight(); 277 bar_width = MAX(80, window_width / 7); 278 if (window_init_frames > 0) { 279 data_width = MAX(1, MIN(data_len, 280 (window_width - bar_width) / zoom / 2)); 281 data_window_len = MIN(data_len, 282 MAX(window_width / 2 / zoom 283 * window_height / 2 / zoom, 200)); 284 data_height = data_window_len / data_width 285 + !!(data_window_len % data_width); 286 } 287 if (IsKeyPressed(KEY_G)) 288 zoom = 2; 289 update_data_width(); 290 ImageResize(&window_image, window_width, window_height); 291 if (window_init_frames) 292 window_init_frames -= 1; 293 } 294 295 mouse_x = MIN(MAX(0, GetMouseX()), window_width); 296 mouse_y = MIN(MAX(0, GetMouseY()), window_height); 297 298 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { 299 drag_mouse_x = mouse_x; 300 drag_mouse_y = mouse_y; 301 drag_zoom_x = zoom_x; 302 drag_zoom_y = zoom_y; 303 drag = true; 304 } else if (!IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { 305 drag = false; 306 } 307 308 if (drag && show_bar && drag_mouse_x < bar_width) { 309 if (mouse_y > window_height - 20) { 310 bar_start += bar_zoom * bar_width * 20; 311 } else if (mouse_y < 20) { 312 bar_start -= bar_zoom * bar_width * 20; 313 } 314 bar_start = MAX(0, MIN(data_len - bar_width 315 * window_height * bar_zoom, bar_start)); 316 317 data_window_start = bar_start + (mouse_y * bar_width + mouse_x) * bar_zoom; 318 data_window_start = MAX(0, MIN(data_len - data_window_len, 319 data_window_start - data_window_len / 2)); 320 update_data_window_start(); 321 } else if (drag) { 322 zoom_x = drag_zoom_x - (mouse_x - drag_mouse_x) * 1.F / zoom; 323 zoom_y = drag_zoom_y - (mouse_y - drag_mouse_y) * 1.F / zoom; 324 } 325 326 mouse_move = GetMouseWheelMove(); 327 if (mouse_move > 0 && zoom < ZOOM_MAX) { 328 zoom_x += (1.F * window_width / zoom / 2) 329 * (1.F * mouse_x / window_width); 330 zoom_y += (1.F * window_height / zoom / 2) 331 * (1.F * mouse_y / window_height); 332 zoom *= 2; 333 } else if (mouse_move < 0 && zoom > 1) { 334 zoom_x -= (1.F * window_width / zoom) 335 * (1.F * mouse_x / window_width); 336 zoom_y -= (1.F * window_height / zoom) 337 * (1.F * mouse_y / window_height); 338 zoom /= 2; 339 } 340 341 show_pos ^= IsKeyPressed(KEY_T); 342 show_bar ^= IsKeyPressed(KEY_B); 343 344 if (IsKeyDown(KEY_LEFT_SHIFT)) { 345 if ((b = key_press_hold(2, KEY_LEFT, KEY_A))) { 346 data_width -= IsKeyDown(KEY_LEFT_ALT) ? 5 : 1; 347 update_data_width(); 348 } else if ((b = key_press_hold(2, KEY_RIGHT, KEY_D))) { 349 data_width += IsKeyDown(KEY_LEFT_ALT) ? 5 : 1; 350 update_data_width(); 351 } 352 } else if (IsKeyDown(KEY_LEFT_ALT)) { 353 if ((b = key_press_hold(2, KEY_LEFT, KEY_A))) { 354 data_window_start -= data_width 355 * (b == HOLD ? 8 : 1); 356 update_data_window_start(); 357 } else if ((b = key_press_hold(2, KEY_RIGHT, KEY_D))) { 358 data_window_start += data_width 359 * (b == HOLD ? 8 : 1); 360 update_data_window_start(); 361 } 362 if ((b = key_press_hold(2, KEY_UP, KEY_W))) { 363 data_window_len -= data_width 364 * (b == HOLD ? 8 : 1); 365 update_data_window_len(); 366 } else if ((b = key_press_hold(2, KEY_DOWN, KEY_S))) { 367 data_window_len += data_width 368 * (b == HOLD ? 8 : 1); 369 update_data_window_len(); 370 } 371 } else { 372 if (key_press_hold(2, KEY_LEFT, KEY_A)) { 373 zoom_x -= 20.F / zoom; 374 } else if (key_press_hold(2, KEY_RIGHT, KEY_D)) { 375 zoom_x += 20.F / zoom; 376 } 377 378 if (key_press_hold(2, KEY_UP, KEY_W)) { 379 zoom_y -= 20.F / zoom; 380 } else if (key_press_hold(2, KEY_DOWN, KEY_S)) { 381 zoom_y += 20.F / zoom; 382 } 383 } 384 385 386 if (IsKeyPressed(KEY_P)) { 387 show_value = !show_value; 388 if (show_value) 389 data_colors = value_colors; 390 else 391 data_colors = entropy_colors; 392 } 393 394 ImageClearBackground(&window_image, BLACK); 395 396 for (y = 0; y < window_height; y++) { 397 data_y = (ssize_t) (zoom_y) + (y / zoom); 398 if (data_y < 0 || data_y >= data_height) 399 continue; 400 401 for (x = 0; x < window_width; x++) { 402 data_x = (ssize_t) (zoom_x) + (x / zoom); 403 if (data_x < 0 || data_x >= data_width) 404 continue; 405 406 data_pos = data_y * data_width + data_x; 407 data_pos += data_window_start; 408 len = data_window_start + data_window_len; 409 if (len % window_width) 410 len += window_width - (len % window_width); 411 if (data_pos < len && data_pos < data_len) { 412 ImageDrawPixel(&window_image, 413 x, y, data_colors[data_pos]); 414 } 415 } 416 } 417 418 if (show_bar) { 419 ImageDrawRectangle(&window_image, 420 0, 0, bar_width, window_height, BLACK); 421 ImageDrawRectangle(&window_image, bar_width, 0, 422 3, window_height, BLACK); 423 ImageDrawLine(&window_image, bar_width + 1, 0, 424 bar_width + 1, window_height, GRAY); 425 426 for (y = 0; y < window_height; y++) { 427 for (x = 0; x < bar_width; x++) { 428 data_pos = bar_start + (y * bar_width + x) * bar_zoom; 429 if (data_pos < data_len) { 430 ImageDrawPixel(&window_image, 431 x, y, value_colors[data_pos]); 432 } 433 } 434 } 435 436 pos = (data_window_start - bar_start) / bar_width / bar_zoom; 437 ImageDrawLine(&window_image, 0, pos, 438 bar_width, pos, WHITE); 439 440 pos = MIN(data_len, data_window_start + data_window_len); 441 pos = (pos - bar_start) / bar_width / bar_zoom; 442 ImageDrawLine(&window_image, 0, pos, 443 bar_width, pos, WHITE); 444 } 445 446 if (show_pos) { 447 data_x = (ssize_t) zoom_x + (mouse_x / zoom); 448 data_y = (ssize_t) zoom_y + (mouse_y / zoom); 449 if (data_x >= 0 && data_x < data_width 450 && data_y >= 0 && data_y < data_height) { 451 pos = data_window_start + data_y * data_width + data_x; 452 snprintf(fmtbuf, sizeof(fmtbuf), "%08lx", pos); 453 len = MeasureText(fmtbuf, 20) + 10; 454 ImageDrawRectangle(&window_image, 455 window_width - len - 9, 0, 456 len + 9, 19, WHITE); 457 ImageDrawText(&window_image, 458 fmtbuf, window_width - len - 6, 459 0, 20, BLACK); 460 if (pos < data_len) { 461 snprintf(fmtbuf, sizeof(fmtbuf), "%02x", 462 file_data[pos]); 463 len = MeasureText(fmtbuf, 20); 464 ImageDrawRectangle(&window_image, 465 window_width - len - 6, 19, 466 len + 6, 19, WHITE); 467 ImageDrawText(&window_image, 468 fmtbuf, window_width - len - 3, 469 20, 19, BLACK); 470 } 471 } 472 } 473 474 Texture2D tex = LoadTextureFromImage(window_image); 475 476 BeginDrawing(); 477 478 ClearBackground(BLACK); 479 480 DrawTexture(tex, 0, 0, WHITE); 481 482 EndDrawing(); 483 484 UnloadTexture(tex); 485 } 486 487 UnloadImage(window_image); 488 489 CloseWindow(); 490 491 free(window_title); 492} 493 494int 495main(int argc, const char **argv) 496{ 497 const char **arg; 498 499 file_path = NULL; 500 entropy_ctx = 32; 501 for (arg = argv + 1; *arg; arg++) { 502 if (!strcmp("-c", *arg)) { 503 arg += 1; 504 if (!arg) usage(); 505 entropy_ctx = atoi(*arg); 506 if (entropy_ctx <= 0) usage(); 507 } else if (!file_path) { 508 file_path = *arg; 509 } else { 510 usage(); 511 } 512 } 513 514 if (!file_path) 515 usage(); 516 517 if (!strcmp(file_path, "-")) 518 file_path = "/dev/stdin"; 519 520 file_data = read_file(file_path, &data_len); 521 522 value_colors = malloc(sizeof(Color) * data_len); 523 if (!value_colors) err(1, "malloc"); 524 init_value_colors(value_colors); 525 526 entropy_colors = malloc(sizeof(Color) * data_len); 527 if (!entropy_colors) err(1, "malloc"); 528 init_entropy_colors(entropy_colors); 529 530 vis(); 531 532 free(value_colors); 533 free(entropy_colors); 534 535 free(file_data); 536}