mirror of
https://github.com/revanced/Apktool.git
synced 2024-12-13 06:17:46 +01:00
Correctly read sparse ResourceTables.
Prior to this change, APKs usually went Package -> TypeSpec -> Config (all) -> Entries. Reading all configs under that TypeSpec. Now we have packages that go Package -> TypeSpec -> Config (single) -> Entries. So we have to read this correctly to make sure we can correctly decode sparse and packed Resource tables.
This commit is contained in:
parent
7e803aeac9
commit
eabb7d819b
@ -156,6 +156,10 @@ public class ResPackage {
|
||||
return mSynthesizedRes.contains(resId);
|
||||
}
|
||||
|
||||
public void removeResSpec(ResResSpec spec) throws AndrolibException {
|
||||
mResSpecs.remove(spec.getId());
|
||||
}
|
||||
|
||||
public void addResSpec(ResResSpec spec) throws AndrolibException {
|
||||
if (mResSpecs.put(spec.getId(), spec) != null) {
|
||||
throw new AndrolibException("Multiple resource specs: " + spec);
|
||||
@ -179,6 +183,9 @@ public class ResPackage {
|
||||
public void addResource(ResResource res) {
|
||||
}
|
||||
|
||||
public void removeResource(ResResource res) {
|
||||
}
|
||||
|
||||
public void addSynthesizedRes(int resId) {
|
||||
mSynthesizedRes.add(new ResID(resId));
|
||||
}
|
||||
|
@ -101,6 +101,10 @@ public class ResResSpec {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public boolean isDummyResSpec() {
|
||||
return getName().startsWith("APKTOOL_DUMMY_");
|
||||
}
|
||||
|
||||
public void addResource(ResResource res) throws AndrolibException {
|
||||
addResource(res, false);
|
||||
}
|
||||
@ -114,6 +118,11 @@ public class ResResSpec {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeResource(ResResource res) throws AndrolibException {
|
||||
ResConfigFlags flags = res.getConfig().getFlags();
|
||||
mResources.remove(flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mId.toString() + " " + mType.toString() + "/" + mName;
|
||||
|
@ -56,6 +56,11 @@ public class ResType {
|
||||
addResource(res, false);
|
||||
}
|
||||
|
||||
public void removeResource(ResResource res) throws AndrolibException {
|
||||
ResResSpec spec = res.getResSpec();
|
||||
mResources.remove(spec);
|
||||
}
|
||||
|
||||
public void addResource(ResResource res, boolean overwrite)
|
||||
throws AndrolibException {
|
||||
ResResSpec spec = res.getResSpec();
|
||||
|
@ -30,16 +30,29 @@ public final class ResTypeSpec {
|
||||
private final ResTable mResTable;
|
||||
private final ResPackage mPackage;
|
||||
|
||||
public ResTypeSpec(String name, ResTable resTable, ResPackage package_) {
|
||||
private final byte mId;
|
||||
private final int mEntryCount;
|
||||
|
||||
public ResTypeSpec(String name, ResTable resTable, ResPackage package_, byte id, int entryCount) {
|
||||
this.mName = name;
|
||||
this.mResTable = resTable;
|
||||
this.mPackage = package_;
|
||||
this.mId = id;
|
||||
this.mEntryCount = entryCount;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public byte getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public int getEntryCount() {
|
||||
return mEntryCount;
|
||||
}
|
||||
|
||||
public boolean isString() {
|
||||
return mName.equalsIgnoreCase("string");
|
||||
}
|
||||
@ -57,6 +70,10 @@ public final class ResTypeSpec {
|
||||
return spec;
|
||||
}
|
||||
|
||||
public void removeResSpec(ResResSpec spec) throws AndrolibException {
|
||||
mResSpecs.remove(spec.getName());
|
||||
}
|
||||
|
||||
public void addResSpec(ResResSpec spec) throws AndrolibException {
|
||||
if (mResSpecs.put(spec.getName(), spec) != null) {
|
||||
throw new AndrolibException(String.format(
|
||||
|
@ -138,39 +138,59 @@ public class ARSCDecoder {
|
||||
}
|
||||
|
||||
private ResTypeSpec readTableTypeSpec() throws AndrolibException, IOException {
|
||||
mTypeSpec = readSingleTableTypeSpec();
|
||||
addTypeSpec(mTypeSpec);
|
||||
|
||||
int type = nextChunk().type;
|
||||
ResTypeSpec resTypeSpec;
|
||||
|
||||
while (type == Header.TYPE_SPEC_TYPE) {
|
||||
resTypeSpec = readSingleTableTypeSpec();
|
||||
addTypeSpec(resTypeSpec);
|
||||
type = nextChunk().type;
|
||||
}
|
||||
|
||||
while (type == Header.TYPE_TYPE) {
|
||||
readTableType();
|
||||
type = nextChunk().type;
|
||||
|
||||
addMissingResSpecs();
|
||||
}
|
||||
|
||||
return mTypeSpec;
|
||||
}
|
||||
|
||||
private ResTypeSpec readSingleTableTypeSpec() throws AndrolibException, IOException {
|
||||
checkChunkType(Header.TYPE_SPEC_TYPE);
|
||||
byte id = mIn.readByte();
|
||||
mIn.skipBytes(3);
|
||||
int entryCount = mIn.readInt();
|
||||
|
||||
mMissingResSpecs = new boolean[entryCount];
|
||||
Arrays.fill(mMissingResSpecs, true);
|
||||
|
||||
if (mFlagsOffsets != null) {
|
||||
mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount));
|
||||
}
|
||||
|
||||
/* flags */mIn.skipBytes(entryCount * 4);
|
||||
|
||||
mResId = (0xff000000 & mResId) | id << 16;
|
||||
mTypeSpec = new ResTypeSpec(mTypeNames.getString(id - 1), mResTable, mPkg);
|
||||
mTypeSpec = new ResTypeSpec(mTypeNames.getString(id - 1), mResTable, mPkg, id, entryCount);
|
||||
mPkg.addType(mTypeSpec);
|
||||
|
||||
while (nextChunk().type == Header.TYPE_TYPE) {
|
||||
readTableType();
|
||||
}
|
||||
|
||||
addMissingResSpecs();
|
||||
|
||||
return mTypeSpec;
|
||||
}
|
||||
|
||||
private ResType readTableType() throws IOException, AndrolibException {
|
||||
checkChunkType(Header.TYPE_TYPE);
|
||||
/* typeId */mIn.skipBytes(1);
|
||||
byte typeId = mIn.readByte();
|
||||
if (mResTypeSpecs.containsKey(typeId)) {
|
||||
mResId = (0xff000000 & mResId) | mResTypeSpecs.get(typeId).getId() << 16;
|
||||
mTypeSpec = mResTypeSpecs.get(typeId);
|
||||
}
|
||||
|
||||
/* res0, res1 */mIn.skipBytes(3);
|
||||
int entryCount = mIn.readInt();
|
||||
/* entriesStart */mIn.skipInt();
|
||||
|
||||
mMissingResSpecs = new boolean[entryCount];
|
||||
Arrays.fill(mMissingResSpecs, true);
|
||||
|
||||
ResConfigFlags flags = readConfigFlags();
|
||||
int[] entryOffsets = mIn.readIntArray(entryCount);
|
||||
|
||||
@ -214,6 +234,14 @@ public class ARSCDecoder {
|
||||
ResResSpec spec;
|
||||
if (mPkg.hasResSpec(resId)) {
|
||||
spec = mPkg.getResSpec(resId);
|
||||
|
||||
if (spec.isDummyResSpec()) {
|
||||
removeResSpec(spec);
|
||||
|
||||
spec = new ResResSpec(resId, mSpecNames.getString(specNamesId), mPkg, mTypeSpec);
|
||||
mPkg.addResSpec(spec);
|
||||
mTypeSpec.addResSpec(spec);
|
||||
}
|
||||
} else {
|
||||
spec = new ResResSpec(resId, mSpecNames.getString(specNamesId), mPkg, mTypeSpec);
|
||||
mPkg.addResSpec(spec);
|
||||
@ -386,6 +414,10 @@ public class ARSCDecoder {
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
private void addTypeSpec(ResTypeSpec resTypeSpec) {
|
||||
mResTypeSpecs.put(resTypeSpec.getId(), resTypeSpec);
|
||||
}
|
||||
|
||||
private void addMissingResSpecs() throws AndrolibException {
|
||||
int resId = mResId & 0xffff0000;
|
||||
|
||||
@ -395,19 +427,30 @@ public class ARSCDecoder {
|
||||
}
|
||||
|
||||
ResResSpec spec = new ResResSpec(new ResID(resId | i), String.format("APKTOOL_DUMMY_%04x", i), mPkg, mTypeSpec);
|
||||
mPkg.addResSpec(spec);
|
||||
mTypeSpec.addResSpec(spec);
|
||||
|
||||
if (mType == null) {
|
||||
mType = mPkg.getOrCreateConfig(new ResConfigFlags());
|
||||
// If we already have this resID dont add it again.
|
||||
if (! mPkg.hasResSpec(new ResID(resId | i))) {
|
||||
mPkg.addResSpec(spec);
|
||||
mTypeSpec.addResSpec(spec);
|
||||
|
||||
if (mType == null) {
|
||||
mType = mPkg.getOrCreateConfig(new ResConfigFlags());
|
||||
}
|
||||
|
||||
ResValue value = new ResBoolValue(false, 0, null);
|
||||
ResResource res = new ResResource(mType, spec, value);
|
||||
|
||||
mPkg.addResource(res);
|
||||
mType.addResource(res);
|
||||
spec.addResource(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResValue value = new ResBoolValue(false, 0, null);
|
||||
ResResource res = new ResResource(mType, spec, value);
|
||||
|
||||
mPkg.addResource(res);
|
||||
mType.addResource(res);
|
||||
spec.addResource(res);
|
||||
private void removeResSpec(ResResSpec spec) throws AndrolibException {
|
||||
if (mPkg.hasResSpec(spec.getId())) {
|
||||
mPkg.removeResSpec(spec);
|
||||
mTypeSpec.removeResSpec(spec);
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,6 +486,7 @@ public class ARSCDecoder {
|
||||
private ResType mType;
|
||||
private int mResId;
|
||||
private boolean[] mMissingResSpecs;
|
||||
private HashMap<Byte, ResTypeSpec> mResTypeSpecs = new HashMap<>();
|
||||
|
||||
private final static short ENTRY_FLAG_COMPLEX = 0x0001;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user