Commit b55975a3 authored by Skylot's avatar Skylot

core: reformat code and fix small issues in BinaryXMLParser

parent 4cb34394
...@@ -41,7 +41,6 @@ subprojects { ...@@ -41,7 +41,6 @@ subprojects {
} }
dependencies { dependencies {
compile 'com.google.android:android:4.1.1.4'
compile 'org.slf4j:slf4j-api:1.7.7' compile 'org.slf4j:slf4j-api:1.7.7'
testCompile 'ch.qos.logback:logback-classic:1.1.2' testCompile 'ch.qos.logback:logback-classic:1.1.2'
......
...@@ -8,17 +8,10 @@ import java.io.File; ...@@ -8,17 +8,10 @@ import java.io.File;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
//import jadx.core.xmlgen.BinaryXMLParser;
public class JadxCLI { public class JadxCLI {
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class); private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
public static void main(String[] args) throws JadxException { public static void main(String[] args) throws JadxException {
/*
BinaryXMLParser bxp = new BinaryXMLParser(args[0],args[1]);
bxp.parse();
System.exit(4);
*/
try { try {
JadxCLIArgs jadxArgs = new JadxCLIArgs(); JadxCLIArgs jadxArgs = new JadxCLIArgs();
if (processArgs(jadxArgs, args)) { if (processArgs(jadxArgs, args)) {
......
...@@ -47,7 +47,7 @@ public final class JadxCLIArgs implements IJadxArgs { ...@@ -47,7 +47,7 @@ public final class JadxCLIArgs implements IJadxArgs {
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true) @Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
protected boolean printHelp = false; protected boolean printHelp = false;
@Parameter(names = {"-x", "--xml"}, description = "try to decode the AndroidManifest.xml, save at current dir") @Parameter(names = {"-x", "--xml"}, description = "try to decode the AndroidManifest.xml")
protected boolean xmlTest = false; protected boolean xmlTest = false;
private final List<File> input = new ArrayList<File>(1); private final List<File> input = new ArrayList<File>(1);
......
...@@ -203,14 +203,17 @@ public final class JadxDecompiler { ...@@ -203,14 +203,17 @@ public final class JadxDecompiler {
reset(); reset();
root = new RootNode(); root = new RootNode();
LOG.info("loading ..."); LOG.info("loading ...");
if(this.args.isXMLTest()) { if (this.args.isXMLTest()) {
InputFile inf = inputFiles.get(0); InputFile inf = inputFiles.get(0);
try { try {
BinaryXMLParser bxp = new BinaryXMLParser(InputFile.loadXMLBuffer(inf.getFile()), "./AndroidManifest.xml"); byte[] buffer = InputFile.loadXMLBuffer(inf.getFile());
//BinaryXMLParser bxp = new BinaryXMLParser(InputFile.loadXMLBuffer(inf.getFile()), "AndroidManifest.xml"); if (buffer != null) {
bxp.parse(); File out = new File(args.getOutDir(), "AndroidManifest.xml");
} catch(IOException ioe) { BinaryXMLParser bxp = new BinaryXMLParser();
LOG.info("Decompiling AndroidManifest.xml failed!"); bxp.parse(buffer, out);
}
} catch (Exception e) {
LOG.info("Decompiling AndroidManifest.xml failed!", e);
} }
} }
root.load(inputFiles); root.load(inputFiles);
......
...@@ -77,7 +77,7 @@ public class InputFile { ...@@ -77,7 +77,7 @@ public class InputFile {
public static byte[] loadXMLBuffer(File file) throws IOException { // FIXME: Public.. Please fix public static byte[] loadXMLBuffer(File file) throws IOException { // FIXME: Public.. Please fix
ZipFile zf = new ZipFile(file); ZipFile zf = new ZipFile(file);
ZipEntry xml = zf.getEntry("AndroidManifest.xml"); ZipEntry xml = zf.getEntry("AndroidManifest.xml");
if(null == xml) { if (xml == null) {
zf.close(); zf.close();
return null; return null;
} }
...@@ -91,7 +91,9 @@ public class InputFile { ...@@ -91,7 +91,9 @@ public class InputFile {
bytesOut.write(buffer, 0, count); bytesOut.write(buffer, 0, count);
} }
} finally { } finally {
if(null != in) in.close(); if (null != in) {
in.close();
}
zf.close(); zf.close();
} }
return bytesOut.toByteArray(); return bytesOut.toByteArray();
......
This source diff could not be displayed because it is too large. You can view the blob instead.
package jadx.core.xmlgen; package jadx.core.xmlgen;
import java.nio.ByteBuffer; import jadx.core.codegen.CodeWriter;
import java.nio.charset.Charset; import jadx.core.utils.exceptions.JadxRuntimeException;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.PrintWriter;
import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import android.R.style; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/* TODO: /* TODO:
Don't die when error occurs Don't die when error occurs
Check error cases, maybe checked const values are not always the same Check error cases, maybe checked const values are not always the same
Better error messages Better error messages
What to do, when Binary XML Manifest is > size(int)? What to do, when Binary XML Manifest is > size(int)?
Check for missung chunk size types Check for missing chunk size types
Implement missing data types Implement missing data types
Use linenumbers to recreate EXACT AndroidManifest Use line numbers to recreate EXACT AndroidManifest
Check Element chunk size Check Element chunk size
*/ */
public class BinaryXMLParser { public class BinaryXMLParser {
private static final Logger LOG = LoggerFactory.getLogger(BinaryXMLParser.class);
private byte[] bytes; private byte[] bytes;
private String[] strings; private String[] strings;
private int count; private int count;
private String nsPrefix="ERROR";
private String nsURI="ERROR"; private String nsPrefix = "ERROR";
private String currentTag="ERROR"; private String nsURI = "ERROR";
private int numtabs=-1; private String currentTag = "ERROR";
private boolean wasOneLiner=false;
private PrintWriter writer; private boolean firstElement;
private boolean wasOneLiner = false;
private CodeWriter writer;
private Map<Integer, String> styleMap = null; private Map<Integer, String> styleMap = null;
public BinaryXMLParser(String xmlfilepath, String xmloutfilepath) { public BinaryXMLParser() {
try {
writer = new PrintWriter(xmloutfilepath,"UTF-8");
} catch(FileNotFoundException fnfe) { die("FNFE"); }
catch(UnsupportedEncodingException uee) { die("UEE"); }
if(null==writer) die("null==writer");
writer.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
File manifest = new File(xmlfilepath);
if(null==manifest) die("null==manifest");
bytes = new byte[(int) manifest.length()];
try {
InputStream is = null;
try {
is = new BufferedInputStream(new FileInputStream(manifest));
int total = 0;
while(total < bytes.length) {
int remain = bytes.length - total;
int read = is.read(bytes, total, remain);
if(read > 0) total += read;
}
} finally {
is.close();
}
} catch(FileNotFoundException fnfe) { die("FILE NOT FOUND"); }
catch(IOException ioe) { die("IOE"); }
count=0;
styleMap = new HashMap<Integer, String>(); styleMap = new HashMap<Integer, String>();
if(null==styleMap) die("null==styleMap");
for(Field f : android.R.style.class.getFields()) {
try { try {
styleMap.put(f.getInt(f.getType()),f.getName()); for (Field f : AndroidR.style.class.getFields()) {
} catch(IllegalAccessException iae) { styleMap.put(f.getInt(f.getType()), f.getName());
die("IAE");
} }
//TODO: also add application constant fields
} catch (IllegalAccessException e) {
throw new JadxRuntimeException("BinaryXMLParser init error", e);
} }
} }
public BinaryXMLParser(byte[] xmlfilebytes, String xmloutfilepath) { public void parse(byte[] xmlBytes, File out) {
System.out.println("XMLOUTFILEPATH: " + xmloutfilepath); LOG.debug("Decoding AndroidManifest.xml, output: {}", out);
try {
writer = new PrintWriter(xmloutfilepath,"UTF-8"); writer = new CodeWriter();
} catch(FileNotFoundException fnfe) { die("FNFE"); } writer.add("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
catch(UnsupportedEncodingException uee) { die("UEE"); } bytes = xmlBytes;
if(null==writer) die("null==writer"); count = 0;
writer.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); firstElement = true;
bytes = xmlfilebytes; decode();
count=0; writer.save(out);
styleMap = new HashMap<Integer, String>();
if(null==styleMap) die("null==styleMap");
for(Field f : android.R.style.class.getFields()) {
try {
styleMap.put(f.getInt(f.getType()),f.getName());
} catch(IllegalAccessException iae) {
die("IAE");
} }
void decode() {
if (cInt16(bytes, count) != 0x0003) {
die("Version is not 3");
} }
if (cInt16(bytes, count) != 0x0008) {
die("Size of header is not 8");
} }
if (cInt32(bytes, count) != bytes.length) {
public void parse() { die("Size of manifest doesn't match");
if(cInt16(bytes, count) != 0x0003) die("Version is not 3"); }
if(cInt16(bytes, count) != 0x0008) die("Size of header is not 8"); while ((count + 2) <= bytes.length) {
if(cInt32(bytes, count) != bytes.length) die("Size of manifest doesn't match");
while((count+2)<=bytes.length) {
int type = cInt16(bytes, count); int type = cInt16(bytes, count);
if(type==0x0001) parseStringPool(); switch (type) {
else if(type==0x0180) parseResourceMap(); case 0x0001:
else if(type==0x0100) parseNameSpace(); parseStringPool();
else if(type==0x0101) parseNameSpaceEnd(); break;
else if(type==0x0102) parseElement(); case 0x0180:
else if(type==0x0103) parseElementEnd(); parseResourceMap();
else if(type==0x0000) continue; // NullType is just doing nothing break;
else die("Type: " + Integer.toHexString(type) + " not yet implemented"); case 0x0100:
parseNameSpace();
break;
case 0x0101:
parseNameSpaceEnd();
break;
case 0x0102:
parseElement();
break;
case 0x0103:
parseElementEnd();
break;
case 0x0000:
// NullType is just doing nothing
break;
default:
die("Type: " + Integer.toHexString(type) + " not yet implemented");
break;
}
} }
writer.close();
} }
private void parseStringPool() { private void parseStringPool() {
if(cInt16(bytes, count) != 0x001c) die("Header header size not 28"); if (cInt16(bytes, count) != 0x001c) {
die("Header header size not 28");
}
int hsize = cInt32(bytes, count); int hsize = cInt32(bytes, count);
int stringCount = cInt32(bytes, count); int stringCount = cInt32(bytes, count);
int styleCount = cInt32(bytes, count); int styleCount = cInt32(bytes, count);
...@@ -126,161 +119,214 @@ public class BinaryXMLParser { ...@@ -126,161 +119,214 @@ public class BinaryXMLParser {
int stringsStart = cInt32(bytes, count); int stringsStart = cInt32(bytes, count);
int stylesStart = cInt32(bytes, count); int stylesStart = cInt32(bytes, count);
int[] stringsOffsets = new int[stringCount]; int[] stringsOffsets = new int[stringCount];
for(int i=0; i<stringCount; i++) { for (int i = 0; i < stringCount; i++) {
stringsOffsets[i] = cInt32(bytes, count); stringsOffsets[i] = cInt32(bytes, count);
} }
strings = new String[stringCount]; strings = new String[stringCount];
for(int i=0; i<stringCount; i++) { for (int i = 0; i < stringCount; i++) {
int off = 8 + stringsStart + stringsOffsets[i]; int off = 8 + stringsStart + stringsOffsets[i];
int strlen = cInt16(bytes, off); int strlen = cInt16(bytes, off);
byte[] str = new byte[strlen*2]; byte[] str = new byte[strlen * 2];
System.arraycopy(bytes, count, str, 0, strlen*2); System.arraycopy(bytes, count, str, 0, strlen * 2);
count+=strlen*2; count += strlen * 2;
strings[i] = new String(str, Charset.forName("UTF-16LE")); strings[i] = new String(str, Charset.forName("UTF-16LE"));
count+=2; count += 2;
} }
} }
private void parseResourceMap() { private void parseResourceMap() {
if(cInt16(bytes, count) != 0x8) die("Header size of resmap is not 8!"); if (cInt16(bytes, count) != 0x8) {
die("Header size of resmap is not 8!");
}
int rhsize = cInt32(bytes, count); int rhsize = cInt32(bytes, count);
int[] ids = new int[(rhsize-8)/4]; int[] ids = new int[(rhsize - 8) / 4];
for(int i=0; i<ids.length; i++) { for (int i = 0; i < ids.length; i++) {
ids[i]=cInt32(bytes, count); ids[i] = cInt32(bytes, count);
} }
} }
private void parseNameSpace() { private void parseNameSpace() {
if(cInt16(bytes, count) != 0x0010) die("NAMESPACE header is not 0x0010"); if (cInt16(bytes, count) != 0x0010) {
if(cInt32(bytes, count) != 0x18) die("NAMESPACE header chunk is not 0x18 big"); die("NAMESPACE header is not 0x0010");
}
if (cInt32(bytes, count) != 0x18) {
die("NAMESPACE header chunk is not 0x18 big");
}
int beginLineNumber = cInt32(bytes, count); int beginLineNumber = cInt32(bytes, count);
int comment = cInt32(bytes, count); int comment = cInt32(bytes, count);
int beginPrefix = cInt32(bytes, count); int beginPrefix = cInt32(bytes, count);
nsPrefix = strings[beginPrefix]; nsPrefix = strings[beginPrefix];
int beginURI = cInt32(bytes, count); int beginURI = cInt32(bytes, count);
nsURI=strings[beginURI]; nsURI = strings[beginURI];
} }
private void parseNameSpaceEnd() { private void parseNameSpaceEnd() {
if(cInt16(bytes, count) != 0x0010) die("NAMESPACE header is not 0x0010"); if (cInt16(bytes, count) != 0x0010) {
if(cInt32(bytes, count) != 0x18) die("NAMESPACE header chunk is not 0x18 big"); die("NAMESPACE header is not 0x0010");
}
if (cInt32(bytes, count) != 0x18) {
die("NAMESPACE header chunk is not 0x18 big");
}
int endLineNumber = cInt32(bytes, count); int endLineNumber = cInt32(bytes, count);
int comment = cInt32(bytes, count); int comment = cInt32(bytes, count);
int endPrefix = cInt32(bytes, count); int endPrefix = cInt32(bytes, count);
nsPrefix = strings[endPrefix]; nsPrefix = strings[endPrefix];
int endURI = cInt32(bytes, count); int endURI = cInt32(bytes, count);
nsURI=strings[endURI]; nsURI = strings[endURI];
} }
private void parseElement() { private void parseElement() {
numtabs+=1; if (firstElement) {
if(cInt16(bytes, count) != 0x0010) die("ELEMENT HEADER SIZE is not 0x10"); firstElement = false;
count+=4; // TODO: Check element chunk size } else {
writer.incIndent();
}
if (cInt16(bytes, count) != 0x0010) {
die("ELEMENT HEADER SIZE is not 0x10");
}
count += 4; // TODO: Check element chunk size
int elementBegLineNumber = cInt32(bytes, count); int elementBegLineNumber = cInt32(bytes, count);
int comment = cInt32(bytes, count); int comment = cInt32(bytes, count);
int startNS = cInt32(bytes, count); int startNS = cInt32(bytes, count);
int startNSName = cInt32(bytes, count); // actually is elementName... int startNSName = cInt32(bytes, count); // actually is elementName...
if(!wasOneLiner && !"ERROR".equals(currentTag) && !currentTag.equals(strings[startNSName])) { if (!wasOneLiner && !"ERROR".equals(currentTag) && !currentTag.equals(strings[startNSName])) {
writer.println(">"); writer.add(">");
} }
wasOneLiner=false; wasOneLiner = false;
currentTag=strings[startNSName]; currentTag = strings[startNSName];
for(int i=0; i<numtabs; i++) writer.print("\t"); writer.startLine("<" + strings[startNSName]);
writer.print("<" + strings[startNSName]);
int attributeStart = cInt16(bytes, count); int attributeStart = cInt16(bytes, count);
if(attributeStart != 0x14) die("startNS's attributeStart is not 0x14"); if (attributeStart != 0x14) {
die("startNS's attributeStart is not 0x14");
}
int attributeSize = cInt16(bytes, count); int attributeSize = cInt16(bytes, count);
if(attributeSize != 0x14) die("startNS's attributeSize is not 0x14"); if (attributeSize != 0x14) {
die("startNS's attributeSize is not 0x14");
}
int attributeCount = cInt16(bytes, count); int attributeCount = cInt16(bytes, count);
int idIndex = cInt16(bytes, count); int idIndex = cInt16(bytes, count);
int classIndex = cInt16(bytes, count); int classIndex = cInt16(bytes, count);
int styleIndex = cInt16(bytes, count); int styleIndex = cInt16(bytes, count);
if("manifest".equals(strings[startNSName])) writer.print(" xmlns:\""+nsURI+"\""); if ("manifest".equals(strings[startNSName])) {
if(attributeCount>0) writer.print(" "); writer.add(" xmlns:\"" + nsURI + "\"");
for(int i=0; i<attributeCount; i++) { }
if (attributeCount > 0) {
writer.add(" ");
}
for (int i = 0; i < attributeCount; i++) {
int attributeNS = cInt32(bytes, count); int attributeNS = cInt32(bytes, count);
int attributeName = cInt32(bytes, count); int attributeName = cInt32(bytes, count);
int attributeRawValue = cInt32(bytes, count); int attributeRawValue = cInt32(bytes, count);
int attrValSize = cInt16(bytes, count); int attrValSize = cInt16(bytes, count);
if(attrValSize != 0x08) die("attrValSize != 0x08 not supported"); if (attrValSize != 0x08) {
if(cInt8(bytes, count) != 0) die("res0 is not 0"); die("attrValSize != 0x08 not supported");
}
if (cInt8(bytes, count) != 0) {
die("res0 is not 0");
}
int attrValDataType = cInt8(bytes, count); int attrValDataType = cInt8(bytes, count);
int attrValData = cInt32(bytes, count); int attrValData = cInt32(bytes, count);
if(attributeNS != -1) writer.print(nsPrefix+":"); if (attributeNS != -1) {
writer.print(strings[attributeName] + "=\""); writer.add(nsPrefix + ":");
if(attrValDataType==0x3) writer.print(strings[attrValData]); }
else if(attrValDataType==0x10) writer.print(attrValData); writer.add(strings[attributeName] + "=\"");
else if(attrValDataType==0x12) { if (attrValDataType == 0x3) {
writer.add(strings[attrValData]);
} else if (attrValDataType == 0x10) {
writer.add(String.valueOf(attrValData));
} else if (attrValDataType == 0x12) {
// FIXME: What to do, when data is always -1? // FIXME: What to do, when data is always -1?
if(attrValData==0) writer.print("false"); if (attrValData == 0) {
else if(attrValData==1 || attrValData==-1) writer.print("true"); writer.add("false");
else writer.print("UNKNOWN_BOOLEAN_TYPE"); } else if (attrValData == 1 || attrValData == -1) {
} else if(attrValDataType==0x1) { writer.add("true");
if(attrValData<0x7f000000) { } else {
writer.print("@*"); writer.add("UNKNOWN_BOOLEAN_TYPE");
if(attributeNS != -1) writer.print(nsPrefix+":"); }
writer.print("style/"+styleMap.get(attrValData).replaceAll("_", ".")); } else if (attrValDataType == 0x1) {
String name = styleMap.get(attrValData);
if (name != null) {
writer.add("@*");
if (attributeNS != -1) {
writer.add(nsPrefix + ":");
}
writer.add("style/" + name.replaceAll("_", "."));
} else { } else {
writer.print("0x" + Integer.toHexString(attrValData)); writer.add("0x" + Integer.toHexString(attrValData));
} }
} else {
// TODO: extract values names from here:
// https://github.com/android/platform_frameworks_base/blob/master/core/res/res/values/attrs_manifest.xml
if ("configChanges".equals(strings[attributeName])) {
if (attrValData == 1152) {
writer.add("orientation");
} else if (attrValData == 4016) {
writer.add("keyboard|keyboardHidden|orientation|screenLayout|uiMode");
} else if (attrValData == 176) {
writer.add("keyboard|keyboardHidden|orientation");
} else if (attrValData == 160) {
writer.add("keyboardHidden|orientation");
} else {
writer.add("UNKNOWN_DATA_" + Integer.toHexString(attrValData));
} }
else {
if("configChanges".equals(strings[attributeName])) {
if(attrValData==1152) writer.print("orientation");
else if(attrValData==4016) writer.print("keyboard|keyboardHidden|orientation|screenLayout|uiMode");
else if(attrValData==176) writer.print("keyboard|keyboardHidden|orientation");
else if(attrValData==160) writer.print("keyboardHidden|orientation");
else writer.print("UNKNOWN_DATA_"+Integer.toHexString(attrValData));
} else { } else {
writer.print("UNKNOWN_DATA_TYPE_" + attrValDataType); writer.add("UNKNOWN_DATA_TYPE_" + attrValDataType);
}
} }
writer.add("\"");
if ((i + 1) < attributeCount) {
writer.add(" ");
} }
writer.print("\"");
if((i+1)<attributeCount) writer.print(" ");
} }
} }
private void parseElementEnd() { private void parseElementEnd() {
if(cInt16(bytes, count) != 0x0010) die("ELEMENT END header is not 0x0010"); if (cInt16(bytes, count) != 0x0010) {
if(cInt32(bytes, count) != 0x18) die("ELEMENT END header chunk is not 0x18 big"); die("ELEMENT END header is not 0x0010");
}
if (cInt32(bytes, count) != 0x18) {
die("ELEMENT END header chunk is not 0x18 big");
}
int endLineNumber = cInt32(bytes, count); int endLineNumber = cInt32(bytes, count);
int comment = cInt32(bytes, count); int comment = cInt32(bytes, count);
int elementNS = cInt32(bytes, count); int elementNS = cInt32(bytes, count);
int elementName = cInt32(bytes, count); int elementName = cInt32(bytes, count);
if(currentTag==strings[elementName]) { if (currentTag.equals(strings[elementName])) {
writer.println(" />"); writer.add("/>");
wasOneLiner=true; wasOneLiner = true;
} else { } else {
for(int i=0; i<numtabs; i++) writer.print("\t"); writer.startLine("</");
writer.print("</"); if (elementNS != -1) {
if(elementNS != -1) writer.print(strings[elementNS]+":"); writer.add(strings[elementNS] + ":");
writer.println(strings[elementName]+">"); }
writer.add(strings[elementName] + ">");
} }
numtabs-=1; writer.decIndent();
} }
private int cInt8(byte[] bytes, int offset) { private int cInt8(byte[] bytes, int offset) {
byte[] tmp = new byte[4]; byte[] tmp = new byte[4];
tmp[3]=bytes[count++]; tmp[3] = bytes[count++];
return ByteBuffer.wrap(tmp).getInt(); return ByteBuffer.wrap(tmp).getInt();
} }
private int cInt16(byte[] bytes, int offset) { private int cInt16(byte[] bytes, int offset) {
byte[] tmp = new byte[4]; byte[] tmp = new byte[4];
tmp[3]=bytes[count++]; tmp[3] = bytes[count++];
tmp[2]=bytes[count++]; tmp[2] = bytes[count++];
return ByteBuffer.wrap(tmp).getInt(); return ByteBuffer.wrap(tmp).getInt();
} }
private int cInt32(byte[] bytes, int offset) { private int cInt32(byte[] bytes, int offset) {
byte[] tmp = new byte[4]; byte[] tmp = new byte[4];
for(int i=0;i <4; i++) tmp[3-i]=bytes[count+i]; for (int i = 0; i < 4; i++) {
count+=4; tmp[3 - i] = bytes[count + i];
}
count += 4;
return ByteBuffer.wrap(tmp).getInt(); return ByteBuffer.wrap(tmp).getInt();
} }
private void die(String message) { private void die(String message) {
System.err.println(message); throw new JadxRuntimeException("Decode error: " + message);
System.exit(3);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment