fix: decode null / empty values

This commit is contained in:
REAndroid 2023-05-01 19:55:22 +02:00
parent d5c3f7563e
commit fea0583f61

View File

@ -1,18 +1,18 @@
/* /*
* Copyright (C) 2022 github.com/REAndroid * Copyright (C) 2022 github.com/REAndroid
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.reandroid.arsc.decoder; package com.reandroid.arsc.decoder;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.PackageBlock;
@ -29,244 +29,244 @@ import java.util.Iterator;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class ValueDecoder { public class ValueDecoder {
public static String escapeSpecialCharacter(String text){ public static String escapeSpecialCharacter(String text){
if(text==null || text.length()==0){ if(text==null || text.length()==0){
return text; return text;
} }
if(isSpecialCharacter(text.charAt(0))){ if(isSpecialCharacter(text.charAt(0))){
return '\\' +text; return '\\' +text;
} }
return text; return text;
} }
public static String unEscapeSpecialCharacter(String text){ public static String unEscapeSpecialCharacter(String text){
if(text==null || text.length()<2){ if(text==null || text.length()<2){
return text; return text;
} }
if(text.charAt(0)!='\\' || !isSpecialCharacter(text.charAt(1))){ if(text.charAt(0)!='\\' || !isSpecialCharacter(text.charAt(1))){
return text; return text;
} }
return text.substring(1); return text.substring(1);
} }
private static boolean isSpecialCharacter(char ch){ private static boolean isSpecialCharacter(char ch){
switch (ch){ switch (ch){
case '@': case '@':
case '?': case '?':
case '#': case '#':
return true; return true;
default: default:
return false; return false;
} }
} }
public static EncodeResult encodeGuessAny(String txt){ public static EncodeResult encodeGuessAny(String txt){
if(txt==null){ if(txt==null){
return null; return null;
} }
if("@empty".equals(txt)){ if("@empty".equals(txt)){
return new EncodeResult(ValueType.NULL, 0); return new EncodeResult(ValueType.NULL, 1);
} }
if("@null".equals(txt)){ if("@null".equals(txt)){
return new EncodeResult(ValueType.REFERENCE, 0); return new EncodeResult(ValueType.REFERENCE, 0);
} }
if("?null".equals(txt)){ if("?null".equals(txt)){
return new EncodeResult(ValueType.ATTRIBUTE, 0); return new EncodeResult(ValueType.ATTRIBUTE, 0);
} }
EncodeResult result=encodeColor(txt); EncodeResult result=encodeColor(txt);
if(result!=null){ if(result!=null){
return result; return result;
} }
result=encodeDimensionOrFloat(txt); result=encodeDimensionOrFloat(txt);
if(result!=null){ if(result!=null){
return result; return result;
} }
result=encodeHexOrInt(txt); result=encodeHexOrInt(txt);
if(result!=null){ if(result!=null){
return result; return result;
} }
return encodeBoolean(txt); return encodeBoolean(txt);
} }
public static EncodeResult encodeBoolean(String txt){ public static EncodeResult encodeBoolean(String txt){
if(txt==null){ if(txt==null){
return null; return null;
} }
txt=txt.trim().toLowerCase(); txt=txt.trim().toLowerCase();
if(txt.equals("true")){ if(txt.equals("true")){
return new EncodeResult(ValueType.INT_BOOLEAN, 0xffffffff); return new EncodeResult(ValueType.INT_BOOLEAN, 0xffffffff);
} }
if(txt.equals("false")){ if(txt.equals("false")){
return new EncodeResult(ValueType.INT_BOOLEAN, 0); return new EncodeResult(ValueType.INT_BOOLEAN, 0);
} }
return null; return null;
} }
public static EncodeResult encodeHexReference(String txt){ public static EncodeResult encodeHexReference(String txt){
if(txt==null){ if(txt==null){
return null; return null;
} }
txt=txt.trim().toLowerCase(); txt=txt.trim().toLowerCase();
Matcher matcher = PATTERN_HEX_REFERENCE.matcher(txt); Matcher matcher = PATTERN_HEX_REFERENCE.matcher(txt);
if(!matcher.find()){ if(!matcher.find()){
return null; return null;
} }
String prefix = matcher.group(1); String prefix = matcher.group(1);
int value = parseHex(matcher.group(2)); int value = parseHex(matcher.group(2));
ValueType valueType; ValueType valueType;
if("?".equals(prefix)){ if("?".equals(prefix)){
valueType = ValueType.ATTRIBUTE; valueType = ValueType.ATTRIBUTE;
}else { }else {
valueType = ValueType.REFERENCE; valueType = ValueType.REFERENCE;
} }
return new EncodeResult(valueType, value); return new EncodeResult(valueType, value);
} }
public static boolean isInteger(String txt){ public static boolean isInteger(String txt){
if(txt==null){ if(txt==null){
return false; return false;
} }
return PATTERN_INTEGER.matcher(txt).matches(); return PATTERN_INTEGER.matcher(txt).matches();
} }
public static boolean isHex(String txt){ public static boolean isHex(String txt){
if(txt==null){ if(txt==null){
return false; return false;
} }
return PATTERN_HEX.matcher(txt).matches(); return PATTERN_HEX.matcher(txt).matches();
} }
public static boolean isReference(String txt){ public static boolean isReference(String txt){
if(txt==null){ if(txt==null){
return false; return false;
} }
if(isNullReference(txt)){ if(isNullReference(txt)){
return true; return true;
} }
if(isHexReference(txt)){ if(isHexReference(txt)){
return true; return true;
} }
return PATTERN_REFERENCE.matcher(txt).matches(); return PATTERN_REFERENCE.matcher(txt).matches();
} }
private static boolean isHexReference(String txt){ private static boolean isHexReference(String txt){
return PATTERN_HEX_REFERENCE.matcher(txt).matches(); return PATTERN_HEX_REFERENCE.matcher(txt).matches();
} }
private static boolean isNullReference(String txt){ private static boolean isNullReference(String txt){
if("@null".equals(txt)||"?null".equals(txt)){ if("@null".equals(txt)||"?null".equals(txt)){
return true; return true;
} }
if("@empty".equals(txt)){ if("@empty".equals(txt)){
return true; return true;
} }
return false; return false;
} }
public static EncodeResult encodeColor(String value){ public static EncodeResult encodeColor(String value){
if(value==null){ if(value==null){
return null; return null;
} }
Matcher matcher = PATTERN_COLOR.matcher(value); Matcher matcher = PATTERN_COLOR.matcher(value);
if(!matcher.find()){ if(!matcher.find()){
return null; return null;
} }
value=matcher.group(1); value=matcher.group(1);
ValueType valueType; ValueType valueType;
if(value.length()==6){ if(value.length()==6){
valueType=ValueType.INT_COLOR_RGB8; valueType=ValueType.INT_COLOR_RGB8;
}else { }else {
valueType=ValueType.INT_COLOR_ARGB8; valueType=ValueType.INT_COLOR_ARGB8;
} }
return new EncodeResult(valueType, parseHex(value)); return new EncodeResult(valueType, parseHex(value));
} }
public static EncodeResult encodeHexOrInt(String numString){ public static EncodeResult encodeHexOrInt(String numString){
if(numString==null){ if(numString==null){
return null; return null;
} }
if(isHex(numString)){ if(isHex(numString)){
return new EncodeResult(ValueType.INT_HEX, parseHex(numString)); return new EncodeResult(ValueType.INT_HEX, parseHex(numString));
} }
if(isInteger(numString)){ if(isInteger(numString)){
return new EncodeResult(ValueType.INT_DEC, parseInteger(numString)); return new EncodeResult(ValueType.INT_DEC, parseInteger(numString));
} }
return null; return null;
} }
public static int parseHex(String hexString){ public static int parseHex(String hexString){
boolean negative=false; boolean negative=false;
hexString=hexString.trim().toLowerCase(); hexString=hexString.trim().toLowerCase();
if(hexString.startsWith("-")){ if(hexString.startsWith("-")){
negative=true; negative=true;
hexString=hexString.substring(1); hexString=hexString.substring(1);
} }
if(!hexString.startsWith("0x")){ if(!hexString.startsWith("0x")){
hexString="0x"+hexString; hexString="0x"+hexString;
} }
long l=Long.decode(hexString); long l=Long.decode(hexString);
if(negative){ if(negative){
l=-l; l=-l;
} }
return (int) l; return (int) l;
} }
public static int parseInteger(String intString){ public static int parseInteger(String intString){
intString=intString.trim(); intString=intString.trim();
boolean negative=false; boolean negative=false;
if(intString.startsWith("-")){ if(intString.startsWith("-")){
negative=true; negative=true;
intString=intString.substring(1); intString=intString.substring(1);
} }
long l=Long.parseLong(intString); long l=Long.parseLong(intString);
if(negative){ if(negative){
l=-l; l=-l;
} }
return (int) l; return (int) l;
} }
public static EncodeResult encodeDimensionOrFloat(String value){ public static EncodeResult encodeDimensionOrFloat(String value){
if(value==null){ if(value==null){
return null; return null;
} }
EncodeResult result = encodeFloat(value); EncodeResult result = encodeFloat(value);
if(result == null){ if(result == null){
result = encodeDimensionOrFraction(value); result = encodeDimensionOrFraction(value);
} }
return result; return result;
} }
public static EncodeResult encodeFloat(String txt){ public static EncodeResult encodeFloat(String txt){
Float value = parseFloat(txt); Float value = parseFloat(txt);
if(value==null){ if(value==null){
return null; return null;
} }
return new EncodeResult(ValueType.FLOAT, return new EncodeResult(ValueType.FLOAT,
Float.floatToIntBits(value)); Float.floatToIntBits(value));
} }
public static Float parseFloat(String txt){ public static Float parseFloat(String txt){
if(txt==null || txt.indexOf('.')<0){ if(txt==null || txt.indexOf('.')<0){
return null; return null;
} }
try{ try{
return Float.parseFloat(txt); return Float.parseFloat(txt);
}catch (NumberFormatException ignored){ }catch (NumberFormatException ignored){
return null; return null;
} }
} }
public static EncodeResult encodeDimensionOrFraction(String value){ public static EncodeResult encodeDimensionOrFraction(String value){
if(value==null){ if(value==null){
return null; return null;
} }
Matcher matcher = PATTERN_DIMEN.matcher(value); Matcher matcher = PATTERN_DIMEN.matcher(value);
if(!matcher.find()){ if(!matcher.find()){
return null; return null;
} }
String number = matcher.group(1); String number = matcher.group(1);
String unit = matcher.group(4); String unit = matcher.group(4);
float fraction = Float.parseFloat(number); float fraction = Float.parseFloat(number);
return encodeDimensionOrFraction(fraction, unit); return encodeDimensionOrFraction(fraction, unit);
} }
private static EncodeResult encodeDimensionOrFraction(float value, String unitSymbol){ private static EncodeResult encodeDimensionOrFraction(float value, String unitSymbol){
ComplexUtil.Unit unit = ComplexUtil.Unit.fromSymbol(unitSymbol); ComplexUtil.Unit unit = ComplexUtil.Unit.fromSymbol(unitSymbol);
ValueType valueType; ValueType valueType;
if(unit == ComplexUtil.Unit.FRACTION || unit == ComplexUtil.Unit.FRACTION_PARENT){ if(unit == ComplexUtil.Unit.FRACTION || unit == ComplexUtil.Unit.FRACTION_PARENT){
valueType = ValueType.FRACTION; valueType = ValueType.FRACTION;
value = value / 100.0f; value = value / 100.0f;
}else { }else {
valueType = ValueType.DIMENSION; valueType = ValueType.DIMENSION;
} }
int result = ComplexUtil.encodeComplex(value, unit); int result = ComplexUtil.encodeComplex(value, unit);
return new EncodeResult(valueType, result); return new EncodeResult(valueType, result);
} }
public static String decodeAttributeName(EntryStore store, PackageBlock currentPackage, int resourceId){ public static String decodeAttributeName(EntryStore store, PackageBlock currentPackage, int resourceId){
EntryGroup entryGroup=searchEntryGroup(store, currentPackage, resourceId); EntryGroup entryGroup=searchEntryGroup(store, currentPackage, resourceId);
if(entryGroup==null){ if(entryGroup==null){
return String.format("@0x%08x", resourceId); return String.format("@0x%08x", resourceId);
@ -354,8 +354,8 @@ import java.util.regex.Pattern;
return decodeIntEntry(store, parentEntry, valueType, data); return decodeIntEntry(store, parentEntry, valueType, data);
} }
public static String decodeIntEntry(EntryStore store, Entry parentEntry, ValueType valueType, int data){ public static String decodeIntEntry(EntryStore store, Entry parentEntry, ValueType valueType, int data){
if(valueType==ValueType.NULL){ if(valueType == ValueType.NULL){
return "@empty"; return decodeNull(data);
} }
if(valueType==ValueType.STRING){ if(valueType==ValueType.STRING){
return decodeIntEntryString(parentEntry, data); return decodeIntEntryString(parentEntry, data);
@ -474,6 +474,8 @@ import java.util.regex.Pattern;
return decodeHex(data); return decodeHex(data);
case INT_DEC: case INT_DEC:
return decodeInt(data); return decodeInt(data);
case NULL:
return decodeNull(data);
} }
return null; return null;
} }
@ -713,6 +715,12 @@ import java.util.regex.Pattern;
private static String decodeInt(int rawVal){ private static String decodeInt(int rawVal){
return String.valueOf(rawVal); return String.valueOf(rawVal);
} }
private static String decodeNull(int data){
if(data == 1){
return "@empty";
}
return "@null";
}
private static String decodeBoolean(int data){ private static String decodeBoolean(int data){
if(data == 0xFFFFFFFF){ if(data == 0xFFFFFFFF){
@ -818,10 +826,10 @@ import java.util.regex.Pattern;
} }
} }
public static final Pattern PATTERN_COLOR = Pattern.compile("^#([0-9a-fA-F]{6,8})$"); 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?)$"); 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_INTEGER = Pattern.compile("^(-?)([0-9]+)$");
private static final Pattern PATTERN_HEX = Pattern.compile("^0x[0-9a-fA-F]+$"); private static final Pattern PATTERN_HEX = Pattern.compile("^0x[0-9a-fA-F]+$");
public static final Pattern PATTERN_REFERENCE = Pattern.compile("^([?@])(([^\\s:@?/]+:)?)([^\\s:@?/]+)/([^\\s:@?/]+)$"); public static final Pattern PATTERN_REFERENCE = Pattern.compile("^([?@])(([^\\s:@?/]+:)?)([^\\s:@?/]+)/([^\\s:@?/]+)$");
public static final Pattern PATTERN_HEX_REFERENCE = Pattern.compile("^([?@])(0x[0-9a-f]{7,8})$"); public static final Pattern PATTERN_HEX_REFERENCE = Pattern.compile("^([?@])(0x[0-9a-f]{7,8})$");
} }