Code Cleanup

+ Refactored a lot of the GUI
+ Fixed a few bugs with the swing components and swing interaction
+ Includes #304
+ Added LAF theme selection
+ Moved the Visual Settings from the Settings menu to the View menu
+ Added a simplified tab names option
This commit is contained in:
Konloch 2021-06-25 18:13:46 -07:00
parent f1db4c2ea8
commit cec2df4685
88 changed files with 3207 additions and 3798 deletions

View File

@ -0,0 +1 @@
e1c0e9bdc5e1a9117385316e9953bf18

View File

@ -0,0 +1 @@
1f1bd37eb6a58bcbeca533757b452fda2e3bf58a

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bulenkov</groupId>
<artifactId>darcula</artifactId>
<version>2017.11bcv</version>
</project>

View File

@ -0,0 +1 @@
18d7207a865f9f1c7ac091ae2175c816

View File

@ -0,0 +1 @@
c24c87ad97daac49a716c8b1b35de813faa14315

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>com.bulenkov</groupId>
<artifactId>darcula</artifactId>
<versioning>
<release>2017.11bcv</release>
<versions>
<version>2017.11bcv</version>
</versions>
<lastUpdated>20210623081823</lastUpdated>
</versioning>
</metadata>

View File

@ -0,0 +1 @@
8c77ae68030a23cf5be1ad543a810476

View File

@ -0,0 +1 @@
890769d1a55221b9f778bfb92a8af22bfd3439df

29
pom.xml
View File

@ -219,30 +219,15 @@
<artifactId>D2Jar-obf</artifactId>
<version>1.0bcv</version>
</dependency>
<dependency>
<groupId>com.bulenkov</groupId>
<artifactId>darcula</artifactId>
<version>2017.11bcv</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>unpack</id>
<!-- In process resource phase such that it doesn't
override other dependencies or BCV's own files -->
<phase>process-resources</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeScope>system</includeScope>
<outputDirectory>${basedir}/target/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
@ -268,7 +253,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
<configuration>
<source>${java.version}</source>
</configuration>
@ -276,7 +261,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>

View File

@ -1,3 +0,0 @@
Manifest-Version: 1.0
Main-Class: the.bytecode.club.bytecodeviewer.BytecodeViewer

View File

@ -10,11 +10,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import me.konloch.kontainer.io.DiskWriter;
import me.konloch.kontainer.io.HTTPRequest;
@ -23,13 +19,15 @@ import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bootloader.Boot;
import the.bytecode.club.bytecodeviewer.api.ClassNodeLoader;
import the.bytecode.club.bytecodeviewer.compilers.Compilers;
import the.bytecode.club.bytecodeviewer.gui.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourcePanelCompileMode;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane;
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
import the.bytecode.club.bytecodeviewer.gui.extras.RunOptions;
import the.bytecode.club.bytecodeviewer.gui.SearchBoxPane;
import the.bytecode.club.bytecodeviewer.gui.extras.SystemErrConsole;
import the.bytecode.club.bytecodeviewer.gui.WorkPane;
import the.bytecode.club.bytecodeviewer.gui.components.RunOptions;
import the.bytecode.club.bytecodeviewer.gui.resourcesearch.SearchBoxPane;
import the.bytecode.club.bytecodeviewer.gui.components.SystemErrConsole;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.WorkPaneMainComponent;
import the.bytecode.club.bytecodeviewer.obfuscators.mapping.Refactorer;
import the.bytecode.club.bytecodeviewer.plugin.PluginManager;
import the.bytecode.club.bytecodeviewer.util.*;
@ -104,6 +102,7 @@ public class BytecodeViewer
public static Refactorer refactorer = new Refactorer();
public static List<FileContainer> files = new ArrayList<>(); //all of BCV's loaded files/classes/etc
public static List<Process> createdProcesses = new ArrayList<>();
public static final DecompilerViewComponent krakatau = new DecompilerViewComponent("Krakatau", true);
public static final boolean EXPERIMENTAL_TAB_CODE = false;
/**
@ -155,14 +154,16 @@ public class BytecodeViewer
* @throws IOException
*/
public static byte[] getClassFile(Class<?> clazz) throws IOException {
InputStream is = clazz.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int r;
byte[] buffer = new byte[8192];
while ((r = Objects.requireNonNull(is).read(buffer)) >= 0) {
baos.write(buffer, 0, r);
try (InputStream is = clazz.getResourceAsStream(
"/" + clazz.getName().replace('.', '/') + ".class");
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
int r;
byte[] buffer = new byte[8192];
while ((r = Objects.requireNonNull(is).read(buffer)) >= 0) {
baos.write(buffer, 0, r);
}
return baos.toByteArray();
}
return baos.toByteArray();
}
/**
@ -176,8 +177,11 @@ public class BytecodeViewer
System.setSecurityManager(sm);
try {
UIManager.put("MenuItem.disabledAreNavigable", Boolean.FALSE);
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
//precache settings file
Settings.preloadSettingsFile();
//setup look and feel
Configuration.lafTheme.setLAF();
if (PREVIEW_COPY && !CommandLineInput.containsCommand(args))
showMessage("WARNING: This is a preview/dev copy, you WON'T be alerted when " + VERSION + " is "
+ "actually out if you use this." + nl +
@ -378,28 +382,36 @@ public class BytecodeViewer
BytecodeViewer.viewer.updateBusyStatus(true);
boolean actuallyTried = false;
for (java.awt.Component c : BytecodeViewer.viewer.workPane.getLoadedViewers()) {
if (c instanceof ClassViewer) {
for (java.awt.Component c : BytecodeViewer.viewer.workPane.getLoadedViewers())
{
if (c instanceof ClassViewer)
{
ClassViewer cv = (ClassViewer) c;
if (cv.smali1 != null && cv.smali1.isEditable() ||
cv.smali2 != null && cv.smali2.isEditable() ||
cv.smali3 != null && cv.smali3.isEditable()) {
//compile smali assembly
if (cv.resourceViewPanel1.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY && cv.resourceViewPanel1.textArea.isEditable() ||
cv.resourceViewPanel2.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY && cv.resourceViewPanel2.textArea.isEditable() ||
cv.resourceViewPanel3.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY && cv.resourceViewPanel3.textArea.isEditable())
{
actuallyTried = true;
Object[] smali = cv.getSmali();
if (smali != null) {
if (smali != null)
{
ClassNode origNode = (ClassNode) smali[0];
String smaliText = (String) smali[1];
byte[] smaliCompiled =
Compilers.smali.compile(smaliText,
origNode.name);
if (smaliCompiled != null) {
byte[] smaliCompiled = Compilers.smali.compile(smaliText, origNode.name);
if (smaliCompiled != null)
{
try {
ClassNode newNode = JarUtils.getNode(smaliCompiled);
BytecodeViewer.updateNode(origNode, newNode);
} catch (Exception e) {
e.printStackTrace();
}
} else {
}
else
{
BytecodeViewer.showMessage("There has been an error with assembling your Smali code, "
+ "please check this. Class: " + origNode.name);
BytecodeViewer.viewer.updateBusyStatus(false);
@ -408,26 +420,30 @@ public class BytecodeViewer
}
}
if (cv.krakatau1 != null && cv.krakatau1.isEditable() ||
cv.krakatau2 != null && cv.krakatau2.isEditable() ||
cv.krakatau3 != null && cv.krakatau3.isEditable()) {
//compile krakatau assembly
if (cv.resourceViewPanel1.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY && cv.resourceViewPanel1.textArea.isEditable() ||
cv.resourceViewPanel2.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY && cv.resourceViewPanel2.textArea.isEditable() ||
cv.resourceViewPanel3.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY && cv.resourceViewPanel3.textArea.isEditable())
{
actuallyTried = true;
Object[] krakatau = cv.getKrakatau();
if (krakatau != null) {
if (krakatau != null)
{
ClassNode origNode = (ClassNode) krakatau[0];
String krakatauText = (String) krakatau[1];
byte[] krakatauCompiled =
Compilers.krakatau.compile(krakatauText,
origNode.name);
if (krakatauCompiled != null) {
byte[] krakatauCompiled = Compilers.krakatau.compile(krakatauText, origNode.name);
if (krakatauCompiled != null)
{
try {
ClassNode newNode = JarUtils.getNode(krakatauCompiled);
BytecodeViewer.updateNode(origNode, newNode);
} catch (Exception e) {
e.printStackTrace();
}
} else {
}
else
{
BytecodeViewer.showMessage("There has been an error with assembling your Krakatau "
+ "Bytecode, please check this. Class: " + origNode.name);
BytecodeViewer.viewer.updateBusyStatus(false);
@ -436,9 +452,11 @@ public class BytecodeViewer
}
}
if (cv.java1 != null && cv.java1.isEditable() ||
cv.java2 != null && cv.java2.isEditable() ||
cv.java3 != null && cv.java3.isEditable()) {
//default to java compiling
if (cv.resourceViewPanel1.textArea != null && cv.resourceViewPanel1.textArea.isEditable() ||
cv.resourceViewPanel2.textArea != null && cv.resourceViewPanel2.textArea.isEditable() ||
cv.resourceViewPanel3.textArea != null && cv.resourceViewPanel3.textArea.isEditable())
{
actuallyTried = true;
Object[] java = cv.getJava();
if (java != null) {
@ -449,10 +467,9 @@ public class BytecodeViewer
errConsole.setText("Error compiling class: " + origNode.name + nl + "Keep in mind most "
+ "decompilers cannot produce compilable classes" + nl + nl);
byte[] javaCompiled =
Compilers.java.compile(javaText,
origNode.name);
if (javaCompiled != null) {
byte[] javaCompiled = Compilers.java.compile(javaText, origNode.name);
if (javaCompiled != null)
{
try {
ClassNode newNode = JarUtils.getNode(javaCompiled);
BytecodeViewer.updateNode(origNode, newNode);
@ -460,7 +477,9 @@ public class BytecodeViewer
e.printStackTrace();
}
errConsole.finished();
} else {
}
else
{
errConsole.pretty();
errConsole.setVisible(true);
errConsole.finished();
@ -554,7 +573,7 @@ public class BytecodeViewer
files.clear();
LazyNameUtil.reset();
Objects.requireNonNull(MainViewerGUI.getComponent(ResourceListPane.class)).resetWorkspace();
Objects.requireNonNull(MainViewerGUI.getComponent(WorkPane.class)).resetWorkspace();
Objects.requireNonNull(MainViewerGUI.getComponent(WorkPaneMainComponent.class)).resetWorkspace();
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class)).resetWorkspace();
the.bytecode.club.bytecodeviewer.api.BytecodeViewer.getClassNodeLoader().clear();
}

View File

@ -8,7 +8,7 @@ import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.decompilers.Decompilers;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.util.JarUtils;
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
@ -200,12 +200,12 @@ public class CommandLineInput {
Thread.sleep(5 * 1000);
if (target.equalsIgnoreCase("all")) {
Decompilers.procyon.decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
Decompiler.PROCYON.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn);
String contents = Decompilers.procyon.decompileClassNode(cn, cw.toByteArray());
String contents = Decompiler.PROCYON.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -218,12 +218,12 @@ public class CommandLineInput {
Thread.sleep(5 * 1000);
if (target.equalsIgnoreCase("all")) {
Decompilers.cfr.decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
Decompiler.CFR.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn);
String contents = Decompilers.cfr.decompileClassNode(cn, cw.toByteArray());
String contents = Decompiler.CFR.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -236,12 +236,12 @@ public class CommandLineInput {
Thread.sleep(5 * 1000);
if (target.equalsIgnoreCase("all")) {
Decompilers.fernflower.decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
Decompiler.FERNFLOWER.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn);
String contents = Decompilers.fernflower.decompileClassNode(cn, cw.toByteArray());
String contents = Decompiler.FERNFLOWER.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -254,12 +254,12 @@ public class CommandLineInput {
Thread.sleep(5 * 1000);
if (target.equalsIgnoreCase("all")) {
Decompilers.krakatau.decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
Decompiler.KRAKATAU.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn);
String contents = Decompilers.krakatau.decompileClassNode(cn, cw.toByteArray());
String contents = Decompiler.KRAKATAU.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -278,7 +278,7 @@ public class CommandLineInput {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn);
String contents = Decompilers.krakatauDA.decompileClassNode(cn, cw.toByteArray());
String contents = Decompiler.KRAKATAU_BYTECODE.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -297,7 +297,7 @@ public class CommandLineInput {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn);
String contents = Decompilers.jdgui.decompileClassNode(cn, cw.toByteArray());
String contents = Decompiler.JDGUI.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
@ -316,7 +316,26 @@ public class CommandLineInput {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn);
String contents = Decompilers.smali.decompileClassNode(cn, cw.toByteArray());
String contents = Decompiler.SMALI.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
} else if (decompiler.equalsIgnoreCase("jadx")) {
System.out.println("Decompiling " + input.getAbsolutePath() + " with JADX");
BytecodeViewer.openFiles(new File[]{input}, false);
Thread.sleep(5 * 1000);
if (target.equalsIgnoreCase("all")) {
System.out.println("Coming soon.");
//Decompiler.smali.decompileToZip(output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = accept(cn);
String contents = Decompiler.JADX.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);

View File

@ -1,8 +1,13 @@
package the.bytecode.club.bytecodeviewer;
import the.bytecode.club.bytecodeviewer.gui.theme.LAFTheme;
import the.bytecode.club.bytecodeviewer.gui.theme.RSTATheme;
import java.io.File;
/**
* A collection of variables that can be configured through the settings menu or some form of UI/plugin
*
* @author Konloch
* @since 6/21/2021
*/
@ -17,6 +22,7 @@ public class Configuration
public static File krakatauTempDir;
public static File krakatauTempJar;
public static boolean displayParentInTab = false; //also change in the main GUI
public static boolean simplifiedTabNames = false;
public static boolean currentlyDumping = false;
public static boolean needsReDump = true;
public static boolean warnForEditing = false;
@ -30,4 +36,7 @@ public class Configuration
public static boolean verifyCorruptedStateOnBoot = false; //eventually may be a setting
public static long lastHotKeyExecuted = System.currentTimeMillis();
}
public static LAFTheme lafTheme = LAFTheme.LIGHT; //lightmode by default since it uses the system theme
public static RSTATheme rstaTheme = lafTheme.getRSTATheme();
}

View File

@ -10,6 +10,9 @@ import java.util.ArrayList;
import java.util.List;
/**
* General program constants, to use this class include everything as a wildcard static import:
* import static the.bytecode.club.bytecodeviewer.Constants.*;
*
* @author Konloch
* @since 6/21/2021
*/

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,8 @@ package the.bytecode.club.bytecodeviewer;
import javax.swing.JFrame;
import me.konloch.kontainer.io.DiskReader;
import me.konloch.kontainer.io.DiskWriter;
import the.bytecode.club.bytecodeviewer.gui.theme.LAFTheme;
import the.bytecode.club.bytecodeviewer.gui.theme.RSTATheme;
import static the.bytecode.club.bytecodeviewer.Constants.*;
@ -291,23 +293,48 @@ public class Settings {
String.valueOf(BytecodeViewer.viewer.ren.isSelected()), false);
DiskWriter.writeNewLine(settingsName,
String.valueOf(BytecodeViewer.viewer.viewPane1.getJADX().getEditable().isSelected()), false);
DiskWriter.writeNewLine(settingsName,
Configuration.lafTheme.name(), false);
DiskWriter.writeNewLine(settingsName,
Configuration.rstaTheme.name(), false);
DiskWriter.writeNewLine(settingsName,
String.valueOf(BytecodeViewer.viewer.simplifyNameInTabTitle.isSelected()), false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public static void preloadSettingsFile()
{
try
{
//precache the file
DiskReader.loadString(settingsName, 0, true);
//process the cached file
Configuration.lafTheme = LAFTheme.valueOf(asString(127));
Configuration.rstaTheme = RSTATheme.valueOf(asString(128));
}
catch (Exception e)
{
//ignore because errors are expected, first start up and outdated settings.
//e.printStackTrace();
}
}
public static void loadSettings() { //utilizes the Disk Reader's caching system.
try {
BytecodeViewer.viewer.rbr.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 1, true)));
BytecodeViewer.viewer.rsy.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 2, false)));
BytecodeViewer.viewer.din.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 3, false)));
BytecodeViewer.viewer.dc4.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 4, false)));
BytecodeViewer.viewer.das.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 5, false)));
BytecodeViewer.viewer.hes.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 6, false)));
BytecodeViewer.viewer.hdc.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 7, false)));
BytecodeViewer.viewer.dgs.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 8, false)));
BytecodeViewer.viewer.ner.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 9, false)));
BytecodeViewer.viewer.den.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 10, false)));
//parse the cached file from memory
BytecodeViewer.viewer.rbr.setSelected(asBoolean(1));
BytecodeViewer.viewer.rsy.setSelected(asBoolean(2));
BytecodeViewer.viewer.din.setSelected(asBoolean(3));
BytecodeViewer.viewer.dc4.setSelected(asBoolean(4));
BytecodeViewer.viewer.das.setSelected(asBoolean(5));
BytecodeViewer.viewer.hes.setSelected(asBoolean(6));
BytecodeViewer.viewer.hdc.setSelected(asBoolean(7));
BytecodeViewer.viewer.dgs.setSelected(asBoolean(8));
BytecodeViewer.viewer.ner.setSelected(asBoolean(9));
BytecodeViewer.viewer.den.setSelected(asBoolean(10));
BytecodeViewer.viewer.rgn.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 11, false)));
BytecodeViewer.viewer.bto.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 12, false)));
BytecodeViewer.viewer.nns.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 13, false)));
@ -378,30 +405,30 @@ public class Settings {
BytecodeViewer.viewer.debugHelpers.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 78, false)));
//79 is deprecated
BytecodeViewer.viewer.updateCheck.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 80, false)));
BytecodeViewer.viewer.viewPane1.setSelectedViewer(getInt(81));
BytecodeViewer.viewer.viewPane2.setSelectedViewer(getInt(82));
BytecodeViewer.viewer.viewPane3.setSelectedViewer(getInt(83));
BytecodeViewer.viewer.viewPane1.setSelectedViewer(asInt(81));
BytecodeViewer.viewer.viewPane2.setSelectedViewer(asInt(82));
BytecodeViewer.viewer.viewPane3.setSelectedViewer(asInt(83));
BytecodeViewer.viewer.refreshOnChange.setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 84, false)));
BytecodeViewer.viewer.refreshOnChange.setSelected(asBoolean(84));
boolean bool = Boolean.parseBoolean(DiskReader.loadString(settingsName, 85, false));
boolean bool = Boolean.parseBoolean(asString(85));
if (bool) {
BytecodeViewer.viewer.setExtendedState(JFrame.MAXIMIZED_BOTH);
BytecodeViewer.viewer.isMaximized = true;
}
//86 is deprecated
//87 is deprecated
Configuration.lastDirectory = DiskReader.loadString(settingsName, 88, false);
Configuration.python = DiskReader.loadString(settingsName, 89, false);
Configuration.rt = DiskReader.loadString(settingsName, 90, false);
BytecodeViewer.viewer.viewPane1.getProcyon().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 91, false)));
BytecodeViewer.viewer.viewPane1.getCFR().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 92, false)));
BytecodeViewer.viewer.viewPane1.getFern().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 93, false)));
BytecodeViewer.viewer.viewPane1.getKrakatau().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 94, false)));
BytecodeViewer.viewer.viewPane1.getSmali().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 95, false)));
BytecodeViewer.viewer.viewPane2.getProcyon().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 96, false)));
BytecodeViewer.viewer.viewPane2.getCFR().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 97, false)));
BytecodeViewer.viewer.viewPane2.getFern().getEditable().setSelected(Boolean.parseBoolean(DiskReader.loadString(settingsName, 98, false)));
Configuration.lastDirectory = asString(88);
Configuration.python = asString(89);
Configuration.rt = asString(90);
BytecodeViewer.viewer.viewPane1.getProcyon().getEditable().setSelected(asBoolean(91));
BytecodeViewer.viewer.viewPane1.getCFR().getEditable().setSelected(asBoolean(92));
BytecodeViewer.viewer.viewPane1.getFern().getEditable().setSelected(asBoolean(93));
BytecodeViewer.viewer.viewPane1.getKrakatau().getEditable().setSelected(asBoolean(94));
BytecodeViewer.viewer.viewPane1.getSmali().getEditable().setSelected(asBoolean(95));
BytecodeViewer.viewer.viewPane2.getProcyon().getEditable().setSelected(asBoolean(96));
BytecodeViewer.viewer.viewPane2.getCFR().getEditable().setSelected(asBoolean(97));
BytecodeViewer.viewer.viewPane2.getFern().getEditable().setSelected(asBoolean(98));
BytecodeViewer.viewer.viewPane2.getKrakatau().getEditable().setSelected(asBoolean(99));
BytecodeViewer.viewer.viewPane2.getSmali().getEditable().setSelected(asBoolean(100));
BytecodeViewer.viewer.viewPane3.getProcyon().getEditable().setSelected(asBoolean(101));
@ -410,26 +437,31 @@ public class Settings {
BytecodeViewer.viewer.viewPane3.getKrakatau().getEditable().setSelected(asBoolean(104));
BytecodeViewer.viewer.viewPane3.getSmali().getEditable().setSelected(asBoolean(105));
BytecodeViewer.viewer.decodeAPKResources.setSelected(asBoolean(106));
Configuration.library = DiskReader.loadString(settingsName, 107, false);
Configuration.library = asString(107);
Configuration.pingback = asBoolean(108);
BytecodeViewer.viewer.viewPane1.getJD().getEditable().setSelected(asBoolean(109));
BytecodeViewer.viewer.viewPane2.getJD().getEditable().setSelected(asBoolean(110));
BytecodeViewer.viewer.viewPane3.getJD().getEditable().setSelected(asBoolean(111));
BytecodeViewer.viewer.fontSpinner.setValue(getInt(112));
BytecodeViewer.viewer.fontSpinner.setValue(asInt(112));
Configuration.deleteForeignLibraries = asBoolean(113);
int apkDecompiler = getInt(114);
if (apkDecompiler == 0)
BytecodeViewer.viewer.apkConversionGroup.setSelected(BytecodeViewer.viewer.apkConversionDex.getModel(), true);
else if (apkDecompiler == 1)
BytecodeViewer.viewer.apkConversionGroup.setSelected(BytecodeViewer.viewer.apkConversionEnjarify.getModel(), true);
//APK Decompiler
switch(asInt(114))
{
case 0:
BytecodeViewer.viewer.apkConversionGroup.setSelected(BytecodeViewer.viewer.apkConversionDex.getModel(), true);
break;
case 1:
BytecodeViewer.viewer.apkConversionGroup.setSelected(BytecodeViewer.viewer.apkConversionEnjarify.getModel(), true);
break;
}
Configuration.python3 = DiskReader.loadString(settingsName, 115, false);
Configuration.javac = DiskReader.loadString(settingsName, 116, false);
Configuration.java = DiskReader.loadString(settingsName, 117, false);
Configuration.python3 = asString(115);
Configuration.javac = asString(116);
Configuration.java = asString(117);
BytecodeViewer.viewer.compileOnSave.setSelected(asBoolean(118));
BytecodeViewer.viewer.autoCompileOnRefresh.setSelected(asBoolean(119));
Configuration.warnForEditing = Boolean.parseBoolean(DiskReader.loadString(settingsName, 120, false));
Configuration.warnForEditing = asBoolean(120);
BytecodeViewer.viewer.showFileInTabTitle.setSelected(asBoolean(121));
Configuration.displayParentInTab = BytecodeViewer.viewer.showFileInTabTitle.isSelected();
BytecodeViewer.viewer.forcePureAsciiAsText.setSelected(asBoolean(122));
@ -437,18 +469,27 @@ public class Settings {
BytecodeViewer.viewer.showClassMethods.setSelected(asBoolean(124));
BytecodeViewer.viewer.ren.setSelected(asBoolean(125));
BytecodeViewer.viewer.viewPane1.getJADX().getEditable().setSelected(asBoolean(126));
//line 127 is used for theme on preload
//line 128 is used for theme on preload
BytecodeViewer.viewer.simplifyNameInTabTitle.setSelected(asBoolean(129));
Configuration.simplifiedTabNames = BytecodeViewer.viewer.simplifyNameInTabTitle.isSelected();
} catch (Exception e) {
//ignore because errors are expected, first start up and outdated settings.
//e.printStackTrace();
}
}
public static String asString(int lineNumber) throws Exception
{
return DiskReader.loadString(settingsName, lineNumber, false);
}
public static boolean asBoolean(int lineNumber) throws Exception
{
return Boolean.parseBoolean(DiskReader.loadString(settingsName, lineNumber, false));
}
public static int getInt(int lineNumber) throws Exception
public static int asInt(int lineNumber) throws Exception
{
return Integer.parseInt(DiskReader.loadString(settingsName, lineNumber, false));
}

