diff --git a/jadx-cli/build.gradle b/jadx-cli/build.gradle
index ccb6863967c5e6ea632e76db2e647b873dcbb865..e77c09f9d81ac2d9254e3b9af112b24f4724e0c0 100644
--- a/jadx-cli/build.gradle
+++ b/jadx-cli/build.gradle
@@ -5,7 +5,7 @@ applicationName = 'jadx'
dependencies {
compile(project(':jadx-core'))
- compile 'com.beust:jcommander:1.47'
+ compile 'com.beust:jcommander:1.72'
compile 'ch.qos.logback:logback-classic:1.2.3'
}
diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java
index 7193906367e24174109591f8c242fea7dba83922..56e1b096c6a3b100411fd3d9bf7467cfaa354a6c 100644
--- a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java
+++ b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java
@@ -4,6 +4,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.JadxDecompiler;
+import jadx.core.utils.exceptions.JadxArgsValidateException;
public class JadxCLI {
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
@@ -22,7 +23,12 @@ public class JadxCLI {
static void processAndSave(JadxCLIArgs inputArgs) {
JadxDecompiler jadx = new JadxDecompiler(inputArgs.toJadxArgs());
- jadx.load();
+ try {
+ jadx.load();
+ } catch (JadxArgsValidateException e) {
+ LOG.error("Incorrect arguments: {}", e.getMessage());
+ System.exit(1);
+ }
jadx.save();
if (jadx.getErrorsCount() != 0) {
jadx.printErrorsReport();
diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
index 38278afba2e8f677eceacf115c93d1bf5ff6c067..09dbddf83f72a24dd3c7c3ae5a3ca66286bf8789 100644
--- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
+++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
@@ -10,7 +10,6 @@ import java.util.stream.Collectors;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
-import com.beust.jcommander.IStringConverter;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterDescription;
@@ -25,7 +24,7 @@ import jadx.core.utils.files.FileUtils;
public class JadxCLIArgs {
- @Parameter(description = " (.dex, .apk, .jar or .class)")
+ @Parameter(description = " (.apk, .dex, .jar or .class)")
protected List files = new ArrayList<>(1);
@Parameter(names = {"-d", "--output-dir"}, description = "output directory")
@@ -37,9 +36,6 @@ public class JadxCLIArgs {
@Parameter(names = {"-dr", "--output-dir-res"}, description = "output directory for resources")
protected String outDirRes;
- @Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
- protected int threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;
-
@Parameter(names = {"-r", "--no-res"}, description = "do not decode resources")
protected boolean skipResources = false;
@@ -49,15 +45,16 @@ public class JadxCLIArgs {
@Parameter(names = {"-e", "--export-gradle"}, description = "save as android gradle project")
protected boolean exportAsGradleProject = false;
+ @Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
+ protected int threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;
+
@Parameter(names = {"--show-bad-code"}, description = "show inconsistent code (incorrectly decompiled)")
protected boolean showInconsistentCode = false;
- @Parameter(names = {"--no-imports"}, converter = InvertedBooleanConverter.class,
- description = "disable use of imports, always write entire package name")
+ @Parameter(names = {"--no-imports"}, description = "disable use of imports, always write entire package name")
protected boolean useImports = true;
- @Parameter(names = "--no-replace-consts", converter = InvertedBooleanConverter.class,
- description = "don't replace constant value with matching constant field")
+ @Parameter(names = "--no-replace-consts", description = "don't replace constant value with matching constant field")
protected boolean replaceConsts = true;
@Parameter(names = {"--escape-unicode"}, description = "escape non latin characters in strings (with \\u)")
@@ -90,6 +87,9 @@ public class JadxCLIArgs {
@Parameter(names = {"-v", "--verbose"}, description = "verbose output")
protected boolean verbose = false;
+ @Parameter(names = {"--version"}, description = "print jadx version")
+ protected boolean printVersion = false;
+
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
protected boolean printHelp = false;
@@ -99,7 +99,7 @@ public class JadxCLIArgs {
private boolean parse(String[] args) {
try {
- new JCommander(this, args);
+ makeJCommander().parse(args);
return true;
} catch (ParameterException e) {
System.err.println("Arguments parse error: " + e.getMessage());
@@ -108,16 +108,24 @@ public class JadxCLIArgs {
}
}
+ private JCommander makeJCommander() {
+ return JCommander.newBuilder().addObject(this).build();
+ }
+
private boolean process() {
- if (isPrintHelp()) {
+ if (printHelp) {
printUsage();
return false;
}
+ if (printVersion) {
+ System.out.println(JadxDecompiler.getVersion());
+ return false;
+ }
try {
if (threadsCount <= 0) {
throw new JadxException("Threads count must be positive, got: " + threadsCount);
}
- if (isVerbose()) {
+ if (verbose) {
ch.qos.logback.classic.Logger rootLogger =
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
// remove INFO ThresholdFilter
@@ -135,7 +143,7 @@ public class JadxCLIArgs {
}
public void printUsage() {
- JCommander jc = new JCommander(this);
+ JCommander jc = makeJCommander();
// print usage in not sorted fields order (by default its sorted by description)
PrintStream out = System.out;
out.println();
@@ -162,13 +170,13 @@ public class JadxCLIArgs {
continue;
}
StringBuilder opt = new StringBuilder();
- opt.append(' ').append(p.getNames());
+ opt.append(" ").append(p.getNames());
addSpaces(opt, maxNamesLen - opt.length() + 2);
opt.append("- ").append(p.getDescription());
out.println(opt);
}
out.println("Example:");
- out.println(" jadx -d out classes.dex");
+ out.println(" jadx -d out classes.dex");
}
private static void addSpaces(StringBuilder str, int count) {
@@ -202,13 +210,6 @@ public class JadxCLIArgs {
return args;
}
- public static class InvertedBooleanConverter implements IStringConverter {
- @Override
- public Boolean convert(String value) {
- return "false".equals(value);
- }
- }
-
public List getFiles() {
return files;
}
@@ -225,10 +226,6 @@ public class JadxCLIArgs {
return outDirRes;
}
- public boolean isPrintHelp() {
- return printHelp;
- }
-
public boolean isSkipResources() {
return skipResources;
}
@@ -241,14 +238,6 @@ public class JadxCLIArgs {
return threadsCount;
}
- public boolean isCFGOutput() {
- return cfgOutput;
- }
-
- public boolean isRawCFGOutput() {
- return rawCfgOutput;
- }
-
public boolean isFallbackMode() {
return fallbackMode;
}
@@ -261,10 +250,6 @@ public class JadxCLIArgs {
return useImports;
}
- public boolean isVerbose() {
- return verbose;
- }
-
public boolean isDeobfuscationOn() {
return deobfuscationOn;
}
@@ -289,6 +274,18 @@ public class JadxCLIArgs {
return escapeUnicode;
}
+ public boolean isEscapeUnicode() {
+ return escapeUnicode;
+ }
+
+ public boolean isCfgOutput() {
+ return cfgOutput;
+ }
+
+ public boolean isRawCfgOutput() {
+ return rawCfgOutput;
+ }
+
public boolean isReplaceConsts() {
return replaceConsts;
}
diff --git a/jadx-cli/src/main/resources/logback.xml b/jadx-cli/src/main/resources/logback.xml
index 670c71320b322ba30a743ad2ba3fd555e6133532..6a1e61641cbd6e8d06309122b52659289ccee8c1 100644
--- a/jadx-cli/src/main/resources/logback.xml
+++ b/jadx-cli/src/main/resources/logback.xml
@@ -5,7 +5,7 @@
INFO
- %d{HH:mm:ss} %-5level - %msg%n
+ %-5level - %msg%n
diff --git a/jadx-cli/src/test/java/jadx/cli/JadxCLIArgsTest.java b/jadx-cli/src/test/java/jadx/cli/JadxCLIArgsTest.java
index 6704d05984b919b0e4146625c0fcf3016754101d..5afff66e4a618268a57b7f505235cb3d8b1fafee 100644
--- a/jadx-cli/src/test/java/jadx/cli/JadxCLIArgsTest.java
+++ b/jadx-cli/src/test/java/jadx/cli/JadxCLIArgsTest.java
@@ -1,22 +1,40 @@
package jadx.cli;
import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class JadxCLIArgsTest {
+ private static final Logger LOG = LoggerFactory.getLogger(JadxCLIArgsTest.class);
+
@Test
public void testInvertedBooleanOption() {
assertThat(parse("--no-replace-consts").isReplaceConsts(), is(false));
assertThat(parse("").isReplaceConsts(), is(true));
}
+ @Test
+ public void testEscapeUnicodeOption() {
+ assertThat(parse("--escape-unicode").isEscapeUnicode(), is(true));
+ assertThat(parse("").isEscapeUnicode(), is(false));
+ }
+
+ @Test
+ public void testSrcOption() {
+ assertThat(parse("--no-src").isSkipSources(), is(true));
+ assertThat(parse("-s").isSkipSources(), is(true));
+ assertThat(parse("").isSkipSources(), is(false));
+ }
+
private JadxCLIArgs parse(String... args) {
JadxCLIArgs jadxArgs = new JadxCLIArgs();
boolean res = jadxArgs.processArgs(args);
assertThat(res, is(true));
+ LOG.info("Jadx args: {}", jadxArgs.toJadxArgs());
return jadxArgs;
}
}
diff --git a/jadx-core/src/main/java/jadx/api/JadxArgs.java b/jadx-core/src/main/java/jadx/api/JadxArgs.java
index cda8778098a340236105254c6e4b0eda32eba3eb..bd6f12548ece31b0391f0e98e16d6a6fb1b1e312 100644
--- a/jadx-core/src/main/java/jadx/api/JadxArgs.java
+++ b/jadx-core/src/main/java/jadx/api/JadxArgs.java
@@ -211,4 +211,31 @@ public class JadxArgs {
public void setExportAsGradleProject(boolean exportAsGradleProject) {
this.exportAsGradleProject = exportAsGradleProject;
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("JadxArgs{");
+ sb.append("inputFiles=").append(inputFiles);
+ sb.append(", outDir=").append(outDir);
+ sb.append(", outDirSrc=").append(outDirSrc);
+ sb.append(", outDirRes=").append(outDirRes);
+ sb.append(", threadsCount=").append(threadsCount);
+ sb.append(", cfgOutput=").append(cfgOutput);
+ sb.append(", rawCFGOutput=").append(rawCFGOutput);
+ sb.append(", fallbackMode=").append(fallbackMode);
+ sb.append(", showInconsistentCode=").append(showInconsistentCode);
+ sb.append(", useImports=").append(useImports);
+ sb.append(", isSkipResources=").append(isSkipResources);
+ sb.append(", isSkipSources=").append(isSkipSources);
+ sb.append(", isDeobfuscationOn=").append(isDeobfuscationOn);
+ sb.append(", isDeobfuscationForceSave=").append(isDeobfuscationForceSave);
+ sb.append(", useSourceNameAsClassAlias=").append(useSourceNameAsClassAlias);
+ sb.append(", deobfuscationMinLength=").append(deobfuscationMinLength);
+ sb.append(", deobfuscationMaxLength=").append(deobfuscationMaxLength);
+ sb.append(", escapeUnicode=").append(escapeUnicode);
+ sb.append(", replaceConsts=").append(replaceConsts);
+ sb.append(", exportAsGradleProject=").append(exportAsGradleProject);
+ sb.append('}');
+ return sb.toString();
+ }
}
diff --git a/jadx-core/src/main/java/jadx/api/JadxArgsValidator.java b/jadx-core/src/main/java/jadx/api/JadxArgsValidator.java
index 761aa09d127d86998eff74909308db9a5fe81eb5..4bb856d298d2864688660abc7bb3bfaf2f5da911 100644
--- a/jadx-core/src/main/java/jadx/api/JadxArgsValidator.java
+++ b/jadx-core/src/main/java/jadx/api/JadxArgsValidator.java
@@ -1,25 +1,44 @@
package jadx.api;
import java.io.File;
+import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import jadx.core.utils.exceptions.JadxRuntimeException;
+import jadx.core.utils.exceptions.JadxArgsValidateException;
public class JadxArgsValidator {
private static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidator.class);
public static void validate(JadxArgs args) {
- if (args.getInputFiles().isEmpty()) {
- throw new JadxRuntimeException("Please specify input file");
+ checkInputFiles(args);
+ validateOutDirs(args);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Effective jadx args: {}", args);
+ }
+ }
+
+ private static void checkInputFiles(JadxArgs args) {
+ List inputFiles = args.getInputFiles();
+ if (inputFiles.isEmpty()) {
+ throw new JadxArgsValidateException("Please specify input file");
+ }
+ if (inputFiles.size() > 1) {
+ for (File inputFile : inputFiles) {
+ String fileName = inputFile.getName();
+ if (fileName.startsWith("--")) {
+ throw new JadxArgsValidateException("Unknown argument: " + fileName);
+ }
+ }
+ throw new JadxArgsValidateException("Only one input file supported");
}
- for (File file : args.getInputFiles()) {
+ for (File file : inputFiles) {
checkFile(file);
}
- validateOutDirs(args);
}
private static void validateOutDirs(JadxArgs args) {
@@ -71,16 +90,16 @@ public class JadxArgsValidator {
private static void checkFile(File file) {
if (!file.exists()) {
- throw new JadxRuntimeException("File not found " + file.getAbsolutePath());
+ throw new JadxArgsValidateException("File not found " + file.getAbsolutePath());
}
if (file.isDirectory()) {
- throw new JadxRuntimeException("Expected file but found directory instead: " + file.getAbsolutePath());
+ throw new JadxArgsValidateException("Expected file but found directory instead: " + file.getAbsolutePath());
}
}
private static void checkDir(File dir) {
if (dir != null && dir.exists() && !dir.isDirectory()) {
- throw new JadxRuntimeException("Output directory exists as file " + dir);
+ throw new JadxArgsValidateException("Output directory exists as file " + dir);
}
}
diff --git a/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxArgsValidateException.java b/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxArgsValidateException.java
new file mode 100644
index 0000000000000000000000000000000000000000..391d3927cd3d52bc3992f8005e39dcf29deb7923
--- /dev/null
+++ b/jadx-core/src/main/java/jadx/core/utils/exceptions/JadxArgsValidateException.java
@@ -0,0 +1,14 @@
+package jadx.core.utils.exceptions;
+
+public class JadxArgsValidateException extends RuntimeException {
+
+ private static final long serialVersionUID = -7457621776087311909L;
+
+ public JadxArgsValidateException(String message) {
+ super(message);
+ }
+
+ public JadxArgsValidateException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java
index 5c28e56f206688f5f454c80d104d446daf1ee34a..bebb12a105ba3a154094a8a1b13c627851b7350a 100644
--- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java
+++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java
@@ -270,7 +270,7 @@ public class JadxSettingsWindow extends JDialog {
});
JCheckBox cfg = new JCheckBox();
- cfg.setSelected(settings.isCFGOutput());
+ cfg.setSelected(settings.isCfgOutput());
cfg.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setCfgOutput(e.getStateChange() == ItemEvent.SELECTED);
@@ -279,7 +279,7 @@ public class JadxSettingsWindow extends JDialog {
});
JCheckBox rawCfg = new JCheckBox();
- rawCfg.setSelected(settings.isRawCFGOutput());
+ rawCfg.setSelected(settings.isRawCfgOutput());
rawCfg.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setRawCfgOutput(e.getStateChange() == ItemEvent.SELECTED);