1 /** 2 * View binary file as image 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.demo.binview.binview; 15 16 import core.runtime; 17 import core.time; 18 19 import std.algorithm.comparison; 20 import std.algorithm.searching; 21 import std.conv; 22 import std.digest.crc; 23 import std.exception; 24 import std.format; 25 import std.math; 26 import std.mmfile; 27 import std.stdio; 28 29 import ae.ui.app.application; 30 import ae.ui.app.main; 31 import ae.ui.shell.shell; 32 import ae.ui.shell.sdl2.shell; 33 import ae.ui.video.bmfont; 34 import ae.ui.video.renderer; 35 import ae.ui.video.sdl2.video; 36 import ae.utils.fps; 37 import ae.utils.graphics.fonts.draw; 38 import ae.utils.graphics.fonts.font8x8; 39 import ae.utils.math; 40 import ae.utils.graphics.image; 41 import ae.utils.meta; 42 43 final class MyApplication : Application 44 { 45 override string getName() { return "Demo/BinView"; } 46 override string getCompanyName() { return "CyberShadow"; } 47 48 Shell shell; 49 50 MmFile f; 51 52 this() 53 { 54 enforce(Runtime.args.length == 2, "Usage: binview FILENAME"); 55 f = new MmFile(Runtime.args[1]); 56 } 57 58 size_t offset = 0; 59 uint width = 256, height; 60 61 enum maxFramesToRender = 3; // include possible back-buffers 62 uint dirty = maxFramesToRender; 63 uint lastWidth, lastHeight; 64 uint bpp = 1; 65 uint zoom = 4; 66 67 override void render(Renderer s) 68 { 69 if (s.width != lastWidth && s.height != lastHeight) 70 dirty = maxFramesToRender; 71 if (dirty == 0) 72 return; 73 74 s.clear(); 75 this.height = s.height / zoom; 76 77 shell.setCaption(format("Offset = 0x%08X Width=0x%X (%d) BPP=%d", offset, width, width, bpp)); 78 79 auto start = min(offset, f.length); 80 auto length = width * height; 81 auto end = min(offset + length * bpp, f.length); 82 83 auto data = f[start .. end]; 84 data = data[0 .. $ - $ % bpp]; 85 86 foreach (i; 0 .. data.length / bpp) 87 { 88 auto bytes = cast(ubyte[])data[i * bpp .. (i+1) * bpp]; 89 if (bytes.canFind!identity) // leave 0 as black 90 { 91 BGRX p; 92 if (bpp > 3) 93 { 94 auto c = crc32Of(bytes); 95 p = BGRX(c[0], c[1], c[2]); 96 } 97 else 98 { 99 ubyte[3] channels; 100 foreach (n, ref c; channels) 101 c = bytes[n % $]; 102 p = BGRX(channels[0], channels[1], channels[2]); 103 } 104 105 auto x = cast(int)(i % width); 106 auto y = cast(int)(i / width); 107 s.fillRect(x * zoom, y * zoom, (x+1) * zoom, (y+1) * zoom, p); 108 } 109 } 110 111 dirty--; 112 lastWidth = s.width; 113 lastHeight = s.height; 114 } 115 116 override int run(string[] args) 117 { 118 shell = new SDL2Shell(this); 119 shell.video = new SDL2SoftwareVideo(); 120 shell.run(); 121 shell.video.shutdown(); 122 return 0; 123 } 124 125 void navigate(uint bytes, bool forward) 126 { 127 if (forward) 128 offset = min(offset + bytes, f.length); 129 else 130 offset = offset > bytes ? offset - bytes : 0; 131 } 132 133 override void handleKeyDown(Key key, dchar character) 134 { 135 switch (key) 136 { 137 case Key.esc: 138 shell.quit(); 139 break; 140 case Key.left : navigate( 1 , false); break; 141 case Key.right : navigate( 1 , true ); break; 142 case Key.up : navigate(width * bpp , false); break; 143 case Key.down : navigate(width * bpp , true ); break; 144 case Key.pageUp : navigate(width * height * bpp, false); break; 145 case Key.pageDown: navigate(width * height * bpp, true ); break; 146 case Key.home : width = width ? width-1 : 0; break; 147 case Key.end : width++; break; 148 default: 149 switch (character) 150 { 151 case '1': 152 .. 153 case '9': 154 bpp = character - '0'; 155 break; 156 case '+': 157 case '=': 158 zoom++; 159 break; 160 case '-': 161 if (zoom > 1) 162 zoom--; 163 break; 164 default: 165 return; 166 } 167 } 168 dirty = maxFramesToRender; 169 } 170 171 override void handleMouseMove(uint x, uint y, MouseButtons buttons) 172 { 173 auto addr = offset + ((y / zoom) * width + (x / zoom)) * bpp; 174 shell.setCaption(format("x=%d y=%d addr=%08X data=%(%02X %)", 175 x / zoom, y / zoom, addr, (addr+bpp) < f.length ? cast(ubyte[])f[addr..addr+bpp] : null)); 176 } 177 178 override void handleQuit() 179 { 180 shell.quit(); 181 } 182 } 183 184 shared static this() 185 { 186 createApplication!MyApplication(); 187 }