View File

@ -10,10 +10,11 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.compilers.Compilers;
import the.bytecode.club.bytecodeviewer.decompilers.InternalDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.decompilers.Decompilers;
import the.bytecode.club.bytecodeviewer.plugin.preinstalled.EZInjection;
import the.bytecode.club.bytecodeviewer.util.JarUtils;
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
import static the.bytecode.club.bytecodeviewer.Constants.*;
@ -41,7 +42,6 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
*
* @author Konloch
*/
public class BytecodeViewer {
private static URLClassLoader cl;
@ -72,7 +72,7 @@ public class BytecodeViewer {
*/
public static List<Class<?>> loadClassesIntoClassLoader() {
try {
File f = new File(tempDirectory + fs + "loaded_temp.jar");
File f = new File(tempDirectory + fs + MiscUtils.randomString(12) + "loaded_temp.jar");
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), f.getAbsolutePath());
JarFile jarFile = new JarFile("" + f.getAbsolutePath());
Enumeration<JarEntry> e = jarFile.entries();
@ -201,8 +201,8 @@ public class BytecodeViewer {
*
* @return The wrapped Krakatau Decompiler instance
*/
public static Decompiler getKrakatauDecompiler() {
return Decompilers.krakatau;
public static InternalDecompiler getKrakatauDecompiler() {
return Decompiler.KRAKATAU.getDecompiler();
}
/**
@ -210,8 +210,8 @@ public class BytecodeViewer {
*
* @return The wrapped Procyon Decompiler instance
*/
public static Decompiler getProcyonDecompiler() {
return Decompilers.procyon;
public static InternalDecompiler getProcyonDecompiler() {
return Decompiler.PROCYON.getDecompiler();
}
/**
@ -219,8 +219,8 @@ public class BytecodeViewer {
*
* @return The wrapped CFR Decompiler instance
*/
public static Decompiler getCFRDecompiler() {
return Decompilers.cfr;
public static InternalDecompiler getCFRDecompiler() {
return Decompiler.CFR.getDecompiler();
}
/**
@ -228,8 +228,8 @@ public class BytecodeViewer {
*
* @return The wrapped FernFlower Decompiler instance
*/
public static Decompiler getFernFlowerDecompiler() {
return Decompilers.fernflower;
public static InternalDecompiler getFernFlowerDecompiler() {
return Decompiler.FERNFLOWER.getDecompiler();
}
/**
@ -237,8 +237,26 @@ public class BytecodeViewer {
*
* @return The wrapped Krakatau Disassembler instance
*/
public static Decompiler getKrakatauDisassembler() {
return Decompilers.krakatauDA;
public static InternalDecompiler getKrakatauDisassembler() {
return Decompiler.KRAKATAU_BYTECODE.getDecompiler();
}
/**
* Returns the wrapped JD-GUI Decompiler instance.
*
* @return The wrapped JD-GUI Decompiler instance
*/
public static InternalDecompiler getDJGUIDecompiler() {
return Decompiler.JDGUI.getDecompiler();
}
/**
* Returns the wrapped JADX Decompiler instance.
*
* @return The wrapped JADX Decompiler instance
*/
public static InternalDecompiler getJADXDecompiler() {
return Decompiler.JADX.getDecompiler();
}
/**
@ -258,13 +276,4 @@ public class BytecodeViewer {
public static the.bytecode.club.bytecodeviewer.compilers.Compiler getSmaliCompiler() {
return Compilers.smali;
}
/**
* Returns the wrapped JD-GUI Decompiler instance.
*
* @return The wrapped JD-GUI Decompiler instance
*/
public static the.bytecode.club.bytecodeviewer.decompilers.Decompiler getDJGUIDecompiler() {
return Decompilers.jdgui;
}
}

View File

@ -1,7 +1,6 @@
package the.bytecode.club.bytecodeviewer.api;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
@ -10,13 +9,14 @@ import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.gui.components.JFrameConsole;
import the.bytecode.club.bytecodeviewer.gui.components.SearchableJTextArea;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.PressKeyListener;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.ReleaseKeyListener;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@ -42,219 +42,11 @@ import the.bytecode.club.bytecodeviewer.Resources;
* @author Konloch
*/
public class PluginConsole extends JFrame {
JTextArea textArea = new JTextArea();
JPanel panel = new JPanel(new BorderLayout());
JScrollPane scrollPane = new JScrollPane();
public JCheckBox check = new JCheckBox("Exact");
final JTextField field = new JTextField();
public PluginConsole(String pluginName) {
this.setIconImages(Resources.iconList);
setTitle("Bytecode Viewer - Plugin Console - " + pluginName);
setSize(new Dimension(542, 316));
getContentPane().add(scrollPane, BorderLayout.CENTER);
scrollPane.setViewportView(textArea);
textArea.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
if ((e.getKeyCode() == KeyEvent.VK_F) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
field.requestFocus();
}
BytecodeViewer.checkHotKey(e);
}
@Override
public void keyReleased(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
JButton searchNext = new JButton();
JButton searchPrev = new JButton();
JPanel buttonPane = new JPanel(new BorderLayout());
buttonPane.add(searchNext, BorderLayout.WEST);
buttonPane.add(searchPrev, BorderLayout.EAST);
searchNext.setIcon(Resources.nextIcon);
searchPrev.setIcon(Resources.prevIcon);
panel.add(buttonPane, BorderLayout.WEST);
panel.add(field, BorderLayout.CENTER);
panel.add(check, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(field.getText(), true));
searchPrev.addActionListener(arg0 -> search(field.getText(), false));
field.addKeyListener(new KeyListener() {
@Override
public void keyReleased(KeyEvent arg0) {
if (arg0.getKeyCode() == KeyEvent.VK_ENTER)
search(field.getText(), true);
}
@Override
public void keyPressed(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
scrollPane.setColumnHeaderView(panel);
this.setLocationRelativeTo(null);
}
/**
* This was really interesting to write.
*
* @author Konloch
*/
public void search(String search, boolean next) {
try {
JTextArea area = textArea;
if (search.isEmpty()) {
highlight(area, "");
return;
}
int startLine = area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1;
int currentLine = 1;
boolean canSearch = false;
String[] test;
if (area.getText().split("\n").length >= 2)
test = area.getText().split("\n");
else
test = area.getText().split("\r");
int lastGoodLine = -1;
int firstPos = -1;
boolean found = false;
if (next) {
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (currentLine == startLine) {
canSearch = true;
} else if (s.contains(search)) {
if (canSearch) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(currentLine - 1)
.getStartOffset());
canSearch = false;
found = true;
}
if (firstPos == -1)
firstPos = currentLine;
}
currentLine++;
}
if (!found && firstPos != -1) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement().getElement(firstPos - 1)
.getStartOffset());
}
} else {
canSearch = true;
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (s.contains(search)) {
if (lastGoodLine != -1 && canSearch)
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
lastGoodLine = currentLine;
if (currentLine >= startLine)
canSearch = false;
}
currentLine++;
}
if (lastGoodLine != -1
&& area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1 == startLine) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1).getStartOffset());
}
}
highlight(area, search);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
private final DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(
new Color(255, 62, 150));
public void highlight(JTextComponent textComp, String pattern) {
if (pattern.isEmpty()) {
textComp.getHighlighter().removeAllHighlights();
return;
}
try {
Highlighter hilite = textComp.getHighlighter();
hilite.removeAllHighlights();
javax.swing.text.Document doc = textComp.getDocument();
String text = doc.getText(0, doc.getLength());
int pos = 0;
if (!check.isSelected()) {
pattern = pattern.toLowerCase();
text = text.toLowerCase();
}
// Search for pattern
while ((pos = text.indexOf(pattern, pos)) >= 0) {
// Create highlighter using private painter and apply around
// pattern
hilite.addHighlight(pos, pos + pattern.length(), painter);
pos += pattern.length();
}
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
/**
* Appends \r\n to the end of your string, then it puts it on the top.
*
* @param t the string you want to append
*/
public void appendText(String t) {
textArea.setText((textArea.getText().isEmpty() ? "" : textArea
.getText() + "\r\n")
+ t);
textArea.setCaretPosition(0);
}
/**
* Sets the text
*
* @param t the text you want set
*/
public void setText(String t) {
textArea.setText(t);
textArea.setCaretPosition(0);
public class PluginConsole extends JFrameConsole
{
public PluginConsole(String pluginName)
{
super("Bytecode Viewer - Plugin Console - " + pluginName);
}
private static final long serialVersionUID = -6556940545421437508L;

View File

@ -29,7 +29,8 @@ import org.objectweb.asm.util.TraceClassVisitor;
*
* @author Thiakil
*/
public class ASMTextifierDecompiler extends Decompiler {
public class ASMTextifierDecompiler extends InternalDecompiler
{
@Override
public String decompileClassNode(ClassNode cn, byte[] b) {
StringWriter writer = new StringWriter();

View File

@ -46,7 +46,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch
*/
public class CFRDecompiler extends Decompiler {
public class CFRDecompiler extends InternalDecompiler
{
private static final String[] WINDOWS_IS_GREAT = new String[]
{

View File

@ -1,6 +1,11 @@
package the.bytecode.club.bytecodeviewer.decompilers;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.decompilers.bytecode.ClassNodeDecompiler;
import the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent;
import javax.swing.*;
import java.util.HashMap;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@ -21,15 +26,90 @@ import org.objectweb.asm.tree.ClassNode;
***************************************************************************/
/**
* Used to represent all of the decompilers/disassemblers BCV contains.
* All of the decompilers/disassemblers BCV uses
*
* @author Konloch
*/
public abstract class Decompiler {
public abstract String decompileClassNode(ClassNode cn, byte[] b);
public abstract void decompileToZip(String sourceJar, String zipName);
}
public enum Decompiler
{
NONE(0, "None", null, (JRadioButtonMenuItem) null),
PROCYON(1, "Procyon Decompiler", new ProcyonDecompiler(), new DecompilerViewComponent("Procyon")),
CFR(2, "CFR Decompiler", new CFRDecompiler(), new DecompilerViewComponent("Procyon")),
FERNFLOWER(3, "FernFlower Decompiler", new FernFlowerDecompiler(), new DecompilerViewComponent("Procyon")),
BYTECODE(4, "Bytecode Disassembler", new ClassNodeDecompiler(), new JRadioButtonMenuItem("Bytecode")),
HEXCODE(5, "Hexcode Viewer", null, new JRadioButtonMenuItem("Hexcode")),
SMALI(6, "Smali Decompiler", new SmaliDisassembler(), new DecompilerViewComponent("Smali")),
KRAKATAU(7, "Krakatau Decompiler", new KrakatauDecompiler(), BytecodeViewer.krakatau),
KRAKATAU_BYTECODE(8, "Krakatau Disassembler", new KrakatauDisassembler(), BytecodeViewer.krakatau),
JDGUI(9, "JD-GUI Decompiler", new JDGUIDecompiler(), new DecompilerViewComponent("Bytecode")),
JADX(10, "JADX Decompiler", new JADXDecompiler(), new DecompilerViewComponent("JADX")),
ASMTextify(11, "ASM Disassembler", new ASMTextifierDecompiler(), new DecompilerViewComponent("ASM Textify")),
;
private final int decompilerIndex;
private final String decompilerName;
private final InternalDecompiler decompiler;
private final DecompilerViewComponent decompilerSelectComponent;
private final JRadioButtonMenuItem basicSelectComponent;
public static final HashMap<Integer, Decompiler> decompilersByIndex = new HashMap<>();
static
{
for(Decompiler d : values())
decompilersByIndex.put(d.decompilerIndex, d);
}
Decompiler(int decompilerIndex, String decompilerName, InternalDecompiler decompiler, DecompilerViewComponent decompilerSelectComponent) {
this.decompilerIndex = decompilerIndex;
this.decompilerName = decompilerName;
this.decompiler = decompiler;
this.decompilerSelectComponent = decompilerSelectComponent;
this.basicSelectComponent = null;
}
Decompiler(int decompilerIndex, String decompilerName, InternalDecompiler decompiler, JRadioButtonMenuItem basicSelectComponent)
{
this.decompilerIndex = decompilerIndex;
this.decompilerName = decompilerName;
this.decompiler = decompiler;
this.decompilerSelectComponent = null;
this.basicSelectComponent = basicSelectComponent;
}
public void addDecompilerToGroup(ButtonGroup group)
{
if(decompilerSelectComponent != null)
decompilerSelectComponent.addToGroup(group);
else if(basicSelectComponent != null)
group.add(basicSelectComponent);
}
public int getDecompilerIndex()
{
return decompilerIndex;
}
public String getDecompilerName()
{
return decompilerName;
}
public InternalDecompiler getDecompiler()
{
return decompiler;
}
public DecompilerViewComponent getDecompilerSelectComponent()
{
return decompilerSelectComponent;
}
public JMenuItem getMenu()
{
if(decompilerSelectComponent != null)
return decompilerSelectComponent.getMenu();
return basicSelectComponent;
}
}

View File

@ -1,18 +0,0 @@
package the.bytecode.club.bytecodeviewer.decompilers;
import the.bytecode.club.bytecodeviewer.decompilers.bytecode.ClassNodeDecompiler;
public class Decompilers {
public final static Decompiler bytecode = new ClassNodeDecompiler();
public final static Decompiler fernflower = new FernFlowerDecompiler();
public final static Decompiler procyon = new ProcyonDecompiler();
public final static Decompiler cfr = new CFRDecompiler();
public final static KrakatauDecompiler krakatau = new KrakatauDecompiler();
public final static KrakatauDisassembler krakatauDA = new KrakatauDisassembler();
public final static SmaliDisassembler smali = new SmaliDisassembler();
public final static Decompiler jdgui = new JDGUIDecompiler();
public final static Decompiler jadx = new JADXDecompiler();
public final static Decompiler textifier = new ASMTextifierDecompiler();
}

View File

@ -38,7 +38,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch
* @author WaterWolf
*/
public class FernFlowerDecompiler extends Decompiler {
public class FernFlowerDecompiler extends InternalDecompiler
{
@Override
public void decompileToZip(String sourceJar, String zipName) {

View File

@ -0,0 +1,33 @@
package the.bytecode.club.bytecodeviewer.decompilers;
import org.objectweb.asm.tree.ClassNode;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* Used to represent a decompiler/disassembler
*
* @author Konloch
*/
public abstract class InternalDecompiler
{
public abstract String decompileClassNode(ClassNode cn, byte[] b);
public abstract void decompileToZip(String sourceJar, String zipName);
}

View File

@ -38,7 +38,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
*
* @author Konloch
*/
public class JADXDecompiler extends Decompiler {
public class JADXDecompiler extends InternalDecompiler
{
@Override
public String decompileClassNode(ClassNode cn, byte[] b) {
String fileStart = tempDirectory + fs;

View File

@ -13,7 +13,6 @@ import jd.cli.util.ClassFileUtil;
import me.konloch.kontainer.io.DiskReader;
import org.jd.core.v1.ClassFileToJavaSourceDecompiler;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Constants;
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
@ -44,7 +43,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author JD-Core developers
*/
public class JDGUIDecompiler extends Decompiler {
public class JDGUIDecompiler extends InternalDecompiler
{
@Override
public String decompileClassNode(ClassNode cn, byte[] b) {

View File

@ -42,7 +42,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch
*/
public class KrakatauDecompiler extends Decompiler {
public class KrakatauDecompiler extends InternalDecompiler
{
public String quick() {
if (Configuration.library.isEmpty())

View File

@ -41,7 +41,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch
*/
public class KrakatauDisassembler extends Decompiler {
public class KrakatauDisassembler extends InternalDecompiler
{
public String decompileClassNode(File krakatauTempJar, File krakatauTempDir, ClassNode cn) {
if (Configuration.python.isEmpty()) {

View File

@ -61,9 +61,10 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch
* @author DeathMarine
*/
public class ProcyonDecompiler extends Decompiler {
public DecompilerSettings getDecompilerSettings() {
public class ProcyonDecompiler extends InternalDecompiler
{
public DecompilerSettings getDecompilerSettings()
{
DecompilerSettings settings = new DecompilerSettings();
settings.setAlwaysGenerateExceptionVariableForCatchBlocks(BytecodeViewer.viewer.alwaysGenerateExceptionVars.isSelected());
settings.setExcludeNestedTypes(BytecodeViewer.viewer.excludeNestedTypes.isSelected());

View File

@ -9,7 +9,6 @@ import java.util.Objects;
import me.konloch.kontainer.io.DiskReader;
import org.apache.commons.io.FileUtils;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.util.Dex2Jar;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
@ -40,7 +39,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Konloch
*/
public class SmaliDisassembler extends Decompiler {
public class SmaliDisassembler extends InternalDecompiler
{
public String decompileClassNode(FileContainer container, ClassNode cn, byte[] b) {
String exception = "";

View File

@ -9,7 +9,7 @@ import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.MethodNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.decompilers.InternalDecompiler;
import static the.bytecode.club.bytecodeviewer.Constants.*;
@ -36,7 +36,8 @@ import static the.bytecode.club.bytecodeviewer.Constants.*;
* @author Bibl
*/
public class ClassNodeDecompiler extends Decompiler {
public class ClassNodeDecompiler extends InternalDecompiler
{
@Override
public String decompileClassNode(ClassNode cn, byte[] b) {

View File

@ -1,328 +0,0 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.imgscalr.Scalr;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.gui.hexviewer.JHexEditor;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.Language;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* Represents any open non-class file.
*
* @author Konloch
*/
public class FileViewer extends Viewer {
private static final long serialVersionUID = 6103372882168257164L;
String name;
private final byte[] contents;
RSyntaxTextArea panelArea = new RSyntaxTextArea();
JPanel panel = new JPanel(new BorderLayout());
JPanel panel2 = new JPanel(new BorderLayout());
public JCheckBox check = new JCheckBox("Exact");
final JTextField field = new JTextField();
public BufferedImage image;
boolean canRefresh = false;
public TabbedPane tabbedPane;
public void setContents() {
String name = this.name.toLowerCase();
panelArea.setCodeFoldingEnabled(true);
panelArea.setAntiAliasingEnabled(true);
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
if ((e.getKeyCode() == KeyEvent.VK_F) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
field.requestFocus();
}
BytecodeViewer.checkHotKey(e);
}
@Override
public void keyReleased(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
String contentsS = new String(contents);
if (!isPureAscii(contentsS)) {
if (name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") ||
name.endsWith(".gif") || name.endsWith(".tif") || name.endsWith(".bmp")) {
canRefresh = true;
try {
image = ImageIO.read(new ByteArrayInputStream(contents)); //gifs fail cause of this
JLabel label = new JLabel("", new ImageIcon(image), JLabel.CENTER);
panel2.add(label, BorderLayout.CENTER);
panel2.addMouseWheelListener(e -> {
int notches = e.getWheelRotation();
if (notches < 0) {
image = Scalr.resize(image, Scalr.Method.SPEED, image.getWidth() + 10,
image.getHeight() + 10);
} else {
image = Scalr.resize(image, Scalr.Method.SPEED, image.getWidth() - 10,
image.getHeight() - 10);
}
panel2.removeAll();
JLabel label1 = new JLabel("", new ImageIcon(image), JLabel.CENTER);
panel2.add(label1, BorderLayout.CENTER);
panel2.updateUI();
});
return;
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
} else if (BytecodeViewer.viewer.forcePureAsciiAsText.isSelected()) {
JHexEditor hex = new JHexEditor(contents);
panel2.add(hex);
return;
}
}
panelArea.setSyntaxEditingStyle(Language.detectLanguage(name, contentsS).getSyntaxConstant());
panelArea.setText(contentsS);
panelArea.setCaretPosition(0);
scrollPane.setColumnHeaderView(panel);
panel2.add(scrollPane);
}
static CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); // or "ISO-8859-1" for ISO Latin 1
public static boolean isPureAscii(String v) {
return asciiEncoder.canEncode(v);
}
public FileViewer(final FileContainer container, final String name, final byte[] contents) {
this.name = name;
this.contents = contents;
this.container = container;
this.setName(name);
this.setLayout(new BorderLayout());
this.add(panel2, BorderLayout.CENTER);
JButton searchNext = new JButton();
JButton searchPrev = new JButton();
JPanel buttonPane = new JPanel(new BorderLayout());
buttonPane.add(searchNext, BorderLayout.WEST);
buttonPane.add(searchPrev, BorderLayout.EAST);
searchNext.setIcon(Resources.nextIcon);
searchPrev.setIcon(Resources.prevIcon);
panel.add(buttonPane, BorderLayout.WEST);
panel.add(field, BorderLayout.CENTER);
panel.add(check, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(field.getText(), true));
searchPrev.addActionListener(arg0 -> search(field.getText(), false));
field.addKeyListener(new KeyListener() {
@Override
public void keyReleased(KeyEvent arg0) {
if (arg0.getKeyCode() == KeyEvent.VK_ENTER)
search(field.getText(), true);
}
@Override
public void keyPressed(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
setContents();
}
/**
* This was really interesting to write.
*
* @author Konloch
*/
public void search(String search, boolean next) {
try {
JTextArea area = panelArea;
if (search.isEmpty()) {
highlight(area, "");
return;
}
int startLine = area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1;
int currentLine = 1;
boolean canSearch = false;
String[] test;
if (area.getText().split("\n").length >= 2)
test = area.getText().split("\n");
else
test = area.getText().split("\r");
int lastGoodLine = -1;
int firstPos = -1;
boolean found = false;
if (next) {
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (currentLine == startLine) {
canSearch = true;
} else if (s.contains(search)) {
if (canSearch) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(currentLine - 1)
.getStartOffset());
canSearch = false;
found = true;
}
if (firstPos == -1)
firstPos = currentLine;
}
currentLine++;
}
if (!found && firstPos != -1) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement().getElement(firstPos - 1)
.getStartOffset());
}
} else {
canSearch = true;
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (s.contains(search)) {
if (lastGoodLine != -1 && canSearch)
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
lastGoodLine = currentLine;
if (currentLine >= startLine)
canSearch = false;
}
currentLine++;
}
if (lastGoodLine != -1
&& area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1 == startLine) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1).getStartOffset());
}
}
highlight(area, search);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
private final DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(
new Color(255, 62, 150));
public void highlight(JTextComponent textComp, String pattern) {
if (pattern.isEmpty()) {
textComp.getHighlighter().removeAllHighlights();
return;
}
try {
Highlighter hilite = textComp.getHighlighter();
hilite.removeAllHighlights();
javax.swing.text.Document doc = textComp.getDocument();
String text = doc.getText(0, doc.getLength());
int pos = 0;
if (!check.isSelected()) {
pattern = pattern.toLowerCase();
text = text.toLowerCase();
}
// Search for pattern
while ((pos = text.indexOf(pattern, pos)) >= 0) {
// Create highlighter using private painter and apply around
// pattern
hilite.addHighlight(pos, pos + pattern.length(), painter);
pos += pattern.length();
}
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public void refresh(JButton src) {
if (!canRefresh) {
src.setEnabled(true);
return;
}
panel2.removeAll();
try {
image = ImageIO.read(new ByteArrayInputStream(contents));
} catch (IOException e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
JLabel label = new JLabel("", new ImageIcon(image), JLabel.CENTER);
panel2.add(label, BorderLayout.CENTER);
panel2.updateUI();
src.setEnabled(true);
}
}

View File

@ -4,22 +4,9 @@ import java.awt.Dimension;
import java.awt.KeyboardFocusManager;
import java.io.File;
import java.util.ArrayList;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import org.objectweb.asm.tree.ClassNode;
@ -28,11 +15,17 @@ import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.Settings;
import the.bytecode.club.bytecodeviewer.api.ExceptionUI;
import the.bytecode.club.bytecodeviewer.gui.extras.AboutWindow;
import the.bytecode.club.bytecodeviewer.gui.extras.RunOptions;
import the.bytecode.club.bytecodeviewer.gui.components.VisibleComponent;
import the.bytecode.club.bytecodeviewer.gui.components.AboutWindow;
import the.bytecode.club.bytecodeviewer.gui.components.RunOptions;
import the.bytecode.club.bytecodeviewer.gui.plugins.MaliciousCodeScannerOptions;
import the.bytecode.club.bytecodeviewer.gui.plugins.ReplaceStringsOptions;
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane;
import the.bytecode.club.bytecodeviewer.gui.resourcesearch.SearchBoxPane;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.DecompilerSelectionPane;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.WorkPaneMainComponent;
import the.bytecode.club.bytecodeviewer.gui.theme.LAFTheme;
import the.bytecode.club.bytecodeviewer.gui.theme.RSTATheme;
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameClasses;
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameFields;
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameMethods;
@ -80,7 +73,7 @@ public class MainViewerGUI extends JFrame {
//main UI components
private static final ArrayList<VisibleComponent> uiComponents = new ArrayList<>();
public final WorkPane workPane = new WorkPane();
public final WorkPaneMainComponent workPane = new WorkPaneMainComponent();
public final ResourceListPane resourcePane = new ResourceListPane();
public final SearchBoxPane searchBoxPane = new SearchBoxPane();
public JSplitPane splitPane1;
@ -108,9 +101,9 @@ public class MainViewerGUI extends JFrame {
//all of the view main menu components
public final JMenu viewMainMenu = new JMenu("View");
public final ViewPane viewPane1 = new ViewPane(1);
public final ViewPane viewPane2 = new ViewPane(2);
public final ViewPane viewPane3 = new ViewPane(3);
public final DecompilerSelectionPane viewPane1 = new DecompilerSelectionPane(1);
public final DecompilerSelectionPane viewPane2 = new DecompilerSelectionPane(2);
public final DecompilerSelectionPane viewPane3 = new DecompilerSelectionPane(3);
//all of the plugins main menu components
public final JMenu pluginsMainMenu = new JMenu("Plugins");
@ -132,6 +125,8 @@ public class MainViewerGUI extends JFrame {
public final JRadioButtonMenuItem apkConversionEnjarify = new JRadioButtonMenuItem("Enjarify");
public final JMenu fontSize = new JMenu("Font Size");
public final JSpinner fontSpinner = new JSpinner();
public final JMenu rstaTheme = new JMenu("Text Area Theme");
public final JMenu lafTheme = new JMenu("Window Theme");
//BCV settings
public final JCheckBoxMenuItem refreshOnChange = new JCheckBoxMenuItem("Refresh On View Change");
private final JCheckBoxMenuItem deleteForeignOutdatedLibs = new JCheckBoxMenuItem("Delete Foreign/Outdated Libs");
@ -147,11 +142,14 @@ public class MainViewerGUI extends JFrame {
public final JMenuItem setOptionalLibrary = new JMenuItem("Set Optional Library Folder");
public final JCheckBoxMenuItem compileOnSave = new JCheckBoxMenuItem("Compile On Save");
public final JCheckBoxMenuItem showFileInTabTitle = new JCheckBoxMenuItem("Show File In Tab Title");
public final JCheckBoxMenuItem simplifyNameInTabTitle = new JCheckBoxMenuItem("Simplify Name In Tab Title");
public final JCheckBoxMenuItem forcePureAsciiAsText = new JCheckBoxMenuItem("Force Pure Ascii As Text");
public final JCheckBoxMenuItem autoCompileOnRefresh = new JCheckBoxMenuItem("Compile On Refresh");
public final JCheckBoxMenuItem decodeAPKResources = new JCheckBoxMenuItem("Decode APK Resources");
public final JCheckBoxMenuItem synchronizedViewing = new JCheckBoxMenuItem("Synchronized Viewing");
public final JCheckBoxMenuItem showClassMethods = new JCheckBoxMenuItem("Show Class Methods");
public final Map<RSTATheme, JRadioButtonMenuItem> rstaThemes = new HashMap<>();
public final Map<LAFTheme, JRadioButtonMenuItem> lafThemes = new HashMap<>();
//CFIDE settings
public final JCheckBoxMenuItem appendBracketsToLabels = new JCheckBoxMenuItem("Append Brackets To Labels");
public JCheckBoxMenuItem debugHelpers = new JCheckBoxMenuItem("Debug Helpers");
@ -384,6 +382,7 @@ public class MainViewerGUI extends JFrame {
public void buildViewMenu()
{
rootMenu.add(viewMainMenu);
viewMainMenu.add(visualSettings);
viewMainMenu.add(viewPane1.menu);
viewMainMenu.add(viewPane2.menu);
viewMainMenu.add(viewPane3.menu);
@ -393,8 +392,8 @@ public class MainViewerGUI extends JFrame {
{
rootMenu.add(settingsMainMenu);
settingsMainMenu.add(visualSettings);
settingsMainMenu.add(new JSeparator());
//settingsMainMenu.add(visualSettings);
//settingsMainMenu.add(new JSeparator());
settingsMainMenu.add(compileOnSave);
settingsMainMenu.add(autoCompileOnRefresh);
settingsMainMenu.add(refreshOnChange);
@ -420,8 +419,54 @@ public class MainViewerGUI extends JFrame {
fontSpinner.setSize(new Dimension(42, 20));
fontSpinner.setModel(new SpinnerNumberModel(12, 1, null, 1));
fontSize.add(fontSpinner);
ButtonGroup rstaGroup = new ButtonGroup();
for (RSTATheme t : RSTATheme.values())
{
JRadioButtonMenuItem item = new JRadioButtonMenuItem(t.getReadableName());
if (Configuration.rstaTheme.equals(t))
item.setSelected(true);
rstaGroup.add(item);
item.addActionListener(e ->
{
Configuration.rstaTheme = t;
item.setSelected(true);
Settings.saveSettings();
});
rstaThemes.put(t, item);
rstaTheme.add(item);
}
ButtonGroup lafGroup = new ButtonGroup();
for (LAFTheme theme : LAFTheme.values())
{
JRadioButtonMenuItem item = new JRadioButtonMenuItem(theme.getReadableName());
if (Configuration.lafTheme.equals(theme))
item.setSelected(true);
lafGroup.add(item);
item.addActionListener(e ->
{
Configuration.lafTheme = theme;
Configuration.rstaTheme = theme.getRSTATheme();
rstaThemes.get(Configuration.rstaTheme).setSelected(true);
item.setSelected(true);
Settings.saveSettings();
});
lafThemes.put(theme, item);
lafTheme.add(item);
}
visualSettings.add(lafTheme);
visualSettings.add(rstaTheme);
visualSettings.add(fontSize);
visualSettings.add(showFileInTabTitle);
visualSettings.add(simplifyNameInTabTitle);
visualSettings.add(synchronizedViewing);
visualSettings.add(showClassMethods);
@ -527,6 +572,10 @@ public class MainViewerGUI extends JFrame {
Configuration.displayParentInTab = BytecodeViewer.viewer.showFileInTabTitle.isSelected();
Settings.saveSettings();
});
simplifyNameInTabTitle.addActionListener(arg0 -> {
Configuration.simplifiedTabNames = BytecodeViewer.viewer.simplifyNameInTabTitle.isSelected();
Settings.saveSettings();
});
}
public void buildPluginMenu()

View File

@ -1,352 +0,0 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
import javax.swing.plaf.basic.BasicButtonUI;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* Component to be used as tabComponent; Contains a JLabel to show the text and a JButton to close the tab it belongs to
*
* @author Konloch
* @author WaterWolf
*/
public class TabbedPane extends JPanel {
private static final long serialVersionUID = -4774885688297538774L;
public static final Color BLANK = new Color(0, 0, 0, 0);
private final JTabbedPane pane;
public final JLabel label;
private DelayTabbedPaneThread probablyABadIdea;
private long startedDragging = 0;
private static long zero = System.currentTimeMillis();
public String tabName;
public String fileContainerName;
public TabbedPane(String fileContainerName, String name, final JTabbedPane pane) {
// unset default FlowLayout' gaps
super(new FlowLayout(FlowLayout.LEFT, 0, 0));
this.tabName = name;
this.fileContainerName = fileContainerName;
if (pane == null)
throw new NullPointerException("TabbedPane is null");
this.pane = pane;
setOpaque(false);
// make JLabel read titles from JTabbedPane
label = new JLabel() {
private static final long serialVersionUID = -5511025206527893360L;
@Override
public String getText() {
final int i = pane.indexOfTabComponent(TabbedPane.this);
if (i != -1)
return pane.getTitleAt(i);
return null;
}
@Override
public Dimension getPreferredSize() {
Dimension realDimension = super.getPreferredSize();
if (realDimension.getWidth() >= 400)
return new Dimension(400, 20);
else
return realDimension;
}
};
this.add(label);
// add more space between the label and the button
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
// tab button
JButton button = new TabButton();
this.add(button);
// add more space to the top of the component
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
JPopupMenu pop_up = new JPopupMenu();
JMenuItem closealltab = new JMenuItem("Close All But This: " + name);
JMenuItem closetab = new JMenuItem("Close Tab: " + name);
closetab.addActionListener(e -> {
String name1 = e.getActionCommand().split(": ")[1];
final int i = pane.indexOfTab(name1);
if (i != -1)
pane.remove(i);
});
closealltab.addActionListener(e -> {
String name12 = e.getActionCommand().split(": ")[1];
while (true) {
int thisID = pane.indexOfTab(name12);
if (pane.getTabCount() <= 1) {
return;
}
if (thisID != 0)
pane.remove(0);
else
pane.remove(1);
}
});
pop_up.add(closealltab);
pop_up.add(closetab);
//setComponentPopupMenu(pop_up);
button.setComponentPopupMenu(pop_up);
button.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getModifiers() == InputEvent.ALT_MASK) {
if (System.currentTimeMillis() - zero >= 100) {
zero = System.currentTimeMillis();
final int i = pane.indexOfTabComponent(TabbedPane.this);
if (i != -1)
pane.remove(i);
}
}
}
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
@Override
public void mousePressed(MouseEvent arg0) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
});
//tab dragging
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
{
/*label.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent arg0) {
}
@Override public void mouseExited(MouseEvent arg0) {
}
@Override public void mousePressed(MouseEvent e) {
onMousePressed(e);
}
@Override public void mouseReleased(MouseEvent e) {
stopDragging(e.getX(), e.getY());
}
});*/
this.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent arg0) {
}
@Override public void mouseExited(MouseEvent arg0) {
}
@Override public void mousePressed(MouseEvent e) {
onMousePressed(e);
}
@Override public void mouseReleased(MouseEvent e) {
stopDragging(e.getX(), e.getY());
}
});
}
//middle click close
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
{
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON2) {
final int i = pane.indexOfTabComponent(TabbedPane.this);
if (i != -1) {
pane.remove(i);
}
}
}
});
}
}
private void stopDragging(int mouseX, int mouseY) {
if (System.currentTimeMillis() - startedDragging >= 210) {
Rectangle bounds = new Rectangle(1, 1, mouseX, mouseY);
System.out.println("debug-5: " + mouseX + ", " + mouseY);
int totalTabs = BytecodeViewer.viewer.workPane.tabs.getTabCount();
int index = -1;
for (int i = 0; i < totalTabs; i++) {
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds())) {
index = i; //replace this tabs position
}
}
if (index == -1) {
for (int i = 0; i < totalTabs; i++) {
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
//do some check to see if it's past the X or Y
if (c != null) {
System.out.println("debug-6: " + c.getBounds());
}
}
}
if (index != -1) {
BytecodeViewer.viewer.workPane.tabs.remove(this);
BytecodeViewer.viewer.workPane.tabs.setTabComponentAt(index, this);
}
}
SwingUtilities.invokeLater(() ->
{
label.setBackground(BLANK);
label.updateUI();
});
}
public void onMousePressed(MouseEvent e)
{
BytecodeViewer.viewer.workPane.tabs.dispatchEvent(e);
if(e.getButton() == 1)
{
startedDragging = System.currentTimeMillis();
//dragging = true;
if (probablyABadIdea != null)
{
probablyABadIdea.stopped = true;
}
probablyABadIdea = new DelayTabbedPaneThread(TabbedPane.this);
probablyABadIdea.start();
repaint();
System.out.println(e.getX()+", "+e.getY());
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
for(int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if(c != null && bounds.intersects(c.getBounds()))
{
BytecodeViewer.viewer.workPane.tabs.setSelectedIndex(i);
}
}
}
else
{
stopDragging(e.getX(), e.getY());
}
}
private class TabButton extends JButton implements ActionListener {
private static final long serialVersionUID = -4492967978286454159L;
public TabButton() {
final int size = 17;
setPreferredSize(new Dimension(size, size));
setToolTipText("Close this tab");
// Make the button looks the same for all Laf's
setUI(new BasicButtonUI());
// Make it transparent
setContentAreaFilled(false);
// No need to be focusable
setFocusable(false);
setBorder(BorderFactory.createEtchedBorder());
setBorderPainted(false);
// Making nice rollover effect
// we use the same listener for all buttons
addMouseListener(buttonMouseListener);
setRolloverEnabled(true);
// Close the proper tab by clicking the button
addActionListener(this);
}
@Override
public void actionPerformed(final ActionEvent e) {
final int i = pane.indexOfTabComponent(TabbedPane.this);
if (i != -1) {
pane.remove(i);
}
}
// we don't want to update UI for this button
@Override
public void updateUI() {
}
// paint the cross
@Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g.create();
// shift the image for pressed buttons
if (getModel().isPressed()) {
g2.translate(1, 1);
}
g2.setStroke(new BasicStroke(2));
g2.setColor(Color.BLACK);
if (getModel().isRollover()) {
g2.setColor(Color.MAGENTA);
}
final int delta = 6;
g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight()
- delta - 1);
g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight()
- delta - 1);
g2.dispose();
}
}
private final static MouseListener buttonMouseListener = new MouseAdapter() {
@Override
public void mouseEntered(final MouseEvent e) {
final Component component = e.getComponent();
if (component instanceof AbstractButton) {
final AbstractButton button = (AbstractButton) component;
button.setBorderPainted(true);
}
}
@Override
public void mouseExited(final MouseEvent e) {
final Component component = e.getComponent();
if (component instanceof AbstractButton) {
final AbstractButton button = (AbstractButton) component;
button.setBorderPainted(false);
}
}
};
}

View File

@ -1,294 +0,0 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashMap;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import static the.bytecode.club.bytecodeviewer.Constants.BLOCK_TAB_MENU;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* The pane that contains all of the classes as tabs.
*
* @author Konloch
* @author WaterWolf
*/
public class WorkPane extends VisibleComponent implements ActionListener {
private static final long serialVersionUID = 6542337997679487946L;
public JTabbedPane tabs;
JPanel buttonPanel;
public JButton refreshClass;
HashMap<String, Integer> workingOn = new HashMap<>();
public WorkPane() {
super("WorkPanel");
setTitle("Work Space");
this.tabs = new JTabbedPane();
JPopupMenu pop_up = new JPopupMenu() {
@Override
public void setVisible(boolean b) {
super.setVisible(b);
}
};
JMenuItem closealltab = new JMenuItem("Close All But This");
JMenuItem closetab = new JMenuItem("Close Tab");
closetab.addActionListener(e -> {
/*String name = e.getActionCommand().split(": ")[1];
final int i = pane.indexOfTab(name);
if (i != -1)
pane.remove(i);*/
});
closealltab.addActionListener(e -> {
String name = e.getActionCommand().split(": ")[1];
System.out.println("debug-3: " + name);
while (true) {
int thisID = tabs.indexOfTab(name);
if (tabs.getTabCount() <= 1) {
return;
}
if (thisID != 0)
tabs.remove(0);
else
tabs.remove(1);
}
});
tabs.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == 3) {
if (BLOCK_TAB_MENU)
return;
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
Point point = tabs.getMousePosition();
System.out.println("debug-1: " + point);
for (int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++) {
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds())) {
pop_up.setVisible(true);
closealltab.setText("Close All But This: " + ((TabbedPane) c).tabName);
closetab.setText("Close Tab: " + ((TabbedPane) c).tabName);
//do something with this shit
//BytecodeViewer.viewer.workPane.tabs.setSelectedIndex(i);
} else {
pop_up.setVisible(false);
}
}
System.out.println("debug-2: " + e.getX() + ", " + e.getY());
}
}
@Override
public void mouseReleased(MouseEvent e) {
}
});
pop_up.add(closealltab);
pop_up.add(closetab);
if (!BLOCK_TAB_MENU)
tabs.setComponentPopupMenu(pop_up);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(tabs, BorderLayout.CENTER);
buttonPanel = new JPanel(new FlowLayout());
refreshClass = new JButton("Refresh");
refreshClass.addActionListener(this);
buttonPanel.add(refreshClass);
buttonPanel.setVisible(false);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
tabs.addContainerListener(new ContainerListener() {
@Override
public void componentAdded(final ContainerEvent e) {
}
@Override
public void componentRemoved(final ContainerEvent e) {
final Component c = e.getChild();
if (c instanceof ClassViewer) {
String containerName = ((ClassViewer) c).container.name + ">";
String fileName = ((ClassViewer) c).name;
if (fileName.startsWith(containerName)) {
workingOn.remove(fileName);
} else {
workingOn.remove(containerName + fileName);
}
}
if (c instanceof FileViewer) {
String containerName = ((FileViewer) c).container.name + ">";
String fileName = ((FileViewer) c).name;
if (fileName.startsWith(containerName)) {
workingOn.remove(fileName);
} else {
workingOn.remove(containerName + fileName);
}
}
}
});
tabs.addChangeListener(arg0 -> buttonPanel.setVisible(tabs.getSelectedIndex() != -1));
this.setVisible(true);
}
public void addWorkingFile(final FileContainer container, String name, final ClassNode cn) {
String workingName = container.name + ">" + name;
String containerName = name;
if (Configuration.displayParentInTab)
containerName = container.name + ">" + name;
if (!workingOn.containsKey(workingName)) {
final ClassViewer tabComp = new ClassViewer(container, containerName, cn);
tabs.add(tabComp);
final int tabCount = tabs.indexOfComponent(tabComp);
workingOn.put(workingName, tabCount);
TabbedPane tabbedPane = new TabbedPane(container.name, name, tabs);
tabComp.tabbedPane = tabbedPane;
tabs.setTabComponentAt(tabCount, tabbedPane);
tabs.setSelectedIndex(tabCount);
} else {
tabs.setSelectedIndex(workingOn.get(workingName));
}
}
public void addFile(final FileContainer container, String name, byte[] contents) {
String workingName = container.name + ">" + name;
if (Configuration.displayParentInTab)
name = container.name + ">" + name;
if (contents == null) //a directory
return;
if (!workingOn.containsKey(workingName)) {
final FileViewer tabComp = new FileViewer(container, name, contents);
tabs.add(tabComp);
final int tabCount = tabs.indexOfComponent(tabComp);
workingOn.put(workingName, tabCount);
TabbedPane tabbedPane = new TabbedPane(null, name, tabs);
tabComp.tabbedPane = tabbedPane;
tabs.setTabComponentAt(tabCount, tabbedPane);
tabs.setSelectedIndex(tabCount);
} else {
try {
tabs.setSelectedIndex(workingOn.get(workingName));
} catch (Exception e) {
//workingOn.remove(workingName);
e.printStackTrace();
}
}
}
public Viewer getCurrentViewer() {
return (Viewer) tabs.getSelectedComponent();
}
public java.awt.Component[] getLoadedViewers() {
return tabs.getComponents();
}
@Override
public void actionPerformed(final ActionEvent arg0) {
Thread t = new Thread(() -> {
if (BytecodeViewer.viewer.autoCompileOnRefresh.isSelected())
try {
if (!BytecodeViewer.compile(false))
return;
} catch (NullPointerException ignored) {
}
final JButton src = (JButton) arg0.getSource();
if (src == refreshClass) {
final Component tabComp = tabs.getSelectedComponent();
if (tabComp != null) {
if (tabComp instanceof ClassViewer) {
src.setEnabled(false);
BytecodeViewer.viewer.updateBusyStatus(true);
((ClassViewer) tabComp).startPaneUpdater(src);
BytecodeViewer.viewer.updateBusyStatus(false);
} else if (tabComp instanceof FileViewer) {
src.setEnabled(false);
BytecodeViewer.viewer.updateBusyStatus(true);
((FileViewer) tabComp).refresh(src);
BytecodeViewer.viewer.updateBusyStatus(false);
}
}
}
});
t.start();
}
public void resetWorkspace() {
tabs.removeAll();
tabs.updateUI();
}
}

View File

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui.extras;
package the.bytecode.club.bytecodeviewer.gui.components;
import java.awt.CardLayout;
import java.awt.Color;

View File

@ -0,0 +1,35 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
/**
* @author Konloch
* @since 6/25/2021
*/
public class ButtonHoverAnimation extends MouseAdapter
{
@Override
public void mouseEntered(final MouseEvent e)
{
final Component component = e.getComponent();
if (component instanceof AbstractButton)
{
final AbstractButton button = (AbstractButton) component;
button.setBorderPainted(true);
}
}
@Override
public void mouseExited(final MouseEvent e)
{
final Component component = e.getComponent();
if (component instanceof AbstractButton)
{
final AbstractButton button = (AbstractButton) component;
button.setBorderPainted(false);
}
}
}

View File

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui;
package the.bytecode.club.bytecodeviewer.gui.components;
import the.bytecode.club.bytecodeviewer.util.RefreshWorkPane;
@ -33,6 +33,7 @@ public class DecompilerViewComponent
menu.add(java);
if(hasBytecodeOption)
menu.add(bytecode);
menu.add(new JSeparator());
menu.add(editable);

View File

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui.extras;
package the.bytecode.club.bytecodeviewer.gui.components;
import java.awt.Dimension;
import javax.swing.BoxLayout;

View File

@ -0,0 +1,18 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import javax.swing.*;
import java.awt.*;
/**
* Display an image on a JLabel element
*
* @author Konloch
* @since 6/25/2021
*/
public class ImageJLabel extends JLabel
{
public ImageJLabel(Image image)
{
super("", new ImageIcon(image), JLabel.CENTER);
}
}

View File

@ -0,0 +1,59 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import the.bytecode.club.bytecodeviewer.Resources;
import javax.swing.*;
import java.awt.*;
/**
* A simple swing JFrame console
*
* @author Konloch
* @since 6/25/2021
*/
public class JFrameConsole extends JFrame
{
private final SearchableJTextArea textArea = new SearchableJTextArea();
public JFrameConsole(String title)
{
this.setIconImages(Resources.iconList);
setTitle(title);
setSize(new Dimension(542, 316));
getContentPane().add(textArea.getScrollPane(), BorderLayout.CENTER);
this.setLocationRelativeTo(null);
}
/**
* Appends \r\n to the end of your string, then it puts it on the top.
*
* @param t the string you want to append
*/
public void appendText(String t)
{
setText((textArea.getText().isEmpty()
? ""
: textArea.getText() + "\r\n"
) + t);
}
/**
* Sets the text
*
* @param t the text you want set
*/
public void setText(String t)
{
textArea.setText(t);
textArea.setCaretPosition(0);
}
public SearchableJTextArea getTextArea()
{
return textArea;
}
private static final long serialVersionUID = -5056940543411437508L;
}

View File

@ -0,0 +1,62 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import java.io.PrintStream;
import static the.bytecode.club.bytecodeviewer.Constants.nl;
/**
* A swing console that can print out from PrintStreams
*
* @author Konloch
* @since 6/25/2021
*/
public class JFrameConsolePrintStream extends JFrameConsole
{
private final PrintStream originalOut;
private final PrintStream newPrintStream;
private final JTextAreaOutputStream s;
public JFrameConsolePrintStream(String title, PrintStream originalOut)
{
super(title);
this.originalOut = originalOut;
s = new JTextAreaOutputStream(getTextArea());
newPrintStream = new PrintStream(s);
}
public void finished()
{
if (originalOut != null)
System.setErr(originalOut);
}
public PrintStream getNewPrintStream()
{
return newPrintStream;
}
public void pretty()
{
s.update();
String[] test;
if (getTextArea().getText().split("\n").length >= 2)
test = getTextArea().getText().split("\n");
else
test = getTextArea().getText().split("\r");
StringBuilder replace = new StringBuilder();
for (String s : test)
{
if (s.startsWith("File '"))
{
String[] split = s.split("'");
String start = split[0] + "'" + split[1] + "', ";
s = s.substring(start.length());
}
replace.append(s).append(nl);
}
setText(replace.toString());
}
}

View File

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui.extras;
package the.bytecode.club.bytecodeviewer.gui.components;
import javax.swing.*;
import java.io.IOException;

View File

@ -0,0 +1,33 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import javax.swing.*;
import java.awt.*;
/**
* @author Konloch
* @since 6/25/2021
*/
public class MaxWidthJLabel extends JLabel
{
private final int width;
private final int height;
public MaxWidthJLabel(String title, int width, int height)
{
super(title);
this.width = width;
this.height = height;
}
@Override
public Dimension getPreferredSize()
{
Dimension realDimension = super.getPreferredSize();
if (realDimension.getWidth() >= width)
return new Dimension(width, height);
else
return realDimension;
}
private static final long serialVersionUID = -5511025206527893360L;
}

View File

@ -0,0 +1,33 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import the.bytecode.club.bytecodeviewer.gui.util.PaneUpdaterThread;
import the.bytecode.club.bytecodeviewer.util.MethodParser;
import javax.swing.*;
import java.awt.*;
/**
* @author Konloch
* @author Waterwolf
* @since 6/24/2021
*/
public class MethodsRenderer extends JLabel implements ListCellRenderer<Object>
{
private final PaneUpdaterThread paneUpdaterThread;
public MethodsRenderer(PaneUpdaterThread paneUpdaterThread)
{
this.paneUpdaterThread = paneUpdaterThread;
setOpaque(true);
}
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
boolean cellHasFocus)
{
MethodParser methods = paneUpdaterThread.viewer.methods.get(paneUpdaterThread.decompilerViewIndex);
MethodParser.Method method = methods.getMethod((Integer) value);
setText(method.toString());
return this;
}
}

View File

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui.extras;
package the.bytecode.club.bytecodeviewer.gui.components;
import java.awt.Dimension;
import javax.swing.JButton;

View File

@ -0,0 +1,92 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.PressKeyListener;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.ReleaseKeyListener;
import the.bytecode.club.bytecodeviewer.util.JTextAreaUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
/**
* Searching on a JTextArea using swing highlighting
*
* @author Konloch
* @since 6/25/2021
*/
public class SearchableJTextArea extends JTextArea
{
private final JScrollPane scrollPane = new JScrollPane();
private final JPanel searchPanel = new JPanel(new BorderLayout());
private final JTextField searchInput = new JTextField();
private final JCheckBox caseSensitiveSearch = new JCheckBox("Exact");
public SearchableJTextArea()
{
scrollPane.setViewportView(this);
scrollPane.setColumnHeaderView(searchPanel);
JButton searchNext = new JButton();
searchNext.setIcon(Resources.nextIcon);
JButton searchPrev = new JButton();
searchPrev.setIcon(Resources.prevIcon);
JPanel buttonPane = new JPanel(new BorderLayout());
buttonPane.add(searchNext, BorderLayout.WEST);
buttonPane.add(searchPrev, BorderLayout.EAST);
searchPanel.add(buttonPane, BorderLayout.WEST);
searchPanel.add(searchInput, BorderLayout.CENTER);
searchPanel.add(caseSensitiveSearch, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(searchInput.getText(), true, caseSensitiveSearch.isSelected()));
searchPrev.addActionListener(arg0 -> search(searchInput.getText(), false, caseSensitiveSearch.isSelected()));
searchInput.addKeyListener(new ReleaseKeyListener(keyEvent ->
{
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
search(searchInput.getText(), true, caseSensitiveSearch.isSelected());
}));
addKeyListener(new PressKeyListener(keyEvent ->
{
if ((keyEvent.getKeyCode() == KeyEvent.VK_F) && ((keyEvent.getModifiers() & KeyEvent.CTRL_MASK) != 0))
searchInput.requestFocus();
BytecodeViewer.checkHotKey(keyEvent);
}));
}
public void search(String search, boolean forwardSearchDirection, boolean caseSensitiveSearch)
{
JTextAreaUtils.search(this, search, forwardSearchDirection, caseSensitiveSearch);
}
public void highlight(String pattern, boolean caseSensitiveSearch)
{
JTextAreaUtils.highlight(this, pattern, caseSensitiveSearch);
}
public JScrollPane getScrollPane()
{
return scrollPane;
}
public JPanel getSearchPanel()
{
return searchPanel;
}
public JTextField getSearchInput()
{
return searchInput;
}
public JCheckBox getCaseSensitiveSearch()
{
return caseSensitiveSearch;
}
}

View File

@ -0,0 +1,93 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.PressKeyListener;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.ReleaseKeyListener;
import the.bytecode.club.bytecodeviewer.util.JTextAreaUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
/**
* Searching on an RSyntaxTextArea using swing highlighting
*
* @author Konloch
* @since 6/25/2021
*/
public class SearchableRSyntaxTextArea extends RSyntaxTextArea
{
private final RTextScrollPane scrollPane = new RTextScrollPane(this);
private final JPanel searchPanel = new JPanel(new BorderLayout());
private final JTextField searchInput = new JTextField();
private final JCheckBox caseSensitiveSearch = new JCheckBox("Exact");
public SearchableRSyntaxTextArea()
{
setAntiAliasingEnabled(true);
scrollPane.setColumnHeaderView(searchPanel);
JButton searchNext = new JButton();
JButton searchPrev = new JButton();
JPanel buttonPane = new JPanel(new BorderLayout());
buttonPane.add(searchNext, BorderLayout.WEST);
buttonPane.add(searchPrev, BorderLayout.EAST);
searchNext.setIcon(Resources.nextIcon);
searchPrev.setIcon(Resources.prevIcon);
searchPanel.add(buttonPane, BorderLayout.WEST);
searchPanel.add(searchInput, BorderLayout.CENTER);
searchPanel.add(caseSensitiveSearch, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(searchInput.getText(), true, caseSensitiveSearch.isSelected()));
searchPrev.addActionListener(arg0 -> search(searchInput.getText(), false, caseSensitiveSearch.isSelected()));
searchInput.addKeyListener(new ReleaseKeyListener(keyEvent ->
{
System.out.println("DEBUG: " + keyEvent.getKeyChar());
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
search(searchInput.getText(), true, caseSensitiveSearch.isSelected());
}));
addKeyListener(new PressKeyListener(keyEvent ->
{
if ((keyEvent.getKeyCode() == KeyEvent.VK_F) && ((keyEvent.getModifiers() & KeyEvent.CTRL_MASK) != 0))
searchInput.requestFocus();
BytecodeViewer.checkHotKey(keyEvent);
}));
}
public void search(String search, boolean forwardSearchDirection, boolean caseSensitiveSearch)
{
JTextAreaUtils.search(this, search, forwardSearchDirection, caseSensitiveSearch);
}
public void highlight(String pattern, boolean caseSensitiveSearch)
{
JTextAreaUtils.highlight(this, pattern, caseSensitiveSearch);
}
public RTextScrollPane getScrollPane()
{
return scrollPane;
}
public JPanel getSearchPanel()
{
return searchPanel;
}
public JTextField getSearchInput()
{
return searchInput;
}
public JCheckBox getCaseSensitiveSearch()
{
return caseSensitiveSearch;
}
}

View File

@ -0,0 +1,57 @@
package the.bytecode.club.bytecodeviewer.gui.components;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.PrintStream;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import static the.bytecode.club.bytecodeviewer.Constants.*;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* A simple console GUI.
*
* @author Konloch
*/
public class SystemErrConsole extends JFrameConsolePrintStream
{
public SystemErrConsole(String title)
{
super(title, System.err);
System.setErr(getNewPrintStream());
}
private static final long serialVersionUID = -6556940545421437508L;
}

View File

@ -0,0 +1,36 @@
package the.bytecode.club.bytecodeviewer.gui.components;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* A simple console GUI.
*
* @author Konloch
*/
public class SystemOutConsole extends JFrameConsolePrintStream
{
public SystemOutConsole(String title)
{
super(title, System.out);
System.setOut(getNewPrintStream());
}
private static final long serialVersionUID = -6666940545499937508L;
}

View File

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui;
package the.bytecode.club.bytecodeviewer.gui.components;
import javax.swing.JInternalFrame;
import org.objectweb.asm.tree.ClassNode;

View File

@ -0,0 +1,36 @@
package the.bytecode.club.bytecodeviewer.gui.components.listeners;
import java.awt.event.*;
/**
* @author Konloch
* @since 6/25/2021
*/
public class MouseClickedListener implements MouseListener
{
private final MouseClickedEvent mouseClickedEvent;
public MouseClickedListener(MouseClickedEvent mouseClickedEvent) {this.mouseClickedEvent = mouseClickedEvent;}
@Override
public void mouseClicked(MouseEvent e) {
mouseClickedEvent.mouseClicked(e);
}
@Override
public void mouseEntered(MouseEvent arg0) { }
@Override
public void mouseExited(MouseEvent arg0) { }
@Override
public void mousePressed(MouseEvent arg0) { }
@Override
public void mouseReleased(MouseEvent e) { }
public interface MouseClickedEvent
{
void mouseClicked(MouseEvent e);
}
}

View File

@ -0,0 +1,32 @@
package the.bytecode.club.bytecodeviewer.gui.components.listeners;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* @author Konloch
* @since 6/25/2021
*/
public class PressKeyListener implements KeyListener
{
private final KeyPressedEvent keyPressedEvent;
public PressKeyListener(KeyPressedEvent keyPressedEvent) {this.keyPressedEvent = keyPressedEvent;}
@Override
public void keyTyped(KeyEvent e) { }
@Override
public void keyPressed(KeyEvent e)
{
keyPressedEvent.keyReleased(e);
}
@Override
public void keyReleased(KeyEvent e) {}
public interface KeyPressedEvent
{
void keyReleased(KeyEvent e);
}
}

View File

@ -0,0 +1,32 @@
package the.bytecode.club.bytecodeviewer.gui.components.listeners;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* @author Konloch
* @since 6/25/2021
*/
public class ReleaseKeyListener implements KeyListener
{
private final KeyReleasedEvent keyReleasedEvent;
public ReleaseKeyListener(KeyReleasedEvent keyReleasedEvent) {this.keyReleasedEvent = keyReleasedEvent;}
@Override
public void keyTyped(KeyEvent e) { }
@Override
public void keyPressed(KeyEvent e) { }
@Override
public void keyReleased(KeyEvent e)
{
keyReleasedEvent.keyReleased(e);
}
public interface KeyReleasedEvent
{
void keyReleased(KeyEvent e);
}
}

View File

@ -1,298 +0,0 @@
package the.bytecode.club.bytecodeviewer.gui.extras;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.PrintStream;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import static the.bytecode.club.bytecodeviewer.Constants.*;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* A simple console GUI.
*
* @author Konloch
*/
public class SystemErrConsole extends JFrame
{
private final JTextArea textArea = new JTextArea();
private final JPanel panel = new JPanel(new BorderLayout());
private final JScrollPane scrollPane = new JScrollPane();
private final JCheckBox check = new JCheckBox("Exact");
private final JTextField field = new JTextField();
private final PrintStream originalOut;
private final JTextAreaOutputStream s;
public SystemErrConsole(String title)
{
this.setIconImages(Resources.iconList);
setTitle(title);
setSize(new Dimension(542, 316));
getContentPane().add(scrollPane, BorderLayout.CENTER);
scrollPane.setViewportView(textArea);
textArea.addKeyListener(new KeyListener()
{
@Override
public void keyPressed(KeyEvent e) {
if ((e.getKeyCode() == KeyEvent.VK_F) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
field.requestFocus();
}
BytecodeViewer.checkHotKey(e);
}
@Override
public void keyReleased(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
JButton searchNext = new JButton();
JButton searchPrev = new JButton();
JPanel buttonPane = new JPanel(new BorderLayout());
buttonPane.add(searchNext, BorderLayout.WEST);
buttonPane.add(searchPrev, BorderLayout.EAST);
searchNext.setIcon(Resources.nextIcon);
searchPrev.setIcon(Resources.prevIcon);
panel.add(buttonPane, BorderLayout.WEST);
panel.add(field, BorderLayout.CENTER);
panel.add(check, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(field.getText(), true));
searchPrev.addActionListener(arg0 -> search(field.getText(), false));
field.addKeyListener(new KeyListener() {
@Override
public void keyReleased(KeyEvent arg0) {
if (arg0.getKeyCode() == KeyEvent.VK_ENTER)
search(field.getText(), true);
}
@Override
public void keyPressed(KeyEvent arg0) {
}
@Override
public void keyTyped(KeyEvent arg0) {
}
});
scrollPane.setColumnHeaderView(panel);
this.setLocationRelativeTo(null);
s = new JTextAreaOutputStream(textArea);
PrintStream printStream = new PrintStream(s);
originalOut = System.err;
System.setErr(printStream);
}
public void finished() {
if (originalOut != null)
System.setErr(originalOut);
}
public void pretty() {
s.update();
String[] test;
if (textArea.getText().split("\n").length >= 2)
test = textArea.getText().split("\n");
else
test = textArea.getText().split("\r");
StringBuilder replace = new StringBuilder();
for (String s : test) {
if (s.startsWith("File '")) {
String[] split = s.split("'");
String start = split[0] + "'" + split[1] + "', ";
s = s.substring(start.length());
}
replace.append(s).append(nl);
}
setText(replace.toString());
}
/**
* This was really interesting to write.
*
* @author Konloch
*/
public void search(String search, boolean next) {
try {
JTextArea area = textArea;
if (search.isEmpty()) {
highlight(area, "");
return;
}
int startLine = area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1;
int currentLine = 1;
boolean canSearch = false;
String[] test;
if (area.getText().split("\n").length >= 2)
test = area.getText().split("\n");
else
test = area.getText().split("\r");
int lastGoodLine = -1;
int firstPos = -1;
boolean found = false;
if (next) {
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (currentLine == startLine) {
canSearch = true;
} else if (s.contains(search)) {
if (canSearch) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(currentLine - 1)
.getStartOffset());
canSearch = false;
found = true;
}
if (firstPos == -1)
firstPos = currentLine;
}
currentLine++;
}
if (!found && firstPos != -1) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement().getElement(firstPos - 1)
.getStartOffset());
}
} else {
canSearch = true;
for (String s : test) {
if (!check.isSelected()) {
s = s.toLowerCase();
search = search.toLowerCase();
}
if (s.contains(search)) {
if (lastGoodLine != -1 && canSearch)
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
lastGoodLine = currentLine;
if (currentLine >= startLine)
canSearch = false;
}
currentLine++;
}
if (lastGoodLine != -1
&& area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1 == startLine) {
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1).getStartOffset());
}
}
highlight(area, search);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
private final DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(
new Color(255, 62, 150));
public void highlight(JTextComponent textComp, String pattern) {
if (pattern.isEmpty()) {
textComp.getHighlighter().removeAllHighlights();
return;
}
try {
Highlighter hilite = textComp.getHighlighter();
hilite.removeAllHighlights();
javax.swing.text.Document doc = textComp.getDocument();
String text = doc.getText(0, doc.getLength());
int pos = 0;
if (!check.isSelected()) {
pattern = pattern.toLowerCase();
text = text.toLowerCase();
}
// Search for pattern
while ((pos = text.indexOf(pattern, pos)) >= 0) {
// Create highlighter using private painter and apply around
// pattern
hilite.addHighlight(pos, pos + pattern.length(), painter);
pos += pattern.length();
}
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
/**
* Appends \r\n to the end of your string, then it puts it on the top.
*
* @param t the string you want to append
*/
public void appendText(String t) {
textArea.setText((textArea.getText().isEmpty() ? "" : textArea
.getText() + "\r\n")
+ t);
textArea.setCaretPosition(0);
}
/**
* Sets the text
*
* @param t the text you want set
*/
public void setText(String t) {
textArea.setText(t);
textArea.setCaretPosition(0);
}
private static final long serialVersionUID = -6556940545421437508L;
}

View File

@ -29,7 +29,7 @@ import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.VisibleComponent;
import the.bytecode.club.bytecodeviewer.gui.components.VisibleComponent;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.FileDrop;
import the.bytecode.club.bytecodeviewer.util.LazyNameUtil;

View File

@ -1,6 +1,6 @@
package the.bytecode.club.bytecodeviewer.gui.resourcelist;
import the.bytecode.club.bytecodeviewer.gui.extras.StringMetricsUtil;
import the.bytecode.club.bytecodeviewer.gui.util.StringMetricsUtil;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;

View File

@ -0,0 +1,54 @@
package the.bytecode.club.bytecodeviewer.gui.resourcesearch;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
import the.bytecode.club.bytecodeviewer.searching.BackgroundSearchThread;
import the.bytecode.club.bytecodeviewer.searching.RegexInsnFinder;
import the.bytecode.club.bytecodeviewer.searching.RegexSearch;
import the.bytecode.club.bytecodeviewer.searching.SearchResultNotifier;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import javax.swing.tree.TreePath;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* @author Konloch
* @since 6/25/2021
*/
class PerformSearch extends BackgroundSearchThread
{
private final SearchBoxPane searchBoxPane;
private final SearchResultNotifier srn;
public PerformSearch(SearchBoxPane searchBoxPane, SearchResultNotifier srn)
{
this.searchBoxPane = searchBoxPane;
this.srn = srn;
}
@Override
public void doSearch()
{
try
{
Pattern.compile(RegexInsnFinder.processRegex(RegexSearch.searchText.getText()), Pattern.MULTILINE);
}
catch (PatternSyntaxException ex)
{
BytecodeViewer.showMessage("You have an error in your regex syntax.");
}
for (FileContainer container : BytecodeViewer.files)
for (ClassNode c : container.classes)
searchBoxPane.searchType.details.search(container, c, srn, searchBoxPane.exact.isSelected());
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class)).search.setEnabled(true);
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class)).search.setText("Search");
searchBoxPane.tree.expandPath(new TreePath(searchBoxPane.tree.getModel().getRoot()));
searchBoxPane.tree.updateUI();
}
}

View File

@ -1,11 +1,9 @@
package the.bytecode.club.bytecodeviewer.gui;
package the.bytecode.club.bytecodeviewer.gui.resourcesearch;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ItemListener;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
@ -15,18 +13,15 @@ import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane;
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.WorkPaneMainComponent;
import the.bytecode.club.bytecodeviewer.gui.components.VisibleComponent;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
import the.bytecode.club.bytecodeviewer.searching.BackgroundSearchThread;
import the.bytecode.club.bytecodeviewer.searching.FieldCallSearch;
import the.bytecode.club.bytecodeviewer.searching.LDCSearch;
import the.bytecode.club.bytecodeviewer.searching.MethodCallSearch;
import the.bytecode.club.bytecodeviewer.searching.RegexInsnFinder;
import the.bytecode.club.bytecodeviewer.searching.RegexSearch;
import the.bytecode.club.bytecodeviewer.searching.SearchResultNotifier;
import the.bytecode.club.bytecodeviewer.searching.SearchTypeDetails;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
/***************************************************************************
@ -55,8 +50,8 @@ import the.bytecode.club.bytecodeviewer.util.FileContainer;
*/
@SuppressWarnings("rawtypes")
public class SearchBoxPane extends VisibleComponent {
public class SearchBoxPane extends VisibleComponent
{
private static final long serialVersionUID = -1098524689236993932L;
public static final SearchRadius[] SEARCH_RADII = SearchRadius.values();
public static final SearchType[] SEARCH_TYPES = SearchType.values();
@ -170,30 +165,7 @@ public class SearchBoxPane extends VisibleComponent {
final SearchResultNotifier srn = debug -> treeRoot.add(new DefaultMutableTreeNode(debug));
if (radius == SearchRadius.All_Classes) {
if (t.finished) {
t = new BackgroundSearchThread() {
@Override
public void doSearch() {
try {
Pattern.compile(RegexInsnFinder.processRegex(RegexSearch.searchText.getText()), Pattern.MULTILINE);
} catch (PatternSyntaxException ex) {
BytecodeViewer.showMessage("You have an error in your regex syntax.");
}
for (FileContainer container : BytecodeViewer.files)
for (ClassNode c : container.classes)
searchType.details.search(container, c, srn, exact.isSelected());
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class))
.search.setEnabled(true);
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class))
.search.setText("Search");
tree.expandPath(new TreePath(tree.getModel().getRoot()));
tree.updateUI();
}
};
t = new PerformSearch(this, srn);
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class))
.search.setEnabled(false);
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class))
@ -203,32 +175,16 @@ public class SearchBoxPane extends VisibleComponent {
BytecodeViewer.showMessage("You currently have a search performing in the background, please wait for that to finish.");
}
} else if (radius == SearchRadius.Current_Class) {
final Viewer cv = Objects.requireNonNull(MainViewerGUI.getComponent(WorkPane.class)).getCurrentViewer();
final ResourceViewer cv = Objects.requireNonNull(MainViewerGUI.getComponent(WorkPaneMainComponent.class)).getCurrentViewer();
if (cv != null) {
searchType.details.search(cv.container, cv.cn, srn, exact.isSelected());
}
}
}
public enum SearchType {
Strings(new LDCSearch()),
Regex(new RegexSearch()),
MethodCall(new MethodCallSearch()),
FieldCall(new FieldCallSearch());
public final SearchTypeDetails details;
SearchType(final SearchTypeDetails details) {
this.details = details;
}
}
public enum SearchRadius {
All_Classes, Current_Class
}
public void resetWorkspace() {
treeRoot.removeAllChildren();
tree.updateUI();
}
}

View File

@ -0,0 +1,10 @@
package the.bytecode.club.bytecodeviewer.gui.resourcesearch;
/**
* @author Konloch
* @since 6/25/2021
*/
public enum SearchRadius
{
All_Classes, Current_Class
}

View File

@ -0,0 +1,22 @@
package the.bytecode.club.bytecodeviewer.gui.resourcesearch;
import the.bytecode.club.bytecodeviewer.searching.*;
/**
* @author Konloch
* @since 6/25/2021
*/
public enum SearchType
{
Strings(new LDCSearch()),
Regex(new RegexSearch()),
MethodCall(new MethodCallSearch()),
FieldCall(new FieldCallSearch());
public final SearchTypeDetails details;
SearchType(final SearchTypeDetails details)
{
this.details = details;
}
}

View File

@ -1,4 +1,7 @@
package the.bytecode.club.bytecodeviewer.gui;
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent;
import javax.swing.*;
@ -6,7 +9,7 @@ import javax.swing.*;
* @author Konloch
* @since 6/21/2021
*/
public class ViewPane
public class DecompilerSelectionPane
{
public final int paneID;
public final JMenu menu;
@ -23,7 +26,7 @@ public class ViewPane
public final JRadioButtonMenuItem bytecode = new JRadioButtonMenuItem("Bytecode");
public final JRadioButtonMenuItem asmTextify = new JRadioButtonMenuItem("ASM Textify");
public ViewPane(int paneID) {
public DecompilerSelectionPane(int paneID) {
this.paneID = paneID;
this.menu = new JMenu("Pane " + paneID);
buildMenu();

View File

@ -0,0 +1,12 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
/**
* @author Konloch
* @since 6/25/2021
*/
public enum ResourcePanelCompileMode
{
JAVA,
KRAKATAU_ASSEMBLY,
SMALI_ASSEMBLY,
}

View File

@ -0,0 +1,107 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.objectweb.asm.ClassWriter;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.gui.components.SearchableRSyntaxTextArea;
import the.bytecode.club.bytecodeviewer.gui.hexviewer.JHexEditor;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.util.PaneUpdaterThread;
import javax.swing.*;
import java.awt.*;
/**
* @author Konloch
* @since 6/24/2021
*/
public class ResourceViewPanel
{
public final JPanel panel = new JPanel(new BorderLayout());
public ClassViewer viewer;
public int decompilerViewIndex = -1;
public SearchableRSyntaxTextArea textArea;
public PaneUpdaterThread updateThread;
public final int panelIndex;
//TODO change the compile mode for Krakatau and Smali assembly
public ResourcePanelCompileMode compileMode = ResourcePanelCompileMode.JAVA;
public ResourceViewPanel(int panelIndex) {this.panelIndex = panelIndex;}
public void createPane(ClassViewer viewer)
{
panel.removeAll();
textArea = null;
if(viewer.cn == null)
{
panel.add(new JLabel("This file has been removed from the reload."));
}
}
public void updatePane(ClassViewer cv, byte[] b, JButton button, boolean isPanelEditable)
{
updateThread = new PaneUpdaterThread(panelIndex, decompilerViewIndex) {
@Override
public void doShit() {
try {
BytecodeViewer.viewer.updateBusyStatus(true);
if(ResourceViewPanel.this.decompilerViewIndex > 0)
{
//hex viewer
if (ResourceViewPanel.this.decompilerViewIndex == 5)
{
final ClassWriter cw = new ClassWriter(0);
cv.cn.accept(cw);
final JHexEditor hex = new JHexEditor(cw.toByteArray());
hex.setFont(new Font(Font.MONOSPACED, Font.PLAIN, (int) BytecodeViewer.viewer.fontSpinner.getValue()));
SwingUtilities.invokeLater(() -> panel.add(hex));
}
else
{
viewer = cv;
updateUpdaterTextArea = (SearchableRSyntaxTextArea) Configuration.rstaTheme.apply(new SearchableRSyntaxTextArea());
final Decompiler decompiler = Decompiler.decompilersByIndex.get(ResourceViewPanel.this.decompilerViewIndex);
final String decompiledSource = decompiler.getDecompiler().decompileClassNode(cv.cn, b);
//decompilerUpdate.update(decompiler.getDecompilerName(), cv.cn, b, decompiler.getDecompiler(), updateUpdaterTextArea);
SwingUtilities.invokeLater(() ->
{
panel.add(updateUpdaterTextArea.getScrollPane());
textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
textArea.setCodeFoldingEnabled(true);
textArea.setAntiAliasingEnabled(true);
textArea.setText(decompiledSource);
textArea.setCaretPosition(0);
textArea.setEditable(isPanelEditable);
textArea.getScrollPane().setColumnHeaderView(new JLabel(decompiler.getDecompilerName() + " - Editable: " + textArea.isEditable()));
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN,
(int) BytecodeViewer.viewer.fontSpinner.getValue()));
});
textArea = updateUpdaterTextArea;
}
}
} catch (java.lang.IndexOutOfBoundsException | java.lang.NullPointerException e) {
//ignore
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
} finally {
cv.resetDivider();
BytecodeViewer.viewer.updateBusyStatus(false);
if (button != null)
button.setEnabled(true);
}
}
};
}
}

View File

@ -0,0 +1,100 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import javax.swing.*;
import javax.swing.plaf.basic.BasicButtonUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @author Konloch
* @since 6/25/2021
*/
public class TabButton extends JButton implements ActionListener
{
private final TabbedPane tabbedPane;
private final int tabIndex;
private final String tabWorkingName;
public TabButton(TabbedPane tabbedPane, int tabIndex, String tabWorkingName)
{
this.tabbedPane = tabbedPane;
this.tabIndex = tabIndex;
this.tabWorkingName = tabWorkingName;
final int size = 17;
setPreferredSize(new Dimension(size, size));
setToolTipText("Close this tab");
// Make the button looks the same for all Laf's
setUI(new BasicButtonUI());
// Make it transparent
setContentAreaFilled(false);
// No need to be focusable
setFocusable(false);
setBorder(BorderFactory.createEtchedBorder());
setBorderPainted(false);
// Making nice rollover effect
// we use the same listener for all buttons
addMouseListener(TabbedPane.buttonHoverAnimation);
setRolloverEnabled(true);
// Close the proper tab by clicking the button
addActionListener(this);
}
public int getTabIndex()
{
return tabIndex;
}
@Override
public void actionPerformed(final ActionEvent e)
{
final int i = tabbedPane.tabs.indexOfTabComponent(tabbedPane);
if (i != -1)
{
tabbedPane.tabs.remove(i);
}
}
// we don't want to update UI for this button
@Override
public void updateUI() { }
// paint the cross
@Override
protected void paintComponent(final Graphics g)
{
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g.create();
// shift the image for pressed buttons
if (getModel().isPressed())
g2.translate(1, 1);
g2.setStroke(new BasicStroke(2));
g2.setColor(Color.BLACK);
if (getModel().isRollover())
g2.setColor(Color.MAGENTA);
final int delta = 6;
g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1);
g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1);
g2.dispose();
}
public TabbedPane getTabbedPane()
{
return tabbedPane;
}
public String getTabWorkingName()
{
return tabWorkingName;
}
public static long getSerialVersionUID()
{
return serialVersionUID;
}
private static final long serialVersionUID = -4492967978286454159L;
}

View File

@ -0,0 +1,36 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.FileViewer;
import java.awt.*;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
/**
* @author Konloch
* @since 6/24/2021
*/
public class TabRemovalEvent implements ContainerListener
{
@Override
public void componentAdded(ContainerEvent e) { }
@Override
public void componentRemoved(ContainerEvent e)
{
final Component c = e.getChild();
if (c instanceof ClassViewer)
{
String workingName = ((ClassViewer) c).workingName;
BytecodeViewer.viewer.workPane.workingOn.remove(workingName);
}
else if (c instanceof FileViewer)
{
String workingName = ((FileViewer) c).workingName;
BytecodeViewer.viewer.workPane.workingOn.remove(workingName);
}
}
}

View File

@ -0,0 +1,250 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.components.ButtonHoverAnimation;
import the.bytecode.club.bytecodeviewer.gui.components.MaxWidthJLabel;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.MouseClickedListener;
import the.bytecode.club.bytecodeviewer.gui.util.DelayTabbedPaneThread;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* Component to be used as tabComponent; Contains a JLabel to show the text and a JButton to close the tab it belongs to
*
* @author Konloch
* @author WaterWolf
*/
public class TabbedPane extends JPanel
{
public final JTabbedPane tabs;
public final JLabel label;
private DelayTabbedPaneThread probablyABadIdea;
private long startedDragging = 0;
public final String tabName;
public final String fileContainerName;
private static long lastMouseClick = System.currentTimeMillis();
public final static MouseListener buttonHoverAnimation = new ButtonHoverAnimation();
public static final Color BLANK_COLOR = new Color(0, 0, 0, 0);
public TabbedPane(int tabIndex, String tabWorkingName, String fileContainerName, String name, final JTabbedPane existingTabs)
{
// unset default FlowLayout' gaps
super(new FlowLayout(FlowLayout.LEFT, 0, 0));
this.tabName = name;
this.fileContainerName = fileContainerName;
if (existingTabs == null)
throw new NullPointerException("TabbedPane is null");
this.tabs = existingTabs;
setOpaque(false);
// make JLabel read titles from JTabbedPane
label = new MaxWidthJLabel(tabName, 400, 20);
this.add(label);
// add more space between the label and the button
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
// tab button
JButton button = new TabButton(this, tabIndex, tabWorkingName);
this.add(button);
// add more space to the top of the component
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
//define the right click pop-up menu
JPopupMenu rightClickMenu = new JPopupMenu();
JMenuItem closeAllTabs = new JMenuItem("Close All But This: " + name);
JMenuItem closeTab = new JMenuItem("Close Tab: " + name);
rightClickMenu.add(closeAllTabs);
rightClickMenu.add(closeTab);
//setComponentPopupMenu(rightClickMenu);
button.setComponentPopupMenu(rightClickMenu);
button.addMouseListener(new MouseClickedListener(e ->
{
if (e.getModifiers() != InputEvent.ALT_MASK || System.currentTimeMillis() - lastMouseClick < 100)
return;
lastMouseClick = System.currentTimeMillis();
final int i = existingTabs.indexOfTabComponent(TabbedPane.this);
if (i != -1)
existingTabs.remove(i);
}));
closeTab.addActionListener(e ->
{
TabButton tabButton = (TabButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabButton.getTabIndex();
if (index != -1)
existingTabs.remove(index);
});
closeAllTabs.addActionListener(e ->
{
TabButton tabButton = (TabButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabButton.getTabIndex();
while (true)
{
if (existingTabs.getTabCount() <= 1)
return;
if (index != 0)
existingTabs.remove(0);
else
existingTabs.remove(1);
}
});
//tab dragging
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
{
/*label.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent arg0) {
}
@Override public void mouseExited(MouseEvent arg0) {
}
@Override public void mousePressed(MouseEvent e) {
onMousePressed(e);
}
@Override public void mouseReleased(MouseEvent e) {
stopDragging(e.getX(), e.getY());
}
});*/
this.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent arg0) {}
@Override public void mouseExited(MouseEvent arg0) {}
@Override public void mousePressed(MouseEvent e) {
onMousePressed(e);
}
@Override public void mouseReleased(MouseEvent e) {
stopDragging(e.getX(), e.getY());
}
});
}
//middle click close
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
{
this.addMouseListener(new MouseAdapter()
{
@Override
public void mouseReleased(MouseEvent e)
{
if (e.getButton() != MouseEvent.BUTTON2)
return;
final int i = existingTabs.indexOfTabComponent(TabbedPane.this);
if (i != -1)
existingTabs.remove(i);
}
});
}
}
private void stopDragging(int mouseX, int mouseY)
{
if (System.currentTimeMillis() - startedDragging >= 210)
{
Rectangle bounds = new Rectangle(1, 1, mouseX, mouseY);
System.out.println("debug-5: " + mouseX + ", " + mouseY);
int totalTabs = BytecodeViewer.viewer.workPane.tabs.getTabCount();
int index = -1;
for (int i = 0; i < totalTabs; i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds()))
index = i; //replace this tabs position
}
if (index == -1)
{
for (int i = 0; i < totalTabs; i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
//do some check to see if it's past the X or Y
if (c != null) {
System.out.println("debug-6: " + c.getBounds());
}
}
}
if (index != -1)
{
BytecodeViewer.viewer.workPane.tabs.remove(this);
BytecodeViewer.viewer.workPane.tabs.setTabComponentAt(index, this);
}
}
SwingUtilities.invokeLater(() ->
{
label.setBackground(BLANK_COLOR);
label.updateUI();
});
}
public void onMousePressed(MouseEvent e)
{
BytecodeViewer.viewer.workPane.tabs.dispatchEvent(e);
if(e.getButton() == 1)
{
startedDragging = System.currentTimeMillis();
//dragging = true;
if (probablyABadIdea != null)
probablyABadIdea.stopped = true;
probablyABadIdea = new DelayTabbedPaneThread(TabbedPane.this);
probablyABadIdea.start();
repaint();
System.out.println(e.getX()+", "+e.getY());
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
for(int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if(c != null && bounds.intersects(c.getBounds()))
BytecodeViewer.viewer.workPane.tabs.setSelectedIndex(i);
}
}
else
{
stopDragging(e.getX(), e.getY());
}
}
private static final long serialVersionUID = -4774885688297538774L;
}

View File

@ -0,0 +1,235 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashMap;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.gui.components.VisibleComponent;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.FileViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
import static the.bytecode.club.bytecodeviewer.Constants.BLOCK_TAB_MENU;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* The pane that contains all of the resources as tabs.
*
* @author Konloch
* @author WaterWolf
*/
public class WorkPaneMainComponent extends VisibleComponent
{
public final JTabbedPane tabs;
public final JPanel buttonPanel;
public final JButton refreshClass;
public final HashMap<String, Integer> workingOn = new HashMap<>();
public WorkPaneMainComponent()
{
super("WorkPanel");
setTitle("Work Space");
this.tabs = new JTabbedPane();
JPopupMenu popUp = new JPopupMenu();
JMenuItem closeAllTabs = new JMenuItem("Close All But This");
JMenuItem closeTab = new JMenuItem("Close Tab");
closeTab.addActionListener(e ->
{
TabButton tabButton = (TabButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabButton.getTabIndex();
if (index != -1)
tabs.remove(index);
});
closeAllTabs.addActionListener(e ->
{
TabButton tabButton = (TabButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabButton.getTabIndex();
while (true)
{
if (tabs.getTabCount() <= 1)
return;
if (index != 0)
tabs.remove(0);
else
tabs.remove(1);
}
});
tabs.addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent e) { }
@Override
public void mouseEntered(MouseEvent arg0) { }
@Override
public void mouseExited(MouseEvent arg0) { }
@Override
public void mousePressed(MouseEvent e)
{
if (BLOCK_TAB_MENU)
return;
if (e.getButton() == 3)
{
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
for (int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds()))
{
popUp.setVisible(true);
closeAllTabs.setText("Close All But This: " + ((TabbedPane) c).tabName);
closeTab.setText("Close Tab: " + ((TabbedPane) c).tabName);
}
else
{
popUp.setVisible(false);
}
}
}
}
@Override
public void mouseReleased(MouseEvent e) { }
});
popUp.add(closeAllTabs);
popUp.add(closeTab);
if (!BLOCK_TAB_MENU)
tabs.setComponentPopupMenu(popUp);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(tabs, BorderLayout.CENTER);
buttonPanel = new JPanel(new FlowLayout());
refreshClass = new JButton("Refresh");
refreshClass.addActionListener((event)->{
Thread t = new Thread(() -> new WorkPaneRefresh(event).run());
t.start();
});
buttonPanel.add(refreshClass);
buttonPanel.setVisible(false);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
tabs.addContainerListener(new TabRemovalEvent());
tabs.addChangeListener(arg0 -> buttonPanel.setVisible(tabs.getSelectedIndex() != -1));
this.setVisible(true);
}
public void addWorkingFile(final FileContainer container, String name, final ClassNode cn)
{
String workingName = container.name + ">" + name;
String containerName = name;
if (Configuration.displayParentInTab)
containerName = container.name + ">" + name;
if (!workingOn.containsKey(workingName))
{
final ClassViewer tabComp = new ClassViewer(container, containerName, cn, workingName);
tabs.add(tabComp);
final int tabIndex = tabs.indexOfComponent(tabComp);
workingOn.put(workingName, tabIndex);
TabbedPane tabbedPane = new TabbedPane(tabIndex, workingName, container.name, name, tabs);
tabComp.tabbedPane = tabbedPane;
tabs.setTabComponentAt(tabIndex, tabbedPane);
tabs.setSelectedIndex(tabIndex);
}
else
{
tabs.setSelectedIndex(workingOn.get(workingName));
}
}
public void addFile(final FileContainer container, String name, byte[] contents)
{
if (contents == null) //a directory
return;
final String workingName = container.name + ">" + name;
if (Configuration.simplifiedTabNames)
name = MiscUtils.getChildFromPath(name);
if (Configuration.displayParentInTab)
name = container.name + ">" + name;
if (!workingOn.containsKey(workingName))
{
final FileViewer tabComp = new FileViewer(container, name, contents, workingName);
tabs.add(tabComp);
final int tabIndex = tabs.indexOfComponent(tabComp);
workingOn.put(workingName, tabIndex);
TabbedPane tabbedPane = new TabbedPane(tabIndex, workingName, container.name, name, tabs);
tabComp.tabbedPane = tabbedPane;
tabs.setTabComponentAt(tabIndex, tabbedPane);
tabs.setSelectedIndex(tabIndex);
}
else
{
try {
tabs.setSelectedIndex(workingOn.get(workingName));
} catch (Exception e) {
//workingOn.remove(workingName);
e.printStackTrace();
}
}
}
public ResourceViewer getCurrentViewer() {
return (ResourceViewer) tabs.getSelectedComponent();
}
public Component[] getLoadedViewers() {
return tabs.getComponents();
}
public void resetWorkspace()
{
tabs.removeAll();
tabs.updateUI();
}
private static final long serialVersionUID = 6542337997679487946L;
}

View File

@ -0,0 +1,65 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.FileViewer;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
/**
* @author Konloch
* @since 6/24/2021
*/
public class WorkPaneRefresh implements Runnable
{
private final ActionEvent event;
public WorkPaneRefresh(ActionEvent event) {
this.event = event;
}
@Override
public void run()
{
if (BytecodeViewer.viewer.autoCompileOnRefresh.isSelected())
try {
if (!BytecodeViewer.compile(false))
return;
} catch (NullPointerException ignored) {
}
JButton src = null;
if(event != null && event.getSource() instanceof JButton)
src = (JButton) event.getSource();
//if (src == BytecodeViewer.viewer.workPane.refreshClass)
{
final Component tabComp = BytecodeViewer.viewer.workPane.tabs.getSelectedComponent();
if(tabComp == null)
return;
if (tabComp instanceof ClassViewer)
{
if(src != null)
src.setEnabled(false);
BytecodeViewer.viewer.updateBusyStatus(true);
((ClassViewer) tabComp).startPaneUpdater(src);
BytecodeViewer.viewer.updateBusyStatus(false);
}
else if (tabComp instanceof FileViewer)
{
if(src != null)
src.setEnabled(false);
BytecodeViewer.viewer.updateBusyStatus(true);
((FileViewer) tabComp).refresh(src);
BytecodeViewer.viewer.updateBusyStatus(false);
}
}
}
}

View File

@ -0,0 +1,569 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourcePanelCompileMode;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourceViewPanel;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane;
import the.bytecode.club.bytecodeviewer.gui.hexviewer.JHexEditor;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JSplitPane;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.Settings;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.MethodParser;
import static the.bytecode.club.bytecodeviewer.util.MethodParser.Method;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* This represents the opened classfile.
*
* @author Konloch
* @author WaterWolf
*/
public class ClassViewer extends ResourceViewer
{
private static final long serialVersionUID = -8650495368920680024L;
public String name;
public JSplitPane sp;
public JSplitPane sp2;
public TabbedPane tabbedPane;
public ResourceViewPanel resourceViewPanel1 = new ResourceViewPanel(0);
public ResourceViewPanel resourceViewPanel2 = new ResourceViewPanel(1);
public ResourceViewPanel resourceViewPanel3 = new ResourceViewPanel(2);
public File[] tempFiles;
public ClassViewer THIS = this;
public List<MethodParser> methods = Arrays.asList(new MethodParser(), new MethodParser(), new MethodParser());
public final String workingName;
private final DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(new Color(255, 62, 150));
/**
* This was really interesting to write.
*
* @author Konloch
*/
public void search(int pane, String search, boolean next)
{
try
{
Component[] com = null;
if (pane == 0) // bytecode
com = resourceViewPanel1.panel.getComponents();
else if (pane == 1)
com = resourceViewPanel2.panel.getComponents();
else if (pane == 2)
com = resourceViewPanel3.panel.getComponents();
if (com == null) // someone fucked up, lets prevent a nullpointer.
return;
for (Component c : com)
{
if (c instanceof RTextScrollPane)
{
RSyntaxTextArea area = (RSyntaxTextArea) ((RTextScrollPane) c).getViewport().getComponent(0);
if (search.isEmpty())
{
highlight(pane, area, "");
return;
}
int startLine = area.getDocument().getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1;
int currentLine = 1;
boolean canSearch = false;
String[] test = (area.getText().split("\n").length >= 2
? area.getText().split("\n")
: area.getText().split("\r"));
int lastGoodLine = -1;
int firstPos = -1;
boolean found = false;
if (next)
{
for (String s : test)
{
if (pane == 0 && !resourceViewPanel1.textArea.getCaseSensitiveSearch().isSelected() ||
pane == 1 && !resourceViewPanel2.textArea.getCaseSensitiveSearch().isSelected())
{
//TODO should pane2 be here? - look into this later
s = s.toLowerCase();
search = search.toLowerCase();
}
if (currentLine == startLine)
{
canSearch = true;
}
else if (s.contains(search))
{
if (canSearch)
{
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(currentLine - 1)
.getStartOffset());
canSearch = false;
found = true;
}
if (firstPos == -1)
firstPos = currentLine;
}
currentLine++;
}
if (!found && firstPos != -1)
{
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(firstPos - 1).getStartOffset());
}
}
else
{
canSearch = true;
for (String s : test)
{
if (pane == 0 && !resourceViewPanel1.textArea.getCaseSensitiveSearch().isSelected() || pane == 1
&& !resourceViewPanel2.textArea.getCaseSensitiveSearch().isSelected() || pane == 2
&& !resourceViewPanel3.textArea.getCaseSensitiveSearch().isSelected())
{
s = s.toLowerCase();
search = search.toLowerCase();
}
if (s.contains(search))
{
if (lastGoodLine != -1 && canSearch)
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
lastGoodLine = currentLine;
if (currentLine >= startLine)
canSearch = false;
}
currentLine++;
}
if (lastGoodLine != -1
&& area.getDocument()
.getDefaultRootElement()
.getElementIndex(area.getCaretPosition()) + 1 == startLine)
{
area.setCaretPosition(area.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
}
}
highlight(pane, area, search);
}
}
}
catch (Exception e)
{
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public void highlight(int pane, JTextComponent textComp, String pattern)
{
if (pattern.isEmpty())
{
textComp.getHighlighter().removeAllHighlights();
return;
}
try
{
Highlighter hilite = textComp.getHighlighter();
hilite.removeAllHighlights();
javax.swing.text.Document doc = textComp.getDocument();
String text = doc.getText(0, doc.getLength());
int pos = 0;
if ((pane == 0 && !resourceViewPanel1.textArea.getCaseSensitiveSearch().isSelected()) || pane == 1
&& !resourceViewPanel2.textArea.getCaseSensitiveSearch().isSelected() || pane == 2
&& !resourceViewPanel3.textArea.getCaseSensitiveSearch().isSelected())
{
pattern = pattern.toLowerCase();
text = text.toLowerCase();
}
// Search for pattern
while ((pos = text.indexOf(pattern, pos)) >= 0)
{
// Create highlighter using private painter and apply around
// pattern
hilite.addHighlight(pos, pos + pattern.length(), painter);
pos += pattern.length();
}
}
catch (Exception e)
{
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public ClassViewer(final FileContainer container, final String name, final ClassNode cn, String workingName)
{
this.workingName = workingName;
this.container = container;
this.name = name;
this.cn = cn;
this.setName(name);
this.setLayout(new BorderLayout());
this.sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, resourceViewPanel1.panel, resourceViewPanel2.panel);
final ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
JHexEditor hex = new JHexEditor(cw.toByteArray());
this.sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp, resourceViewPanel3.panel);
this.add(sp2, BorderLayout.CENTER);
hex.setMaximumSize(new Dimension(0, Integer.MAX_VALUE));
hex.setSize(0, Integer.MAX_VALUE);
startPaneUpdater(null);
this.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
resetDivider();
}
});
}
public void resetDivider() {
SwingUtilities.invokeLater(() ->
{
sp.setResizeWeight(0.5);
if (resourceViewPanel2.decompilerViewIndex != 0 && resourceViewPanel1.decompilerViewIndex != 0) {
setDividerLocation(sp, 0.5);
} else if (resourceViewPanel1.decompilerViewIndex != 0) {
setDividerLocation(sp, 1);
} else if (resourceViewPanel2.decompilerViewIndex != 0) {
sp.setResizeWeight(1);
setDividerLocation(sp, 0);
} else {
setDividerLocation(sp, 0);
}
if (resourceViewPanel3.decompilerViewIndex != 0) {
sp2.setResizeWeight(0.7);
setDividerLocation(sp2, 0.7);
if ((resourceViewPanel2.decompilerViewIndex == 0 && resourceViewPanel1.decompilerViewIndex != 0)
|| (resourceViewPanel1.decompilerViewIndex == 0 && resourceViewPanel2.decompilerViewIndex != 0)) {
setDividerLocation(sp2, 0.5);
} else if (resourceViewPanel1.decompilerViewIndex == 0) {
setDividerLocation(sp2, 0);
}
} else {
sp.setResizeWeight(1);
sp2.setResizeWeight(0);
setDividerLocation(sp2, 1);
}
});
}
public void startPaneUpdater(final JButton button)
{
this.cn = BytecodeViewer.getClassNode(container, cn.name); //update the classnode
setPanes();
resourceViewPanel1.createPane(this);
resourceViewPanel2.createPane(this);
resourceViewPanel3.createPane(this);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch (Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException ignored) { }
}
final byte[] b = cw.toByteArray();
resourceViewPanel1.updatePane(this, b, button, isPanel1Editable());
resourceViewPanel2.updatePane(this, b, button, isPanel2Editable());
resourceViewPanel3.updatePane(this, b, button, isPanel3Editable());
Thread t = new Thread(() ->
{
BytecodeViewer.viewer.updateBusyStatus(true);
while (Configuration.currentlyDumping)
{
//wait until it's not dumping
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
tempFiles = BytecodeViewer.dumpTempFile(container);
BytecodeViewer.viewer.updateBusyStatus(false);
if (resourceViewPanel1.decompilerViewIndex > 0)
resourceViewPanel1.updateThread.startNewThread();
if (resourceViewPanel2.decompilerViewIndex > 0)
resourceViewPanel2.updateThread.startNewThread();
if (resourceViewPanel3.decompilerViewIndex > 0)
resourceViewPanel3.updateThread.startNewThread();
});
t.start();
if (isPanel1Editable() || isPanel2Editable() || isPanel3Editable())
{
if (Configuration.warnForEditing)
return;
Configuration.warnForEditing = true;
if (!BytecodeViewer.viewer.autoCompileOnRefresh.isSelected()
&& !BytecodeViewer.viewer.compileOnSave.isSelected())
{
BytecodeViewer.showMessage("Make sure to compile (File>Compile or Ctrl + T) whenever you want to "
+ "test or export your changes.\nYou can set compile automatically on refresh or on save "
+ "in the settings menu.");
Settings.saveSettings();
}
}
}
public Object[] getSmali() {
if (resourceViewPanel1.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY)
return new Object[]{cn, resourceViewPanel1.textArea.getText()};
if (resourceViewPanel2.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY)
return new Object[]{cn, resourceViewPanel2.textArea.getText()};
if (resourceViewPanel3.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY)
return new Object[]{cn, resourceViewPanel3.textArea.getText()};
return null;
}
public Object[] getKrakatau() {
if (resourceViewPanel1.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY)
return new Object[]{cn, resourceViewPanel1.textArea.getText()};
if (resourceViewPanel2.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY)
return new Object[]{cn, resourceViewPanel2.textArea.getText()};
if (resourceViewPanel3.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY)
return new Object[]{cn, resourceViewPanel3.textArea.getText()};
return null;
}
public Object[] getJava() {
if (resourceViewPanel1.textArea != null)
return new Object[]{cn, resourceViewPanel1.textArea.getText()};
if (resourceViewPanel2.textArea != null)
return new Object[]{cn, resourceViewPanel2.textArea.getText()};
if (resourceViewPanel3.textArea != null)
return new Object[]{cn, resourceViewPanel3.textArea.getText()};
return null;
}
public void setPanes() {
resourceViewPanel1.decompilerViewIndex = BytecodeViewer.viewer.viewPane1.getSelectedViewer();
resourceViewPanel2.decompilerViewIndex = BytecodeViewer.viewer.viewPane2.getSelectedViewer();
resourceViewPanel3.decompilerViewIndex = BytecodeViewer.viewer.viewPane3.getSelectedViewer();
}
public boolean isPanel1Editable() {
setPanes();
return BytecodeViewer.viewer.viewPane1.isPaneEditable();
}
public boolean isPanel2Editable() {
setPanes();
return BytecodeViewer.viewer.viewPane2.isPaneEditable();
}
public boolean isPanel3Editable() {
setPanes();
return BytecodeViewer.viewer.viewPane3.isPaneEditable();
}
/**
* Whoever wrote this function, THANK YOU!
*
* @param splitter
* @param proportion
* @return
*/
public static JSplitPane setDividerLocation(final JSplitPane splitter,
final double proportion) {
if (splitter.isShowing()) {
if (splitter.getWidth() > 0 && splitter.getHeight() > 0) {
splitter.setDividerLocation(proportion);
} else {
splitter.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent ce) {
splitter.removeComponentListener(this);
setDividerLocation(splitter, proportion);
}
});
}
} else {
splitter.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0
&& splitter.isShowing()) {
splitter.removeHierarchyListener(this);
setDividerLocation(splitter, proportion);
}
}
});
}
return splitter;
}
public static void selectMethod(RSyntaxTextArea area, int methodLine) {
if (methodLine != area.getCaretLineNumber()) {
setCaretLine(area, methodLine);
setViewLine(area, methodLine);
}
}
public static void selectMethod(ClassViewer classViewer, int paneId, Method method) {
RSyntaxTextArea area = null;
switch (paneId) {
case 0:
area = classViewer.resourceViewPanel1.updateThread.updateUpdaterTextArea;
break;
case 1:
area = classViewer.resourceViewPanel2.updateThread.updateUpdaterTextArea;
break;
case 2:
area = classViewer.resourceViewPanel3.updateThread.updateUpdaterTextArea;
break;
}
if (area != null) {
MethodParser methods = classViewer.methods.get(paneId);
if (methods != null) {
int methodLine = methods.findMethod(method);
if (methodLine != -1) {
selectMethod(area, methodLine);
}
}
}
}
public static String getLineText(RSyntaxTextArea area, int line) {
try {
if (line < area.getLineCount()) {
int start = area.getLineStartOffset(line);
int end = area.getLineEndOffset(line);
return area.getText(start, end - start).trim();
}
} catch (BadLocationException ignored) { }
return "";
}
public static int getMaxViewLine(RSyntaxTextArea area)
{
Container parent = area.getParent();
if (parent instanceof JViewport)
{
JViewport viewport = (JViewport) parent;
int y = viewport.getViewSize().height - viewport.getExtentSize().height;
int lineHeight = area.getLineHeight();
return y >= lineHeight ? y / lineHeight : 0;
}
return 0;
}
public static int getViewLine(RSyntaxTextArea area)
{
Container parent = area.getParent();
if (parent instanceof JViewport)
{
JViewport viewport = (JViewport) parent;
Point point = viewport.getViewPosition();
int lineHeight = area.getLineHeight();
return point.y >= lineHeight ? point.y / lineHeight : 0;
}
return 0;
}
public static void setViewLine(RSyntaxTextArea area, int line)
{
Container parent = area.getParent();
if (parent instanceof JViewport)
{
JViewport viewport = (JViewport) parent;
int maxLine = ClassViewer.getMaxViewLine(area);
line = Math.min(line, maxLine);
viewport.setViewPosition(new Point(0, line * area.getLineHeight()));
}
}
public static void setCaretLine(RSyntaxTextArea area, int line)
{
try {
area.setCaretPosition(area.getLineStartOffset(line));
} catch (BadLocationException ignored) { }
}
}

