diff --git a/README.md b/README.md
index 62a59db33..e408e9d88 100644
--- a/README.md
+++ b/README.md
@@ -42,20 +42,22 @@ Run **jadx** on itself:
```
jadx[-gui] [options] (.dex, .apk, .jar or .class)
options:
- -d, --output-dir - output directory
- -j, --threads-count - processing threads count
- -f, --fallback - make simple dump (using goto instead of 'if', 'for', etc)
- -r, --no-res - do not decode resources
- -s, --no-src - do not decompile source code
- --show-bad-code - show inconsistent code (incorrectly decompiled)
- --cfg - save methods control flow graph to dot file
- --raw-cfg - save methods control flow graph (use raw instructions)
- -v, --verbose - verbose output
- --deobf - activate deobfuscation
- --deobf-min - min length of name
- --deobf-max - max length of name
- --deobf-rewrite-cfg - force to save deobfuscation map
- -h, --help - print this help
+ -d, --output-dir - output directory
+ -j, --threads-count - processing threads count
+ -f, --fallback - make simple dump (using goto instead of 'if', 'for', etc)
+ -r, --no-res - do not decode resources
+ -s, --no-src - do not decompile source code
+ --show-bad-code - show inconsistent code (incorrectly decompiled)
+ --cfg - save methods control flow graph to dot file
+ --raw-cfg - save methods control flow graph (use raw instructions)
+ -v, --verbose - verbose output
+ --deobf - activate deobfuscation
+ --deobf-min - min length of name
+ --deobf-max - max length of name
+ --deobf-rewrite-cfg - force to save deobfuscation map
+ --deobf-use-sourcename - use source file name as class name alias
+ --escape-unicode - escape non latin characters in strings (with \u)
+ -h, --help - print this help
Example:
jadx -d out classes.dex
```
diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
index bf88d1ce3..b1add245c 100644
--- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
+++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
@@ -1,5 +1,7 @@
package jadx.cli;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
import jadx.api.IJadxArgs;
import jadx.api.JadxDecompiler;
import jadx.core.utils.exceptions.JadxException;
@@ -67,6 +69,9 @@ public class JadxCLIArgs implements IJadxArgs {
@Parameter(names = {"--deobf-use-sourcename"}, description = "use source file name as class name alias")
protected boolean deobfuscationUseSourceNameAsAlias = false;
+ @Parameter(names = {"--escape-unicode"}, description = "escape non latin characters in strings (with \\u)")
+ protected boolean escapeUnicode = false;
+
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
protected boolean printHelp = false;
@@ -117,7 +122,10 @@ public class JadxCLIArgs implements IJadxArgs {
ch.qos.logback.classic.Logger rootLogger =
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
// remove INFO ThresholdFilter
- rootLogger.getAppender("STDOUT").clearAllFilters();
+ Appender appender = rootLogger.getAppender("STDOUT");
+ if (appender != null) {
+ appender.clearAllFilters();
+ }
}
} catch (JadxException e) {
System.err.println("ERROR: " + e.getMessage());
@@ -251,4 +259,9 @@ public class JadxCLIArgs implements IJadxArgs {
public boolean useSourceNameAsClassAlias() {
return deobfuscationUseSourceNameAsAlias;
}
+
+ @Override
+ public boolean escapeUnicode() {
+ return escapeUnicode;
+ }
}
diff --git a/jadx-core/src/main/java/jadx/api/IJadxArgs.java b/jadx-core/src/main/java/jadx/api/IJadxArgs.java
index a0fbc0f72..2c5034d63 100644
--- a/jadx-core/src/main/java/jadx/api/IJadxArgs.java
+++ b/jadx-core/src/main/java/jadx/api/IJadxArgs.java
@@ -30,4 +30,6 @@ public interface IJadxArgs {
boolean isDeobfuscationForceSave();
boolean useSourceNameAsClassAlias();
+
+ boolean escapeUnicode();
}
diff --git a/jadx-core/src/main/java/jadx/api/JadxArgs.java b/jadx-core/src/main/java/jadx/api/JadxArgs.java
index b09004fca..7d789f1ef 100644
--- a/jadx-core/src/main/java/jadx/api/JadxArgs.java
+++ b/jadx-core/src/main/java/jadx/api/JadxArgs.java
@@ -24,6 +24,8 @@ public class JadxArgs implements IJadxArgs {
private int deobfuscationMinLength = 0;
private int deobfuscationMaxLength = Integer.MAX_VALUE;
+ private boolean escapeUnicode = false;
+
@Override
public File getOutDir() {
return outDir;
@@ -149,4 +151,13 @@ public class JadxArgs implements IJadxArgs {
public void setDeobfuscationMaxLength(int deobfuscationMaxLength) {
this.deobfuscationMaxLength = deobfuscationMaxLength;
}
+
+ @Override
+ public boolean escapeUnicode() {
+ return escapeUnicode;
+ }
+
+ public void setEscapeUnicode(boolean escapeUnicode) {
+ this.escapeUnicode = escapeUnicode;
+ }
}
diff --git a/jadx-core/src/main/java/jadx/core/codegen/AnnotationGen.java b/jadx-core/src/main/java/jadx/core/codegen/AnnotationGen.java
index 083e4511a..4d0180d18 100644
--- a/jadx-core/src/main/java/jadx/core/codegen/AnnotationGen.java
+++ b/jadx-core/src/main/java/jadx/core/codegen/AnnotationGen.java
@@ -130,11 +130,11 @@ public class AnnotationGen {
return;
}
if (val instanceof String) {
- code.add(StringUtils.unescapeString((String) val));
+ code.add(getStringUtils().unescapeString((String) val));
} else if (val instanceof Integer) {
code.add(TypeGen.formatInteger((Integer) val));
} else if (val instanceof Character) {
- code.add(StringUtils.unescapeChar((Character) val));
+ code.add(getStringUtils().unescapeChar((Character) val));
} else if (val instanceof Boolean) {
code.add(Boolean.TRUE.equals(val) ? "true" : "false");
} else if (val instanceof Float) {
@@ -172,4 +172,8 @@ public class AnnotationGen {
throw new JadxRuntimeException("Can't decode value: " + val + " (" + val.getClass() + ")");
}
}
+
+ private StringUtils getStringUtils() {
+ return cls.dex().root().getStringUtils();
+ }
}
diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
index e807d6551..ac8718faf 100644
--- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
+++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java
@@ -348,7 +348,7 @@ public class ClassGen {
if (fv != null) {
code.add(" = ");
if (fv.getValue() == null) {
- code.add(TypeGen.literalToString(0, f.getType()));
+ code.add(TypeGen.literalToString(0, f.getType(), cls));
} else {
if (fv.getValueType() == InitType.CONST) {
annotationGen.encodeValue(code, fv.getValue());
diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
index db6d52cd8..bbd972f41 100644
--- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
+++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java
@@ -38,7 +38,6 @@ import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.RegionUtils;
-import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException;
@@ -130,8 +129,8 @@ public class InsnGen {
code.add(mgen.getNameGen().assignArg(arg));
}
- private static String lit(LiteralArg arg) {
- return TypeGen.literalToString(arg.getLiteral(), arg.getType());
+ private String lit(LiteralArg arg) {
+ return TypeGen.literalToString(arg.getLiteral(), arg.getType(), mth);
}
private void instanceField(CodeWriter code, FieldInfo field, InsnArg arg) throws CodegenException {
@@ -236,7 +235,7 @@ public class InsnGen {
switch (insn.getType()) {
case CONST_STR:
String str = ((ConstStringNode) insn).getString();
- code.add(StringUtils.unescapeString(str));
+ code.add(mth.dex().root().getStringUtils().unescapeString(str));
break;
case CONST_CLASS:
diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
index 5a5d2b1dc..2efa5ba2e 100644
--- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
+++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
@@ -255,7 +255,7 @@ public class RegionGen extends InsnGen {
}
}
} else if (k instanceof Integer) {
- code.add(TypeGen.literalToString((Integer) k, arg.getType()));
+ code.add(TypeGen.literalToString((Integer) k, arg.getType(), mth));
} else {
throw new JadxRuntimeException("Unexpected key in switch: " + (k != null ? k.getClass() : null));
}
diff --git a/jadx-core/src/main/java/jadx/core/codegen/TypeGen.java b/jadx-core/src/main/java/jadx/core/codegen/TypeGen.java
index c54ca0205..7ed158b57 100644
--- a/jadx-core/src/main/java/jadx/core/codegen/TypeGen.java
+++ b/jadx-core/src/main/java/jadx/core/codegen/TypeGen.java
@@ -1,7 +1,9 @@
package jadx.core.codegen;
+import jadx.api.JadxArgs;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.PrimitiveType;
+import jadx.core.dex.nodes.IDexNode;
import jadx.core.utils.StringUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
@@ -31,7 +33,16 @@ public class TypeGen {
*
* @throws JadxRuntimeException for incorrect type or literal value
*/
+ public static String literalToString(long lit, ArgType type, IDexNode dexNode) {
+ return literalToString(lit, type, dexNode.root().getStringUtils());
+ }
+
+ @Deprecated
public static String literalToString(long lit, ArgType type) {
+ return literalToString(lit, type, new StringUtils(new JadxArgs()));
+ }
+
+ private static String literalToString(long lit, ArgType type, StringUtils stringUtils) {
if (type == null || !type.isTypeKnown()) {
String n = Long.toString(lit);
if (Math.abs(lit) > 100) {
@@ -46,7 +57,7 @@ public class TypeGen {
case BOOLEAN:
return lit == 0 ? "false" : "true";
case CHAR:
- return StringUtils.unescapeChar((char) lit);
+ return stringUtils.unescapeChar((char) lit);
case BYTE:
return formatByte((byte) lit);
case SHORT:
diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
index a53a55f26..a5eb176fe 100644
--- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
+++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java
@@ -43,7 +43,7 @@ import com.android.dex.ClassData.Method;
import com.android.dex.ClassDef;
import com.android.dx.rop.code.AccessFlags;
-public class ClassNode extends LineAttrNode implements ILoadable {
+public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);
private final DexNode dex;
@@ -472,10 +472,16 @@ public class ClassNode extends LineAttrNode implements ILoadable {
return accessFlags;
}
+ @Override
public DexNode dex() {
return dex;
}
+ @Override
+ public RootNode root() {
+ return dex.root();
+ }
+
public String getRawName() {
return clsInfo.getRawName();
}
diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java
index 384d786e8..a63941170 100644
--- a/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java
+++ b/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java
@@ -28,7 +28,7 @@ import com.android.dex.MethodId;
import com.android.dex.ProtoId;
import com.android.dex.TypeList;
-public class DexNode {
+public class DexNode implements IDexNode {
public static final int NO_INDEX = -1;
@@ -210,10 +210,16 @@ public class DexNode {
return dexBuf.open(offset);
}
+ @Override
public RootNode root() {
return root;
}
+ @Override
+ public DexNode dex() {
+ return this;
+ }
+
@Override
public String toString() {
return "DEX";
diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/IDexNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/IDexNode.java
new file mode 100644
index 000000000..4d6bb337f
--- /dev/null
+++ b/jadx-core/src/main/java/jadx/core/dex/nodes/IDexNode.java
@@ -0,0 +1,9 @@
+package jadx.core.dex.nodes;
+
+public interface IDexNode {
+
+ DexNode dex();
+
+ RootNode root();
+}
+
diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java
index e0fd69017..09792e2d5 100644
--- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java
+++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java
@@ -44,7 +44,7 @@ import com.android.dex.Code;
import com.android.dex.Code.CatchHandler;
import com.android.dex.Code.Try;
-public class MethodNode extends LineAttrNode implements ILoadable {
+public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
private static final Logger LOG = LoggerFactory.getLogger(MethodNode.class);
private final MethodInfo mthInfo;
@@ -594,10 +594,16 @@ public class MethodNode extends LineAttrNode implements ILoadable {
this.region = region;
}
+ @Override
public DexNode dex() {
return parentClass.dex();
}
+ @Override
+ public RootNode root() {
+ return dex().root();
+ }
+
public MethodInfo getMethodInfo() {
return mthInfo;
}
diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java
index 8bff12bb0..79118bed8 100644
--- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java
+++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java
@@ -7,6 +7,7 @@ import jadx.api.ResourcesLoader;
import jadx.core.clsp.ClspGraph;
import jadx.core.dex.info.ClassInfo;
import jadx.core.utils.ErrorsCounter;
+import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.files.DexFile;
@@ -31,6 +32,7 @@ public class RootNode {
private final ErrorsCounter errorsCounter = new ErrorsCounter();
private final IJadxArgs args;
+ private final StringUtils stringUtils;
private List dexNodes;
private Map resourcesNames = new HashMap();
@@ -41,6 +43,7 @@ public class RootNode {
public RootNode(IJadxArgs args) {
this.args = args;
+ this.stringUtils = new StringUtils(args);
}
public void load(List inputFiles) throws DecodeException {
@@ -194,4 +197,8 @@ public class RootNode {
public IJadxArgs getArgs() {
return args;
}
+
+ public StringUtils getStringUtils() {
+ return stringUtils;
+ }
}
diff --git a/jadx-core/src/main/java/jadx/core/utils/StringUtils.java b/jadx-core/src/main/java/jadx/core/utils/StringUtils.java
index 7ddaffde9..153fcb1e7 100644
--- a/jadx-core/src/main/java/jadx/core/utils/StringUtils.java
+++ b/jadx-core/src/main/java/jadx/core/utils/StringUtils.java
@@ -1,14 +1,21 @@
package jadx.core.utils;
+import jadx.api.IJadxArgs;
+
public class StringUtils {
- private StringUtils() {
+ private final boolean escapeUnicode;
+
+ public StringUtils(IJadxArgs args) {
+ this.escapeUnicode = args.escapeUnicode();
}
- public static String unescapeString(String str) {
+ public String unescapeString(String str) {
int len = str.length();
+ if (len == 0) {
+ return "\"\"";
+ }
StringBuilder res = new StringBuilder();
-
for (int i = 0; i < len; i++) {
int c = str.charAt(i) & 0xFFFF;
processChar(c, res);
@@ -16,7 +23,7 @@ public class StringUtils {
return '"' + res.toString() + '"';
}
- public static String unescapeChar(char ch) {
+ public String unescapeChar(char ch) {
if (ch == '\'') {
return "'\\\''";
}
@@ -25,7 +32,7 @@ public class StringUtils {
return '\'' + res.toString() + '\'';
}
- private static void processChar(int c, StringBuilder res) {
+ private void processChar(int c, StringBuilder res) {
switch (c) {
case '\n': res.append("\\n"); break;
case '\r': res.append("\\r"); break;
@@ -37,10 +44,10 @@ public class StringUtils {
case '\\': res.append("\\\\"); break;
default:
- if (32 <= c && c <= 126) {
- res.append((char) c);
- } else {
+ if (c < 32 || c >= 127 && escapeUnicode) {
res.append("\\u").append(String.format("%04x", c));
+ } else {
+ res.append((char) c);
}
break;
}
diff --git a/jadx-core/src/test/groovy/jadx/tests/TestStringUtils.groovy b/jadx-core/src/test/groovy/jadx/tests/TestStringUtils.groovy
index 8977ad6c0..10a975f6f 100644
--- a/jadx-core/src/test/groovy/jadx/tests/TestStringUtils.groovy
+++ b/jadx-core/src/test/groovy/jadx/tests/TestStringUtils.groovy
@@ -1,13 +1,17 @@
package jadx.tests
+import jadx.api.JadxArgs
import jadx.core.utils.StringUtils
import spock.lang.Specification
class TestStringUtils extends Specification {
def "unescape string"() {
+ def args = new JadxArgs()
+ args.setEscapeUnicode(true)
+ def stringUtils = new StringUtils(args)
expect:
- StringUtils.unescapeString(input) == "\"$expected\""
+ stringUtils.unescapeString(input) == "\"$expected\""
where:
input | expected
@@ -26,12 +30,14 @@ class TestStringUtils extends Specification {
def "unescape char"() {
expect:
- StringUtils.unescapeChar(input as char) == "'$expected'"
+ new StringUtils(new JadxArgs()).unescapeChar(input as char) == "'$expected'"
where:
input | expected
'a' | "a"
+ ' ' | " "
'\n' | "\\n"
'\'' | "\\\'"
+ '\0' | "\\u0000"
}
}
diff --git a/jadx-gui/build.gradle b/jadx-gui/build.gradle
index 0db5c19f7..4ef6e1dbc 100644
--- a/jadx-gui/build.gradle
+++ b/jadx-gui/build.gradle
@@ -5,7 +5,7 @@ mainClassName = 'jadx.gui.JadxGUI'
dependencies {
compile(project(":jadx-core"))
compile(project(":jadx-cli"))
- compile 'com.fifesoft:rsyntaxtextarea:2.5.7'
+ compile 'com.fifesoft:rsyntaxtextarea:2.5.8'
compile 'com.google.code.gson:gson:2.3.1'
compile files('libs/jfontchooser-1.0.5.jar')
compile 'com.googlecode.concurrent-trees:concurrent-trees:2.4.0'
diff --git a/jadx-gui/src/main/java/jadx/gui/JadxGUI.java b/jadx-gui/src/main/java/jadx/gui/JadxGUI.java
index 0a1c8fe84..71cce9abe 100644
--- a/jadx-gui/src/main/java/jadx/gui/JadxGUI.java
+++ b/jadx-gui/src/main/java/jadx/gui/JadxGUI.java
@@ -30,7 +30,7 @@ public class JadxGUI {
}
});
} catch (Throwable e) {
- LOG.error("Error: {}", e.getMessage());
+ LOG.error("Error: {}", e.getMessage(), e);
System.exit(1);
}
}
diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java
index e445db64b..d7290ac55 100644
--- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java
+++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java
@@ -165,6 +165,10 @@ public class JadxSettings extends JadxCLIArgs {
this.deobfuscationUseSourceNameAsAlias = useSourceNameAsAlias;
}
+ public void setEscapeUnicode(boolean escapeUnicode) {
+ this.escapeUnicode = escapeUnicode;
+ }
+
public boolean isUseFastSearch() {
return false;
// return useFastSearch;
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 2fef752e1..db4712b0c 100644
--- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java
+++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java
@@ -67,6 +67,8 @@ public class JadxSettingsWindow extends JDialog {
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.add(makeDeobfuscationGroup());
+ panel.add(makeDecompilationGroup());
+ panel.add(makeEditorGroup());
panel.add(makeOtherGroup());
JButton saveBtn = new JButton(NLS.str("preferences.save"));
@@ -179,15 +181,29 @@ public class JadxSettingsWindow extends JDialog {
return deobfGroup;
}
- private SettingsGroup makeOtherGroup() {
- JCheckBox update = new JCheckBox();
- update.setSelected(settings.isCheckForUpdates());
- update.addItemListener(new ItemListener() {
- public void itemStateChanged(ItemEvent e) {
- settings.setCheckForUpdates(e.getStateChange() == ItemEvent.SELECTED);
+ private SettingsGroup makeEditorGroup() {
+ JButton fontBtn = new JButton(NLS.str("preferences.select_font"));
+ fontBtn.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ JFontChooser fontChooser = new JFontChooser();
+ fontChooser.setSelectedFont(settings.getFont());
+ int result = fontChooser.showDialog(JadxSettingsWindow.this);
+ if (result == JFontChooser.OK_OPTION) {
+ Font font = fontChooser.getSelectedFont();
+ LOG.info("Selected Font : {}", font);
+ settings.setFont(font);
+ mainWindow.updateFont(font);
+ }
}
});
+ SettingsGroup other = new SettingsGroup(NLS.str("preferences.editor"));
+ other.addRow(NLS.str("preferences.font"), fontBtn);
+ return other;
+ }
+
+ private SettingsGroup makeDecompilationGroup() {
JCheckBox fallback = new JCheckBox();
fallback.setSelected(settings.isFallbackMode());
fallback.addItemListener(new ItemListener() {
@@ -224,6 +240,42 @@ public class JadxSettingsWindow extends JDialog {
}
});
+ JCheckBox autoStartJobs = new JCheckBox();
+ autoStartJobs.setSelected(settings.isAutoStartJobs());
+ autoStartJobs.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED);
+ }
+ });
+
+ JCheckBox escapeUnicode = new JCheckBox();
+ escapeUnicode.setSelected(settings.escapeUnicode());
+ escapeUnicode.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ settings.setEscapeUnicode(e.getStateChange() == ItemEvent.SELECTED);
+ needReload();
+ }
+ });
+
+ SettingsGroup other = new SettingsGroup(NLS.str("preferences.decompile"));
+ other.addRow(NLS.str("preferences.threads"), threadsCount);
+ other.addRow(NLS.str("preferences.start_jobs"), autoStartJobs);
+ other.addRow(NLS.str("preferences.showInconsistentCode"), showInconsistentCode);
+ other.addRow(NLS.str("preferences.escapeUnicode"), escapeUnicode);
+ other.addRow(NLS.str("preferences.fallback"), fallback);
+ other.addRow(NLS.str("preferences.skipResourcesDecode"), resourceDecode);
+ return other;
+ }
+
+ private SettingsGroup makeOtherGroup() {
+ JCheckBox update = new JCheckBox();
+ update.setSelected(settings.isCheckForUpdates());
+ update.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ settings.setCheckForUpdates(e.getStateChange() == ItemEvent.SELECTED);
+ }
+ });
+
JCheckBox cfg = new JCheckBox();
cfg.setSelected(settings.isCFGOutput());
cfg.addItemListener(new ItemListener() {
@@ -242,30 +294,6 @@ public class JadxSettingsWindow extends JDialog {
}
});
- JButton fontBtn = new JButton(NLS.str("preferences.select_font"));
- fontBtn.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseClicked(MouseEvent e) {
- JFontChooser fontChooser = new JFontChooser();
- fontChooser.setSelectedFont(settings.getFont());
- int result = fontChooser.showDialog(JadxSettingsWindow.this);
- if (result == JFontChooser.OK_OPTION) {
- Font font = fontChooser.getSelectedFont();
- LOG.info("Selected Font : {}", font);
- settings.setFont(font);
- mainWindow.updateFont(font);
- }
- }
- });
-
- JCheckBox autoStartJobs = new JCheckBox();
- autoStartJobs.setSelected(settings.isAutoStartJobs());
- autoStartJobs.addItemListener(new ItemListener() {
- public void itemStateChanged(ItemEvent e) {
- settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED);
- }
- });
-
JCheckBox fastSearch = new JCheckBox();
fastSearch.setEnabled(false);
fastSearch.setSelected(settings.isUseFastSearch());
@@ -277,15 +305,9 @@ public class JadxSettingsWindow extends JDialog {
SettingsGroup other = new SettingsGroup(NLS.str("preferences.other"));
other.addRow(NLS.str("preferences.check_for_updates"), update);
- other.addRow(NLS.str("preferences.threads"), threadsCount);
- other.addRow(NLS.str("preferences.fallback"), fallback);
- other.addRow(NLS.str("preferences.showInconsistentCode"), showInconsistentCode);
- other.addRow(NLS.str("preferences.skipResourcesDecode"), resourceDecode);
other.addRow(NLS.str("preferences.cfg"), cfg);
other.addRow(NLS.str("preferences.raw_cfg"), rawCfg);
- other.addRow(NLS.str("preferences.font"), fontBtn);
other.addRow(NLS.str("preferences.fast_search"), fastSearch);
- other.addRow(NLS.str("preferences.start_jobs"), autoStartJobs);
return other;
}
diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties
index dee8c19b9..ecaebbc4d 100644
--- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties
+++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties
@@ -52,10 +52,13 @@ usage_dialog.label=Usage for:
preferences.title=Preferences
preferences.deobfuscation=Deobfuscation
+preferences.editor=Editor
+preferences.decompile=Decompilation
preferences.other=Other
preferences.check_for_updates=Check for updates on startup
-preferences.fallback=Fallback (simple dump)
+preferences.fallback=Fallback mode (simple dump)
preferences.showInconsistentCode=Show inconsistent code
+preferences.escapeUnicode=Escape unicode
preferences.skipResourcesDecode=Don't decode resources
preferences.threads=Processing threads count
preferences.cfg=Generate methods CFG graphs (in 'dot' format)
diff --git a/jadx-samples/src/main/java/jadx/samples/TestUnicode.java b/jadx-samples/src/main/java/jadx/samples/TestUnicode.java
new file mode 100644
index 000000000..af3149d56
--- /dev/null
+++ b/jadx-samples/src/main/java/jadx/samples/TestUnicode.java
@@ -0,0 +1,43 @@
+package jadx.samples;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestUnicode extends AbstractTest {
+
+ /**
+ * Some unicode strings from:
+ * http://www.ltg.ed.ac.uk/~richard/unicode-sample-3-2.html
+ */
+ public List strings() {
+ List list = new ArrayList();
+ list.add("! \" # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \\ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~");
+ list.add("¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ");
+ list.add("∀ ∁ ∂ ∃ ∄ ∅ ∆ ∇ ∈ ∉ ∊ ∋ ∌ ∍ ∎ ∏ ∐ ∑ − ∓ ∔ ∕ ∖ ∗ ∘ ∙ √ ∛ ∜ ∝ ∞ ∟ ∠ ∡ ∢ ∣ ∤ ∥ ∦ ∧ ∨ ∩ ∪ ∫ ∬ ∭ ∮ ∯ ∰ ∱ ∲ ∳ ∴ ∵ ∶ ∷ ∸ ∹ ∺ ∻ ∼ ∽ ∾ ∿ ≀ ≁ ≂ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≏ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≠ ≡ ≢ ≣ ≤ ≥ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿");
+ list.add("Ѐ Ё Ђ Ѓ Є Ѕ І Ї Ј Љ Њ Ћ Ќ Ѝ Ў Џ А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я а б в г д е ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я ѐ ё ђ ѓ є ѕ і ї");
+ list.add("ぁ あ ぃ い ぅ う ぇ え ぉ お か が き ぎ く ぐ け げ こ ご さ ざ し じ す ず せ ぜ そ ぞ た だ ち ぢ っ つ づ て で と ど な に ぬ ね の は ば ぱ ひ び ぴ ふ ぶ ぷ へ べ ぺ ほ ぼ ぽ ま み む め も ゃ や ゅ ゆ ょ よ ら り る れ ろ ゎ わ ゐ ゑ を ん");
+ list.add("ァ ア ィ イ ゥ ウ ェ エ ォ オ カ ガ キ ギ ク グ ケ ゲ コ ゴ サ ザ シ ジ ス ズ セ ゼ ソ ゾ タ ダ チ ヂ ッ ツ ヅ テ デ ト ド ナ ニ ヌ ネ ノ ハ バ パ ヒ ビ ピ フ ブ プ ヘ ベ ペ ホ ボ ポ マ ミ ム メ モ ャ ヤ ュ ユ ョ ヨ ラ リ ル レ ロ ヮ ワ ヰ ヱ ヲ ン ヴ ヵ ヶ");
+ list.add("一 丁 丂 七 丄 丅 丆 万 丈 三 上 下 丌 不 与 丏 丐 丑 丒 专 且 丕 世 丗 丘 丙 业 丛 东 丝 丞 丟 丠 両 丢 丣 两 严 並 丧 丨 丩 个 丫 丬 中 丮 丯 丰 丱 串 丳 临 丵 丶 丷 丸 丹 为 主 丼 丽 举 丿 乀 乁 乂 乃 乄 久 乆 乇 么 义 乊 之 乌 乍 乎 乏 乐 乑 乒 乓 乔 乕 乖 乗 乘 乙 乚 乛 乜 九 乞 也 习 乡 乢 乣 乤 乥 书 乧 乨 乩 乪 乫 乬 乭 乮 乯 买 乱 乲 乳 乴 乵 乶 乷 乸 乹 乺 乻 乼 乽 乾 乿");
+ list.add("豈 更 車 賈 滑 串 句 龜 龜 契 金 喇 奈 懶 癩 羅 蘿 螺 裸 邏 樂 洛 烙 珞 落 酪 駱 亂 卵 欄 爛 蘭 鸞 嵐 濫 藍 襤 拉 臘 蠟 廊 朗 浪 狼 郎 來 冷 勞 擄 櫓 爐 盧 老 蘆 虜 路 露 魯 鷺 碌 祿 綠 菉 錄 鹿 論 壟 弄 籠 聾 牢 磊 賂 雷 壘 屢 樓 淚 漏 累 縷 陋 勒 肋 凜 凌 稜 綾 菱 陵 讀 拏 樂 諾 丹 寧 怒 率 異 北 磻 便 復 不 泌 數 索 參 塞 省 葉 說 殺 辰 沈 拾 若 掠 略 亮 兩 凉 梁 糧 良 諒 量 勵");
+ list.add("ㄱ ㄲ ㄳ ㄴ ㄵ ㄶ ㄷ ㄸ ㄹ ㄺ ㄻ ㄼ ㄽ ㄾ ㄿ ㅀ ㅁ ㅂ ㅃ ㅄ ㅅ ㅆ ㅇ ㅈ ㅉ ㅊ ㅋ ㅌ ㅍ ㅎ ㅏ ㅐ ㅑ ㅒ ㅓ ㅔ ㅕ ㅖ ㅗ ㅘ ㅙ ㅚ ㅛ ㅜ ㅝ ㅞ ㅟ ㅠ ㅡ ㅢ ㅣ ㅤ ㅥ ㅦ ㅧ ㅨ ㅩ ㅪ ㅫ ㅬ ㅭ ㅮ ㅯ ㅰ ㅱ ㅲ ㅳ ㅴ ㅵ ㅶ ㅷ ㅸ ㅹ ㅺ ㅻ ㅼ ㅽ ㅾ ㅿ ㆀ ㆁ ㆂ ㆃ ㆄ ㆅ ㆆ ㆇ ㆈ ㆉ ㆊ ㆋ ㆌ");
+ list.add("가 각 갂 갃 간 갅 갆 갇 갈 갉 갊 갋 갌 갍 갎 갏 감 갑 값 갓 갔 강 갖 갗 갘 같 갚 갛 개 객 갞 갟 갠 갡 갢 갣 갤 갥 갦 갧 갨 갩 갪 갫 갬 갭 갮 갯 갰 갱 갲 갳 갴 갵 갶 갷 갸 갹 갺 갻 갼 갽 갾 갿 걀 걁 걂 걃 걄 걅 걆 걇 걈 걉 걊 걋 걌 걍 걎 걏 걐 걑 걒 걓 걔 걕 걖 걗 걘 걙 걚 걛 걜 걝 걞 걟 걠 걡 걢 걣 걤 걥 걦 걧 걨 걩 걪 걫 걬 걭 걮 걯 거 걱 걲 걳 건 걵 걶 걷 걸 걹 걺 걻 걼 걽 걾 걿");
+ list.add( "؛ ؟ ء آ أ ؤ إ ئ ا ب ة ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ـ ف ق ك ل م ن ه و ى ي ً ٌ ٍ َ ُ ِ ّ ْ ٓ ٔ ٕ ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩ ٪ ٫ ٬ ٭ ٮ ٯ ٰ ٱ ٲ ٳ ٴ ٵ ٶ ٷ ٸ ٹ ٺ ٻ ټ ٽ پ ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ ڒ ړ ڔ ڕ ږ ڗ ژ ڙ ښ ڛ ڜ ڝ ڞ ڟ ڠ ڡ ڢ ڣ ڤ ڥ ڦ ڧ ڨ ک ڪ ګ ڬ");
+ list.add( "ஃ அ ஆ இ ஈ உ ஊ எ ஏ ஐ ஒ ஓ ஔ க ங ச ஜ ஞ ட ண த ந ன ப ம ய ர ற ல ள ழ வ ஷ ஸ ஹ ா ி ீ ு ூ ெ ே ை ொ ோ ௌ ் ௗ ௧ ௨ ௩ ௪ ௫ ௬ ௭ ௮ ௯ ௰ ௱ ௲");
+ list.add( "\uD800\uDF00 \uD800\uDF01 \uD800\uDF02 \uD800\uDF03 \uD800\uDF04 \uD800\uDF05 \uD800\uDF06 \uD800\uDF07 \uD800\uDF08 \uD800\uDF09 \uD800\uDF0A \uD800\uDF0B \uD800\uDF0C \uD800\uDF0D \uD800\uDF0E \uD800\uDF0F \uD800\uDF10 \uD800\uDF11 \uD800\uDF12 \uD800\uDF13 \uD800\uDF14 \uD800\uDF15 \uD800\uDF16 \uD800\uDF17 \uD800\uDF18 \uD800\uDF19 \uD800\uDF1A \uD800\uDF1B \uD800\uDF1C \uD800\uDF1D \uD800\uDF1E \uD800\uDF20 \uD800\uDF21 \uD800\uDF22 \uD800\uDF23");
+ list.add("𐌀 𐌁 𐌂 𐌃 𐌄 𐌅 𐌆 𐌇 𐌈 𐌉 𐌊 𐌋 𐌌 𐌍 𐌎 𐌏 𐌐 𐌑 𐌒 𐌓 𐌔 𐌕 𐌖 𐌗 𐌘 𐌙 𐌚 𐌛 𐌜 𐌝 𐌞 𐌠 𐌡 𐌢 𐌣");
+ return list;
+ }
+
+ @Override
+ public boolean testRun() throws Exception {
+ String s = "\uD800\uDF0F"; // 𐌏
+ int codePoint = s.codePointAt(0);
+ System.out.println(s + " = " + codePoint);
+ assertEquals(codePoint, 66319);
+ return true;
+ }
+
+ public static void main(String[] args) throws Exception {
+ new TestUnicode().testRun();
+ }
+}