diff --git a/README.md b/README.md index fa80f9f..aad02c9 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,13 @@ 352 B | 0 B/ 708.00 KB | 0.049% ``` -ddhx is a simple, fast hexadecimal file viewer meant to replace my +ddhx is a simple TUI hexadecimal file viewer meant to replace my [0xdd](https://github.com/dd86k/0xdd) utility, written in a proper system language, mostly for myself to use. +A lot of the code is pretty crappy, but this was mostly written on in a whim, +so I don't entirely care. + # View ``` diff --git a/docs/ddhx.1 b/docs/ddhx.1 index c91773d..5969532 100644 --- a/docs/ddhx.1 +++ b/docs/ddhx.1 @@ -2,14 +2,14 @@ ." Please read man-pages(7) and groff_man(7) about the manual page format. ." Don't forget to respect the format of this document! ." -.TH ddhx 1 "June 2019" dd86k "User manual" +.TH ddhx 1 "August 2020" dd86k "User manual" .SH NAME ddhx - Interactive hexadecimal file viewer. .SH SYNOPSIS .SY ddhx .OP \-w width -.OP \-o offset +.OP \-o offsetmode .OP \-m mode .RI file .IR @@ -17,7 +17,7 @@ .SH DESCRIPTION .B ddhx -is a fast interactive hexadecimal terminal file viewer with several search features. +is a simple interactive hexadecimal terminal file viewer with several search features. .SH OPTIONS .IP file diff --git a/src/Menu.d b/src/Menu.d index d55ec4d..9fe3edb 100644 --- a/src/Menu.d +++ b/src/Menu.d @@ -32,7 +32,7 @@ //TODO: GC-free merge prepend and readln(buf), then split string[] argv = cast(string[])(prepend ~ readln[0..$-1]).split; // split ' ', no empty entries - hxoffsetbar; + ddhx_update_offsetbar; const size_t argc = argv.length; if (argc == 0) return; @@ -40,27 +40,27 @@ switch (argv[0]) { case "g", "goto": if (argc <= 1) { - msgalt("Missing position (number)"); + ddhx_msglow("Missing position (number)"); break; } switch (argv[1]) { case "e", "end": - hxgoto(fsize - screenl); + ddhx_seek_unsafe(fsize - screenl); break; case "h", "home": - hxgoto(0); + ddhx_seek_unsafe(0); break; default: - gotostr(argv[1]); + ddhx_seek(argv[1]); } break; case "s", "search": // Search if (argc <= 1) { - msgalt("Missing data type"); + ddhx_msglow("Missing data type"); break; } if (argc <= 2) { - msgalt("Missing data argument"); + ddhx_msglow("Missing data argument"); break; } @@ -85,7 +85,7 @@ search_utf16(value); break; default: - msgalt("Invalid type (%s)", argv[1]); + ddhx_msglow("Invalid type (%s)", argv[1]); break; } break; // "search" @@ -93,19 +93,19 @@ if (argc > 1) search_utf8(argv[1]); else - msgalt("Missing argument (utf8)"); + ddhx_msglow("Missing argument (utf8)"); break; case "sw": // Search UTF-16 string if (argc > 1) search_utf16(argv[1]); else - msgalt("Missing argument (utf16)"); + ddhx_msglow("Missing argument (utf16)"); break; //TODO: UTF-32 search alias case "sb": // Search byte SEARCH_BYTE: if (argc <= 1) { - msgalt("Missing argument (u8)"); + ddhx_msglow("Missing argument (u8)"); break; } import utils : unformat; @@ -113,58 +113,58 @@ if (unformat(argv[1], l)) { search_u8(l & 0xFF); } else { - msgalt("Could not parse number"); + ddhx_msglow("Could not parse number"); } break; - case "i", "info": hxfileinfo; break; + case "i", "info": ddhx_fileinfo; break; case "o", "offset": import settings : HandleOffset; if (argc <= 1) { - msgalt("Missing offset"); + ddhx_msglow("Missing offset"); break; } HandleOffset(argv[1]); - hxoffsetbar; - hxrender_r; + ddhx_update_offsetbar; + ddhx_render_raw; break; - case "refresh": hxrefresh_a; break; - case "quit": hxexit; break; + case "refresh": ddhx_refresh; break; + case "quit": ddhx_exit; break; case "about": enum C = "Written by dd86k. " ~ COPYRIGHT; - msgalt(C); + ddhx_msglow(C); break; case "version": enum V = "ddhx " ~ APP_VERSION ~ ", " ~ __TIMESTAMP__; - msgalt(V); + ddhx_msglow(V); break; // // Setting manager // case "set": if (argc <= 1) { - msgalt("Missing setting"); + ddhx_msglow("Missing setting"); break; } if (argc <= 2) { - msgalt("Missing setting option"); + ddhx_msglow("Missing setting option"); break; } switch (argv[1]) { case "width", "w": HandleWidth(argv[2]); - hxprep; - hxrefresh_a; + ddhx_prep; + ddhx_refresh; break; case "offset", "o": HandleOffset(argv[2]); conclear; - hxrefresh_a; + ddhx_refresh; break; default: - msgalt("Unknown setting: %s", argv[1]); + ddhx_msglow("Unknown setting: %s", argv[1]); break; } break; - default: msgalt("Unknown command: %s", argv[0]); break; + default: ddhx_msglow("Unknown command: %s", argv[0]); break; } } \ No newline at end of file diff --git a/src/ddcon.d b/src/ddcon.d index aa5c62c..ec9b28d 100644 --- a/src/ddcon.d +++ b/src/ddcon.d @@ -150,22 +150,19 @@ case MOUSE_EVENT: switch (ir.MouseEvent.dwEventFlags) { case MOUSE_WHEELED: - // Down=0xFF880000 Up=0x00780000 - k.value = ir.MouseEvent.dwButtonState == 0xFF880000 ? + // Up=0x00780000 Down=0xFF880000 + k.value = ir.MouseEvent.dwButtonState > 0xFF_0000 ? Mouse.ScrollDown : Mouse.ScrollUp; return; - default: + default: goto L_READ; } - break; default: goto L_READ; } -L_RETURN: - k.value = 0; } else version (Posix) { //TODO: Get modifier keys states - // Commenting this section will echo the character - // And also it won't do anything to getchar + // Commenting this section will echo the character and make + // getchar unusable tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); int c = getchar; diff --git a/src/ddhx.d b/src/ddhx.d index e9cab54..4a1f121 100644 --- a/src/ddhx.d +++ b/src/ddhx.d @@ -65,18 +65,19 @@ /// Main app entry point /// Params: pos = File position to start with -extern (C) void ddhx_main(long pos) { import settings : HandleWidth; fpos = pos; tfsize = formatsize(tfsizebuf, fsize); coninit; - hxprep; + ddhx_prep; conclear; - hxoffsetbar; - hxrender_r; - hxinfobar_r; + ddhx_update_offsetbar; + if (ddhx_render_raw < conheight - 2) + ddhx_update_infobar; + else + ddhx_update_infobar_raw; InputInfo k = void; KEY: @@ -89,48 +90,48 @@ case Key.UpArrow, Key.K: if (fpos - BytesPerRow >= 0) - hxgoto(fpos - BytesPerRow); + ddhx_seek_unsafe(fpos - BytesPerRow); else - hxgoto(0); + ddhx_seek_unsafe(0); break; case Key.DownArrow, Key.J: if (fpos + screenl + BytesPerRow <= fsize) - hxgoto(fpos + BytesPerRow); + ddhx_seek_unsafe(fpos + BytesPerRow); else - hxgoto(fsize - screenl); + ddhx_seek_unsafe(fsize - screenl); break; case Key.LeftArrow, Key.H: if (fpos - 1 >= 0) // Else already at 0 - hxgoto(fpos - 1); + ddhx_seek_unsafe(fpos - 1); break; case Key.RightArrow, Key.L: if (fpos + screenl + 1 <= fsize) - hxgoto(fpos + 1); + ddhx_seek_unsafe(fpos + 1); else - hxgoto(fsize - screenl); + ddhx_seek_unsafe(fsize - screenl); break; case Key.PageUp, Mouse.ScrollUp: if (fpos - cast(long)screenl >= 0) - hxgoto(fpos - screenl); + ddhx_seek_unsafe(fpos - screenl); else - hxgoto(0); + ddhx_seek_unsafe(0); break; case Key.PageDown, Mouse.ScrollDown: if (fpos + screenl + screenl <= fsize) - hxgoto(fpos + screenl); + ddhx_seek_unsafe(fpos + screenl); else - hxgoto(fsize - screenl); + ddhx_seek_unsafe(fsize - screenl); break; case Key.Home: - hxgoto(k.key.ctrl ? 0 : fpos - (fpos % BytesPerRow)); + ddhx_seek_unsafe(k.key.ctrl ? 0 : fpos - (fpos % BytesPerRow)); break; case Key.End: if (k.key.ctrl) { - hxgoto(fsize - screenl); + ddhx_seek_unsafe(fsize - screenl); } else { const long np = fpos + (BytesPerRow - fpos % BytesPerRow); - hxgoto(np + screenl <= fsize ? np : fsize - screenl); + ddhx_seek_unsafe(np + screenl <= fsize ? np : fsize - screenl); } break; @@ -143,39 +144,39 @@ break; case Key.G: hxmenu("g "); - hxoffsetbar(); + ddhx_update_offsetbar(); break; case Key.I: - hxfileinfo; + ddhx_fileinfo; break; case Key.R, Key.F5: - hxrefresh_a; + ddhx_refresh; break; case Key.A: HandleWidth("a"); - hxrefresh_a; + ddhx_refresh; break; - case Key.Q: hxexit; break; + case Key.Q: ddhx_exit; break; default: } goto KEY; } /// Refresh the entire screen -extern (C) -void hxrefresh_a() { - hxprep; +void ddhx_refresh() { + ddhx_prep; conclear; - hxoffsetbar; - hxrender_r; - hxinfobar_r; + ddhx_update_offsetbar; + if (ddhx_render_raw < conheight - 2) + ddhx_update_infobar; + else + ddhx_update_infobar_raw; } /** * Update the upper offset bar. */ -extern (C) -void hxoffsetbar() { +void ddhx_update_offsetbar() { char [8]format = cast(char[8])" %02X"; // default format[4] = formatTable[CurrentOffsetType]; conpos(0, 0); @@ -186,15 +187,13 @@ } /// Update the bottom current information bar. -extern (C) -void hxinfobar() { +void ddhx_update_infobar() { conpos(0, conheight - 1); - hxinfobar_r; + ddhx_update_infobar_raw; } /// Updates information bar without cursor position call. -extern (C) -void hxinfobar_r() { +void ddhx_update_infobar_raw() { char[32] bl = void, cp = void; writef(" %*s | %*s/%*s | %7.3f%%", 7, formatsize(bl, screenl), // Buffer size @@ -205,8 +204,7 @@ } /// Determine screensize -extern (C) -void hxprep() { +void ddhx_prep() { const int bufs = (conheight - 2) * BytesPerRow; // Proposed buffer size screenl = fsize >= bufs ? bufs : cast(uint)fsize; } @@ -217,14 +215,15 @@ * Sets CurrentPosition. * Params: pos = New position */ -extern (C) -void hxgoto(long pos) { +void ddhx_seek_unsafe(long pos) { if (screenl < fsize) { fpos = pos; - hxrender; - hxinfobar_r; + if (ddhx_render < conheight - 2) + ddhx_update_infobar; + else + ddhx_update_infobar_raw; } else - msgalt("Navigation disabled, buffer too small"); + ddhx_msglow("Navigation disabled, buffer too small"); } /** @@ -232,12 +231,11 @@ * Checks bounds and calls Goto. * Params: pos = New position */ -extern (C) -void hxgoto_c(long pos) { +void ddhx_seek(long pos) { if (pos + screenl > fsize) - hxgoto(fsize - screenl); + ddhx_seek_unsafe(fsize - screenl); else - hxgoto(pos); + ddhx_seek_unsafe(pos); } /** @@ -245,7 +243,7 @@ * Includes offset checking (+/- notation). * Params: str = String as a number */ -void gotostr(string str) { +void ddhx_seek(string str) { byte rel = void; // Lazy code if (str[0] == '+') { // relative position rel = 1; @@ -256,45 +254,44 @@ } long l = void; if (unformat(str, l) == false) { - msgalt("Could not parse number"); + ddhx_msglow("Could not parse number"); return; } switch (rel) { case 1: if (fpos + l - screenl < fsize) - hxgoto(fpos + l); + ddhx_seek_unsafe(fpos + l); break; case 2: if (fpos - l >= 0) - hxgoto(fpos - l); + ddhx_seek_unsafe(fpos - l); break; default: if (l >= 0 && l < fsize - screenl) { - hxgoto(l); + ddhx_seek_unsafe(l); } else { import std.format : format; - msgalt(format("Range too far or negative: %d (%XH)", l, l)); + ddhx_msglow(format("Range too far or negative: %d (%XH)", l, l)); } } } /// Update display from buffer -extern (C) -void hxrender() { +uint ddhx_render() { conpos(0, 1); - hxrender_r; + return ddhx_render_raw; } /// Update display from buffer without setting cursor -extern (C) -void hxrender_r() { +/// Returns: The number of lines printed on screen +uint ddhx_render_raw() { __gshared char[] hexTable = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', ]; - size_t brow = BytesPerRow; /// bytes per row - size_t minw = cast(int)brow * 3; + int brow = BytesPerRow; /// bytes per row + int minw = cast(int)brow * 3; char[1024] a = void, d = void; a[brow] = d[minw] = '\0'; @@ -302,36 +299,46 @@ size_t p = cast(size_t)fpos, wlen = p + screenl; /// window length const ubyte[] fbuf = cast(ubyte[])CFile[p..wlen]; - char[12] bytef = cast(char[12])"%08X %s %s\n"; + char[18] bytef = cast(char[18])"%8zX %.*s %.*s\n"; bytef[3] = formatTable[CurrentOffsetType]; + //TODO: Rework loop + uint ld; /// number of lines printed + bool over = void; + ubyte b = void; for (size_t bi; p < wlen; p += brow) { - const bool over = p + brow > fsize; + over = p + brow > fsize; if (over) { brow = cast(uint)(fsize - p); minw = brow * 3; } - for (size_t di, ai; ai < brow; ++ai) { - const ubyte b = fbuf[bi++]; + size_t ai; + for (size_t di; ai < brow; ++ai) { + b = fbuf[bi++]; d[di++] = ' '; d[di++] = hexTable[b >> 4]; d[di++] = hexTable[b & 15]; a[ai] = b > 0x7E || b < 0x20 ? DEFAULT_CHAR : b; } - writef(bytef, p, d[0..minw], a[0..brow]); + //TODO: Row remainder - if (over) return; + printf(cast(char*)bytef, p, minw, cast(char*)d, brow, cast(char*)a); + + ++ld; + + if (over) break; } + return ld; } /** * Message once (upper bar) * Params: msg = Message string */ -void msg(string msg) { +void ddhx_msgtop(string msg) { conpos(0, 0); writef("%s%*s", msg, (conwidth - 1) - msg.length, " "); } @@ -340,7 +347,7 @@ * Message once (bottom bar) * Params: msg = Message string */ -void msgalt(string msg) { +void ddhx_msglow(string msg) { conpos(0, conheight - 1); writef("%s%*s", msg, (conwidth - 1) - msg.length, " "); } @@ -351,23 +358,22 @@ * f = Format * arg = String argument */ -void msgalt(string f, string arg) { +void ddhx_msglow(string f, string arg) { + //TODO: (string format, ...) import std.format : format; - msgalt(format(f, arg)); + ddhx_msglow(format(f, arg)); } /// Print some file information at the bottom bar -extern (C) -void hxfileinfo() { +void ddhx_fileinfo() { import std.format : sformat; import std.path : baseName; char[256] b = void; - msgalt(cast(string)b.sformat!"%s %s"(tfsize, fname.baseName)); + ddhx_msglow(cast(string)b.sformat!"%s %s"(tfsize, fname.baseName)); } /// Exits ddhx -extern (C) -void hxexit() { +void ddhx_exit() { import core.stdc.stdlib : exit; conclear; exit(0); diff --git a/src/searcher.d b/src/searcher.d index c553e9b..3d0544b 100644 --- a/src/searcher.d +++ b/src/searcher.d @@ -12,6 +12,17 @@ /// File search chunk buffer size private enum CHUNK_SIZE = 4096; +private struct search_t { + union { + ubyte[2] a16; + short i16; + ubyte[4] a32; + int i32; + ubyte[8] a64; + long i64; + } +} + /** * Search an UTF-8/ASCII string * Params: s = string @@ -59,68 +70,65 @@ * Params: b = ubyte */ void search_u8(const ubyte b) { - msgalt("Searching byte..."); - ubyte[1] a = [ b ]; + ddhx_msglow("Searching byte..."); + ubyte[1] a = void; + a[0] = b; search_arr(a, "byte"); - msgalt("Byte not found"); } /** * Search for a 16-bit value. * Params: - * s = Input + * input = Input * invert = Invert endianness */ -void search_u16(string s, bool invert = false) { +void search_u16(string input, bool invert = false) { long l = void; - if (unformat(s, l) == false) { - msgalt("Could not parse number"); + if (unformat(input, l) == false) { + ddhx_msglow("Could not parse number"); return; } - const ushort u16 = invert ? bswap16(cast(ushort)l) : cast(ushort)l; - ubyte[2] la = void; - *(cast(ushort*)la) = u16; - search_arr(la, "u16"); + search_t s = void; + s.i32 = invert ? bswap16(cast(short)l) : cast(short)l; + search_arr(s.a16, "u16"); } /** * Search for a 32-bit value. * Params: - * s = Input + * input = Input * invert = Invert endianness */ -void search_u32(string s, bool invert = false) { +void search_u32(string input, bool invert = false) { long l = void; - if (unformat(s, l) == false) { - msgalt("Could not parse number"); + if (unformat(input, l) == false) { + ddhx_msglow("Could not parse number"); return; } - const uint u32 = invert ? bswap32(cast(uint)l) : cast(uint)l; - ubyte[4] la = void; - *(cast(uint*)la) = u32; - search_arr(la, "u32"); + search_t s = void; + s.i32 = invert ? bswap32(cast(int)l) : cast(int)l; + search_arr(s.a32, "u32"); } /** * Search for a 64-bit value. * Params: - * s = Input + * input = Input * invert = Invert endianness */ -void search_u64(string s, bool invert = false) { +void search_u64(string input, bool invert = false) { long l = void; - if (unformat(s, l) == false) { - msgalt("Could not parse number"); + if (unformat(input, l) == false) { + ddhx_msglow("Could not parse number"); return; } - if (invert) l = bswap64(l); - ubyte[8] la = void; - *(cast(long*)la) = l; - search_arr(la, "u64"); + search_t s = void; + s.i64 = invert ? bswap64(l) : l; + search_arr(s.a64, "u64"); } private void search_arr(ubyte[] data, string type) { - msgalt(" Searching %s", type); + ddhx_msglow(" Searching %s...", type); const ubyte firstbyte = data[0]; const size_t datalen = data.length; size_t pos = cast(size_t)fpos + 1; // do not affect file position itself @@ -137,7 +145,7 @@ if (ilen < buflen) { // Within CHUNK if (buf[i..i + datalen] == data) { S_FOUND: - hxgoto_c(pos + i); + ddhx_seek(pos + i); return; } } else if (ilen < fsize) { // Out-of-chunk @@ -149,5 +157,5 @@ pos = posmax; posmax += CHUNK_SIZE; } while (pos < fsize); - msgalt(" Not found (%s)", type); + ddhx_msglow(" Not found (%s)", type); } \ No newline at end of file diff --git a/src/settings.d b/src/settings.d index 03b6f33..7e6e614 100644 --- a/src/settings.d +++ b/src/settings.d @@ -27,20 +27,16 @@ * cli = From CLI (Assumes false by default) */ void HandleWidth(string val, bool cli = false) { + import ddcon : coninit; switch (val[0]) { case 'a': // Automatic - version (Windows) { -//TODO: Fix with CLI, returns 65535 (why can't Windows work?) - if (!cli) BytesPerRow = getBytesPerRow; - } else { + version (Windows) + if (cli) coninit; BytesPerRow = getBytesPerRow; - } break; - case 'd': // Default BytesPerRow = 16; break; - default: long l; if (unformat(val, l)) { @@ -49,7 +45,7 @@ writefln(ETRANGE, l, ushort.max); exit(1); } else - msgalt("Number out of range"); + ddhx_msglow("Number out of range"); return; } BytesPerRow = l & 0xFFFF; @@ -58,7 +54,7 @@ writeln(ENOPARSE); exit(1); } else - msgalt(ENOPARSE); + ddhx_msglow(ENOPARSE); } } } @@ -89,7 +85,7 @@ writef("Unknown mode parameter: %s", val); exit(1); } else { - msgalt(" Invalid offset type: %s", val); + ddhx_msglow(" Invalid offset type: %s", val); } break; }