View File

@ -0,0 +1,149 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer;
import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.imgscalr.Scalr;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.gui.components.ImageJLabel;
import the.bytecode.club.bytecodeviewer.gui.components.SearchableRSyntaxTextArea;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.PressKeyListener;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.ReleaseKeyListener;
import the.bytecode.club.bytecodeviewer.gui.hexviewer.JHexEditor;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane;
import the.bytecode.club.bytecodeviewer.util.FileContainer;
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
import the.bytecode.club.bytecodeviewer.util.SyntaxLanguage;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* Represents any open non-class file.
*
* @author Konloch
*/
public class FileViewer extends ResourceViewer
{
public final String name;
public final byte[] contents;
public final String workingName;
public final SearchableRSyntaxTextArea textArea = (SearchableRSyntaxTextArea)
Configuration.rstaTheme.apply(new SearchableRSyntaxTextArea());
public final JPanel mainPanel = new JPanel(new BorderLayout());
public TabbedPane tabbedPane;
public BufferedImage image;
public boolean canRefresh;
public FileViewer(final FileContainer container, final String name, final byte[] contents, String workingName)
{
this.name = name;
this.contents = contents;
this.workingName = workingName;
this.container = container;
this.setName(name);
this.setLayout(new BorderLayout());
this.add(mainPanel, BorderLayout.CENTER);
setContents();
}
public void setContents()
{
final String nameLowerCase = this.name.toLowerCase();
final String contentsAsString = new String(contents);
//image viewer
if (!MiscUtils.isPureAscii(contentsAsString))
{
//TODO webp?
if (nameLowerCase.endsWith(".png") || nameLowerCase.endsWith(".jpg") || nameLowerCase.endsWith(".jpeg") ||
nameLowerCase.endsWith(".gif") || nameLowerCase.endsWith(".tif") || nameLowerCase.endsWith(".bmp"))
{
canRefresh = true;
image = MiscUtils.loadImage(image, contents); //gifs fail because of this
mainPanel.add(new ImageJLabel(image), BorderLayout.CENTER);
mainPanel.addMouseWheelListener(e ->
{
int notches = e.getWheelRotation();
if (notches < 0) //zoom in
image = Scalr.resize(image, Scalr.Method.SPEED, image.getWidth() + 10,
image.getHeight() + 10);
else //zoom out
image = Scalr.resize(image, Scalr.Method.SPEED, image.getWidth() - 10,
image.getHeight() - 10);
mainPanel.removeAll();
mainPanel.add(new ImageJLabel(image), BorderLayout.CENTER);
mainPanel.updateUI();
});
return;
}
//hex viewer
else if (BytecodeViewer.viewer.forcePureAsciiAsText.isSelected())
{
JHexEditor hex = new JHexEditor(contents);
mainPanel.add(hex);
return;
}
}
textArea.setCodeFoldingEnabled(true);
textArea.setSyntaxEditingStyle(SyntaxLanguage.detectLanguage(nameLowerCase, contentsAsString).getSyntaxConstant());
textArea.setText(contentsAsString);
textArea.setCaretPosition(0);
mainPanel.add(textArea.getScrollPane());
}
public void refresh(JButton src)
{
if (!canRefresh)
{
src.setEnabled(true);
return;
}
mainPanel.removeAll();
image = MiscUtils.loadImage(image, contents);
JLabel label = new JLabel("", new ImageIcon(image), JLabel.CENTER);
mainPanel.add(label, BorderLayout.CENTER);
mainPanel.updateUI();
src.setEnabled(true);
}
private static final long serialVersionUID = 6103372882168257164L;
}

View File

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui;
package the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer;
import javax.swing.JPanel;
import org.objectweb.asm.tree.ClassNode;
@ -22,11 +22,11 @@ import the.bytecode.club.bytecodeviewer.util.FileContainer;
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
public abstract class Viewer extends JPanel {
public abstract class ResourceViewer extends JPanel
{
public ClassNode cn;
public String name;
FileContainer container;
public FileContainer container;
private static final long serialVersionUID = -2965538493489119191L;
}
}

View File

@ -0,0 +1,39 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.synchronizedscroll;
import org.objectweb.asm.Type;
import java.util.Arrays;
/**
* @author Konloch
* @since 6/24/2021
*/
public class MethodData
{
public String name, desc;
@Override
public boolean equals(final Object o)
{
return equals((MethodData) o);
}
public boolean equals(final MethodData md)
{
return this.name.equals(md.name) && this.desc.equals(md.desc);
}
public String constructPattern()
{
final StringBuilder pattern = new StringBuilder();
pattern.append(name).append(" *\\(");
final org.objectweb.asm.Type[] types = org.objectweb.asm.Type
.getArgumentTypes(desc);
pattern.append("(.*)");
Arrays.stream(types).map(Type::getClassName)
.forEach(clazzName -> pattern.append(clazzName.substring(clazzName.lastIndexOf(".") + 1)).append(
"(.*)"));
pattern.append("\\) *\\{");
return pattern.toString();
}
}

