Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file choosers #19

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
27 changes: 27 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*


target/
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ formats, based on [Kaitai Struct](http://kaitai.io) project. This vis
tool uses [Java's Swing](https://en.wikipedia.org/wiki/Swing_%28Java%29)
for its GUI elements.

![screenshot](etc/screenshot.png)

Its functionality is akin to similar projects:

* [kaitai_struct_visualizer](https://github.com/kaitai-io/kaitai_struct_visualizer) in Ruby
Expand All @@ -26,17 +28,36 @@ that can read described data structure from a file / stream and give
access to it in a nice, easy-to-comprehend API.

## Build
Install java, maven and, if on windows, git-bash

Run in console:
Install Java (JDK 8 or newer) and Apache Maven. If on Windows, also install Git.

Download or clone this repository. Run this command:

```bash
mvn install
```

then the output will be in a subdirectory called `target`.

## Usage

Depending on the system, double-clicking on the jar file may launch the GUI.

Otherwise, launch the GUI via the command-line. The exact name of the jar file will vary. The form is:

```bash
java -jar kaitai_struct_visualizer_java.jar
```

The input files can be specified as arguments, in this order:

```bash
java -jar kaitai_struct_visualizer_java.jar binaryFileToParse ksyFile
```

## Licensing

This GUI vis tool project itself is copyright (C) 2016-2019 Kaitai
This GUI vis tool project itself is copyright (C) 2016-2022 Kaitai
Project.

This program is free software: you can redistribute it and/or modify
Expand Down
Binary file added etc/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
195 changes: 187 additions & 8 deletions src/main/java/io/kaitai/struct/visualizer/MainWindow.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,205 @@
package io.kaitai.struct.visualizer;

import io.kaitai.struct.ByteBufferKaitaiStream;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.io.IOException;
import java.util.Arrays;

public class MainWindow extends JFrame {
private static final String APP_NAME = "Kaitai Struct Visualizer";
static final String APP_NAME = "Kaitai Struct Visualizer";
private static final String VERSION = "0.8";

private VisualizerPanel vis;
private final VisualizerPanel visualizerPanel;
private JLabel jLabelSelectedKsyFile;
private JLabel jLabelSelectedBinaryFile;

// use default privacy so that VisualizerPanel can access it
JLabel jLabelStatus;
JButton jButtonChooseKsyFile;


public MainWindow() throws IOException {
public MainWindow() {
super(APP_NAME + " v" + VERSION);
vis = new VisualizerPanel();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().add(vis.getSplitPane());

//noinspection ConstantConditions - suppress IntelliJ warning that getResource() might return null
setIconImages(Arrays.asList(
new ImageIcon(getClass().getResource("/kaitai-struct-icon-48.png")).getImage(),
new ImageIcon(getClass().getResource("/kaitai-struct-icon-32.png")).getImage(),
new ImageIcon(getClass().getResource("/kaitai-struct-icon-16.png")).getImage()
));

getContentPane().setLayout(new BorderLayout());
getContentPane().add(getFileChooserPanel(), BorderLayout.NORTH);
visualizerPanel = new VisualizerPanel(this);
getContentPane().add(visualizerPanel.getSplitPane(), BorderLayout.CENTER);
getContentPane().add(getStatusBarPanel(), BorderLayout.SOUTH);

pack();
setVisible(true);
}

public static void main(final String arg[]) throws Exception {
private JPanel getFileChooserPanel() {
final JPanel retVal = new JPanel();
retVal.setLayout(new GridBagLayout());
retVal.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); //add padding

GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.NONE;
constraints.weightx = 0.0;
constraints.insets = new Insets(2, 5, 2, 5);
constraints.anchor = GridBagConstraints.EAST;
constraints.gridx = 0;
constraints.gridy = 0;
retVal.add(new JLabel("KSY file:"), constraints);

constraints.gridy = 1;
retVal.add(new JLabel("Binary file:"), constraints);

final JFileChooser fileChooserKsyFile = new JFileChooser();
fileChooserKsyFile.setMultiSelectionEnabled(false);
fileChooserKsyFile.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooserKsyFile.setFileFilter(new FileNameExtensionFilter("KSY files", "ksy"));
fileChooserKsyFile.setAcceptAllFileFilterUsed(true);
fileChooserKsyFile.setDialogTitle("Choose KSY file");

final JFileChooser fileChooserBinaryFile = new JFileChooser();
fileChooserBinaryFile.setMultiSelectionEnabled(false);
fileChooserBinaryFile.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooserKsyFile.setAcceptAllFileFilterUsed(true);
fileChooserBinaryFile.setDialogTitle("Choose binary file to parse");

jButtonChooseKsyFile = new JButton("Browse...");
jButtonChooseKsyFile.addActionListener(e -> {
final int buttonClicked = fileChooserKsyFile.showOpenDialog(null);
if (buttonClicked == JFileChooser.APPROVE_OPTION) {
setKsyFile(fileChooserKsyFile.getSelectedFile().getAbsolutePath());
}
});

final JButton jButtonChooseBinaryFileToParse = new JButton("Browse...");
jButtonChooseBinaryFileToParse.addActionListener(e -> {
final int buttonClicked = fileChooserBinaryFile.showOpenDialog(null);
if (buttonClicked == JFileChooser.APPROVE_OPTION) {
setBinaryFileToParse(fileChooserBinaryFile.getSelectedFile().getAbsolutePath());
}
});

constraints.anchor = GridBagConstraints.WEST;
constraints.gridx = 1;
constraints.gridy = 0;
retVal.add(jButtonChooseKsyFile, constraints);

constraints.gridy = 1;
retVal.add(jButtonChooseBinaryFileToParse, constraints);

constraints.gridx = 2;
constraints.gridy = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 1.0;
jLabelSelectedKsyFile = new JLabel("(no KSY file selected)");
retVal.add(jLabelSelectedKsyFile, constraints);

constraints.gridy = 1;
jLabelSelectedBinaryFile = new JLabel("(no binary file selected)");
retVal.add(jLabelSelectedBinaryFile, constraints);

return retVal;
}

private JPanel getStatusBarPanel() {
jLabelStatus = new JLabel("Ready.");
final JPanel statusBarPanel = new JPanel();
statusBarPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 2));
statusBarPanel.add(jLabelStatus);
return statusBarPanel;
}

/**
* Set the Kaitai Struct YAML file, and start compiling it into a Java class.
* <p>
* If the binary file has also been set, then the GUI will be updated when compilation is finished.
*
* @param pathToKsyFile location of the KSY file
*/
private void setKsyFile(String pathToKsyFile) {
jLabelSelectedKsyFile.setText(pathToKsyFile);
jButtonChooseKsyFile.setEnabled(false);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

// The compileKsyFile() method will re-enable the button and change the cursor when it's finished.
visualizerPanel.compileKsyFile(pathToKsyFile);
}

/**
* Set the binary file to be parsed with Kaitai Struct.
* <p>
* If the Kaitai Struct parser has also been compiled, then the GUI will also be updated.
*
* @param pathToBinaryFile location of the binary file to parse
*/
private void setBinaryFileToParse(String pathToBinaryFile) {
jLabelSelectedBinaryFile.setText(pathToBinaryFile);

final ByteBufferKaitaiStream streamToParse;
try {
streamToParse = new ByteBufferKaitaiStream(pathToBinaryFile);
} catch (IOException ex) {
ex.printStackTrace();
final String message = "<html>Couldn't open the selected file (\"" + pathToBinaryFile + "\") for parsing.<br>" +
"The exception was: " + ex + ".<br>" +
"See the console for the full stack trace.";
JOptionPane.showMessageDialog(this, message, APP_NAME, JOptionPane.ERROR_MESSAGE);
return;
}
visualizerPanel.setBinaryStreamToParse(streamToParse);

if (visualizerPanel.isParserReady()) {
try {
visualizerPanel.parseFileAndUpdateGui();
} catch (Exception ex) {
ex.printStackTrace();
final String message = "<html>There was an error initializing Kaitai Struct or parsing the file.<br>" +
"The exception was: " + ex + "<br>" +
"See the console for the full stack trace.";
JOptionPane.showMessageDialog(this, message, APP_NAME, JOptionPane.ERROR_MESSAGE);
}
}
}

public static void main(final String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
MainWindow mw = new MainWindow();
mw.vis.loadAll(arg[0], arg[1]);

// Swing stuff should be done on the Swing Event Dispatch Thread.
SwingUtilities.invokeLater(() -> {
final MainWindow mainWindow = new MainWindow();

switch (args.length) {
case 0:
// No command-line arguments, don't do anything else.
break;
case 2:
final String binaryFileToParse = args[0];
final String ksyFileName = args[1];
if (!ksyFileName.endsWith(".ksy")) {
System.err.println("Warning: the second argument does not have file extension .ksy.");
printCommandLineUsage();
}
mainWindow.setBinaryFileToParse(binaryFileToParse);
mainWindow.setKsyFile(ksyFileName);
break;
default:
printCommandLineUsage();
break;
}
});

}

private static void printCommandLineUsage() {
System.out.println("Command-line usage: java -jar kaitai_struct_visualizer_java.jar binaryFile ksyFile");
}
}
Loading