package us.deathmarine.luyten; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.io.Writer; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipException; import java.util.zip.ZipOutputStream; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; import com.strobel.assembler.metadata.ITypeLoader; import com.strobel.assembler.metadata.JarTypeLoader; import com.strobel.assembler.metadata.MetadataSystem; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.assembler.metadata.TypeReference; import com.strobel.core.StringUtilities; import com.strobel.decompiler.DecompilationOptions; import com.strobel.decompiler.DecompilerSettings; import com.strobel.decompiler.PlainTextOutput; import com.strobel.decompiler.languages.java.JavaFormattingOptions; /** * Performs Save and Save All */ public class FileSaver { private JProgressBar bar; private JLabel label; private boolean cancel; private boolean extracting; public FileSaver(JProgressBar bar, JLabel label) { this.bar = bar; this.label = label; final JPopupMenu menu = new JPopupMenu("Cancel"); final JMenuItem item = new JMenuItem("Cancel"); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { setCancel(true); } }); menu.add(item); this.label.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent ev) { if (SwingUtilities.isRightMouseButton(ev) && isExtracting()) menu.show(ev.getComponent(), ev.getX(), ev.getY()); } }); } public void saveText(final String text, final File file) { new Thread(new Runnable() { @Override public void run() { DecompilerSettings settings = cloneSettings(); boolean isUnicodeEnabled = settings.isUnicodeOutputEnabled(); long time = System.currentTimeMillis(); try (FileOutputStream fos = new FileOutputStream(file); OutputStreamWriter writer = isUnicodeEnabled ? new OutputStreamWriter(fos, "UTF-8") : new OutputStreamWriter(fos); BufferedWriter bw = new BufferedWriter(writer);) { label.setText("Extracting: " + file.getName()); bar.setVisible(true); bw.write(text); bw.flush(); label.setText("Completed: " + getTime(time)); } catch (Exception e1) { label.setText("Cannot save file: " + file.getName()); Luyten.showExceptionDialog("Unable to save file!\n", e1); } finally { setExtracting(false); bar.setVisible(false); } } }).start(); } public void saveAllDecompiled(final File inFile, final File outFile) { new Thread(new Runnable() { @Override public void run() { long time = System.currentTimeMillis(); try { bar.setVisible(true); setExtracting(true); label.setText("Extracting: " + outFile.getName()); System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); String inFileName = inFile.getName().toLowerCase(); if (inFileName.endsWith(".jar") || inFileName.endsWith(".zip")) { doSaveJarDecompiled(inFile, outFile); } else if (inFileName.endsWith(".class")) { doSaveClassDecompiled(inFile, outFile); } else { doSaveUnknownFile(inFile, outFile); } if (cancel) { label.setText("Cancelled"); outFile.delete(); setCancel(false); } else { label.setText("Completed: " + getTime(time)); } } catch (Exception e1) { label.setText("Cannot save file: " + outFile.getName()); Luyten.showExceptionDialog("Unable to save file!\n", e1); } finally { setExtracting(false); bar.setVisible(false); } } }).start(); } private void doSaveJarDecompiled(File inFile, File outFile) throws Exception { try (JarFile jfile = new JarFile(inFile); FileOutputStream dest = new FileOutputStream(outFile); BufferedOutputStream buffDest = new BufferedOutputStream(dest); ZipOutputStream out = new ZipOutputStream(buffDest);) { bar.setMinimum(0); bar.setMaximum(jfile.size()); byte data[] = new byte[1024]; DecompilerSettings settings = cloneSettings(); LuytenTypeLoader typeLoader = new LuytenTypeLoader(); MetadataSystem metadataSystem = new MetadataSystem(typeLoader); ITypeLoader jarLoader = new JarTypeLoader(jfile); typeLoader.getTypeLoaders().add(jarLoader); DecompilationOptions decompilationOptions = new DecompilationOptions(); decompilationOptions.setSettings(settings); decompilationOptions.setFullDecompilation(true); List<String> mass = null; JarEntryFilter jarEntryFilter = new JarEntryFilter(jfile); LuytenPreferences luytenPrefs = ConfigSaver.getLoadedInstance().getLuytenPreferences(); if (luytenPrefs.isFilterOutInnerClassEntries()) { mass = jarEntryFilter.getEntriesWithoutInnerClasses(); } else { mass = jarEntryFilter.getAllEntriesFromJar(); } Enumeration<JarEntry> ent = jfile.entries(); Set<String> history = new HashSet<String>(); int tick = 0; while (ent.hasMoreElements() && !cancel) { bar.setValue(++tick); JarEntry entry = ent.nextElement(); if (!mass.contains(entry.getName())) continue; label.setText("Extracting: " + entry.getName()); bar.setVisible(true); if (entry.getName().endsWith(".class")) { JarEntry etn = new JarEntry(entry.getName().replace(".class", ".java")); label.setText("Extracting: " + etn.getName()); System.out.println("[SaveAll]: " + etn.getName() + " -> " + outFile.getName()); if (history.add(etn.getName())) { out.putNextEntry(etn); try { boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); String internalName = StringUtilities.removeRight(entry.getName(), ".class"); TypeReference type = metadataSystem.lookupType(internalName); TypeDefinition resolvedType = null; if ((type == null) || ((resolvedType = type.resolve()) == null)) { throw new Exception("Unable to resolve type."); } Writer writer = isUnicodeEnabled ? new OutputStreamWriter(out, "UTF-8") : new OutputStreamWriter(out); PlainTextOutput plainTextOutput = new PlainTextOutput(writer); plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); writer.flush(); } catch (Exception e) { label.setText("Cannot decompile file: " + entry.getName()); Luyten.showExceptionDialog("Unable to Decompile file!\nSkipping file...", e); } finally { out.closeEntry(); } } } else { try { JarEntry etn = new JarEntry(entry.getName()); if (entry.getName().endsWith(".java")) etn = new JarEntry(entry.getName().replace(".java", ".src.java")); if (history.add(etn.getName())) { out.putNextEntry(etn); try { InputStream in = jfile.getInputStream(etn); if (in != null) { try { int count; while ((count = in.read(data, 0, 1024)) != -1) { out.write(data, 0, count); } } finally { in.close(); } } } finally { out.closeEntry(); } } } catch (ZipException ze) { if (!ze.getMessage().contains("duplicate")) { throw ze; } } } } } } private void doSaveClassDecompiled(File inFile, File outFile) throws Exception { DecompilerSettings settings = cloneSettings(); LuytenTypeLoader typeLoader = new LuytenTypeLoader(); MetadataSystem metadataSystem = new MetadataSystem(typeLoader); TypeReference type = metadataSystem.lookupType(inFile.getCanonicalPath()); DecompilationOptions decompilationOptions = new DecompilationOptions(); decompilationOptions.setSettings(settings); decompilationOptions.setFullDecompilation(true); boolean isUnicodeEnabled = decompilationOptions.getSettings().isUnicodeOutputEnabled(); TypeDefinition resolvedType = null; if (type == null || ((resolvedType = type.resolve()) == null)) { throw new Exception("Unable to resolve type."); } StringWriter stringwriter = new StringWriter(); PlainTextOutput plainTextOutput = new PlainTextOutput(stringwriter); plainTextOutput.setUnicodeOutputEnabled(isUnicodeEnabled); settings.getLanguage().decompileType(resolvedType, plainTextOutput, decompilationOptions); String decompiledSource = stringwriter.toString(); System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); try (FileOutputStream fos = new FileOutputStream(outFile); OutputStreamWriter writer = isUnicodeEnabled ? new OutputStreamWriter(fos, "UTF-8") : new OutputStreamWriter(fos); BufferedWriter bw = new BufferedWriter(writer);) { bw.write(decompiledSource); bw.flush(); } } private void doSaveUnknownFile(File inFile, File outFile) throws Exception { try (FileInputStream in = new FileInputStream(inFile); FileOutputStream out = new FileOutputStream(outFile);) { System.out.println("[SaveAll]: " + inFile.getName() + " -> " + outFile.getName()); byte data[] = new byte[1024]; int count; while ((count = in.read(data, 0, 1024)) != -1) { out.write(data, 0, count); } } } private DecompilerSettings cloneSettings() { DecompilerSettings settings = ConfigSaver.getLoadedInstance().getDecompilerSettings(); DecompilerSettings newSettings = new DecompilerSettings(); if (newSettings.getJavaFormattingOptions() == null) { newSettings.setJavaFormattingOptions(JavaFormattingOptions.createDefault()); } // synchronized: against main menu changes synchronized (settings) { newSettings.setExcludeNestedTypes(settings.getExcludeNestedTypes()); newSettings.setFlattenSwitchBlocks(settings.getFlattenSwitchBlocks()); newSettings.setForceExplicitImports(settings.getForceExplicitImports()); newSettings.setForceExplicitTypeArguments(settings.getForceExplicitTypeArguments()); newSettings.setOutputFileHeaderText(settings.getOutputFileHeaderText()); newSettings.setLanguage(settings.getLanguage()); newSettings.setShowSyntheticMembers(settings.getShowSyntheticMembers()); newSettings.setAlwaysGenerateExceptionVariableForCatchBlocks( settings.getAlwaysGenerateExceptionVariableForCatchBlocks()); newSettings.setOutputDirectory(settings.getOutputDirectory()); newSettings.setRetainRedundantCasts(settings.getRetainRedundantCasts()); newSettings.setIncludeErrorDiagnostics(settings.getIncludeErrorDiagnostics()); newSettings.setIncludeLineNumbersInBytecode(settings.getIncludeLineNumbersInBytecode()); newSettings.setRetainPointlessSwitches(settings.getRetainPointlessSwitches()); newSettings.setUnicodeOutputEnabled(settings.isUnicodeOutputEnabled()); newSettings.setMergeVariables(settings.getMergeVariables()); newSettings.setShowDebugLineNumbers(settings.getShowDebugLineNumbers()); } return newSettings; } public boolean isCancel() { return cancel; } public void setCancel(boolean cancel) { this.cancel = cancel; } public boolean isExtracting() { return extracting; } public void setExtracting(boolean extracting) { this.extracting = extracting; } public static String getTime(long time) { long lap = System.currentTimeMillis() - time; lap = lap / 1000; StringBuilder sb = new StringBuilder(); long hour = ((lap / 60) / 60); long min = ((lap - (hour * 60 * 60)) / 60); long sec = ((lap - (hour * 60 * 60) - (min * 60))); if (hour > 0) sb.append("Hour:").append(hour).append(" "); sb.append("Min(s): ").append(min).append(" Sec: ").append(sec); return sb.toString(); } }