View File

@ -0,0 +1,51 @@
package the.bytecode.club.bytecodeviewer.gui.theme;
import com.bulenkov.darcula.DarculaLaf;
import javax.swing.*;
/**
* @author Konloch
* @author ThexXTURBOXx
* @since 6/24/2021
*/
public enum LAFTheme
{
LIGHT("Light Theme (Requires restart)", RSTATheme.DEFAULT), //System theme
DARK("Dark Theme (Requires restart)", RSTATheme.DARK), //Darcula
;
private final String readableName;
private final RSTATheme rstaTheme;
LAFTheme(String readableName, RSTATheme rstaTheme)
{
this.readableName = readableName;
this.rstaTheme = rstaTheme;
}
public String getReadableName()
{
return readableName;
}
public RSTATheme getRSTATheme()
{
return rstaTheme;
}
public void setLAF() throws ClassNotFoundException, UnsupportedLookAndFeelException, InstantiationException, IllegalAccessException
{
switch(this)
{
default:
case LIGHT:
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
break;
case DARK:
UIManager.setLookAndFeel(new DarculaLaf());
break;
}
}
}

View File

@ -0,0 +1,54 @@
package the.bytecode.club.bytecodeviewer.gui.theme;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Theme;
import the.bytecode.club.bytecodeviewer.Constants;
/**
* @author ThexXTURBOXx
* @since 6/23/2021
*/
public enum RSTATheme
{
DEFAULT("Default (Recommended Light)", null),
DARK("Dark (Recommended Dark)", "/org/fife/ui/rsyntaxtextarea/themes/dark.xml"),
DEFAULT_ALT( "Default-Alt", "/org/fife/ui/rsyntaxtextarea/themes/default-alt.xml"),
ECLIPSE("Eclipse", "/org/fife/ui/rsyntaxtextarea/themes/eclipse.xml"),
IDEA("IntelliJ", "/org/fife/ui/rsyntaxtextarea/themes/idea.xml"),
VS("Visual Studio", "/org/fife/ui/rsyntaxtextarea/themes/vs.xml"),
DRUID( "Druid (Dark)", "/org/fife/ui/rsyntaxtextarea/themes/druid.xml"),
MONOKAI( "Monokai (Dark)", "/org/fife/ui/rsyntaxtextarea/themes/monokai.xml");
private final String readableName;
private final String file;
RSTATheme(String readableName, String file)
{
this.readableName = readableName;
this.file = file;
}
public String getReadableName() {
return readableName;
}
public RSyntaxTextArea apply(RSyntaxTextArea area) {
if (file != null) {
try {
Theme.load(Constants.class.getResourceAsStream(file)).apply(area);
} catch (Throwable ignored) {
}
}
return area;
}
public static RSTATheme parse(String name) {
for (RSTATheme t : values()) {
if (t.name().equals(name)) {
return t;
}
}
return DEFAULT;
}
}

