Tabbed Console

A better solution to the plugin console for when you have multiple resources inside of BCV
This commit is contained in:
Konloch 2021-07-14 21:59:18 -07:00
parent 3e82f29b59
commit ced48c76fd
8 changed files with 174 additions and 25 deletions

View file

@ -86,7 +86,6 @@ import static the.bytecode.club.bytecodeviewer.util.MiscUtils.guessLanguage;
* + Fix hook inject for EZ-Injection * + Fix hook inject for EZ-Injection
* *
* TODO FEATURES: * TODO FEATURES:
* + Console Plugin needs tabbed consoles instead of creating a new window for each console
* + CLI Headless needs to be supported * + CLI Headless needs to be supported
* + Add stackmapframes to bytecode decompiler * + Add stackmapframes to bytecode decompiler
* + Add JEB decompiler optionally, requires them to add jeb library jar * + Add JEB decompiler optionally, requires them to add jeb library jar

View file

@ -1,6 +1,7 @@
package the.bytecode.club.bytecodeviewer.api; package the.bytecode.club.bytecodeviewer.api;
import the.bytecode.club.bytecodeviewer.gui.components.SystemConsole; import the.bytecode.club.bytecodeviewer.gui.components.SystemConsole;
import the.bytecode.club.bytecodeviewer.plugin.PluginManager;
/*************************************************************************** /***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@ -28,10 +29,24 @@ import the.bytecode.club.bytecodeviewer.gui.components.SystemConsole;
public class PluginConsole extends SystemConsole public class PluginConsole extends SystemConsole
{ {
private boolean showWindow;
public PluginConsole(String pluginName) public PluginConsole(String pluginName)
{ {
super("Bytecode Viewer - Plugin Console - " + pluginName); super("Bytecode Viewer - Plugin Console - " + pluginName);
PluginManager.addConsole(this);
} }
@Override
public void setVisible(boolean b)
{
//do nothing
if(!showWindow)
return;
super.setVisible(b);
}
private static final long serialVersionUID = -6556940545421437508L; private static final long serialVersionUID = -6556940545421437508L;
} }

View file

@ -1,15 +1,12 @@
package the.bytecode.club.bytecodeviewer.gui.components; package the.bytecode.club.bytecodeviewer.gui.components;
import me.konloch.kontainer.io.DiskWriter; import me.konloch.kontainer.io.DiskWriter;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.Constants;
import the.bytecode.club.bytecodeviewer.resources.IconResources; import the.bytecode.club.bytecodeviewer.resources.IconResources;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.io.File; import java.io.File;
import static the.bytecode.club.bytecodeviewer.Constants.fs;
import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory; import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory;
/*************************************************************************** /***************************************************************************
@ -38,14 +35,17 @@ import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory;
*/ */
public class JFrameConsole extends JFrame public class JFrameConsole extends JFrame
{ {
private final SearchableJTextArea textArea = new SearchableJTextArea(); private String containerName;
private int consoleID;
private final SearchableJTextArea textArea;
public JFrameConsole(String title) public JFrameConsole(String title)
{ {
this.setIconImages(IconResources.iconList); setIconImages(IconResources.iconList);
setTitle(title); setTitle(title);
setSize(new Dimension(542, 316)); setSize(new Dimension(542, 316));
textArea = new SearchableJTextArea();
getContentPane().add(textArea.getScrollPane(), BorderLayout.CENTER); getContentPane().add(textArea.getScrollPane(), BorderLayout.CENTER);
this.setLocationRelativeTo(null); this.setLocationRelativeTo(null);
@ -71,33 +71,78 @@ public class JFrameConsole extends JFrame
*/ */
public void setText(String t) public void setText(String t)
{ {
textArea.setText(trim(t)); textArea.setText(trimConsoleText(t));
textArea.setCaretPosition(0); textArea.setCaretPosition(0);
} }
/**
* Returns the SearchableJTextArea pane
*/
public SearchableJTextArea getTextArea() public SearchableJTextArea getTextArea()
{ {
return textArea; return textArea;
} }
public String trim(String s) /**
* Returns the console ID
*/
public int getConsoleID()
{
return consoleID;
}
/**
* Returns the current container name
*/
public String getContainerName()
{
return containerName;
}
/**
* Set the console ID
*/
public void setConsoleID(int consoleID)
{
this.consoleID = consoleID;
}
/**
* Set the container name
*/
public void setContainerName(String containerName)
{
this.containerName = containerName;
}
/**
* Trims the console text to prevent killing the swing thread
*/
public String trimConsoleText(final String s)
{ {
int len = s.length(); int len = s.length();
int max = 500_000; //TODO this can be increased to 1,000,000 the lower number was chosen to be safe
//TODO this should also be a setting eventually
int max = 500_000;
if(len >= max) if(len >= max)
{ {
//TODO if two consoles are ran at the same time and exceed the maximum this file will be overwritten //TODO if two consoles are ran at the same time and exceed the maximum this file will be overwritten
//save to disk final File tempFile = new File(tempDirectory, "console_" + consoleID + ".log");
File tempFile = new File(tempDirectory+fs+"console.log"); new Thread(()->
DiskWriter.replaceFile(tempFile.getAbsolutePath(), s, false); {
//save to disk
DiskWriter.replaceFile(tempFile.getAbsolutePath(), s, false);
}, "Console Log Saving").start();
//trim //trim
int skipped = len - max; int skipped = len - max;
s = s.substring(0, max); String trimmed = s.substring(0, max);
s = ("Skipping " + skipped + " chars, allowing " + max + "\n\r") trimmed = ("WARNING: Skipping " + skipped + " chars, allowing " + max + "\n\r")
+ "Full log saved to: " + tempFile.getAbsolutePath() + "\n\r" + "Full log saved to: " + tempFile.getAbsolutePath() + "\n\r\n\r"
+ s; + trimmed;
return trimmed;
} }
return s; return s;

View file

@ -0,0 +1,51 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import the.bytecode.club.bytecodeviewer.resources.IconResources;
import javax.swing.*;
import java.awt.*;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* @author Konloch
* @since 7/14/2021
*/
public class JFrameConsoleTabbed extends JFrame
{
private final JTabbedPane tabbedPane;
public JFrameConsoleTabbed(String title)
{
setIconImages(IconResources.iconList);
setTitle(title);
setSize(new Dimension(542, 316));
tabbedPane = new JTabbedPane();
getContentPane().add(tabbedPane, BorderLayout.CENTER);
this.setLocationRelativeTo(null);
}
public void addConsole(Component console, String containerName)
{
tabbedPane.add(console, containerName);
}
}

View file

@ -18,8 +18,6 @@ package the.bytecode.club.bytecodeviewer.gui.components;
* along with this program. If not, see <http://www.gnu.org/licenses/>. * * along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/ ***************************************************************************/
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/** /**
* A simple console GUI. * A simple console GUI.
* *

View file

@ -301,7 +301,7 @@ public class ResourceListPane extends TranslatedVisibleComponent implements File
if (c.name.equals(cheapHax)) if (c.name.equals(cheapHax))
container = c; container = c;
} }
String name = nameBuffer.toString(); String name = nameBuffer.toString();
//TODO add file header check //TODO add file header check
@ -311,13 +311,13 @@ public class ResourceListPane extends TranslatedVisibleComponent implements File
name.substring(0, name.length() - ".class".length())); name.substring(0, name.length() - ".class".length()));
if (cn != null) if (cn != null)
BytecodeViewer.viewer.workPane.addClassResource(container, nameBuffer.toString()); BytecodeViewer.viewer.workPane.addClassResource(container, name);
else else
BytecodeViewer.viewer.workPane.addFileResource(container, nameBuffer.toString()); BytecodeViewer.viewer.workPane.addFileResource(container, name);
} }
else else if(container.resourceFiles.containsKey(name))
{ {
BytecodeViewer.viewer.workPane.addFileResource(container, nameBuffer.toString()); BytecodeViewer.viewer.workPane.addFileResource(container, name);
} }
} }

View file

@ -23,6 +23,10 @@ public class TabRemovalEvent implements ContainerListener
public void componentRemoved(ContainerEvent e) public void componentRemoved(ContainerEvent e)
{ {
final Component c = e.getChild(); final Component c = e.getChild();
if(!(c instanceof ResourceViewer))
return;
String workingName = ((ResourceViewer) c).resource.workingName; String workingName = ((ResourceViewer) c).resource.workingName;
BytecodeViewer.viewer.workPane.openedTabs.remove(workingName); BytecodeViewer.viewer.workPane.openedTabs.remove(workingName);
} }

View file

@ -5,6 +5,8 @@ import java.util.*;
import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileFilter;
import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.api.Plugin; import the.bytecode.club.bytecodeviewer.api.Plugin;
import the.bytecode.club.bytecodeviewer.api.PluginConsole;
import the.bytecode.club.bytecodeviewer.gui.components.JFrameConsoleTabbed;
import the.bytecode.club.bytecodeviewer.plugin.strategies.*; import the.bytecode.club.bytecodeviewer.plugin.strategies.*;
import the.bytecode.club.bytecodeviewer.util.MiscUtils; import the.bytecode.club.bytecodeviewer.util.MiscUtils;
@ -35,11 +37,20 @@ import the.bytecode.club.bytecodeviewer.util.MiscUtils;
* @author Bibl * @author Bibl
* @since 01/16/16, 14:36, Adaptable PluginLaunchStrategy system. * @since 01/16/16, 14:36, Adaptable PluginLaunchStrategy system.
*/ */
public final class PluginManager {
public final class PluginManager
{
private static final Map<String, PluginLaunchStrategy> launchStrategies = new HashMap<>(); private static final Map<String, PluginLaunchStrategy> launchStrategies = new HashMap<>();
private static final PluginFileFilter filter = new PluginFileFilter(); private static final PluginFileFilter filter = new PluginFileFilter();
private static List<Plugin> pluginInstances = new ArrayList<>(); private static List<Plugin> pluginInstances = new ArrayList<>();
//TODO this system needs to be redone, currently it will conflict if more than one plugin is ran at the same time
// the solution is to tie the plugin object into the plugin console,
// then move all of this into the plugin class as non-static objects
private static Plugin activePlugin;
private static JFrameConsoleTabbed activeTabbedConsole;
private static int consoleCount = 0;
static static
{ {
@ -67,6 +78,15 @@ public final class PluginManager {
*/ */
public static void runPlugin(Plugin newPluginInstance) public static void runPlugin(Plugin newPluginInstance)
{ {
//reset the console count
consoleCount = 0;
//reset the active tabbed console
activeTabbedConsole = null;
//reset the active plugin
activePlugin = newPluginInstance;
//clean the plugin list from dead threads //clean the plugin list from dead threads
pluginInstances.removeIf(Plugin::isFinished); pluginInstances.removeIf(Plugin::isFinished);
@ -96,6 +116,23 @@ public final class PluginManager {
if (p != null) if (p != null)
runPlugin(p); runPlugin(p);
} }
/**
* Add an active console from a plugin being ran
*/
public static void addConsole(PluginConsole console)
{
int id = consoleCount++;
if(activeTabbedConsole == null)
{
activeTabbedConsole = new JFrameConsoleTabbed(console.getTitle());
activeTabbedConsole.setVisible(true);
}
console.setConsoleID(id);
activeTabbedConsole.addConsole(console.getComponent(0), activePlugin.activeContainer.name);
}
public static void register(String name, PluginLaunchStrategy strat) { public static void register(String name, PluginLaunchStrategy strat) {
launchStrategies.put(name, strat); launchStrategies.put(name, strat);