mirror of
https://github.com/revanced/Apktool.git
synced 2025-01-21 01:07:34 +01:00
refactor: split child classes to own file for decoders (#3116)
This commit is contained in:
parent
e85472fee8
commit
ab6f1b416e
@ -17,15 +17,12 @@
|
|||||||
package brut.androlib.res;
|
package brut.androlib.res;
|
||||||
|
|
||||||
import brut.androlib.exceptions.AndrolibException;
|
import brut.androlib.exceptions.AndrolibException;
|
||||||
import brut.androlib.exceptions.CantFindFrameworkResException;
|
|
||||||
import brut.androlib.Config;
|
import brut.androlib.Config;
|
||||||
import brut.androlib.meta.MetaInfo;
|
import brut.androlib.meta.MetaInfo;
|
||||||
import brut.androlib.meta.PackageInfo;
|
import brut.androlib.meta.PackageInfo;
|
||||||
import brut.androlib.meta.VersionInfo;
|
import brut.androlib.meta.VersionInfo;
|
||||||
import brut.androlib.res.data.*;
|
import brut.androlib.res.data.*;
|
||||||
import brut.androlib.res.decoder.*;
|
import brut.androlib.res.decoder.*;
|
||||||
import brut.androlib.res.decoder.ARSCDecoder.ARSCData;
|
|
||||||
import brut.androlib.res.decoder.ARSCDecoder.FlagsOffset;
|
|
||||||
import brut.androlib.res.util.ExtMXSerializer;
|
import brut.androlib.res.util.ExtMXSerializer;
|
||||||
import brut.androlib.res.util.ExtXmlSerializer;
|
import brut.androlib.res.util.ExtXmlSerializer;
|
||||||
import brut.androlib.res.xml.ResValuesXmlSerializable;
|
import brut.androlib.res.xml.ResValuesXmlSerializable;
|
||||||
@ -37,13 +34,8 @@ import org.apache.commons.io.IOUtils;
|
|||||||
import org.xmlpull.v1.XmlSerializer;
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.zip.CRC32;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipFile;
|
|
||||||
import java.util.zip.ZipOutputStream;
|
|
||||||
|
|
||||||
final public class AndrolibResources {
|
final public class AndrolibResources {
|
||||||
|
|
||||||
@ -53,8 +45,6 @@ final public class AndrolibResources {
|
|||||||
|
|
||||||
private final static Logger LOGGER = Logger.getLogger(AndrolibResources.class.getName());
|
private final static Logger LOGGER = Logger.getLogger(AndrolibResources.class.getName());
|
||||||
|
|
||||||
private File mFrameworkDirectory = null;
|
|
||||||
|
|
||||||
private ExtFile mFramework = null;
|
private ExtFile mFramework = null;
|
||||||
|
|
||||||
private String mMinSdkVersion = null;
|
private String mMinSdkVersion = null;
|
||||||
@ -80,10 +70,6 @@ final public class AndrolibResources {
|
|||||||
this.config = Config.getDefaultConfig();
|
this.config = Config.getDefaultConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResTable getResTable(ExtFile apkFile) throws AndrolibException {
|
|
||||||
return getResTable(apkFile, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg)
|
public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
ResTable resTable = new ResTable(this);
|
ResTable resTable = new ResTable(this);
|
||||||
|
@ -20,6 +20,8 @@ import brut.androlib.Config;
|
|||||||
import brut.androlib.exceptions.AndrolibException;
|
import brut.androlib.exceptions.AndrolibException;
|
||||||
import brut.androlib.exceptions.CantFindFrameworkResException;
|
import brut.androlib.exceptions.CantFindFrameworkResException;
|
||||||
import brut.androlib.res.decoder.ARSCDecoder;
|
import brut.androlib.res.decoder.ARSCDecoder;
|
||||||
|
import brut.androlib.res.decoder.arsc.ARSCData;
|
||||||
|
import brut.androlib.res.decoder.arsc.FlagsOffset;
|
||||||
import brut.util.Jar;
|
import brut.util.Jar;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
@ -33,7 +35,6 @@ import java.util.zip.ZipFile;
|
|||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
public class Framework {
|
public class Framework {
|
||||||
|
|
||||||
private final Config config;
|
private final Config config;
|
||||||
|
|
||||||
private File mFrameworkDirectory = null;
|
private File mFrameworkDirectory = null;
|
||||||
@ -63,7 +64,7 @@ public class Framework {
|
|||||||
in = zip.getInputStream(entry);
|
in = zip.getInputStream(entry);
|
||||||
byte[] data = IOUtils.toByteArray(in);
|
byte[] data = IOUtils.toByteArray(in);
|
||||||
|
|
||||||
ARSCDecoder.ARSCData arsc = ARSCDecoder.decode(new ByteArrayInputStream(data), true, true);
|
ARSCData arsc = ARSCDecoder.decode(new ByteArrayInputStream(data), true, true);
|
||||||
publicizeResources(data, arsc.getFlagsOffsets());
|
publicizeResources(data, arsc.getFlagsOffsets());
|
||||||
|
|
||||||
File outFile = new File(getFrameworkDirectory(), arsc
|
File outFile = new File(getFrameworkDirectory(), arsc
|
||||||
@ -140,8 +141,8 @@ public class Framework {
|
|||||||
publicizeResources(arsc, ARSCDecoder.decode(new ByteArrayInputStream(arsc), true, true).getFlagsOffsets());
|
publicizeResources(arsc, ARSCDecoder.decode(new ByteArrayInputStream(arsc), true, true).getFlagsOffsets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void publicizeResources(byte[] arsc, ARSCDecoder.FlagsOffset[] flagsOffsets) {
|
public void publicizeResources(byte[] arsc, FlagsOffset[] flagsOffsets) {
|
||||||
for (ARSCDecoder.FlagsOffset flags : flagsOffsets) {
|
for (FlagsOffset flags : flagsOffsets) {
|
||||||
int offset = flags.offset + 3;
|
int offset = flags.offset + 3;
|
||||||
int end = offset + 4 * flags.count;
|
int end = offset + 4 * flags.count;
|
||||||
while (offset < end) {
|
while (offset < end) {
|
||||||
|
@ -20,13 +20,16 @@ import android.util.TypedValue;
|
|||||||
import brut.androlib.exceptions.AndrolibException;
|
import brut.androlib.exceptions.AndrolibException;
|
||||||
import brut.androlib.res.data.*;
|
import brut.androlib.res.data.*;
|
||||||
import brut.androlib.res.data.value.*;
|
import brut.androlib.res.data.value.*;
|
||||||
|
import brut.androlib.res.decoder.arsc.ARSCData;
|
||||||
|
import brut.androlib.res.decoder.arsc.ARSCHeader;
|
||||||
|
import brut.androlib.res.decoder.arsc.EntryData;
|
||||||
|
import brut.androlib.res.decoder.arsc.FlagsOffset;
|
||||||
import brut.util.Duo;
|
import brut.util.Duo;
|
||||||
import brut.util.ExtDataInput;
|
import brut.util.ExtDataInput;
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
import com.google.common.io.LittleEndianDataInputStream;
|
||||||
import org.apache.commons.io.input.CountingInputStream;
|
import org.apache.commons.io.input.CountingInputStream;
|
||||||
|
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
@ -50,7 +53,7 @@ public class ARSCDecoder {
|
|||||||
ResPackage[] pkgs = decoder.readTableHeader();
|
ResPackage[] pkgs = decoder.readTableHeader();
|
||||||
return new ARSCData(pkgs, decoder.mFlagsOffsets == null
|
return new ARSCData(pkgs, decoder.mFlagsOffsets == null
|
||||||
? null
|
? null
|
||||||
: decoder.mFlagsOffsets.toArray(new FlagsOffset[0]), resTable);
|
: decoder.mFlagsOffsets.toArray(new FlagsOffset[0]));
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new AndrolibException("Could not decode arsc file", ex);
|
throw new AndrolibException("Could not decode arsc file", ex);
|
||||||
}
|
}
|
||||||
@ -72,7 +75,7 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ResPackage[] readTableHeader() throws IOException, AndrolibException {
|
private ResPackage[] readTableHeader() throws IOException, AndrolibException {
|
||||||
nextChunkCheckType(Header.TYPE_TABLE);
|
nextChunkCheckType(ARSCHeader.TYPE_TABLE);
|
||||||
int packageCount = mIn.readInt();
|
int packageCount = mIn.readInt();
|
||||||
|
|
||||||
mTableStrings = StringBlock.read(mIn);
|
mTableStrings = StringBlock.read(mIn);
|
||||||
@ -87,7 +90,7 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ResPackage readTablePackage() throws IOException, AndrolibException {
|
private ResPackage readTablePackage() throws IOException, AndrolibException {
|
||||||
checkChunkType(Header.XML_TYPE_PACKAGE);
|
checkChunkType(ARSCHeader.XML_TYPE_PACKAGE);
|
||||||
int id = mIn.readInt();
|
int id = mIn.readInt();
|
||||||
|
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
@ -128,19 +131,19 @@ public class ARSCDecoder {
|
|||||||
boolean flag = true;
|
boolean flag = true;
|
||||||
while (flag) {
|
while (flag) {
|
||||||
switch (mHeader.type) {
|
switch (mHeader.type) {
|
||||||
case Header.XML_TYPE_SPEC_TYPE:
|
case ARSCHeader.XML_TYPE_SPEC_TYPE:
|
||||||
readTableTypeSpec();
|
readTableTypeSpec();
|
||||||
break;
|
break;
|
||||||
case Header.XML_TYPE_LIBRARY:
|
case ARSCHeader.XML_TYPE_LIBRARY:
|
||||||
readLibraryType();
|
readLibraryType();
|
||||||
break;
|
break;
|
||||||
case Header.XML_TYPE_OVERLAY:
|
case ARSCHeader.XML_TYPE_OVERLAY:
|
||||||
readOverlaySpec();
|
readOverlaySpec();
|
||||||
break;
|
break;
|
||||||
case Header.XML_TYPE_OVERLAY_POLICY:
|
case ARSCHeader.XML_TYPE_OVERLAY_POLICY:
|
||||||
readOverlayPolicySpec();
|
readOverlayPolicySpec();
|
||||||
break;
|
break;
|
||||||
case Header.XML_TYPE_STAGED_ALIAS:
|
case ARSCHeader.XML_TYPE_STAGED_ALIAS:
|
||||||
readStagedAliasSpec();
|
readStagedAliasSpec();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -153,7 +156,7 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readLibraryType() throws AndrolibException, IOException {
|
private void readLibraryType() throws AndrolibException, IOException {
|
||||||
checkChunkType(Header.XML_TYPE_LIBRARY);
|
checkChunkType(ARSCHeader.XML_TYPE_LIBRARY);
|
||||||
int libraryCount = mIn.readInt();
|
int libraryCount = mIn.readInt();
|
||||||
|
|
||||||
int packageId;
|
int packageId;
|
||||||
@ -165,7 +168,7 @@ public class ARSCDecoder {
|
|||||||
LOGGER.info(String.format("Decoding Shared Library (%s), pkgId: %d", packageName, packageId));
|
LOGGER.info(String.format("Decoding Shared Library (%s), pkgId: %d", packageName, packageId));
|
||||||
}
|
}
|
||||||
|
|
||||||
while(nextChunk().type == Header.XML_TYPE_TYPE) {
|
while(nextChunk().type == ARSCHeader.XML_TYPE_TYPE) {
|
||||||
readTableTypeSpec();
|
readTableTypeSpec();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +184,7 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readOverlaySpec() throws AndrolibException, IOException {
|
private void readOverlaySpec() throws AndrolibException, IOException {
|
||||||
checkChunkType(Header.XML_TYPE_OVERLAY);
|
checkChunkType(ARSCHeader.XML_TYPE_OVERLAY);
|
||||||
String name = mIn.readNullEndedString(256, true);
|
String name = mIn.readNullEndedString(256, true);
|
||||||
String actor = mIn.readNullEndedString(256, true);
|
String actor = mIn.readNullEndedString(256, true);
|
||||||
LOGGER.fine(String.format("Overlay name: \"%s\", actor: \"%s\")", name, actor));
|
LOGGER.fine(String.format("Overlay name: \"%s\", actor: \"%s\")", name, actor));
|
||||||
@ -190,7 +193,7 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readOverlayPolicySpec() throws AndrolibException, IOException {
|
private void readOverlayPolicySpec() throws AndrolibException, IOException {
|
||||||
checkChunkType(Header.XML_TYPE_OVERLAY_POLICY);
|
checkChunkType(ARSCHeader.XML_TYPE_OVERLAY_POLICY);
|
||||||
mIn.skipInt(); // policyFlags
|
mIn.skipInt(); // policyFlags
|
||||||
int count = mIn.readInt();
|
int count = mIn.readInt();
|
||||||
|
|
||||||
@ -208,7 +211,7 @@ public class ARSCDecoder {
|
|||||||
int type = nextChunk().type;
|
int type = nextChunk().type;
|
||||||
ResTypeSpec resTypeSpec;
|
ResTypeSpec resTypeSpec;
|
||||||
|
|
||||||
while (type == Header.XML_TYPE_SPEC_TYPE) {
|
while (type == ARSCHeader.XML_TYPE_SPEC_TYPE) {
|
||||||
resTypeSpec = readSingleTableTypeSpec();
|
resTypeSpec = readSingleTableTypeSpec();
|
||||||
addTypeSpec(resTypeSpec);
|
addTypeSpec(resTypeSpec);
|
||||||
type = nextChunk().type;
|
type = nextChunk().type;
|
||||||
@ -220,7 +223,7 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (type == Header.XML_TYPE_TYPE) {
|
while (type == ARSCHeader.XML_TYPE_TYPE) {
|
||||||
readTableType();
|
readTableType();
|
||||||
|
|
||||||
// skip "TYPE 8 chunks" and/or padding data at the end of this chunk
|
// skip "TYPE 8 chunks" and/or padding data at the end of this chunk
|
||||||
@ -236,7 +239,7 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ResTypeSpec readSingleTableTypeSpec() throws AndrolibException, IOException {
|
private ResTypeSpec readSingleTableTypeSpec() throws AndrolibException, IOException {
|
||||||
checkChunkType(Header.XML_TYPE_SPEC_TYPE);
|
checkChunkType(ARSCHeader.XML_TYPE_SPEC_TYPE);
|
||||||
int id = mIn.readUnsignedByte();
|
int id = mIn.readUnsignedByte();
|
||||||
mIn.skipBytes(3);
|
mIn.skipBytes(3);
|
||||||
int entryCount = mIn.readInt();
|
int entryCount = mIn.readInt();
|
||||||
@ -252,7 +255,7 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ResType readTableType() throws IOException, AndrolibException {
|
private ResType readTableType() throws IOException, AndrolibException {
|
||||||
checkChunkType(Header.XML_TYPE_TYPE);
|
checkChunkType(ARSCHeader.XML_TYPE_TYPE);
|
||||||
int typeId = mIn.readUnsignedByte() - mTypeIdOffset;
|
int typeId = mIn.readUnsignedByte() - mTypeIdOffset;
|
||||||
if (mResTypeSpecs.containsKey(typeId)) {
|
if (mResTypeSpecs.containsKey(typeId)) {
|
||||||
mResId = (0xff000000 & mResId) | mResTypeSpecs.get(typeId).getId() << 16;
|
mResId = (0xff000000 & mResId) | mResTypeSpecs.get(typeId).getId() << 16;
|
||||||
@ -263,7 +266,7 @@ public class ARSCDecoder {
|
|||||||
mIn.skipBytes(2); // reserved
|
mIn.skipBytes(2); // reserved
|
||||||
int entryCount = mIn.readInt();
|
int entryCount = mIn.readInt();
|
||||||
int entriesStart = mIn.readInt();
|
int entriesStart = mIn.readInt();
|
||||||
mMissingResSpecMap = new LinkedHashMap();
|
mMissingResSpecMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
ResConfigFlags flags = readConfigFlags();
|
ResConfigFlags flags = readConfigFlags();
|
||||||
int position = (mHeader.startPosition + entriesStart) - (entryCount * 4);
|
int position = (mHeader.startPosition + entriesStart) - (entryCount * 4);
|
||||||
@ -279,7 +282,7 @@ public class ARSCDecoder {
|
|||||||
LOGGER.fine("Sparse type flags detected: " + mTypeSpec.getName());
|
LOGGER.fine("Sparse type flags detected: " + mTypeSpec.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap<Integer, Integer> entryOffsetMap = new LinkedHashMap();
|
HashMap<Integer, Integer> entryOffsetMap = new LinkedHashMap<>();
|
||||||
for (int i = 0; i < entryCount; i++) {
|
for (int i = 0; i < entryCount; i++) {
|
||||||
if ((typeFlags & 0x01) != 0) {
|
if ((typeFlags & 0x01) != 0) {
|
||||||
entryOffsetMap.put(mIn.readUnsignedShort(), mIn.readUnsignedShort());
|
entryOffsetMap.put(mIn.readUnsignedShort(), mIn.readUnsignedShort());
|
||||||
@ -596,15 +599,15 @@ public class ARSCDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeResSpec(ResResSpec spec) throws AndrolibException {
|
private void removeResSpec(ResResSpec spec) {
|
||||||
if (mPkg.hasResSpec(spec.getId())) {
|
if (mPkg.hasResSpec(spec.getId())) {
|
||||||
mPkg.removeResSpec(spec);
|
mPkg.removeResSpec(spec);
|
||||||
mTypeSpec.removeResSpec(spec);
|
mTypeSpec.removeResSpec(spec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Header nextChunk() throws IOException {
|
private ARSCHeader nextChunk() throws IOException {
|
||||||
return mHeader = Header.read(mIn, mCountIn);
|
return mHeader = ARSCHeader.read(mIn, mCountIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkChunkType(int expectedType) throws AndrolibException {
|
private void checkChunkType(int expectedType) throws AndrolibException {
|
||||||
@ -625,7 +628,7 @@ public class ARSCDecoder {
|
|||||||
private final List<FlagsOffset> mFlagsOffsets;
|
private final List<FlagsOffset> mFlagsOffsets;
|
||||||
private final boolean mKeepBroken;
|
private final boolean mKeepBroken;
|
||||||
|
|
||||||
private Header mHeader;
|
private ARSCHeader mHeader;
|
||||||
private StringBlock mTableStrings;
|
private StringBlock mTableStrings;
|
||||||
private StringBlock mTypeNames;
|
private StringBlock mTypeNames;
|
||||||
private StringBlock mSpecNames;
|
private StringBlock mSpecNames;
|
||||||
@ -641,113 +644,7 @@ public class ARSCDecoder {
|
|||||||
private final static short ENTRY_FLAG_PUBLIC = 0x0002;
|
private final static short ENTRY_FLAG_PUBLIC = 0x0002;
|
||||||
private final static short ENTRY_FLAG_WEAK = 0x0004;
|
private final static short ENTRY_FLAG_WEAK = 0x0004;
|
||||||
|
|
||||||
public static class Header {
|
|
||||||
public final short type;
|
|
||||||
public final int headerSize;
|
|
||||||
public final int chunkSize;
|
|
||||||
public final int startPosition;
|
|
||||||
public final int endPosition;
|
|
||||||
|
|
||||||
public Header(short type, int headerSize, int chunkSize, int headerStart) {
|
|
||||||
this.type = type;
|
|
||||||
this.headerSize = headerSize;
|
|
||||||
this.chunkSize = chunkSize;
|
|
||||||
this.startPosition = headerStart;
|
|
||||||
this.endPosition = headerStart + chunkSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Header read(ExtDataInput in, CountingInputStream countIn) throws IOException {
|
|
||||||
short type;
|
|
||||||
int start = countIn.getCount();
|
|
||||||
try {
|
|
||||||
type = in.readShort();
|
|
||||||
} catch (EOFException ex) {
|
|
||||||
return new Header(TYPE_NONE, 0, 0, countIn.getCount());
|
|
||||||
}
|
|
||||||
return new Header(type, in.readShort(), in.readInt(), start);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final static short TYPE_NONE = -1;
|
|
||||||
public final static short TYPE_STRING_POOL = 0x0001;
|
|
||||||
public final static short TYPE_TABLE = 0x0002;
|
|
||||||
public final static short TYPE_XML = 0x0003;
|
|
||||||
|
|
||||||
public final static short XML_TYPE_PACKAGE = 0x0200;
|
|
||||||
public final static short XML_TYPE_TYPE = 0x0201;
|
|
||||||
public final static short XML_TYPE_SPEC_TYPE = 0x0202;
|
|
||||||
public final static short XML_TYPE_LIBRARY = 0x0203;
|
|
||||||
public final static short XML_TYPE_OVERLAY = 0x0204;
|
|
||||||
public final static short XML_TYPE_OVERLAY_POLICY = 0x0205;
|
|
||||||
public final static short XML_TYPE_STAGED_ALIAS = 0x0206;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FlagsOffset {
|
|
||||||
public final int offset;
|
|
||||||
public final int count;
|
|
||||||
|
|
||||||
public FlagsOffset(int offset, int count) {
|
|
||||||
this.offset = offset;
|
|
||||||
this.count = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class EntryData {
|
|
||||||
public short mFlags;
|
|
||||||
public int mSpecNamesId;
|
|
||||||
public ResValue mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class.getName());
|
|
||||||
private static final int KNOWN_CONFIG_BYTES = 56;
|
private static final int KNOWN_CONFIG_BYTES = 56;
|
||||||
|
|
||||||
public static class ARSCData {
|
private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class.getName());
|
||||||
|
|
||||||
public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets, ResTable resTable) {
|
|
||||||
mPackages = packages;
|
|
||||||
mFlagsOffsets = flagsOffsets;
|
|
||||||
mResTable = resTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FlagsOffset[] getFlagsOffsets() {
|
|
||||||
return mFlagsOffsets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResPackage[] getPackages() {
|
|
||||||
return mPackages;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResPackage getOnePackage() throws AndrolibException {
|
|
||||||
if (mPackages.length <= 0) {
|
|
||||||
throw new AndrolibException("Arsc file contains zero packages");
|
|
||||||
} else if (mPackages.length != 1) {
|
|
||||||
int id = findPackageWithMostResSpecs();
|
|
||||||
LOGGER.info("Arsc file contains multiple packages. Using package "
|
|
||||||
+ mPackages[id].getName() + " as default.");
|
|
||||||
|
|
||||||
return mPackages[id];
|
|
||||||
}
|
|
||||||
return mPackages[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int findPackageWithMostResSpecs() {
|
|
||||||
int count = mPackages[0].getResSpecCount();
|
|
||||||
int id = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < mPackages.length; i++) {
|
|
||||||
if (mPackages[i].getResSpecCount() >= count) {
|
|
||||||
count = mPackages[i].getResSpecCount();
|
|
||||||
id = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResTable getResTable() {
|
|
||||||
return mResTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ResPackage[] mPackages;
|
|
||||||
private final FlagsOffset[] mFlagsOffsets;
|
|
||||||
private final ResTable mResTable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import android.content.res.XmlResourceParser;
|
|||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import brut.androlib.exceptions.AndrolibException;
|
import brut.androlib.exceptions.AndrolibException;
|
||||||
import brut.androlib.res.data.ResID;
|
import brut.androlib.res.data.ResID;
|
||||||
|
import brut.androlib.res.decoder.axml.NamespaceStack;
|
||||||
import brut.androlib.res.xml.ResXmlEncoders;
|
import brut.androlib.res.xml.ResXmlEncoders;
|
||||||
import brut.util.ExtDataInput;
|
import brut.util.ExtDataInput;
|
||||||
import com.google.common.io.LittleEndianDataInputStream;
|
import com.google.common.io.LittleEndianDataInputStream;
|
||||||
@ -39,8 +40,6 @@ import java.util.logging.Logger;
|
|||||||
* open(), close(), or failed call to next(). (2) Closed state, which
|
* open(), close(), or failed call to next(). (2) Closed state, which
|
||||||
* parser obtains after open(), close(), or failed call to next(). In
|
* parser obtains after open(), close(), or failed call to next(). In
|
||||||
* this state methods return invalid values or throw exceptions.
|
* this state methods return invalid values or throw exceptions.
|
||||||
*
|
|
||||||
* <p>TODO: * check all methods in closed state
|
|
||||||
*/
|
*/
|
||||||
public class AXmlResourceParser implements XmlResourceParser {
|
public class AXmlResourceParser implements XmlResourceParser {
|
||||||
|
|
||||||
@ -48,11 +47,6 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
resetEventInfo();
|
resetEventInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AXmlResourceParser(InputStream stream) {
|
|
||||||
this();
|
|
||||||
open(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AndrolibException getFirstError() {
|
public AndrolibException getFirstError() {
|
||||||
return mFirstError;
|
return mFirstError;
|
||||||
}
|
}
|
||||||
@ -88,7 +82,6 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
resetEventInfo();
|
resetEventInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ///////////////////////////////// iteration
|
|
||||||
@Override
|
@Override
|
||||||
public int next() throws XmlPullParserException, IOException {
|
public int next() throws XmlPullParserException, IOException {
|
||||||
if (m_reader == null) {
|
if (m_reader == null) {
|
||||||
@ -155,7 +148,7 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getEventType() throws XmlPullParserException {
|
public int getEventType(){
|
||||||
return m_event;
|
return m_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +219,6 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
return m_strings.getString(uri);
|
return m_strings.getString(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ///////////////////////////////// attributes
|
|
||||||
@Override
|
@Override
|
||||||
public String getClassAttribute() {
|
public String getClassAttribute() {
|
||||||
if (m_classAttribute == -1) {
|
if (m_classAttribute == -1) {
|
||||||
@ -541,7 +533,6 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ///////////////////////////////// dummies
|
|
||||||
@Override
|
@Override
|
||||||
public void setInput(InputStream stream, String inputEncoding) {
|
public void setInput(InputStream stream, String inputEncoding) {
|
||||||
open(stream);
|
open(stream);
|
||||||
@ -605,189 +596,6 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
throw new XmlPullParserException(E_NOT_SUPPORTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////////// implementation
|
|
||||||
/**
|
|
||||||
* Namespace stack, holds prefix+uri pairs, as well as depth information.
|
|
||||||
* All information is stored in one int[] array. Array consists of depth
|
|
||||||
* frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count;
|
|
||||||
* Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable
|
|
||||||
* bottom-up traversal. increaseDepth adds depth frame, decreaseDepth
|
|
||||||
* removes it. push/pop operations operate only in current depth frame.
|
|
||||||
* decreaseDepth removes any remaining (not pop'ed) namespace pairs. findXXX
|
|
||||||
* methods search all depth frames starting from the last namespace pair of
|
|
||||||
* current depth frame. All functions that operate with int, use -1 as
|
|
||||||
* 'invalid value'.
|
|
||||||
*
|
|
||||||
* !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !!
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private static final class NamespaceStack {
|
|
||||||
|
|
||||||
public NamespaceStack() {
|
|
||||||
m_data = new int[32];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reset() {
|
|
||||||
m_dataLength = 0;
|
|
||||||
m_depth = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCurrentCount() {
|
|
||||||
if (m_dataLength == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int offset = m_dataLength - 1;
|
|
||||||
return m_data[offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAccumulatedCount(int depth) {
|
|
||||||
if (m_dataLength == 0 || depth < 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (depth > m_depth) {
|
|
||||||
depth = m_depth;
|
|
||||||
}
|
|
||||||
int accumulatedCount = 0;
|
|
||||||
int offset = 0;
|
|
||||||
for (; depth != 0; --depth) {
|
|
||||||
int count = m_data[offset];
|
|
||||||
accumulatedCount += count;
|
|
||||||
offset += (2 + count * 2);
|
|
||||||
}
|
|
||||||
return accumulatedCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void push(int prefix, int uri) {
|
|
||||||
if (m_depth == 0) {
|
|
||||||
increaseDepth();
|
|
||||||
}
|
|
||||||
ensureDataCapacity(2);
|
|
||||||
int offset = m_dataLength - 1;
|
|
||||||
int count = m_data[offset];
|
|
||||||
m_data[offset - 1 - count * 2] = count + 1;
|
|
||||||
m_data[offset] = prefix;
|
|
||||||
m_data[offset + 1] = uri;
|
|
||||||
m_data[offset + 2] = count + 1;
|
|
||||||
m_dataLength += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean pop() {
|
|
||||||
if (m_dataLength == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int offset = m_dataLength - 1;
|
|
||||||
int count = m_data[offset];
|
|
||||||
if (count == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
count -= 1;
|
|
||||||
offset -= 2;
|
|
||||||
m_data[offset] = count;
|
|
||||||
offset -= (1 + count * 2);
|
|
||||||
m_data[offset] = count;
|
|
||||||
m_dataLength -= 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPrefix(int index) {
|
|
||||||
return get(index, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getUri(int index) {
|
|
||||||
return get(index, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int findPrefix(int uri) {
|
|
||||||
return find(uri, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDepth() {
|
|
||||||
return m_depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void increaseDepth() {
|
|
||||||
ensureDataCapacity(2);
|
|
||||||
int offset = m_dataLength;
|
|
||||||
m_data[offset] = 0;
|
|
||||||
m_data[offset + 1] = 0;
|
|
||||||
m_dataLength += 2;
|
|
||||||
m_depth += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void decreaseDepth() {
|
|
||||||
if (m_dataLength == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int offset = m_dataLength - 1;
|
|
||||||
int count = m_data[offset];
|
|
||||||
if ((offset - 1 - count * 2) == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_dataLength -= 2 + count * 2;
|
|
||||||
m_depth -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureDataCapacity(int capacity) {
|
|
||||||
int available = (m_data.length - m_dataLength);
|
|
||||||
if (available > capacity) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int newLength = (m_data.length + available) * 2;
|
|
||||||
int[] newData = new int[newLength];
|
|
||||||
System.arraycopy(m_data, 0, newData, 0, m_dataLength);
|
|
||||||
m_data = newData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int find(int prefixOrUri, boolean prefix) {
|
|
||||||
if (m_dataLength == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int offset = m_dataLength - 1;
|
|
||||||
for (int i = m_depth; i != 0; --i) {
|
|
||||||
int count = m_data[offset];
|
|
||||||
offset -= 2;
|
|
||||||
for (; count != 0; --count) {
|
|
||||||
if (prefix) {
|
|
||||||
if (m_data[offset] == prefixOrUri) {
|
|
||||||
return m_data[offset + 1];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (m_data[offset + 1] == prefixOrUri) {
|
|
||||||
return m_data[offset];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
offset -= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int get(int index, boolean prefix) {
|
|
||||||
if (m_dataLength == 0 || index < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = m_depth; i != 0; --i) {
|
|
||||||
int count = m_data[offset];
|
|
||||||
if (index >= count) {
|
|
||||||
index -= count;
|
|
||||||
offset += (2 + count * 2);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
offset += (1 + index * 2);
|
|
||||||
if (!prefix) {
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
return m_data[offset];
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int[] m_data;
|
|
||||||
private int m_dataLength;
|
|
||||||
private int m_depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getAttributeOffset(int index) {
|
private int getAttributeOffset(int index) {
|
||||||
if (m_event != START_TAG) {
|
if (m_event != START_TAG) {
|
||||||
throw new IndexOutOfBoundsException("Current event is not START_TAG.");
|
throw new IndexOutOfBoundsException("Current event is not START_TAG.");
|
||||||
@ -833,9 +641,7 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
if (m_strings == null) {
|
if (m_strings == null) {
|
||||||
m_reader.skipCheckInt(CHUNK_AXML_FILE, CHUNK_AXML_FILE_BROKEN);
|
m_reader.skipCheckInt(CHUNK_AXML_FILE, CHUNK_AXML_FILE_BROKEN);
|
||||||
|
|
||||||
/*
|
// chunkSize
|
||||||
* chunkSize
|
|
||||||
*/
|
|
||||||
m_reader.skipInt();
|
m_reader.skipInt();
|
||||||
m_strings = StringBlock.read(m_reader);
|
m_strings = StringBlock.read(m_reader);
|
||||||
m_namespaces.increaseDepth();
|
m_namespaces.increaseDepth();
|
||||||
@ -952,11 +758,6 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ///////////////////////////////// data
|
|
||||||
/*
|
|
||||||
* All values are essentially indices, e.g. m_name is an index of name in
|
|
||||||
* m_strings.
|
|
||||||
*/
|
|
||||||
private ExtDataInput m_reader;
|
private ExtDataInput m_reader;
|
||||||
private ResAttrDecoder mAttrDecoder;
|
private ResAttrDecoder mAttrDecoder;
|
||||||
private AndrolibException mFirstError;
|
private AndrolibException mFirstError;
|
||||||
@ -967,6 +768,8 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
private final NamespaceStack m_namespaces = new NamespaceStack();
|
private final NamespaceStack m_namespaces = new NamespaceStack();
|
||||||
private final String android_ns = "http://schemas.android.com/apk/res/android";
|
private final String android_ns = "http://schemas.android.com/apk/res/android";
|
||||||
private boolean m_decreaseDepth;
|
private boolean m_decreaseDepth;
|
||||||
|
|
||||||
|
// All values are essentially indices, e.g. m_name is an index of name in m_strings.
|
||||||
private int m_event;
|
private int m_event;
|
||||||
private int m_lineNumber;
|
private int m_lineNumber;
|
||||||
private int m_name;
|
private int m_name;
|
||||||
|
@ -18,6 +18,8 @@ package brut.androlib.res.decoder;
|
|||||||
|
|
||||||
import brut.androlib.exceptions.AndrolibException;
|
import brut.androlib.exceptions.AndrolibException;
|
||||||
import brut.androlib.exceptions.CantFind9PatchChunkException;
|
import brut.androlib.exceptions.CantFind9PatchChunkException;
|
||||||
|
import brut.androlib.res.decoder.ninepatch.NinePatchData;
|
||||||
|
import brut.androlib.res.decoder.ninepatch.OpticalInset;
|
||||||
import brut.util.ExtDataInput;
|
import brut.util.ExtDataInput;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
@ -63,7 +65,7 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
|||||||
im2.createGraphics().drawImage(im, 1, 1, w, h, null);
|
im2.createGraphics().drawImage(im, 1, 1, w, h, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
NinePatch np = getNinePatch(data);
|
NinePatchData np = getNinePatch(data);
|
||||||
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
|
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
|
||||||
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
|
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
|
||||||
|
|
||||||
@ -122,11 +124,11 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private NinePatch getNinePatch(byte[] data) throws AndrolibException,
|
private NinePatchData getNinePatch(byte[] data) throws AndrolibException,
|
||||||
IOException {
|
IOException {
|
||||||
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
|
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
|
||||||
find9patchChunk(di, NP_CHUNK_TYPE);
|
find9patchChunk(di, NP_CHUNK_TYPE);
|
||||||
return NinePatch.decode(di);
|
return NinePatchData.decode(di);
|
||||||
}
|
}
|
||||||
|
|
||||||
private OpticalInset getOpticalInset(byte[] data) throws AndrolibException,
|
private OpticalInset getOpticalInset(byte[] data) throws AndrolibException,
|
||||||
@ -169,57 +171,4 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
|||||||
private static final int OI_CHUNK_TYPE = 0x6e704c62; // npLb
|
private static final int OI_CHUNK_TYPE = 0x6e704c62; // npLb
|
||||||
private static final int NP_COLOR = 0xff000000;
|
private static final int NP_COLOR = 0xff000000;
|
||||||
private static final int OI_COLOR = 0xffff0000;
|
private static final int OI_COLOR = 0xffff0000;
|
||||||
|
|
||||||
private static class NinePatch {
|
|
||||||
public final int padLeft, padRight, padTop, padBottom;
|
|
||||||
public final int[] xDivs, yDivs;
|
|
||||||
|
|
||||||
public NinePatch(int padLeft, int padRight, int padTop, int padBottom,
|
|
||||||
int[] xDivs, int[] yDivs) {
|
|
||||||
this.padLeft = padLeft;
|
|
||||||
this.padRight = padRight;
|
|
||||||
this.padTop = padTop;
|
|
||||||
this.padBottom = padBottom;
|
|
||||||
this.xDivs = xDivs;
|
|
||||||
this.yDivs = yDivs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static NinePatch decode(ExtDataInput di) throws IOException {
|
|
||||||
di.skipBytes(1); // wasDeserialized
|
|
||||||
byte numXDivs = di.readByte();
|
|
||||||
byte numYDivs = di.readByte();
|
|
||||||
di.skipBytes(1); // numColors
|
|
||||||
di.skipBytes(8); // xDivs/yDivs offset
|
|
||||||
int padLeft = di.readInt();
|
|
||||||
int padRight = di.readInt();
|
|
||||||
int padTop = di.readInt();
|
|
||||||
int padBottom = di.readInt();
|
|
||||||
di.skipBytes(4); // colorsOffset
|
|
||||||
int[] xDivs = di.readIntArray(numXDivs);
|
|
||||||
int[] yDivs = di.readIntArray(numYDivs);
|
|
||||||
|
|
||||||
return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, yDivs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class OpticalInset {
|
|
||||||
public final int layoutBoundsLeft, layoutBoundsTop, layoutBoundsRight, layoutBoundsBottom;
|
|
||||||
|
|
||||||
public OpticalInset(int layoutBoundsLeft, int layoutBoundsTop,
|
|
||||||
int layoutBoundsRight, int layoutBoundsBottom) {
|
|
||||||
this.layoutBoundsLeft = layoutBoundsLeft;
|
|
||||||
this.layoutBoundsTop = layoutBoundsTop;
|
|
||||||
this.layoutBoundsRight = layoutBoundsRight;
|
|
||||||
this.layoutBoundsBottom = layoutBoundsBottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OpticalInset decode(ExtDataInput di) throws IOException {
|
|
||||||
int layoutBoundsLeft = Integer.reverseBytes(di.readInt());
|
|
||||||
int layoutBoundsTop = Integer.reverseBytes(di.readInt());
|
|
||||||
int layoutBoundsRight = Integer.reverseBytes(di.readInt());
|
|
||||||
int layoutBoundsBottom = Integer.reverseBytes(di.readInt());
|
|
||||||
return new OpticalInset(layoutBoundsLeft, layoutBoundsTop,
|
|
||||||
layoutBoundsRight, layoutBoundsBottom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@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
|
||||||
|
*
|
||||||
|
* https://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.
|
||||||
|
*/
|
||||||
|
package brut.androlib.res.decoder.arsc;
|
||||||
|
|
||||||
|
import brut.androlib.exceptions.AndrolibException;
|
||||||
|
import brut.androlib.res.data.ResPackage;
|
||||||
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class ARSCData {
|
||||||
|
private final ResPackage[] mPackages;
|
||||||
|
private final FlagsOffset[] mFlagsOffsets;
|
||||||
|
|
||||||
|
public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets) {
|
||||||
|
mPackages = packages;
|
||||||
|
mFlagsOffsets = flagsOffsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlagsOffset[] getFlagsOffsets() {
|
||||||
|
return mFlagsOffsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResPackage[] getPackages() {
|
||||||
|
return mPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResPackage getOnePackage() throws AndrolibException {
|
||||||
|
if (mPackages.length == 0) {
|
||||||
|
throw new AndrolibException("Arsc file contains zero packages");
|
||||||
|
} else if (mPackages.length != 1) {
|
||||||
|
int id = findPackageWithMostResSpecs();
|
||||||
|
LOGGER.info("Arsc file contains multiple packages. Using package "
|
||||||
|
+ mPackages[id].getName() + " as default.");
|
||||||
|
|
||||||
|
return mPackages[id];
|
||||||
|
}
|
||||||
|
return mPackages[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int findPackageWithMostResSpecs() {
|
||||||
|
int count = mPackages[0].getResSpecCount();
|
||||||
|
int id = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < mPackages.length; i++) {
|
||||||
|
if (mPackages[i].getResSpecCount() >= count) {
|
||||||
|
count = mPackages[i].getResSpecCount();
|
||||||
|
id = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(ARSCData.class.getName());
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@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
|
||||||
|
*
|
||||||
|
* https://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.
|
||||||
|
*/
|
||||||
|
package brut.androlib.res.decoder.arsc;
|
||||||
|
|
||||||
|
import brut.util.ExtDataInput;
|
||||||
|
import org.apache.commons.io.input.CountingInputStream;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class ARSCHeader {
|
||||||
|
public final short type;
|
||||||
|
public final int headerSize;
|
||||||
|
public final int chunkSize;
|
||||||
|
public final int startPosition;
|
||||||
|
public final int endPosition;
|
||||||
|
|
||||||
|
public ARSCHeader(short type, int headerSize, int chunkSize, int headerStart) {
|
||||||
|
this.type = type;
|
||||||
|
this.headerSize = headerSize;
|
||||||
|
this.chunkSize = chunkSize;
|
||||||
|
this.startPosition = headerStart;
|
||||||
|
this.endPosition = headerStart + chunkSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ARSCHeader read(ExtDataInput in, CountingInputStream countIn) throws IOException {
|
||||||
|
short type;
|
||||||
|
int start = countIn.getCount();
|
||||||
|
try {
|
||||||
|
type = in.readShort();
|
||||||
|
} catch (EOFException ex) {
|
||||||
|
return new ARSCHeader(TYPE_NONE, 0, 0, countIn.getCount());
|
||||||
|
}
|
||||||
|
return new ARSCHeader(type, in.readShort(), in.readInt(), start);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static short TYPE_NONE = -1;
|
||||||
|
public final static short TYPE_STRING_POOL = 0x0001;
|
||||||
|
public final static short TYPE_TABLE = 0x0002;
|
||||||
|
public final static short TYPE_XML = 0x0003;
|
||||||
|
|
||||||
|
public final static short XML_TYPE_PACKAGE = 0x0200;
|
||||||
|
public final static short XML_TYPE_TYPE = 0x0201;
|
||||||
|
public final static short XML_TYPE_SPEC_TYPE = 0x0202;
|
||||||
|
public final static short XML_TYPE_LIBRARY = 0x0203;
|
||||||
|
public final static short XML_TYPE_OVERLAY = 0x0204;
|
||||||
|
public final static short XML_TYPE_OVERLAY_POLICY = 0x0205;
|
||||||
|
public final static short XML_TYPE_STAGED_ALIAS = 0x0206;
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@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
|
||||||
|
*
|
||||||
|
* https://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.
|
||||||
|
*/
|
||||||
|
package brut.androlib.res.decoder.arsc;
|
||||||
|
|
||||||
|
import brut.androlib.res.data.value.ResValue;
|
||||||
|
|
||||||
|
public class EntryData {
|
||||||
|
public short mFlags;
|
||||||
|
public int mSpecNamesId;
|
||||||
|
public ResValue mValue;
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@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
|
||||||
|
*
|
||||||
|
* https://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.
|
||||||
|
*/
|
||||||
|
package brut.androlib.res.decoder.arsc;
|
||||||
|
|
||||||
|
public class FlagsOffset {
|
||||||
|
public final int offset;
|
||||||
|
public final int count;
|
||||||
|
|
||||||
|
public FlagsOffset(int offset, int count) {
|
||||||
|
this.offset = offset;
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@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
|
||||||
|
*
|
||||||
|
* https://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.
|
||||||
|
*/
|
||||||
|
package brut.androlib.res.decoder.axml;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace stack, holds prefix+uri pairs, as well as depth information.
|
||||||
|
* All information is stored in one int[] array. Array consists of depth
|
||||||
|
* frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count;
|
||||||
|
* Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable
|
||||||
|
* bottom-up traversal. increaseDepth adds depth frame, decreaseDepth
|
||||||
|
* removes it. push/pop operations operate only in current depth frame.
|
||||||
|
* decreaseDepth removes any remaining (not pop'ed) namespace pairs. findXXX
|
||||||
|
* methods search all depth frames starting from the last namespace pair of
|
||||||
|
* current depth frame. All functions that operate with int, use -1 as
|
||||||
|
* 'invalid value'.
|
||||||
|
* <p>
|
||||||
|
* !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !!
|
||||||
|
*/
|
||||||
|
public final class NamespaceStack {
|
||||||
|
private int[] m_data;
|
||||||
|
private int m_dataLength;
|
||||||
|
private int m_depth;
|
||||||
|
|
||||||
|
public NamespaceStack() {
|
||||||
|
m_data = new int[32];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
m_dataLength = 0;
|
||||||
|
m_depth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentCount() {
|
||||||
|
if (m_dataLength == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int offset = m_dataLength - 1;
|
||||||
|
return m_data[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAccumulatedCount(int depth) {
|
||||||
|
if (m_dataLength == 0 || depth < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (depth > m_depth) {
|
||||||
|
depth = m_depth;
|
||||||
|
}
|
||||||
|
int accumulatedCount = 0;
|
||||||
|
int offset = 0;
|
||||||
|
for (; depth != 0; --depth) {
|
||||||
|
int count = m_data[offset];
|
||||||
|
accumulatedCount += count;
|
||||||
|
offset += (2 + count * 2);
|
||||||
|
}
|
||||||
|
return accumulatedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push(int prefix, int uri) {
|
||||||
|
if (m_depth == 0) {
|
||||||
|
increaseDepth();
|
||||||
|
}
|
||||||
|
ensureDataCapacity(2);
|
||||||
|
int offset = m_dataLength - 1;
|
||||||
|
int count = m_data[offset];
|
||||||
|
m_data[offset - 1 - count * 2] = count + 1;
|
||||||
|
m_data[offset] = prefix;
|
||||||
|
m_data[offset + 1] = uri;
|
||||||
|
m_data[offset + 2] = count + 1;
|
||||||
|
m_dataLength += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean pop() {
|
||||||
|
if (m_dataLength == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int offset = m_dataLength - 1;
|
||||||
|
int count = m_data[offset];
|
||||||
|
if (count == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
count -= 1;
|
||||||
|
offset -= 2;
|
||||||
|
m_data[offset] = count;
|
||||||
|
offset -= (1 + count * 2);
|
||||||
|
m_data[offset] = count;
|
||||||
|
m_dataLength -= 2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPrefix(int index) {
|
||||||
|
return get(index, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUri(int index) {
|
||||||
|
return get(index, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int findPrefix(int uri) {
|
||||||
|
return find(uri, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDepth() {
|
||||||
|
return m_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseDepth() {
|
||||||
|
ensureDataCapacity(2);
|
||||||
|
int offset = m_dataLength;
|
||||||
|
m_data[offset] = 0;
|
||||||
|
m_data[offset + 1] = 0;
|
||||||
|
m_dataLength += 2;
|
||||||
|
m_depth += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decreaseDepth() {
|
||||||
|
if (m_dataLength == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int offset = m_dataLength - 1;
|
||||||
|
int count = m_data[offset];
|
||||||
|
if ((offset - 1 - count * 2) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_dataLength -= 2 + count * 2;
|
||||||
|
m_depth -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureDataCapacity(int capacity) {
|
||||||
|
int available = (m_data.length - m_dataLength);
|
||||||
|
if (available > capacity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int newLength = (m_data.length + available) * 2;
|
||||||
|
int[] newData = new int[newLength];
|
||||||
|
System.arraycopy(m_data, 0, newData, 0, m_dataLength);
|
||||||
|
m_data = newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int find(int prefixOrUri, boolean prefix) {
|
||||||
|
if (m_dataLength == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int offset = m_dataLength - 1;
|
||||||
|
for (int i = m_depth; i != 0; --i) {
|
||||||
|
int count = m_data[offset];
|
||||||
|
offset -= 2;
|
||||||
|
for (; count != 0; --count) {
|
||||||
|
if (prefix) {
|
||||||
|
if (m_data[offset] == prefixOrUri) {
|
||||||
|
return m_data[offset + 1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m_data[offset + 1] == prefixOrUri) {
|
||||||
|
return m_data[offset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int get(int index, boolean prefix) {
|
||||||
|
if (m_dataLength == 0 || index < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int offset = 0;
|
||||||
|
for (int i = m_depth; i != 0; --i) {
|
||||||
|
int count = m_data[offset];
|
||||||
|
if (index >= count) {
|
||||||
|
index -= count;
|
||||||
|
offset += (2 + count * 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
offset += (1 + index * 2);
|
||||||
|
if (!prefix) {
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
return m_data[offset];
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@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
|
||||||
|
*
|
||||||
|
* https://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.
|
||||||
|
*/
|
||||||
|
package brut.androlib.res.decoder.ninepatch;
|
||||||
|
|
||||||
|
import brut.util.ExtDataInput;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class NinePatchData
|
||||||
|
{
|
||||||
|
public final int padLeft, padRight, padTop, padBottom;
|
||||||
|
public final int[] xDivs, yDivs;
|
||||||
|
|
||||||
|
public NinePatchData(int padLeft, int padRight, int padTop, int padBottom, int[] xDivs, int[] yDivs) {
|
||||||
|
this.padLeft = padLeft;
|
||||||
|
this.padRight = padRight;
|
||||||
|
this.padTop = padTop;
|
||||||
|
this.padBottom = padBottom;
|
||||||
|
this.xDivs = xDivs;
|
||||||
|
this.yDivs = yDivs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NinePatchData decode(ExtDataInput di) throws IOException {
|
||||||
|
di.skipBytes(1); // wasDeserialized
|
||||||
|
byte numXDivs = di.readByte();
|
||||||
|
byte numYDivs = di.readByte();
|
||||||
|
di.skipBytes(1); // numColors
|
||||||
|
di.skipBytes(8); // xDivs/yDivs offset
|
||||||
|
int padLeft = di.readInt();
|
||||||
|
int padRight = di.readInt();
|
||||||
|
int padTop = di.readInt();
|
||||||
|
int padBottom = di.readInt();
|
||||||
|
di.skipBytes(4); // colorsOffset
|
||||||
|
int[] xDivs = di.readIntArray(numXDivs);
|
||||||
|
int[] yDivs = di.readIntArray(numYDivs);
|
||||||
|
|
||||||
|
return new NinePatchData(padLeft, padRight, padTop, padBottom, xDivs, yDivs);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
|
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@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
|
||||||
|
*
|
||||||
|
* https://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.
|
||||||
|
*/
|
||||||
|
package brut.androlib.res.decoder.ninepatch;
|
||||||
|
|
||||||
|
import brut.util.ExtDataInput;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class OpticalInset {
|
||||||
|
public final int layoutBoundsLeft, layoutBoundsTop, layoutBoundsRight, layoutBoundsBottom;
|
||||||
|
|
||||||
|
public OpticalInset(int layoutBoundsLeft, int layoutBoundsTop, int layoutBoundsRight, int layoutBoundsBottom) {
|
||||||
|
this.layoutBoundsLeft = layoutBoundsLeft;
|
||||||
|
this.layoutBoundsTop = layoutBoundsTop;
|
||||||
|
this.layoutBoundsRight = layoutBoundsRight;
|
||||||
|
this.layoutBoundsBottom = layoutBoundsBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OpticalInset decode(ExtDataInput di) throws IOException {
|
||||||
|
int layoutBoundsLeft = Integer.reverseBytes(di.readInt());
|
||||||
|
int layoutBoundsTop = Integer.reverseBytes(di.readInt());
|
||||||
|
int layoutBoundsRight = Integer.reverseBytes(di.readInt());
|
||||||
|
int layoutBoundsBottom = Integer.reverseBytes(di.readInt());
|
||||||
|
return new OpticalInset(layoutBoundsLeft, layoutBoundsTop, layoutBoundsRight, layoutBoundsBottom);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user