Resource Exporting Cleanup
This commit is contained in:
parent
51ce387345
commit
b0f5e938db
12 changed files with 425 additions and 317 deletions
|
@ -31,9 +31,9 @@ import the.bytecode.club.bytecodeviewer.plugin.preinstalled.ShowMainMethods;
|
||||||
import the.bytecode.club.bytecodeviewer.plugin.preinstalled.StackFramesRemover;
|
import the.bytecode.club.bytecodeviewer.plugin.preinstalled.StackFramesRemover;
|
||||||
import the.bytecode.club.bytecodeviewer.plugin.preinstalled.ZKMStringDecrypter;
|
import the.bytecode.club.bytecodeviewer.plugin.preinstalled.ZKMStringDecrypter;
|
||||||
import the.bytecode.club.bytecodeviewer.plugin.preinstalled.ZStringArrayDecrypter;
|
import the.bytecode.club.bytecodeviewer.plugin.preinstalled.ZStringArrayDecrypter;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.exporting.Export;
|
||||||
import the.bytecode.club.bytecodeviewer.util.*;
|
import the.bytecode.club.bytecodeviewer.util.*;
|
||||||
import the.bytecode.club.bytecodeviewer.resources.ResourceDecompiling;
|
import the.bytecode.club.bytecodeviewer.resources.ResourceDecompiling;
|
||||||
import the.bytecode.club.bytecodeviewer.resources.ResourceExporting;
|
|
||||||
|
|
||||||
import static the.bytecode.club.bytecodeviewer.Constants.*;
|
import static the.bytecode.club.bytecodeviewer.Constants.*;
|
||||||
|
|
||||||
|
@ -341,10 +341,10 @@ public class MainViewerGUI extends JFrame
|
||||||
reloadResources.addActionListener(arg0 -> reloadResources());
|
reloadResources.addActionListener(arg0 -> reloadResources());
|
||||||
runButton.addActionListener(e -> runResources());
|
runButton.addActionListener(e -> runResources());
|
||||||
compileButton.addActionListener(arg0 -> compileOnNewThread());
|
compileButton.addActionListener(arg0 -> compileOnNewThread());
|
||||||
saveAsRunnableJar.addActionListener(e -> ResourceExporting.saveAsRunnableJar());
|
saveAsRunnableJar.addActionListener(e -> Export.RUNNABLE_JAR.getExporter().promptForExport());
|
||||||
saveAsAPK.addActionListener(arg0 -> ResourceExporting.saveAsAPK());
|
saveAsAPK.addActionListener(arg0 -> Export.APK.getExporter().promptForExport());
|
||||||
saveAsDex.addActionListener(arg0 -> ResourceExporting.saveAsDex());
|
saveAsDex.addActionListener(arg0 -> Export.DEX.getExporter().promptForExport());
|
||||||
saveAsZip.addActionListener(arg0 -> ResourceExporting.saveAsZip());
|
saveAsZip.addActionListener(arg0 -> Export.ZIP.getExporter().promptForExport());
|
||||||
decompileSaveAll.addActionListener(arg0 -> ResourceDecompiling.decompileSaveAll());
|
decompileSaveAll.addActionListener(arg0 -> ResourceDecompiling.decompileSaveAll());
|
||||||
decompileSaveOpened.addActionListener(arg0 -> ResourceDecompiling.decompileSaveOpenedOnly());
|
decompileSaveOpened.addActionListener(arg0 -> ResourceDecompiling.decompileSaveOpenedOnly());
|
||||||
about.addActionListener(arg0 -> aboutWindow.setVisible(true));
|
about.addActionListener(arg0 -> aboutWindow.setVisible(true));
|
||||||
|
|
|
@ -53,15 +53,17 @@ public class ExportJar extends JFrame
|
||||||
|
|
||||||
scrollPane.setColumnHeaderView(new JLabel("META-INF/MANIFEST.MF:"));
|
scrollPane.setColumnHeaderView(new JLabel("META-INF/MANIFEST.MF:"));
|
||||||
|
|
||||||
final JTextArea mani = new JTextArea();
|
final JTextArea manifest = new JTextArea();
|
||||||
mani.setText("Manifest-Version: 1.0\r\nClass-Path: .\r\nMain-Class: ");
|
manifest.setText("Manifest-Version: 1.0\r\nClass-Path: .\r\nMain-Class: ");
|
||||||
scrollPane.setViewportView(mani);
|
scrollPane.setViewportView(manifest);
|
||||||
getContentPane().add(btnNewButton);
|
getContentPane().add(btnNewButton);
|
||||||
|
|
||||||
btnNewButton.addActionListener(arg0 -> {
|
btnNewButton.addActionListener(arg0 ->
|
||||||
|
{
|
||||||
BytecodeViewer.viewer.updateBusyStatus(true);
|
BytecodeViewer.viewer.updateBusyStatus(true);
|
||||||
Thread t = new Thread(() -> {
|
Thread t = new Thread(() ->
|
||||||
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), jarPath, mani.getText());
|
{
|
||||||
|
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), jarPath, manifest.getText());
|
||||||
BytecodeViewer.viewer.updateBusyStatus(false);
|
BytecodeViewer.viewer.updateBusyStatus(false);
|
||||||
});
|
});
|
||||||
t.start();
|
t.start();
|
||||||
|
|
|
@ -1,294 +0,0 @@
|
||||||
package the.bytecode.club.bytecodeviewer.resources;
|
|
||||||
|
|
||||||
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
|
||||||
import the.bytecode.club.bytecodeviewer.Configuration;
|
|
||||||
import the.bytecode.club.bytecodeviewer.gui.components.ExportJar;
|
|
||||||
import the.bytecode.club.bytecodeviewer.gui.components.FileChooser;
|
|
||||||
import the.bytecode.club.bytecodeviewer.gui.components.MultipleChoiceDialogue;
|
|
||||||
import the.bytecode.club.bytecodeviewer.util.*;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.filechooser.FileFilter;
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static the.bytecode.club.bytecodeviewer.Constants.fs;
|
|
||||||
import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Konloch
|
|
||||||
* @since 6/21/2021
|
|
||||||
*/
|
|
||||||
public class ResourceExporting
|
|
||||||
{
|
|
||||||
public static void saveAsRunnableJar()
|
|
||||||
{
|
|
||||||
if (BytecodeViewer.getLoadedClasses().isEmpty())
|
|
||||||
{
|
|
||||||
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread exportThread = new Thread(() ->
|
|
||||||
{
|
|
||||||
if (BytecodeViewer.viewer.compileOnSave.isSelected() && !BytecodeViewer.compile(false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
JFileChooser fc = new FileChooser(new File(Configuration.lastDirectory),
|
|
||||||
"Select Jar Export",
|
|
||||||
"Jar Archives",
|
|
||||||
"jar");
|
|
||||||
|
|
||||||
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
|
|
||||||
if (returnVal == JFileChooser.APPROVE_OPTION)
|
|
||||||
{
|
|
||||||
Configuration.lastDirectory = fc.getSelectedFile().getAbsolutePath();
|
|
||||||
File file = fc.getSelectedFile();
|
|
||||||
String path = file.getAbsolutePath();
|
|
||||||
|
|
||||||
//auto append .jar
|
|
||||||
if (!path.endsWith(".jar"))
|
|
||||||
path = path + ".jar";
|
|
||||||
|
|
||||||
if (new File(path).exists())
|
|
||||||
{
|
|
||||||
MultipleChoiceDialogue dialogue = new MultipleChoiceDialogue("Bytecode Viewer - Overwrite File",
|
|
||||||
"Are you sure you wish to overwrite this existing file?",
|
|
||||||
new String[]{"Yes", "No"});
|
|
||||||
|
|
||||||
if (dialogue.promptChoice() == 0) {
|
|
||||||
file.delete();
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new ExportJar(path).setVisible(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
exportThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void saveAsZip()
|
|
||||||
{
|
|
||||||
if (BytecodeViewer.getLoadedClasses().isEmpty())
|
|
||||||
{
|
|
||||||
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread exportThread = new Thread(() ->
|
|
||||||
{
|
|
||||||
if (BytecodeViewer.viewer.compileOnSave.isSelected() && !BytecodeViewer.compile(false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
JFileChooser fc = new FileChooser(new File(Configuration.lastDirectory),
|
|
||||||
"Select Zip Export",
|
|
||||||
"Zip Archives",
|
|
||||||
"zip");
|
|
||||||
|
|
||||||
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
|
|
||||||
if (returnVal == JFileChooser.APPROVE_OPTION)
|
|
||||||
{
|
|
||||||
Configuration.lastDirectory = fc.getSelectedFile().getAbsolutePath();
|
|
||||||
File file = fc.getSelectedFile();
|
|
||||||
|
|
||||||
//auto append .zip
|
|
||||||
if (!file.getAbsolutePath().endsWith(".zip"))
|
|
||||||
file = new File(file.getAbsolutePath() + ".zip");
|
|
||||||
|
|
||||||
if (file.exists())
|
|
||||||
{
|
|
||||||
MultipleChoiceDialogue dialogue = new MultipleChoiceDialogue("Bytecode Viewer - Overwrite File",
|
|
||||||
"Are you sure you wish to overwrite this existing file?",
|
|
||||||
new String[]{"Yes", "No"});
|
|
||||||
|
|
||||||
if (dialogue.promptChoice() == 0) {
|
|
||||||
file.delete();
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final File file2 = file;
|
|
||||||
|
|
||||||
BytecodeViewer.viewer.updateBusyStatus(true);
|
|
||||||
Thread saveThread = new Thread(() ->
|
|
||||||
{
|
|
||||||
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), file2.getAbsolutePath());
|
|
||||||
BytecodeViewer.viewer.updateBusyStatus(false);
|
|
||||||
});
|
|
||||||
saveThread.start();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
exportThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void saveAsDex()
|
|
||||||
{
|
|
||||||
if (BytecodeViewer.getLoadedClasses().isEmpty())
|
|
||||||
{
|
|
||||||
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread t = new Thread(() ->
|
|
||||||
{
|
|
||||||
if (BytecodeViewer.viewer.compileOnSave.isSelected() && !BytecodeViewer.compile(false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
JFileChooser fc = new FileChooser(new File(Configuration.lastDirectory),
|
|
||||||
"Select DEX Export",
|
|
||||||
"Android DEX Files",
|
|
||||||
"dex");
|
|
||||||
|
|
||||||
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
|
|
||||||
if (returnVal == JFileChooser.APPROVE_OPTION)
|
|
||||||
{
|
|
||||||
Configuration.lastDirectory = fc.getSelectedFile().getAbsolutePath();
|
|
||||||
final File file = fc.getSelectedFile();
|
|
||||||
String output = file.getAbsolutePath();
|
|
||||||
|
|
||||||
//auto append .dex
|
|
||||||
if (!output.endsWith(".dex"))
|
|
||||||
output = output + ".dex";
|
|
||||||
|
|
||||||
final File file2 = new File(output);
|
|
||||||
if (file2.exists())
|
|
||||||
{
|
|
||||||
MultipleChoiceDialogue dialogue = new MultipleChoiceDialogue("Bytecode Viewer - Overwrite File",
|
|
||||||
"Are you sure you wish to overwrite this existing file?",
|
|
||||||
new String[]{"Yes", "No"});
|
|
||||||
|
|
||||||
if (dialogue.promptChoice() == 0) {
|
|
||||||
file.delete();
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread t16 = new Thread(() -> {
|
|
||||||
BytecodeViewer.viewer.updateBusyStatus(true);
|
|
||||||
final String input = tempDirectory + fs + MiscUtils.getRandomizedName() + ".jar";
|
|
||||||
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), input);
|
|
||||||
|
|
||||||
Thread t15 = new Thread(() -> {
|
|
||||||
Dex2Jar.saveAsDex(new File(input), file2);
|
|
||||||
|
|
||||||
BytecodeViewer.viewer.updateBusyStatus(false);
|
|
||||||
});
|
|
||||||
t15.start();
|
|
||||||
});
|
|
||||||
t16.start();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
t.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void saveAsAPK()
|
|
||||||
{
|
|
||||||
if (BytecodeViewer.getLoadedClasses().isEmpty())
|
|
||||||
{
|
|
||||||
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if theres only one file in the container don't bother asking
|
|
||||||
List<FileContainer> containers = BytecodeViewer.getFiles();
|
|
||||||
List<FileContainer> validContainers = new ArrayList<>();
|
|
||||||
List<String> validContainersNames = new ArrayList<>();
|
|
||||||
FileContainer container;
|
|
||||||
|
|
||||||
for (FileContainer fileContainer : containers)
|
|
||||||
{
|
|
||||||
if (fileContainer.APKToolContents != null && fileContainer.APKToolContents.exists())
|
|
||||||
{
|
|
||||||
validContainersNames.add(fileContainer.name);
|
|
||||||
validContainers.add(fileContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validContainers.isEmpty())
|
|
||||||
{
|
|
||||||
container = validContainers.get(0);
|
|
||||||
|
|
||||||
if (validContainers.size() >= 2)
|
|
||||||
{
|
|
||||||
JOptionPane pane = new JOptionPane("Which file would you like to export as an APK?");
|
|
||||||
Object[] options = validContainersNames.toArray(new String[0]);
|
|
||||||
|
|
||||||
pane.setOptions(options);
|
|
||||||
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Select APK");
|
|
||||||
dialog.setVisible(true);
|
|
||||||
Object obj = pane.getValue();
|
|
||||||
int result = -1;
|
|
||||||
for (int k = 0; k < options.length; k++)
|
|
||||||
if (options[k].equals(obj))
|
|
||||||
result = k;
|
|
||||||
|
|
||||||
container = containers.get(result);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BytecodeViewer.showMessage("You can only export as APK from a valid APK file. Make sure "
|
|
||||||
+ "Settings>Decode Resources is ticked on.\n\nTip: Try exporting as DEX, it doesn't rely on "
|
|
||||||
+ "decoded APK resources");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final FileContainer finalContainer = container;
|
|
||||||
|
|
||||||
Thread exportThread = new Thread(() ->
|
|
||||||
{
|
|
||||||
if (BytecodeViewer.viewer.compileOnSave.isSelected() && !BytecodeViewer.compile(false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
JFileChooser fc = new FileChooser(new File(Configuration.lastDirectory),
|
|
||||||
"Select APK Export",
|
|
||||||
"Android APK",
|
|
||||||
"apk");
|
|
||||||
|
|
||||||
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
|
|
||||||
if (returnVal == JFileChooser.APPROVE_OPTION)
|
|
||||||
{
|
|
||||||
Configuration.lastDirectory = fc.getSelectedFile().getAbsolutePath();
|
|
||||||
final File file = fc.getSelectedFile();
|
|
||||||
String output = file.getAbsolutePath();
|
|
||||||
|
|
||||||
//auto appened .apk
|
|
||||||
if (!output.endsWith(".apk"))
|
|
||||||
output = output + ".apk";
|
|
||||||
|
|
||||||
final File file2 = new File(output);
|
|
||||||
if (file2.exists())
|
|
||||||
{
|
|
||||||
MultipleChoiceDialogue dialogue = new MultipleChoiceDialogue("Bytecode Viewer - Overwrite File",
|
|
||||||
"Are you sure you wish to overwrite this existing file?",
|
|
||||||
new String[]{"Yes", "No"});
|
|
||||||
|
|
||||||
if (dialogue.promptChoice() == 0) {
|
|
||||||
file.delete();
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread saveThread = new Thread(() ->
|
|
||||||
{
|
|
||||||
BytecodeViewer.viewer.updateBusyStatus(true);
|
|
||||||
final String input = tempDirectory + fs + MiscUtils.getRandomizedName() + ".jar";
|
|
||||||
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), input);
|
|
||||||
|
|
||||||
Thread buildAPKThread = new Thread(() ->
|
|
||||||
{
|
|
||||||
APKTool.buildAPK(new File(input), file2, finalContainer);
|
|
||||||
BytecodeViewer.viewer.updateBusyStatus(false);
|
|
||||||
});
|
|
||||||
buildAPKThread.start();
|
|
||||||
});
|
|
||||||
saveThread.start();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
exportThread.start();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.resources.exporting;
|
||||||
|
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.exporting.impl.APKExport;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.exporting.impl.DexExport;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.exporting.impl.RunnableJarExporter;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.exporting.impl.ZipExport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
* @since 6/27/2021
|
||||||
|
*/
|
||||||
|
public enum Export
|
||||||
|
{
|
||||||
|
RUNNABLE_JAR(new RunnableJarExporter()),
|
||||||
|
ZIP(new ZipExport()),
|
||||||
|
DEX(new DexExport()),
|
||||||
|
APK(new APKExport())
|
||||||
|
;
|
||||||
|
|
||||||
|
private final Exporter exporter;
|
||||||
|
|
||||||
|
Export(Exporter exporter) {this.exporter = exporter;}
|
||||||
|
|
||||||
|
public Exporter getExporter()
|
||||||
|
{
|
||||||
|
return exporter;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.resources.exporting;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
* @since 6/27/2021
|
||||||
|
*/
|
||||||
|
public interface Exporter
|
||||||
|
{
|
||||||
|
void promptForExport();
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.resources.exporting.impl;
|
||||||
|
|
||||||
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.Configuration;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.FileChooser;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.MultipleChoiceDialogue;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.exporting.Exporter;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.APKTool;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.FileContainer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.JarUtils;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static the.bytecode.club.bytecodeviewer.Constants.fs;
|
||||||
|
import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
* @since 6/27/2021
|
||||||
|
*/
|
||||||
|
public class APKExport implements Exporter
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void promptForExport()
|
||||||
|
{
|
||||||
|
if (BytecodeViewer.getLoadedClasses().isEmpty())
|
||||||
|
{
|
||||||
|
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if theres only one file in the container don't bother asking
|
||||||
|
List<FileContainer> containers = BytecodeViewer.getFiles();
|
||||||
|
List<FileContainer> validContainers = new ArrayList<>();
|
||||||
|
List<String> validContainersNames = new ArrayList<>();
|
||||||
|
FileContainer container;
|
||||||
|
|
||||||
|
for (FileContainer fileContainer : containers)
|
||||||
|
{
|
||||||
|
if (fileContainer.APKToolContents != null && fileContainer.APKToolContents.exists())
|
||||||
|
{
|
||||||
|
validContainersNames.add(fileContainer.name);
|
||||||
|
validContainers.add(fileContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validContainers.isEmpty())
|
||||||
|
{
|
||||||
|
container = validContainers.get(0);
|
||||||
|
|
||||||
|
if (validContainers.size() >= 2)
|
||||||
|
{
|
||||||
|
JOptionPane pane = new JOptionPane("Which file would you like to export as an APK?");
|
||||||
|
Object[] options = validContainersNames.toArray(new String[0]);
|
||||||
|
|
||||||
|
pane.setOptions(options);
|
||||||
|
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Select APK");
|
||||||
|
dialog.setVisible(true);
|
||||||
|
Object obj = pane.getValue();
|
||||||
|
int result = -1;
|
||||||
|
for (int k = 0; k < options.length; k++)
|
||||||
|
if (options[k].equals(obj))
|
||||||
|
result = k;
|
||||||
|
|
||||||
|
container = containers.get(result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BytecodeViewer.showMessage("You can only export as APK from a valid APK file. Make sure "
|
||||||
|
+ "Settings>Decode Resources is ticked on.\n\nTip: Try exporting as DEX, it doesn't rely on "
|
||||||
|
+ "decoded APK resources");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final FileContainer finalContainer = container;
|
||||||
|
|
||||||
|
Thread exportThread = new Thread(() ->
|
||||||
|
{
|
||||||
|
if (BytecodeViewer.viewer.compileOnSave.isSelected() && !BytecodeViewer.compile(false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
JFileChooser fc = new FileChooser(new File(Configuration.lastDirectory),
|
||||||
|
"Select APK Export",
|
||||||
|
"Android APK",
|
||||||
|
"apk");
|
||||||
|
|
||||||
|
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
|
||||||
|
if (returnVal == JFileChooser.APPROVE_OPTION)
|
||||||
|
{
|
||||||
|
Configuration.lastDirectory = fc.getSelectedFile().getAbsolutePath();
|
||||||
|
final File file = fc.getSelectedFile();
|
||||||
|
String output = file.getAbsolutePath();
|
||||||
|
|
||||||
|
//auto appened .apk
|
||||||
|
if (!output.endsWith(".apk"))
|
||||||
|
output = output + ".apk";
|
||||||
|
|
||||||
|
final File file2 = new File(output);
|
||||||
|
if (file2.exists())
|
||||||
|
{
|
||||||
|
MultipleChoiceDialogue dialogue = new MultipleChoiceDialogue("Bytecode Viewer - Overwrite File",
|
||||||
|
"Are you sure you wish to overwrite this existing file?",
|
||||||
|
new String[]{"Yes", "No"});
|
||||||
|
|
||||||
|
if (dialogue.promptChoice() == 0) {
|
||||||
|
file.delete();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread saveThread = new Thread(() ->
|
||||||
|
{
|
||||||
|
BytecodeViewer.viewer.updateBusyStatus(true);
|
||||||
|
final String input = tempDirectory + fs + MiscUtils.getRandomizedName() + ".jar";
|
||||||
|
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), input);
|
||||||
|
|
||||||
|
Thread buildAPKThread = new Thread(() ->
|
||||||
|
{
|
||||||
|
APKTool.buildAPK(new File(input), file2, finalContainer);
|
||||||
|
BytecodeViewer.viewer.updateBusyStatus(false);
|
||||||
|
});
|
||||||
|
buildAPKThread.start();
|
||||||
|
});
|
||||||
|
saveThread.start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
exportThread.start();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.resources.exporting.impl;
|
||||||
|
|
||||||
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.Configuration;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.FileChooser;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.MultipleChoiceDialogue;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.exporting.Exporter;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.Dex2Jar;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.JarUtils;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.MiscUtils;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import static the.bytecode.club.bytecodeviewer.Constants.fs;
|
||||||
|
import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
* @since 6/27/2021
|
||||||
|
*/
|
||||||
|
public class DexExport implements Exporter
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void promptForExport()
|
||||||
|
{
|
||||||
|
if (BytecodeViewer.getLoadedClasses().isEmpty())
|
||||||
|
{
|
||||||
|
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread exportThread = new Thread(() ->
|
||||||
|
{
|
||||||
|
if (BytecodeViewer.viewer.compileOnSave.isSelected() && !BytecodeViewer.compile(false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
JFileChooser fc = new FileChooser(new File(Configuration.lastDirectory),
|
||||||
|
"Select DEX Export",
|
||||||
|
"Android DEX Files",
|
||||||
|
"dex");
|
||||||
|
|
||||||
|
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
|
||||||
|
if (returnVal == JFileChooser.APPROVE_OPTION)
|
||||||
|
{
|
||||||
|
Configuration.lastDirectory = fc.getSelectedFile().getAbsolutePath();
|
||||||
|
final File file = fc.getSelectedFile();
|
||||||
|
String output = file.getAbsolutePath();
|
||||||
|
|
||||||
|
//auto append .dex
|
||||||
|
if (!output.endsWith(".dex"))
|
||||||
|
output = output + ".dex";
|
||||||
|
|
||||||
|
File outputPath = new File(output);
|
||||||
|
if (outputPath.exists())
|
||||||
|
{
|
||||||
|
MultipleChoiceDialogue dialogue = new MultipleChoiceDialogue("Bytecode Viewer - Overwrite File",
|
||||||
|
"Are you sure you wish to overwrite this existing file?",
|
||||||
|
new String[]{"Yes", "No"});
|
||||||
|
|
||||||
|
if (dialogue.promptChoice() == 0) {
|
||||||
|
file.delete();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread saveAsJar = new Thread(() ->
|
||||||
|
{
|
||||||
|
BytecodeViewer.viewer.updateBusyStatus(true);
|
||||||
|
final String input = tempDirectory + fs + MiscUtils.getRandomizedName() + ".jar";
|
||||||
|
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), input);
|
||||||
|
|
||||||
|
Thread saveAsDex = new Thread(() ->
|
||||||
|
{
|
||||||
|
Dex2Jar.saveAsDex(new File(input), outputPath);
|
||||||
|
|
||||||
|
BytecodeViewer.viewer.updateBusyStatus(false);
|
||||||
|
});
|
||||||
|
saveAsDex.start();
|
||||||
|
});
|
||||||
|
saveAsJar.start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
exportThread.start();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.resources.exporting.impl;
|
||||||
|
|
||||||
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.Configuration;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.ExportJar;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.FileChooser;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.MultipleChoiceDialogue;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.exporting.Exporter;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
* @since 6/27/2021
|
||||||
|
*/
|
||||||
|
public class RunnableJarExporter implements Exporter
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void promptForExport()
|
||||||
|
{
|
||||||
|
if (BytecodeViewer.getLoadedClasses().isEmpty())
|
||||||
|
{
|
||||||
|
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread exportThread = new Thread(() ->
|
||||||
|
{
|
||||||
|
if (BytecodeViewer.viewer.compileOnSave.isSelected() && !BytecodeViewer.compile(false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
JFileChooser fc = new FileChooser(new File(Configuration.lastDirectory),
|
||||||
|
"Select Jar Export",
|
||||||
|
"Jar Archives",
|
||||||
|
"jar");
|
||||||
|
|
||||||
|
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
|
||||||
|
if (returnVal == JFileChooser.APPROVE_OPTION)
|
||||||
|
{
|
||||||
|
Configuration.lastDirectory = fc.getSelectedFile().getAbsolutePath();
|
||||||
|
File file = fc.getSelectedFile();
|
||||||
|
String path = file.getAbsolutePath();
|
||||||
|
|
||||||
|
//auto append .jar
|
||||||
|
if (!path.endsWith(".jar"))
|
||||||
|
path = path + ".jar";
|
||||||
|
|
||||||
|
if (new File(path).exists())
|
||||||
|
{
|
||||||
|
MultipleChoiceDialogue dialogue = new MultipleChoiceDialogue("Bytecode Viewer - Overwrite File",
|
||||||
|
"Are you sure you wish to overwrite this existing file?",
|
||||||
|
new String[]{"Yes", "No"});
|
||||||
|
|
||||||
|
if (dialogue.promptChoice() == 0) {
|
||||||
|
file.delete();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new ExportJar(path).setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
exportThread.start();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.resources.exporting.impl;
|
||||||
|
|
||||||
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.Configuration;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.FileChooser;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.components.MultipleChoiceDialogue;
|
||||||
|
import the.bytecode.club.bytecodeviewer.resources.exporting.Exporter;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.JarUtils;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
* @since 6/27/2021
|
||||||
|
*/
|
||||||
|
public class ZipExport implements Exporter
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void promptForExport()
|
||||||
|
{
|
||||||
|
if (BytecodeViewer.getLoadedClasses().isEmpty())
|
||||||
|
{
|
||||||
|
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread exportThread = new Thread(() ->
|
||||||
|
{
|
||||||
|
if (BytecodeViewer.viewer.compileOnSave.isSelected() && !BytecodeViewer.compile(false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
JFileChooser fc = new FileChooser(new File(Configuration.lastDirectory),
|
||||||
|
"Select Zip Export",
|
||||||
|
"Zip Archives",
|
||||||
|
"zip");
|
||||||
|
|
||||||
|
int returnVal = fc.showSaveDialog(BytecodeViewer.viewer);
|
||||||
|
if (returnVal == JFileChooser.APPROVE_OPTION)
|
||||||
|
{
|
||||||
|
Configuration.lastDirectory = fc.getSelectedFile().getAbsolutePath();
|
||||||
|
File file = fc.getSelectedFile();
|
||||||
|
|
||||||
|
//auto append .zip
|
||||||
|
if (!file.getAbsolutePath().endsWith(".zip"))
|
||||||
|
file = new File(file.getAbsolutePath() + ".zip");
|
||||||
|
|
||||||
|
if (file.exists())
|
||||||
|
{
|
||||||
|
MultipleChoiceDialogue dialogue = new MultipleChoiceDialogue("Bytecode Viewer - Overwrite File",
|
||||||
|
"Are you sure you wish to overwrite this existing file?",
|
||||||
|
new String[]{"Yes", "No"});
|
||||||
|
|
||||||
|
if (dialogue.promptChoice() == 0) {
|
||||||
|
file.delete();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final File file2 = file;
|
||||||
|
|
||||||
|
BytecodeViewer.viewer.updateBusyStatus(true);
|
||||||
|
Thread saveThread = new Thread(() ->
|
||||||
|
{
|
||||||
|
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), file2.getAbsolutePath());
|
||||||
|
BytecodeViewer.viewer.updateBusyStatus(false);
|
||||||
|
});
|
||||||
|
saveThread.start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
exportThread.start();
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import the.bytecode.club.bytecodeviewer.resources.importing.impl.*;
|
||||||
* @author Konloch
|
* @author Konloch
|
||||||
* @since 6/26/2021
|
* @since 6/26/2021
|
||||||
*/
|
*/
|
||||||
public enum ImportType
|
public enum Import
|
||||||
{
|
{
|
||||||
DIRECTORY(new DirectoryResourceImporter()),
|
DIRECTORY(new DirectoryResourceImporter()),
|
||||||
FILE(new FileResourceImporter()),
|
FILE(new FileResourceImporter()),
|
||||||
|
@ -18,7 +18,7 @@ public enum ImportType
|
||||||
|
|
||||||
private final Importer importer;
|
private final Importer importer;
|
||||||
|
|
||||||
ImportType(Importer importer) {this.importer = importer;}
|
Import(Importer importer) {this.importer = importer;}
|
||||||
|
|
||||||
public Importer getImporter()
|
public Importer getImporter()
|
||||||
{
|
{
|
|
@ -54,33 +54,33 @@ public class ImportResource implements Runnable
|
||||||
{
|
{
|
||||||
if (file.isDirectory())
|
if (file.isDirectory())
|
||||||
{
|
{
|
||||||
ImportType.DIRECTORY.getImporter().open(file);
|
Import.DIRECTORY.getImporter().open(file);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (fn.endsWith(".jar") || fn.endsWith(".zip") || fn.endsWith(".war"))
|
if (fn.endsWith(".jar") || fn.endsWith(".zip") || fn.endsWith(".war"))
|
||||||
{
|
{
|
||||||
if(!ImportType.ZIP.getImporter().open(file))
|
if(!Import.ZIP.getImporter().open(file))
|
||||||
update = false;
|
update = false;
|
||||||
}
|
}
|
||||||
else if (fn.endsWith(".class"))
|
else if (fn.endsWith(".class"))
|
||||||
{
|
{
|
||||||
if(!ImportType.CLASS.getImporter().open(file))
|
if(!Import.CLASS.getImporter().open(file))
|
||||||
update = false;
|
update = false;
|
||||||
}
|
}
|
||||||
else if (fn.endsWith(".apk"))
|
else if (fn.endsWith(".apk"))
|
||||||
{
|
{
|
||||||
ImportType.APK.getImporter().open(file);
|
Import.APK.getImporter().open(file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (fn.endsWith(".dex"))
|
else if (fn.endsWith(".dex"))
|
||||||
{
|
{
|
||||||
ImportType.DEX.getImporter().open(file);
|
Import.DEX.getImporter().open(file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImportType.FILE.getImporter().open(file);
|
Import.FILE.getImporter().open(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package the.bytecode.club.bytecodeviewer.resources.importing.impl;
|
package the.bytecode.club.bytecodeviewer.resources.importing.impl;
|
||||||
|
|
||||||
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
import the.bytecode.club.bytecodeviewer.resources.importing.ImportType;
|
import the.bytecode.club.bytecodeviewer.resources.importing.Import;
|
||||||
import the.bytecode.club.bytecodeviewer.resources.importing.Importer;
|
import the.bytecode.club.bytecodeviewer.resources.importing.Importer;
|
||||||
import the.bytecode.club.bytecodeviewer.util.FileContainer;
|
import the.bytecode.club.bytecodeviewer.util.FileContainer;
|
||||||
|
|
||||||
|
@ -58,15 +58,15 @@ public class DirectoryResourceImporter implements Importer
|
||||||
//attempt to import archives automatically
|
//attempt to import archives automatically
|
||||||
if (fileName.endsWith(".jar") || fileName.endsWith(".zip") || fileName.endsWith(".war"))
|
if (fileName.endsWith(".jar") || fileName.endsWith(".zip") || fileName.endsWith(".war"))
|
||||||
{
|
{
|
||||||
ImportType.ZIP.getImporter().open(child);
|
Import.ZIP.getImporter().open(child);
|
||||||
}
|
}
|
||||||
else if (fileName.endsWith(".apk"))
|
else if (fileName.endsWith(".apk"))
|
||||||
{
|
{
|
||||||
ImportType.APK.getImporter().open(child);
|
Import.APK.getImporter().open(child);
|
||||||
}
|
}
|
||||||
else if (fileName.endsWith(".dex"))
|
else if (fileName.endsWith(".dex"))
|
||||||
{
|
{
|
||||||
ImportType.DEX.getImporter().open(child);
|
Import.DEX.getImporter().open(child);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue