From c5c4a042a7dfdd9d6c25ddabe559c6d9c46054cf Mon Sep 17 00:00:00 2001 From: REAndroid Date: Mon, 27 Mar 2023 14:26:35 -0400 Subject: [PATCH] make general purpose Decoder for Value --- .../com/reandroid/arsc/decoder/Decoder.java | 147 ++++++++++++++++++ .../reandroid/arsc/decoder/ValueDecoder.java | 56 +++++-- 2 files changed, 190 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/reandroid/arsc/decoder/Decoder.java diff --git a/src/main/java/com/reandroid/arsc/decoder/Decoder.java b/src/main/java/com/reandroid/arsc/decoder/Decoder.java new file mode 100644 index 0000000..9120575 --- /dev/null +++ b/src/main/java/com/reandroid/arsc/decoder/Decoder.java @@ -0,0 +1,147 @@ + /* + * Copyright (C) 2022 github.com/REAndroid + * + * 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 + * + * http://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 com.reandroid.arsc.decoder; + +import com.reandroid.apk.AndroidFrameworks; +import com.reandroid.apk.FrameworkApk; +import com.reandroid.arsc.ApkFile; +import com.reandroid.arsc.chunk.MainChunk; +import com.reandroid.arsc.chunk.PackageBlock; +import com.reandroid.arsc.chunk.TableBlock; +import com.reandroid.arsc.chunk.xml.AndroidManifestBlock; +import com.reandroid.arsc.chunk.xml.ResXmlDocument; +import com.reandroid.arsc.group.EntryGroup; +import com.reandroid.arsc.value.AttributeValue; +import com.reandroid.arsc.value.Value; +import com.reandroid.arsc.value.ValueType; +import com.reandroid.common.EntryStore; + +import java.io.IOException; + +public class Decoder { + private final EntryStore entryStore; + private int currentPackageId; + public Decoder(EntryStore entryStore, int currentPackageId){ + this.entryStore = entryStore; + this.currentPackageId = currentPackageId; + } + public String decodeResourceName(int resourceId){ + EntryGroup entryGroup = getEntryStore().getEntryGroup(resourceId); + if(entryGroup!=null){ + return entryGroup.getSpecName(); + } + return null; + } + public String decodeValue(Value value){ + if(value==null){ + return null; + } + ValueType valueType = value.getValueType(); + if(valueType == ValueType.STRING){ + return value.getValueAsString(); + } + return ValueDecoder.decode(getEntryStore(), getCurrentPackageId(), value); + } + public String decodeAttributeValue(AttributeValue attributeValue){ + if(attributeValue == null){ + return null; + } + return ValueDecoder.decode(getEntryStore(), getCurrentPackageId(), attributeValue); + } + private EntryStore getEntryStore() { + return entryStore; + } + public int getCurrentPackageId() { + return currentPackageId; + } + public void setCurrentPackageId(int currentPackageId) { + this.currentPackageId = currentPackageId; + } + + public static Decoder create(ResXmlDocument resXmlDocument){ + MainChunk mainChunk = resXmlDocument.getMainChunk(); + if(mainChunk == null){ + return getNullEntryStoreDecoder(); + } + ApkFile apkFile = mainChunk.getApkFile(); + if(apkFile == null){ + return getNullEntryStoreDecoder(); + } + TableBlock tableBlock = apkFile.getTableBlock(); + if(tableBlock == null){ + return getNullEntryStoreDecoder(); + } + AndroidManifestBlock manifestBlock = apkFile.getAndroidManifestBlock(); + if(manifestBlock!=null){ + int currentPackageId = manifestBlock.guessCurrentPackageId(); + if(currentPackageId!=0){ + return create(tableBlock, currentPackageId); + } + } + return create(tableBlock); + } + public static Decoder create(TableBlock tableBlock){ + if(tableBlock.getFrameWorks().size()==0){ + tableBlock.addFramework(getFramework()); + } + int currentPackageId; + PackageBlock packageBlock = tableBlock.pickOne(); + if(packageBlock!=null){ + currentPackageId = packageBlock.getId(); + }else { + // 0x7f most common + currentPackageId = 0x7f; + } + return create(tableBlock, currentPackageId); + } + public static Decoder create(TableBlock tableBlock, int currentPackageId){ + if(tableBlock.getFrameWorks().size()==0){ + TableBlock framework = getFramework(); + if(framework!=null){ + PackageBlock packageBlock = framework.pickOne(); + if(packageBlock!=null && packageBlock.getId() != currentPackageId){ + tableBlock.addFramework(framework); + } + } + } + return new Decoder(tableBlock, currentPackageId); + } + private static TableBlock getFramework(){ + try { + FrameworkApk frameworkApk = AndroidFrameworks.getCurrent(); + if(frameworkApk == null){ + frameworkApk = AndroidFrameworks.getLatest(); + AndroidFrameworks.setCurrent(frameworkApk); + } + return frameworkApk.getTableBlock(); + } catch (IOException ignored) { + } + return null; + } + + public static Decoder getNullEntryStoreDecoder(){ + if(NULL_ENTRY_STORE_DECODER!=null){ + return NULL_ENTRY_STORE_DECODER; + } + synchronized (Decoder.class){ + TableBlock tableBlock = new TableBlock(); + Decoder decoder = new Decoder(tableBlock, 0x7f); + NULL_ENTRY_STORE_DECODER = decoder; + return decoder; + } + } + private static Decoder NULL_ENTRY_STORE_DECODER; +} diff --git a/src/main/java/com/reandroid/arsc/decoder/ValueDecoder.java b/src/main/java/com/reandroid/arsc/decoder/ValueDecoder.java index d4136ef..aacefd5 100755 --- a/src/main/java/com/reandroid/arsc/decoder/ValueDecoder.java +++ b/src/main/java/com/reandroid/arsc/decoder/ValueDecoder.java @@ -69,6 +69,9 @@ import java.util.regex.Pattern; if("@null".equals(txt)){ return new EncodeResult(ValueType.REFERENCE, 0); } + if("?null".equals(txt)){ + return new EncodeResult(ValueType.ATTRIBUTE, 0); + } EncodeResult result=encodeColor(txt); if(result!=null){ return result; @@ -143,7 +146,7 @@ import java.util.regex.Pattern; return PATTERN_HEX_REFERENCE.matcher(txt).matches(); } private static boolean isNullReference(String txt){ - if("@null".equals(txt)){ + if("@null".equals(txt)||"?null".equals(txt)){ return true; } if("@empty".equals(txt)){ @@ -326,17 +329,6 @@ import java.util.regex.Pattern; } return decode(valueType, data); } - public static String decodeIntEntry(EntryStore store, Entry entry){ - if(entry ==null){ - return null; - } - TableEntry tableEntry = entry.getTableEntry(); - if(tableEntry == null || (tableEntry instanceof ResTableMapEntry)){ - return null; - } - ResValue resValue =(ResValue) tableEntry.getValue(); - return decodeIntEntry(store, resValue); - } public static String decodeIntEntry(EntryStore store, ResValue resValue){ if(resValue ==null){ return null; @@ -406,6 +398,44 @@ import java.util.regex.Pattern; ValueType valueType= resValue.getValueType(); return buildReferenceValue(store, entry, valueType, resourceId); } + public static String decode(EntryStore entryStore, int currentPackageId, Value value){ + + ValueType valueType = value.getValueType(); + if(valueType == ValueType.STRING){ + return value.getValueAsString(); + } + int data = value.getData(); + if(valueType==ValueType.REFERENCE || valueType==ValueType.ATTRIBUTE){ + String currentPackageName = getPackageName(entryStore, currentPackageId); + return buildReferenceValue(entryStore, + valueType, currentPackageName, + data); + } + return decode(valueType, data); + } + public static String decode(EntryStore entryStore, int currentPackageId, AttributeValue attributeValue){ + ValueType valueType = attributeValue.getValueType(); + if(valueType == ValueType.STRING){ + return attributeValue.getValueAsString(); + } + int data = attributeValue.getData(); + if(valueType==ValueType.REFERENCE || valueType==ValueType.ATTRIBUTE){ + String currentPackageName = getPackageName(entryStore, currentPackageId); + return buildReferenceValue(entryStore, + valueType, currentPackageName, + data); + } + if(valueType==ValueType.INT_DEC || valueType==ValueType.INT_HEX){ + String result = decodeAttribute(entryStore, + attributeValue.getNameResourceID(), + attributeValue.getData()); + if(result!=null){ + return result; + } + } + return decode(valueType, data); + } + @Deprecated public static String decode(EntryStore entryStore, int currentPackageId, int nameResourceId, ValueType valueType, int rawVal){ String currPackageName=getPackageName(entryStore, currentPackageId); String result=buildReferenceValue(entryStore, valueType, currPackageName, rawVal); @@ -506,7 +536,7 @@ import java.util.regex.Pattern; atOrQues='@'; }else if(valueType==ValueType.ATTRIBUTE){ if(resourceId==0){ - return "@empty"; + return "?null"; } atOrQues='?'; }else {