From e23eb9cf6b69caf7988d43afaf0b0d4e2f9a8301 Mon Sep 17 00:00:00 2001 From: Anthony Morris Date: Fri, 5 Aug 2016 14:17:19 +0100 Subject: [PATCH] Use Guava's LittleEndianDataInputStream. This replaces the custom LittleEndianDataInputStream with guava's implementation. To do this, I had to fix ExtDataInput to better handle the case where skipBytes doesn't skip all the bytes (the tests failed without this, and succeed with it). This appears to be the main difference between the two implementations. Guava's implementation is preferred because it is already a dependency and because its license is clearer (the previous implementation had a vague "public domain" comment in the thread which may not be legally sufficient). Fixes #1166 --- .../androlib/res/decoder/ARSCDecoder.java | 7 +- .../res/decoder/AXmlResourceParser.java | 8 +- .../LittleEndianDataInputStream.java | 139 ------------------ .../src/main/java/brut/util/ExtDataInput.java | 16 ++ 4 files changed, 27 insertions(+), 143 deletions(-) delete mode 100644 brut.apktool/apktool-lib/src/main/java/com/peterfranza/LittleEndianDataInputStream.java diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java index 0e64575b..abcb8eaa 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java @@ -23,7 +23,7 @@ import brut.androlib.res.data.value.*; import brut.util.Duo; import brut.androlib.res.data.ResTable; import brut.util.ExtDataInput; -import com.peterfranza.LittleEndianDataInputStream; +import com.google.common.io.LittleEndianDataInputStream; import java.io.*; import java.math.BigInteger; import java.util.*; @@ -60,7 +60,10 @@ public class ARSCDecoder { } else { mFlagsOffsets = null; } - mIn = new ExtDataInput(new LittleEndianDataInputStream(arscStream)); + // We need to explicitly cast to DataInput as otherwise the constructor is ambiguous. + // We choose DataInput instead of InputStream as ExtDataInput wraps an InputStream in + // a DataInputStream which is big-endian and ignores the little-endian behavior. + mIn = new ExtDataInput((DataInput) new LittleEndianDataInputStream(arscStream)); mResTable = resTable; mKeepBroken = keepBroken; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java index 6b2e71d4..8c1fe60e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java @@ -20,7 +20,8 @@ import android.util.TypedValue; import brut.androlib.AndrolibException; import brut.androlib.res.xml.ResXmlEncoders; import brut.util.ExtDataInput; -import com.peterfranza.LittleEndianDataInputStream; +import com.google.common.io.LittleEndianDataInputStream; +import java.io.DataInput; import java.io.IOException; import java.io.InputStream; import java.io.Reader; @@ -69,7 +70,10 @@ public class AXmlResourceParser implements XmlResourceParser { public void open(InputStream stream) { close(); if (stream != null) { - m_reader = new ExtDataInput(new LittleEndianDataInputStream(stream)); + // We need to explicitly cast to DataInput as otherwise the constructor is ambiguous. + // We choose DataInput instead of InputStream as ExtDataInput wraps an InputStream in + // a DataInputStream which is big-endian and ignores the little-endian behavior. + m_reader = new ExtDataInput((DataInput) new LittleEndianDataInputStream(stream)); } } diff --git a/brut.apktool/apktool-lib/src/main/java/com/peterfranza/LittleEndianDataInputStream.java b/brut.apktool/apktool-lib/src/main/java/com/peterfranza/LittleEndianDataInputStream.java deleted file mode 100644 index 8777b9b1..00000000 --- a/brut.apktool/apktool-lib/src/main/java/com/peterfranza/LittleEndianDataInputStream.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.peterfranza; - -import java.io.DataInput; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; - -public class LittleEndianDataInputStream implements DataInput { - - public LittleEndianDataInputStream(InputStream in) { - this.in = in; - this.d = new DataInputStream(in); - w = new byte[8]; - } - - public int available() throws IOException { - return d.available(); - } - - - public final short readShort() throws IOException - { - d.readFully(w, 0, 2); - return (short)( - (w[1]&0xff) << 8 | - (w[0]&0xff)); - } - - /** - * Note, returns int even though it reads a short. - */ - public final int readUnsignedShort() throws IOException - { - d.readFully(w, 0, 2); - return ( - (w[1]&0xff) << 8 | - (w[0]&0xff)); - } - - /** - * like DataInputStream.readChar except little endian. - */ - public final char readChar() throws IOException - { - d.readFully(w, 0, 2); - return (char) ( - (w[1]&0xff) << 8 | - (w[0]&0xff)); - } - - /** - * like DataInputStream.readInt except little endian. - */ - public final int readInt() throws IOException - { - d.readFully(w, 0, 4); - return - (w[3]) << 24 | - (w[2]&0xff) << 16 | - (w[1]&0xff) << 8 | - (w[0]&0xff); - } - - /** - * like DataInputStream.readLong except little endian. - */ - public final long readLong() throws IOException - { - d.readFully(w, 0, 8); - return - (long)(w[7]) << 56 | - (long)(w[6]&0xff) << 48 | - (long)(w[5]&0xff) << 40 | - (long)(w[4]&0xff) << 32 | - (long)(w[3]&0xff) << 24 | - (long)(w[2]&0xff) << 16 | - (long)(w[1]&0xff) << 8 | - (long)(w[0]&0xff); - } - - public final float readFloat() throws IOException { - return Float.intBitsToFloat(readInt()); - } - - public final double readDouble() throws IOException { - return Double.longBitsToDouble(readLong()); - } - - public final int read(byte b[], int off, int len) throws IOException { - return in.read(b, off, len); - } - - public final void readFully(byte b[]) throws IOException { - d.readFully(b, 0, b.length); - } - - public final void readFully(byte b[], int off, int len) throws IOException { - d.readFully(b, off, len); - } - - public final int skipBytes(int n) throws IOException { - return d.skipBytes(n); - } - - public final boolean readBoolean() throws IOException { - return d.readBoolean(); - } - - public final byte readByte() throws IOException { - return d.readByte(); - } - - public int read() throws IOException { - return in.read(); - } - - public final int readUnsignedByte() throws IOException { - return d.readUnsignedByte(); - } - - @Deprecated - public final String readLine() throws IOException { - return d.readLine(); - } - - public final String readUTF() throws IOException { - return d.readUTF(); - } - - public final void close() throws IOException { - d.close(); - } - - private DataInputStream d; // to get at high level readFully methods of - // DataInputStream - private InputStream in; // to get at the low-level read methods of - // InputStream - private byte w[]; // work array for buffering input -} diff --git a/brut.j.util/src/main/java/brut/util/ExtDataInput.java b/brut.j.util/src/main/java/brut/util/ExtDataInput.java index 3e8764ca..72ef4140 100644 --- a/brut.j.util/src/main/java/brut/util/ExtDataInput.java +++ b/brut.j.util/src/main/java/brut/util/ExtDataInput.java @@ -76,6 +76,22 @@ public class ExtDataInput extends DataInputDelegate { } } + /** + * The general contract of DataInput doesn't guarantee all the bytes requested will be skipped + * and failure can occur for many reasons. We override this to try harder to skip all the bytes + * requested (this is similar to DataInputStream's wrapper). + */ + public final int skipBytes(int n) throws IOException { + int total = 0; + int cur = 0; + + while ((total < n) && ((cur = (int) super.skipBytes(n - total)) > 0)) { + total += cur; + } + + return total; + } + public String readNullEndedString(int length, boolean fixed) throws IOException { StringBuilder string = new StringBuilder(16);