@@ -280,21 +280,21 @@ private ResType readTableType() throws IOException, AndrolibException {
280
280
281
281
mHeader .checkForUnreadHeader (mIn );
282
282
283
+ boolean isOffset16 = (typeFlags & TABLE_TYPE_FLAG_OFFSET16 ) != 0 ;
284
+ boolean isSparse = (typeFlags & TABLE_TYPE_FLAG_SPARSE ) != 0 ;
285
+
283
286
// Be sure we don't poison mResTable by marking the application as sparse
284
287
// Only flag the ResTable as sparse if the main package is not loaded.
285
- if (( typeFlags & TABLE_TYPE_FLAG_SPARSE ) != 0 && !mResTable .isMainPkgLoaded ()) {
288
+ if (isSparse && !mResTable .isMainPkgLoaded ()) {
286
289
mResTable .setSparseResources (true );
287
290
}
288
291
289
- if ((typeFlags & TABLE_TYPE_FLAG_OFFSET16 ) != 0 ) {
290
- LOGGER .warning ("Please report this application to Apktool for a fix: https://github.com/iBotPeaches/Apktool/issues/3367" );
291
- throw new AndrolibException ("Unexpected TYPE_FLAG_OFFSET16" );
292
- }
293
-
294
292
HashMap <Integer , Integer > entryOffsetMap = new LinkedHashMap <>();
295
293
for (int i = 0 ; i < entryCount ; i ++) {
296
- if (( typeFlags & TABLE_TYPE_FLAG_SPARSE ) != 0 ) {
294
+ if (isSparse ) {
297
295
entryOffsetMap .put (mIn .readUnsignedShort (), mIn .readUnsignedShort ());
296
+ } else if (isOffset16 ) {
297
+ entryOffsetMap .put (i , mIn .readUnsignedShort ());
298
298
} else {
299
299
entryOffsetMap .put (i , mIn .readInt ());
300
300
}
@@ -310,11 +310,12 @@ private ResType readTableType() throws IOException, AndrolibException {
310
310
}
311
311
312
312
mType = flags .isInvalid && !mKeepBroken ? null : mPkg .getOrCreateConfig (flags );
313
+ int noEntry = isOffset16 ? NO_ENTRY_OFFSET16 : NO_ENTRY ;
313
314
314
315
for (int i : entryOffsetMap .keySet ()) {
315
316
mResId = (mResId & 0xffff0000 ) | i ;
316
317
int offset = entryOffsetMap .get (i );
317
- if (offset == NO_ENTRY ) {
318
+ if (offset == noEntry ) {
318
319
mMissingResSpecMap .put (mResId , typeId );
319
320
continue ;
320
321
}
@@ -347,25 +348,35 @@ private ResType readTableType() throws IOException, AndrolibException {
347
348
348
349
private EntryData readEntryData () throws IOException , AndrolibException {
349
350
short size = mIn .readShort ();
350
- if (size < 0 ) {
351
- throw new AndrolibException ("Entry size is under 0 bytes." );
351
+ short flags = mIn .readShort ();
352
+
353
+ boolean isComplex = (flags & ENTRY_FLAG_COMPLEX ) != 0 ;
354
+ boolean isCompact = (flags & ENTRY_FLAG_COMPACT ) != 0 ;
355
+
356
+ if (size < 0 && !isCompact ) {
357
+ throw new AndrolibException ("Entry size is under 0 bytes and not compactly packed." );
352
358
}
353
359
354
- short flags = mIn .readShort ();
355
360
int specNamesId = mIn .readInt ();
356
- if (specNamesId == NO_ENTRY ) {
361
+ if (specNamesId == NO_ENTRY && ! isCompact ) {
357
362
return null ;
358
363
}
359
364
360
- boolean isComplex = ( flags & ENTRY_FLAG_COMPLEX ) != 0 ;
361
- boolean isCompact = ( flags & ENTRY_FLAG_COMPACT ) != 0 ;
362
-
365
+ // #3366 - In a compactly packed entry, the key index is the size & type is higher 8 bits on flags.
366
+ // We assume a size of 8 bytes for compact entries and the specNamesId is the data itself encoded.
367
+ ResValue value ;
363
368
if (isCompact ) {
364
- LOGGER .warning ("Please report this application to Apktool for a fix: https://github.com/iBotPeaches/Apktool/issues/3366" );
365
- throw new AndrolibException ("Unexpected entry type: compact" );
369
+ byte type = (byte ) ((flags >> 8 ) & 0xFF );
370
+ value = readCompactValue (type , specNamesId );
371
+
372
+ // To keep code below happy - we know if compact that the size has the key index encoded.
373
+ specNamesId = size ;
374
+ } else if (isComplex ) {
375
+ value = readComplexEntry ();
376
+ } else {
377
+ value = readValue ();
366
378
}
367
379
368
- ResValue value = isComplex ? readComplexEntry () : readValue ();
369
380
// #2824 - In some applications the res entries are duplicated with the 2nd being malformed.
370
381
// AOSP skips this, so we will do the same.
371
382
if (value == null ) {
@@ -443,6 +454,12 @@ private ResBagValue readComplexEntry() throws IOException, AndrolibException {
443
454
return factory .bagFactory (parent , items , mTypeSpec );
444
455
}
445
456
457
+ private ResIntBasedValue readCompactValue (byte type , int data ) throws AndrolibException {
458
+ return type == TypedValue .TYPE_STRING
459
+ ? mPkg .getValueFactory ().factory (mTableStrings .getHTML (data ), data )
460
+ : mPkg .getValueFactory ().factory (type , data , null );
461
+ }
462
+
446
463
private ResIntBasedValue readValue () throws IOException , AndrolibException {
447
464
int size = mIn .readShort ();
448
465
if (size < 8 ) {
@@ -686,6 +703,7 @@ private void checkChunkType(int expectedType) throws AndrolibException {
686
703
private static final int KNOWN_CONFIG_BYTES = 64 ;
687
704
688
705
private static final int NO_ENTRY = 0xFFFFFFFF ;
706
+ private static final int NO_ENTRY_OFFSET16 = 0xFFFF ;
689
707
690
708
private static final Logger LOGGER = Logger .getLogger (ARSCDecoder .class .getName ());
691
709
}
0 commit comments