View File

@ -1,4 +1,6 @@
package the.bytecode.club.bytecodeviewer.gui;
package the.bytecode.club.bytecodeviewer.gui.util;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane;
import java.awt.Color;
import javax.swing.SwingUtilities;
@ -6,7 +8,8 @@ import javax.swing.SwingUtilities;
/**
* @author Konloch
*/
public class DelayTabbedPaneThread extends Thread {
public class DelayTabbedPaneThread extends Thread
{
public boolean stopped = false;
private final TabbedPane pane;

View File

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui;
package the.bytecode.club.bytecodeviewer.gui.util;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@ -19,17 +19,13 @@ package the.bytecode.club.bytecodeviewer.gui;
***************************************************************************/
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.InputEvent;
import java.util.Objects;
import java.util.regex.Matcher;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JViewport;
import javax.swing.ListCellRenderer;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
@ -37,9 +33,12 @@ import javax.swing.event.ChangeListener;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.components.MethodsRenderer;
import the.bytecode.club.bytecodeviewer.gui.components.SearchableRSyntaxTextArea;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.util.MethodParser;
import static the.bytecode.club.bytecodeviewer.gui.TabbedPane.BLANK;
import static the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane.BLANK_COLOR;
/**
* Allows us to run a background thread
@ -47,21 +46,40 @@ import static the.bytecode.club.bytecodeviewer.gui.TabbedPane.BLANK;
* @author Konloch
* @author DreamSworK
*/
public abstract class PaneUpdaterThread extends Thread {
public abstract class PaneUpdaterThread implements Runnable
{
public ClassViewer viewer;
public RSyntaxTextArea panelArea;
public RTextScrollPane scrollPane;
public SearchableRSyntaxTextArea updateUpdaterTextArea;
public JComboBox<Integer> methodsList;
public int decompiler;
public int paneId;
private Thread thread;
public int paneIndex;
public int decompilerViewIndex;
public PaneUpdaterThread(int paneIndex, int decompilerViewIndex)
{
this.paneIndex = paneIndex;
this.decompilerViewIndex = decompilerViewIndex;
}
public abstract void doShit();
public void startNewThread()
{
thread = new Thread(this);
thread.start();
}
@Override
public void run() {
public void run()
{
if(decompilerViewIndex == 0)
return;
doShit();
synchronizePane();
attachCtrlMouseWheelZoom(scrollPane, panelArea); //freezes the UI for some reason
attachCtrlMouseWheelZoom(updateUpdaterTextArea.getScrollPane(), updateUpdaterTextArea); //freezes the UI for some reason
//probably cause BCV is doing dumb shit with the swing thread
}
@ -89,9 +107,9 @@ public abstract class PaneUpdaterThread extends Thread {
public final CaretListener caretListener = new CaretListener() {
@Override
public void caretUpdate(CaretEvent e) {
MethodParser methods = viewer.methods.get(paneId);
MethodParser methods = viewer.methods.get(paneIndex);
if (methods != null) {
int methodLine = methods.findActiveMethod(panelArea.getCaretLineNumber());
int methodLine = methods.findActiveMethod(updateUpdaterTextArea.getCaretLineNumber());
if (methodLine != -1) {
if (BytecodeViewer.viewer.showClassMethods.isSelected()) {
if (methodsList != null) {
@ -102,11 +120,11 @@ public abstract class PaneUpdaterThread extends Thread {
}
if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) {
int panes = 2;
if (viewer.panel3 != null)
if (viewer.resourceViewPanel3.panel != null)
panes = 3;
for (int i = 0; i < panes; i++) {
if (i != paneId) {
if (i != paneIndex) {
ClassViewer.selectMethod(viewer, i, methods.getMethod(methodLine));
}
}
@ -120,41 +138,41 @@ public abstract class PaneUpdaterThread extends Thread {
@Override
public void stateChanged(ChangeEvent e) {
int panes = 2;
if (viewer.panel3 != null)
if (viewer.resourceViewPanel3.panel != null)
panes = 3;
if (BytecodeViewer.viewer.synchronizedViewing.isSelected()) {
if (panelArea.isShowing() && (panelArea.hasFocus() || panelArea.getMousePosition() != null)) {
int caretLine = panelArea.getCaretLineNumber();
int maxViewLine = ClassViewer.getMaxViewLine(panelArea);
int activeViewLine = ClassViewer.getViewLine(panelArea);
if (updateUpdaterTextArea.isShowing() && (updateUpdaterTextArea.hasFocus() || updateUpdaterTextArea.getMousePosition() != null)) {
int caretLine = updateUpdaterTextArea.getCaretLineNumber();
int maxViewLine = ClassViewer.getMaxViewLine(updateUpdaterTextArea);
int activeViewLine = ClassViewer.getViewLine(updateUpdaterTextArea);
int activeLine = (activeViewLine == maxViewLine && caretLine > maxViewLine) ? caretLine :
activeViewLine;
int activeLineDelta = -1;
MethodParser.Method activeMethod = null;
MethodParser activeMethods = viewer.methods.get(paneId);
MethodParser activeMethods = viewer.methods.get(paneIndex);
if (activeMethods != null) {
int activeMethodLine = activeMethods.findActiveMethod(activeLine);
if (activeMethodLine != -1) {
activeLineDelta = activeLine - activeMethodLine;
activeMethod = activeMethods.getMethod(activeMethodLine);
ClassViewer.selectMethod(panelArea, activeMethodLine);
ClassViewer.selectMethod(updateUpdaterTextArea, activeMethodLine);
}
}
for (int i = 0; i < panes; i++) {
if (i != paneId) {
if (i != paneIndex) {
int setLine = -1;
RSyntaxTextArea area = null;
switch (i) {
case 0:
area = viewer.t1.panelArea;
area = viewer.resourceViewPanel1.updateThread.updateUpdaterTextArea;
break;
case 1:
area = viewer.t2.panelArea;
area = viewer.resourceViewPanel2.updateThread.updateUpdaterTextArea;
break;
case 2:
area = viewer.t3.panelArea;
area = viewer.resourceViewPanel3.updateThread.updateUpdaterTextArea;
break;
}
@ -183,30 +201,19 @@ public abstract class PaneUpdaterThread extends Thread {
}
}
};
class MethodsRenderer extends JLabel implements ListCellRenderer<Object> {
public MethodsRenderer() {
setOpaque(true);
}
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
MethodParser methods = viewer.methods.get(paneId);
MethodParser.Method method = methods.getMethod((Integer) value);
setText(method.toString());
return this;
}
}
public void synchronizePane() {
JViewport viewport = scrollPane.getViewport();
public void synchronizePane()
{
if(decompilerViewIndex == 5 || decompilerViewIndex < 0)
return;
JViewport viewport = updateUpdaterTextArea.getScrollPane().getViewport();
viewport.addChangeListener(viewportListener);
panelArea.addCaretListener(caretListener);
final MethodParser methods = viewer.methods.get(paneId);
for (int i = 0; i < panelArea.getLineCount(); i++) {
String lineText = ClassViewer.getLineText(panelArea, i);
updateUpdaterTextArea.addCaretListener(caretListener);
final MethodParser methods = viewer.methods.get(paneIndex);
for (int i = 0; i < updateUpdaterTextArea.getLineCount(); i++) {
String lineText = ClassViewer.getLineText(updateUpdaterTextArea, i);
Matcher regexMatcher = MethodParser.regex.matcher(lineText);
if (regexMatcher.find()) {
String methodName = regexMatcher.group("name");
@ -221,21 +228,23 @@ public abstract class PaneUpdaterThread extends Thread {
for (Integer line : methods.getMethodsLines()) {
methodsList.addItem(line);
}
methodsList.setRenderer(new PaneUpdaterThread.MethodsRenderer());
methodsList.addActionListener(e -> {
methodsList.setRenderer(new MethodsRenderer(this));
methodsList.addActionListener(e ->
{
int line = (int) Objects.requireNonNull(methodsList.getSelectedItem());
RSyntaxTextArea area = null;
switch (paneId) {
case 0:
area = viewer.t1.panelArea;
break;
case 1:
area = viewer.t2.panelArea;
break;
case 2:
area = viewer.t3.panelArea;
break;
switch (paneIndex)
{
case 0:
area = viewer.resourceViewPanel1.updateThread.updateUpdaterTextArea;
break;
case 1:
area = viewer.resourceViewPanel2.updateThread.updateUpdaterTextArea;
break;
case 2:
area = viewer.resourceViewPanel3.updateThread.updateUpdaterTextArea;
break;
}
if (area != null)
@ -243,11 +252,11 @@ public abstract class PaneUpdaterThread extends Thread {
});
JPanel panel = new JPanel(new BorderLayout());
panel.add(scrollPane.getColumnHeader().getComponent(0), BorderLayout.NORTH);
panel.add(updateUpdaterTextArea.getScrollPane().getColumnHeader().getComponent(0), BorderLayout.NORTH);
panel.add(methodsList, BorderLayout.SOUTH);
methodsList.setBackground(BLANK);
scrollPane.getColumnHeader().removeAll();
scrollPane.getColumnHeader().add(panel);
methodsList.setBackground(BLANK_COLOR);
updateUpdaterTextArea.getScrollPane().getColumnHeader().removeAll();
updateUpdaterTextArea.getScrollPane().getColumnHeader().add(panel);
}
}
}

View File

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.gui.extras;
package the.bytecode.club.bytecodeviewer.gui.util;
import java.awt.*;
import java.awt.font.FontRenderContext;

View File

@ -15,7 +15,7 @@ import org.objectweb.asm.tree.MethodNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.api.Plugin;
import the.bytecode.club.bytecodeviewer.gui.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
import the.bytecode.club.bytecodeviewer.plugin.PluginManager;
/***************************************************************************

View File

@ -0,0 +1,170 @@
package the.bytecode.club.bytecodeviewer.util;
import javax.swing.*;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Document;
import javax.swing.text.Highlighter;
import java.awt.*;
/**
* This allows functionality to main the same between JTextArea and RSyntaxTextArea text panels
*
* @author Konloch
* @since 6/25/2021
*/
public class JTextAreaUtils
{
private static final DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(new Color(255, 62, 150));
/**
* This was really interesting to write.
*
* @author Konloch
*/
public static void search(JTextArea textArea, String search, boolean forwardSearchDirection, boolean caseSensitiveSearch)
{
try
{
if (search.isEmpty())
{
highlight(textArea, "", caseSensitiveSearch);
return;
}
int startLine = textArea.getDocument().getDefaultRootElement()
.getElementIndex(textArea.getCaretPosition()) + 1;
int currentLine = 1;
boolean canSearch = false;
String[] test = (textArea.getText().split("\n").length >= 2
? textArea.getText().split("\n")
: textArea.getText().split("\r"));
int lastGoodLine = -1;
int firstPos = -1;
boolean found = false;
if (forwardSearchDirection)
{
for (String s : test)
{
if (!caseSensitiveSearch)
{
s = s.toLowerCase();
search = search.toLowerCase();
}
if (currentLine == startLine)
{
canSearch = true;
}
else if (s.contains(search))
{
if (canSearch)
{
textArea.setCaretPosition(textArea.getDocument()
.getDefaultRootElement()
.getElement(currentLine - 1)
.getStartOffset());
canSearch = false;
found = true;
}
if (firstPos == -1)
firstPos = currentLine;
}
currentLine++;
}
if (!found && firstPos != -1)
{
textArea.setCaretPosition(textArea.getDocument()
.getDefaultRootElement().getElement(firstPos - 1)
.getStartOffset());
}
}
else
{
canSearch = true;
for (String s : test)
{
if (!caseSensitiveSearch)
{
s = s.toLowerCase();
search = search.toLowerCase();
}
if (s.contains(search))
{
if (lastGoodLine != -1 && canSearch)
textArea.setCaretPosition(textArea.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1)
.getStartOffset());
lastGoodLine = currentLine;
if (currentLine >= startLine)
canSearch = false;
}
currentLine++;
}
if (lastGoodLine != -1
&& textArea.getDocument().getDefaultRootElement()
.getElementIndex(textArea.getCaretPosition()) + 1 == startLine)
{
textArea.setCaretPosition(textArea.getDocument()
.getDefaultRootElement()
.getElement(lastGoodLine - 1).getStartOffset());
}
}
highlight(textArea, search, caseSensitiveSearch);
}
catch (Exception e)
{
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public static void highlight(JTextArea textArea,String pattern, boolean caseSensitiveSearch)
{
if (pattern.isEmpty())
{
textArea.getHighlighter().removeAllHighlights();
return;
}
try
{
Highlighter highlighter = textArea.getHighlighter();
highlighter.removeAllHighlights();
Document doc = textArea.getDocument();
String text = doc.getText(0, doc.getLength());
int pos = 0;
if (!caseSensitiveSearch)
{
pattern = pattern.toLowerCase();
text = text.toLowerCase();
}
// Search for pattern
while ((pos = text.indexOf(pattern, pos)) >= 0)
{
// Create highlighter using private painter and apply around
// pattern
highlighter.addHighlight(pos, pos + pattern.length(), painter);
pos += pattern.length();
}
}
catch (Exception e)
{
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
}

View File

@ -1,13 +1,17 @@
package the.bytecode.club.bytecodeviewer.util;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.tree.ClassNode;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.List;
import static the.bytecode.club.bytecodeviewer.Constants.gson;
@ -35,7 +39,9 @@ import static the.bytecode.club.bytecodeviewer.Constants.gson;
* @author Konloch
*/
public class MiscUtils {
public class MiscUtils
{
private static CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); // or "ISO-8859-1" for ISO Latin 1
private static final String AB = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static final String AN = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static final Random rnd = new Random();
@ -192,4 +198,30 @@ public class MiscUtils {
field.setAccessible(true);
((Map<String, String>) field.get(env)).put(name, val);
}
public static BufferedImage loadImage(BufferedImage defaultImage, byte[] contents)
{
try {
return ImageIO.read(new ByteArrayInputStream(contents));
} catch (IOException e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
return defaultImage;
}
public static boolean isPureAscii(String v) {
return asciiEncoder.canEncode(v);
}
public static String getChildFromPath(String path)
{
if (path.contains("/"))
{
String[] pathParts = StringUtils.split(path, "/");
return pathParts[pathParts.length-1];
}
return path;
}
}

View File

@ -5,7 +5,7 @@ import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.api.ExceptionUI;
import the.bytecode.club.bytecodeviewer.decompilers.Decompilers;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
@ -101,7 +101,7 @@ public class ResourceDecompiling
if (result == 0) {
Thread t12 = new Thread(() -> {
try {
Decompilers.procyon.decompileToZip(tempZip.getAbsolutePath(),
Decompiler.PROCYON.getDecompiler().decompileToZip(tempZip.getAbsolutePath(),
MiscUtils.append(javaSucks, "-proycon.zip"));
BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) {
@ -112,7 +112,7 @@ public class ResourceDecompiling
Thread t2 = new Thread(() -> {
try {
BytecodeViewer.viewer.updateBusyStatus(true);
Decompilers.cfr.decompileToZip(tempZip.getAbsolutePath(),
Decompiler.CFR.getDecompiler().decompileToZip(tempZip.getAbsolutePath(),
MiscUtils.append(javaSucks, "-CFR.zip"));
BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) {
@ -123,7 +123,7 @@ public class ResourceDecompiling
Thread t3 = new Thread(() -> {
try {
BytecodeViewer.viewer.updateBusyStatus(true);
Decompilers.fernflower.decompileToZip(tempZip.getAbsolutePath(),
Decompiler.FERNFLOWER.getDecompiler().decompileToZip(tempZip.getAbsolutePath(),
MiscUtils.append(javaSucks, "-fernflower.zip"));
BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) {
@ -134,7 +134,7 @@ public class ResourceDecompiling
Thread t4 = new Thread(() -> {
try {
BytecodeViewer.viewer.updateBusyStatus(true);
Decompilers.krakatau.decompileToZip(tempZip.getAbsolutePath(),
Decompiler.KRAKATAU.getDecompiler().decompileToZip(tempZip.getAbsolutePath(),
MiscUtils.append(javaSucks, "-kraktau.zip"));
BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) {
@ -146,7 +146,7 @@ public class ResourceDecompiling
if (result == 1) {
Thread t12 = new Thread(() -> {
try {
Decompilers.procyon.decompileToZip(tempZip.getAbsolutePath(), path);
Decompiler.PROCYON.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), path);
BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) {
new ExceptionUI(e);
@ -157,7 +157,7 @@ public class ResourceDecompiling
if (result == 2) {
Thread t12 = new Thread(() -> {
try {
Decompilers.cfr.decompileToZip(tempZip.getAbsolutePath(), path);
Decompiler.CFR.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), path);
BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) {
new ExceptionUI(e);
@ -168,7 +168,7 @@ public class ResourceDecompiling
if (result == 3) {
Thread t12 = new Thread(() -> {
try {
Decompilers.fernflower.decompileToZip(tempZip.getAbsolutePath(), path);
Decompiler.FERNFLOWER.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), path);
BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) {
new ExceptionUI(e);
@ -180,7 +180,7 @@ public class ResourceDecompiling
if (result == 4) {
Thread t12 = new Thread(() -> {
try {
Decompilers.krakatau.decompileToZip(tempZip.getAbsolutePath(), path);
Decompiler.KRAKATAU.getDecompiler().decompileToZip(tempZip.getAbsolutePath(), path);
BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) {
new ExceptionUI(e);
@ -288,28 +288,28 @@ public class ResourceDecompiling
try {
DiskWriter.replaceFile(MiscUtils.append(file, "-proycon.java"),
Decompilers.procyon.decompileClassNode(cn, cw.toByteArray()), false);
Decompiler.PROCYON.getDecompiler().decompileClassNode(cn, cw.toByteArray()), false);
} catch (Exception e) {
e.printStackTrace();
}
try {
DiskWriter.replaceFile(MiscUtils.append(file, "-CFR.java"),
Decompilers.cfr.decompileClassNode(cn, cw.toByteArray()), false);
Decompiler.CFR.getDecompiler().decompileClassNode(cn, cw.toByteArray()), false);
} catch (Exception e) {
e.printStackTrace();
}
try {
DiskWriter.replaceFile(MiscUtils.append(file, "-fernflower.java"),
Decompilers.fernflower.decompileClassNode(cn, cw.toByteArray()), false);
Decompiler.FERNFLOWER.getDecompiler().decompileClassNode(cn, cw.toByteArray()), false);
} catch (Exception e) {
e.printStackTrace();
}
try {
DiskWriter.replaceFile(MiscUtils.append(file, "-kraktau.java"),
Decompilers.krakatau.decompileClassNode(cn, cw.toByteArray()), false);
Decompiler.KRAKATAU.getDecompiler().decompileClassNode(cn, cw.toByteArray()), false);
} catch (Exception e) {
e.printStackTrace();
}
@ -337,8 +337,7 @@ public class ResourceDecompiling
} catch (InterruptedException ignored) {
}
}
String contents = Decompilers.procyon.decompileClassNode(cn,
cw.toByteArray());
String contents = Decompiler.PROCYON.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) {
@ -364,7 +363,7 @@ public class ResourceDecompiling
} catch (InterruptedException ignored) {
}
}
String contents = Decompilers.cfr.decompileClassNode(cn, cw.toByteArray());
String contents = Decompiler.CFR.getDecompiler().decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.updateBusyStatus(false);
} catch (Exception e) {
@ -391,7 +390,7 @@ public class ResourceDecompiling
} catch (InterruptedException ignored) {
}
}
String contents = Decompilers.fernflower.decompileClassNode(cn,
String contents = Decompiler.FERNFLOWER.getDecompiler().decompileClassNode(cn,
cw.toByteArray());
DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.updateBusyStatus(false);
@ -419,7 +418,7 @@ public class ResourceDecompiling
}
}
String contents = Decompilers.krakatau.decompileClassNode(cn,
String contents = Decompiler.KRAKATAU.getDecompiler().decompileClassNode(cn,
cw.toByteArray());
DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.updateBusyStatus(false);

View File

@ -1,7 +1,7 @@
package the.bytecode.club.bytecodeviewer.util;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.extras.ExportJar;
import the.bytecode.club.bytecodeviewer.gui.components.ExportJar;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;

View File

@ -3,8 +3,8 @@ package the.bytecode.club.bytecodeviewer.util;
import java.util.function.BiFunction;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
public enum Language {
public enum SyntaxLanguage
{
XML(SyntaxConstants.SYNTAX_STYLE_XML, (n, c) -> n.endsWith(".xml")
|| c.startsWith("<?xml") || c.startsWith("<xml")),
PYTHON(SyntaxConstants.SYNTAX_STYLE_PYTHON, (n, c) -> n.endsWith(".py") || n.endsWith(".python")),
@ -42,13 +42,13 @@ public enum Language {
TYPESCRIPT(SyntaxConstants.SYNTAX_STYLE_TYPESCRIPT, (n, c) -> n.endsWith(".ts")),
NONE(SyntaxConstants.SYNTAX_STYLE_NONE, (n, c) -> false);
public static final Language[] VALUES = values();
public static final SyntaxLanguage[] VALUES = values();
private final BiFunction<String, String, Boolean> criteria;
private final String syntaxConstant;
Language(String syntaxConstant, BiFunction<String, String, Boolean> criteria) {
SyntaxLanguage(String syntaxConstant, BiFunction<String, String, Boolean> criteria) {
this.criteria = criteria;
this.syntaxConstant = syntaxConstant;
}
@ -61,13 +61,12 @@ public enum Language {
return syntaxConstant;
}
public static Language detectLanguage(String fileName, String content) {
for (Language lang : VALUES) {
public static SyntaxLanguage detectLanguage(String fileName, String content) {
for (SyntaxLanguage lang : VALUES) {
if (lang.isLanguage(fileName, content)) {
return lang;
}
}
return NONE;
}
}
}

View File

@ -1,3 +0,0 @@
Manifest-Version: 1.0
Main-Class: the.bytecode.club.bytecodeviewer.BytecodeViewer

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 B

After

Width:  |  Height:  |  Size: 1.9 KiB