Hex Viewer

Hex Viewer

----------------- HexView.java -----------------

import java.io.*;
import java.util.*;
import java.nio.*;
import java.nio.charset.*;

import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.control.cell.*;
import javafx.scene.layout.*;
import javafx.scene.text.*;
import javafx.collections.*;
import javafx.beans.property.*;
import javafx.util.*;

public class HexView extends Application {

  RandomAccessFile raf = null;
  int size = 0;

  @Override
  public void start(Stage stage) {

    File file;
    List<String> p = getParameters().getRaw();
    if (!p.isEmpty()) {
      file = new File(p.get(0));
      if (!file.isFile()) throw new RuntimeException(file.getName() + " file not found");
    } else {
      file = new File("demo.bin");
      if (!file.exists()) createDemo(file);
    }

    try {
      raf = new RandomAccessFile(file, "rw");
      if (raf.length() > 0) size = (int)((raf.length() - 1) / 16 + 1);
    } catch (Exception e) { }

    iniCharTable(Charset.defaultCharset());

    BorderPane layout = new BorderPane();
    stage.setScene(new Scene(layout, 700, 400));

    TableView<Integer> tableView = new TableView<>();
    tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

    tableView.setItems(new ObservableListBase<Integer>() {
      Integer value = Integer.valueOf(-1);

      @Override
      public int size() {
        return size;
      }

      @Override
      public Integer get(int index) {
        if (index < 0 || index >= size()) throw new IndexOutOfBoundsException();
        if (index != value.intValue()) {
          value = Integer.valueOf(index);
        }
        return value;
      }
    });

    TableColumn<Integer, String> column = new TableColumn<>(file.getName());

    column.setCellValueFactory(cellData -> {
      return new ReadOnlyObjectWrapper<String>(createItem(cellData.getValue()));
    });

    Font font = Font.font("Monospaced");
    Callback<TableColumn<Integer, String>,TableCell<Integer, String>> columnCellFactory = column.getCellFactory();
    column.setCellFactory(col -> {
      TableCell<Integer, String> cell = columnCellFactory.call(col);
      cell.setFont(font);
      return cell;
    });

    tableView.getColumns().add(column);
    tableView.getSelectionModel().selectFirst();
    tableView.sortPolicyProperty().set(t -> false);

    layout.setCenter(tableView);

    stage.setOnCloseRequest(event -> {
      try { raf.close(); } catch (Exception e) { }
    });

    stage.show();
  }

  StringBuilder sb = new StringBuilder();
  char charTable[] = new char[224];
  byte[] bytes = new byte[16];
  char emp = '\u0020', priv = '\uE000', pnt = '\u00B7';
  char hexDigit[] = {
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
  };
  char charControl[] = {
    '\u0020', '\u263A', '\u263B', '\u2665', '\u2666', '\u2663', '\u2660', '\u2022',
    '\u25D8', '\u25CB', '\u25D9', '\u2642', '\u2640', '\u266A', '\u266B', '\u263C',
    '\u25BA', '\u25C4', '\u2195', '\u203C', '\u00B6', '\u00A7', '\u25AC', '\u21A8',
    '\u2191', '\u2193', '\u2192', '\u2190', '\u221F', '\u2194', '\u25B2', '\u25BC'
  };

  boolean iniCharTable(Charset charset) {

    CharsetDecoder decoder = charset.newDecoder();
    boolean sbcs = decoder.maxCharsPerByte() == 1 && decoder.averageCharsPerByte() == 1;
    if (!sbcs) decoder = Charset.forName("Cp1252").newDecoder();
    ByteBuffer bb = ByteBuffer.allocate(1);
    CharBuffer cb = CharBuffer.allocate(1);
    for (int i = 0; i < 224; i++) {
      try {
        bb.put(0, (byte)(i + 0x20));
        bb.position(0);
        cb.position(0);
        decoder.reset();
        if (decoder.decode(bb, cb, true).isError()) charTable[i] = priv;
        else charTable[i] = cb.get(0);
      } catch (Exception e) { charTable[i] = priv; }
    }
    return sbcs;
  }

  String createItem(int value) {

    sb.setLength(0);
    int length = 0;
    try { 
      raf.seek(value * 16);
      length = raf.read(bytes);
    } catch (Exception e) { return null; }

    for (int i = 28; i >= 0; i-=4) {
      sb.append(hexDigit[0x0F & value >>> i]);
    }
    sb.append('0').append(':').append(emp).append(emp);

    for (int i = 0; i < 16; i++) {
      if (i < length) {
        sb.append(hexDigit[0x0F & bytes[i] >> 4]);
        sb.append(hexDigit[0x0F & bytes[i]]);
      } else { sb.append(emp).append(emp); }
      sb.append(emp);
      if (i == 7) sb.append(emp);
    }
    sb.append(emp);
    for (int i = 0; i < 16; i++) {
      if (i >= length) { sb.append(emp); continue; }
      int code = 0xFF & (int)bytes[i];
      if (code >= 0x00 && code < 0x20) { sb.append(charControl[code]); continue; }
      char uc = charTable[code - 0x20];
      if (uc >= '\u0000' && uc < '\u0020') sb.append(charControl[(int)uc]);
      else if (uc == '\u0085' || uc == '\u2028' || uc == '\u2029') sb.append(charControl[0x0A]);
      else if (uc == priv) sb.append(pnt);
      else sb.append(uc);
    }
    return sb.toString();
  }

  void createDemo(File file) {
    try (RandomAccessFile rafd = new RandomAccessFile(file, "rw")) {
      rafd.setLength(0L);
      for (int i = 0; i < 256; i++) rafd.write((byte)i);
      rafd.write("End".getBytes());
    } catch (Exception e) { }
  }
}

Download ZIP

Back