From 1b240b2281bd4f73a612821568ebd6430575c858 Mon Sep 17 00:00:00 2001 From: REAndroid Date: Thu, 4 May 2023 01:21:41 +0200 Subject: [PATCH] fix encode decode errors --- .../apk/xmldecoder/BagDecoderAttr.java | 3 +- .../apk/xmlencoder/XMLValuesEncoder.java | 8 +- .../apk/xmlencoder/XMLValuesEncoderArray.java | 15 ++- .../apk/xmlencoder/XMLValuesEncoderAttr.java | 14 +++ .../reandroid/arsc/chunk/PackageBlock.java | 3 + .../com/reandroid/arsc/decoder/ColorUtil.java | 114 ++++++++++++++++++ .../reandroid/arsc/decoder/ValueDecoder.java | 33 +---- .../com/reandroid/arsc/util/ResNameMap.java | 2 +- .../com/reandroid/arsc/value/ValueItem.java | 3 + 9 files changed, 159 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/reandroid/arsc/decoder/ColorUtil.java diff --git a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderAttr.java b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderAttr.java index cedee2f..5c98eb6 100644 --- a/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderAttr.java +++ b/src/main/java/com/reandroid/apk/xmldecoder/BagDecoderAttr.java @@ -18,6 +18,7 @@ package com.reandroid.apk.xmldecoder; import com.reandroid.apk.XmlHelper; import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.ResTableMapEntry; +import com.reandroid.arsc.value.ValueType; import com.reandroid.arsc.value.attribute.AttributeBag; import com.reandroid.arsc.value.attribute.AttributeBagItem; import com.reandroid.common.EntryStore; @@ -56,7 +57,7 @@ class BagDecoderAttr extends BagDecoder{ writer.attribute("name", name); int rawVal = item.getData(); String value; - if(is_flag){ + if(item.getBagItem().getValueType() == ValueType.INT_HEX){ value = String.format("0x%08x", rawVal); }else { value = String.valueOf(rawVal); diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoder.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoder.java index 1d920be..a35239a 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoder.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoder.java @@ -84,7 +84,13 @@ class XMLValuesEncoder { } void encodeReferenceValue(Entry entry, String value){ int resourceId = getMaterials().resolveReference(value); - entry.setValueAsReference(resourceId); + ValueType valueType; + if(value.charAt(0) == '?'){ + valueType = ValueType.ATTRIBUTE; + }else{ + valueType = ValueType.REFERENCE; + } + entry.setValueAsRaw(valueType, resourceId); } void encodeStringValue(Entry entry, String value){ diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderArray.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderArray.java index 520dea2..677b309 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderArray.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderArray.java @@ -46,8 +46,16 @@ class XMLValuesEncoderArray extends XMLValuesEncoderBag{ bagItem.setNameLow((short) (i+1)); String valueText=child.getTextContent(); - - if(force_string){ + if(ValueDecoder.isReference(valueText)){ + ValueType valueType; + if(valueText.charAt(0) == '?'){ + valueType = ValueType.ATTRIBUTE; + }else { + valueType = ValueType.REFERENCE; + } + bagItem.setTypeAndData(valueType, + getMaterials().resolveReference(valueText)); + }else if(force_string){ bagItem.setValueAsString(ValueDecoder .unEscapeSpecialCharacter(valueText)); }else if(force_integer){ @@ -59,9 +67,6 @@ class XMLValuesEncoderArray extends XMLValuesEncoderBag{ } bagItem.setTypeAndData(ValueType.INT_DEC, ValueDecoder.parseInteger(valueText)); - }else if(ValueDecoder.isReference(valueText)){ - bagItem.setTypeAndData(ValueType.REFERENCE, - getMaterials().resolveReference(valueText)); }else if(EncodeUtil.isEmpty(valueText)) { bagItem.setTypeAndData(ValueType.NULL, 0); }else { diff --git a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderAttr.java b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderAttr.java index 03acca4..b120064 100644 --- a/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderAttr.java +++ b/src/main/java/com/reandroid/apk/xmlencoder/XMLValuesEncoderAttr.java @@ -134,6 +134,20 @@ class XMLValuesEncoderAttr extends XMLValuesEncoderBag{ } return 0; } + private boolean isFlag(XMLElement parent){ + if(parent.getChildesCount()==0){ + return false; + } + String tagName=parent.getChildAt(0).getTagName(); + return "flag".equals(tagName); + } + private boolean isEnum(XMLElement parent){ + if(parent.getChildesCount()==0){ + return false; + } + String tagName=parent.getChildAt(0).getTagName(); + return "enum".equals(tagName); + } private int decodeUnknownAttributeHex(String name){ if(name.length()==0||name.charAt(0)!='@'){ return 0; diff --git a/src/main/java/com/reandroid/arsc/chunk/PackageBlock.java b/src/main/java/com/reandroid/arsc/chunk/PackageBlock.java index 1164817..8534ccd 100755 --- a/src/main/java/com/reandroid/arsc/chunk/PackageBlock.java +++ b/src/main/java/com/reandroid/arsc/chunk/PackageBlock.java @@ -331,6 +331,9 @@ public class PackageBlock extends Chunk public SpecTypePair getSpecTypePair(String typeName){ return getSpecTypePairArray().getSpecTypePair(typeName); } + public SpecTypePair getSpecTypePair(int typeId){ + return getSpecTypePairArray().getPair((byte) typeId); + } public EntryGroup getEntryGroup(String typeName, String entryName){ return getSpecTypePairArray().getEntryGroup(typeName, entryName); } diff --git a/src/main/java/com/reandroid/arsc/decoder/ColorUtil.java b/src/main/java/com/reandroid/arsc/decoder/ColorUtil.java new file mode 100644 index 0000000..41e1330 --- /dev/null +++ b/src/main/java/com/reandroid/arsc/decoder/ColorUtil.java @@ -0,0 +1,114 @@ +/* + * 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.arsc.value.ValueType; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ColorUtil { + + public static String decode(ValueType valueType, int data){ + if(valueType == null){ + return null; + } + int index; + switch (valueType){ + case INT_COLOR_RGB4: + index = 5; + break; + case INT_COLOR_ARGB4: + index = 4; + break; + case INT_COLOR_RGB8: + index = 2; + break; + case INT_COLOR_ARGB8: + index = 0; + break; + default: + return null; + } + String hex = String.format("%08x", data); + return "#" + hex.substring(index); + } + public static ValueDecoder.EncodeResult encode(String hexColor){ + if(hexColor == null){ + return null; + } + hexColor = hexColor.toUpperCase(); + Matcher matcher = PATTERN_COLOR.matcher(hexColor); + if(!matcher.matches()){ + return null; + } + char[] s = hexColor.toCharArray(); + int len = s.length; + ValueType valueType; + int color = 0; + + if (len == 4) { + valueType = ValueType.INT_COLOR_RGB4; + color |= 0xFF0000; + color |= get_hex(s[1]) << 20; + color |= get_hex(s[1]) << 16; + color |= get_hex(s[2]) << 12; + color |= get_hex(s[2]) << 8; + color |= get_hex(s[3]) << 4; + color |= get_hex(s[3]); + } else if (len == 5) { + valueType = ValueType.INT_COLOR_ARGB4; + color |= 0xFFFF0000; + color |= get_hex(s[1]) << 28; + color |= get_hex(s[1]) << 24; + color |= get_hex(s[2]) << 20; + color |= get_hex(s[2]) << 16; + color |= get_hex(s[3]) << 12; + color |= get_hex(s[3]) << 8; + color |= get_hex(s[4]) << 4; + color |= get_hex(s[4]); + } else if (len == 7) { + valueType = ValueType.INT_COLOR_RGB8; + color |= 0xFF000000; + color |= get_hex(s[1]) << 20; + color |= get_hex(s[2]) << 16; + color |= get_hex(s[3]) << 12; + color |= get_hex(s[4]) << 8; + color |= get_hex(s[5]) << 4; + color |= get_hex(s[6]); + } else if (len == 9) { + valueType = ValueType.INT_COLOR_ARGB8; + color |= get_hex(s[1]) << 28; + color |= get_hex(s[2]) << 24; + color |= get_hex(s[3]) << 20; + color |= get_hex(s[4]) << 16; + color |= get_hex(s[5]) << 12; + color |= get_hex(s[6]) << 8; + color |= get_hex(s[7]) << 4; + color |= get_hex(s[8]); + }else { + return null; + } + return new ValueDecoder.EncodeResult(valueType, color); + } + private static int get_hex(char ch){ + if(ch <= '9'){ + return ch - '0'; + } + return 10 + (ch - 'A'); + } + public static final Pattern PATTERN_COLOR = Pattern.compile("^#([0-9a-fA-F]{3,8})$"); +} diff --git a/src/main/java/com/reandroid/arsc/decoder/ValueDecoder.java b/src/main/java/com/reandroid/arsc/decoder/ValueDecoder.java index c100bb8..e28cc43 100755 --- a/src/main/java/com/reandroid/arsc/decoder/ValueDecoder.java +++ b/src/main/java/com/reandroid/arsc/decoder/ValueDecoder.java @@ -165,21 +165,7 @@ public class ValueDecoder { return false; } public static EncodeResult encodeColor(String value){ - if(value==null){ - return null; - } - Matcher matcher = PATTERN_COLOR.matcher(value); - if(!matcher.find()){ - 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)); + return ColorUtil.encode(value); } public static EncodeResult encodeHexOrInt(String numString){ if(numString==null){ @@ -468,14 +454,13 @@ public class ValueDecoder { if(valueType==null){ return null; } + String hexColor = ColorUtil.decode(valueType, data); + if(hexColor != null){ + return hexColor; + } switch (valueType){ case INT_BOOLEAN: return decodeBoolean(data); - case INT_COLOR_ARGB4: - case INT_COLOR_ARGB8: - case INT_COLOR_RGB4: - case INT_COLOR_RGB8: - return decodeColor(data); case DIMENSION: case FLOAT: case FRACTION: @@ -739,13 +724,6 @@ public class ValueDecoder { return "false"; } - private static String decodeColor(int rawVal){ - String hex=String.format("%x", rawVal); - if(hex.length()<=6){ - return String.format("#%06x", rawVal); - } - return String.format("#%08x", rawVal); - } private static String decodeDimensionOrFloat(ValueType valueType, int rawVal){ if(valueType==ValueType.FLOAT){ float f=Float.intBitsToFloat(rawVal); @@ -836,7 +814,6 @@ public class ValueDecoder { } } - 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-9]+(E\\+?-?[0-9]+)?)?)(px|di?p|sp|pt|in|mm|%p?)$"); private static final Pattern PATTERN_INTEGER = Pattern.compile("^(-?)([0-9]+)$"); private static final Pattern PATTERN_HEX = Pattern.compile("^0x[0-9a-fA-F]+$"); diff --git a/src/main/java/com/reandroid/arsc/util/ResNameMap.java b/src/main/java/com/reandroid/arsc/util/ResNameMap.java index 3a65d3c..e4db8c6 100644 --- a/src/main/java/com/reandroid/arsc/util/ResNameMap.java +++ b/src/main/java/com/reandroid/arsc/util/ResNameMap.java @@ -47,7 +47,7 @@ public class ResNameMap { valueMap=new HashMap<>(); mainMap.put(type, valueMap); } - valueMap.put(name, value); + valueMap.putIfAbsent(name, value); } } public void clear(){ diff --git a/src/main/java/com/reandroid/arsc/value/ValueItem.java b/src/main/java/com/reandroid/arsc/value/ValueItem.java index 5dfdf32..f8f4750 100755 --- a/src/main/java/com/reandroid/arsc/value/ValueItem.java +++ b/src/main/java/com/reandroid/arsc/value/ValueItem.java @@ -229,6 +229,9 @@ import java.util.Objects; && Objects.equals(str, getValueAsString())){ return; } + if(str == null){ + str = ""; + } StringItem stringItem = getStringPool().getOrCreate(str); setData(stringItem.getIndex()); setValueType(ValueType.STRING);