mirror of
https://github.com/revanced/Apktool.git
synced 2025-01-20 16:57:34 +01:00
Added new mechanisms of smali decoding and building.
This commit is contained in:
parent
f9c9588f1d
commit
060572e824
225
src/brut/androlib/src/DebugInjector.java
Normal file
225
src/brut/androlib/src/DebugInjector.java
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* under the License.
|
||||
*/
|
||||
package brut.androlib.src;
|
||||
|
||||
import brut.androlib.AndrolibException;
|
||||
import java.util.ListIterator;
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
public class DebugInjector {
|
||||
|
||||
public static void inject(ListIterator<String> it, StringBuilder out)
|
||||
throws AndrolibException {
|
||||
new DebugInjector(it, out).inject();
|
||||
}
|
||||
|
||||
private DebugInjector(ListIterator<String> it, StringBuilder out) {
|
||||
mIt = it;
|
||||
mOut = out;
|
||||
}
|
||||
|
||||
private void inject() throws AndrolibException {
|
||||
String definition = nextAndAppend();
|
||||
if (definition.contains(" abstract ")) {
|
||||
nextAndAppend();
|
||||
return;
|
||||
}
|
||||
injectParameters(definition);
|
||||
|
||||
boolean end = false;
|
||||
while (!end) {
|
||||
end = step();
|
||||
}
|
||||
}
|
||||
|
||||
private void injectParameters(String definition) throws AndrolibException {
|
||||
int pos = definition.indexOf('(');
|
||||
if (pos == -1) {
|
||||
throw new AndrolibException();
|
||||
}
|
||||
int pos2 = definition.indexOf(')', pos);
|
||||
if (pos2 == -1) {
|
||||
throw new AndrolibException();
|
||||
}
|
||||
String params = definition.substring(pos + 1, pos2);
|
||||
|
||||
int i = definition.contains(" static ") ? 0 : 1;
|
||||
int argc = TypeName.listFromInternalName(params).size() + i;
|
||||
while(i < argc) {
|
||||
mOut.append(".parameter \"p").append(i).append("\"\n");
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean step() {
|
||||
String line = next();
|
||||
if (line.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (line.charAt(0)) {
|
||||
case '#':
|
||||
return false;
|
||||
case ':':
|
||||
append(line);
|
||||
return false;
|
||||
case '.':
|
||||
return processDirective(line);
|
||||
default:
|
||||
return processInstruction(line);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean processDirective(String line) {
|
||||
String line2 = line.substring(1);
|
||||
if (
|
||||
line2.startsWith("line ") ||
|
||||
line2.equals("prologue") ||
|
||||
line2.startsWith("local ") ||
|
||||
line2.startsWith("parameter")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
append(line);
|
||||
if (line2.equals("end method")) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
line2.startsWith("annotation ") ||
|
||||
line2.equals("sparse-switch") ||
|
||||
line2.startsWith("packed-switch ") ||
|
||||
line2.startsWith("array-data ")
|
||||
) {
|
||||
while(true) {
|
||||
line2 = nextAndAppend();
|
||||
if (line2.startsWith(".end ")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processInstruction(String line) {
|
||||
if (mFirstInstruction) {
|
||||
mOut.append(".prologue\n");
|
||||
mFirstInstruction = false;
|
||||
}
|
||||
mOut.append(".line ").append(mIt.nextIndex()).append('\n')
|
||||
.append(line).append('\n');
|
||||
|
||||
int pos = line.indexOf(' ');
|
||||
if (pos == -1) {
|
||||
return false;
|
||||
}
|
||||
Opcode opcode = Opcode.getOpcodeByName(line.substring(0, pos));
|
||||
if (! opcode.setsRegister()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int pos2 = line.indexOf(',', pos);
|
||||
String register = pos2 == -1 ? line.substring(pos + 1) :
|
||||
line.substring(pos + 1, pos2);
|
||||
|
||||
mOut.append(".local ").append(register).append(", ").append(register)
|
||||
.append(':').append(getRegisterTypeForOpcode(opcode)).append('\n');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getRegisterTypeForOpcode(Opcode opcode) {
|
||||
switch (opcode.value) {
|
||||
case (byte)0x0d: // ?
|
||||
case (byte)0x1a:
|
||||
case (byte)0x1b:
|
||||
case (byte)0x1c: // ?
|
||||
case (byte)0x22:
|
||||
case (byte)0x23: // ?
|
||||
case (byte)0xf4: // ?
|
||||
return "Ljava/lang/Object;";
|
||||
case (byte)0x1f: // ?
|
||||
case (byte)0x20: // ?
|
||||
return "Z";
|
||||
case (byte)0x21: // ?
|
||||
return "I";
|
||||
}
|
||||
|
||||
String name = opcode.name;
|
||||
int pos = name.lastIndexOf('-');
|
||||
if (pos != -1) {
|
||||
int pos2 = name.indexOf('/');
|
||||
String type = pos2 == -1 ? name.substring(pos + 1) :
|
||||
name.substring(pos + 1, pos2);
|
||||
|
||||
if (type.equals("object")) {
|
||||
return "Ljava/lang/Object;";
|
||||
}
|
||||
if (type.equals("int")) {
|
||||
return "I";
|
||||
}
|
||||
if (type.equals("boolean")) {
|
||||
return "Z";
|
||||
}
|
||||
if (type.equals("float")) {
|
||||
return "F";
|
||||
}
|
||||
if (type.equals("double")) {
|
||||
return "D";
|
||||
}
|
||||
if (type.equals("long")) {
|
||||
return "J";
|
||||
}
|
||||
if (type.equals("byte")) {
|
||||
return "B";
|
||||
}
|
||||
if (type.equals("char")) {
|
||||
return "C";
|
||||
}
|
||||
if (type.equals("short")) {
|
||||
return "S";
|
||||
}
|
||||
if (type.equals("long")) {
|
||||
return "J";
|
||||
}
|
||||
}
|
||||
|
||||
return "I";
|
||||
}
|
||||
|
||||
private String next() {
|
||||
return mIt.next().trim();
|
||||
}
|
||||
|
||||
private String nextAndAppend() {
|
||||
String line = next();
|
||||
append(line);
|
||||
return line;
|
||||
}
|
||||
|
||||
private void append(String append) {
|
||||
mOut.append(append).append('\n');
|
||||
}
|
||||
|
||||
private final ListIterator<String> mIt;
|
||||
private final StringBuilder mOut;
|
||||
|
||||
private boolean mFirstInstruction = true;
|
||||
}
|
109
src/brut/androlib/src/SmaliBuilder.java
Normal file
109
src/brut/androlib/src/SmaliBuilder.java
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* under the License.
|
||||
*/
|
||||
package brut.androlib.src;
|
||||
|
||||
import brut.androlib.AndrolibException;
|
||||
import brut.androlib.res.util.ExtFile;
|
||||
import brut.androlib.util.DexFileBuilder;
|
||||
import brut.directory.DirectoryException;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
public class SmaliBuilder {
|
||||
|
||||
public static void build(ExtFile smaliDir, File dexFile, boolean debug)
|
||||
throws AndrolibException {
|
||||
new SmaliBuilder(smaliDir, dexFile, debug).build();
|
||||
}
|
||||
|
||||
private SmaliBuilder(ExtFile smaliDir, File dexFile, boolean debug) {
|
||||
mSmaliDir = smaliDir;
|
||||
mDexFile = dexFile;
|
||||
mDebug = debug;
|
||||
}
|
||||
|
||||
private void build() throws AndrolibException {
|
||||
try {
|
||||
mDexBuilder = new DexFileBuilder();
|
||||
for (String fileName : mSmaliDir.getDirectory().getFiles(true)) {
|
||||
buildFile(fileName);
|
||||
}
|
||||
mDexBuilder.writeTo(mDexFile);
|
||||
} catch (IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildFile(String fileName) throws AndrolibException,
|
||||
IOException {
|
||||
File inFile = new File(mSmaliDir, fileName);
|
||||
InputStream inStream = new FileInputStream(inFile);
|
||||
|
||||
if (fileName.endsWith(".smali")) {
|
||||
mDexBuilder.addSmaliFile(inFile);
|
||||
return;
|
||||
}
|
||||
if (! fileName.endsWith(".java")) {
|
||||
throw new AndrolibException("Unknown file type: " + inFile);
|
||||
}
|
||||
|
||||
StringBuilder out = new StringBuilder();
|
||||
List<String> lines = IOUtils.readLines(inStream);
|
||||
|
||||
if (!mDebug) {
|
||||
final String[] linesArray = lines.toArray(new String[0]);
|
||||
for (int i = 2; i < linesArray.length - 2; i++) {
|
||||
out.append(linesArray[i]).append('\n');
|
||||
}
|
||||
} else {
|
||||
lines.remove(lines.size() - 1);
|
||||
lines.remove(lines.size() - 1);
|
||||
ListIterator<String> it = lines.listIterator(2);
|
||||
|
||||
out.append(".source \"").append(inFile.getName()).append("\"\n");
|
||||
while (it.hasNext()) {
|
||||
String line = it.next().trim();
|
||||
if (line.isEmpty() || line.charAt(0) == '#' ||
|
||||
line.startsWith(".source")) {
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith(".method ")) {
|
||||
it.previous();
|
||||
DebugInjector.inject(it, out);
|
||||
continue;
|
||||
}
|
||||
|
||||
out.append(line).append('\n');
|
||||
}
|
||||
}
|
||||
mDexBuilder.addSmaliFile(
|
||||
IOUtils.toInputStream(out.toString()), fileName);
|
||||
}
|
||||
|
||||
private final ExtFile mSmaliDir;
|
||||
private final File mDexFile;
|
||||
private final boolean mDebug;
|
||||
|
||||
private DexFileBuilder mDexBuilder;
|
||||
}
|
88
src/brut/androlib/src/SmaliDecoder.java
Normal file
88
src/brut/androlib/src/SmaliDecoder.java
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* under the License.
|
||||
*/
|
||||
package brut.androlib.src;
|
||||
|
||||
import brut.androlib.AndrolibException;
|
||||
import brut.androlib.src.mod.IndentingWriter;
|
||||
import java.io.*;
|
||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||
import org.jf.baksmali.baksmali;
|
||||
import org.jf.dexlib.ClassDefItem;
|
||||
import org.jf.dexlib.DexFile;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
public class SmaliDecoder {
|
||||
|
||||
public static void decode(File apkFile, File outDir, boolean debug)
|
||||
throws AndrolibException {
|
||||
new SmaliDecoder(apkFile, outDir, debug).decode();
|
||||
}
|
||||
|
||||
private SmaliDecoder(File apkFile, File outDir, boolean debug) {
|
||||
mApkFile = apkFile;
|
||||
mOutDir = outDir;
|
||||
mDebug = debug;
|
||||
}
|
||||
|
||||
private void decode() throws AndrolibException {
|
||||
try {
|
||||
baksmali.useLocalsDirective = true;
|
||||
baksmali.useSequentialLabels = true;
|
||||
|
||||
DexFile dexFile = new DexFile(mApkFile);
|
||||
for (ClassDefItem classDefItem :
|
||||
dexFile.ClassDefsSection.getItems()) {
|
||||
decodeClassDefItem(classDefItem);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void decodeClassDefItem(ClassDefItem classDefItem)
|
||||
throws AndrolibException, IOException {
|
||||
TypeName name = TypeName.fromInternalName(
|
||||
classDefItem.getClassType().getTypeDescriptor());
|
||||
File outFile = new File(mOutDir, name.getFilePath(true)
|
||||
+ (mDebug ? ".java" : ".smali"));
|
||||
|
||||
if (outFile.exists()) {
|
||||
throw new AndrolibException(
|
||||
"File already exists: " + outFile);
|
||||
}
|
||||
outFile.getParentFile().mkdirs();
|
||||
|
||||
IndentingWriter indentWriter =
|
||||
new IndentingWriter(new FileWriter(outFile));
|
||||
|
||||
if (mDebug) {
|
||||
indentWriter.write("package " + name.package_ + "; class "
|
||||
+ name.getName(true, true) + " {/*\n\n");
|
||||
}
|
||||
new ClassDefinition(classDefItem).writeTo(indentWriter);
|
||||
if (mDebug) {
|
||||
indentWriter.write("\n*/}\n");
|
||||
}
|
||||
indentWriter.close();
|
||||
}
|
||||
|
||||
private final File mApkFile;
|
||||
private final File mOutDir;
|
||||
private final boolean mDebug;
|
||||
}
|
205
src/brut/androlib/src/TypeName.java
Normal file
205
src/brut/androlib/src/TypeName.java
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package brut.androlib.src;
|
||||
|
||||
import brut.androlib.AndrolibException;
|
||||
import brut.util.Duo;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
public class TypeName {
|
||||
public final String package_;
|
||||
public final String type;
|
||||
public final String innerType;
|
||||
public final int array;
|
||||
|
||||
public TypeName(String type, int array) {
|
||||
this(null, type, null, array);
|
||||
}
|
||||
|
||||
public TypeName(String package_, String type, String innerType, int array) {
|
||||
this.package_ = package_;
|
||||
this.type = type;
|
||||
this.innerType = innerType;
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public String getShortenedName() {
|
||||
return getName("java.lang".equals(package_), isFileOwner());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return getName(false, false);
|
||||
}
|
||||
|
||||
public String getName(boolean excludePackage, boolean separateInner) {
|
||||
String name =
|
||||
(package_ == null || excludePackage ? "" : package_ + '.') +
|
||||
type +
|
||||
(innerType != null ? (separateInner ? '$' : '.') + innerType : "");
|
||||
for (int i = 0; i < array; i++) {
|
||||
name += "[]";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getJavaFilePath() {
|
||||
return getFilePath(isFileOwner()) + ".java";
|
||||
}
|
||||
|
||||
public String getSmaliFilePath() {
|
||||
return getFilePath(true) + ".smali";
|
||||
}
|
||||
|
||||
public String getFilePath(boolean separateInner) {
|
||||
return package_.replace('.', File.separatorChar) + File.separatorChar
|
||||
+ type + (separateInner && isInner() ? "$" + innerType : "");
|
||||
}
|
||||
|
||||
public boolean isInner() {
|
||||
return innerType != null;
|
||||
}
|
||||
|
||||
public boolean isArray() {
|
||||
return array != 0;
|
||||
}
|
||||
|
||||
public boolean isFileOwner() {
|
||||
if (mIsFileOwner == null) {
|
||||
mIsFileOwner = true;
|
||||
if (isInner()) {
|
||||
char c = innerType.charAt(0);
|
||||
if (c < '0' || c > '9') {
|
||||
mIsFileOwner = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mIsFileOwner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
public static TypeName fromInternalName(String internal)
|
||||
throws AndrolibException {
|
||||
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
|
||||
if (duo.m2 != internal.length()) {
|
||||
throw new AndrolibException(
|
||||
"Invalid internal name: " + internal);
|
||||
}
|
||||
return duo.m1;
|
||||
}
|
||||
|
||||
public static List<TypeName> listFromInternalName(String internal)
|
||||
throws AndrolibException {
|
||||
List<TypeName> types = new ArrayList<TypeName>();
|
||||
while (! internal.isEmpty()) {
|
||||
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
|
||||
types.add(duo.m1);
|
||||
internal = internal.substring(duo.m2);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
public static Duo<TypeName, Integer> fetchFromInternalName(String internal)
|
||||
throws AndrolibException {
|
||||
String origInternal = internal;
|
||||
int array = 0;
|
||||
|
||||
boolean isArray = false;
|
||||
do {
|
||||
if (internal.isEmpty()) {
|
||||
throw new AndrolibException(
|
||||
"Invalid internal name: " + origInternal);
|
||||
}
|
||||
isArray = internal.charAt(0) == '[';
|
||||
if (isArray) {
|
||||
array++;
|
||||
internal = internal.substring(1);
|
||||
}
|
||||
} while (isArray);
|
||||
|
||||
int length = array + 1;
|
||||
String package_ = null;
|
||||
String type = null;
|
||||
String innerType = null;
|
||||
switch (internal.charAt(0)) {
|
||||
case 'B':
|
||||
type = "byte";
|
||||
break;
|
||||
case 'C':
|
||||
type = "char";
|
||||
break;
|
||||
case 'D':
|
||||
type = "double";
|
||||
break;
|
||||
case 'F':
|
||||
type = "float";
|
||||
break;
|
||||
case 'I':
|
||||
type = "int";
|
||||
break;
|
||||
case 'J':
|
||||
type = "long";
|
||||
break;
|
||||
case 'S':
|
||||
type = "short";
|
||||
break;
|
||||
case 'Z':
|
||||
type = "boolean";
|
||||
break;
|
||||
case 'V':
|
||||
type = "void";
|
||||
break;
|
||||
case 'L':
|
||||
int pos = internal.indexOf(';');
|
||||
if (pos == -1) {
|
||||
throw new AndrolibException(
|
||||
"Invalid internal name: " + origInternal);
|
||||
}
|
||||
length += pos;
|
||||
internal = internal.substring(1, pos);
|
||||
|
||||
pos = internal.lastIndexOf('/');
|
||||
package_ = internal.substring(0, pos).replace('/', '.');
|
||||
type = internal.substring(pos + 1);
|
||||
|
||||
pos = type.indexOf('$');
|
||||
if (pos != -1) {
|
||||
innerType = type.substring(pos + 1);
|
||||
type = type.substring(0, pos);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new AndrolibException(
|
||||
"Invalid internal name: " + origInternal);
|
||||
}
|
||||
|
||||
return new Duo<TypeName, Integer>(
|
||||
new TypeName(package_, type, innerType, array), length);
|
||||
}
|
||||
|
||||
|
||||
private Boolean mIsFileOwner;
|
||||
}
|
29
src/brut/androlib/src/mod/IndentingWriter.java
Normal file
29
src/brut/androlib/src/mod/IndentingWriter.java
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* under the License.
|
||||
*/
|
||||
package brut.androlib.src.mod;
|
||||
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
public class IndentingWriter extends org.jf.baksmali.IndentingWriter {
|
||||
|
||||
public IndentingWriter(Writer writer) {
|
||||
super(writer);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user