/* Software Name : AsmDex
* Version : 1.0
*
* Copyright © 2012 France Télécom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.ow2.asmdex.lowLevelUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* Reads the different kinds of primitive Dalvik values from an encapsulated stream.
*
* @author Pierre Crégut
* @author Julien névo (slight modifications)
*/
public class DalvikValueReader implements IDalvikValueReader {
private byte[] contents;
/**
* Position in stream
*/
protected int pos = 0;
/**
* Constructor encapsulating an array of bytes.
* @param contents
*/
public DalvikValueReader(byte[] contents) {
this.contents = contents;
}
/**
* Returns the byte array included to this reader. It is <i>not</i> copied.
* @return the byte array included to this reader.
*/
public byte[] getContents() {
return contents;
}
/**
* Constructor encapsulating an input stream. We need to find the size of the file
* This is given by the offset. We first read up to the size, and then fill the byte
* array. The size in the byte array is NOT correct.
* @param dexBytes the bytes containing the resource/dex
* @param size_offset offset from the begining to find the total file size
* @throws IOException
*/
public DalvikValueReader(byte[] dexBytes, int size_offset) throws IOException {
InputStream stream = new ByteArrayInputStream(dexBytes);
byte[] header = new byte[size_offset];
if (stream.read(header) != size_offset)
throw new IOException("Cannot skip Resource header");
int file_size = sint(stream);
byte [] file = new byte [file_size];
int to_skip = size_offset + 4;
while(file_size - to_skip != 0) {
int read = stream.read(file, to_skip, file_size - to_skip);
if (read == 0)
throw new IOException("Truncated Resource file " + read + "/" + file_size + "/" + to_skip);
to_skip += read;
}
System.arraycopy(header, 0, file, 0, size_offset);
this.contents = file;
}
/**
* Reads an integer directly from an input stream. Usually used
* while builder the value reader.
* @param stream
* @return the value read
* @throws IOException
*/
final public static int sint(InputStream stream) throws IOException {
byte [] contents = new byte [4];
if (stream.read(contents) != 4) throw new RuntimeException("Cannot read integer");
return ((contents [0] & 0xFF) | ((contents[1] & 0xFF) << 8) | ((contents[2] & 0xFF) << 16) | ((contents[3] & 0xFF) << 24));
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#sbyte()
*/
final public byte sbyte() {
return contents[pos++];
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#ubyte()
*/
final public short ubyte() {
return (short) (contents[pos++] & 0xff);
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#sshort()
*/
final public short sshort() {
short v = (short) ((contents [pos] & 0xff) | ((contents[pos + 1] & 0xff) << 8));
pos += 2;
return v;
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#ushort()
*/
final public int ushort() {
return ((contents [pos++] & 0xff) | ((contents[pos++] & 0xff) << 8));
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#sint()
*/
final public int sint() {
int v = ((contents [pos] & 0xff)| ((contents[pos + 1] & 0xff) << 8) | ((contents[pos + 2] & 0xff) << 16) | ((contents[pos + 3] & 0xff) << 24));
pos += 4;
return v;
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#uint()
*/
final public int uint() {
// WARNING : int returned whereas a long *should* be used (but slower !). For now, it works fine.
int v = ((contents [pos] & 0xff) | ((contents[pos + 1] & 0xff) << 8) | ((contents[pos + 2] & 0xff) << 16)) | ((contents[pos + 3] & 0xff) << 24);
pos += 4;
return v;
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#sleb128()
*/
final public int sleb128() {
int r, v;
v = contents[pos++] & 0xff;
r = v & 0x7f;
if (v >= 0x80) {
v = contents[pos++] & 0xff;
r |= (v & 0x7f) << 7;
if (v >= 0x80) {
v = contents[pos++] & 0xff;
r |= (v & 0x7f) << 14;
if (v >= 0x80) {
v = contents[pos++] & 0xff;
r |= (v & 0x7f) << 21;
if (v >= 0x80) {
v = contents[pos++] & 0xff;
r |= (v & 0x7f) << 28;
if (v >= 0x80) {
throw new RuntimeException("Bad sleb128");
}
} else if ((v & 0x40) != 0) r |= 0xf0000000;
} else if ((v & 0x40) != 0) r |= 0xffe00000;
} else if ((v & 0x40) != 0) r |= 0xffffc000;
} else if ((v & 0x40) != 0) r |= 0xffffff80;
return r;
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#uleb128()
*/
//final public long uleb128(){
final public int uleb128(){
// WARNING : int returned whereas a long *should* be used (but slower !). For now, no problem...
int r;
int v;
v = contents[pos++] & 0xff;
r = v & 0x7f;
if (v >= 0x80) {
v = contents[pos++] & 0xff;
r |= (v & 0x7f) << 7;
if (v >= 0x80) {
v = contents[pos++] & 0xff;
r |= (v & 0x7f) << 14;
if (v >= 0x80) {
v = contents[pos++] & 0xff;
r |= (v & 0x7f) << 21;
if (v >= 0x80) {
v = contents[pos++] & 0xff;
r |= (v & 0x7f) << 28;
if (v >= 0x80) {
throw new RuntimeException("Bad uleb128");
}
}
}
}
}
return r;
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#uleb128_p1()
*/
//final public long uleb128_p1() {
final public int uleb128_p1() {
return uleb128() - 1;
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#uleb128_16()
*/
final public long uleb128_16(){
long r;
int v;
v = ushort();
r = v & 0x7fff;
if (v > 0x8000) {
v = ushort();
r |= (v & 0x7fff) << 15;
}
return r;
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#sizedLong(int)
*/
final public long sizedLong(int sz) {
long result = 0;
int length = sz + 1;
for(int i=0; i < length; i++) {
short v = ubyte();
result = result | (long)v << (8*i); // v must be cast, else we may lose data !
}
return result;
}
/**
* Extends a long read with SizedLong of length sz according to its sign.
* @param l
* @param sz size-1 of the encoded number.
* @return the value read
*/
final public long completeSignSizedLong(long l, int sz) {
sz++;
int shift = (8 - sz) * 8;
return l << shift >> shift; // Generates the sign.
}
/**
* Reads a given number of bytes and fills the given array.
* @param b the byte array to be filled.
*/
public void bytes(byte [] b) {
for(int i=0; i < b.length; i++) b[i] = contents[pos+i];
pos += b.length;
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#utf8String()
*/
public String utf8String() {
StringBuilder buf = new StringBuilder();
int c;
int v;
while( (c = (contents[pos++] & 0xff)) != 0) {
if ((c & 0x80) == 0x80) {
if ((c & 0xe0) == 0xc0) {
c &= 0x1f;
v = contents[pos++] & 0x3f;
c = c << 6 | v;
} else if ((c & 0xf0) == 0xe0) {
v = contents[pos++] & 0x3f;
c = c << 6 | v;
v = contents[pos++] & 0x3f;
c = c << 6 | v;
} else {
System.out.println("Bad (point 4) UTF 8 " + Integer.toBinaryString(c));
}
}
buf.append((char) c);
}
return buf.toString();
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#seek(int)
*/
final public void seek(int pos) { this.pos = pos; }
public void relativeSeek(int offset) {
pos += offset;
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#getPos()
*/
final public int getPos() { return pos; }
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#unicodeString(int)
*/
public String unicodeString(int strSize) {
char [] content = new char [strSize];
for(int i=0; i < strSize; i++) content[i] = (char) ushort();
int c;
if ((c = ushort()) != 0) {
System.out.println("Did not find the ending character\n " + Arrays.toString(content) + " " + c );
}
return new String(content);
}
/* (non-Javadoc)
* @see org.ow2.asmdex.IDalvikValueReader#hasMore()
*/
final public boolean hasMore() {
return pos < contents.length;
}
// For debug only - not exposed.
int peek(int i) { return ((int) contents[i]) & 0xff; }
@Override
final public void skipInt() {
pos += 4;
}
@Override
final public void skipShort() {
pos += 2;
}
@Override
final public void skipByte() {
pos += 1;
}
}