diff --git a/src/main/java/com/reandroid/lib/apk/ResourceIds.java b/src/main/java/com/reandroid/lib/apk/ResourceIds.java index 9abc140..3a62be9 100644 --- a/src/main/java/com/reandroid/lib/apk/ResourceIds.java +++ b/src/main/java/com/reandroid/lib/apk/ResourceIds.java @@ -24,7 +24,6 @@ import com.reandroid.lib.json.JSONObject; import com.reandroid.xml.*; import java.io.*; -import java.nio.charset.StandardCharsets; import java.util.*; public class ResourceIds { @@ -248,6 +247,21 @@ import java.util.*; this.id = id; this.typeMap = new HashMap<>(); } + public Type.Entry getEntry(String typeName, String name){ + Type type=getType(typeName); + if(type==null){ + return null; + } + return type.getEntry(name); + } + private Type getType(String typeName){ + for(Type type:typeMap.values()){ + if(type.getName().equals(typeName)){ + return type; + } + } + return null; + } public int getIdInt(){ return 0xff & id; } @@ -443,6 +457,14 @@ import java.util.*; this.id = id; this.entryMap = new HashMap<>(); } + public Entry getEntry(String entryName){ + for(Entry entry:entryMap.values()){ + if(entry.getName().equals(entryName)){ + return entry; + } + } + return null; + } public int getIdInt(){ return 0xff & id; } diff --git a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java index b681100..d22e081 100755 --- a/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java +++ b/src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java @@ -322,10 +322,6 @@ import java.util.Set; } public void setValueAsInteger(int val){ setValueType(ValueType.INT_DEC); - ResXmlAttributeArray array=getParentResXmlAttributeArray(); - if(array!=null && array.getFirstIntAttribute()==this){ - setValueType(ValueType.FIRST_INT); - } setRawValue(val); setValueStringReference(-1); } diff --git a/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java b/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java index 024d2c6..49512ec 100755 --- a/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java +++ b/src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java @@ -31,6 +31,49 @@ import java.util.regex.Pattern; public class ValueDecoder { + public static EncodeResult encodeGuessAny(String txt){ + if(txt==null){ + return null; + } + EncodeResult result=encodeColor(txt); + if(result!=null){ + return result; + } + result=encodeDimensionOrFloat(txt); + if(result!=null){ + return result; + } + result=encodeHexOrInt(txt); + if(result!=null){ + return result; + } + return encodeBoolean(txt); + } + public static EncodeResult encodeBoolean(String txt){ + if(txt==null){ + return null; + } + txt=txt.trim().toLowerCase(); + if(txt.equals("true")){ + return new EncodeResult(ValueType.INT_BOOLEAN, 0xffffffff); + } + if(txt.equals("false")){ + return new EncodeResult(ValueType.INT_BOOLEAN, 0); + } + return null; + } + public static boolean isInteger(String txt){ + if(txt==null){ + return false; + } + return PATTERN_INTEGER.matcher(txt).matches(); + } + public static boolean isHex(String txt){ + if(txt==null){ + return false; + } + return PATTERN_HEX.matcher(txt).matches(); + } public static boolean isReference(String txt){ if(txt==null){ return false; @@ -49,33 +92,94 @@ import java.util.regex.Pattern; } return false; } - public static int encodeFloatOrDimension(String dimensionString){ - if(dimensionString==null){ - return 0; + public static EncodeResult encodeColor(String value){ + if(value==null){ + return null; } - Matcher matcher=PATTERN_DIMEN.matcher(dimensionString); + Matcher matcher = PATTERN_COLOR.matcher(value); if(!matcher.find()){ - return 0; + return null; + } + value=matcher.group(1); + ValueType valueType; + if(value.length()==6){ + valueType=ValueType.INT_COLOR_RGB8; + }else { + valueType=ValueType.INT_COLOR_ARGB8; + } + return new EncodeResult(valueType, parseHex(value)); + } + public static EncodeResult encodeHexOrInt(String numString){ + if(numString==null){ + return null; + } + if(isHex(numString)){ + return new EncodeResult(ValueType.INT_HEX, parseHex(numString)); + } + if(isInteger(numString)){ + return new EncodeResult(ValueType.INT_DEC, parseHex(numString)); + } + return null; + } + public static int parseHex(String hexString){ + boolean negative=false; + hexString=hexString.trim().toLowerCase(); + if(hexString.startsWith("-")){ + negative=true; + hexString=hexString.substring(1); + } + if(!hexString.startsWith("0x")){ + hexString="0x"+hexString; + } + long l=Long.decode(hexString); + if(negative){ + l=-l; + } + return (int) l; + } + public static int parseInteger(String intString){ + intString=intString.trim(); + boolean negative=false; + if(intString.startsWith("-")){ + negative=true; + intString=intString.substring(1); + } + long l=Long.parseLong(intString); + if(negative){ + l=-l; + } + return (int) l; + } + public static EncodeResult encodeDimensionOrFloat(String value){ + if(value==null){ + return null; + } + Matcher matcher=PATTERN_DIMEN.matcher(value); + if(!matcher.find()){ + return null; } String sign = matcher.group(1); String number = matcher.group(2); String unit = matcher.group(3); - float value = Float.parseFloat(number); + float fraction = Float.parseFloat(number); if("-".equals(sign)){ - value=-value; + fraction=-fraction; } - return encodeFloatOrDimension(value, unit); + return encodeDimensionOrFloat(fraction, unit); } - private static int encodeFloatOrDimension(float val, String unit){ + private static EncodeResult encodeDimensionOrFloat(float val, String unit){ if(unit==null||"".equals(unit)){ - return Float.floatToIntBits(val); + return new EncodeResult(ValueType.FLOAT, + Float.floatToIntBits(val)); } + ValueType valueType = ValueType.DIMENSION; int index=0; if("%".equals(unit)||"%p".equals(unit)){ val=val/100.0f; if("%p".equals(unit)){ index=1; } + valueType = ValueType.FRACTION; }else { index=ValueDecoder.getDimensionIndex(unit); } @@ -93,10 +197,10 @@ import java.util.regex.Pattern; } } } - shift=shift<<4; - result= result | shift; - result= result | index; - return result; + shift = shift<<4; + result = result | shift; + result = result | index; + return new EncodeResult(valueType, result); } public static String decodeAttributeName(EntryStore store, PackageBlock currentPackage, int resourceId){ @@ -252,7 +356,7 @@ import java.util.regex.Pattern; // Should not happen the string could be in ResXmlBlock, but if you are lazy here it goes return decodeString(entryStore, currentPackageId, rawVal); } - if(valueType==ValueType.FIRST_INT||valueType==ValueType.INT_HEX){ + if(valueType==ValueType.FIRST_INT||valueType==ValueType.INT_DEC||valueType==ValueType.INT_HEX){ result=decodeAttribute(entryStore, nameResourceId, rawVal); if(result!=null){ return result; @@ -683,6 +787,18 @@ import java.util.regex.Pattern; str=str.trim(); return str.length()==0; } + public static class EncodeResult{ + public final ValueType valueType; + public final int value; + public EncodeResult(ValueType valueType, int value){ + this.valueType=valueType; + this.value=value; + } + @Override + public String toString(){ + return valueType+": "+String.format("0x%08x", value); + } + } private static final String[] DIMENSION_UNIT_STRS = new String[] { "px", "dip", "sp", "pt", "in", "mm" }; private static final float MANTISSA_MULT = 1.0f / (1 << 8); @@ -690,8 +806,8 @@ import java.util.regex.Pattern; 1.0f * MANTISSA_MULT, 1.0f / (1 << 7) * MANTISSA_MULT, 1.0f / (1 << 15) * MANTISSA_MULT, 1.0f / (1 << 23) * MANTISSA_MULT }; - private static final Pattern PATTERN_COLOR = Pattern.compile("^#([0-9a-fA-F]{6,8})$"); - private static final Pattern PATTERN_DIMEN = Pattern.compile("^(-?)([0-9]+\\.[0-9E\\-+]+)([dimnpstx%]{0,3})$"); + public static final Pattern PATTERN_COLOR = Pattern.compile("^#([0-9a-fA-F]{6,8})$"); + public static final Pattern PATTERN_DIMEN = Pattern.compile("^(-?)([0-9]+\\.[0-9E\\-+]+)([dimnpstx%]{0,3})$"); private static final Pattern PATTERN_INTEGER = Pattern.compile("^(-?)([0-9]+)$"); private static final Pattern PATTERN_HEX = Pattern.compile("^0x[0-9a-fA-F]+$"); public static final Pattern PATTERN_REFERENCE = Pattern.compile("^([?@])(([^\\s:@?/]+:)?)([^\\s:@?/]+)/([^\\s:@?/]+)$"); diff --git a/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java b/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java index c7493eb..5e2ecde 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java +++ b/src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java @@ -285,6 +285,9 @@ public class EntryBlock extends Block implements JSONConvert { return mSpecReference.get(); } public void setSpecReference(int ref){ + if(mSpecReference==null){ + return; + } int old=mSpecReference.get(); if(ref==old){ return; @@ -294,7 +297,9 @@ public class EntryBlock extends Block implements JSONConvert { } public void setSpecReference(SpecString specString){ removeSpecRef(); - mSpecReference.set(specString.getIndex()); + if(mSpecReference!=null){ + mSpecReference.set(specString.getIndex()); + } } public BaseResValue getResValue(){ return mResValue; @@ -639,6 +644,9 @@ public class EntryBlock extends Block implements JSONConvert { } } private void removeSpecRef(){ + if(mSpecReference==null){ + return; + } TypeBlock typeBlock=getTypeBlock(); if(typeBlock==null){ return; diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java b/src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java index f69fa14..f1e2331 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java +++ b/src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java @@ -80,6 +80,7 @@ public class ResValueBag extends BaseResValue { count=0; } mCount.set(count); + mResValueBagItemArray.setChildesCount(count); } @Override @@ -121,7 +122,7 @@ public class ResValueBag extends BaseResValue { } private void refreshCount(){ - setCount(getResValueBagItemArray().childesCount()); + mCount.set(getResValueBagItemArray().childesCount()); } @Override protected int onWriteBytes(OutputStream writer) throws IOException { diff --git a/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java b/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java index 26701fa..1ad728c 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java +++ b/src/main/java/com/reandroid/lib/arsc/value/ResValueBagItem.java @@ -171,7 +171,7 @@ public class ResValueBagItem extends BaseResValueItem{ return; } beforeDataValueChanged(); - setShort(OFFSET_DATA+2, val); + setShort(OFFSET_DATA, val); afterDataValueChanged(); } @Override diff --git a/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeBag.java b/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeBag.java index 2682b3b..b9f87c5 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeBag.java +++ b/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeBag.java @@ -15,8 +15,10 @@ */ package com.reandroid.lib.arsc.value.attribute; +import com.reandroid.lib.arsc.decoder.ValueDecoder; import com.reandroid.lib.arsc.value.EntryBlock; import com.reandroid.lib.arsc.value.ResValueBag; +import com.reandroid.lib.arsc.value.ValueType; import com.reandroid.lib.common.EntryStore; @@ -25,6 +27,26 @@ public class AttributeBag { public AttributeBag(AttributeBagItem[] bagItems){ this.mBagItems=bagItems; } + public ValueDecoder.EncodeResult encodeName(String valueString){ + if(valueString==null){ + return null; + } + int value=0; + boolean foundOnce=false; + String[] names=valueString.split("[\\s|]+"); + for(String name:names){ + AttributeBagItem item=searchByName(name); + if(item==null){ + continue; + } + value|=item.getBagItem().getData(); + foundOnce=true; + } + if(!foundOnce){ + return null; + } + return new ValueDecoder.EncodeResult(ValueType.INT_HEX, value); + } public String decodeAttributeValue(EntryStore entryStore, int attrValue){ AttributeBagItem[] bagItems=searchValue(attrValue); return AttributeBagItem.toString(entryStore, bagItems); @@ -32,6 +54,18 @@ public class AttributeBag { public String decodeValueType(){ return AttributeValueType.toString(getValueTypes()); } + public AttributeBagItem searchByName(String entryName){ + AttributeBagItem[] bagItems= getBagItems(); + for(AttributeBagItem item:bagItems){ + if(item.isType()){ + continue; + } + if(entryName.equals(item.getNameOrHex())){ + return item; + } + } + return null; + } public AttributeBagItem[] searchValue(int attrValue){ if(isFlag()){ return searchFlagValue(attrValue); diff --git a/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeItemType.java b/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeItemType.java index 4c3a8c2..286176a 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeItemType.java +++ b/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeItemType.java @@ -45,6 +45,9 @@ public enum AttributeItemType { return null; } name=name.toUpperCase(); + if(name.equals("FORMATS")){ + return FORMAT; + } for(AttributeItemType bagType:values()){ if(name.equals(bagType.name())){ return bagType; diff --git a/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeValueType.java b/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeValueType.java index 0e816ad..582ae9f 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeValueType.java +++ b/src/main/java/com/reandroid/lib/arsc/value/attribute/AttributeValueType.java @@ -15,10 +15,11 @@ */ package com.reandroid.lib.arsc.value.attribute; -import java.util.ArrayList; -import java.util.List; +import com.reandroid.lib.arsc.value.ValueType; -public enum AttributeValueType { +import java.util.*; + + public enum AttributeValueType { REFERENCE((byte) 0x01), STRING((byte) 0x02), INTEGER((byte) 0x04), @@ -129,4 +130,49 @@ public enum AttributeValueType { } return null; } + public static Set toValueTypes(AttributeValueType[] types){ + Set results=new HashSet<>(); + if(types==null){ + return results; + } + for(AttributeValueType type:types){ + if(type==ANY){ + return new HashSet<>(Arrays.asList(ValueType.values())); + } + switch (type){ + case REFERENCE: + results.add(ValueType.REFERENCE); + results.add(ValueType.ATTRIBUTE); + results.add(ValueType.DYNAMIC_REFERENCE); + results.add(ValueType.DYNAMIC_ATTRIBUTE); + break; + case COLOR: + results.add(ValueType.INT_COLOR_ARGB8); + results.add(ValueType.INT_COLOR_ARGB4); + results.add(ValueType.INT_COLOR_RGB8); + results.add(ValueType.INT_COLOR_RGB4); + break; + case STRING: + results.add(ValueType.STRING); + break; + case BOOL: + results.add(ValueType.INT_BOOLEAN); + break; + case FLOAT: + case FRACTION: + results.add(ValueType.FLOAT); + results.add(ValueType.FRACTION); + break; + case DIMENSION: + results.add(ValueType.FRACTION); + results.add(ValueType.DIMENSION); + break; + case INTEGER: + results.add(ValueType.INT_DEC); + results.add(ValueType.INT_HEX); + break; + } + } + return results; + } } diff --git a/src/main/java/com/reandroid/lib/arsc/value/plurals/PluralsQuantity.java b/src/main/java/com/reandroid/lib/arsc/value/plurals/PluralsQuantity.java index d05bc2b..fcc2898 100755 --- a/src/main/java/com/reandroid/lib/arsc/value/plurals/PluralsQuantity.java +++ b/src/main/java/com/reandroid/lib/arsc/value/plurals/PluralsQuantity.java @@ -48,7 +48,7 @@ public enum PluralsQuantity { if(name==null){ return null; } - name=name.toUpperCase(); + name=name.trim().toUpperCase(); PluralsQuantity[] all=values(); for(PluralsQuantity pq:all){ if(name.equals(pq.name())){ diff --git a/src/main/java/com/reandroid/lib/common/ResourceResolver.java b/src/main/java/com/reandroid/lib/common/ResourceResolver.java new file mode 100644 index 0000000..fd8c7de --- /dev/null +++ b/src/main/java/com/reandroid/lib/common/ResourceResolver.java @@ -0,0 +1,21 @@ + /* + * 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.lib.common; + +public interface ResourceResolver { + int resolveResourceId(String packageName, String type, String name); + int resolveResourceId(int packageId, String type, String name); +}