diff --git a/.gitignore b/.gitignore index fecea56..4c6e148 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,11 @@ *.iml .idea/ dependency-reduced-pom.xml +classes/ libs/**.jar libs/*.jar libs/org + +idea-flex.skeleton +jflex-1.7.0-2.jar diff --git a/.gitmodules b/.gitmodules index c4a5308..f4bd9d1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "libs/RSyntaxTextArea"] path = libs/RSyntaxTextArea url = git@github.com:LLVM-but-worse/RSyntaxTextArea.git +[submodule "mapleir"] + path = mapleir + url = git@github.com:llvm-but-worse/jda-mapleir.git diff --git a/README.md b/README.md index e99d574..ef042c8 100644 --- a/README.md +++ b/README.md @@ -2,43 +2,79 @@ ![JDA Logo](docs/icon.png) -Java Disassembler (JDA) is a fork of Bytecode Viewer (BCV) that adds advanced code analysis features. The basic principles of BCV are maintained; however, many aspects are different. +Tired of seeing this??? -## Motivation -The main reason JDA was forked was that BCV was becoming too large and cumbersome, with many redundant, unused, or -useless features. The issue with removing them is that there could be many BCV users that relied on such features. -Because of this, it is more appropriate to fork JDA as a separate project. Additionally, BCV's development has -stagnated, and the last official build is from July 2015. It has since grown increasingly difficult and arcane to -compile BCV from source, and full jars often range into 20Mb in size. It has become necessary to fork BCV in order -to make large-scale changes. +```java +// $FF: Couldn't be decompiled +``` -In addition to the growing power and complexity of commercial obfuscation programs for Java, it has become increasingly -necessary to develop improved reverse engineering and static analysis tools. For this reason, JDA has been developed -in order to provide professional-quality static analysis tools for JVM-based languages. +The Java Disassembler (JDA) is a GUI reverse engineering tool that can turn this: -## Scope -With that in mind, JDA's goal is to be a light-weight yet powerful Java static disassembler. BCV suffered from a -multitude of issues, but a large one was that it tried to be a dynamic reverse engineering tool (debugger) as well -as a static tool (disassembler) at the same time, only succeeding partially in the latter. JDA's role is to provide -a platform and interface for the core features such as analysis and disassembly. Additionally, many useless -or irrelevant features have been removed. JDA's scope as a program currently is to be a platform for reverse engineering -tools to be built on top of. +```java +// $FF: Couldn't be decompiled +// java.lang.IllegalArgumentException: Invalid type: @ +// at org.jetbrains.java.decompiler.struct.gen.VarType.getType(VarType.java:405) +// at org.jetbrains.java.decompiler.struct.gen.VarType.(VarType.java:90) +// at org.jetbrains.java.decompiler.struct.gen.VarType.(VarType.java:62) +// ... +``` -In the future, JDA will have powerful static analysis tools, such as control and data flow analysis, code contraction -(copy and constant propagation), whole binary cross referencing (xrefs), and an IR engine. However, these standard -core utilities will be distributed separately as the [MapleIR](https://github.com/LLVM-but-worse/maple-ir) plugin. +into this: -![MapleIR demo](docs/demo.png) +```java +public void keyPressed(KeyEvent var1) { + super.keyPressed(var1); + int var10000 = var1.getKeyCode(); + int var10001 = (3 << 2 & 9 | 5 | 7) ^ 5; + int var10003 = 0 ^ 1165448477 ^ 958591453 ^ 2085987521; + if (var10000 == var10001) { + 11.iiIIiiiiIiIIi(this.IIiiIiiiIIiiI, this.IiIIiiiiiiiiI, this.IIiiiiiiIIiIi); + } +} +``` + +and finally this: + +```java +public void keyPressed(KeyEvent var1) { + super.keyPressed(var1); + if (var1.getKeyCode() == 10) { + 11.iiIIiiiiIiIIi(this.IIiiIiiiIIiiI, this.IiIIiiiiiiiiI, this.IIiiiiiiIIiIi); + } +} +``` ## Features - - Ergonomic design for low-level bytecode reversing +JDA offers powerful static analysis tools, such as control and data flow analysis, and code simplification +built using a custom IL. Moreover, many tasks expected of a disassembler such as constant and string searching are available. +These standard core utilities are with the [MapleIR](https://github.com/LLVM-but-worse/maple-ir) plugin. You +can also access the IL API and integrate into the UI by writing your own plugins in Java. +In the near future it will support whole binary cross referencing (xrefs) and more. + + - Ergonomic design for high-level browsing or low-level bytecode reversing - [Data-flow analysis with copy and constant propagation](docs/propagation-analysis.png) (provided by MapleIR) - Support for a variety of decompilers - - Cross-reference bytecode, decompilation, and IR. + - Side-by-side view of decompilation, bytecode, and IL. + +![MapleIR demo](docs/demo.png) + +## Motivation +Due to the growing power and complexity of commercial obfuscation programs for Java, it has become +necessary to develop improved reverse engineering and static analysis tools. JDA was developed to +provide professional-quality static analysis tools for JVM-based languages. + +JDA began as a fork of Bytecode Viewer (BCV). BCV suffered heavily from bloat, poor performance, and +stagnant development. In JDA many useless or irrelevant features have been removed, and significant parts +of the codebase have been cleaned up or rewritten entirely. + +## Scope +With that in mind, JDA's goal is to be a focused, light-weight yet powerful Java static disassembler. +JDA's role is to provide a platform and interface for the core features such as analysis and disassembly. +Therefore, JDA's scope is to be a platform for Java reverse engineering tools to be built on top of. More to come in the future. -## Maple-IR Plugin +## MapleIR Plugin To install the plugin put the plugin jar in `~/.jda/plugins` (or equivalently, `%USERPROFILE%\.jda\plugins` on Windows), then restart. diff --git a/docs/demo.png b/docs/demo.png index 30ef58c..3e08407 100644 Binary files a/docs/demo.png and b/docs/demo.png differ diff --git a/docs/icon.png b/docs/icon.png index 959d08e..3c71dbd 100644 Binary files a/docs/icon.png and b/docs/icon.png differ diff --git a/docs/propagation-analysis.png b/docs/propagation-analysis.png index 40f3df9..14fa3dc 100644 Binary files a/docs/propagation-analysis.png and b/docs/propagation-analysis.png differ diff --git a/libs/RSyntaxTextArea b/libs/RSyntaxTextArea index cabae4e..b94c1b8 160000 --- a/libs/RSyntaxTextArea +++ b/libs/RSyntaxTextArea @@ -1 +1 @@ -Subproject commit cabae4e404bb0b859b0b3d58d1a0630d4028f82f +Subproject commit b94c1b893a15dcdeefe37f4b0d0f45dc252a7f08 diff --git a/libs/fernflower b/libs/fernflower index 02fdbec..c0f01b9 160000 --- a/libs/fernflower +++ b/libs/fernflower @@ -1 +1 @@ -Subproject commit 02fdbec13231c16a8f8479a464327c57cd1a8681 +Subproject commit c0f01b977386766352dd01d116c9f80b9f683ebe diff --git a/mapleir b/mapleir new file mode 160000 index 0000000..1ba5a4a --- /dev/null +++ b/mapleir @@ -0,0 +1 @@ +Subproject commit 1ba5a4a7405798e6bf74feb42c706af3ae68e596 diff --git a/mapleir/.gitignore b/mapleir/.gitignore deleted file mode 100644 index 8b8c81d..0000000 --- a/mapleir/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target/ -dependency-reduced-pom.xml diff --git a/mapleir/pom.xml b/mapleir/pom.xml deleted file mode 100644 index 556a1fa..0000000 --- a/mapleir/pom.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - 4.0.0 - - org.mapleir - jdaplugin - 0.0.1-SNAPSHOT - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-shade-plugin - 2.4.3 - - - package - - shade - - - - - org.mapleir - - - - - org.mapleir.jdaplugin.MaplePlugin - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.0.2 - - - - maple-ir - - - - - - - - ${project.basedir}/src/main/resources - - - - - - - club.bytecode.the - jda - 1.1.0 - - - org.mapleir - main - 0.0.1-ALPHA - - - diff --git a/mapleir/src/main/java/org/mapleir/jdaplugin/DeobfuscateFilter.java b/mapleir/src/main/java/org/mapleir/jdaplugin/DeobfuscateFilter.java deleted file mode 100644 index 47fc82d..0000000 --- a/mapleir/src/main/java/org/mapleir/jdaplugin/DeobfuscateFilter.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.mapleir.jdaplugin; - -import club.bytecode.the.jda.decompilers.filter.DecompileFilter; -import org.mapleir.ir.algorithms.BoissinotDestructor; -import org.mapleir.ir.cfg.ControlFlowGraph; -import org.mapleir.ir.cfg.builder.ControlFlowGraphBuilder; -import org.mapleir.ir.codegen.ControlFlowGraphDumper; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; - -public class DeobfuscateFilter implements DecompileFilter, MapleComponent { - @Override - public void process(ClassNode cn) { - if (cn == null) - return; - for (MethodNode mn : cn.methods) { - ControlFlowGraph cfg = ControlFlowGraphBuilder.build(mn); - BoissinotDestructor.leaveSSA(cfg); - cfg.getLocals().realloc(cfg); - (new ControlFlowGraphDumper(cfg, mn)).dump(); - System.out.println("Processed " + mn); - } - } - - @Override - public String getName() { - return "Deobfuscator"; - } -} diff --git a/mapleir/src/main/java/org/mapleir/jdaplugin/ILDecompiler.java b/mapleir/src/main/java/org/mapleir/jdaplugin/ILDecompiler.java deleted file mode 100644 index c8c9d78..0000000 --- a/mapleir/src/main/java/org/mapleir/jdaplugin/ILDecompiler.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.mapleir.jdaplugin; - -import club.bytecode.the.jda.FileContainer; -import club.bytecode.the.jda.api.JDANamespace; -import club.bytecode.the.jda.decompilers.JDADecompiler; -import org.mapleir.ir.algorithms.BoissinotDestructor; -import org.mapleir.ir.cfg.ControlFlowGraph; -import org.mapleir.ir.cfg.builder.ControlFlowGraphBuilder; -import org.mapleir.ir.printer.ClassPrinter; -import org.mapleir.ir.printer.FieldNodePrinter; -import org.mapleir.ir.printer.MethodNodePrinter; -import org.mapleir.propertyframework.api.IPropertyDictionary; -import org.mapleir.propertyframework.util.PropertyHelper; -import org.mapleir.stdlib.util.TabbedStringWriter; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; - -public class ILDecompiler extends JDADecompiler implements MapleComponent { - @Override - public String decompileClassNode(FileContainer container, ClassNode cn) { - TabbedStringWriter sw = new TabbedStringWriter(); - sw.setTabString(" "); - IPropertyDictionary settings = PropertyHelper.createDictionary(); - final FieldNodePrinter fieldPrinter = new FieldNodePrinter(sw, settings); - final MethodNodePrinter methodPrinter = new MethodNodePrinter(sw, settings) { - @Override - protected ControlFlowGraph getCfg(MethodNode mn) { - ControlFlowGraph cfg = ControlFlowGraphBuilder.build(mn); - BoissinotDestructor.leaveSSA(cfg); - cfg.getLocals().realloc(cfg); - return cfg; - } - }; - ClassPrinter cp = new ClassPrinter(sw, settings, fieldPrinter, methodPrinter); - cp.print(cn); - return sw.toString(); - } - - @Override - public String getName() { - return "MapleIL"; - } - - @Override - public JDANamespace getNamespace() { - return MaplePlugin.getInstance().getNamespace(); - } -} diff --git a/mapleir/src/main/java/org/mapleir/jdaplugin/IRDecompiler.java b/mapleir/src/main/java/org/mapleir/jdaplugin/IRDecompiler.java deleted file mode 100644 index 6ade1a7..0000000 --- a/mapleir/src/main/java/org/mapleir/jdaplugin/IRDecompiler.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.mapleir.jdaplugin; - -import club.bytecode.the.jda.decompilers.bytecode.*; -import org.mapleir.ir.algorithms.BoissinotDestructor; -import org.mapleir.ir.cfg.ControlFlowGraph; -import org.mapleir.ir.cfg.builder.ControlFlowGraphBuilder; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; - -public class IRDecompiler extends BytecodeDecompiler implements MapleComponent { - @Override - protected MethodNodeDecompiler getMethodNodeDecompiler(PrefixedStringBuilder sb, ClassNode cn, Iterator it) { - return new IRMethodDecompiler(this, sb, it.next(), cn); - } - - @Override - public String getName() { - return "MapleIR"; - } -} - -class IRMethodDecompiler extends MethodNodeDecompiler { - public IRMethodDecompiler(BytecodeDecompiler parent, PrefixedStringBuilder sb, MethodNode mn, ClassNode cn) { - super(parent, sb, mn, cn); - } - - @Override - protected InstructionPrinter getInstructionPrinter(MethodNode m, TypeAndName[] args) { - return new IRInstructionPrinter(this, m, args); - } -} - -class IRInstructionPrinter extends InstructionPrinter { - public IRInstructionPrinter(MethodNodeDecompiler parent, MethodNode m, TypeAndName[] args) { - super(parent, m, args); - } - - @Override - public ArrayList createPrint() { - ControlFlowGraph cfg = ControlFlowGraphBuilder.build(mNode); - BoissinotDestructor.leaveSSA(cfg); - cfg.getLocals().realloc(cfg); - String result = cfg.toString(); - return new ArrayList<>(Arrays.asList(result.split("\n"))); - } -} diff --git a/mapleir/src/main/java/org/mapleir/jdaplugin/MapleComponent.java b/mapleir/src/main/java/org/mapleir/jdaplugin/MapleComponent.java deleted file mode 100644 index 0c0f33b..0000000 --- a/mapleir/src/main/java/org/mapleir/jdaplugin/MapleComponent.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.mapleir.jdaplugin; - -import club.bytecode.the.jda.api.JDANamespace; -import club.bytecode.the.jda.api.JDANamespacedComponent; - -// Is this bad design...? -public interface MapleComponent extends JDANamespacedComponent { - @Override - default JDANamespace getNamespace() { - return MaplePlugin.getInstance().getNamespace(); - } -} diff --git a/mapleir/src/main/java/org/mapleir/jdaplugin/MaplePlugin.java b/mapleir/src/main/java/org/mapleir/jdaplugin/MaplePlugin.java deleted file mode 100644 index d19af98..0000000 --- a/mapleir/src/main/java/org/mapleir/jdaplugin/MaplePlugin.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.mapleir.jdaplugin; - -import club.bytecode.the.jda.FileContainer; -import club.bytecode.the.jda.api.JDAPlugin; -import club.bytecode.the.jda.api.JDAPluginNamespace; -import club.bytecode.the.jda.decompilers.Decompilers; -import club.bytecode.the.jda.decompilers.filter.DecompileFilters; -import org.mapleir.DefaultInvocationResolver; -import org.mapleir.app.client.SimpleApplicationContext; -import org.mapleir.app.service.ApplicationClassSource; -import org.mapleir.context.AnalysisContext; -import org.mapleir.context.BasicAnalysisContext; -import org.mapleir.context.IRCache; -import org.mapleir.ir.cfg.builder.ControlFlowGraphBuilder; -import org.mapleir.jdaplugin.gui.AboutDialog; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - -public class MaplePlugin implements JDAPlugin { - private static MaplePlugin instance; - - public final Map cxts = new HashMap<>(); - public final JDAPluginNamespace namespace = new JDAPluginNamespace(this); - - public MaplePlugin() { - instance = this; - } - - public static void main(String[] args) { - throw new NotImplementedException(); - } - - public static MaplePlugin getInstance() { - return instance; - } - - @Override - public String getName() { - return "MapleIR"; - } - - @Override - public JDAPluginNamespace getNamespace() { - return namespace; - } - - @Override - public void onLoad() { - Decompilers.registerDecompiler(new IRDecompiler()); - Decompilers.registerDecompiler(new ILDecompiler()); - DecompileFilters.registerFilter(new DeobfuscateFilter()); - System.out.println("MapleIR plugin loaded"); - } - - @Override - public void onUnload() { - - } - - @Override - public void onGUILoad() { - } - - @Override - public void onExit() { - } - - @Override - public void onOpenFile(FileContainer fileContainer) { - // todo - ApplicationClassSource app = new ApplicationClassSource(fileContainer.name, new HashSet<>()); - AnalysisContext newCxt = new BasicAnalysisContext.BasicContextBuilder() - .setApplication(app) - .setInvocationResolver(new DefaultInvocationResolver(app)) - .setCache(new IRCache(ControlFlowGraphBuilder::build)) - .setApplicationContext(new SimpleApplicationContext(app)) - .build(); - // when we get around to it, do tracing, IPA stuff here. - cxts.put(fileContainer, newCxt); - } - - @Override - public void onCloseFile(FileContainer fc) { - cxts.remove(fc); - } - - @Override - public void onPluginButton() { - new AboutDialog().show(); - } -} diff --git a/mapleir/src/main/java/org/mapleir/jdaplugin/gui/AboutDialog.java b/mapleir/src/main/java/org/mapleir/jdaplugin/gui/AboutDialog.java deleted file mode 100644 index 8f3d2bb..0000000 --- a/mapleir/src/main/java/org/mapleir/jdaplugin/gui/AboutDialog.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.mapleir.jdaplugin.gui; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; - -public class AboutDialog extends JDialog { - public AboutDialog() { - super(new JFrame(), "MapleIR - About", true); - rootPane.setBorder(new EmptyBorder(10, 10, 10, 10)); - Box b = Box.createVerticalBox(); - b.add(Box.createGlue()); - b.add(new JLabel("Powered by MapleIR")); - b.add(Box.createGlue()); - getContentPane().add(b, "Center"); - - JPanel okPanel = new JPanel(); - JButton ok = new JButton("OK"); - okPanel.add(ok); - getContentPane().add(okPanel, "South"); - - ok.addActionListener(evt -> setVisible(false)); - - setSize(250, 100); - } -} diff --git a/mapleirInjector/src/org/mapleir/jdaplugin/MapleInjectedJDA.java b/mapleirInjector/src/org/mapleir/jdaplugin/MapleInjectedJDA.java index ad8907b..480a9fc 100644 --- a/mapleirInjector/src/org/mapleir/jdaplugin/MapleInjectedJDA.java +++ b/mapleirInjector/src/org/mapleir/jdaplugin/MapleInjectedJDA.java @@ -7,7 +7,7 @@ */ public class MapleInjectedJDA { public static void main(String[] args) { - JDA.injectedPlugin = MaplePlugin::new; + JDA.autoloadPlugin = MaplePlugin::new; JDA.main(args); } } diff --git a/pom.xml b/pom.xml index 10bd84b..973fe1a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ club.bytecode.the jda - 1.1.0 + 1.2.0 @@ -17,7 +17,7 @@ false - file://${project.basedir}/libs + ${project.baseUri}libs @@ -99,7 +99,7 @@ ${project.basedir}/libs/fernflower/src - ${project.basedir}/libs/RSyntaxTextArea/src/main/java + ${project.basedir}/libs/RSyntaxTextArea/RSyntaxTextArea/src/main/java ... @@ -112,7 +112,7 @@ ${project.basedir}/src/main/resources - ${project.basedir}/libs/RSyntaxTextArea/src/main/resources + ${project.basedir}/libs/RSyntaxTextArea/RSyntaxTextArea/src/main/resources @@ -146,12 +146,37 @@ org.bitbucket.mstrobel procyon-compilertools - 0.5.32 + 0.5.36 org.mapleir modasm 0.0.1-ALPHA + + org.mapleir + stdlib + 0.0.1-ALPHA + + + org.ow2.asm + asm + 7.1 + + + org.ow2.asm + asm-tree + 7.1 + + + org.ow2.asm + asm-util + 7.1 + + + org.ow2.asm + asm-commons + 7.1 + diff --git a/src/main/java/club/bytecode/the/jda/FileContainer.java b/src/main/java/club/bytecode/the/jda/FileContainer.java index 7c18c3e..107d8e4 100644 --- a/src/main/java/club/bytecode/the/jda/FileContainer.java +++ b/src/main/java/club/bytecode/the/jda/FileContainer.java @@ -1,10 +1,9 @@ package club.bytecode.the.jda; -import org.objectweb.asm.ClassReader; +import club.bytecode.the.jda.util.BytecodeUtils; import org.objectweb.asm.tree.ClassNode; import java.io.File; -import java.util.HashMap; import java.util.Map; /** @@ -14,28 +13,29 @@ */ public class FileContainer { - public FileContainer(File f) { + public FileContainer(File f, Map files) { this.file = f; this.name = f.getAbsolutePath(); + this.files = files; } public final File file; public final String name; + public final Map files; - public HashMap files = new HashMap<>(); // this is assigned outside the class?! public ClassNode loadClassFile(String filename) { byte[] bytes = files.get(filename); if (bytes == null) return null; - ClassReader reader = new ClassReader(bytes); - ClassNode classNode = new ClassNode(); - reader.accept(classNode, ClassReader.EXPAND_FRAMES); - return classNode; + return BytecodeUtils.loadClass(bytes); } public String findClassfile(String className) { String candidate = className + ".class"; + if (name.endsWith(".class")) { // this is a single .class file. we need to strip the package path out. + candidate = JDA.getClassName(candidate); + } if (files.containsKey(candidate)) return candidate; return ""; diff --git a/src/main/java/club/bytecode/the/jda/JDA.java b/src/main/java/club/bytecode/the/jda/JDA.java index 88995ce..77cf155 100644 --- a/src/main/java/club/bytecode/the/jda/JDA.java +++ b/src/main/java/club/bytecode/the/jda/JDA.java @@ -4,6 +4,9 @@ import club.bytecode.the.jda.api.JDANamespace; import club.bytecode.the.jda.api.JDAPlugin; import club.bytecode.the.jda.api.PluginLoader; +import club.bytecode.the.jda.decompilers.filter.DecompileFilters; +import club.bytecode.the.jda.decompilers.filter.DropLocalVariableTableFilter; +import club.bytecode.the.jda.decompilers.filter.IllegalAnnotationFilter; import club.bytecode.the.jda.gui.MainViewerGUI; import club.bytecode.the.jda.gui.fileviewer.BytecodeFoldParser; import club.bytecode.the.jda.gui.fileviewer.BytecodeTokenizer; @@ -11,6 +14,7 @@ import club.bytecode.the.jda.gui.fileviewer.ViewerFile; import club.bytecode.the.jda.gui.navigation.FileNavigationPane; import club.bytecode.the.jda.settings.Settings; +import club.bytecode.the.jda.util.BytecodeUtils; import club.bytecode.the.jda.util.GuiUtils; import club.bytecode.the.jda.util.MiscUtils; import org.apache.commons.io.FileUtils; @@ -19,7 +23,6 @@ import org.fife.ui.rsyntaxtextarea.TokenMakerFactory; import org.fife.ui.rsyntaxtextarea.folding.CurlyFoldParser; import org.fife.ui.rsyntaxtextarea.folding.FoldParserManager; -import org.objectweb.asm.ClassWriter; import org.objectweb.asm.tree.ClassNode; import javax.swing.*; @@ -31,12 +34,12 @@ import java.net.MalformedURLException; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import java.util.function.Supplier; public class JDA { - /*per version*/ - public static final String version = "1.1.0"; - /* Constants */ + public static final String version = "1.2.0"; + public static final String ISSUE_TRACKER_URL = "https://github.com/LLVM-but-worse/jda/issues"; public static final String fs = System.getProperty("file.separator"); public static final String nl = System.getProperty("line.separator"); @@ -45,9 +48,9 @@ public class JDA { public static final File recentsFile = new File(dataDir, "recentfiles.jda"); public static final File settingsFile = new File(dataDir, "settings.jda"); private static final long start = System.currentTimeMillis(); - /*the rest*/ + public static MainViewerGUI viewer = null; - private static List files = new ArrayList<>(); //all of BCV's loaded files/classes/etc + private static List files = new ArrayList<>(); //all of JDA's loaded files/classes/etc private static int maxRecentFiles = 25; private static List recentFiles = new ArrayList<>(); public static String lastDirectory = ""; @@ -56,7 +59,7 @@ public class JDA { public static final JDANamespace namespace = JDADefaultNamespace.INSTANCE; private static List plugins = new ArrayList<>(); - public static Supplier injectedPlugin = null; // for testing purposes only. + public static Supplier autoloadPlugin = null; // for testing purposes, a plugin to load on startup. /** * Main startup @@ -64,17 +67,16 @@ public class JDA { * @param args files you want to open or CLI */ public static void main(String[] args) { - // Fix antialiasing - System.setProperty("awt.useSystemAAFontSettings", "lcd"); - System.setProperty("swing.aatext", "true"); + GuiUtils.setAntialiasingSettings(); if (SystemUtils.IS_OS_LINUX) { GuiUtils.setWmClassName("JDA"); } GuiUtils.setLookAndFeel(); try { - System.out.println("JDA (BCV Fork) v" + version); + System.out.println("JDA v" + version); getJDADirectory(); + registerModules(); loadPlugins(); Settings.loadGUI(); @@ -83,6 +85,7 @@ public static void main(String[] args) { recentFiles.addAll(FileUtils.readLines(recentsFile, "UTF-8")); viewer = new MainViewerGUI(); + JDA.onGUILoad(); Boot.boot(); JDA.boot(args); } catch (Exception e) { @@ -103,12 +106,17 @@ public static void unloadPlugin(JDAPlugin plugin) { public static List getLoadedPlugins() { return Collections.unmodifiableList(plugins); } - + + private static void registerModules() { + DecompileFilters.registerFilter(new IllegalAnnotationFilter()); + DecompileFilters.registerFilter(new DropLocalVariableTableFilter()); + } + private static void loadPlugins() throws MalformedURLException { - if (injectedPlugin != null) { - JDAPlugin plugin = injectedPlugin.get(); - System.out.println("Loading dependency-injected plugin " + plugin.getName()); - loadPlugin(injectedPlugin.get()); + if (autoloadPlugin != null) { + JDAPlugin plugin = autoloadPlugin.get(); + System.out.println("Loading statically-loaded plugin " + plugin.getName()); + loadPlugin(autoloadPlugin.get()); System.out.println("Skipping other plugins."); return; } @@ -140,7 +148,6 @@ public static void onGUILoad() { plugins.forEach(JDAPlugin::onGUILoad); } - // todo: rewrite /** * The version checker thread */ @@ -193,11 +200,7 @@ private static void onExit() { */ public static void waitForTasks() { while (jobCount.get() > 0) { - try { - Thread.sleep(10L); - } catch (InterruptedException e) { - e.printStackTrace(); - } + GuiUtils.sleep(10L); } } @@ -213,7 +216,7 @@ public static void setBusy(boolean busy) { else jobCount.decrementAndGet(); assert (jobCount.get() >= 0); - viewer.setIcon(busy); + viewer.setIcon(jobCount.get() > 0); } public static byte[] getFileBytes(ViewerFile file) { @@ -231,7 +234,7 @@ public static boolean hasFile(ViewerFile file) { // try to get class bytes by exporting classnode, else fallback to getting the bytes from the actual file itself public static byte[] getClassBytes(FileContainer container, ClassNode cn) { - byte[] result = dumpClassToBytes(cn); + byte[] result = BytecodeUtils.dumpClassToBytes(cn); if (result != null) return result; result = getClassFileBytes(container, cn.name); @@ -244,24 +247,11 @@ public static byte[] getClassFileBytes(FileContainer container, String className return null; return bytes; } - - public static byte[] dumpClassToBytes(ClassNode cn) { - // we have to do this, or else decompile filters don't work. - try { - ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); - cn.accept(writer); - return writer.toByteArray(); - } catch (Exception e) { - System.err.println("Exception while dumping class " + cn.name + ": "); - e.printStackTrace(); - return null; - } - } public static final String HACK_PREFIX = "\0JDA-hack"; public static File getClassFileProxy(ClassNode cn) { - return new File('/' + HACK_PREFIX, cn + ".class"); + return new File('/' + HACK_PREFIX, cn.name + ".class"); } public static String extractProxyClassName(String fileName) { @@ -272,19 +262,15 @@ public static String getClassFilePath(String fileName) { return fileName.replace(File.separator, "/").substring(0, fileName.length() - ".class".length()); } + /** + * Returns the last part of a fully qualified name, e.g. the class name. + * @param fullyQualifiedName a fully qualified name like "java/lang/String" + * @return just the name without the package name, e.g. "String" + */ public static String getClassName(String fullyQualifiedName) { return fullyQualifiedName.substring(fullyQualifiedName.lastIndexOf('/') + 1); } - // WTF???? - public static Map getLoadedBytes() { - Map data = new HashMap<>(); - for (FileContainer container : files) { - data.putAll(container.getFiles()); - } - return data; - } - /** * Opens a file, optional if it should append to the recent files menu * @@ -312,7 +298,7 @@ public static void openFiles(final File[] files, boolean recentFiles, FileNaviga if (!fileToOpen.exists()) { showMessage("The file " + fileToOpen.getAbsolutePath() + " could not be found."); } else if (fileToOpen.isDirectory()) { - FileNavigationPane.FileNode newNode = fnp.addTreeElement(new FileContainer(fileToOpen), parent); + FileNavigationPane.FileNode newNode = fnp.addTreeElement(new FileContainer(fileToOpen, new HashMap<>()), parent); openFiles(fileToOpen.listFiles(), false, newNode); } else if (fn.endsWith(".jar") || fn.endsWith(".zip")) { try { @@ -326,8 +312,7 @@ public static void openFiles(final File[] files, boolean recentFiles, FileNaviga HashMap files1 = new HashMap<>(); byte[] bytes = JarUtils.getBytes(new FileInputStream(fileToOpen)); files1.put(fileToOpen.getName(), bytes); - FileContainer container = new FileContainer(fileToOpen); - container.files = files1; + FileContainer container = new FileContainer(fileToOpen, files1); openFile(container); fnp.addTreeElement(container, parent); } @@ -360,6 +345,20 @@ public static List getOpenFiles() { return Collections.unmodifiableList(files); } + /** + * + * @param filename the filename to search all open FileContainers for + * @return the FileContainer which holds the specified file or null if not found + */ + public static FileContainer findContainerForFile(String filename) { + for (FileContainer container : files) { + if (container.getFiles().containsKey(filename)) { + return container; + } + } + return null; + } + /** * Send a message to alert the user * @@ -369,6 +368,25 @@ public static void showMessage(String message) { JOptionPane.showMessageDialog(viewer, message); } + /** + * Ask a yes/no dialog + * @param question the body text of the dialog box to be created + * @param title the window title of the dialog box to be created + * @return true or false for yes or no + */ + public static boolean askYesNoDialog(String question, String title) { + JOptionPane pane = new JOptionPane(question); + Object[] options = new String[]{"Yes", "No"}; + pane.setOptions(options); + JDialog dialog = pane.createDialog(viewer, "JDA - " + title); + dialog.setVisible(true); + Object obj = pane.getValue(); + for (int k = 0; k < options.length; k++) + if (options[k].equals(obj) && k == 0) + return true; + return false; + } + /** * Resets the workspace with optional user input required * @@ -376,15 +394,8 @@ public static void showMessage(String message) { */ public static void resetWorkSpace(boolean ask) { if (ask) { - JOptionPane pane = new JOptionPane("Are you sure you want to reset the workspace?\n\rIt will also reset your file navigator and search."); - Object[] options = new String[]{"Yes", "No"}; - pane.setOptions(options); - JDialog dialog = pane.createDialog(viewer, "JDA - Reset Workspace"); - dialog.setVisible(true); - Object obj = pane.getValue(); - for (int k = 0; k < options.length; k++) - if (options[k].equals(obj) && k != 0) - return; + if (!askYesNoDialog("Are you sure you want to reset the workspace?\n\rIt will also reset your file navigator and search.", "Reset Workspace")) + return; } closeResources(false); @@ -393,15 +404,8 @@ public static void resetWorkSpace(boolean ask) { public static void closeResources(boolean ask) { if (ask) { - JOptionPane pane = new JOptionPane("Are you sure you want to close all resources?"); - Object[] options = new String[]{"Yes", "No"}; - pane.setOptions(options); - JDialog dialog = pane.createDialog(viewer, "JDA - Close Resources"); - dialog.setVisible(true); - Object obj = pane.getValue(); - for (int k = 0; k < options.length; k++) - if (options[k].equals(obj) && k != 0) - return; + if (!askYesNoDialog("Are you sure you want to close all resources?", "Close Resources")) + return; } JDA.setBusy(true); @@ -438,8 +442,6 @@ public static void addRecentFile(File f) { resetRecentFilesMenu(); } - private static ArrayList killList2 = new ArrayList<>(); - /** * resets the recent files menu */ @@ -456,31 +458,10 @@ public static void resetRecentFilesMenu() { } } - public static ArrayList createdRandomizedNames = new ArrayList<>(); - /** - * Ensures it will only return a uniquely generated names, contains a dupe checker to be sure + * Returns the JDA directory * - * @return the unique randomized name of 25 characters. - */ - public static String getRandomizedName() { - boolean generated = false; - String name = ""; - while (!generated) { - String randomizedName = MiscUtils.randomString(25); - if (!createdRandomizedNames.contains(randomizedName)) { - createdRandomizedNames.add(randomizedName); - name = randomizedName; - generated = true; - } - } - return name; - } - - /** - * Returns the BCV directory - * - * @return the static BCV directory + * @return the static JDA directory */ public static String getJDADirectory() { while (!dataDir.exists()) @@ -516,8 +497,6 @@ private static boolean isShiftDown(KeyEvent e) { /** * Checks the hotkeys - * - * @param e */ public static void checkHotKey(KeyEvent e) { if ((e.getKeyCode() == KeyEvent.VK_O) && isCtrlDown(e)) { @@ -583,6 +562,8 @@ public String getDescription() { } } + public static Function> searchCallback = JDA::search; + public static List search(String needle) { List matches = new ArrayList<>(); for (FileContainer fc : JDA.getOpenFiles()) { diff --git a/src/main/java/club/bytecode/the/jda/JarUtils.java b/src/main/java/club/bytecode/the/jda/JarUtils.java index 27123e2..225b6a7 100644 --- a/src/main/java/club/bytecode/the/jda/JarUtils.java +++ b/src/main/java/club/bytecode/the/jda/JarUtils.java @@ -23,9 +23,7 @@ public class JarUtils { public static FileContainer load(final File jarFile) throws IOException { - FileContainer container = new FileContainer(jarFile); HashMap files = new HashMap<>(); - ZipInputStream jis = new ZipInputStream(new FileInputStream(jarFile)); ZipEntry entry; while ((entry = jis.getNextEntry()) != null) { @@ -47,77 +45,7 @@ public static FileContainer load(final File jarFile) throws IOException { } } jis.close(); - container.files = files; - return container; - } - - - public static ArrayList loadClasses(final File jarFile) throws IOException { - ArrayList classes = new ArrayList<>(); - ZipInputStream jis = new ZipInputStream(new FileInputStream(jarFile)); - ZipEntry entry; - while ((entry = jis.getNextEntry()) != null) { - try { - final String name = entry.getName(); - if (name.endsWith(".class")) { - byte[] bytes = getBytes(jis); - String cafebabe = String.format("%02X%02X%02X%02X", bytes[0], bytes[1], bytes[2], bytes[3]); - if (cafebabe.toLowerCase().equals("cafebabe")) { - try { - final ClassNode cn = getNode(bytes); - classes.add(cn); - } catch (Exception e) { - e.printStackTrace(); - } - } else { - System.out.println(jarFile + ">" + name + ": Header does not start with CAFEBABE, ignoring."); - } - } - - } catch (Exception e) { - new ExceptionUI(e, "loading classes from jarfile"); - } finally { - jis.closeEntry(); - } - } - jis.close(); - return classes; - } - - /** - * Loads resources only, just for .APK - * - * @param zipFile the input zip file - * @throws IOException - */ - public static HashMap loadResources(final File zipFile) throws IOException { - if (!zipFile.exists()) - return null; //just ignore - - HashMap files = new HashMap<>(); - - ZipInputStream jis = new ZipInputStream(new FileInputStream(zipFile)); - ZipEntry entry; - while ((entry = jis.getNextEntry()) != null) { - try { - final String name = entry.getName(); - if (!name.endsWith(".class")) { - if (!entry.isDirectory()) - files.put(name, getBytes(jis)); - - jis.closeEntry(); - continue; - } - } catch (Exception e) { - new ExceptionUI(e, "loading resources from file"); - } finally { - jis.closeEntry(); - } - } - jis.close(); - - return files; - + return new FileContainer(jarFile, files); } /** @@ -135,123 +63,9 @@ public static byte[] getBytes(final InputStream is) throws IOException { baos.write(buffer, 0, a); } baos.close(); - buffer = null; return baos.toByteArray(); } - /** - * Creates a new ClassNode instances from the provided byte[] - * - * @param bytez the class file's byte[] - * @return the ClassNode instance - */ - public static ClassNode getNode(final byte[] bytez) { - ClassReader cr = new ClassReader(bytez); - ClassNode cn = new ClassNode(); - try { - cr.accept(cn, ClassReader.EXPAND_FRAMES); - } catch (Exception e) { - try { - cr.accept(cn, ClassReader.SKIP_FRAMES); - } catch (Exception e2) { - e2.printStackTrace(); //just skip it - } - } - cr = null; - return cn; - } - - /** - * Saves as jar with manifest - * - * @param nodeList the loaded ClassNodes - * @param path the exact path of the output jar file - * @param manifest the manifest contents - */ - public static void saveAsJar(ArrayList nodeList, String path, String manifest) { - try { - JarOutputStream out = new JarOutputStream(new FileOutputStream(path)); - for (ClassNode cn : nodeList) { - ClassWriter cw = new ClassWriter(0); - cn.accept(cw); - - out.putNextEntry(new ZipEntry(cn.name + ".class")); - out.write(cw.toByteArray()); - out.closeEntry(); - } - - out.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF")); - out.write((manifest.trim() + "\r\n\r\n").getBytes()); - out.closeEntry(); - - for (FileContainer container : JDA.getOpenFiles()) - for (Entry entry : container.files.entrySet()) { - String filename = entry.getKey(); - if (!filename.startsWith("META-INF")) { - out.putNextEntry(new ZipEntry(filename)); - out.write(entry.getValue()); - out.closeEntry(); - } - } - - out.close(); - } catch (IOException e) { - new ExceptionUI(e, "saving as jar"); - } - } - - /** - * Saves a jar without the manifest - * - * @param nodeList The loaded ClassNodes - * @param path the exact jar output path - */ - public static void saveAsJarClassesOnly(ArrayList nodeList, String path) { - try { - JarOutputStream out = new JarOutputStream(new FileOutputStream(path)); - ArrayList noDupe = new ArrayList<>(); - for (ClassNode cn : nodeList) { - ClassWriter cw = new ClassWriter(0); - cn.accept(cw); - - String name = cn.name + ".class"; - - if (!noDupe.contains(name)) { - noDupe.add(name); - out.putNextEntry(new ZipEntry(name)); - out.write(cw.toByteArray()); - out.closeEntry(); - } - } - - noDupe.clear(); - out.close(); - } catch (IOException e) { - new ExceptionUI(e, "saving as jar"); - } - } - - public static void saveAsJarClassesOnly(Map nodeList, String path) { - try { - JarOutputStream out = new JarOutputStream(new FileOutputStream(path)); - ArrayList noDupe = new ArrayList<>(); - for (Entry cn : nodeList.entrySet()) { - String name = cn.getKey(); - if (!noDupe.contains(name)) { - noDupe.add(name); - out.putNextEntry(new ZipEntry(name)); - out.write(cn.getValue()); - out.closeEntry(); - } - } - - noDupe.clear(); - out.close(); - } catch (IOException e) { - new ExceptionUI(e, "saving as jar"); - } - } - public static void saveAsJar(Map nodeList, String path) { try { JarOutputStream out = new JarOutputStream(new FileOutputStream(path)); diff --git a/src/main/java/club/bytecode/the/jda/Resources.java b/src/main/java/club/bytecode/the/jda/Resources.java index 581fc0e..726a413 100644 --- a/src/main/java/club/bytecode/the/jda/Resources.java +++ b/src/main/java/club/bytecode/the/jda/Resources.java @@ -15,35 +15,35 @@ */ public class Resources { - public static ImageIcon nextIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/nextIcon.png")); - public static ImageIcon prevIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/prevIcon.png")); - public static ImageIcon busyIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/1.gif")); - public static ImageIcon busyB64Icon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/busyIcon2.gif")); - public static ImageIcon batIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/bat.png")); - public static ImageIcon shIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/sh.png")); - public static ImageIcon csharpIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/c#.png")); - public static ImageIcon cplusplusIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/c++.png")); - public static ImageIcon configIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/config.png")); - public static ImageIcon jarIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/jar.png")); - public static ImageIcon zipIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/zip.png")); - public static ImageIcon packagesIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/package.png")); - public static ImageIcon folderIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/folder.png")); - public static ImageIcon fileIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/file.png")); - public static ImageIcon textIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/text.png")); - public static ImageIcon classIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/class.png")); - public static ImageIcon imageIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/image.png")); - public static ImageIcon decodedIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/decoded.png")); - public static ImageIcon javaIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/java.png")); - - public static ImageIcon fileNavigatorIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/icon.png")); - public static ImageIcon fileViewerIcon = new ImageIcon(Resources.class.getClass().getResource("/club/bytecode/the/jda/images/icon.png")); + public static ImageIcon nextIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/nextIcon.png")); + public static ImageIcon prevIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/prevIcon.png")); + public static ImageIcon busyIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/1.gif")); + public static ImageIcon busyB64Icon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/busyIcon2.gif")); + public static ImageIcon batIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/bat.png")); + public static ImageIcon shIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/sh.png")); + public static ImageIcon csharpIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/c#.png")); + public static ImageIcon cplusplusIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/c++.png")); + public static ImageIcon configIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/config.png")); + public static ImageIcon jarIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/jar.png")); + public static ImageIcon zipIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/zip.png")); + public static ImageIcon packagesIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/package.png")); + public static ImageIcon folderIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/folder.png")); + public static ImageIcon fileIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/file.png")); + public static ImageIcon textIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/text.png")); + public static ImageIcon classIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/class.png")); + public static ImageIcon imageIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/image.png")); + public static ImageIcon decodedIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/decoded.png")); + public static ImageIcon javaIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/java.png")); + + public static ImageIcon fileNavigatorIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/icon.png")); + public static ImageIcon fileViewerIcon = new ImageIcon(Resources.class.getResource("/club/bytecode/the/jda/images/icon.png")); public static ArrayList iconList; public static BufferedImage icon; static { try { - icon = ImageIO.read(Resources.class.getClass().getResourceAsStream("/club/bytecode/the/jda/images/icon.png")); + icon = ImageIO.read(Resources.class.getResourceAsStream("/club/bytecode/the/jda/images/icon.png")); } catch (IOException e) { System.err.println("Failed to load program icon:"); e.printStackTrace(); diff --git a/src/main/java/club/bytecode/the/jda/decompilers/FernflowerDecompiler.java b/src/main/java/club/bytecode/the/jda/decompilers/FernflowerDecompiler.java index 212ff90..0d8673e 100644 --- a/src/main/java/club/bytecode/the/jda/decompilers/FernflowerDecompiler.java +++ b/src/main/java/club/bytecode/the/jda/decompilers/FernflowerDecompiler.java @@ -76,7 +76,7 @@ public String decompileClassNode(FileContainer container, final ClassNode cn) { requestedCn = container.loadClassFile(container.findClassfile(className)); if (requestedCn == null) { System.err.println("Couldn't load " + externalPath); - throw new IOException(container + "$" + cn + " is missing"); + throw new IOException(container + "$" + cn.name + " is missing"); } applyFilters(requestedCn); classCache.put(className, requestedCn); diff --git a/src/main/java/club/bytecode/the/jda/decompilers/JDADecompiler.java b/src/main/java/club/bytecode/the/jda/decompilers/JDADecompiler.java index 02cf1d3..d358ff5 100644 --- a/src/main/java/club/bytecode/the/jda/decompilers/JDADecompiler.java +++ b/src/main/java/club/bytecode/the/jda/decompilers/JDADecompiler.java @@ -11,7 +11,7 @@ import java.io.StringWriter; /** - * Used to represent all of the decompilers/disassemblers BCV contains. + * Used to represent all of the decompilers/disassemblers JDA contains. * * @author Konloch */ diff --git a/src/main/java/club/bytecode/the/jda/decompilers/ProcyonDecompiler.java b/src/main/java/club/bytecode/the/jda/decompilers/ProcyonDecompiler.java index a2de140..f409994 100644 --- a/src/main/java/club/bytecode/the/jda/decompilers/ProcyonDecompiler.java +++ b/src/main/java/club/bytecode/the/jda/decompilers/ProcyonDecompiler.java @@ -78,7 +78,6 @@ public DecompilerSettings getDecompilerSettings() { public String decompileClassNode(FileContainer container, final ClassNode cn) { try { byte[] bytes = JDA.getClassBytes(container, cn); - final Map loadedClasses = JDA.getLoadedBytes(); MetadataSystem metadataSystem = new MetadataSystem(new ITypeLoader() { private InputTypeLoader backLoader = new InputTypeLoader(); @@ -89,14 +88,17 @@ public boolean tryLoadType(String s, Buffer buffer) { buffer.position(0); return true; } else { - byte[] toUse = loadedClasses.get(s + ".class"); - if (toUse != null) { - buffer.putByteArray(toUse, 0, toUse.length); - buffer.position(0); - return true; - } else { - return backLoader.tryLoadType(s, buffer); + String classFilename = s + ".class"; + FileContainer otherContainer = JDA.findContainerForFile(classFilename); + if (otherContainer != null) { + byte[] toUse = otherContainer.getFiles().get(classFilename); + if (toUse != null) { + buffer.putByteArray(toUse, 0, toUse.length); + buffer.position(0); + return true; + } } + return backLoader.tryLoadType(s, buffer); } } }); diff --git a/src/main/java/club/bytecode/the/jda/decompilers/bytecode/InstructionPrinter.java b/src/main/java/club/bytecode/the/jda/decompilers/bytecode/InstructionPrinter.java index 9333399..c2ff402 100644 --- a/src/main/java/club/bytecode/the/jda/decompilers/bytecode/InstructionPrinter.java +++ b/src/main/java/club/bytecode/the/jda/decompilers/bytecode/InstructionPrinter.java @@ -17,13 +17,13 @@ * @author Bibl */ public class InstructionPrinter { - private final MethodNodeDecompiler parent; + protected final MethodNodeDecompiler parent; /** * The MethodNode to print **/ - protected MethodNode mNode; - private TypeAndName[] args; + protected final MethodNode mNode; + protected final TypeAndName[] args; protected int[] pattern; protected boolean match; @@ -142,7 +142,7 @@ public ArrayList createPrint() { } else if (ain instanceof MultiANewArrayInsnNode) { line = printMultiANewArrayInsnNode((MultiANewArrayInsnNode) ain); } else { - line = "// UNADDED OPCODE: " + nameOpcode(ain.opcode()) + " " + ain.toString(); + line = "// UNADDED OPCODE: " + nameOpcode(ain.getOpcode()) + " " + ain.toString(); } if (!line.equals("")) { if (match) @@ -159,7 +159,7 @@ public ArrayList createPrint() { protected String printVarInsnNode(VarInsnNode vin, ListIterator it) { StringBuilder sb = new StringBuilder(); - sb.append(nameOpcode(vin.opcode())); + sb.append(nameOpcode(vin.getOpcode())); sb.append(" "); sb.append(vin.var); if (parent.createComments()) { @@ -177,31 +177,19 @@ protected String printVarInsnNode(VarInsnNode vin, ListIterator it) { } protected String printIntInsnNode(IntInsnNode iin, ListIterator it) { - return nameOpcode(iin.opcode()) + " " + iin.operand; + return nameOpcode(iin.getOpcode()) + " " + iin.operand; } protected String printFieldInsnNode(FieldInsnNode fin, ListIterator it) { - String desc = Type.getType(fin.desc).getClassName(); - if (desc == null || desc.equals("null")) - desc = fin.desc; - return nameOpcode(fin.opcode()) + " " + fin.owner + "." + fin.name + ":" + desc; + String desc = fin.desc; + return nameOpcode(fin.getOpcode()) + " " + fin.owner + "." + fin.name + ":" + desc; } protected String printMethodInsnNode(MethodInsnNode min, ListIterator it) { StringBuilder sb = new StringBuilder(); - sb.append(nameOpcode(min.opcode())).append(" ").append(min.owner).append(" ").append(min.name).append("("); + sb.append(nameOpcode(min.getOpcode())).append(" ").append(min.owner).append(" ").append(min.name).append("("); String desc = min.desc; - try { - if (Type.getType(min.desc) != null) - desc = Type.getType(min.desc).getClassName(); - - if (desc == null || desc.equals("null")) - desc = min.desc; - } catch (java.lang.ArrayIndexOutOfBoundsException e) { - - } - sb.append(desc); sb.append(");"); @@ -211,17 +199,17 @@ protected String printMethodInsnNode(MethodInsnNode min, ListIterator it) { protected String printLdcInsnNode(LdcInsnNode ldc, ListIterator it) { if (ldc.cst instanceof String) - return nameOpcode(ldc.opcode()) + " \"" + StringEscapeUtils.escapeJava(ldc.cst.toString()) + "\" (" + ldc.cst.getClass().getCanonicalName() + ")"; + return nameOpcode(ldc.getOpcode()) + " \"" + StringEscapeUtils.escapeJava(ldc.cst.toString()) + "\" (" + ldc.cst.getClass().getCanonicalName() + ")"; - return nameOpcode(ldc.opcode()) + " " + StringEscapeUtils.escapeJava(ldc.cst.toString()) + " (" + ldc.cst.getClass().getCanonicalName() + ")"; + return nameOpcode(ldc.getOpcode()) + " " + StringEscapeUtils.escapeJava(ldc.cst.toString()) + " (" + ldc.cst.getClass().getCanonicalName() + ")"; } protected String printInsnNode(InsnNode in, ListIterator it) { - return nameOpcode(in.opcode()); + return nameOpcode(in.getOpcode()); } protected String printJumpInsnNode(JumpInsnNode jin, ListIterator it) { - String line = nameOpcode(jin.opcode()) + " L" + resolveLabel(jin.label); + String line = nameOpcode(jin.getOpcode()) + " L" + resolveLabel(jin.label); return line; } @@ -236,16 +224,7 @@ protected String printLabelnode(LabelNode label) { protected String printTypeInsnNode(TypeInsnNode tin) { try { String desc = tin.desc; - try { - if (Type.getType(tin.desc) != null) - desc = Type.getType(tin.desc).getClassName(); - - if (desc == null || desc.equals("null")) - desc = tin.desc; - } catch (java.lang.ArrayIndexOutOfBoundsException | UnsupportedOperationException e) { - - } - return nameOpcode(tin.opcode()) + " " + desc; + return nameOpcode(tin.getOpcode()) + " " + desc; } catch (Exception e) { new ExceptionUI(e, "printing instruction"); } @@ -253,11 +232,11 @@ protected String printTypeInsnNode(TypeInsnNode tin) { } protected String printIincInsnNode(IincInsnNode iin) { - return nameOpcode(iin.opcode()) + " " + iin.var + " " + iin.incr; + return nameOpcode(iin.getOpcode()) + " " + iin.var + " " + iin.incr; } protected String printTableSwitchInsnNode(TableSwitchInsnNode tin) { - String line = nameOpcode(tin.opcode()) + " \n"; + String line = nameOpcode(tin.getOpcode()) + " \n"; List labels = tin.labels; int count = 0; for (int i = tin.min; i < tin.max + 1; i++) { @@ -268,7 +247,7 @@ protected String printTableSwitchInsnNode(TableSwitchInsnNode tin) { } protected String printLookupSwitchInsnNode(LookupSwitchInsnNode lin) { - String line = nameOpcode(lin.opcode()) + ": \n"; + String line = nameOpcode(lin.getOpcode()) + ": \n"; List keys = lin.keys; List labels = lin.labels; @@ -284,20 +263,9 @@ protected String printLookupSwitchInsnNode(LookupSwitchInsnNode lin) { protected String printInvokeDynamicInsNode(InvokeDynamicInsnNode idin) { StringBuilder sb = new StringBuilder(); final String bsmName = idin.bsm.getName(); - sb.append(nameOpcode(idin.opcode())).append(" ").append(bsmName).append("<"); + sb.append(nameOpcode(idin.getOpcode())).append(" ").append(bsmName).append("<"); String desc = idin.desc; - String partedDesc = idin.desc.substring(2); - try { - if (Type.getType(partedDesc) != null) - desc = Type.getType(partedDesc).getClassName(); - - if (desc == null || desc.equals("null")) - desc = idin.desc; - } catch (java.lang.ArrayIndexOutOfBoundsException | UnsupportedOperationException e) { - - } - sb.append(desc); sb.append(">(\n"); Object[] bsmArgs = idin.bsmArgs; @@ -328,11 +296,11 @@ protected String printInvokeDynamicInsNode(InvokeDynamicInsnNode idin) { } protected String printMultiANewArrayInsnNode(MultiANewArrayInsnNode manain) { - return nameOpcode(manain.opcode()) + " " + manain.desc; + return nameOpcode(manain.getOpcode()) + " " + manain.desc; } protected String nameOpcode(int opcode) { - return " " + OpcodeInfo.OPCODES.get(opcode).toLowerCase(); + return " " + OpcodeInfo.OPCODES.getOrDefault(opcode, String.format("unknown#%02x", opcode)).toLowerCase(); } protected int resolveLabel(LabelNode label) { diff --git a/src/main/java/club/bytecode/the/jda/decompilers/bytecode/MethodNodeDecompiler.java b/src/main/java/club/bytecode/the/jda/decompilers/bytecode/MethodNodeDecompiler.java index e5d43fa..ef84168 100644 --- a/src/main/java/club/bytecode/the/jda/decompilers/bytecode/MethodNodeDecompiler.java +++ b/src/main/java/club/bytecode/the/jda/decompilers/bytecode/MethodNodeDecompiler.java @@ -15,10 +15,11 @@ */ public class MethodNodeDecompiler { - private final BytecodeDecompiler parent; + protected final BytecodeDecompiler parent; protected final PrefixedStringBuilder sb; protected final MethodNode mn; protected final ClassNode cn; + protected boolean printDetailedMetadata = true; public MethodNodeDecompiler(BytecodeDecompiler parent, PrefixedStringBuilder sb, MethodNode mn, ClassNode cn) { this.parent = parent; @@ -41,7 +42,7 @@ public PrefixedStringBuilder decompile() { // Descriptor if (createDescriptors()) { sb.append(" // "); - sb.append(mn.owner.name); + sb.append(cn.name); sb.append("."); sb.append(mn.name); sb.append(mn.desc); @@ -134,31 +135,33 @@ else if (mn.name.equals("")) InstructionPrinter insnPrinter = getInstructionPrinter(mn, args); - addAttrList(mn.attrs, "attr", sb, insnPrinter); - addAttrList(mn.invisibleAnnotations, "invisAnno", sb, insnPrinter); - addAttrList(mn.invisibleAnnotations, "invisLocalVarAnno", sb, insnPrinter); - addAttrList(mn.invisibleTypeAnnotations, "invisTypeAnno", sb, insnPrinter); - addAttrList(mn.localVariables, "localVar", sb, insnPrinter); - addAttrList(mn.visibleAnnotations, "visAnno", sb, insnPrinter); - addAttrList(mn.visibleLocalVariableAnnotations, "visLocalVarAnno", sb, insnPrinter); - addAttrList(mn.visibleTypeAnnotations, "visTypeAnno", sb, insnPrinter); - - // Exception table - for (Object o : mn.tryCatchBlocks) { - TryCatchBlockNode tcbn = (TryCatchBlockNode) o; - sb.append(" "); - sb.append("TryCatch: L"); - sb.append(insnPrinter.resolveLabel(tcbn.start)); - sb.append(" to L"); - sb.append(insnPrinter.resolveLabel(tcbn.end)); - sb.append(" handled by L"); - sb.append(insnPrinter.resolveLabel(tcbn.handler)); - sb.append(": "); - if (tcbn.type != null) - sb.append(tcbn.type); - else - sb.append("Type is null."); - sb.append(JDA.nl); + if (printDetailedMetadata) { + addAttrList(mn.attrs, "attr", sb, insnPrinter); + addAttrList(mn.invisibleAnnotations, "invisAnno", sb, insnPrinter); + addAttrList(mn.invisibleAnnotations, "invisLocalVarAnno", sb, insnPrinter); + addAttrList(mn.invisibleTypeAnnotations, "invisTypeAnno", sb, insnPrinter); + addAttrList(mn.localVariables, "localVar", sb, insnPrinter); + addAttrList(mn.visibleAnnotations, "visAnno", sb, insnPrinter); + addAttrList(mn.visibleLocalVariableAnnotations, "visLocalVarAnno", sb, insnPrinter); + addAttrList(mn.visibleTypeAnnotations, "visTypeAnno", sb, insnPrinter); + + // Exception table + for (Object o : mn.tryCatchBlocks) { + TryCatchBlockNode tcbn = (TryCatchBlockNode) o; + sb.append(" "); + sb.append("TryCatch: L"); + sb.append(insnPrinter.resolveLabel(tcbn.start)); + sb.append(" to L"); + sb.append(insnPrinter.resolveLabel(tcbn.end)); + sb.append(" handled by L"); + sb.append(insnPrinter.resolveLabel(tcbn.handler)); + sb.append(": "); + if (tcbn.type != null) + sb.append(tcbn.type); + else + sb.append("Type is null."); + sb.append(JDA.nl); + } } // Instructions @@ -269,4 +272,8 @@ boolean createDescriptors() { boolean appendHandlerComments() { return parent.getSettings().getEntry("append-handler-comments").getBool(); } + + public BytecodeDecompiler getParent() { + return parent; + } } diff --git a/src/main/java/club/bytecode/the/jda/decompilers/bytecode/OpcodeInfo.java b/src/main/java/club/bytecode/the/jda/decompilers/bytecode/OpcodeInfo.java index 6e084de..903aca9 100644 --- a/src/main/java/club/bytecode/the/jda/decompilers/bytecode/OpcodeInfo.java +++ b/src/main/java/club/bytecode/the/jda/decompilers/bytecode/OpcodeInfo.java @@ -72,7 +72,7 @@ public static final int[] toOpcodes(String opcodeString) { public static final String opcodesToString(AbstractInsnNode[] ains) { String[] ops = new String[ains.length]; for(int i = 0; i < ains.length; i++) { - ops[i] = OPCODES.get(ains[i].opcode()); + ops[i] = OPCODES.get(ains[i].getOpcode()); } return Arrays.toString(ops).replace("[", "").replace("]", ""); } diff --git a/src/main/java/club/bytecode/the/jda/decompilers/filter/DecompileFilters.java b/src/main/java/club/bytecode/the/jda/decompilers/filter/DecompileFilters.java index 15fcd21..1adbff4 100644 --- a/src/main/java/club/bytecode/the/jda/decompilers/filter/DecompileFilters.java +++ b/src/main/java/club/bytecode/the/jda/decompilers/filter/DecompileFilters.java @@ -10,6 +10,7 @@ public class DecompileFilters { public static void registerFilter(DecompileFilter filter) { BY_NAME.put(filter.getFullName(), filter); + System.out.println("Decompile filter registered: " + filter.getFullName()); } public static Collection getAllFilters() { diff --git a/src/main/java/club/bytecode/the/jda/decompilers/filter/DropLocalVariableTableFilter.java b/src/main/java/club/bytecode/the/jda/decompilers/filter/DropLocalVariableTableFilter.java new file mode 100644 index 0000000..d5dee38 --- /dev/null +++ b/src/main/java/club/bytecode/the/jda/decompilers/filter/DropLocalVariableTableFilter.java @@ -0,0 +1,22 @@ +package club.bytecode.the.jda.decompilers.filter; + +import club.bytecode.the.jda.JDA; +import club.bytecode.the.jda.api.JDANamespace; +import org.objectweb.asm.tree.ClassNode; + +public class DropLocalVariableTableFilter implements DecompileFilter { + @Override + public void process(ClassNode cn) { + cn.methods.forEach(methodNode -> methodNode.localVariables.clear()); + } + + @Override + public String getName() { + return "Drop local variable table"; + } + + @Override + public JDANamespace getNamespace() { + return JDA.namespace; + } +} diff --git a/src/main/java/club/bytecode/the/jda/decompilers/filter/IllegalAnnotationFilter.java b/src/main/java/club/bytecode/the/jda/decompilers/filter/IllegalAnnotationFilter.java new file mode 100644 index 0000000..bc8233d --- /dev/null +++ b/src/main/java/club/bytecode/the/jda/decompilers/filter/IllegalAnnotationFilter.java @@ -0,0 +1,28 @@ +package club.bytecode.the.jda.decompilers.filter; + +import club.bytecode.the.jda.JDA; +import club.bytecode.the.jda.api.JDANamespace; +import org.objectweb.asm.tree.ClassNode; + +public class IllegalAnnotationFilter implements DecompileFilter { + @Override + public void process(ClassNode cn) { + cn.methods.forEach(methodNode -> { + if (methodNode.invisibleAnnotations != null) + methodNode.invisibleAnnotations.removeIf(node -> node.desc.equals("@")); + }); + + if (cn.invisibleAnnotations != null) + cn.invisibleAnnotations.removeIf(node -> node.desc.equals("@")); + } + + @Override + public String getName() { + return "Kill illegal annotations"; + } + + @Override + public JDANamespace getNamespace() { + return JDA.namespace; + } +} diff --git a/src/main/java/club/bytecode/the/jda/gui/MainViewerGUI.java b/src/main/java/club/bytecode/the/jda/gui/MainViewerGUI.java index eb1df4f..90d52e3 100644 --- a/src/main/java/club/bytecode/the/jda/gui/MainViewerGUI.java +++ b/src/main/java/club/bytecode/the/jda/gui/MainViewerGUI.java @@ -40,13 +40,13 @@ public class MainViewerGUI extends JFrame implements IPersistentWindow { public final ButtonGroup[] panelGroups = new ButtonGroup[NUM_PANEL_GROUPS]; public JMenuBar menuBar; - public JMenu viewMenu; public JMenu fileMenu; - public JMenu windowMenu; public JMenu editMenu; + public JMenu searchMenu; + public JMenu viewMenu; + public JMenu windowMenu; public JMenu optionsMenu; public JMenu helpMenu; - public JMenu pluginsMenu; public boolean isMaximized = false; public Point unmaximizedPos; @@ -66,6 +66,7 @@ public class MainViewerGUI extends JFrame implements IPersistentWindow { private JMenuItem spinnerMenu = new JMenuItem(""); public FontOptionsDialog fontOptionsDialog = new FontOptionsDialog(); + public MainViewerGUI() { initializeWindows(); @@ -132,8 +133,6 @@ public void componentMoved(ComponentEvent e) { unmaximizedPos = getLocation(); this.setLocationRelativeTo(null); - - JDA.onGUILoad(); } private void initializeMenubar() { @@ -221,7 +220,7 @@ private void initializeMenubar() { // ------------------------------------------------------------------------------------------- // Plugins menu - pluginsMenu = new JMenu("Plugins"); + JMenu pluginsMenu = new JMenu("Plugins"); editMenu.add(pluginsMenu); for (JDAPlugin plugin : JDA.getLoadedPlugins()) { JMenuItem button = new JMenuItem(plugin.getName()); @@ -232,9 +231,9 @@ private void initializeMenubar() { // =========================================================================================== // Search menu // =========================================================================================== - JMenu searchMenu = new JMenu("Search"); + searchMenu = new JMenu("Search"); editMenu.add(searchMenu); - JMenuItem constantButton = new JMenuItem("Raw constant"); + JMenuItem constantButton = new JMenuItem("Binary constant"); constantButton.addActionListener((e) -> doSearchDialog()); searchMenu.add(constantButton); menuBar.add(searchMenu); @@ -474,18 +473,7 @@ public void refreshView() { } public void reloadResources() { - JOptionPane pane = new JOptionPane("Are you sure you wish to reload the resources?"); - Object[] options = new String[]{"Yes", "No"}; - pane.setOptions(options); - JDialog dialog = pane.createDialog(JDA.viewer, "JDA - Reload Resources"); - 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; - - if (result == 0) { + if (JDA.askYesNoDialog("Are you sure you wish to reload the resources?", "Reload Resources")) { List reopenContainers = new ArrayList<>(); for (FileContainer container : JDA.getOpenFiles()) reopenContainers.add(container.file); @@ -511,21 +499,19 @@ public void reloadResources() { } private void saveAsZip() { - if (JDA.getLoadedBytes().isEmpty()) { + if (JDA.getOpenFiles().isEmpty()) { JDA.showMessage("First open a class, jar, or zip file."); return; } - (new Thread(() -> { - })).start(); + JDA.showMessage("This feature hasn't been implemented yet. Please submit an issue if you are interested!"); } private void saveAsRunnableJar() { - if (JDA.getLoadedBytes().isEmpty()) { + if (JDA.getOpenFiles().isEmpty()) { JDA.showMessage("First open a class, jar, or zip file."); return; } - (new Thread(() -> { - })).start(); + JDA.showMessage("This feature hasn't been implemented yet. Please submit an issue if you are interested!"); } private void decompileSaveOpenedClasses() { @@ -533,8 +519,7 @@ private void decompileSaveOpenedClasses() { JDA.showMessage("First open a class, jar, or zip file."); return; } - (new Thread(() -> { - })).start(); + JDA.showMessage("This feature hasn't been implemented yet. Please submit an issue if you are interested!"); } private void decompileSaveAllClasses() { @@ -542,23 +527,11 @@ private void decompileSaveAllClasses() { JDA.showMessage("First open a class, jar, or zip file."); return; } - (new Thread(() -> { - })).start(); + JDA.showMessage("This feature hasn't been implemented yet. Please submit an issue if you are interested!"); } private void exitPrompt() { - JOptionPane pane = new JOptionPane("Are you sure you want to exit?"); - Object[] options = new String[]{"Yes", "No"}; - pane.setOptions(options); - JDialog dialog = pane.createDialog(JDA.viewer, "JDA - Exit"); - 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; - - if (result == 0) { + if (JDA.askYesNoDialog("Are you sure you want to exit?", "Exit")) { System.exit(0); } } diff --git a/src/main/java/club/bytecode/the/jda/gui/components/SortedUniqueListModel.java b/src/main/java/club/bytecode/the/jda/gui/components/SortedUniqueListModel.java new file mode 100644 index 0000000..94d1e1e --- /dev/null +++ b/src/main/java/club/bytecode/the/jda/gui/components/SortedUniqueListModel.java @@ -0,0 +1,105 @@ +package club.bytecode.the.jda.gui.components; + +import javax.swing.*; +import java.util.Collection; +import java.util.Iterator; +import java.util.SortedSet; +import java.util.TreeSet; + +public class SortedUniqueListModel extends AbstractListModel implements Iterable { + SortedSet model; + private boolean deferringUpdates; + + public SortedUniqueListModel() { + model = new TreeSet<>(); + } + + public int getSize() { + return model.size(); + } + + // performance hack + public void deferUpdates() { + deferringUpdates = true; + } + + public void commitUpdates() { + deferringUpdates = false; + fireUpdate(); + } + + public T getElementAt(int index) { + Iterator it = model.iterator(); + for (int i = 0; i < index; i++) + if (!it.hasNext()) return null; + else it.next(); + if (!it.hasNext()) return null; + return it.next(); + } + + public void add(T element) { + if (model.add(element)) { + fireUpdate(); + } + } + + public void fireUpdate() { + if (deferringUpdates) + return; + fireContentsChanged(this, 0, getSize()); + } + + public void addAll(Collection elements) { + model.addAll(elements); + fireUpdate(); + } + + public void clear() { + model.clear(); + fireUpdate(); + } + + public boolean contains(T element) { + return model.contains(element); + } + + public T firstElement() { + return model.first(); + } + + @Override + public Iterator iterator() { + return new Iterator() { + Iterator delegate = model.iterator(); + + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + @Override + public T next() { + return delegate.next(); + } + + @Override + public void remove() { + delegate.remove(); + fireUpdate(); + } + }; + } + + public T lastElement() { + return model.last(); + } + + public boolean removeElement(T element) { + boolean removed = model.remove(element); + if (removed) { + fireUpdate(); + } + return removed; + } +} + diff --git a/src/main/java/club/bytecode/the/jda/gui/dialogs/AboutWindow.java b/src/main/java/club/bytecode/the/jda/gui/dialogs/AboutWindow.java index bb75f97..062cbb5 100644 --- a/src/main/java/club/bytecode/the/jda/gui/dialogs/AboutWindow.java +++ b/src/main/java/club/bytecode/the/jda/gui/dialogs/AboutWindow.java @@ -38,7 +38,7 @@ public AboutWindow() { try { String text = IOUtils.toString(Resources.class.getResourceAsStream("/club/bytecode/the/jda/html/about.html"), "UTF-8"); text = text.replace("$JDA_VERSION$", JDA.version); - text = text.replace("$JDA_ICON$", Resources.class.getClass().getResource("/club/bytecode/the/jda/images/icon.png").toString()); + text = text.replace("$JDA_ICON$", Resources.class.getResource("/club/bytecode/the/jda/images/icon.png").toString()); editorPane.setText(text); } catch (IOException e) { System.err.println("Couldn't load about html:"); diff --git a/src/main/java/club/bytecode/the/jda/gui/fileviewer/BytecodeTokenizer.flex b/src/main/java/club/bytecode/the/jda/gui/fileviewer/BytecodeTokenizer.flex index 01afd5d..352c955 100644 --- a/src/main/java/club/bytecode/the/jda/gui/fileviewer/BytecodeTokenizer.flex +++ b/src/main/java/club/bytecode/the/jda/gui/fileviewer/BytecodeTokenizer.flex @@ -1,3 +1,6 @@ +// COMPILE THIS FILE WITH GRAMMARKIT PLUGIN FOR INTELLIJ +// (JFLEX 1.7.0) +// OR ELSE IT WON'T FUCKING WORK package club.bytecode.the.jda.gui.fileviewer; import java.io.*; @@ -5,7 +8,6 @@ import javax.swing.text.Segment; import org.fife.ui.rsyntaxtextarea.*; - /** * Scanner the JDA Bytecode.

