aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2023-03-14 15:17:27 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2023-03-14 15:17:27 +0100
commit3443df8b41f959cceddd0cbc4d9ce32129d1a4d8 (patch)
tree1c64ce64d12602ca0194c09417fed9053d099ce5
parentInitial commit. (diff)
downloadhex-viewer-3443df8b41f959cceddd0cbc4d9ce32129d1a4d8.tar.gz
hex-viewer-3443df8b41f959cceddd0cbc4d9ce32129d1a4d8.tar.xz
code
-rw-r--r--Makefile6
-rw-r--r--analyze-gz.c280
-rw-r--r--ui.c167
3 files changed, 453 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1221184
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+CFLAGS = -Wall -std=c11 -ggdb
+LDLIBS = -lncurses
+
+analyze-gc: analyze-gz.c
+
+ui: ui.c
diff --git a/analyze-gz.c b/analyze-gz.c
new file mode 100644
index 0000000..a0c6d40
--- /dev/null
+++ b/analyze-gz.c
@@ -0,0 +1,280 @@
+#define _POSIX_C_SOURCE 200809L
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <ctype.h>
+
+#define ID1 31
+#define ID2 139
+
+enum compression_method {
+ DEFLATE = 8,
+};
+
+enum flag {
+ FTEXT = 1 << 0,
+ FHCRC = 1 << 1,
+ FEXTRA = 1 << 2,
+ FNAME = 1 << 3,
+ FCOMMENT = 1 << 4,
+};
+
+enum operating_system {
+ FAT = 0,
+ AMIGA,
+ VMS,
+ UNIX,
+ CMS,
+ ATARI_TOS,
+ HPFS,
+ MACINTOSH,
+ Z_SYSTEM,
+ CP_M,
+ TOPS20,
+ NTFS,
+ QDOS,
+ ACORN_RISCOS,
+};
+
+const char *os_str(enum operating_system os) {
+ switch (os) {
+ case FAT: return "FAT filesystem (MS-DOS, OS/2, NT/Win32)";
+ case AMIGA: return "Amiga";
+ case VMS: return "VMS (or OpenVMS)";
+ case UNIX: return "Unix";
+ case CMS: return "VM/CMS";
+ case ATARI_TOS: return "Atari TOS";
+ case HPFS: return "HPFS filesystem (OS/2, NT)";
+ case MACINTOSH: return "Macintosh";
+ case Z_SYSTEM: return "Z-System";
+ case CP_M: return "CP/M";
+ case TOPS20: return "TOPS-20";
+ case NTFS: return "NTFS filesystem (NT)";
+ case QDOS: return "QDOS";
+ case ACORN_RISCOS: return "Acorn RISCOS";
+ default: return "unknown";
+ }
+}
+
+struct member {
+ uint8_t id1, id2, cm, flg;
+ uint32_t mtime;
+ uint8_t xfl, os;
+} __attribute__((packed));
+
+
+enum color {
+ BLACK = 0,
+ RED,
+ GREEN,
+ YELLOW,
+ BLUE,
+ PURPLE,
+ CYAN,
+ WHITE,
+};
+
+
+struct segment {
+ enum color c;
+ size_t len;
+ uint8_t *data;
+};
+
+enum color next_color(void) {
+ static uint8_t cc = 0;
+ cc += 1;
+ cc %= 6;
+ return cc + 1;
+}
+
+void color_put(enum color c) {
+ printf("\x1b[0;3%im", c);
+}
+
+void color_reset(void) {
+ printf("\x1b[m");
+}
+
+#define LINELEN 0x8
+struct segment line_segments[LINELEN];
+size_t file_idx = 0;
+size_t line_idx = 0;
+size_t current_segment = 0;
+
+void cleanup_line(void) {
+ for (int i = 0; i < current_segment; i++) {
+ free(line_segments[i].data);
+ }
+}
+
+void put_byte(uint8_t byte) {
+ static bool flip = 0;
+ printf("%02x", byte);
+ flip = !flip;
+ if (! flip) {
+ printf(" ");
+ }
+}
+
+void flush_line() {
+ printf("%06lx: ", file_idx);
+ file_idx += LINELEN;
+ for (int i = 0; i < current_segment; i++) {
+ color_put(line_segments[i].c);
+ for (int b = 0; b < line_segments[i].len; b++) {
+ put_byte(line_segments[i].data[b]);
+ }
+ color_reset();
+ }
+ for (int i = 0; i < current_segment; i++) {
+ color_put(line_segments[i].c);
+ for (int b = 0; b < line_segments[i].len; b++) {
+ uint8_t ch = line_segments[i].data[b];
+ if (isprint(ch)) {
+ printf("%c", ch);
+ } else {
+ printf(".");
+ }
+ }
+ color_reset();
+ }
+ printf("\n");
+}
+
+void add_segment(struct segment segment) {
+ line_segments[current_segment++] = segment;
+ line_idx += segment.len;
+ if (line_idx == LINELEN) {
+ flush_line();
+ cleanup_line();
+ current_segment = 0;
+ line_idx = 0;
+ }
+}
+
+/*
+ * Buf MUST be a malloc:ed buffer, which is invalidated on this call.
+ */
+void write_chunk(uint8_t *buf, size_t len) {
+ if (line_idx + len >= LINELEN) {
+ enum color c = next_color();
+ size_t len1 = LINELEN - line_idx,
+ len2 = len - len1;
+ uint8_t *buf1 = malloc(len1),
+ *buf2 = malloc(len2);
+ memcpy(buf1, buf, len1);
+ memcpy(buf2, buf + len1, len2);
+ free(buf);
+ struct segment
+ seg1 = {
+ .c = c,
+ .len = len1,
+ .data = buf1,
+ },
+ seg2 = {
+ .c = c,
+ .len = len2,
+ .data = buf2,
+ };
+ add_segment(seg1);
+ add_segment(seg2);
+ } else {
+ struct segment seg = {
+ .c = next_color(),
+ .len = len,
+ .data = buf,
+ };
+ add_segment(seg);
+ }
+}
+
+void write_chunk_s(uint8_t *buf, size_t len) {
+ assert (len >= 0);
+ uint8_t *true_buf = malloc(len);
+ memcpy(true_buf, buf, len);
+ write_chunk(true_buf, len);
+}
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ fprintf(stderr, "Usage: %s <filename.gz>\n", argv[0]);
+ return 1;
+ }
+
+ FILE *f = fopen(argv[1], "rb");
+ size_t len;
+
+ struct member header;
+ uint8_t *extra_fields = NULL;
+ uint16_t crc16 = 0;
+ char *filename = NULL;
+ char *file_comment = NULL;
+
+ len = fread(&header, sizeof(header), 1, f);
+
+ if (len != 1) {
+ fprintf(stderr, "Unexpected end of header\n");
+ return 1;
+ }
+
+ write_chunk_s((uint8_t[2]) {header.id1, header.id2}, 2);
+ write_chunk_s(&header.cm, 1);
+ write_chunk_s(&header.flg, 1);
+ {
+ size_t s = sizeof(header.mtime);
+ uint8_t *buf = malloc(s);
+ memcpy(buf, &header.mtime, s);
+ write_chunk(buf, s);
+ }
+ write_chunk_s(&header.xfl, 1);
+ write_chunk_s(&header.os, 1);
+
+ if (header.flg & FEXTRA) {
+ uint16_t xlen;
+ len = fread(&xlen, sizeof(xlen), 1, f);
+ {
+ uint8_t *buf = malloc(len);
+ memcpy(buf, &xlen, len);
+ write_chunk(buf, len);
+ }
+ extra_fields = malloc(xlen);
+ len = fread(extra_fields, 1, xlen, f);
+ write_chunk(extra_fields, xlen);
+ }
+
+ if (header.flg & FNAME) {
+ size_t n;
+ len = getdelim(&filename, &n, '\0', f);
+ write_chunk((uint8_t*) filename, n);
+ }
+
+ if (header.flg & FCOMMENT) {
+ size_t n;
+ len = getdelim(&file_comment, &n, '\0', f);
+ write_chunk((uint8_t*) file_comment, n);
+ }
+
+ if (header.flg & FHCRC) {
+ len = fread(&crc16, sizeof(crc16), 1, f);
+ {
+ uint8_t *buf = malloc(len);
+ memcpy(buf, &crc16, len);
+ write_chunk(buf, len);
+ }
+ }
+
+// cleanup:
+ if (extra_fields) free(extra_fields);
+ if (filename) free(filename);
+ if (file_comment) free(file_comment);
+ flush_line();
+ cleanup_line();
+
+ printf("\n");
+
+
+}
diff --git a/ui.c b/ui.c
new file mode 100644
index 0000000..f114a41
--- /dev/null
+++ b/ui.c
@@ -0,0 +1,167 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <ncurses.h>
+
+#define LINE_LEN 0x10
+
+// int screen_height, screen_width;
+
+struct screen {
+ WINDOW *lineno, *hex, *chr;
+ int top_address;
+ void *mem;
+ size_t mem_len;
+ int hx, hy;
+} screen = {
+ .hx = -1, .hy = -1,
+};
+
+void draw_lines() {
+ for (int y = 0; y < LINES; y++) {
+ mvwprintw(screen.lineno, y, 0, "%08x:", screen.top_address + y * LINE_LEN);
+ wrefresh(screen.lineno);
+ }
+}
+
+void draw_hex() {
+ for (int y = 0; y < LINES; y++) {
+ for (int x = 0; x < LINE_LEN; x++) {
+ if (screen.hy == y && screen.hx == x) {
+ wattron(screen.hex, A_REVERSE);
+ }
+ mvwprintw(screen.hex, y, x * 2 + (x/2), "%02x",
+ 0xFF & ((char*) screen.mem)[screen.top_address + y * LINE_LEN + x]);
+ if (screen.hy == y && screen.hx == x) {
+ wattroff(screen.hex, A_REVERSE);
+ }
+ }
+ wrefresh(screen.hex);
+ }
+}
+
+void draw_chr() {
+ for (int y = 0; y < LINES; y++) {
+ for (int x = 0; x < LINE_LEN; x++) {
+ if (screen.hy == y && screen.hx == x) {
+ wattron(screen.chr, A_REVERSE);
+ }
+ char c = ((char*) screen.mem)[screen.top_address + y * LINE_LEN + x];
+ mvwprintw(screen.chr, y, x, "%c", isprint(c) ? c : '.');
+ if (screen.hy == y && screen.hx == x) {
+ wattroff(screen.chr, A_REVERSE);
+ }
+ }
+ wrefresh(screen.chr);
+ }
+}
+
+void draw_screen() {
+
+ draw_lines();
+ draw_hex();
+ draw_chr();
+ /* TODO end at end of file */
+}
+
+int main() {
+
+ /* Terminal supporting mouse movements */
+ setenv("TERM", "xterm-1003", true);
+
+ initscr();
+ clear();
+ noecho();
+ cbreak();
+ curs_set(0);
+
+ start_color();
+
+ // getmaxyx(stdscr, screen_height, screen_width);
+
+ keypad(stdscr, true);
+
+ screen.lineno = newwin(0, 10, 0, 0);
+ screen.hex = newwin(0, (int) ceil(LINE_LEN * 2.5), 0, 10);
+ screen.chr = newwin(0, LINE_LEN, 0, 10 + (int) ceil(LINE_LEN * 2.5));
+
+ int fd = open("payload.gz", O_RDONLY);
+ struct stat statbuf;
+ if (fstat(fd, &statbuf) == -1) {
+ goto end;
+ }
+ screen.mem_len = statbuf.st_size;
+ screen.mem = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+
+ mousemask(ALL_MOUSE_EVENTS|REPORT_MOUSE_POSITION, NULL);
+
+ refresh();
+
+ draw_screen();
+
+ int ch;
+ MEVENT event;
+ while (1) {
+ ch = wgetch(stdscr);
+ switch (ch) {
+ case KEY_MOUSE:
+ wmove(stdscr, 0, 0);
+ switch (getmouse(&event)) {
+ case OK:
+ if (event.bstate & BUTTON4_PRESSED) {
+ screen.top_address -= LINE_LEN;
+ }
+ if (event.bstate & BUTTON5_PRESSED) {
+ screen.top_address += LINE_LEN;
+ }
+
+ if (event.x >= 10 && event.x < 10 + (int) ceil(LINE_LEN * 2.5)) {
+ screen.hx = (int) ((event.x - 10) / 2.5);
+ screen.hy = event.y;
+ } else if (event.x >= 10 + (int) ceil(LINE_LEN * 2.5) && event.x < (int) ceil(LINE_LEN * 2.5) + 10 + LINE_LEN) {
+ screen.hx = event.x - (10 + (int) ceil(LINE_LEN * 2.5));
+ screen.hy = event.y;
+ } else {
+ screen.hx = -1;
+ screen.hy = -1;
+ }
+
+ draw_screen();
+ break;
+ case ERR:
+ // wprintw(win, "err");
+ break;
+ }
+ draw_screen();
+ refresh();
+ break;
+ case KEY_RESIZE:
+ wresize(screen.lineno, LINES, 0);
+ wresize(screen.hex, LINES, 0);
+ wresize(screen.chr, LINES, 0);
+ draw_screen();
+ mvprintw(0, 0, "%ix%i", COLS, LINES);
+ refresh();
+ break;
+ case KEY_ENTER:
+ goto end;
+ }
+ }
+
+end:
+ munmap(screen.mem, 0);
+ delwin(screen.lineno);
+ delwin(screen.hex);
+ delwin(screen.chr);
+ endwin();
+ return 0;
+}