Skip to content

Commit

Permalink
Merge branch 'iBotPeaches:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
b4byhuey authored Aug 10, 2023
2 parents da7ea4f + 1243dd5 commit 23e9470
Show file tree
Hide file tree
Showing 11 changed files with 53 additions and 128 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:
paths:
- '**.java'
- '**.gradle'
- '**.kts'
- 'brut.apktool/apktool-lib/src/main/resources/**'
- 'brut.apktool/apktool-lib/src/test/**'
- '.github/workflows/**'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,6 @@ boolean isSynthesized(ResID resId) {
return mSynthesizedRes.contains(resId);
}

public void removeResSpec(ResResSpec spec) {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,6 @@ public ResTypeSpec getType() {
return mType;
}

public boolean isDummyResSpec() {
return getName().startsWith("APKTOOL_DUMMY_");
}

public void addResource(ResResource res) throws AndrolibException {
addResource(res, false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,10 @@ public void setSharedLibrary(boolean flag) {
}

public void setSparseResources(boolean flag) {
if (mApkInfo.sparseResources != flag) {
LOGGER.info("Sparsely packed resources detected.");
}
mApkInfo.sparseResources = flag;

}

public void clearSdkInfo() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,11 @@ public final class ResTypeSpec {
private final String mName;
private final Map<String, ResResSpec> mResSpecs = new LinkedHashMap<>();

private final ResTable mResTable;
private final ResPackage mPackage;

private final int mId;
private final int mEntryCount;

public ResTypeSpec(String name, ResTable resTable, ResPackage package_, int id, int entryCount) {
public ResTypeSpec(String name, int id) {
this.mName = name;
this.mResTable = resTable;
this.mPackage = package_;
this.mId = id;
this.mEntryCount = entryCount;
}

public String getName() {
Expand All @@ -68,10 +61,6 @@ public ResResSpec getResSpecUnsafe(String name) {
return mResSpecs.get(name);
}

public void removeResSpec(ResResSpec spec) {
mResSpecs.remove(spec.getName());
}

public void addResSpec(ResResSpec spec) throws AndrolibException {
if (mResSpecs.put(spec.getName(), spec) != null) {
throw new AndrolibException(String.format("Multiple res specs: %s/%s", getName(), spec.getName()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,6 @@ public void serializeToResValuesXml(XmlSerializer serializer,
}
}

// Dummy attributes should be <item> with type attribute
if (res.getResSpec().isDummyResSpec()) {
item = true;
}

// Android does not allow values (false) for ids.xml anymore
// https://issuetracker.google.com/issues/80475496
// But it decodes as a ResBoolean, which makes no sense. So force it to empty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,18 @@ private ARSCDecoder(InputStream arscStream, ResTable resTable, boolean storeFlag

private ResPackage[] readResourceTable() throws IOException, AndrolibException {
Set<ResPackage> pkgs = new LinkedHashSet<>();

ResTypeSpec typeSpec;
int chunkNumber = 1;

chunkLoop:
for (;;) {
nextChunk();

LOGGER.fine(String.format(
"Chunk #%d start: type=0x%04x chunkSize=0x%08x", chunkNumber++, mHeader.type, mHeader.chunkSize
));

switch (mHeader.type) {
case ARSCHeader.RES_NULL_TYPE:
readUnknownChunk();
Expand Down Expand Up @@ -112,10 +118,6 @@ private ResPackage[] readResourceTable() throws IOException, AndrolibException {
}
}

if (mPkg != null && mPkg.getResSpecCount() > 0) {
addMissingResSpecs();
}

return pkgs.toArray(new ResPackage[0]);
}

Expand Down Expand Up @@ -245,7 +247,7 @@ private ResTypeSpec readTableSpecType() throws AndrolibException, IOException {
mHeader.checkForUnreadHeader(mIn);

mIn.skipBytes(entryCount * 4); // flags
mTypeSpec = new ResTypeSpec(mTypeNames.getString(id - 1), mResTable, mPkg, id, entryCount);
mTypeSpec = new ResTypeSpec(mTypeNames.getString(id - 1), id);
mPkg.addType(mTypeSpec);

return mTypeSpec;
Expand All @@ -264,15 +266,11 @@ private ResType readTableType() throws IOException, AndrolibException {
int entryCount = mIn.readInt();
mIn.skipInt(); // entriesStart

mMissingResSpecMap = new LinkedHashMap<>();
ResConfigFlags flags = readConfigFlags();

mHeader.checkForUnreadHeader(mIn);

if ((typeFlags & 0x01) != 0) {
LOGGER.fine("Sparse type flags detected: " + mTypeSpec.getName());

// We've detected sparse resources, lets record this so we can rebuild in that same format
mResTable.setSparseResources(true);
}

Expand All @@ -297,12 +295,11 @@ private ResType readTableType() throws IOException, AndrolibException {
mType = flags.isInvalid && !mKeepBroken ? null : mPkg.getOrCreateConfig(flags);

for (int i : entryOffsetMap.keySet()) {
mResId = (mResId & 0xffff0000) | i;
int offset = entryOffsetMap.get(i);
if (offset == NO_ENTRY) {
continue;
}
mMissingResSpecMap.put(i, false);
mResId = (mResId & 0xffff0000) | i;

// As seen in some recent APKs - there are more entries reported than can fit in the chunk.
if (mIn.position() == mHeader.endPosition) {
Expand Down Expand Up @@ -369,14 +366,6 @@ private void readEntry(EntryData entryData) throws AndrolibException {
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);
Expand Down Expand Up @@ -598,41 +587,6 @@ private void addTypeSpec(ResTypeSpec resTypeSpec) {
mResTypeSpecs.put(resTypeSpec.getId(), resTypeSpec);
}

private void addMissingResSpecs() throws AndrolibException {
int resId = mResId & 0xffff0000;

for (int i : mMissingResSpecMap.keySet()) {
if (mMissingResSpecMap.get(i)) continue;

ResResSpec spec = new ResResSpec(new ResID(resId | i), "APKTOOL_DUMMY_" + Integer.toHexString(i), mPkg, mTypeSpec);

// If we already have this resID don't add it again.
if (! mPkg.hasResSpec(new ResID(resId | i))) {
mPkg.addResSpec(spec);
mTypeSpec.addResSpec(spec);

if (mType == null) {
mType = mPkg.getOrCreateConfig(new ResConfigFlags());
}

// We are going to make dummy attributes a null reference (@null) now instead of a boolean false.
// This is because aapt2 is much more strict when it comes to what we can put in an application.
ResValue value = new ResReferenceValue(mPkg, 0, "");

ResResource res = new ResResource(mType, spec, value);
mType.addResource(res);
spec.addResource(res);
}
}
}

private void removeResSpec(ResResSpec spec) {
if (mPkg.hasResSpec(spec.getId())) {
mPkg.removeResSpec(spec);
mTypeSpec.removeResSpec(spec);
}
}

private ARSCHeader nextChunk() throws IOException {
return mHeader = ARSCHeader.read(mIn);
}
Expand All @@ -658,7 +612,6 @@ private void checkChunkType(int expectedType) throws AndrolibException {
private ResType mType;
private int mResId;
private int mTypeIdOffset = 0;
private HashMap<Integer, Boolean> mMissingResSpecMap;
private final HashMap<Integer, ResTypeSpec> mResTypeSpecs = new HashMap<>();

private final static short ENTRY_FLAG_COMPLEX = 0x0001;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void decode(ResResource res, Directory inDir, Directory outDir, Map<Strin
resFileMapping.put(inFilePath, outFilePath);
}

LOGGER.fine("Decoding file: " + inFilePath + " to: " + outFilePath);
LOGGER.fine("Decoding file " + inFilePath + " to " + outFilePath);

try {
if (typeName.equals("raw")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ public static StringBlock readWithoutChunk(ExtCountingDataInput reader, int star
}

StringBlock block = new StringBlock();
block.m_isUTF8 = (flags & UTF8_FLAG) != 0;
block.m_stringOffsets = reader.readSafeIntArray(stringCount, startPosition + stringsOffset);
block.mIsUtf8 = (flags & UTF8_FLAG) != 0;
block.mStringOffsets = reader.readSafeIntArray(stringCount, startPosition + stringsOffset);

if (styleCount != 0) {
block.m_styleOffsets = reader.readSafeIntArray(styleCount, startPosition + stylesOffset);
block.mStyleOffsets = reader.readSafeIntArray(styleCount, startPosition + stylesOffset);
}

// #3236 - Some applications give a style offset, but have 0 styles. Make this check more robust.
Expand All @@ -72,12 +72,12 @@ public static StringBlock readWithoutChunk(ExtCountingDataInput reader, int star
size = stylesOffset - stringsOffset;
}

block.m_strings = new byte[size];
reader.readFully(block.m_strings);
block.mStrings = new byte[size];
reader.readFully(block.mStrings);

if (hasStyles) {
size = chunkSize - stylesOffset;
block.m_styles = reader.readIntArray(size / 4);
block.mStyles = reader.readIntArray(size / 4);
}

// In case we aren't 4 byte aligned we need to skip the remaining bytes.
Expand All @@ -97,18 +97,18 @@ public static StringBlock readWithoutChunk(ExtCountingDataInput reader, int star
* @return String
*/
public String getString(int index) {
if (index < 0 || m_stringOffsets == null || index >= m_stringOffsets.length) {
if (index < 0 || mStringOffsets == null || index >= mStringOffsets.length) {
return null;
}
int offset = m_stringOffsets[index];
int offset = mStringOffsets[index];
int length;

int[] val;
if (m_isUTF8) {
val = getUtf8(m_strings, offset);
if (mIsUtf8) {
val = getUtf8(mStrings, offset);
offset = val[0];
} else {
val = getUtf16(m_strings, offset);
val = getUtf16(mStrings, offset);
offset += val[0];
}
length = val[1];
Expand Down Expand Up @@ -155,16 +155,16 @@ public int find(String string) {
if (string == null) {
return -1;
}
for (int i = 0; i != m_stringOffsets.length; ++i) {
int offset = m_stringOffsets[i];
int length = getShort(m_strings, offset);
for (int i = 0; i != mStringOffsets.length; ++i) {
int offset = mStringOffsets[i];
int length = getShort(mStrings, offset);
if (length != string.length()) {
continue;
}
int j = 0;
for (; j != length; ++j) {
offset += 2;
if (string.charAt(j) != getShort(m_strings, offset)) {
if (string.charAt(j) != getShort(mStrings, offset)) {
break;
}
}
Expand All @@ -180,8 +180,8 @@ private StringBlock() {

@VisibleForTesting
StringBlock(byte[] strings, boolean isUTF8) {
m_strings = strings;
m_isUTF8 = isUTF8;
mStrings = strings;
mIsUtf8 = isUTF8;
}

/**
Expand All @@ -190,15 +190,15 @@ private StringBlock() {
* start index in string * third int is tag end index in string
*/
private int[] getStyle(int index) {
if (m_styleOffsets == null || m_styles == null|| index >= m_styleOffsets.length) {
if (mStyleOffsets == null || mStyles == null|| index >= mStyleOffsets.length) {
return null;
}
int offset = m_styleOffsets[index] / 4;
int offset = mStyleOffsets[index] / 4;
int count = 0;
int[] style;

for (int i = offset; i < m_styles.length; ++i) {
if (m_styles[i] == -1) {
for (int i = offset; i < mStyles.length; ++i) {
if (mStyles[i] == -1) {
break;
}
count += 1;
Expand All @@ -209,34 +209,34 @@ private int[] getStyle(int index) {
}
style = new int[count];

for (int i = offset, j = 0; i < m_styles.length;) {
if (m_styles[i] == -1) {
for (int i = offset, j = 0; i < mStyles.length;) {
if (mStyles[i] == -1) {
break;
}
style[j++] = m_styles[i++];
style[j++] = mStyles[i++];
}
return style;
}

@VisibleForTesting
String decodeString(int offset, int length) {
try {
final ByteBuffer wrappedBuffer = ByteBuffer.wrap(m_strings, offset, length);
return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode(wrappedBuffer).toString();
final ByteBuffer wrappedBuffer = ByteBuffer.wrap(mStrings, offset, length);
return (mIsUtf8 ? UTF8_DECODER : UTF16LE_DECODER).decode(wrappedBuffer).toString();
} catch (CharacterCodingException ex) {
if (!m_isUTF8) {
if (!mIsUtf8) {
LOGGER.warning("Failed to decode a string at offset " + offset + " of length " + length);
return null;
}
} catch (IndexOutOfBoundsException ex) {
if (!m_isUTF8) {
if (!mIsUtf8) {
LOGGER.warning("String extends outside of pool at " + offset + " of length " + length);
return null;
}
}

try {
final ByteBuffer wrappedBufferRetry = ByteBuffer.wrap(m_strings, offset, length);
final ByteBuffer wrappedBufferRetry = ByteBuffer.wrap(mStrings, offset, length);
// in some places, Android uses 3-byte UTF-8 sequences instead of 4-bytes.
// If decoding failed, we try to use CESU-8 decoder, which is closer to what Android actually uses.
return CESU8_DECODER.decode(wrappedBufferRetry).toString();
Expand Down Expand Up @@ -285,11 +285,11 @@ private static int[] getUtf16(byte[] array, int offset) {
return new int[] {2, val * 2};
}

private int[] m_stringOffsets;
private byte[] m_strings;
private int[] m_styleOffsets;
private int[] m_styles;
private boolean m_isUTF8;
private int[] mStringOffsets;
private byte[] mStrings;
private int[] mStyleOffsets;
private int[] mStyles;
private boolean mIsUtf8;

private final CharsetDecoder UTF16LE_DECODER = StandardCharsets.UTF_16LE.newDecoder();
private final CharsetDecoder UTF8_DECODER = StandardCharsets.UTF_8.newDecoder();
Expand Down
Loading

0 comments on commit 23e9470

Please sign in to comment.