* @@ -64,7 +66,6 @@ import org.fife.ui.rsyntaxtextarea.*; public BytecodeTokenizer() { } - /** * Adds the token specified to the current linked list of tokens. * @@ -73,7 +74,7 @@ import org.fife.ui.rsyntaxtextarea.*; */ private void addHyperlinkToken(int start, int end, int tokenType) { int so = start + offsetShift; - addToken(zzBuffer, start,end, tokenType, so, true); + addToken(zzBuffer.toString().toCharArray(), start,end, tokenType, so, true); } @@ -95,7 +96,7 @@ import org.fife.ui.rsyntaxtextarea.*; */ private void addToken(int start, int end, int tokenType) { int so = start + offsetShift; - addToken(zzBuffer, start,end, tokenType, so, false); + addToken(zzBuffer.toString().toCharArray(), start,end, tokenType, so, false); } @@ -142,18 +143,18 @@ import org.fife.ui.rsyntaxtextarea.*; public Token getTokenList(Segment text, int initialTokenType, int startOffset) { resetTokenList(); - this.offsetShift = -text.offset + startOffset; + this.offsetShift = startOffset; // Start off in the proper state. int state = Token.NULL; switch (initialTokenType) { case Token.COMMENT_MULTILINE: state = MLC; - start = text.offset; + start = 0; break; case Token.COMMENT_DOCUMENTATION: state = DOCCOMMENT; - start = text.offset; + start = 0; break; default: state = Token.NULL; @@ -161,8 +162,7 @@ import org.fife.ui.rsyntaxtextarea.*; s = text; try { - yyreset(zzReader); - yybegin(state); + reset(text, 0, text.count, state); return yylex(); } catch (IOException ioe) { ioe.printStackTrace(); @@ -172,51 +172,6 @@ import org.fife.ui.rsyntaxtextarea.*; } - /** - * Refills the input buffer. - * - * Note: this is the correct zzRefill. Delete the other one. - * - * @return true if EOF was reached, otherwise - * false. - */ - private boolean zzRefill() { - return zzCurrentPos>=s.offset+s.count; - } - - - /** - * Resets the scanner to read from a new input stream. - * Does not close the old reader. - * - * All internal variables are reset, the old input stream - * cannot be reused (internal buffer is discarded and lost). - * Lexical state is set to YY_INITIAL. - * - * Note: this is the correct yyreset. Delete the other one. - * - * @param reader the new input stream - */ - public final void yyreset(Reader reader) { - // 's' has been updated. - zzBuffer = s.array; - /* - * We replaced the line below with the two below it because zzRefill - * no longer "refills" the buffer (since the way we do it, it's always - * "full" the first time through, since it points to the segment's - * array). So, we assign zzEndRead here. - */ - zzStartRead = zzEndRead = s.offset; - zzStartRead = s.offset; - zzEndRead = zzStartRead + s.count - 1; - zzCurrentPos = zzMarkedPos = s.offset; - zzLexicalState = YYINITIAL; - zzReader = reader; - zzAtBOL = true; - zzAtEOF = false; - } - - %} Letter = ([A-Za-z]) @@ -346,6 +301,7 @@ URL = (((https?|f(tp|ile))"://"|"www.")({URLCharacters}{URLEndCharacter})?) "super" | "switch" | "synchronized" | + "synthetic" | "this" | "throw" | "throws" | diff --git a/src/main/java/club/bytecode/the/jda/gui/fileviewer/BytecodeTokenizer.java b/src/main/java/club/bytecode/the/jda/gui/fileviewer/BytecodeTokenizer.java index 575fe60..aad2c41 100644 --- a/src/main/java/club/bytecode/the/jda/gui/fileviewer/BytecodeTokenizer.java +++ b/src/main/java/club/bytecode/the/jda/gui/fileviewer/BytecodeTokenizer.java @@ -1,5 +1,8 @@ -/* The following code was generated by JFlex 1.6.1 */ +/* The following code was generated by JFlex 1.7.0 tweaked for IntelliJ platform */ +// COMPILE THIS FILE WITH GRAMMARKIT PLUGIN FOR INTELLIJ +// (JFLEX 1.7.0) +// OR ELSE IT WON'T FUCKING WORK package club.bytecode.the.jda.gui.fileviewer; import java.io.*; @@ -7,7 +10,6 @@ import org.fife.ui.rsyntaxtextarea.*; - /** * Scanner the JDA Bytecode.

* @@ -73,23 +75,30 @@ public class BytecodeTokenizer extends AbstractJFlexCTokenMaker { /** * Translates characters to character classes + * Chosen bits are [9, 6, 6] + * Total runtime size is 1568 bytes */ - private static final String ZZ_CMAP_PACKED = - "\11\0\1\25\1\11\1\26\1\27\1\24\22\0\1\25\1\55\1\16"+ - "\1\21\1\23\1\55\1\57\1\10\2\101\1\31\1\51\1\50\1\40"+ - "\1\41\1\30\1\4\1\5\1\103\1\17\1\104\1\104\2\7\2\3"+ - "\1\60\1\50\1\52\1\53\1\54\1\56\1\100\1\6\1\33\1\6"+ - "\1\71\1\37\1\36\5\1\1\22\5\1\1\76\5\1\1\35\2\1"+ - "\1\101\1\12\1\101\1\20\1\2\1\0\1\45\1\34\1\65\1\63"+ - "\1\44\1\15\1\74\1\61\1\67\1\106\1\77\1\32\1\70\1\14"+ - "\1\62\1\64\1\105\1\43\1\46\1\42\1\13\1\73\1\72\1\66"+ - "\1\75\1\102\1\107\1\20\1\47\1\56\6\0\1\26\u1fa2\0\1\26"+ - "\1\26\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\uffff\0\udfe6\0"; + public static int ZZ_CMAP(int ch) { + return ZZ_CMAP_A[(ZZ_CMAP_Y[ZZ_CMAP_Z[ch>>12]|((ch>>6)&0x3f)]<<6)|(ch&0x3f)]; + } - /** - * Translates characters to character classes - */ - private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + /* The ZZ_CMAP_Z table has 272 entries */ + static final char ZZ_CMAP_Z[] = zzUnpackCMap( + "\1\0\1\100\1\200\u010d\100"); + + /* The ZZ_CMAP_Y table has 192 entries */ + static final char ZZ_CMAP_Y[] = zzUnpackCMap( + "\1\0\1\1\1\2\175\3\1\4\77\3"); + + /* The ZZ_CMAP_A table has 320 entries */ + static final char ZZ_CMAP_A[] = zzUnpackCMap( + "\11\0\1\25\1\11\1\26\1\27\1\24\22\0\1\25\1\55\1\16\1\21\1\23\1\55\1\57\1\10"+ + "\2\101\1\31\1\51\1\50\1\40\1\41\1\30\1\4\1\5\1\103\1\17\2\104\2\7\2\3\1\60"+ + "\1\50\1\52\1\53\1\54\1\56\1\100\1\6\1\33\1\6\1\71\1\37\1\36\5\1\1\22\5\1\1"+ + "\76\5\1\1\35\2\1\1\101\1\12\1\101\1\20\1\2\1\0\1\45\1\34\1\65\1\63\1\44\1"+ + "\15\1\74\1\61\1\67\1\106\1\77\1\32\1\70\1\14\1\62\1\64\1\105\1\43\1\46\1\42"+ + "\1\13\1\73\1\72\1\66\1\75\1\102\1\107\1\20\1\47\1\56\6\0\1\26\242\0\2\26\26"+ + "\0"); /** * Translates DFA states to action switch labels. @@ -110,15 +119,15 @@ public class BytecodeTokenizer extends AbstractJFlexCTokenMaker { "\1\27\14\2\1\6\1\35\1\6\1\36\17\2\1\27"+ "\20\2\1\27\7\2\1\27\6\2\1\37\16\2\1\1"+ "\37\0\1\1\2\17\1\4\10\2\1\6\1\40\1\37"+ - "\5\2\1\41\20\2\1\27\26\2\1\1\2\0\1\42"+ + "\5\2\1\41\21\2\1\27\26\2\1\1\2\0\1\42"+ "\2\0\1\43\10\0\1\44\17\0\1\45\1\1\1\4"+ - "\2\2\1\27\1\6\5\2\1\27\33\2\1\1\35\0"+ + "\2\2\1\27\1\6\5\2\1\27\34\2\1\1\35\0"+ "\1\1\1\4\3\2\1\6\4\2\1\46\23\2\1\1"+ "\22\0\23\2\1\1\2\0\1\44\6\0\12\2\11\0"+ "\11\2\5\0\3\2\5\0\1\2\2\0"; private static int [] zzUnpackAction() { - int [] result = new int[603]; + int [] result = new int[605]; int offset = 0; offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); return result; @@ -192,19 +201,19 @@ private static int zzUnpackAction(String packed, int offset, int [] result) { "\0\u5fa0\0\u5fe8\0\u6030\0\u6078\0\u60c0\0\u6108\0\u6150\0\u6198"+ "\0\u61e0\0\u6228\0\u6270\0\u62b8\0\u6300\0\u6348\0\u6390\0\u63d8"+ "\0\u6420\0\u6468\0\u64b0\0\u64f8\0\u6540\0\u6588\0\u65d0\0\u6618"+ - "\0\u6660\0\u66a8\0\u66f0\0\u0288\0\u6738\0\u6780\0\u67c8\0\u6810"+ + "\0\u6660\0\u66a8\0\u66f0\0\u6738\0\u0288\0\u6780\0\u67c8\0\u6810"+ "\0\u6858\0\u68a0\0\u68e8\0\u6930\0\u6978\0\u69c0\0\u6a08\0\u6a50"+ "\0\u6a98\0\u6ae0\0\u6b28\0\u6b70\0\u6bb8\0\u6c00\0\u6c48\0\u6c90"+ - "\0\u6cd8\0\u6d20\0\u6d68\0\u6db0\0\u6df8\0\u6e40\0\u6e88\0\u3d50"+ - "\0\u6ed0\0\u6f18\0\u6f60\0\u6fa8\0\u6ff0\0\u7038\0\u7080\0\u70c8"+ + "\0\u6cd8\0\u6d20\0\u6d68\0\u6db0\0\u6df8\0\u6e40\0\u6e88\0\u6ed0"+ + "\0\u3d50\0\u6f18\0\u6f60\0\u6fa8\0\u6ff0\0\u7038\0\u7080\0\u70c8"+ "\0\u7110\0\u7158\0\u71a0\0\u71e8\0\u7230\0\u7278\0\u72c0\0\u7308"+ "\0\u7350\0\u7398\0\u73e0\0\u7428\0\u7470\0\u74b8\0\u7500\0\u7548"+ - "\0\u7590\0\u75d8\0\u7620\0\u7668\0\u76b0\0\u63d8\0\u76f8\0\u64b0"+ - "\0\u7740\0\u7788\0\u77d0\0\u7818\0\u7860\0\u78a8\0\u78f0\0\u7938"+ + "\0\u7590\0\u75d8\0\u7620\0\u7668\0\u76b0\0\u76f8\0\u7740\0\u6420"+ + "\0\u7788\0\u64f8\0\u77d0\0\u7818\0\u7860\0\u78a8\0\u78f0\0\u7938"+ "\0\u7980\0\u79c8\0\u7a10\0\u7a58\0\u7aa0\0\u7ae8\0\u7b30\0\u7b78"+ "\0\u7bc0\0\u7c08\0\u7c50\0\u7c98\0\u7ce0\0\u7d28\0\u7d70\0\u7db8"+ - "\0\u6b70\0\u7e00\0\u7e48\0\u7e90\0\u7ed8\0\u7f20\0\u7f68\0\u7fb0"+ - "\0\u7ff8\0\u8040\0\u8088\0\u0168\0\u80d0\0\u8118\0\u8160\0\u81a8"+ + "\0\u7e00\0\u7e48\0\u6bb8\0\u7e90\0\u7ed8\0\u7f20\0\u7f68\0\u7fb0"+ + "\0\u7ff8\0\u8040\0\u8088\0\u80d0\0\u8118\0\u0168\0\u8160\0\u81a8"+ "\0\u81f0\0\u8238\0\u8280\0\u82c8\0\u8310\0\u8358\0\u83a0\0\u83e8"+ "\0\u8430\0\u8478\0\u84c0\0\u8508\0\u8550\0\u8598\0\u85e0\0\u8628"+ "\0\u8670\0\u86b8\0\u8700\0\u8748\0\u8790\0\u87d8\0\u8820\0\u8868"+ @@ -215,13 +224,13 @@ private static int zzUnpackAction(String packed, int offset, int [] result) { "\0\u91b0\0\u91f8\0\u9240\0\u9288\0\u92d0\0\u9318\0\u9360\0\u93a8"+ "\0\u93f0\0\u9438\0\u9480\0\u94c8\0\u9510\0\u9558\0\u95a0\0\u95e8"+ "\0\u9630\0\u9678\0\u96c0\0\u9708\0\u9750\0\u9798\0\u97e0\0\u9828"+ - "\0\u9870\0\u98b8\0\u9900\0\u9948\0\u9990\0\u99d8\0\u9a20\0\u4698"+ - "\0\u9a68\0\u9ab0\0\u9af8\0\u9b40\0\u9b88\0\u9bd0\0\u9c18\0\u9c60"+ + "\0\u9870\0\u98b8\0\u9900\0\u9948\0\u9990\0\u99d8\0\u9a20\0\u9a68"+ + "\0\u9ab0\0\u4698\0\u9af8\0\u9b40\0\u9b88\0\u9bd0\0\u9c18\0\u9c60"+ "\0\u9ca8\0\u9cf0\0\u9d38\0\u9d80\0\u9dc8\0\u9e10\0\u9e58\0\u9ea0"+ - "\0\u9ee8\0\u9f30\0\u9f78"; + "\0\u9ee8\0\u9f30\0\u9f78\0\u9fc0\0\ua008"; private static int [] zzUnpackRowMap() { - int [] result = new int[603]; + int [] result = new int[605]; int offset = 0; offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); return result; @@ -816,327 +825,330 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) { "\6\6\2\0\1\u010d\4\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\4\6\1\u0164\12\6\1\5\1\0"+ - "\5\6\1\0\1\5\1\6\1\u0165\5\6\2\0\1\101"+ - "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ - "\3\0\6\6\2\0\5\6\12\0\17\6\1\5\1\0"+ - "\1\6\1\u0166\3\6\1\0\1\5\7\6\2\0\1\101"+ - "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ - "\3\0\6\6\2\0\3\6\1\u0167\1\6\12\0\17\6"+ + "\2\0\1\u0164\4\6\12\0\4\6\1\u0165\12\6\1\5"+ + "\1\0\5\6\1\0\1\5\1\6\1\u0166\5\6\2\0"+ + "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ + "\1\5\3\0\6\6\2\0\5\6\12\0\17\6\1\5"+ + "\1\0\1\6\1\u0167\3\6\1\0\1\5\7\6\2\0"+ + "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ + "\1\5\3\0\6\6\2\0\3\6\1\u0168\1\6\12\0"+ + "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ + "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ + "\1\5\3\0\2\6\1\u0169\3\6\2\0\5\6\12\0"+ + "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ + "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ + "\1\5\3\0\1\u0162\5\6\2\0\5\6\12\0\17\6"+ "\1\5\1\0\5\6\1\0\1\5\7\6\2\0\1\101"+ - "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ - "\3\0\2\6\1\u0168\3\6\2\0\5\6\12\0\17\6"+ + "\2\6\1\u016a\1\0\1\6\1\0\1\5\2\6\2\0"+ + "\1\5\3\0\6\6\2\0\4\6\1\u016b\12\0\17\6"+ "\1\5\1\0\5\6\1\0\1\5\7\6\2\0\1\101"+ "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ - "\3\0\1\u0162\5\6\2\0\5\6\12\0\17\6\1\5"+ - "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\2\6"+ - "\1\u0169\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ - "\3\0\6\6\2\0\4\6\1\u016a\12\0\17\6\1\5"+ + "\3\0\6\6\2\0\1\u016c\4\6\12\0\17\6\1\5"+ + "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\3\6"+ + "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ + "\6\6\2\0\5\6\12\0\12\6\1\u016d\4\6\1\5"+ "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ - "\6\6\2\0\1\u016b\4\6\12\0\17\6\1\5\1\0"+ + "\6\6\2\0\5\6\12\0\16\6\1\u016e\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\12\6\1\u016c\4\6\1\5\1\0"+ - "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ + "\2\0\5\6\12\0\17\6\1\5\1\0\1\6\1\322"+ + "\3\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\16\6\1\u016d\1\5\1\0\5\6"+ - "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ - "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\5\6\12\0\17\6\1\5\1\0\1\6\1\322\3\6"+ - "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ - "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\4\6\1\361\12\0\17\6\1\5\1\0\5\6\1\0"+ - "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ - "\12\0\4\6\1\u016e\12\6\1\5\1\0\5\6\1\0"+ - "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\332"+ - "\1\6\1\322\2\6\12\0\17\6\1\5\1\0\5\6"+ + "\2\0\4\6\1\361\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ "\5\6\12\0\4\6\1\u016f\12\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\1\6\1\u014f\3\6\12\0\17\6\1\5\1\0\5\6"+ + "\1\332\1\6\1\322\2\6\12\0\17\6\1\5\1\0"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ + "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ + "\2\0\5\6\12\0\4\6\1\u0170\12\6\1\5\1\0"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ + "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ + "\2\0\1\6\1\u014f\3\6\12\0\17\6\1\5\1\0"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ + "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ + "\2\0\1\u0171\3\6\1\u0172\12\0\17\6\1\5\1\0"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ + "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ + "\2\0\2\6\1\u0173\2\6\12\0\17\6\1\5\1\0"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ + "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ + "\2\0\1\u0174\4\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\1\u0170\3\6\1\u0171\12\0\17\6\1\5\1\0\5\6"+ + "\5\6\12\0\1\6\1\u0175\15\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\2\6\1\u0172\2\6\12\0\17\6\1\5\1\0\5\6"+ - "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ + "\3\6\1\u0176\1\6\12\0\6\6\1\u0177\10\6\1\5"+ + "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\1\121"+ + "\2\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ + "\3\0\6\6\2\0\2\6\1\322\2\6\12\0\1\6"+ + "\1\u0178\15\6\1\5\1\0\5\6\1\0\1\5\7\6"+ + "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ + "\2\0\1\5\3\0\6\6\2\0\1\322\1\6\1\322"+ + "\2\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ + "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ + "\17\6\1\5\1\0\3\6\1\322\1\6\1\0\1\5"+ + "\7\6\2\0\1\101\1\6\1\u0179\1\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\1\u0173\4\6\12\0\17\6\1\5\1\0\5\6\1\0"+ - "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ - "\12\0\1\6\1\u0174\15\6\1\5\1\0\5\6\1\0"+ + "\5\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ + "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ + "\4\6\1\322\12\6\1\5\1\0\5\6\1\0\1\5"+ + "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ + "\2\6\2\0\1\5\3\0\1\u017a\5\6\2\0\5\6"+ + "\12\0\1\6\1\u015f\15\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\3\6"+ - "\1\u0175\1\6\12\0\6\6\1\u0176\10\6\1\5\1\0"+ - "\5\6\1\0\1\5\7\6\2\0\1\101\1\121\2\6"+ - "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ - "\6\6\2\0\2\6\1\322\2\6\12\0\1\6\1\u0177"+ - "\15\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ - "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\1\322\1\6\1\322\2\6"+ - "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ - "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\17\6"+ - "\1\5\1\0\3\6\1\322\1\6\1\0\1\5\7\6"+ - "\2\0\1\101\1\6\1\u0178\1\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ - "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ - "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\4\6"+ - "\1\322\12\6\1\5\1\0\5\6\1\0\1\5\7\6"+ - "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\1\u0179\5\6\2\0\5\6\12\0"+ - "\1\6\1\u015f\15\6\1\5\1\0\5\6\1\0\1\5"+ + "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\u017b"+ + "\4\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ + "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ + "\6\6\1\u017c\10\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\1\u017a\4\6"+ + "\2\6\2\0\1\5\3\0\1\364\5\6\2\0\5\6"+ "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\6\6"+ - "\1\u017b\10\6\1\5\1\0\5\6\1\0\1\5\7\6"+ - "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\1\364\5\6\2\0\5\6\12\0"+ - "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ - "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\3\6\1\u017c\1\6\12\0"+ - "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ - "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\5\6\12\0\1\6\1\322"+ - "\15\6\1\5\1\0\5\6\1\0\10\5\2\0\1\5"+ - "\1\u017d\2\5\1\0\1\5\1\0\3\5\2\0\1\5"+ - "\3\0\6\5\2\0\5\5\12\0\20\5\1\0\5\5"+ - "\61\0\1\u017e\73\0\1\u0122\127\0\1\u017f\64\0\1\u0180"+ - "\126\0\1\u0181\73\0\1\u0126\127\0\1\u0182\64\0\1\u0183"+ - "\110\0\1\u0184\110\0\1\u0185\127\0\1\u0186\66\0\1\u0187"+ - "\112\0\1\u0188\17\0\1\u0189\64\0\1\u018a\110\0\1\u018b"+ - "\1\u018c\57\0\1\u018d\141\0\1\u018e\125\0\1\u018f\66\0"+ - "\1\u0190\106\0\1\u0191\107\0\1\u0192\20\0\1\u0193\67\0"+ - "\1\u0194\133\0\1\u0195\102\0\1\u0196\107\0\1\u0197\41\0"+ - "\1\u0198\140\0\1\u0199\122\0\1\u019a\73\0\1\u013d\127\0"+ - "\1\u019b\64\0\1\u019c\46\0\3\5\5\u019d\2\0\3\5"+ - "\1\u019d\1\0\1\u019d\1\0\3\5\2\0\1\5\3\0"+ - "\1\5\2\u019d\1\5\2\u019d\2\0\2\5\2\u019d\1\5"+ - "\12\0\2\5\1\u019d\1\5\1\u019d\3\5\1\u019d\7\5"+ - "\1\0\1\5\2\u019d\2\5\1\0\2\102\1\u0143\5\310"+ - "\2\0\3\102\1\310\1\0\1\310\1\0\3\102\2\0"+ - "\1\102\3\0\1\102\2\310\1\102\2\310\2\0\2\102"+ - "\2\310\1\102\12\0\2\102\1\310\1\102\1\310\3\102"+ - "\1\310\7\102\1\0\1\102\2\310\2\102\1\0\3\311"+ - "\5\u019e\1\116\1\0\3\311\1\u019e\1\311\1\u019e\13\311"+ - "\2\u019e\1\311\2\u019e\4\311\2\u019e\15\311\1\u019e\1\311"+ - "\1\u019e\3\311\1\u019e\11\311\2\u019e\3\311\1\5\7\6"+ - "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\1\6\1\u019f\3\6"+ + "\2\0\1\5\3\0\6\6\2\0\3\6\1\u017d\1\6"+ "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\12\6"+ - "\1\364\4\6\1\5\1\0\5\6\1\0\1\5\7\6"+ - "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\1\u014f\4\6\12\0"+ - "\2\6\1\322\14\6\1\5\1\0\5\6\1\0\1\5"+ + "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\1\6"+ + "\1\322\15\6\1\5\1\0\5\6\1\0\10\5\2\0"+ + "\1\5\1\u017e\2\5\1\0\1\5\1\0\3\5\2\0"+ + "\1\5\3\0\6\5\2\0\5\5\12\0\20\5\1\0"+ + "\5\5\61\0\1\u017f\73\0\1\u0122\127\0\1\u0180\64\0"+ + "\1\u0181\126\0\1\u0182\73\0\1\u0126\127\0\1\u0183\64\0"+ + "\1\u0184\110\0\1\u0185\110\0\1\u0186\127\0\1\u0187\66\0"+ + "\1\u0188\112\0\1\u0189\17\0\1\u018a\64\0\1\u018b\110\0"+ + "\1\u018c\1\u018d\57\0\1\u018e\141\0\1\u018f\125\0\1\u0190"+ + "\66\0\1\u0191\106\0\1\u0192\107\0\1\u0193\20\0\1\u0194"+ + "\67\0\1\u0195\133\0\1\u0196\102\0\1\u0197\107\0\1\u0198"+ + "\41\0\1\u0199\140\0\1\u019a\122\0\1\u019b\73\0\1\u013d"+ + "\127\0\1\u019c\64\0\1\u019d\46\0\3\5\5\u019e\2\0"+ + "\3\5\1\u019e\1\0\1\u019e\1\0\3\5\2\0\1\5"+ + "\3\0\1\5\2\u019e\1\5\2\u019e\2\0\2\5\2\u019e"+ + "\1\5\12\0\2\5\1\u019e\1\5\1\u019e\3\5\1\u019e"+ + "\7\5\1\0\1\5\2\u019e\2\5\1\0\2\102\1\u0143"+ + "\5\310\2\0\3\102\1\310\1\0\1\310\1\0\3\102"+ + "\2\0\1\102\3\0\1\102\2\310\1\102\2\310\2\0"+ + "\2\102\2\310\1\102\12\0\2\102\1\310\1\102\1\310"+ + "\3\102\1\310\7\102\1\0\1\102\2\310\2\102\1\0"+ + "\3\311\5\u019f\1\116\1\0\3\311\1\u019f\1\311\1\u019f"+ + "\13\311\2\u019f\1\311\2\u019f\4\311\2\u019f\15\311\1\u019f"+ + "\1\311\1\u019f\3\311\1\u019f\11\311\2\u019f\3\311\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\1\6\1\364"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\1\6\1\u01a0"+ "\3\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\4\6\1\u01a0"+ - "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ - "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\1\322\5\6\2\0\5\6\12\0"+ - "\13\6\1\322\3\6\1\5\1\0\5\6\1\0\1\5"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ + "\12\6\1\364\4\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\1\u01a1\5\6\2\0\5\6"+ - "\12\0\17\6\1\5\1\0\5\6\1\0\3\140\5\u01a2"+ - "\2\140\1\337\2\140\1\u01a2\1\340\1\u01a2\13\140\2\u01a2"+ - "\1\140\2\u01a2\4\140\2\u01a2\15\140\1\u01a2\1\140\1\u01a2"+ - "\3\140\1\u01a2\11\140\2\u01a2\3\140\1\5\7\6\2\0"+ - "\1\101\1\u01a3\2\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\17\6"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\1\u014f\4\6"+ + "\12\0\2\6\1\322\14\6\1\5\1\0\5\6\1\0"+ + "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ + "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\6"+ + "\1\364\3\6\12\0\17\6\1\5\1\0\5\6\1\0"+ + "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ + "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\4\6"+ + "\1\u01a1\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ + "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ + "\2\6\2\0\1\5\3\0\1\322\5\6\2\0\5\6"+ + "\12\0\13\6\1\322\3\6\1\5\1\0\5\6\1\0"+ + "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ + "\1\5\2\6\2\0\1\5\3\0\1\u01a2\5\6\2\0"+ + "\5\6\12\0\17\6\1\5\1\0\5\6\1\0\3\140"+ + "\5\u01a3\2\140\1\337\2\140\1\u01a3\1\340\1\u01a3\13\140"+ + "\2\u01a3\1\140\2\u01a3\4\140\2\u01a3\15\140\1\u01a3\1\140"+ + "\1\u01a3\3\140\1\u01a3\11\140\2\u01a3\3\140\1\5\7\6"+ + "\2\0\1\101\1\u01a4\2\6\1\0\1\6\1\0\1\5"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ + "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ + "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ + "\1\5\3\0\6\6\2\0\4\6\1\u01a5\12\0\17\6"+ "\1\5\1\0\5\6\1\0\1\5\7\6\2\0\1\101"+ "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ - "\3\0\6\6\2\0\4\6\1\u01a4\12\0\17\6\1\5"+ + "\3\0\6\6\2\0\5\6\12\0\16\6\1\322\1\5"+ "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ - "\6\6\2\0\5\6\12\0\16\6\1\322\1\5\1\0"+ - "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ - "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\2\6\1\u01a5\2\6\12\0\17\6\1\5\1\0"+ + "\6\6\2\0\2\6\1\u01a6\2\6\12\0\17\6\1\5"+ + "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\3\6"+ + "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ + "\6\6\2\0\4\6\1\u016f\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\4\6\1\u016e\12\0\17\6\1\5\1\0\5\6"+ + "\2\0\4\6\1\u01a7\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\4\6\1\u01a6\12\0\17\6\1\5\1\0\5\6\1\0"+ - "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\2\6"+ - "\1\u01a7\2\6\12\0\17\6\1\5\1\0\5\6\1\0"+ - "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ - "\12\0\11\6\1\u01a8\5\6\1\5\1\0\5\6\1\0"+ - "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\6"+ - "\1\u01a9\3\6\12\0\17\6\1\5\1\0\5\6\1\0"+ - "\1\5\7\6\2\0\1\101\1\6\1\u01aa\1\6\1\0"+ - "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\17\6\1\5\1\0\5\6\1\0"+ - "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\6"+ - "\1\u01ab\3\6\12\0\17\6\1\5\1\0\5\6\1\0"+ + "\2\6\1\u01a8\2\6\12\0\17\6\1\5\1\0\5\6"+ + "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ + "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ + "\5\6\12\0\11\6\1\u01a9\5\6\1\5\1\0\5\6"+ + "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ + "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ + "\1\6\1\u01aa\3\6\12\0\17\6\1\5\1\0\5\6"+ + "\1\0\1\5\7\6\2\0\1\101\1\6\1\u01ab\1\6"+ + "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ + "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\5\6"+ + "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ + "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ + "\1\6\1\u01ac\3\6\12\0\17\6\1\5\1\0\5\6"+ + "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ + "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ + "\5\6\12\0\1\6\1\u01ad\15\6\1\5\1\0\5\6"+ + "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ + "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ + "\5\6\12\0\14\6\1\u01ae\2\6\1\5\1\0\5\6"+ + "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ + "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ + "\1\6\1\u0172\3\6\12\0\17\6\1\5\1\0\5\6"+ + "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ + "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ + "\4\6\1\u01af\12\0\17\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ - "\12\0\1\6\1\u01ac\15\6\1\5\1\0\5\6\1\0"+ + "\12\0\4\6\1\u01b0\12\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ - "\12\0\14\6\1\u01ad\2\6\1\5\1\0\5\6\1\0"+ - "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\6"+ - "\1\u0171\3\6\12\0\17\6\1\5\1\0\5\6\1\0"+ + "\12\0\6\6\1\u011a\10\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\4\6"+ - "\1\u01ae\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ - "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ - "\4\6\1\u01af\12\6\1\5\1\0\5\6\1\0\1\5"+ + "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\u014f"+ + "\4\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ - "\6\6\1\u011a\10\6\1\5\1\0\5\6\1\0\1\5"+ - "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\1\u014f\4\6"+ - "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ + "\1\u01b1\16\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\1\u01b0"+ + "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\1\u01b2"+ "\16\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\5\6\12\0\5\6\1\u01b1"+ - "\11\6\1\5\1\0\5\6\1\0\1\5\1\6\1\u0165"+ + "\1\5\3\0\6\6\2\0\5\6\12\0\5\6\1\u01b3"+ + "\11\6\1\5\1\0\5\6\1\0\1\5\1\6\1\u0166"+ "\5\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ - "\1\101\1\u01b2\2\6\1\0\1\6\1\0\1\5\2\6"+ + "\1\101\1\u01b4\2\6\1\0\1\6\1\0\1\5\2\6"+ "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\17\6"+ "\1\5\1\0\5\6\1\0\1\5\7\6\2\0\1\101"+ "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\1\354\5\6\2\0\5\6\12\0\17\6\1\5"+ "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ - "\6\6\2\0\5\6\12\0\6\6\1\u01b3\10\6\1\5"+ + "\6\6\2\0\5\6\12\0\6\6\1\u01b5\10\6\1\5"+ "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ - "\6\6\2\0\1\u01b4\4\6\12\0\17\6\1\5\1\0"+ + "\6\6\2\0\1\u01b6\4\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\2\6\1\u01b5\2\6\12\0\17\6\1\5\1\0"+ + "\2\0\2\6\1\u01b7\2\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\3\6\1\u01b6\1\6\12\0\17\6\1\5\1\0"+ + "\2\0\3\6\1\u01b8\1\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\3\6\1\u01b7\1\6\12\0\17\6\1\5\1\0"+ + "\2\0\3\6\1\u01b9\1\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ "\2\0\5\6\12\0\1\322\16\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\5\6\12\0\16\6\1\u01b8\1\5\1\0\5\6\1\0"+ + "\5\6\12\0\16\6\1\u01ba\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ - "\12\0\6\6\1\u01b9\10\6\1\5\1\0\5\6\1\0"+ + "\12\0\6\6\1\u01bb\10\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\322"+ "\4\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\1\6\1\u01ba"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\1\6\1\u01bc"+ "\3\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\3\6\1\u01bb"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\3\6\1\u01bd"+ "\1\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ - "\16\6\1\u01bc\1\5\1\0\5\6\1\0\1\5\7\6"+ + "\16\6\1\u01be\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\4\6"+ - "\1\u01bd\12\6\1\5\1\0\5\6\1\0\1\5\7\6"+ + "\1\u01bf\12\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\4\6"+ - "\1\u01be\12\6\1\5\1\0\5\6\1\0\1\5\7\6"+ - "\2\0\1\101\1\6\1\u01bf\1\6\1\0\1\6\1\0"+ + "\1\u01c0\12\6\1\5\1\0\5\6\1\0\1\5\7\6"+ + "\2\0\1\101\1\6\1\u01c1\1\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\4\6\1\u01c0\12\0"+ + "\2\0\1\5\3\0\6\6\2\0\4\6\1\u01c2\12\0"+ "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\2\6\1\u01c1\2\6\12\0"+ + "\1\5\3\0\6\6\2\0\2\6\1\u01c3\2\6\12\0"+ "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\5\6\12\0\6\6\1\u01c2"+ + "\1\5\3\0\6\6\2\0\5\6\12\0\6\6\1\u01c4"+ "\10\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\1\u01c3\4\6\12\0\17\6"+ + "\1\5\3\0\6\6\2\0\1\u01c5\4\6\12\0\17\6"+ "\1\5\1\0\5\6\1\0\1\5\7\6\2\0\1\101"+ "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\6\6\2\0\1\236\4\6\12\0\17\6\1\5"+ - "\1\0\5\6\1\0\3\5\5\u01c4\2\0\3\5\1\u01c4"+ - "\1\0\1\u01c4\1\0\3\5\2\0\1\5\3\0\1\5"+ - "\2\u01c4\1\5\2\u01c4\2\0\2\5\2\u01c4\1\5\12\0"+ - "\2\5\1\u01c4\1\5\1\u01c4\3\5\1\u01c4\7\5\1\0"+ - "\1\5\2\u01c4\2\5\31\0\1\u01c5\125\0\1\u0122\11\0"+ - "\1\u017e\30\0\1\u0180\1\u01c6\5\u0180\1\u01c6\2\0\3\u0180"+ - "\1\0\1\u0180\1\0\1\u01c6\2\u0180\4\0\1\u0180\1\u01c6"+ - "\6\u0180\2\u01c6\5\u0180\1\0\2\u01c6\1\0\1\u01c6\1\0"+ - "\4\u01c6\17\u0180\2\u01c6\5\u0180\31\0\1\u01c7\125\0\1\u0126"+ - "\11\0\1\u0181\30\0\1\u0183\1\u01c8\5\u0183\1\u01c8\2\0"+ - "\3\u0183\1\0\1\u0183\1\0\1\u01c8\2\u0183\4\0\1\u0183"+ - "\1\u01c8\6\u0183\2\u01c8\5\u0183\1\0\2\u01c8\1\0\1\u01c8"+ - "\1\0\4\u01c8\17\u0183\2\u01c8\5\u0183\63\0\1\u01c9\71\0"+ - "\1\u01ca\15\0\1\u01cb\107\0\1\u018c\40\0\1\u01cc\164\0"+ - "\1\u01cd\51\0\1\u01ce\11\0\1\u01cf\124\0\1\u01d0\115\0"+ - "\1\u01d1\105\0\1\u01d2\104\0\1\u01d3\70\0\1\u01d4\111\0"+ - "\1\u01d5\106\0\1\u01d6\107\0\1\u01d7\107\0\1\u01d8\111\0"+ - "\1\u01d9\55\0\1\u01da\25\0\1\u01db\132\0\1\u01dc\105\0"+ - "\1\u01dd\105\0\1\u01de\60\0\1\u01df\105\0\1\u01e0\125\0"+ - "\1\u013d\11\0\1\u019a\30\0\1\u019c\1\u01e1\5\u019c\1\u01e1"+ - "\2\0\3\u019c\1\0\1\u019c\1\0\1\u01e1\2\u019c\4\0"+ - "\1\u019c\1\u01e1\6\u019c\2\u01e1\5\u019c\1\0\2\u01e1\1\0"+ - "\1\u01e1\1\0\4\u01e1\17\u019c\2\u01e1\5\u019c\1\0\3\5"+ - "\5\u01e2\2\0\3\5\1\u01e2\1\0\1\u01e2\1\0\3\5"+ - "\2\0\1\5\3\0\1\5\2\u01e2\1\5\2\u01e2\2\0"+ - "\2\5\2\u01e2\1\5\12\0\2\5\1\u01e2\1\5\1\u01e2"+ - "\3\5\1\u01e2\7\5\1\0\1\5\2\u01e2\2\5\1\0"+ - "\3\311\5\u01e3\1\116\1\0\3\311\1\u01e3\1\311\1\u01e3"+ - "\13\311\2\u01e3\1\311\2\u01e3\4\311\2\u01e3\15\311\1\u01e3"+ - "\1\311\1\u01e3\3\311\1\u01e3\11\311\2\u01e3\3\311\1\5"+ + "\1\0\5\6\1\0\3\5\5\u01c6\2\0\3\5\1\u01c6"+ + "\1\0\1\u01c6\1\0\3\5\2\0\1\5\3\0\1\5"+ + "\2\u01c6\1\5\2\u01c6\2\0\2\5\2\u01c6\1\5\12\0"+ + "\2\5\1\u01c6\1\5\1\u01c6\3\5\1\u01c6\7\5\1\0"+ + "\1\5\2\u01c6\2\5\31\0\1\u01c7\125\0\1\u0122\11\0"+ + "\1\u017f\30\0\1\u0181\1\u01c8\5\u0181\1\u01c8\2\0\3\u0181"+ + "\1\0\1\u0181\1\0\1\u01c8\2\u0181\4\0\1\u0181\1\u01c8"+ + "\6\u0181\2\u01c8\5\u0181\1\0\2\u01c8\1\0\1\u01c8\1\0"+ + "\4\u01c8\17\u0181\2\u01c8\5\u0181\31\0\1\u01c9\125\0\1\u0126"+ + "\11\0\1\u0182\30\0\1\u0184\1\u01ca\5\u0184\1\u01ca\2\0"+ + "\3\u0184\1\0\1\u0184\1\0\1\u01ca\2\u0184\4\0\1\u0184"+ + "\1\u01ca\6\u0184\2\u01ca\5\u0184\1\0\2\u01ca\1\0\1\u01ca"+ + "\1\0\4\u01ca\17\u0184\2\u01ca\5\u0184\63\0\1\u01cb\71\0"+ + "\1\u01cc\15\0\1\u01cd\107\0\1\u018d\40\0\1\u01ce\164\0"+ + "\1\u01cf\51\0\1\u01d0\11\0\1\u01d1\124\0\1\u01d2\115\0"+ + "\1\u01d3\105\0\1\u01d4\104\0\1\u01d5\70\0\1\u01d6\111\0"+ + "\1\u01d7\106\0\1\u01d8\107\0\1\u01d9\107\0\1\u01da\111\0"+ + "\1\u01db\55\0\1\u01dc\25\0\1\u01dd\132\0\1\u01de\105\0"+ + "\1\u01df\105\0\1\u01e0\60\0\1\u01e1\105\0\1\u01e2\125\0"+ + "\1\u013d\11\0\1\u019b\30\0\1\u019d\1\u01e3\5\u019d\1\u01e3"+ + "\2\0\3\u019d\1\0\1\u019d\1\0\1\u01e3\2\u019d\4\0"+ + "\1\u019d\1\u01e3\6\u019d\2\u01e3\5\u019d\1\0\2\u01e3\1\0"+ + "\1\u01e3\1\0\4\u01e3\17\u019d\2\u01e3\5\u019d\1\0\3\5"+ + "\5\u01e4\2\0\3\5\1\u01e4\1\0\1\u01e4\1\0\3\5"+ + "\2\0\1\5\3\0\1\5\2\u01e4\1\5\2\u01e4\2\0"+ + "\2\5\2\u01e4\1\5\12\0\2\5\1\u01e4\1\5\1\u01e4"+ + "\3\5\1\u01e4\7\5\1\0\1\5\2\u01e4\2\5\1\0"+ + "\3\311\5\u01e5\1\116\1\0\3\311\1\u01e5\1\311\1\u01e5"+ + "\13\311\2\u01e5\1\311\2\u01e5\4\311\2\u01e5\15\311\1\u01e5"+ + "\1\311\1\u01e5\3\311\1\u01e5\11\311\2\u01e5\3\311\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\1\6\1\u01e4"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\1\6\1\u01e6"+ "\3\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\1\u01e5\4\6"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\1\u01e7\4\6"+ "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\1\u01e6\5\6\2\0\5\6\12\0"+ - "\17\6\1\5\1\0\5\6\1\0\3\140\5\u01e7\2\140"+ - "\1\337\2\140\1\u01e7\1\340\1\u01e7\13\140\2\u01e7\1\140"+ - "\2\u01e7\4\140\2\u01e7\15\140\1\u01e7\1\140\1\u01e7\3\140"+ - "\1\u01e7\11\140\2\u01e7\3\140\1\5\7\6\2\0\1\101"+ + "\2\0\1\5\3\0\1\u01e8\5\6\2\0\5\6\12\0"+ + "\17\6\1\5\1\0\5\6\1\0\3\140\5\u01e9\2\140"+ + "\1\337\2\140\1\u01e9\1\340\1\u01e9\13\140\2\u01e9\1\140"+ + "\2\u01e9\4\140\2\u01e9\15\140\1\u01e9\1\140\1\u01e9\3\140"+ + "\1\u01e9\11\140\2\u01e9\3\140\1\5\7\6\2\0\1\101"+ "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ - "\3\0\6\6\2\0\5\6\12\0\3\6\1\u01a7\13\6"+ + "\3\0\6\6\2\0\5\6\12\0\3\6\1\u01a8\13\6"+ "\1\5\1\0\5\6\1\0\1\5\7\6\2\0\1\101"+ "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ - "\3\0\6\6\2\0\1\u01e8\4\6\12\0\17\6\1\5"+ + "\3\0\6\6\2\0\1\u01ea\4\6\12\0\17\6\1\5"+ "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ - "\6\6\2\0\3\6\1\u01e9\1\6\12\0\17\6\1\5"+ + "\6\6\2\0\3\6\1\u01eb\1\6\12\0\17\6\1\5"+ "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ - "\6\6\2\0\5\6\12\0\6\6\1\u01ea\10\6\1\5"+ + "\6\6\2\0\5\6\12\0\6\6\1\u01ec\10\6\1\5"+ "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ - "\6\6\2\0\4\6\1\u01eb\12\0\17\6\1\5\1\0"+ - "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u01ec"+ + "\6\6\2\0\4\6\1\u01ed\12\0\17\6\1\5\1\0"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u01ee"+ "\1\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\6\6\2\0\5\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ @@ -1144,37 +1156,40 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) { "\2\0\5\6\12\0\2\6\1\361\14\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\3\6\1\u01ed\1\6\12\0\17\6\1\5\1\0"+ + "\2\0\3\6\1\u01ef\1\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ "\2\0\5\6\12\0\11\6\1\322\5\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ - "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\1\u01ee"+ + "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\1\u01f0"+ "\5\6\2\0\5\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\1\u01ef\4\6\12\0\17\6\1\5\1\0\5\6\1\0"+ + "\1\u01f1\4\6\12\0\17\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ - "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\u01f0"+ + "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\u01f2"+ "\4\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\1\6\1\u01f1"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\2\6\1\377"+ + "\2\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ + "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\1\6\1\u01f3"+ "\3\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ "\4\6\1\322\2\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ "\5\6\12\0\17\6\1\5\1\0\1\6\1\322\3\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ - "\1\0\1\5\2\6\2\0\1\5\3\0\1\u0171\5\6"+ + "\1\0\1\5\2\6\2\0\1\5\3\0\1\u0172\5\6"+ "\2\0\5\6\12\0\17\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\2\6"+ - "\1\u01f2\2\6\12\0\17\6\1\5\1\0\5\6\1\0"+ + "\1\u01f4\2\6\12\0\17\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\3\6"+ "\1\377\1\6\12\0\17\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ - "\12\0\4\6\1\u01f3\12\6\1\5\1\0\5\6\1\0"+ + "\12\0\4\6\1\u01f5\12\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\364"+ "\4\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ @@ -1183,51 +1198,51 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) { "\13\6\1\364\3\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ - "\4\6\1\u01f4\12\6\1\5\1\0\5\6\1\0\1\5"+ - "\7\6\2\0\1\101\1\6\1\u01f5\1\6\1\0\1\6"+ + "\4\6\1\u01f6\12\6\1\5\1\0\5\6\1\0\1\5"+ + "\7\6\2\0\1\101\1\6\1\u01f7\1\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ "\5\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ - "\7\6\2\0\1\101\2\6\1\u01f6\1\0\1\6\1\0"+ + "\7\6\2\0\1\101\2\6\1\u01f8\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ - "\2\0\1\101\1\6\1\u01f7\1\6\1\0\1\6\1\0"+ + "\2\0\1\101\1\6\1\u01f9\1\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\2\6\1\u01f8\2\6"+ + "\2\0\1\5\3\0\6\6\2\0\2\6\1\u01fa\2\6"+ "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\7\6"+ - "\1\u01f9\7\6\1\5\1\0\5\6\1\0\1\5\7\6"+ + "\1\u01fb\7\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ "\2\0\1\5\3\0\6\6\2\0\5\6\12\0\7\6"+ - "\1\u01fa\7\6\1\5\1\0\5\6\1\0\1\5\7\6"+ - "\2\0\1\101\1\6\1\u01fb\1\6\1\0\1\6\1\0"+ + "\1\u01fc\7\6\1\5\1\0\5\6\1\0\1\5\7\6"+ + "\2\0\1\101\1\6\1\u01fd\1\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\1\u01fc\4\6\12\0"+ + "\2\0\1\5\3\0\6\6\2\0\1\u01fe\4\6\12\0"+ "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\5\6\12\0\7\6\1\u01fd"+ + "\1\5\3\0\6\6\2\0\5\6\12\0\7\6\1\u01ff"+ "\7\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\3\6\1\u01fe\1\6\12\0"+ + "\1\5\3\0\6\6\2\0\3\6\1\u0200\1\6\12\0"+ "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\5\6\12\0\1\6\1\u01ff"+ - "\15\6\1\5\1\0\5\6\1\0\3\5\5\u0200\2\0"+ - "\3\5\1\u0200\1\0\1\u0200\1\0\3\5\2\0\1\5"+ - "\3\0\1\5\2\u0200\1\5\2\u0200\2\0\2\5\2\u0200"+ - "\1\5\12\0\2\5\1\u0200\1\5\1\u0200\3\5\1\u0200"+ - "\7\5\1\0\1\5\2\u0200\2\5\31\0\1\u0180\107\0"+ - "\1\u0183\122\0\1\u0201\111\0\1\u0202\134\0\1\u0203\60\0"+ - "\1\u0204\130\0\1\u0205\36\0\1\u0206\160\0\1\u0207\105\0"+ - "\1\u0208\72\0\1\u0209\106\0\1\u018c\75\0\1\u020a\121\0"+ - "\1\u020b\133\0\1\u018c\113\0\1\u020c\56\0\1\u020d\132\0"+ - "\1\u018c\110\0\1\u020e\117\0\1\u020f\54\0\1\u0210\141\0"+ - "\1\u0211\55\0\1\u020f\107\0\1\u0212\56\0\1\u01dd\124\0"+ - "\1\u019c\57\0\3\5\5\6\2\0\3\5\1\6\1\0"+ + "\1\5\3\0\6\6\2\0\5\6\12\0\1\6\1\u0201"+ + "\15\6\1\5\1\0\5\6\1\0\3\5\5\u0202\2\0"+ + "\3\5\1\u0202\1\0\1\u0202\1\0\3\5\2\0\1\5"+ + "\3\0\1\5\2\u0202\1\5\2\u0202\2\0\2\5\2\u0202"+ + "\1\5\12\0\2\5\1\u0202\1\5\1\u0202\3\5\1\u0202"+ + "\7\5\1\0\1\5\2\u0202\2\5\31\0\1\u0181\107\0"+ + "\1\u0184\122\0\1\u0203\111\0\1\u0204\134\0\1\u0205\60\0"+ + "\1\u0206\130\0\1\u0207\36\0\1\u0208\160\0\1\u0209\105\0"+ + "\1\u020a\72\0\1\u020b\106\0\1\u018d\75\0\1\u020c\121\0"+ + "\1\u020d\133\0\1\u018d\113\0\1\u020e\56\0\1\u020f\132\0"+ + "\1\u018d\110\0\1\u0210\117\0\1\u0211\54\0\1\u0212\141\0"+ + "\1\u0213\55\0\1\u0211\107\0\1\u0214\56\0\1\u01df\124\0"+ + "\1\u019d\57\0\3\5\5\6\2\0\3\5\1\6\1\0"+ "\1\6\1\0\3\5\2\0\1\5\3\0\1\5\2\6"+ "\1\5\2\6\2\0\2\5\2\6\1\5\12\0\2\5"+ "\1\6\1\5\1\6\3\5\1\6\7\5\1\0\1\5"+ @@ -1236,8 +1251,8 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) { "\2\115\15\311\1\115\1\311\1\115\3\311\1\115\11\311"+ "\2\115\3\311\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\3\6\1\u01e6\1\6\12\0\17\6\1\5\1\0"+ - "\5\6\1\0\1\5\1\6\1\u0213\5\6\2\0\1\101"+ + "\2\0\3\6\1\u01e8\1\6\12\0\17\6\1\5\1\0"+ + "\5\6\1\0\1\5\1\6\1\u0215\5\6\2\0\1\101"+ "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\6\6\2\0\5\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ @@ -1246,7 +1261,7 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) { "\5\6\1\0\3\140\5\15\2\140\1\337\2\140\1\15"+ "\1\340\1\15\13\140\2\15\1\140\2\15\4\140\2\15"+ "\15\140\1\15\1\140\1\15\3\140\1\15\11\140\2\15"+ - "\3\140\1\5\1\6\1\u0214\5\6\2\0\1\101\3\6"+ + "\3\140\1\5\1\6\1\u0216\5\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\1\6\1\u014f\1\6"+ @@ -1254,17 +1269,17 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) { "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\2\6\1\u0215\2\6\12\0\17\6\1\5\1\0\5\6"+ + "\2\6\1\u0217\2\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\5\6\12\0\11\6\1\u0216\5\6\1\5\1\0\5\6"+ + "\5\6\12\0\11\6\1\u0218\5\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\5\6\12\0\4\6\1\u0171\12\6\1\5\1\0\5\6"+ + "\5\6\12\0\4\6\1\u0172\12\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\2\6\1\u0217\2\6\12\0\17\6\1\5\1\0\5\6"+ - "\1\0\1\5\1\6\1\u01bf\5\6\2\0\1\101\3\6"+ + "\2\6\1\u0219\2\6\12\0\17\6\1\5\1\0\5\6"+ + "\1\0\1\5\1\6\1\u01c1\5\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\2\6\1\124\1\0"+ @@ -1272,74 +1287,74 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) { "\2\0\5\6\12\0\17\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\5\6"+ - "\12\0\1\6\1\u0218\15\6\1\5\1\0\5\6\1\0"+ + "\12\0\1\6\1\u021a\15\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\1\330\5\6\2\0"+ "\5\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ - "\2\6\2\0\1\5\3\0\6\6\2\0\1\u0219\4\6"+ + "\2\6\2\0\1\5\3\0\6\6\2\0\1\u021b\4\6"+ "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\3\6\1\0\1\6\1\0\1\5\2\6"+ - "\2\0\1\5\3\0\6\6\2\0\3\6\1\u021a\1\6"+ + "\2\0\1\5\3\0\6\6\2\0\3\6\1\u021c\1\6"+ "\12\0\17\6\1\5\1\0\5\6\1\0\1\5\7\6"+ "\2\0\1\101\1\364\2\6\1\0\1\6\1\0\1\5"+ "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\3\6\1\u021b\1\6\12\0"+ + "\1\5\3\0\6\6\2\0\3\6\1\u021d\1\6\12\0"+ "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\5\6\12\0\4\6\1\u021c"+ + "\1\5\3\0\6\6\2\0\5\6\12\0\4\6\1\u021e"+ "\12\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\4\6\1\u021d\12\0\2\6"+ - "\1\u021e\3\6\1\u021f\3\6\1\u0220\4\6\1\5\1\0"+ + "\1\5\3\0\6\6\2\0\4\6\1\u021f\12\0\2\6"+ + "\1\u0220\3\6\1\u0221\3\6\1\u0222\4\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\3\6\1\u0221\13\6\1\5\1\0"+ + "\2\0\5\6\12\0\3\6\1\u0223\13\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\3\6\1\u0222\13\6\1\5\1\0"+ + "\2\0\5\6\12\0\3\6\1\u0224\13\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\1\121\2\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\5\6"+ - "\1\0\1\5\1\6\1\u0223\5\6\2\0\1\101\3\6"+ + "\1\0\1\5\1\6\1\u0225\5\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\2\6\1\u0224\2\6\12\0\17\6\1\5\1\0\5\6"+ + "\2\6\1\u0226\2\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\1\6\1\173\1\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\1\6\1\u0225\3\6\12\0\17\6\1\5\1\0\5\6"+ - "\1\0\3\5\5\u0226\2\0\3\5\1\u0226\1\0\1\u0226"+ - "\1\0\3\5\2\0\1\5\3\0\1\5\2\u0226\1\5"+ - "\2\u0226\2\0\2\5\2\u0226\1\5\12\0\2\5\1\u0226"+ - "\1\5\1\u0226\3\5\1\u0226\7\5\1\0\1\5\2\u0226"+ - "\2\5\70\0\1\u0227\103\0\1\u0228\72\0\1\u018c\55\0"+ - "\1\u018c\125\0\1\u01d2\140\0\1\u01d2\66\0\1\u01d9\110\0"+ - "\1\u018c\76\0\1\u0229\121\0\1\u022a\130\0\1\u022b\104\0"+ - "\1\u022c\41\0\1\u0227\155\0\1\u0204\25\0\47\u020f\1\u018c"+ - "\40\u020f\43\0\1\u022d\126\0\1\u022e\70\0\1\u022f\44\0"+ + "\1\6\1\u0227\3\6\12\0\17\6\1\5\1\0\5\6"+ + "\1\0\3\5\5\u0228\2\0\3\5\1\u0228\1\0\1\u0228"+ + "\1\0\3\5\2\0\1\5\3\0\1\5\2\u0228\1\5"+ + "\2\u0228\2\0\2\5\2\u0228\1\5\12\0\2\5\1\u0228"+ + "\1\5\1\u0228\3\5\1\u0228\7\5\1\0\1\5\2\u0228"+ + "\2\5\70\0\1\u0229\103\0\1\u022a\72\0\1\u018d\55\0"+ + "\1\u018d\125\0\1\u01d4\140\0\1\u01d4\66\0\1\u01db\110\0"+ + "\1\u018d\76\0\1\u022b\121\0\1\u022c\130\0\1\u022d\104\0"+ + "\1\u022e\41\0\1\u0229\155\0\1\u0206\25\0\47\u0211\1\u018d"+ + "\40\u0211\43\0\1\u022f\126\0\1\u0230\70\0\1\u0231\44\0"+ "\1\5\3\6\2\322\2\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ "\2\0\5\6\12\0\17\6\1\5\1\0\1\6\1\322"+ "\3\6\1\0\1\5\3\6\2\322\2\6\2\0\1\101"+ "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\6\6\2\0\5\6\12\0\17\6\1\5\1\0"+ - "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u0171"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u0172"+ "\1\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\6\6\2\0\5\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ "\2\0\5\6\12\0\6\6\1\u0101\10\6\1\5\1\0"+ - "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u0230"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u0232"+ "\1\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\6\6\2\0\5\6\12\0\17\6\1\5\1\0"+ - "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u0231"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u0233"+ "\1\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\6\6\2\0\5\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ @@ -1347,25 +1362,25 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) { "\2\0\2\6\1\330\2\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\4\6\1\u0171\12\0\17\6\1\5\1\0\5\6"+ + "\2\0\4\6\1\u0172\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ "\5\6\12\0\4\6\1\364\12\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\2\6\1\u0232\2\6\12\0\17\6\1\5\1\0\5\6"+ + "\2\6\1\u0234\2\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\1\u01b4\4\6\12\0\3\6\1\u0233\13\6\1\5\1\0"+ + "\1\u01b6\4\6\12\0\3\6\1\u0235\13\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\14\6\1\u0234\2\6\1\5\1\0"+ - "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u0235"+ + "\2\0\5\6\12\0\14\6\1\u0236\2\6\1\5\1\0"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u0237"+ "\1\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\6\6\2\0\5\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\6\6\1\u0236\10\6\1\5\1\0"+ + "\2\0\5\6\12\0\6\6\1\u0238\10\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\364"+ "\1\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\6\6\2\0\2\6\1\u0118\2\6\12\0\17\6"+ @@ -1375,94 +1390,94 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) { "\2\6\12\0\13\6\1\u0117\3\6\1\5\1\0\5\6"+ "\1\0\1\5\3\6\2\322\2\6\2\0\1\101\3\6"+ "\1\0\1\322\1\0\1\5\2\6\2\0\1\5\3\0"+ - "\6\6\2\0\5\6\12\0\7\6\1\u0237\7\6\1\5"+ + "\6\6\2\0\5\6\12\0\7\6\1\u0239\7\6\1\5"+ "\1\0\1\6\2\322\2\6\1\0\1\5\7\6\2\0"+ - "\1\101\1\6\1\u0238\1\6\1\0\1\6\1\0\1\5"+ + "\1\101\1\6\1\u023a\1\6\1\0\1\6\1\0\1\5"+ "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ "\17\6\1\5\1\0\5\6\1\0\1\5\7\6\2\0"+ "\1\101\3\6\1\0\1\6\1\0\1\5\2\6\2\0"+ - "\1\5\3\0\6\6\2\0\2\6\1\u0239\2\6\12\0"+ + "\1\5\3\0\6\6\2\0\2\6\1\u023b\2\6\12\0"+ "\17\6\1\5\1\0\5\6\1\0\3\5\5\242\2\0"+ "\3\5\1\242\1\0\1\242\1\0\3\5\2\0\1\5"+ "\3\0\1\5\2\242\1\5\2\242\2\0\2\5\2\242"+ "\1\5\12\0\2\5\1\242\1\5\1\242\3\5\1\242"+ - "\7\5\1\0\1\5\2\242\2\5\46\0\1\u023a\110\0"+ - "\1\u023b\77\0\1\u023c\32\0\1\u023d\60\0\1\u01d2\112\0"+ - "\1\u023e\105\0\1\u023f\111\0\1\u0240\124\0\1\u0241\114\0"+ - "\1\u0242\20\0\1\5\7\6\2\0\1\101\3\6\1\0"+ + "\7\5\1\0\1\5\2\242\2\5\46\0\1\u023c\110\0"+ + "\1\u023d\77\0\1\u023e\32\0\1\u023f\60\0\1\u01d4\112\0"+ + "\1\u0240\105\0\1\u0241\111\0\1\u0242\124\0\1\u0243\114\0"+ + "\1\u0244\20\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\13\6\1\u0243\3\6\1\5\1\0"+ + "\2\0\5\6\12\0\13\6\1\u0245\3\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\6\6\1\u0244\10\6\1\5\1\0"+ + "\2\0\5\6\12\0\6\6\1\u0246\10\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\1\6\1\u0245\15\6\1\5\1\0"+ + "\2\0\5\6\12\0\1\6\1\u0247\15\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\2\6\1\u0246\2\6\12\0\17\6\1\5\1\0"+ - "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u0247"+ + "\2\0\2\6\1\u0248\2\6\12\0\17\6\1\5\1\0"+ + "\5\6\1\0\1\5\7\6\2\0\1\101\1\6\1\u0249"+ "\1\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ "\3\0\6\6\2\0\5\6\12\0\17\6\1\5\1\0"+ "\5\6\1\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\1\u0248\4\6\12\0\17\6\1\5\1\0\5\6"+ + "\2\0\1\u024a\4\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\1\6\1\u0249\3\6\12\0\17\6\1\5\1\0\5\6"+ + "\1\6\1\u024b\3\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\4\6\1\322\2\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ "\1\361\4\6\12\0\17\6\1\5\1\0\5\6\1\0"+ - "\1\5\7\6\2\0\1\101\1\6\1\u024a\1\6\1\0"+ + "\1\5\7\6\2\0\1\101\1\6\1\u024c\1\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ - "\2\0\5\6\12\0\5\6\1\u024b\11\6\1\5\1\0"+ - "\5\6\33\0\1\u018c\122\0\1\u024c\131\0\1\u024d\65\0"+ - "\1\u024e\104\0\1\u024f\142\0\1\u018c\44\0\1\u020f\117\0"+ - "\1\u020f\107\0\1\u0250\45\0\1\5\7\6\2\0\1\101"+ + "\2\0\5\6\12\0\5\6\1\u024d\11\6\1\5\1\0"+ + "\5\6\33\0\1\u018d\122\0\1\u024e\131\0\1\u024f\65\0"+ + "\1\u0250\104\0\1\u0251\142\0\1\u018d\44\0\1\u0211\117\0"+ + "\1\u0211\107\0\1\u0252\45\0\1\5\7\6\2\0\1\101"+ "\3\6\1\0\1\6\1\0\1\5\2\6\2\0\1\5"+ - "\3\0\6\6\2\0\1\u016e\4\6\12\0\17\6\1\5"+ + "\3\0\6\6\2\0\1\u016f\4\6\12\0\17\6\1\5"+ "\1\0\5\6\1\0\1\5\7\6\2\0\1\101\3\6"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ - "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\1\u0219"+ + "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\1\u021b"+ "\4\6\1\0\1\5\7\6\2\0\1\101\2\6\1\322"+ "\1\0\1\6\1\0\1\5\2\6\2\0\1\5\3\0"+ "\6\6\2\0\5\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\5\6\12\0\4\6\1\u0251\12\6\1\5\1\0\5\6"+ + "\5\6\12\0\4\6\1\u0253\12\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\3\6\1\u0252\1\6\12\0\17\6\1\5\1\0\5\6"+ + "\3\6\1\u0254\1\6\12\0\17\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\1\u0253\4\6\12\0\17\6\1\5\1\0\5\6\1\0"+ + "\1\u0255\4\6\12\0\17\6\1\5\1\0\5\6\1\0"+ "\1\5\7\6\2\0\1\101\3\6\1\0\1\6\1\0"+ "\1\5\2\6\2\0\1\5\3\0\6\6\2\0\1\375"+ "\4\6\12\0\17\6\1\5\1\0\5\6\1\0\1\5"+ "\7\6\2\0\1\101\3\6\1\0\1\6\1\0\1\5"+ "\2\6\2\0\1\5\3\0\6\6\2\0\5\6\12\0"+ - "\6\6\1\u0171\10\6\1\5\1\0\5\6\16\0\1\u0254"+ - "\136\0\1\u0255\105\0\1\u0256\111\0\1\u0257\134\0\1\u0258"+ + "\6\6\1\u0172\10\6\1\5\1\0\5\6\16\0\1\u0256"+ + "\136\0\1\u0257\105\0\1\u0258\111\0\1\u0259\134\0\1\u025a"+ "\16\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ - "\5\6\12\0\6\6\1\u0259\10\6\1\5\1\0\5\6"+ + "\5\6\12\0\6\6\1\u025b\10\6\1\5\1\0\5\6"+ "\1\0\1\5\7\6\2\0\1\101\3\6\1\0\1\6"+ "\1\0\1\5\2\6\2\0\1\5\3\0\6\6\2\0"+ "\5\6\12\0\7\6\1\u0162\7\6\1\5\1\0\5\6"+ - "\1\0\1\5\7\6\2\0\1\101\1\u0259\2\6\1\0"+ + "\1\0\1\5\7\6\2\0\1\101\1\u025b\2\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ "\2\0\5\6\12\0\17\6\1\5\1\0\5\6\45\0"+ - "\1\u025a\75\0\1\u0257\122\0\1\u018c\125\0\1\u018c\106\0"+ - "\1\u025b\25\0\1\5\7\6\2\0\1\101\3\6\1\0"+ + "\1\u025c\75\0\1\u0259\122\0\1\u018d\125\0\1\u018d\106\0"+ + "\1\u025d\25\0\1\5\7\6\2\0\1\101\3\6\1\0"+ "\1\6\1\0\1\5\2\6\2\0\1\5\3\0\6\6"+ "\2\0\3\6\1\317\1\6\12\0\17\6\1\5\1\0"+ - "\5\6\43\0\1\u023f\132\0\1\u020f\22\0"; + "\5\6\43\0\1\u0241\132\0\1\u0211\22\0"; private static int [] zzUnpackTrans() { - int [] result = new int[40896]; + int [] result = new int[41040]; int offset = 0; offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); return result; @@ -1488,7 +1503,7 @@ private static int zzUnpackTrans(String packed, int offset, int [] result) { private static final int ZZ_PUSHBACK_2BIG = 2; /* error messages for the codes above */ - private static final String ZZ_ERROR_MSG[] = { + private static final String[] ZZ_ERROR_MSG = { "Unknown internal scanner error", "Error: could not match input", "Error: pushback value was too large" @@ -1505,13 +1520,13 @@ private static int zzUnpackTrans(String packed, int offset, int [] result) { "\22\1\1\11\1\1\1\11\41\1\1\0\34\1\2\0"+ "\1\11\4\0\1\11\2\0\1\11\20\0\1\1\2\0"+ "\3\1\1\0\5\1\1\11\25\1\1\11\101\1\37\0"+ - "\15\1\1\11\57\1\2\0\1\1\2\0\1\1\10\0"+ - "\1\11\17\0\51\1\35\0\37\1\22\0\24\1\2\0"+ + "\15\1\1\11\60\1\2\0\1\1\2\0\1\1\10\0"+ + "\1\11\17\0\52\1\35\0\37\1\22\0\24\1\2\0"+ "\1\1\6\0\12\1\11\0\11\1\5\0\3\1\5\0"+ "\1\1\2\0"; private static int [] zzUnpackAttribute() { - int [] result = new int[603]; + int [] result = new int[605]; int offset = 0; offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); return result; @@ -1540,7 +1555,7 @@ private static int zzUnpackAttribute(String packed, int offset, int [] result) { /** this buffer contains the current text to be matched and is the source of the yytext() string */ - private char zzBuffer[]; + private CharSequence zzBuffer = ""; /** the textposition at the last accepting state */ private int zzMarkedPos; @@ -1555,19 +1570,7 @@ the source of the yytext() string */ from input */ private int zzEndRead; - /** number of newlines encountered up to the start of the matched text */ - private int yyline; - - /** the number of characters up to the start of the matched text */ - private int yychar; - /** - * the number of characters from the last newline up to the start of the - * matched text - */ - private int yycolumn; - - /** * zzAtBOL == true <=> the scanner is currently at the beginning of a line */ private boolean zzAtBOL = true; @@ -1577,14 +1580,6 @@ the source of the yytext() string */ /** denotes if the user-EOF-code has already been executed */ private boolean zzEOFDone; - - /** - * The number of occupied positions in zzBuffer beyond zzEndRead. - * When a lead/high surrogate has been read from the input stream - * into the final zzBuffer position, this will have a value of 1; - * otherwise, it will have a value of 0. - */ - private int zzFinalHighSurrogate = 0; /* user code: */ public static final String SYNTAX_STYLE_BYTECODE = "text/bytecode"; @@ -1597,7 +1592,6 @@ the source of the yytext() string */ public BytecodeTokenizer() { } - /** * Adds the token specified to the current linked list of tokens. * @@ -1606,7 +1600,7 @@ public BytecodeTokenizer() { */ private void addHyperlinkToken(int start, int end, int tokenType) { int so = start + offsetShift; - addToken(zzBuffer, start,end, tokenType, so, true); + addToken(zzBuffer.toString().toCharArray(), start,end, tokenType, so, true); } @@ -1628,7 +1622,7 @@ private void addToken(int tokenType) { */ private void addToken(int start, int end, int tokenType) { int so = start + offsetShift; - addToken(zzBuffer, start,end, tokenType, so, false); + addToken(zzBuffer.toString().toCharArray(), start,end, tokenType, so, false); } @@ -1675,18 +1669,18 @@ public String[] getLineCommentStartAndEnd(int languageIndex) { public Token getTokenList(Segment text, int initialTokenType, int startOffset) { resetTokenList(); - this.offsetShift = -text.offset + startOffset; + this.offsetShift = startOffset; // Start off in the proper state. int state = Token.NULL; switch (initialTokenType) { case Token.COMMENT_MULTILINE: state = MLC; - start = text.offset; + start = 0; break; case Token.COMMENT_DOCUMENTATION: state = DOCCOMMENT; - start = text.offset; + start = 0; break; default: state = Token.NULL; @@ -1694,8 +1688,7 @@ public Token getTokenList(Segment text, int initialTokenType, int startOffset) { s = text; try { - yyreset(zzReader); - yybegin(state); + reset(text, 0, text.count, state); return yylex(); } catch (IOException ioe) { ioe.printStackTrace(); @@ -1705,51 +1698,6 @@ public Token getTokenList(Segment text, int initialTokenType, int startOffset) { } - /** - * Refills the input buffer. - * - * Note: this is the correct zzRefill. Delete the other one. - * - * @return true if EOF was reached, otherwise - * false. - */ - private boolean zzRefill() { - return zzCurrentPos>=s.offset+s.count; - } - - - /** - * Resets the scanner to read from a new input stream. - * Does not close the old reader. - * - * All internal variables are reset, the old input stream - * cannot be reused (internal buffer is discarded and lost). - * Lexical state is set to YY_INITIAL. - * - * Note: this is the correct yyreset. Delete the other one. - * - * @param reader the new input stream - */ - public final void yyreset(Reader reader) { - // 's' has been updated. - zzBuffer = s.array; - /* - * We replaced the line below with the two below it because zzRefill - * no longer "refills" the buffer (since the way we do it, it's always - * "full" the first time through, since it points to the segment's - * array). So, we assign zzEndRead here. - */ - zzStartRead = zzEndRead = s.offset; - zzStartRead = s.offset; - zzEndRead = zzStartRead + s.count - 1; - zzCurrentPos = zzMarkedPos = s.offset; - zzLexicalState = YYINITIAL; - zzReader = reader; - zzAtBOL = true; - zzAtEOF = false; - } - - /** @@ -1769,10 +1717,14 @@ public BytecodeTokenizer(java.io.Reader in) { * @return the unpacked character translation table */ private static char [] zzUnpackCMap(String packed) { - char [] map = new char[0x110000]; + int size = 0; + for (int i = 0, length = packed.length(); i < length; i += 2) { + size += packed.charAt(i); + } + char[] map = new char[size]; int i = 0; /* index in packed string */ int j = 0; /* index in unpacked array */ - while (i < 216) { + while (i < packed.length()) { int count = packed.charAt(i++); char value = packed.charAt(i++); do map[j++] = value; while (--count > 0); @@ -1780,16 +1732,32 @@ public BytecodeTokenizer(java.io.Reader in) { return map; } - + public final int getTokenStart() { + return zzStartRead; + } + + public final int getTokenEnd() { + return getTokenStart() + yylength(); + } + + public void reset(CharSequence buffer, int start, int end, int initialState) { + zzBuffer = buffer; + zzCurrentPos = zzMarkedPos = zzStartRead = start; + zzAtEOF = false; + zzAtBOL = true; + zzEndRead = end; + yybegin(initialState); + } + /** - * Closes the input stream. + * Refills the input buffer. + * + * @return false, iff there was new input. + * + * @exception java.io.IOException if any I/O-Error occurs */ - public final void yyclose() throws java.io.IOException { - zzAtEOF = true; /* indicate end of file */ - zzEndRead = zzStartRead; /* invalidate buffer */ - - if (zzReader != null) - zzReader.close(); + private boolean zzRefill() throws java.io.IOException { + return true; } @@ -1814,24 +1782,24 @@ public final void yybegin(int newState) { /** * Returns the text matched by the current regular expression. */ - public final String yytext() { - return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); + public final CharSequence yytext() { + return zzBuffer.subSequence(zzStartRead, zzMarkedPos); } /** - * Returns the character at position pos from the - * matched text. - * + * Returns the character at position pos from the + * matched text. + * * It is equivalent to yytext().charAt(pos), but faster * - * @param pos the position of the character to fetch. + * @param pos the position of the character to fetch. * A value from 0 to yylength()-1. * * @return the character at position pos */ public final char yycharat(int pos) { - return zzBuffer[zzStartRead+pos]; + return zzBuffer.charAt(zzStartRead+pos); } @@ -1844,10 +1812,10 @@ public final int yylength() { /** - * Reports an error that occured while scanning. + * Reports an error that occurred while scanning. * - * In a wellformed scanner (no or only correct usage of - * yypushback(int) and a match-all fallback rule) this method + * In a wellformed scanner (no or only correct usage of + * yypushback(int) and a match-all fallback rule) this method * will only be called with things that "Can't Possibly Happen". * If this method is called, something is seriously wrong * (e.g. a JFlex bug producing a faulty scanner etc.). @@ -1867,7 +1835,7 @@ private void zzScanError(int errorCode) { } throw new Error(message); - } + } /** @@ -1901,8 +1869,7 @@ public org.fife.ui.rsyntaxtextarea.Token yylex() throws java.io.IOException { int zzCurrentPosL; int zzMarkedPosL; int zzEndReadL = zzEndRead; - char [] zzBufferL = zzBuffer; - char [] zzCMapL = ZZ_CMAP; + CharSequence zzBufferL = zzBuffer; int [] zzTransL = ZZ_TRANS; int [] zzRowMapL = ZZ_ROWMAP; @@ -1914,7 +1881,7 @@ public org.fife.ui.rsyntaxtextarea.Token yylex() throws java.io.IOException { zzAction = -1; zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; - + zzState = ZZ_LEXSTATE[zzLexicalState]; // set up zzAction for empty match case: @@ -1926,9 +1893,10 @@ public org.fife.ui.rsyntaxtextarea.Token yylex() throws java.io.IOException { zzForAction: { while (true) { - + if (zzCurrentPosL < zzEndReadL) { - zzInput = zzBufferL[zzCurrentPosL++]; + zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL/*, zzEndReadL*/); + zzCurrentPosL += Character.charCount(zzInput); } else if (zzAtEOF) { zzInput = YYEOF; @@ -1949,10 +1917,11 @@ else if (zzAtEOF) { break zzForAction; } else { - zzInput = zzBufferL[zzCurrentPosL++]; + zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL/*, zzEndReadL*/); + zzCurrentPosL += Character.charCount(zzInput); } } - int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ]; + int zzNext = zzTransL[ zzRowMapL[zzState] + ZZ_CMAP(zzInput) ]; if (zzNext == -1) break zzForAction; zzState = zzNext; @@ -1971,23 +1940,23 @@ else if (zzAtEOF) { if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { zzAtEOF = true; - switch (zzLexicalState) { + switch (zzLexicalState) { case YYINITIAL: { addNullToken(); return firstToken; - } - case 604: break; + } // fall though + case 606: break; case MLC: { addToken(start,zzStartRead-1, Token.COMMENT_MULTILINE); return firstToken; - } - case 605: break; + } // fall though + case 607: break; case DOCCOMMENT: { yybegin(YYINITIAL); addToken(start,zzEndRead, Token.COMMENT_DOCUMENTATION); return firstToken; - } - case 606: break; + } // fall though + case 608: break; case EOL_COMMENT: { addToken(start,zzStartRead-1, Token.COMMENT_EOL); addNullToken(); return firstToken; - } - case 607: break; + } // fall though + case 609: break; default: return null; } @@ -1996,159 +1965,197 @@ else if (zzAtEOF) { switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { case 1: { addToken(Token.ERROR_IDENTIFIER); - } + } + // fall through case 39: break; case 2: { addToken(Token.IDENTIFIER); - } + } + // fall through case 40: break; case 3: { addToken(Token.LITERAL_NUMBER_DECIMAL_INT); - } + } + // fall through case 41: break; case 4: { addToken(Token.ERROR_CHAR); addNullToken(); return firstToken; - } + } + // fall through case 42: break; case 5: { addNullToken(); return firstToken; - } + } + // fall through case 43: break; case 6: { addToken(Token.ERROR_STRING_DOUBLE); addNullToken(); return firstToken; - } + } + // fall through case 44: break; case 7: { addToken(Token.OPERATOR); - } + } + // fall through case 45: break; case 8: { addToken(Token.WHITESPACE); - } + } + // fall through case 46: break; case 9: { addToken(Token.SEPARATOR); - } + } + // fall through case 47: break; case 10: { addToken(Token.ANNOTATION); - } + } + // fall through case 48: break; case 11: { - } + } + // fall through case 49: break; case 12: { addToken(start,zzStartRead-1, Token.COMMENT_MULTILINE); return firstToken; - } + } + // fall through case 50: break; case 13: { addToken(start,zzStartRead-1, Token.COMMENT_DOCUMENTATION); return firstToken; - } + } + // fall through case 51: break; case 14: { addToken(start,zzStartRead-1, Token.COMMENT_EOL); addNullToken(); return firstToken; - } + } + // fall through case 52: break; case 15: { addToken(Token.ERROR_NUMBER_FORMAT); - } + } + // fall through case 53: break; case 16: { addToken(Token.LITERAL_NUMBER_FLOAT); - } + } + // fall through case 54: break; case 17: { addToken(Token.LITERAL_NUMBER_HEXADECIMAL); - } + } + // fall through case 55: break; case 18: { addToken(Token.ERROR_CHAR); - } + } + // fall through case 56: break; case 19: { addToken(Token.LITERAL_STRING_DOUBLE_QUOTE); - } + } + // fall through case 57: break; case 20: { addToken(TOKENTYPE_LABEL); - } + } + // fall through case 58: break; case 21: { start = zzMarkedPos-2; yybegin(EOL_COMMENT); - } + } + // fall through case 59: break; case 22: { start = zzMarkedPos-2; yybegin(MLC); - } + } + // fall through case 60: break; case 23: { addToken(Token.RESERVED_WORD); - } + } + // fall through case 61: break; case 24: { yybegin(YYINITIAL); addToken(start,zzStartRead+1, Token.COMMENT_MULTILINE); - } + } + // fall through case 62: break; case 25: { yybegin(YYINITIAL); addToken(start,zzStartRead+1, Token.COMMENT_DOCUMENTATION); - } + } + // fall through case 63: break; case 26: { int temp=zzStartRead; addToken(start,zzStartRead-1, Token.COMMENT_DOCUMENTATION); addToken(temp,zzMarkedPos-1, Token.COMMENT_MARKUP); start = zzMarkedPos; - } + } + // fall through case 64: break; case 27: { int temp=zzStartRead; addToken(start,zzStartRead-1, Token.COMMENT_EOL); addToken(temp,zzMarkedPos-1, TOKENTYPE_LABEL); start = zzMarkedPos; - } + } + // fall through case 65: break; case 28: { addToken(Token.LITERAL_CHAR); - } + } + // fall through case 66: break; case 29: { addToken(Token.ERROR_STRING_DOUBLE); - } + } + // fall through case 67: break; case 30: { start = zzMarkedPos-3; yybegin(DOCCOMMENT); - } + } + // fall through case 68: break; case 31: { addToken(Token.DATA_TYPE); - } + } + // fall through case 69: break; case 32: { addToken(Token.COMMENT_MULTILINE); - } + } + // fall through case 70: break; case 33: { addToken(Token.LITERAL_BOOLEAN); - } + } + // fall through case 71: break; case 34: { int temp=zzStartRead; addToken(start,zzStartRead-1, Token.COMMENT_MULTILINE); addHyperlinkToken(temp,zzMarkedPos-1, Token.COMMENT_MULTILINE); start = zzMarkedPos; - } + } + // fall through case 72: break; case 35: { int temp=zzStartRead; addToken(start,zzStartRead-1, Token.COMMENT_DOCUMENTATION); addHyperlinkToken(temp,zzMarkedPos-1, Token.COMMENT_DOCUMENTATION); start = zzMarkedPos; - } + } + // fall through case 73: break; case 36: { int temp=zzStartRead; addToken(start,zzStartRead-1, Token.COMMENT_DOCUMENTATION); addToken(temp,zzMarkedPos-1, Token.COMMENT_KEYWORD); start = zzMarkedPos; - } + } + // fall through case 74: break; case 37: { int temp=zzStartRead; addToken(start,zzStartRead-1, Token.COMMENT_EOL); addHyperlinkToken(temp,zzMarkedPos-1, Token.COMMENT_EOL); start = zzMarkedPos; - } + } + // fall through case 75: break; case 38: { addToken(Token.RESERVED_WORD_2); - } + } + // fall through case 76: break; default: zzScanError(ZZ_NO_MATCH); - } + } } } } diff --git a/src/main/java/club/bytecode/the/jda/gui/fileviewer/DecompileThread.java b/src/main/java/club/bytecode/the/jda/gui/fileviewer/DecompileThread.java index 8c134b6..ede9ba4 100644 --- a/src/main/java/club/bytecode/the/jda/gui/fileviewer/DecompileThread.java +++ b/src/main/java/club/bytecode/the/jda/gui/fileviewer/DecompileThread.java @@ -45,30 +45,34 @@ public void run() { decompiler.applyFilters(cn); decompileResult = decompiler.decompileClassNode(viewer.getFile().container, cn); } - String text = stripUndisplayableChars(decompileResult); - RSyntaxTextArea panelArea; - if (decompiler instanceof BytecodeDecompiler) { - panelArea = new BytecodeSyntaxArea(text); - } else { - panelArea = new JDATextArea(text); - } - final RTextScrollPane scrollPane = new RTextScrollPane(panelArea); - StringBuilder topLabelText = new StringBuilder(decompiler.getName()); - for (DecompileFilter filter : decompiler.getSettings().getEnabledFilters()) { - topLabelText.append(" + ").append(filter.getName()); - } - scrollPane.setColumnHeaderView(new JLabel(topLabelText.toString())); - SwingUtilities.invokeLater(() -> target.add(scrollPane)); - viewer.updatePane(paneId, panelArea, decompiler); + SwingUtilities.invokeLater(() -> { + RSyntaxTextArea panelArea; + if (decompiler instanceof BytecodeDecompiler) { + panelArea = new BytecodeSyntaxArea(text); + } else { + panelArea = new JDATextArea(text); + } + + final RTextScrollPane scrollPane = new RTextScrollPane(panelArea); + StringBuilder topLabelText = new StringBuilder(decompiler.getName()); + for (DecompileFilter filter : decompiler.getSettings().getEnabledFilters()) { + topLabelText.append(" + ").append(filter.getName()); + } + scrollPane.setColumnHeaderView(new JLabel(topLabelText.toString())); + target.add(scrollPane); + viewer.updatePane(paneId, panelArea, decompiler); + }); } catch (Exception e) { new ExceptionUI(e, "decompiling " + viewer.getFile().name); } finally { - viewer.resetDivider(); - JDA.setBusy(false); - if (button != null) - button.setEnabled(true); + SwingUtilities.invokeLater(() -> { + viewer.resetDivider(); + JDA.setBusy(false); + if (button != null) + button.setEnabled(true); + }); } } diff --git a/src/main/java/club/bytecode/the/jda/gui/fileviewer/JDATextArea.java b/src/main/java/club/bytecode/the/jda/gui/fileviewer/JDATextArea.java index b2492a6..49535eb 100644 --- a/src/main/java/club/bytecode/the/jda/gui/fileviewer/JDATextArea.java +++ b/src/main/java/club/bytecode/the/jda/gui/fileviewer/JDATextArea.java @@ -159,7 +159,7 @@ else if (isStringSelected()) { } else return; - new SearchDialog(tokenName, JDA.search(tokenName)).setVisible(true); + new SearchDialog(tokenName, JDA.searchCallback.apply(tokenName)).setVisible(true); } private void doRenameDialog() { diff --git a/src/main/java/club/bytecode/the/jda/gui/fileviewer/ViewerFile.java b/src/main/java/club/bytecode/the/jda/gui/fileviewer/ViewerFile.java index 9a9dd58..c4e2cfb 100644 --- a/src/main/java/club/bytecode/the/jda/gui/fileviewer/ViewerFile.java +++ b/src/main/java/club/bytecode/the/jda/gui/fileviewer/ViewerFile.java @@ -5,18 +5,20 @@ import java.util.Objects; // really just a pair of FileContainer and String name -public class ViewerFile { +public class ViewerFile implements Comparable { public final FileContainer container; public final String name; + private final String toStringCached; public ViewerFile(FileContainer container, String name) { this.container = container; this.name = name; + toStringCached = container + "$" + name; } @Override public String toString() { - return container + "$" + name; + return toStringCached; } @Override @@ -24,12 +26,19 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ViewerFile that = (ViewerFile) o; - return Objects.equals(container, that.container) && + boolean result = Objects.equals(container, that.container) && Objects.equals(name, that.name); + assert (result == (compareTo(that) == 0)); + return result; } @Override public int hashCode() { return Objects.hash(container, name); } + + @Override + public int compareTo(ViewerFile other) { + return toStringCached.compareTo(other.toStringCached); + } } diff --git a/src/main/java/club/bytecode/the/jda/gui/search/SearchDialog.java b/src/main/java/club/bytecode/the/jda/gui/search/SearchDialog.java index 0fa9437..95e1cca 100644 --- a/src/main/java/club/bytecode/the/jda/gui/search/SearchDialog.java +++ b/src/main/java/club/bytecode/the/jda/gui/search/SearchDialog.java @@ -1,41 +1,168 @@ package club.bytecode.the.jda.gui.search; -import club.bytecode.the.jda.FileContainer; import club.bytecode.the.jda.JDA; +import club.bytecode.the.jda.gui.components.SortedUniqueListModel; import club.bytecode.the.jda.gui.fileviewer.ViewerFile; import club.bytecode.the.jda.settings.Settings; import net.miginfocom.swing.MigLayout; -import org.mapleir.stdlib.util.Pair; import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.Iterator; import java.util.List; public class SearchDialog extends JDialog { + private final List searchResults; + private final JList list; + private final JTextArea searchBox; + private final ListModel listModel; + + private String oldFilter = ""; + public SearchDialog(String needle, List matches) { super(new JFrame(), "Search Results", true); + searchResults = matches; Container pane = getContentPane(); pane.setPreferredSize(new Dimension(850, 400)); pane.setLayout(new MigLayout("fill")); pane.add(new JLabel(needle + " found in:"), "pushx, growx, wrap"); - JList> list = new JList(matches.toArray()); + listModel = createSortedListModel(); + list = new JList<>(listModel); list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); list.setLayoutOrientation(JList.VERTICAL); + list.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent evt) { JList list = (JList)evt.getSource(); if (evt.getClickCount() == 2) { int index = list.locationToIndex(evt.getPoint()); - ViewerFile vf = matches.get(index); - JDA.viewer.navigator.openClassFileToWorkSpace(vf); + openResult(index); + } + } + }); + + searchBox = new JTextArea(); + searchBox.setRows(1); + searchBox.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + e.consume(); + list.requestFocus(); } } + + @Override + public void keyReleased(KeyEvent e) { + } + }); + + searchBox.getDocument().addDocumentListener(new DocumentListener(){ + @Override public void insertUpdate(DocumentEvent e) { filter(); } + @Override public void removeUpdate(DocumentEvent e) { filter(); } + @Override public void changedUpdate(DocumentEvent e) {} + private void filter() { + String filter = searchBox.getText(); + updateFilter((SortedUniqueListModel) list.getModel(), filter); + oldFilter = filter; + } + }); + + list.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + if (e.getKeyChar() == '\n') e.consume(); + else focusSearch(e); + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) openResult(list.getSelectedIndex()); + else focusSearch(e); + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) e.consume(); + else focusSearch(e); + } }); + list.setFont(Settings.getCodeFont()); JScrollPane listScroller = new JScrollPane(list); - pane.add(listScroller, "grow, push"); + + pane.add(listScroller, "grow, push, wrap"); + pane.add(searchBox, "grow"); pack(); } + + public void openResult(int index) { + ViewerFile vf = listModel.getElementAt(index); + JDA.viewer.navigator.openClassFileToWorkSpace(vf); + } + + public void focusSearch(KeyEvent e) { + searchBox.setText(""); + searchBox.requestFocus(); + forwardKeyEvent(searchBox, e); + } + + private void forwardKeyEvent(Component receiver, KeyEvent e) { + receiver.dispatchEvent(new KeyEvent(receiver, e.getID(), e.getWhen(), e.getModifiers(), e.getKeyCode(), e.getKeyChar())); + } + + private ListModel createSortedListModel() { + SortedUniqueListModel model = new SortedUniqueListModel<>(); + model.addAll(searchResults); + return model; + } + + public void updateFilter(SortedUniqueListModel model, String filter) { + if (oldFilter.equals(filter)) + return; + + if (filter.isEmpty()) { + model.deferUpdates(); + model.clear(); + model.addAll(searchResults); + model.commitUpdates(); + return; + } + + model.deferUpdates(); // make sure to commit me + String filterLower = filter.toLowerCase(); + String oldFilterLower = oldFilter.toLowerCase(); + if (oldFilterLower.contains(filterLower)) { + for (ViewerFile vf : searchResults) { + String s = vf.toString().toLowerCase(); + if (s.contains(filterLower)) { + model.add(vf); + } + } + } else if (filterLower.contains(oldFilterLower)) { + for (Iterator it = model.iterator(); it.hasNext(); ) { + ViewerFile vf = it.next(); // copy because we remove as we iterate. inefficient + if (!vf.toString().toLowerCase().contains(filterLower)) { + it.remove(); + } + } + } else for (ViewerFile vf : searchResults) { + if (!vf.toString().toLowerCase().contains(filter)) { + model.removeElement(vf); + } else { + model.add(vf); + } + } + model.commitUpdates(); + } } diff --git a/src/main/java/club/bytecode/the/jda/util/BytecodeUtils.java b/src/main/java/club/bytecode/the/jda/util/BytecodeUtils.java new file mode 100644 index 0000000..4b022db --- /dev/null +++ b/src/main/java/club/bytecode/the/jda/util/BytecodeUtils.java @@ -0,0 +1,35 @@ +package club.bytecode.the.jda.util; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.commons.JSRInlinerAdapter; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +public class BytecodeUtils { + public static byte[] dumpClassToBytes(ClassNode cn) { + // we have to do this, or else decompile filters don't work. + try { + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); + cn.accept(writer); + return writer.toByteArray(); + } catch (Exception e) { + System.err.println("Exception while dumping class " + cn.name + ": "); + e.printStackTrace(); + return null; + } + } + + public static MethodNode applyJsrInlineAdapter(MethodNode mn) { + final JSRInlinerAdapter adapter = new JSRInlinerAdapter(mn, mn.access, mn.name, mn.desc, mn.signature, mn.exceptions.toArray(new String[0])); + mn.accept(adapter); + return adapter; + } + + public static ClassNode loadClass(byte[] bytes) { + ClassReader reader = new ClassReader(bytes); + ClassNode classNode = new ClassNode(); + reader.accept(classNode, ClassReader.EXPAND_FRAMES); + return classNode; + } +} diff --git a/src/main/java/club/bytecode/the/jda/util/GuiUtils.java b/src/main/java/club/bytecode/the/jda/util/GuiUtils.java index 09c6a94..da72aab 100644 --- a/src/main/java/club/bytecode/the/jda/util/GuiUtils.java +++ b/src/main/java/club/bytecode/the/jda/util/GuiUtils.java @@ -48,4 +48,17 @@ public static void setLookAndFeel() { } } } + + public static void sleep(long millis) { + try { + Thread.sleep(millis); + } catch(InterruptedException e) { + e.printStackTrace(); + } + } + + public static void setAntialiasingSettings() { + System.setProperty("awt.useSystemAAFontSettings", "lcd"); + System.setProperty("swing.aatext", "true"); + } } diff --git a/src/main/resources/club/bytecode/the/jda/html/about.html b/src/main/resources/club/bytecode/the/jda/html/about.html index 6717a48..3df637b 100644 --- a/src/main/resources/club/bytecode/the/jda/html/about.html +++ b/src/main/resources/club/bytecode/the/jda/html/about.html @@ -4,7 +4,7 @@

JDA - The Java Disassembler

Version $JDA_VERSION$
-(c) 2016-2018 ecx86
+(c) 2016-2019 ecx86
Icon by Brayden Gregerson