package jadx.core.xmlgen.entry; import jadx.core.xmlgen.ParserConstants; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ValuesParser extends ParserConstants { private static final Logger LOG = LoggerFactory.getLogger(ValuesParser.class); private final String[] strings; private final Map<Integer, String> resMap; public ValuesParser(String[] strings, Map<Integer, String> resMap) { this.strings = strings; this.resMap = resMap; } public String getValueString(ResourceEntry ri) { RawValue simpleValue = ri.getSimpleValue(); if (simpleValue != null) { return decodeValue(simpleValue); } List<RawNamedValue> namedValues = ri.getNamedValues(); List<String> strList = new ArrayList<String>(namedValues.size()); for (RawNamedValue value : namedValues) { String nameStr = decodeNameRef(value.getNameRef()); String valueStr = decodeValue(value.getRawValue()); if (nameStr == null) { strList.add(valueStr); } else { strList.add(nameStr + "=" + valueStr); } } return strList.toString(); } public String decodeValue(RawValue value) { int dataType = value.getDataType(); int data = value.getData(); return decodeValue(dataType, data); } public String decodeValue(int dataType, int data) { switch (dataType) { case TYPE_NULL: return null; case TYPE_STRING: return strings[data]; case TYPE_INT_DEC: return Integer.toString(data); case TYPE_INT_HEX: return Integer.toHexString(data); case TYPE_INT_BOOLEAN: return data == 0 ? "false" : "true"; case TYPE_FLOAT: return Float.toString(Float.intBitsToFloat(data)); case TYPE_INT_COLOR_ARGB8: return String.format("#%08x", data); case TYPE_INT_COLOR_RGB8: return String.format("#%06x", data & 0xFFFFFF); case TYPE_INT_COLOR_ARGB4: return String.format("#%04x", data & 0xFFFF); case TYPE_INT_COLOR_RGB4: return String.format("#%03x", data & 0xFFF); case TYPE_REFERENCE: { String ri = resMap.get(data); if (ri == null) { return "?unknown_ref: " + Integer.toHexString(data); } return "@" + ri; } case TYPE_ATTRIBUTE: { String ri = resMap.get(data); if (ri == null) { return "?unknown_attr_ref: " + Integer.toHexString(data); } return "?" + ri; } case TYPE_DIMENSION: return decodeComplex(data, false); case TYPE_FRACTION: return decodeComplex(data, true); default: LOG.warn("Unknown data type: 0x{} {}", Integer.toHexString(dataType), data); return " ?0x" + Integer.toHexString(dataType) + " " + data; } } private String decodeNameRef(int nameRef) { int ref = nameRef; if (isResInternalId(nameRef)) { ref = nameRef & ATTR_TYPE_ANY; if (ref == 0) { return null; } } String ri = resMap.get(ref); if (ri != null) { return ri.replace('/', '.'); } return "?0x" + Integer.toHexString(nameRef); } private String decodeComplex(int data, boolean isFraction) { double value = (data & COMPLEX_MANTISSA_MASK << COMPLEX_MANTISSA_SHIFT) * RADIX_MULTS[data >> COMPLEX_RADIX_SHIFT & COMPLEX_RADIX_MASK]; int unitType = data & COMPLEX_UNIT_MASK; String unit; if (isFraction) { value *= 100; switch (unitType) { case COMPLEX_UNIT_FRACTION: unit = "%"; break; case COMPLEX_UNIT_FRACTION_PARENT: unit = "%p"; break; default: unit = "?f" + Integer.toHexString(unitType); } } else { switch (unitType) { case COMPLEX_UNIT_PX: unit = "px"; break; case COMPLEX_UNIT_DIP: unit = "dp"; break; case COMPLEX_UNIT_SP: unit = "sp"; break; case COMPLEX_UNIT_PT: unit = "pt"; break; case COMPLEX_UNIT_IN: unit = "in"; break; case COMPLEX_UNIT_MM: unit = "mm"; break; default: unit = "?d" + Integer.toHexString(unitType); } } return doubleToString(value) + unit; } private static String doubleToString(double value) { if (value == Math.ceil(value)) { return Integer.toString((int) value); } else { // remove trailing zeroes NumberFormat f = NumberFormat.getInstance(); f.setMaximumFractionDigits(4); f.setMinimumIntegerDigits(1); return f.format(value); } } }