better hex conversion

This commit is contained in:
REAndroid 2023-05-04 15:01:57 +02:00
parent 1b240b2281
commit f5ec1381a4
40 changed files with 1459 additions and 1320 deletions

View File

@ -16,6 +16,7 @@
package com.reandroid.apk; package com.reandroid.apk;
import com.reandroid.archive.InputSource; import com.reandroid.archive.InputSource;
import com.reandroid.arsc.util.HexUtil;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@ -148,7 +149,7 @@ public class PathSanitizer {
} }
private static String createUniqueName(String name){ private static String createUniqueName(String name){
int hash = name.hashCode(); int hash = name.hashCode();
return String.format("alias_%08x", hash).toLowerCase(); return "alias_" + HexUtil.toHexNoPrefix8(hash);
} }
private static boolean isGoodSimpleName(String name){ private static boolean isGoodSimpleName(String name){
if(name==null){ if(name==null){

View File

@ -1,4 +1,4 @@
/* /*
* 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");
@ -19,6 +19,7 @@ import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.pool.SpecStringPool; import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.util.ResNameMap; import com.reandroid.arsc.util.ResNameMap;
import com.reandroid.json.JSONArray; import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
@ -379,7 +380,7 @@ import java.util.*;
type.add(entry); type.add(entry);
} }
public String getHexId(){ public String getHexId(){
return String.format("0x%02x", id); return HexUtil.toHex2(id);
} }
public JSONObject toJson(){ public JSONObject toJson(){
JSONObject jsonObject=new JSONObject(); JSONObject jsonObject=new JSONObject();
@ -575,7 +576,7 @@ import java.util.*;
return entryMap.get(entryId); return entryMap.get(entryId);
} }
public String getHexId(){ public String getHexId(){
return String.format("0x%02x", id); return HexUtil.toHex2(id);
} }
public void add(Entry entry){ public void add(Entry entry){
if(entry==null){ if(entry==null){
@ -733,7 +734,7 @@ import java.util.*;
| (getEntryId() & 0xffff); | (getEntryId() & 0xffff);
} }
public String getHexId(){ public String getHexId(){
return String.format("0x%08x", getResourceId()); return HexUtil.toHex8(getResourceId());
} }
public void writeXml(String indent, Writer writer) throws IOException{ public void writeXml(String indent, Writer writer) throws IOException{

View File

@ -21,6 +21,7 @@ import com.reandroid.arsc.chunk.StagedAlias;
import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.chunk.TypeBlock;
import com.reandroid.arsc.container.SpecTypePair; import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
import java.io.File; import java.io.File;
@ -73,7 +74,7 @@ public class TableBlockJson {
private String getFileName(TypeBlock typeBlock){ private String getFileName(TypeBlock typeBlock){
StringBuilder builder=new StringBuilder(); StringBuilder builder=new StringBuilder();
builder.append(String.format("%03d-", typeBlock.getIndex())); builder.append(String.format("%03d-", typeBlock.getIndex()));
builder.append(String.format("0x%02x", typeBlock.getTypeId())); builder.append(HexUtil.toHex2(typeBlock.getTypeId()));
String name= typeBlock.getTypeName(); String name= typeBlock.getTypeName();
builder.append('-').append(name); builder.append('-').append(name);
builder.append(typeBlock.getResConfig().getQualifiers()); builder.append(typeBlock.getResConfig().getQualifiers());
@ -81,7 +82,7 @@ public class TableBlockJson {
} }
private String getDirName(PackageBlock packageBlock){ private String getDirName(PackageBlock packageBlock){
StringBuilder builder=new StringBuilder(); StringBuilder builder=new StringBuilder();
builder.append(String.format("0x%02x", packageBlock.getId())); builder.append(HexUtil.toHex2((byte) packageBlock.getId()));
builder.append("-"); builder.append("-");
builder.append(packageBlock.getIndex()); builder.append(packageBlock.getIndex());
String name= ApkUtil.sanitizeForFileName(packageBlock.getName()); String name= ApkUtil.sanitizeForFileName(packageBlock.getName());

View File

@ -16,6 +16,7 @@
package com.reandroid.apk.xmldecoder; package com.reandroid.apk.xmldecoder;
import com.reandroid.apk.XmlHelper; import com.reandroid.apk.XmlHelper;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResTableMapEntry; import com.reandroid.arsc.value.ResTableMapEntry;
import com.reandroid.arsc.value.ValueType; import com.reandroid.arsc.value.ValueType;
@ -58,7 +59,7 @@ class BagDecoderAttr<OUTPUT> extends BagDecoder<OUTPUT>{
int rawVal = item.getData(); int rawVal = item.getData();
String value; String value;
if(item.getBagItem().getValueType() == ValueType.INT_HEX){ if(item.getBagItem().getValueType() == ValueType.INT_HEX){
value = String.format("0x%08x", rawVal); value = HexUtil.toHex8(rawVal);
}else { }else {
value = String.valueOf(rawVal); value = String.valueOf(rawVal);
} }

View File

@ -1,359 +1,360 @@
/* /*
* 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.apk.xmlencoder; package com.reandroid.apk.xmlencoder;
import com.reandroid.apk.APKLogger; import com.reandroid.apk.APKLogger;
import com.reandroid.apk.FrameworkApk; import com.reandroid.apk.FrameworkApk;
import com.reandroid.apk.ResourceIds; import com.reandroid.apk.ResourceIds;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.chunk.TypeBlock;
import com.reandroid.arsc.container.SpecTypePair; import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.decoder.ValueDecoder; import com.reandroid.arsc.decoder.ValueDecoder;
import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.item.SpecString; import com.reandroid.arsc.item.SpecString;
import com.reandroid.arsc.util.FrameworkTable; import com.reandroid.arsc.util.FrameworkTable;
import com.reandroid.arsc.util.ResNameMap; import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.util.ResNameMap;
import com.reandroid.common.Frameworks; import com.reandroid.arsc.value.Entry;
import com.reandroid.common.Frameworks;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
public class EncodeMaterials { public class EncodeMaterials {
private final Set<ResourceIds.Table.Package> packageIdSet = new HashSet<>(); private final Set<ResourceIds.Table.Package> packageIdSet = new HashSet<>();
private PackageBlock currentPackage; private PackageBlock currentPackage;
private final Set<FrameworkTable> frameworkTables = new HashSet<>(); private final Set<FrameworkTable> frameworkTables = new HashSet<>();
private APKLogger apkLogger; private APKLogger apkLogger;
private boolean mForceCreateNamespaces = true; private boolean mForceCreateNamespaces = true;
private Set<String> mFrameworkPackageNames; private Set<String> mFrameworkPackageNames;
private final ResNameMap<Entry> mLocalResNameMap = new ResNameMap<>(); private final ResNameMap<Entry> mLocalResNameMap = new ResNameMap<>();
public EncodeMaterials(){ public EncodeMaterials(){
} }
public SpecString getSpecString(String name){ public SpecString getSpecString(String name){
return currentPackage.getSpecStringPool() return currentPackage.getSpecStringPool()
.get(name) .get(name)
.get(0); .get(0);
} }
public Entry getAttributeBlock(String refString){ public Entry getAttributeBlock(String refString){
String type = "attr"; String type = "attr";
Entry entry = getAttributeBlock(type, refString); Entry entry = getAttributeBlock(type, refString);
if(entry == null){ if(entry == null){
type = "^attr-private"; type = "^attr-private";
entry = getAttributeBlock(type, refString); entry = getAttributeBlock(type, refString);
} }
return entry; return entry;
} }
private Entry getAttributeBlock(String type, String refString){ private Entry getAttributeBlock(String type, String refString){
String packageName = null; String packageName = null;
String name = refString; String name = refString;
int i=refString.lastIndexOf(':'); int i=refString.lastIndexOf(':');
if(i>=0){ if(i>=0){
packageName=refString.substring(0, i); packageName=refString.substring(0, i);
name=refString.substring(i+1); name=refString.substring(i+1);
} }
if(EncodeUtil.isEmpty(packageName) if(EncodeUtil.isEmpty(packageName)
|| packageName.equals(getCurrentPackageName()) || packageName.equals(getCurrentPackageName())
|| !isFrameworkPackageName(packageName)){ || !isFrameworkPackageName(packageName)){
return getLocalEntry(type, name); return getLocalEntry(type, name);
} }
return getFrameworkEntry(type, name); return getFrameworkEntry(type, name);
} }
public int resolveReference(String refString){ public int resolveReference(String refString){
if("@null".equals(refString)){ if("@null".equals(refString)){
return 0; return 0;
} }
Matcher matcher = ValueDecoder.PATTERN_REFERENCE.matcher(refString); Matcher matcher = ValueDecoder.PATTERN_REFERENCE.matcher(refString);
if(!matcher.find()){ if(!matcher.find()){
ValueDecoder.EncodeResult ref = ValueDecoder.encodeHexReference(refString); ValueDecoder.EncodeResult ref = ValueDecoder.encodeHexReference(refString);
if(ref!=null){ if(ref!=null){
return ref.value; return ref.value;
} }
ref = ValueDecoder.encodeNullReference(refString); ref = ValueDecoder.encodeNullReference(refString);
if(ref!=null){ if(ref!=null){
return ref.value; return ref.value;
} }
throw new EncodeException( throw new EncodeException(
"Not proper reference string: '"+refString+"'"); "Not proper reference string: '"+refString+"'");
} }
String prefix=matcher.group(1); String prefix=matcher.group(1);
String packageName = matcher.group(2); String packageName = matcher.group(2);
if(packageName!=null && packageName.endsWith(":")){ if(packageName!=null && packageName.endsWith(":")){
packageName=packageName.substring(0, packageName.length()-1); packageName=packageName.substring(0, packageName.length()-1);
} }
String type = matcher.group(4); String type = matcher.group(4);
String name = matcher.group(5); String name = matcher.group(5);
if(EncodeUtil.isEmpty(packageName) if(EncodeUtil.isEmpty(packageName)
|| packageName.equals(getCurrentPackageName()) || packageName.equals(getCurrentPackageName())
|| !isFrameworkPackageName(packageName)){ || !isFrameworkPackageName(packageName)){
return resolveLocalResourceId(type, name); return resolveLocalResourceId(type, name);
} }
return resolveFrameworkResourceId(packageName, type, name); return resolveFrameworkResourceId(packageName, type, name);
} }
public int resolveLocalResourceId(String type, String name){ public int resolveLocalResourceId(String type, String name){
for(ResourceIds.Table.Package pkg:packageIdSet){ for(ResourceIds.Table.Package pkg:packageIdSet){
Integer resId = pkg.getResourceId(type, name); Integer resId = pkg.getResourceId(type, name);
if(resId!=null){ if(resId!=null){
return resId; return resId;
} }
} }
EntryGroup entryGroup=getLocalEntryGroup(type, name); EntryGroup entryGroup=getLocalEntryGroup(type, name);
if(entryGroup!=null){ if(entryGroup!=null){
return entryGroup.getResourceId(); return entryGroup.getResourceId();
} }
throw new EncodeException("Local entry not found: " + throw new EncodeException("Local entry not found: " +
"type="+type+ "type="+type+
", name="+name); ", name="+name);
} }
public int resolveFrameworkResourceId(String packageName, String type, String name){ public int resolveFrameworkResourceId(String packageName, String type, String name){
Entry entry = getFrameworkEntry(packageName, type, name); Entry entry = getFrameworkEntry(packageName, type, name);
if(entry !=null){ if(entry !=null){
return entry.getResourceId(); return entry.getResourceId();
} }
throw new EncodeException("Framework entry not found: " + throw new EncodeException("Framework entry not found: " +
"package="+packageName+ "package="+packageName+
", type="+type+ ", type="+type+
", name="+name); ", name="+name);
} }
public int resolveFrameworkResourceId(int packageId, String type, String name){ public int resolveFrameworkResourceId(int packageId, String type, String name){
Entry entry = getFrameworkEntry(packageId, type, name); Entry entry = getFrameworkEntry(packageId, type, name);
if(entry !=null){ if(entry !=null){
return entry.getResourceId(); return entry.getResourceId();
} }
throw new EncodeException("Framework entry not found: " + throw new EncodeException("Framework entry not found: " +
"packageId="+String.format("0x%02x", packageId)+ "packageId=" + HexUtil.toHex2((byte) packageId)+
", type="+type+ ", type="+type+
", name="+name); ", name="+name);
} }
public EntryGroup getLocalEntryGroup(String type, String name){ public EntryGroup getLocalEntryGroup(String type, String name){
for(EntryGroup entryGroup : currentPackage.listEntryGroup()){ for(EntryGroup entryGroup : currentPackage.listEntryGroup()){
if(type.equals(entryGroup.getTypeName()) && if(type.equals(entryGroup.getTypeName()) &&
name.equals(entryGroup.getSpecName())){ name.equals(entryGroup.getSpecName())){
return entryGroup; return entryGroup;
} }
} }
for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){ for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){
for(EntryGroup entryGroup : packageBlock.listEntryGroup()){ for(EntryGroup entryGroup : packageBlock.listEntryGroup()){
if(type.equals(entryGroup.getTypeName()) && if(type.equals(entryGroup.getTypeName()) &&
name.equals(entryGroup.getSpecName())){ name.equals(entryGroup.getSpecName())){
return entryGroup; return entryGroup;
} }
} }
} }
return null; return null;
} }
public Entry getLocalEntry(String type, String name){ public Entry getLocalEntry(String type, String name){
Entry entry =mLocalResNameMap.get(type, name); Entry entry =mLocalResNameMap.get(type, name);
if(entry !=null){ if(entry !=null){
return entry; return entry;
} }
loadLocalEntryMap(type); loadLocalEntryMap(type);
entry =mLocalResNameMap.get(type, name); entry =mLocalResNameMap.get(type, name);
if(entry !=null){ if(entry !=null){
return entry; return entry;
} }
entry = searchLocalEntry(type, name); entry = searchLocalEntry(type, name);
if(entry !=null){ if(entry !=null){
mLocalResNameMap.add(type, name, entry); mLocalResNameMap.add(type, name, entry);
} }
return entry; return entry;
} }
private Entry searchLocalEntry(String type, String name){ private Entry searchLocalEntry(String type, String name){
for(EntryGroup entryGroup : currentPackage.listEntryGroup()){ for(EntryGroup entryGroup : currentPackage.listEntryGroup()){
if(type.equals(entryGroup.getTypeName()) && if(type.equals(entryGroup.getTypeName()) &&
name.equals(entryGroup.getSpecName())){ name.equals(entryGroup.getSpecName())){
return entryGroup.pickOne(); return entryGroup.pickOne();
} }
} }
SpecTypePair specTypePair=currentPackage.searchByTypeName(type); SpecTypePair specTypePair=currentPackage.searchByTypeName(type);
if(specTypePair!=null){ if(specTypePair!=null){
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){ for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
for(Entry entry :typeBlock.listEntries(true)){ for(Entry entry :typeBlock.listEntries(true)){
if(name.equals(entry.getName())){ if(name.equals(entry.getName())){
return entry; return entry;
} }
} }
break; break;
} }
} }
for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){ for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){
if(packageBlock==currentPackage){ if(packageBlock==currentPackage){
continue; continue;
} }
specTypePair=packageBlock.searchByTypeName(type); specTypePair=packageBlock.searchByTypeName(type);
if(specTypePair!=null){ if(specTypePair!=null){
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){ for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
for(Entry entry :typeBlock.listEntries(true)){ for(Entry entry :typeBlock.listEntries(true)){
if(name.equals(entry.getName())){ if(name.equals(entry.getName())){
return entry; return entry;
} }
} }
break; break;
} }
} }
} }
return null; return null;
} }
private void loadLocalEntryMap(String type){ private void loadLocalEntryMap(String type){
ResNameMap<Entry> localMap = mLocalResNameMap; ResNameMap<Entry> localMap = mLocalResNameMap;
for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){ for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){
SpecTypePair specTypePair=packageBlock.searchByTypeName(type); SpecTypePair specTypePair=packageBlock.searchByTypeName(type);
if(specTypePair!=null){ if(specTypePair!=null){
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){ for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
for(Entry entry :typeBlock.listEntries(true)){ for(Entry entry :typeBlock.listEntries(true)){
localMap.add(entry.getTypeName(), localMap.add(entry.getTypeName(),
entry.getName(), entry); entry.getName(), entry);
} }
} }
} }
} }
} }
public Entry getFrameworkEntry(String type, String name){ public Entry getFrameworkEntry(String type, String name){
for(FrameworkTable table:frameworkTables){ for(FrameworkTable table:frameworkTables){
Entry entry = table.searchEntry(type, name); Entry entry = table.searchEntry(type, name);
if(entry !=null){ if(entry !=null){
return entry; return entry;
} }
} }
return null; return null;
} }
private boolean isFrameworkPackageName(String packageName){ private boolean isFrameworkPackageName(String packageName){
return getFrameworkPackageNames().contains(packageName); return getFrameworkPackageNames().contains(packageName);
} }
private Set<String> getFrameworkPackageNames(){ private Set<String> getFrameworkPackageNames(){
if(mFrameworkPackageNames!=null){ if(mFrameworkPackageNames!=null){
return mFrameworkPackageNames; return mFrameworkPackageNames;
} }
Set<String> results=new HashSet<>(); Set<String> results=new HashSet<>();
for(FrameworkTable table:frameworkTables){ for(FrameworkTable table:frameworkTables){
for(PackageBlock packageBlock:table.listPackages()){ for(PackageBlock packageBlock:table.listPackages()){
results.add(packageBlock.getName()); results.add(packageBlock.getName());
} }
} }
mFrameworkPackageNames=results; mFrameworkPackageNames=results;
return results; return results;
} }
public Entry getFrameworkEntry(String packageName, String type, String name){ public Entry getFrameworkEntry(String packageName, String type, String name){
for(FrameworkTable table:frameworkTables){ for(FrameworkTable table:frameworkTables){
for(PackageBlock packageBlock:table.listPackages()){ for(PackageBlock packageBlock:table.listPackages()){
if(packageName.equals(packageBlock.getName())){ if(packageName.equals(packageBlock.getName())){
Entry entry = table.searchEntry(type, name); Entry entry = table.searchEntry(type, name);
if(entry !=null){ if(entry !=null){
return entry; return entry;
} }
} }
} }
} }
return null; return null;
} }
public Entry getFrameworkEntry(int packageId, String type, String name){ public Entry getFrameworkEntry(int packageId, String type, String name){
for(FrameworkTable table:frameworkTables){ for(FrameworkTable table:frameworkTables){
for(PackageBlock packageBlock:table.listPackages()){ for(PackageBlock packageBlock:table.listPackages()){
if(packageId==packageBlock.getId()){ if(packageId==packageBlock.getId()){
Entry entry = table.searchEntry(type, name); Entry entry = table.searchEntry(type, name);
if(entry !=null){ if(entry !=null){
return entry; return entry;
} }
} }
} }
} }
return null; return null;
} }
public EncodeMaterials setForceCreateNamespaces(boolean force) { public EncodeMaterials setForceCreateNamespaces(boolean force) {
this.mForceCreateNamespaces = force; this.mForceCreateNamespaces = force;
return this; return this;
} }
public EncodeMaterials addPackageIds(ResourceIds.Table.Package packageIds) { public EncodeMaterials addPackageIds(ResourceIds.Table.Package packageIds) {
packageIds.loadEntryMap(); packageIds.loadEntryMap();
this.packageIdSet.add(packageIds); this.packageIdSet.add(packageIds);
return this; return this;
} }
public EncodeMaterials setCurrentPackage(PackageBlock currentPackage) { public EncodeMaterials setCurrentPackage(PackageBlock currentPackage) {
this.currentPackage = currentPackage; this.currentPackage = currentPackage;
return this; return this;
} }
public EncodeMaterials addFramework(FrameworkApk frameworkApk) { public EncodeMaterials addFramework(FrameworkApk frameworkApk) {
if(frameworkApk!=null){ if(frameworkApk!=null){
addFramework(frameworkApk.getTableBlock()); addFramework(frameworkApk.getTableBlock());
} }
return this; return this;
} }
public EncodeMaterials addFramework(FrameworkTable frameworkTable) { public EncodeMaterials addFramework(FrameworkTable frameworkTable) {
frameworkTable.loadResourceNameMap(); frameworkTable.loadResourceNameMap();
this.frameworkTables.add(frameworkTable); this.frameworkTables.add(frameworkTable);
this.mFrameworkPackageNames=null; this.mFrameworkPackageNames=null;
return this; return this;
} }
public EncodeMaterials setAPKLogger(APKLogger logger) { public EncodeMaterials setAPKLogger(APKLogger logger) {
this.apkLogger = logger; this.apkLogger = logger;
return this; return this;
} }
public PackageBlock getCurrentPackage() { public PackageBlock getCurrentPackage() {
return currentPackage; return currentPackage;
} }
public boolean isForceCreateNamespaces() { public boolean isForceCreateNamespaces() {
return mForceCreateNamespaces; return mForceCreateNamespaces;
} }
public String getCurrentPackageName(){ public String getCurrentPackageName(){
return currentPackage.getName(); return currentPackage.getName();
} }
public int getCurrentPackageId(){ public int getCurrentPackageId(){
return currentPackage.getId(); return currentPackage.getId();
} }
public void logMessage(String msg) { public void logMessage(String msg) {
if(apkLogger!=null){ if(apkLogger!=null){
apkLogger.logMessage(msg); apkLogger.logMessage(msg);
} }
} }
public void logError(String msg, Throwable tr) { public void logError(String msg, Throwable tr) {
if(apkLogger!=null){ if(apkLogger!=null){
apkLogger.logError(msg, tr); apkLogger.logError(msg, tr);
} }
} }
public void logVerbose(String msg) { public void logVerbose(String msg) {
if(apkLogger!=null){ if(apkLogger!=null){
apkLogger.logVerbose(msg); apkLogger.logVerbose(msg);
} }
} }
public static EncodeMaterials create(TableBlock tableBlock){ public static EncodeMaterials create(TableBlock tableBlock){
PackageBlock packageBlock = tableBlock.pickOne(); PackageBlock packageBlock = tableBlock.pickOne();
if(packageBlock==null){ if(packageBlock==null){
throw new EncodeException("No packages found on table block"); throw new EncodeException("No packages found on table block");
} }
return create(packageBlock); return create(packageBlock);
} }
public static EncodeMaterials create(PackageBlock packageBlock){ public static EncodeMaterials create(PackageBlock packageBlock){
ResourceIds resourceIds = new ResourceIds(); ResourceIds resourceIds = new ResourceIds();
resourceIds.loadPackageBlock(packageBlock); resourceIds.loadPackageBlock(packageBlock);
ResourceIds.Table.Package packageId = resourceIds.getTable().listPackages().get(0); ResourceIds.Table.Package packageId = resourceIds.getTable().listPackages().get(0);
EncodeMaterials encodeMaterials = new EncodeMaterials() EncodeMaterials encodeMaterials = new EncodeMaterials()
.addPackageIds(packageId) .addPackageIds(packageId)
.setCurrentPackage(packageBlock); .setCurrentPackage(packageBlock);
TableBlock tableBlock = packageBlock.getTableBlock(); TableBlock tableBlock = packageBlock.getTableBlock();
for(TableBlock frameworkTable:tableBlock.getFrameWorks()){ for(TableBlock frameworkTable:tableBlock.getFrameWorks()){
if(frameworkTable instanceof FrameworkTable){ if(frameworkTable instanceof FrameworkTable){
encodeMaterials.addFramework((FrameworkTable) frameworkTable); encodeMaterials.addFramework((FrameworkTable) frameworkTable);
} }
} }
return encodeMaterials; return encodeMaterials;
} }
} }

View File

@ -17,6 +17,7 @@ package com.reandroid.archive2;
import com.reandroid.archive2.block.CentralEntryHeader; import com.reandroid.archive2.block.CentralEntryHeader;
import com.reandroid.archive2.block.LocalFileHeader; import com.reandroid.archive2.block.LocalFileHeader;
import com.reandroid.arsc.util.HexUtil;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
@ -116,6 +117,7 @@ public class ArchiveEntry extends ZipEntry {
@Override @Override
public String toString(){ public String toString(){
return "["+ getFileOffset()+"] "+getName()+getComment()+String.format(" 0x%08x", getCrc()); return "["+ getFileOffset()+"] " + getName() + getComment()
+ HexUtil.toHex(" 0x", getCrc(), 8);
} }
} }

View File

@ -16,6 +16,7 @@
package com.reandroid.archive2.block; package com.reandroid.archive2.block;
import com.reandroid.archive2.ZipSignature; import com.reandroid.archive2.ZipSignature;
import com.reandroid.arsc.util.HexUtil;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -134,12 +135,12 @@ public class CentralEntryHeader extends CommonHeader {
builder.append(", "); builder.append(", ");
} }
builder.append("SIG=").append(getSignature()); builder.append("SIG=").append(getSignature());
builder.append(", versionMadeBy=").append(String.format("0x%04x", getVersionMadeBy())); builder.append(", versionMadeBy=").append(HexUtil.toHex4((short) getVersionMadeBy()));
builder.append(", versionExtract=").append(String.format("0x%04x", getVersionExtract())); builder.append(", versionExtract=").append(HexUtil.toHex4((short) getVersionExtract()));
builder.append(", GP={").append(getGeneralPurposeFlag()).append("}"); builder.append(", GP={").append(getGeneralPurposeFlag()).append("}");
builder.append(", method=").append(getMethod()); builder.append(", method=").append(getMethod());
builder.append(", date=").append(getDate()); builder.append(", date=").append(getDate());
builder.append(", crc=").append(String.format("0x%08x", getCrc())); builder.append(", crc=").append(HexUtil.toHex8(getCrc()));
builder.append(", cSize=").append(getCompressedSize()); builder.append(", cSize=").append(getCompressedSize());
builder.append(", size=").append(getSize()); builder.append(", size=").append(getSize());
builder.append(", fileNameLength=").append(getFileNameLength()); builder.append(", fileNameLength=").append(getFileNameLength());

View File

@ -16,6 +16,7 @@
package com.reandroid.archive2.block; package com.reandroid.archive2.block;
import com.reandroid.archive2.ZipSignature; import com.reandroid.archive2.ZipSignature;
import com.reandroid.arsc.util.HexUtil;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -287,12 +288,12 @@ public abstract class CommonHeader extends ZipHeader {
builder.append(", "); builder.append(", ");
} }
builder.append("SIG=").append(getSignature()); builder.append("SIG=").append(getSignature());
builder.append(", versionMadeBy=").append(String.format("0x%04x", getVersionMadeBy())); builder.append(", versionMadeBy=").append(HexUtil.toHex4((short) getVersionMadeBy()));
builder.append(", platform=").append(String.format("0x%02x", getPlatform())); builder.append(", platform=").append(HexUtil.toHex2((byte) getPlatform()));
builder.append(", GP={").append(getGeneralPurposeFlag()).append("}"); builder.append(", GP={").append(getGeneralPurposeFlag()).append("}");
builder.append(", method=").append(getMethod()); builder.append(", method=").append(getMethod());
builder.append(", date=").append(getDate()); builder.append(", date=").append(getDate());
builder.append(", crc=").append(String.format("0x%08x", getCrc())); builder.append(", crc=").append(HexUtil.toHex8(getCrc()));
builder.append(", cSize=").append(getCompressedSize()); builder.append(", cSize=").append(getCompressedSize());
builder.append(", size=").append(getSize()); builder.append(", size=").append(getSize());
builder.append(", fileNameLength=").append(getFileNameLength()); builder.append(", fileNameLength=").append(getFileNameLength());

View File

@ -16,6 +16,7 @@
package com.reandroid.archive2.block; package com.reandroid.archive2.block;
import com.reandroid.archive2.ZipSignature; import com.reandroid.archive2.ZipSignature;
import com.reandroid.arsc.util.HexUtil;
public class DataDescriptor extends ZipHeader{ public class DataDescriptor extends ZipHeader{
public DataDescriptor() { public DataDescriptor() {
@ -44,7 +45,7 @@ public class DataDescriptor extends ZipHeader{
public String toString(){ public String toString(){
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append(getSignature()); builder.append(getSignature());
builder.append(", crc=").append(String.format("0x%08x", getCrc())); builder.append(", crc=").append(HexUtil.toHex8(getCrc()));
builder.append(", compressed=").append(getCompressedSize()); builder.append(", compressed=").append(getCompressedSize());
builder.append(", size=").append(getSize()); builder.append(", size=").append(getSize());
return builder.toString(); return builder.toString();

View File

@ -16,6 +16,7 @@
package com.reandroid.archive2.block; package com.reandroid.archive2.block;
import com.reandroid.archive2.ZipSignature; import com.reandroid.archive2.ZipSignature;
import com.reandroid.arsc.util.HexUtil;
public class EndRecord extends ZipHeader{ public class EndRecord extends ZipHeader{
public EndRecord() { public EndRecord() {
@ -78,7 +79,7 @@ public class EndRecord extends ZipHeader{
builder.append(", total dirs=").append(getTotalNumberOfDirectories()); builder.append(", total dirs=").append(getTotalNumberOfDirectories());
builder.append(", length=").append(getLengthOfCentralDirectory()); builder.append(", length=").append(getLengthOfCentralDirectory());
builder.append(", offset=").append(getOffsetOfCentralDirectory()); builder.append(", offset=").append(getOffsetOfCentralDirectory());
builder.append(", last=").append(String.format("0x%08x", getLastShort())); builder.append(", last=").append(HexUtil.toHex8(getLastShort()));
return builder.toString(); return builder.toString();
} }

View File

@ -16,6 +16,7 @@
package com.reandroid.archive2.block; package com.reandroid.archive2.block;
import com.reandroid.arsc.decoder.ValueDecoder; import com.reandroid.arsc.decoder.ValueDecoder;
import com.reandroid.arsc.util.HexUtil;
import java.util.Objects; import java.util.Objects;
@ -39,7 +40,7 @@ public class SignatureId implements Comparable<SignatureId>{
if (this.name != null) { if (this.name != null) {
return name + FILE_EXT_RAW; return name + FILE_EXT_RAW;
} }
return String.format("0x%08x", id) + FILE_EXT_RAW; return HexUtil.toHex8(id) + FILE_EXT_RAW;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
@ -66,7 +67,7 @@ public class SignatureId implements Comparable<SignatureId>{
if (name != null) { if (name != null) {
return name; return name;
} }
return "UNKNOWN(" + String.format("0x%08x", id) + ")"; return "UNKNOWN(" + HexUtil.toHex8(id) + ")";
} }
public static SignatureId valueOf(String name) { public static SignatureId valueOf(String name) {
if (name == null) { if (name == null) {

View File

@ -15,6 +15,8 @@
*/ */
package com.reandroid.archive2.io; package com.reandroid.archive2.io;
import com.reandroid.arsc.util.HexUtil;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.zip.CRC32; import java.util.zip.CRC32;
@ -124,6 +126,6 @@ public class CountingInputStream<T extends InputStream> extends InputStream {
if(!mFinished || crc==null){ if(!mFinished || crc==null){
return "[" + size + "]: " + inputStream.getClass().getSimpleName(); return "[" + size + "]: " + inputStream.getClass().getSimpleName();
} }
return "[size=" + size +", crc=" + String.format("0x%08x", mCheckSum) + "]: " + inputStream.getClass().getSimpleName(); return "[size=" + size +", crc=" + HexUtil.toHex8(mCheckSum) + "]: " + inputStream.getClass().getSimpleName();
} }
} }

View File

@ -1,20 +1,22 @@
/* /*
* 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.chunk; package com.reandroid.arsc.chunk;
import com.reandroid.arsc.util.HexUtil;
public enum ChunkType { public enum ChunkType {
NULL((short)0x0000), NULL((short)0x0000),
@ -45,7 +47,7 @@ public enum ChunkType {
@Override @Override
public String toString(){ public String toString(){
return name()+String.format("(0x%04x)", ((int) ID)); return name() + "(" + HexUtil.toHex4(ID) + ")";
} }
public static ChunkType get(short id){ public static ChunkType get(short id){

View File

@ -29,6 +29,7 @@ import com.reandroid.arsc.list.StagedAliasList;
import com.reandroid.arsc.pool.SpecStringPool; import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.pool.TypeStringPool; import com.reandroid.arsc.pool.TypeStringPool;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.LibraryInfo; import com.reandroid.arsc.value.LibraryInfo;
import com.reandroid.arsc.value.ResConfig; import com.reandroid.arsc.value.ResConfig;
@ -466,7 +467,7 @@ public class PackageBlock extends Chunk<PackageHeader>
StringBuilder builder=new StringBuilder(); StringBuilder builder=new StringBuilder();
builder.append(super.toString()); builder.append(super.toString());
builder.append(", id="); builder.append(", id=");
builder.append(String.format("0x%02x", getId())); builder.append(HexUtil.toHex2((byte) getId()));
builder.append(", name="); builder.append(", name=");
builder.append(getName()); builder.append(getName());
int libCount=getLibraryBlock().getLibraryCount(); int libCount=getLibraryBlock().getLibraryCount();

View File

@ -1,162 +1,163 @@
/* /*
* 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.chunk; package com.reandroid.arsc.chunk;
import com.reandroid.arsc.array.TypeBlockArray; import com.reandroid.arsc.array.TypeBlockArray;
import com.reandroid.arsc.container.SpecTypePair; import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.header.SpecHeader; import com.reandroid.arsc.header.SpecHeader;
import com.reandroid.arsc.item.*; import com.reandroid.arsc.item.*;
import com.reandroid.json.JSONConvert; import com.reandroid.arsc.util.HexUtil;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;
import java.util.List; import java.util.List;
public class SpecBlock extends Chunk<SpecHeader> implements JSONConvert<JSONObject> { public class SpecBlock extends Chunk<SpecHeader> implements JSONConvert<JSONObject> {
private final SpecFlagsArray specFlagsArray; private final SpecFlagsArray specFlagsArray;
public SpecBlock() { public SpecBlock() {
super(new SpecHeader(), 1); super(new SpecHeader(), 1);
SpecHeader header = getHeaderBlock(); SpecHeader header = getHeaderBlock();
this.specFlagsArray = new SpecFlagsArray(header.getEntryCount()); this.specFlagsArray = new SpecFlagsArray(header.getEntryCount());
addChild(specFlagsArray); addChild(specFlagsArray);
} }
public void destroy(){ public void destroy(){
setParent(null); setParent(null);
getSpecFlagsArray().clear(); getSpecFlagsArray().clear();
} }
public SpecFlag getSpecFlag(int id){ public SpecFlag getSpecFlag(int id){
return getSpecFlagsArray().getFlag(id); return getSpecFlagsArray().getFlag(id);
} }
public SpecFlagsArray getSpecFlagsArray(){ public SpecFlagsArray getSpecFlagsArray(){
return specFlagsArray; return specFlagsArray;
} }
public List<Integer> listSpecFlags(){ public List<Integer> listSpecFlags(){
return specFlagsArray.toList(); return specFlagsArray.toList();
} }
public byte getTypeId(){ public byte getTypeId(){
return getHeaderBlock().getId().get(); return getHeaderBlock().getId().get();
} }
public int getId(){ public int getId(){
return getHeaderBlock().getId().unsignedInt(); return getHeaderBlock().getId().unsignedInt();
} }
public void setId(int id){ public void setId(int id){
setTypeId((byte) (0xff & id)); setTypeId((byte) (0xff & id));
} }
public void setTypeId(byte id){ public void setTypeId(byte id){
getHeaderBlock().getId().set(id); getHeaderBlock().getId().set(id);
getTypeBlockArray().setTypeId(id); getTypeBlockArray().setTypeId(id);
} }
public TypeBlockArray getTypeBlockArray(){ public TypeBlockArray getTypeBlockArray(){
SpecTypePair specTypePair=getSpecTypePair(); SpecTypePair specTypePair=getSpecTypePair();
if(specTypePair!=null){ if(specTypePair!=null){
return specTypePair.getTypeBlockArray(); return specTypePair.getTypeBlockArray();
} }
return null; return null;
} }
SpecTypePair getSpecTypePair(){ SpecTypePair getSpecTypePair(){
return getParent(SpecTypePair.class); return getParent(SpecTypePair.class);
} }
public int getEntryCount() { public int getEntryCount() {
return specFlagsArray.size(); return specFlagsArray.size();
} }
public void setEntryCount(int count){ public void setEntryCount(int count){
specFlagsArray.setSize(count); specFlagsArray.setSize(count);
specFlagsArray.refresh(); specFlagsArray.refresh();
} }
@Override @Override
protected void onChunkRefreshed() { protected void onChunkRefreshed() {
specFlagsArray.refresh(); specFlagsArray.refresh();
} }
public void merge(SpecBlock specBlock){ public void merge(SpecBlock specBlock){
if(specBlock == null || specBlock==this){ if(specBlock == null || specBlock==this){
return; return;
} }
this.getSpecFlagsArray().merge(specBlock.getSpecFlagsArray()); this.getSpecFlagsArray().merge(specBlock.getSpecFlagsArray());
} }
@Override @Override
public String toString(){ public String toString(){
StringBuilder builder=new StringBuilder(); StringBuilder builder=new StringBuilder();
builder.append(super.toString()); builder.append(super.toString());
TypeBlockArray typeBlockArray=getTypeBlockArray(); TypeBlockArray typeBlockArray=getTypeBlockArray();
if(typeBlockArray!=null){ if(typeBlockArray!=null){
builder.append(", typesCount="); builder.append(", typesCount=");
builder.append(typeBlockArray.childesCount()); builder.append(typeBlockArray.childesCount());
} }
return builder.toString(); return builder.toString();
} }
@Override @Override
public JSONObject toJson() { public JSONObject toJson() {
JSONObject jsonObject=new JSONObject(); JSONObject jsonObject=new JSONObject();
jsonObject.put(TypeBlock.NAME_id, getId()); jsonObject.put(TypeBlock.NAME_id, getId());
jsonObject.put(NAME_spec_flags, getSpecFlagsArray().toJson()); jsonObject.put(NAME_spec_flags, getSpecFlagsArray().toJson());
return jsonObject; return jsonObject;
} }
@Override @Override
public void fromJson(JSONObject json) { public void fromJson(JSONObject json) {
setId(json.getInt(TypeBlock.NAME_id)); setId(json.getInt(TypeBlock.NAME_id));
getSpecFlagsArray().fromJson(json.optJSONArray(NAME_spec_flags)); getSpecFlagsArray().fromJson(json.optJSONArray(NAME_spec_flags));
} }
public enum Flag{ public enum Flag{
SPEC_PUBLIC((byte) 0x40), SPEC_PUBLIC((byte) 0x40),
SPEC_STAGED_API((byte) 0x20); SPEC_STAGED_API((byte) 0x20);
private final byte flag; private final byte flag;
Flag(byte flag) { Flag(byte flag) {
this.flag = flag; this.flag = flag;
} }
public byte getFlag() { public byte getFlag() {
return flag; return flag;
} }
public static boolean isPublic(byte flag){ public static boolean isPublic(byte flag){
return (SPEC_PUBLIC.flag & flag) == SPEC_PUBLIC.flag; return (SPEC_PUBLIC.flag & flag) == SPEC_PUBLIC.flag;
} }
public static boolean isStagedApi(byte flag){ public static boolean isStagedApi(byte flag){
return (SPEC_STAGED_API.flag & flag) == SPEC_STAGED_API.flag; return (SPEC_STAGED_API.flag & flag) == SPEC_STAGED_API.flag;
} }
public static String toString(byte flagValue){ public static String toString(byte flagValue){
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
boolean appendOnce = false; boolean appendOnce = false;
int sum = 0; int sum = 0;
int flagValueInt = flagValue & 0xff; int flagValueInt = flagValue & 0xff;
for(Flag flag:values()){ for(Flag flag:values()){
int flagInt = flag.flag & 0xff; int flagInt = flag.flag & 0xff;
if((flagInt & flagValueInt) != flagInt){ if((flagInt & flagValueInt) != flagInt){
continue; continue;
} }
if(appendOnce){ if(appendOnce){
builder.append('|'); builder.append('|');
} }
builder.append(flag); builder.append(flag);
appendOnce = true; appendOnce = true;
sum = sum | flagInt; sum = sum | flagInt;
} }
if(sum != flagValueInt){ if(sum != flagValueInt){
if(appendOnce){ if(appendOnce){
builder.append('|'); builder.append('|');
} }
builder.append(String.format("0x%02x", flagValueInt)); builder.append(HexUtil.toHex2((byte) flagValueInt));
} }
return builder.toString(); return builder.toString();
} }
} }
public static final String NAME_spec = "spec"; public static final String NAME_spec = "spec";
public static final String NAME_spec_flags = "spec_flags"; public static final String NAME_spec_flags = "spec_flags";
public static final String NAME_flag = "flag"; public static final String NAME_flag = "flag";
} }

View File

@ -21,6 +21,7 @@ import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*; import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.ResXmlStringPool; import com.reandroid.arsc.pool.ResXmlStringPool;
import com.reandroid.arsc.pool.StringPool; import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.AttributeValue; import com.reandroid.arsc.value.AttributeValue;
import com.reandroid.arsc.value.ValueItem; import com.reandroid.arsc.value.ValueItem;
import com.reandroid.arsc.value.ValueType; import com.reandroid.arsc.value.ValueType;
@ -419,10 +420,10 @@ public class ResXmlAttribute extends ValueItem implements AttributeValue, Compar
if(group==null){ if(group==null){
//Lets ignore such error until XML encoder implemented //Lets ignore such error until XML encoder implemented
//throw new XMLException("Failed to decode attribute name: " //throw new XMLException("Failed to decode attribute name: "
//+ String.format("@0x%08x", resourceId)); //HexUtil.toHex8("@0x", resourceId));
name=String.format("@0x%08x", resourceId); name = HexUtil.toHex8("@0x", resourceId);
}else { }else {
name=group.getSpecName(); name = group.getSpecName();
} }
} }
String prefix = getNamePrefix(); String prefix = getNamePrefix();
@ -445,7 +446,7 @@ public class ResXmlAttribute extends ValueItem implements AttributeValue, Compar
if(fullName!=null ){ if(fullName!=null ){
int id=getNameResourceID(); int id=getNameResourceID();
if(id!=0){ if(id!=0){
fullName=fullName+"(@"+String.format("0x%08x",id)+")"; fullName=fullName+"(@"+ HexUtil.toHex8(id)+")";
} }
String valStr; String valStr;
ValueType valueType=getValueType(); ValueType valueType=getValueType();
@ -456,7 +457,7 @@ public class ResXmlAttribute extends ValueItem implements AttributeValue, Compar
}else if (valueType==ValueType.INT_DEC){ }else if (valueType==ValueType.INT_DEC){
valStr = String.valueOf(getData()); valStr = String.valueOf(getData());
}else { }else {
valStr = "["+valueType+"] " + String.format("0x%08x",getData()); valStr = "["+valueType+"] " + HexUtil.toHex8(getData());
} }
if(valStr!=null){ if(valStr!=null){
return fullName+"=\""+valStr+"\""; return fullName+"=\""+valStr+"\"";

View File

@ -27,6 +27,7 @@ import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.TypeString; import com.reandroid.arsc.item.TypeString;
import com.reandroid.arsc.pool.SpecStringPool; import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResConfig; import com.reandroid.arsc.value.ResConfig;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
@ -338,7 +339,7 @@ public class SpecTypePair extends BlockContainer<Block>
@Override @Override
public String toString(){ public String toString(){
StringBuilder builder=new StringBuilder(); StringBuilder builder=new StringBuilder();
builder.append(String.format("0x%02x", getTypeId())); builder.append(HexUtil.toHex2(getTypeId()));
builder.append(" ("); builder.append(" (");
TypeString ts = getTypeString(); TypeString ts = getTypeString();
if(ts!=null){ if(ts!=null){

View File

@ -15,6 +15,7 @@
*/ */
package com.reandroid.arsc.decoder; package com.reandroid.arsc.decoder;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.ValueType; import com.reandroid.arsc.value.ValueType;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -43,7 +44,7 @@ public class ColorUtil {
default: default:
return null; return null;
} }
String hex = String.format("%08x", data); String hex = HexUtil.toHexNoPrefix8(data);
return "#" + hex.substring(index); return "#" + hex.substring(index);
} }
public static ValueDecoder.EncodeResult encode(String hexColor){ public static ValueDecoder.EncodeResult encode(String hexColor){

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.apk.AndroidFrameworks; import com.reandroid.apk.AndroidFrameworks;
@ -25,6 +25,7 @@ import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.util.FrameworkTable; import com.reandroid.arsc.util.FrameworkTable;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.AttributeValue; import com.reandroid.arsc.value.AttributeValue;
import com.reandroid.arsc.value.Value; import com.reandroid.arsc.value.Value;
import com.reandroid.arsc.value.ValueType; import com.reandroid.arsc.value.ValueType;
@ -57,7 +58,7 @@ public class Decoder {
return null; return null;
} }
private String hexResourceName(int resourceId){ private String hexResourceName(int resourceId){
return String.format("@0x%08x", resourceId); return HexUtil.toHex8("@0x", resourceId);
} }
public String decodeValue(Value value){ public String decodeValue(Value value){
if(value==null){ if(value==null){

View File

@ -20,6 +20,7 @@ import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.item.TableString; import com.reandroid.arsc.item.TableString;
import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.*; import com.reandroid.arsc.value.*;
import com.reandroid.arsc.value.attribute.AttributeBag; import com.reandroid.arsc.value.attribute.AttributeBag;
import com.reandroid.common.EntryStore; import com.reandroid.common.EntryStore;
@ -265,11 +266,11 @@ public class ValueDecoder {
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 HexUtil.toHex8("@0x", resourceId);
} }
Entry entry =entryGroup.pickOne(); Entry entry = entryGroup.pickOne();
if(entry ==null){ if(entry == null){
return String.format("@0x%08x", resourceId); return HexUtil.toHex8("@0x", resourceId);
} }
String prefix=null; String prefix=null;
if(currentPackage!=null){ if(currentPackage!=null){
@ -705,7 +706,7 @@ public class ValueDecoder {
} }
private static String decodeHex(int rawVal){ private static String decodeHex(int rawVal){
return String.format("0x%x", rawVal); return HexUtil.toHex(rawVal, 1);
} }
private static String decodeInt(int rawVal){ private static String decodeInt(int rawVal){
return String.valueOf(rawVal); return String.valueOf(rawVal);
@ -786,7 +787,7 @@ public class ValueDecoder {
return entry1; return entry1;
} }
private static String toHexResourceId(int resourceId){ private static String toHexResourceId(int resourceId){
return String.format("0x%08x", resourceId); return HexUtil.toHex8(resourceId);
} }
private static boolean isEqualString(String str1, String str2){ private static boolean isEqualString(String str1, String str2){
if(isEmpty(str1)){ if(isEmpty(str1)){
@ -810,7 +811,7 @@ public class ValueDecoder {
} }
@Override @Override
public String toString(){ public String toString(){
return valueType+": "+String.format("0x%08x", value); return valueType+": "+HexUtil.toHex8(value);
} }
} }

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.header; package com.reandroid.arsc.header;
import com.reandroid.arsc.base.BlockContainer; import com.reandroid.arsc.base.BlockContainer;
@ -27,11 +27,12 @@ import com.reandroid.arsc.item.ByteArray;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.ShortItem; import com.reandroid.arsc.item.ShortItem;
import com.reandroid.arsc.util.HexBytesWriter; import com.reandroid.arsc.util.HexBytesWriter;
import com.reandroid.arsc.util.HexUtil;
import java.io.*; import java.io.*;
import java.util.List; import java.util.List;
public class HeaderBlock extends ExpandableBlockContainer implements BlockLoad { public class HeaderBlock extends ExpandableBlockContainer implements BlockLoad {
private final ShortItem mType; private final ShortItem mType;
private final ShortItem mHeaderSize; private final ShortItem mHeaderSize;
private final IntegerItem mChunkSize; private final IntegerItem mChunkSize;
@ -57,7 +58,7 @@ import java.util.List;
return countBytes(); return countBytes();
} }
public ByteArray getExtraBytes() { public ByteArray getExtraBytes() {
return extraBytes; return extraBytes;
} }
public void setHeaderLoaded(HeaderLoaded headerLoaded){ public void setHeaderLoaded(HeaderLoaded headerLoaded){
this.mHeaderLoaded=headerLoaded; this.mHeaderLoaded=headerLoaded;
@ -111,9 +112,9 @@ import java.util.List;
} }
/**Non buffering reader*/ /**Non buffering reader*/
public int readBytes(InputStream inputStream) throws IOException{ public int readBytes(InputStream inputStream) throws IOException{
int result = onReadBytes(inputStream); int result = onReadBytes(inputStream);
super.notifyBlockLoad(); super.notifyBlockLoad();
return result; return result;
} }
private int onReadBytes(InputStream inputStream) throws IOException { private int onReadBytes(InputStream inputStream) throws IOException {
int readCount = readBytes(inputStream, this); int readCount = readBytes(inputStream, this);
@ -218,7 +219,7 @@ import java.util.List;
builder.append(type.toString()); builder.append(type.toString());
}else { }else {
builder.append("Unknown type="); builder.append("Unknown type=");
builder.append(String.format("0x%02x", (0xffff & t))); builder.append(HexUtil.toHex4(t));
} }
builder.append("{ValueHeader="); builder.append("{ValueHeader=");
builder.append(getHeaderSize()); builder.append(getHeaderSize());

View File

@ -1,4 +1,4 @@
/* /*
* 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");
@ -16,7 +16,9 @@
package com.reandroid.arsc.item; package com.reandroid.arsc.item;
public class ByteItem extends BlockItem { import com.reandroid.arsc.util.HexUtil;
public class ByteItem extends BlockItem {
public ByteItem() { public ByteItem() {
super(1); super(1);
} }
@ -47,7 +49,7 @@ public class ByteItem extends BlockItem {
return 0xff & get(); return 0xff & get();
} }
public String toHex(){ public String toHex(){
return String.format("0x%02x", unsignedInt()); return HexUtil.toHex2(get());
} }
@Override @Override
public String toString(){ public String toString(){

View File

@ -1,27 +1,27 @@
/* /*
* 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.item; package com.reandroid.arsc.item;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.io.BlockReader; import java.io.IOException;
import java.io.InputStream;
import java.io.IOException; public class IntegerItem extends BlockItem implements ReferenceItem{
import java.io.InputStream;
public class IntegerItem extends BlockItem implements ReferenceItem{
private int mCache; private int mCache;
public IntegerItem(){ public IntegerItem(){
super(4); super(4);
@ -50,7 +50,7 @@ package com.reandroid.arsc.item;
return get() & 0x00000000ffffffffL; return get() & 0x00000000ffffffffL;
} }
public String toHex(){ public String toHex(){
return String.format("0x%08x", get()); return HexUtil.toHex8(get());
} }
@Override @Override
protected void onBytesChanged() { protected void onBytesChanged() {

View File

@ -1,20 +1,22 @@
/* /*
* 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.item; package com.reandroid.arsc.item;
import com.reandroid.arsc.util.HexUtil;
public class LongItem extends BlockItem{ public class LongItem extends BlockItem{
private long mCache; private long mCache;
public LongItem() { public LongItem() {
@ -31,7 +33,7 @@ public class LongItem extends BlockItem{
return mCache; return mCache;
} }
public String toHex(){ public String toHex(){
return String.format("0x%016x", get()); return HexUtil.toHex(get(), 16);
} }
@Override @Override

View File

@ -1,22 +1,23 @@
/* /*
* 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.item; package com.reandroid.arsc.item;
import com.reandroid.arsc.chunk.xml.ResXmlDocument; import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.pool.ResXmlStringPool; import com.reandroid.arsc.pool.ResXmlStringPool;
import com.reandroid.arsc.util.HexUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -90,7 +91,7 @@ public class ResXmlID extends IntegerItem {
builder.append(getIndex()); builder.append(getIndex());
} }
builder.append(':'); builder.append(':');
builder.append(String.format("0x%08x", get())); builder.append(HexUtil.toHex8(get()));
builder.append('}'); builder.append('}');
return builder.toString(); return builder.toString();
} }

View File

@ -1,26 +1,27 @@
/* /*
* 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.item; package com.reandroid.arsc.item;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.util.HexUtil;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
public class ShortItem extends BlockItem { public class ShortItem extends BlockItem {
private short mCache; private short mCache;
public ShortItem(){ public ShortItem(){
@ -46,7 +47,7 @@ package com.reandroid.arsc.item;
return 0xffff & get(); return 0xffff & get();
} }
public String toHex(){ public String toHex(){
return String.format("0x%04x", unsignedInt()); return HexUtil.toHex4(get());
} }
@Override @Override
protected void onBytesChanged() { protected void onBytesChanged() {

View File

@ -1,21 +1,22 @@
/* /*
* 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.item; package com.reandroid.arsc.item;
import com.reandroid.arsc.chunk.SpecBlock; import com.reandroid.arsc.chunk.SpecBlock;
import com.reandroid.arsc.util.HexUtil;
public class SpecFlag extends IndirectItem<SpecFlagsArray> { public class SpecFlag extends IndirectItem<SpecFlagsArray> {
public SpecFlag(SpecFlagsArray specFlagsArray, int offset) { public SpecFlag(SpecFlagsArray specFlagsArray, int offset) {
@ -58,7 +59,7 @@ public class SpecFlag extends IndirectItem<SpecFlagsArray> {
} }
int val = getInteger(); int val = getInteger();
if(val != 0){ if(val != 0){
return String.format("0x%08x", val); return HexUtil.toHex8(val);
} }
return ""; return "";
} }

View File

@ -17,6 +17,7 @@ package com.reandroid.arsc.item;
import com.reandroid.arsc.pool.TypeStringPool; import com.reandroid.arsc.pool.TypeStringPool;
import com.reandroid.arsc.util.HexUtil;
public class TypeString extends StringItem { public class TypeString extends StringItem {
public TypeString(boolean utf8) { public TypeString(boolean utf8) {
@ -40,6 +41,6 @@ public class TypeString extends StringItem {
} }
@Override @Override
public String toString(){ public String toString(){
return String.format("0x%02x", getId())+':'+get(); return HexUtil.toHex2((byte) getId())+':'+get();
} }
} }

View File

@ -106,7 +106,7 @@ public class HexBytesWriter {
} }
} }
private void writeHex(Writer writer, byte b) throws IOException { private void writeHex(Writer writer, byte b) throws IOException {
String hex = String.format("%02x", (0xff & b)).toUpperCase(); String hex = HexUtil.toHex(null, (0xff & b), 2).toUpperCase();
writer.write(hex); writer.write(hex);
} }
private void writeString(Writer writer, int width, int position) throws IOException { private void writeString(Writer writer, int width, int position) throws IOException {

View File

@ -0,0 +1,84 @@
/*
* 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.util;
public class HexUtil {
public static String toHex2(byte num){
return toHex((long)(num & 0x00000000000000ffL), 2);
}
public static String toHex4(short num){
return toHex((long)(num & 0x000000000000ffffL), 4);
}
public static String toHex8(int num){
return toHex(num, 8);
}
public static String toHex8(long num){
return toHex(num, 8);
}
public static String toHex(int num, int minLength){
return toHex((0x00000000ffffffffL & num), minLength);
}
public static String toHex(long num, int minLength){
String hex = Long.toHexString(num);
StringBuilder builder = new StringBuilder();
builder.append('0');
builder.append('x');
int rem = minLength - hex.length();
for(int i=0; i < rem; i++){
builder.append('0');
}
builder.append(hex);
return builder.toString();
}
public static String toHexNoPrefix8(int num){
return toHex(null, (0x00000000ffffffffL & num), 8);
}
public static String toHexNoPrefix(int num, int minLength){
return toHex(null, (0x00000000ffffffffL & num), minLength);
}
public static String toHex8(String prefix, int num){
return toHex(prefix, (0x00000000ffffffffL & num), 8);
}
public static String toHex(String prefix, int num, int minLength){
return toHex(prefix, (0x00000000ffffffffL & num), minLength);
}
public static String toHex(String prefix, long num, int minLength){
String hex = Long.toHexString(num);
StringBuilder builder = new StringBuilder();
if(prefix != null){
builder.append(prefix);
}
int rem = minLength - hex.length();
for(int i=0; i < rem; i++){
builder.append('0');
}
builder.append(hex);
return builder.toString();
}
public static int parseHex(String hexString){
hexString = trim0x(hexString);
return (int) Long.parseLong(hexString, 16);
}
private static String trim0x(String hexString){
if(hexString == null || hexString.length() < 3){
return hexString;
}
if(hexString.charAt(0) == '0' && hexString.charAt(1) == 'x'){
hexString = hexString.substring(2);
}
return hexString;
}
}

View File

@ -29,6 +29,7 @@ import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*; import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.SpecStringPool; import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
@ -429,7 +430,7 @@ public class Entry extends Block implements JSONConvert<JSONObject> {
@Override @Override
public String toString(){ public String toString(){
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append(String.format("0x%08x", getResourceId())); builder.append(HexUtil.toHex8(getResourceId()));
builder.append(' '); builder.append(' ');
ResConfig resConfig = getResConfig(); ResConfig resConfig = getResConfig();
if(resConfig!=null){ if(resConfig!=null){

View File

@ -1,20 +1,21 @@
/* /*
* 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.value; package com.reandroid.arsc.value;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
public class EntryHeaderMap extends ValueHeader { public class EntryHeaderMap extends ValueHeader {
@ -92,7 +93,7 @@ public class EntryHeaderMap extends ValueHeader {
int parentId = getParentId(); int parentId = getParentId();
if(parentId!=0){ if(parentId!=0){
builder.append(", parentId="); builder.append(", parentId=");
builder.append(String.format("0x%08x", getParentId())); builder.append(HexUtil.toHex8(getParentId()));
} }
builder.append(", count=").append(getValuesCount()); builder.append(", count=").append(getValuesCount());
return builder.toString(); return builder.toString();

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.value; package com.reandroid.arsc.value;
import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.Block;
@ -20,6 +20,7 @@ import com.reandroid.arsc.base.BlockCounter;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.FixedLengthString; import com.reandroid.arsc.item.FixedLengthString;
import com.reandroid.arsc.item.IntegerItem; import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
@ -117,7 +118,7 @@ public class LibraryInfo extends Block implements JSONConvert<JSONObject> {
public String toString(){ public String toString(){
StringBuilder builder=new StringBuilder(); StringBuilder builder=new StringBuilder();
builder.append("LIBRARY{"); builder.append("LIBRARY{");
builder.append(String.format("0x%02x", getPackageId())); builder.append(HexUtil.toHex2((byte) getPackageId()));
builder.append(':'); builder.append(':');
String name=getPackageName(); String name=getPackageName();
if(name==null){ if(name==null){

View File

@ -1,22 +1,23 @@
/* /*
* 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.value; package com.reandroid.arsc.value;
import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
public class ResValueMap extends ValueItem implements AttributeValue{ public class ResValueMap extends ValueItem implements AttributeValue{
@ -110,7 +111,7 @@ public class ResValueMap extends ValueItem implements AttributeValue{
} }
@Override @Override
public String toString(){ public String toString(){
return "name="+String.format("0x%08x", getName()) return "name=" + HexUtil.toHex8(getName())
+", "+super.toString(); +", "+super.toString();
} }

View File

@ -1,6 +1,22 @@
/*
* 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.value; package com.reandroid.arsc.value;
import com.reandroid.arsc.item.ByteArray; import com.reandroid.arsc.item.ByteArray;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
@ -22,7 +38,7 @@ public class StagedAliasEntry extends ByteArray implements JSONConvert<JSONObjec
return getInteger(0); return getInteger(0);
} }
public void setStagedResId(int id){ public void setStagedResId(int id){
putInteger(0, id); putInteger(0, id);
} }
public int getFinalizedResId(){ public int getFinalizedResId(){
return getInteger(4); return getInteger(4);
@ -32,8 +48,8 @@ public class StagedAliasEntry extends ByteArray implements JSONConvert<JSONObjec
} }
@Override @Override
public String toString(){ public String toString(){
return "stagedResId="+String.format("0x%08x",getStagedResId()) return "stagedResId=" + HexUtil.toHex8(getStagedResId())
+", finalizedResId="+String.format("0x%08x",getFinalizedResId()); +", finalizedResId=" + HexUtil.toHex8(getFinalizedResId());
} }
@Override @Override
public JSONObject toJson() { public JSONObject toJson() {

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.value; package com.reandroid.arsc.value;
import com.reandroid.arsc.base.Block; import com.reandroid.arsc.base.Block;
@ -24,308 +24,309 @@ import com.reandroid.arsc.item.ReferenceItem;
import com.reandroid.arsc.item.StringItem; import com.reandroid.arsc.item.StringItem;
import com.reandroid.arsc.pool.StringPool; import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.pool.TableStringPool; import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject; import com.reandroid.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
public abstract class ValueItem extends BlockItem implements Value, public abstract class ValueItem extends BlockItem implements Value,
JSONConvert<JSONObject>{ JSONConvert<JSONObject>{
private ReferenceItem mStringReference; private ReferenceItem mStringReference;
private final int sizeOffset; private final int sizeOffset;
public ValueItem(int bytesLength, int sizeOffset) { public ValueItem(int bytesLength, int sizeOffset) {
super(bytesLength); super(bytesLength);
this.sizeOffset = sizeOffset; this.sizeOffset = sizeOffset;
writeSize();
}
void linkTableStrings(TableStringPool tableStringPool){ writeSize();
if(getValueType() == ValueType.STRING){ }
linkStringReference(tableStringPool);
}
}
public void onRemoved(){
unLinkStringReference();
}
protected void onDataChanged(){
}
public void refresh(){
writeSize();
}
byte getRes0(){ void linkTableStrings(TableStringPool tableStringPool){
return getBytesInternal()[this.sizeOffset + OFFSET_RES0]; if(getValueType() == ValueType.STRING){
} linkStringReference(tableStringPool);
public byte getType(){ }
return getBytesInternal()[this.sizeOffset + OFFSET_TYPE]; }
} public void onRemoved(){
public void setType(byte type){ unLinkStringReference();
if(type == getType()){ }
return; protected void onDataChanged(){
} }
byte[] bts = getBytesInternal(); public void refresh(){
int offset = this.sizeOffset + OFFSET_TYPE; writeSize();
byte old = bts[offset]; }
bts[offset] = type;
onTypeChanged(old, type); byte getRes0(){
onDataChanged(); return getBytesInternal()[this.sizeOffset + OFFSET_RES0];
} }
public int getSize(){ public byte getType(){
return 0xffff & getShort(getBytesInternal(), this.sizeOffset + OFFSET_SIZE); return getBytesInternal()[this.sizeOffset + OFFSET_TYPE];
} }
public void setSize(int size){ public void setType(byte type){
size = this.sizeOffset + size; if(type == getType()){
setBytesLength(size, false); return;
writeSize(); }
} byte[] bts = getBytesInternal();
private void writeSize(){ int offset = this.sizeOffset + OFFSET_TYPE;
int offset = this.sizeOffset; byte old = bts[offset];
int size = countBytes() - offset; bts[offset] = type;
putShort(getBytesInternal(), offset + OFFSET_SIZE, (short) size); onTypeChanged(old, type);
} onDataChanged();
protected void onDataLoaded(){ }
if(getValueType() == ValueType.STRING){ public int getSize(){
linkStringReference(); return 0xffff & getShort(getBytesInternal(), this.sizeOffset + OFFSET_SIZE);
}else { }
unLinkStringReference(); public void setSize(int size){
} size = this.sizeOffset + size;
} setBytesLength(size, false);
@Override writeSize();
public ValueType getValueType(){ }
return ValueType.valueOf(getType()); private void writeSize(){
} int offset = this.sizeOffset;
@Override int size = countBytes() - offset;
public void setValueType(ValueType valueType){ putShort(getBytesInternal(), offset + OFFSET_SIZE, (short) size);
byte type = 0; }
if(valueType!=null){ protected void onDataLoaded(){
type = valueType.getByte(); if(getValueType() == ValueType.STRING){
} linkStringReference();
setType(type); }else {
} unLinkStringReference();
@Override }
public int getData(){ }
return getInteger(getBytesInternal(), this.sizeOffset + OFFSET_DATA); @Override
} public ValueType getValueType(){
@Override return ValueType.valueOf(getType());
public void setData(int data){ }
byte[] bts = getBytesInternal(); @Override
int old = getInteger(bts, this.sizeOffset + OFFSET_DATA); public void setValueType(ValueType valueType){
if(old == data){ byte type = 0;
return; if(valueType!=null){
} type = valueType.getByte();
unLinkStringReference(); }
putInteger(bts, this.sizeOffset + OFFSET_DATA, data); setType(type);
if(ValueType.STRING==getValueType()){ }
linkStringReference(); @Override
} public int getData(){
onDataChanged(); return getInteger(getBytesInternal(), this.sizeOffset + OFFSET_DATA);
} }
@Override
public void setData(int data){
byte[] bts = getBytesInternal();
int old = getInteger(bts, this.sizeOffset + OFFSET_DATA);
if(old == data){
return;
}
unLinkStringReference();
putInteger(bts, this.sizeOffset + OFFSET_DATA, data);
if(ValueType.STRING==getValueType()){
linkStringReference();
}
onDataChanged();
}
public StringItem getDataAsPoolString(){ public StringItem getDataAsPoolString(){
if(getValueType()!=ValueType.STRING){ if(getValueType()!=ValueType.STRING){
return null; return null;
} }
StringPool<?> stringPool = getStringPool(); StringPool<?> stringPool = getStringPool();
if(stringPool == null){ if(stringPool == null){
return null; return null;
} }
return stringPool.get(getData()); return stringPool.get(getData());
} }
private void onTypeChanged(byte old, byte type){ private void onTypeChanged(byte old, byte type){
byte typeString = ValueType.STRING.getByte(); byte typeString = ValueType.STRING.getByte();
if(old == typeString){ if(old == typeString){
unLinkStringReference(); unLinkStringReference();
}else if(type == typeString){ }else if(type == typeString){
linkStringReference(); linkStringReference();
} }
} }
private void linkStringReference(){ private void linkStringReference(){
StringPool<?> stringPool = getStringPool(); StringPool<?> stringPool = getStringPool();
if(stringPool == null || stringPool.isStringLinkLocked()){ if(stringPool == null || stringPool.isStringLinkLocked()){
return; return;
} }
linkStringReference(stringPool); linkStringReference(stringPool);
} }
private void linkStringReference(StringPool<?> stringPool){ private void linkStringReference(StringPool<?> stringPool){
StringItem tableString = stringPool.get(getData()); StringItem tableString = stringPool.get(getData());
if(tableString == null){ if(tableString == null){
unLinkStringReference(); unLinkStringReference();
return; return;
} }
ReferenceItem stringReference = mStringReference; ReferenceItem stringReference = mStringReference;
if(stringReference!=null){ if(stringReference!=null){
unLinkStringReference(); unLinkStringReference();
} }
stringReference = new ReferenceBlock<>(this, this.sizeOffset + OFFSET_DATA); stringReference = new ReferenceBlock<>(this, this.sizeOffset + OFFSET_DATA);
mStringReference = stringReference; mStringReference = stringReference;
tableString.addReference(stringReference); tableString.addReference(stringReference);
} }
private void unLinkStringReference(){ private void unLinkStringReference(){
ReferenceItem stringReference = mStringReference; ReferenceItem stringReference = mStringReference;
if(stringReference==null){ if(stringReference==null){
return; return;
} }
mStringReference = null; mStringReference = null;
onUnlinkDataString(stringReference); onUnlinkDataString(stringReference);
} }
protected void onUnlinkDataString(ReferenceItem referenceItem){ protected void onUnlinkDataString(ReferenceItem referenceItem){
StringPool<?> stringPool = getStringPool(); StringPool<?> stringPool = getStringPool();
if(stringPool == null){ if(stringPool == null){
return; return;
} }
stringPool.removeReference(referenceItem); stringPool.removeReference(referenceItem);
} }
public StringPool<?> getStringPool(){ public StringPool<?> getStringPool(){
Block parent = getParent(); Block parent = getParent();
while (parent!=null){ while (parent!=null){
if(parent instanceof MainChunk){ if(parent instanceof MainChunk){
return ((MainChunk) parent).getStringPool(); return ((MainChunk) parent).getStringPool();
} }
parent=parent.getParent(); parent=parent.getParent();
} }
return null; return null;
} }
@Override @Override
public void onReadBytes(BlockReader reader) throws IOException { public void onReadBytes(BlockReader reader) throws IOException {
int readSize = initializeBytes(reader); int readSize = initializeBytes(reader);
super.onReadBytes(reader); super.onReadBytes(reader);
if(readSize<8){ if(readSize<8){
setBytesLength(this.sizeOffset + 8, false); setBytesLength(this.sizeOffset + 8, false);
writeSize(); writeSize();
} }
} }
private int initializeBytes(BlockReader reader) throws IOException { private int initializeBytes(BlockReader reader) throws IOException {
int position = reader.getPosition(); int position = reader.getPosition();
int offset = this.sizeOffset; int offset = this.sizeOffset;
reader.offset(offset); reader.offset(offset);
int readSize = reader.readUnsignedShort(); int readSize = reader.readUnsignedShort();
int size = readSize; int size = readSize;
if(size<8){ if(size<8){
if(reader.available()>=8){ if(reader.available()>=8){
size = 8; size = 8;
} }
} }
reader.seek(position); reader.seek(position);
setBytesLength(offset + size, false); setBytesLength(offset + size, false);
return readSize; return readSize;
} }
@Override @Override
public String getValueAsString(){ public String getValueAsString(){
StringItem stringItem = getDataAsPoolString(); StringItem stringItem = getDataAsPoolString();
if(stringItem!=null){ if(stringItem!=null){
String value = stringItem.getHtml(); String value = stringItem.getHtml();
if(value == null){ if(value == null){
value = ""; value = "";
} }
return value; return value;
} }
return null; return null;
} }
public void setValueAsString(String str){ public void setValueAsString(String str){
if(getValueType() == ValueType.STRING if(getValueType() == ValueType.STRING
&& Objects.equals(str, getValueAsString())){ && Objects.equals(str, getValueAsString())){
return; return;
} }
if(str == null){ if(str == null){
str = ""; str = "";
} }
StringItem stringItem = getStringPool().getOrCreate(str); StringItem stringItem = getStringPool().getOrCreate(str);
setData(stringItem.getIndex()); setData(stringItem.getIndex());
setValueType(ValueType.STRING); setValueType(ValueType.STRING);
} }
public boolean getValueAsBoolean(){ public boolean getValueAsBoolean(){
return getData()!=0; return getData()!=0;
} }
public void setValueAsBoolean(boolean val){ public void setValueAsBoolean(boolean val){
setValueType(ValueType.INT_BOOLEAN); setValueType(ValueType.INT_BOOLEAN);
int data=val?0xffffffff:0; int data=val?0xffffffff:0;
setData(data); setData(data);
} }
public void setTypeAndData(ValueType valueType, int data){ public void setTypeAndData(ValueType valueType, int data){
setData(data); setData(data);
setValueType(valueType); setValueType(valueType);
} }
public void merge(ValueItem valueItem){ public void merge(ValueItem valueItem){
if(valueItem == null || valueItem==this){ if(valueItem == null || valueItem==this){
return; return;
} }
setSize(valueItem.getSize()); setSize(valueItem.getSize());
ValueType coming = valueItem.getValueType(); ValueType coming = valueItem.getValueType();
if(coming == ValueType.STRING){ if(coming == ValueType.STRING){
setValueAsString(valueItem.getValueAsString()); setValueAsString(valueItem.getValueAsString());
}else { }else {
setTypeAndData(coming, valueItem.getData()); setTypeAndData(coming, valueItem.getData());
} }
} }
@Override @Override
public JSONObject toJson() { public JSONObject toJson() {
if(isNull()){ if(isNull()){
return null; return null;
} }
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
ValueType valueType = getValueType(); ValueType valueType = getValueType();
jsonObject.put(NAME_value_type, valueType.name()); jsonObject.put(NAME_value_type, valueType.name());
if(valueType==ValueType.STRING){ if(valueType==ValueType.STRING){
jsonObject.put(NAME_data, getValueAsString()); jsonObject.put(NAME_data, getValueAsString());
}else if(valueType==ValueType.INT_BOOLEAN){ }else if(valueType==ValueType.INT_BOOLEAN){
jsonObject.put(NAME_data, getValueAsBoolean()); jsonObject.put(NAME_data, getValueAsBoolean());
}else { }else {
jsonObject.put(NAME_data, getData()); jsonObject.put(NAME_data, getData());
} }
return jsonObject; return jsonObject;
} }
@Override @Override
public void fromJson(JSONObject json) { public void fromJson(JSONObject json) {
ValueType valueType = ValueType.fromName(json.getString(NAME_value_type)); ValueType valueType = ValueType.fromName(json.getString(NAME_value_type));
if(valueType==ValueType.STRING){ if(valueType==ValueType.STRING){
setValueAsString(json.optString(NAME_data, "")); setValueAsString(json.optString(NAME_data, ""));
}else if(valueType==ValueType.INT_BOOLEAN){ }else if(valueType==ValueType.INT_BOOLEAN){
setValueAsBoolean(json.getBoolean(NAME_data)); setValueAsBoolean(json.getBoolean(NAME_data));
}else { }else {
setValueType(valueType); setValueType(valueType);
setData(json.getInt(NAME_data)); setData(json.getInt(NAME_data));
} }
} }
@Override @Override
public String toString(){ public String toString(){
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
int size = getSize(); int size = getSize();
if(size!=8){ if(size!=8){
builder.append("size=").append(getSize()); builder.append("size=").append(getSize());
builder.append(", "); builder.append(", ");
} }
builder.append("type="); builder.append("type=");
ValueType valueType=getValueType(); ValueType valueType=getValueType();
if(valueType!=null){ if(valueType!=null){
builder.append(valueType); builder.append(valueType);
}else { }else {
builder.append(String.format("0x%02x", (0xff & getType()))); builder.append(HexUtil.toHex2(getType()));
} }
builder.append(", data="); builder.append(", data=");
int data = getData(); int data = getData();
if(valueType==ValueType.STRING){ if(valueType==ValueType.STRING){
StringItem tableString = getDataAsPoolString(); StringItem tableString = getDataAsPoolString();
if(tableString!=null){ if(tableString!=null){
builder.append(tableString.getHtml()); builder.append(tableString.getHtml());
}else { }else {
builder.append(String.format("0x%08x", data)); builder.append(HexUtil.toHex8(data));
} }
}else { }else {
builder.append(String.format("0x%08x", data)); builder.append(HexUtil.toHex8(data));
} }
return builder.toString(); return builder.toString();
} }
private static final int OFFSET_SIZE = 0; private static final int OFFSET_SIZE = 0;
private static final int OFFSET_RES0 = 2; private static final int OFFSET_RES0 = 2;
private static final int OFFSET_TYPE = 3; private static final int OFFSET_TYPE = 3;
private static final int OFFSET_DATA = 4; private static final int OFFSET_DATA = 4;
public static final String NAME_data = "data"; public static final String NAME_data = "data";
public static final String NAME_value_type = "value_type"; public static final String NAME_value_type = "value_type";
} }

View File

@ -1,95 +1,96 @@
/* /*
* 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.value.array; package com.reandroid.arsc.value.array;
import com.reandroid.arsc.decoder.ValueDecoder; import com.reandroid.arsc.decoder.ValueDecoder;
import com.reandroid.arsc.item.StringItem; import com.reandroid.arsc.item.StringItem;
import com.reandroid.arsc.item.TableString; import com.reandroid.arsc.item.TableString;
import com.reandroid.arsc.value.ValueType; import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.bag.BagItem; import com.reandroid.arsc.value.ValueType;
import com.reandroid.arsc.value.ResValueMap; import com.reandroid.arsc.value.bag.BagItem;
import com.reandroid.arsc.value.ResValueMap;
public class ArrayBagItem extends BagItem { public class ArrayBagItem extends BagItem {
private ArrayBagItem(ResValueMap valueMap) { private ArrayBagItem(ResValueMap valueMap) {
super(valueMap); super(valueMap);
} }
private ArrayBagItem(StringItem str) { private ArrayBagItem(StringItem str) {
super(str); super(str);
} }
private ArrayBagItem(ValueType valueType, int value) { private ArrayBagItem(ValueType valueType, int value) {
super(valueType, value); super(valueType, value);
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("<item>"); builder.append("<item>");
if (hasStringValue()) { if (hasStringValue()) {
builder.append(getStringValue()); builder.append(getStringValue());
} else { } else {
builder.append(String.format("0x%08x", getValue())); builder.append(HexUtil.toHex8(getValue()));
} }
builder.append("</item>"); builder.append("</item>");
return builder.toString(); return builder.toString();
} }
protected static ArrayBagItem create(ResValueMap valueMap) { protected static ArrayBagItem create(ResValueMap valueMap) {
if (valueMap == null) { if (valueMap == null) {
return null; return null;
} }
return new ArrayBagItem(valueMap); return new ArrayBagItem(valueMap);
} }
public static ArrayBagItem create(ValueType valueType, int value) { public static ArrayBagItem create(ValueType valueType, int value) {
if (valueType == null || valueType == ValueType.STRING) { if (valueType == null || valueType == ValueType.STRING) {
return null; return null;
} }
return new ArrayBagItem(valueType, value); return new ArrayBagItem(valueType, value);
} }
protected static ArrayBagItem copyOf(ResValueMap resValueMap) { protected static ArrayBagItem copyOf(ResValueMap resValueMap) {
ValueType valueType = resValueMap.getValueType(); ValueType valueType = resValueMap.getValueType();
if (valueType == ValueType.STRING) { if (valueType == ValueType.STRING) {
return new ArrayBagItem(resValueMap.getDataAsPoolString()); return new ArrayBagItem(resValueMap.getDataAsPoolString());
} else { } else {
return new ArrayBagItem(valueType, resValueMap.getData()); return new ArrayBagItem(valueType, resValueMap.getData());
} }
} }
public static ArrayBagItem encoded(ValueDecoder.EncodeResult encodeResult) { public static ArrayBagItem encoded(ValueDecoder.EncodeResult encodeResult) {
if (encodeResult == null) { if (encodeResult == null) {
return null; return null;
} }
return create(encodeResult.valueType, encodeResult.value); return create(encodeResult.valueType, encodeResult.value);
} }
public static ArrayBagItem integer(int n) { public static ArrayBagItem integer(int n) {
return create(ValueType.INT_DEC, n); return create(ValueType.INT_DEC, n);
} }
public static ArrayBagItem string(TableString str) { public static ArrayBagItem string(TableString str) {
if (str == null) { if (str == null) {
return null; return null;
} }
return new ArrayBagItem(str); return new ArrayBagItem(str);
} }
public static ArrayBagItem reference(int resourceId) { public static ArrayBagItem reference(int resourceId) {
return create(ValueType.REFERENCE, resourceId); return create(ValueType.REFERENCE, resourceId);
} }
} }

View File

@ -1,22 +1,23 @@
/* /*
* 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.value.attribute; package com.reandroid.arsc.value.attribute;
import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.group.EntryGroup;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ResValueMap; import com.reandroid.arsc.value.ResValueMap;
import com.reandroid.common.EntryStore; import com.reandroid.common.EntryStore;
@ -36,7 +37,7 @@ public class AttributeBagItem {
public String getNameOrHex(EntryStore entryStore){ public String getNameOrHex(EntryStore entryStore){
String name=getName(entryStore); String name=getName(entryStore);
if(name==null){ if(name==null){
name=String.format("@0x%08x", getBagItem().getName()); name=HexUtil.toHex8("@0x", getBagItem().getName());
} }
return name; return name;
} }
@ -162,7 +163,7 @@ public class AttributeBagItem {
} }
ResValueMap item=getBagItem(); ResValueMap item=getBagItem();
builder.append(getNameOrHex()); builder.append(getNameOrHex());
builder.append("=").append(String.format("0x%x", item.getData())); builder.append("=").append(HexUtil.toHex8(item.getData()));
return builder.toString(); return builder.toString();
} }
@ -214,6 +215,4 @@ public class AttributeBagItem {
} }
return null; return null;
} }
} }

View File

@ -18,6 +18,7 @@
import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.item.StringItem; import com.reandroid.arsc.item.StringItem;
import com.reandroid.arsc.item.TableString; import com.reandroid.arsc.item.TableString;
import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.*; import com.reandroid.arsc.value.*;
import com.reandroid.arsc.value.bag.BagItem; import com.reandroid.arsc.value.bag.BagItem;
@ -83,7 +84,7 @@
} }
private String formattedRefValue() { private String formattedRefValue() {
return String.format("@0x%08x", getValue()); return HexUtil.toHex8("@0x", getValue());
} }
@Override @Override

View File

@ -1,189 +1,190 @@
/* /*
* 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.value.style; package com.reandroid.arsc.value.style;
import com.reandroid.arsc.decoder.ValueDecoder; import com.reandroid.arsc.decoder.ValueDecoder;
import com.reandroid.arsc.item.StringItem; import com.reandroid.arsc.item.StringItem;
import com.reandroid.arsc.item.TableString; import com.reandroid.arsc.item.TableString;
import com.reandroid.arsc.value.attribute.AttributeBag; import com.reandroid.arsc.util.HexUtil;
import com.reandroid.arsc.value.attribute.AttributeBagItem; import com.reandroid.arsc.value.attribute.AttributeBag;
import com.reandroid.arsc.value.bag.BagItem; import com.reandroid.arsc.value.attribute.AttributeBagItem;
import com.reandroid.arsc.value.Entry; import com.reandroid.arsc.value.bag.BagItem;
import com.reandroid.arsc.value.ResValueMap; import com.reandroid.arsc.value.Entry;
import com.reandroid.arsc.value.ValueType; import com.reandroid.arsc.value.ResValueMap;
import com.reandroid.common.EntryStore; import com.reandroid.arsc.value.ValueType;
import com.reandroid.common.EntryStore;
public class StyleBagItem extends BagItem { public class StyleBagItem extends BagItem {
private StyleBagItem(ResValueMap bagItem) { private StyleBagItem(ResValueMap bagItem) {
super(bagItem); super(bagItem);
} }
private StyleBagItem(ValueType valueType, int data) { private StyleBagItem(ValueType valueType, int data) {
super(valueType, data); super(valueType, data);
} }
private StyleBagItem(StringItem str) { private StyleBagItem(StringItem str) {
super(str); super(str);
} }
public String getName() { public String getName() {
if (mBagItem == null) { if (mBagItem == null) {
return null; return null;
} }
Entry block = mBagItem.getEntry(); Entry block = mBagItem.getEntry();
if (block == null) { if (block == null) {
return null; return null;
} }
char prefix = 0; char prefix = 0;
return block.buildResourceName(mBagItem.getName(), prefix, false); return block.buildResourceName(mBagItem.getName(), prefix, false);
} }
public Entry getAttributeEntry(EntryStore entryStore) { public Entry getAttributeEntry(EntryStore entryStore) {
if (mBagItem == null) { if (mBagItem == null) {
return null; return null;
} }
return entryStore.getEntryGroup(mBagItem.getName()).pickOne(); return entryStore.getEntryGroup(mBagItem.getName()).pickOne();
} }
public int getNameId() { public int getNameId() {
if (mBagItem == null) { if (mBagItem == null) {
return 0; return 0;
} }
return mBagItem.getName(); return mBagItem.getName();
} }
public boolean hasAttributeValue() { public boolean hasAttributeValue() {
return getValueType() == ValueType.ATTRIBUTE; return getValueType() == ValueType.ATTRIBUTE;
} }
public boolean hasIntValue() { public boolean hasIntValue() {
ValueType valueType = getValueType(); ValueType valueType = getValueType();
return valueType == ValueType.INT_DEC || valueType == ValueType.INT_HEX; return valueType == ValueType.INT_DEC || valueType == ValueType.INT_HEX;
} }
public String getValueAsReference() { public String getValueAsReference() {
ValueType valueType = getValueType(); ValueType valueType = getValueType();
if (valueType != ValueType.REFERENCE && valueType != ValueType.ATTRIBUTE) { if (valueType != ValueType.REFERENCE && valueType != ValueType.ATTRIBUTE) {
throw new IllegalArgumentException("Not REF ValueType=" + valueType); throw new IllegalArgumentException("Not REF ValueType=" + valueType);
} }
Entry entry = getBagItem().getEntry(); Entry entry = getBagItem().getEntry();
if (entry == null) { if (entry == null) {
return null; return null;
} }
char prefix = '@'; char prefix = '@';
boolean includeType = true; boolean includeType = true;
if (valueType == ValueType.ATTRIBUTE) { if (valueType == ValueType.ATTRIBUTE) {
prefix = '?'; prefix = '?';
includeType = false; includeType = false;
} }
int id = getValue(); int id = getValue();
return entry.buildResourceName(id, prefix, includeType); return entry.buildResourceName(id, prefix, includeType);
} }
public String decodeAttributeValue(AttributeBag attr, EntryStore entryStore) { public String decodeAttributeValue(AttributeBag attr, EntryStore entryStore) {
if (!hasIntValue()) { if (!hasIntValue()) {
return null; return null;
} }
return attr.decodeAttributeValue(entryStore, getValue()); return attr.decodeAttributeValue(entryStore, getValue());
} }
public AttributeBagItem[] getFlagsOrEnum(AttributeBag attr) { public AttributeBagItem[] getFlagsOrEnum(AttributeBag attr) {
if (!hasIntValue()) { if (!hasIntValue()) {
return null; return null;
} }
return attr.searchValue(getValue()); return attr.searchValue(getValue());
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("<item name=\""); builder.append("<item name=\"");
String name = getName(); String name = getName();
if (name == null) { if (name == null) {
name = String.format("@0x%08x", getNameId()); name = HexUtil.toHex8("@0x", getNameId());
} }
builder.append(name); builder.append(name);
builder.append("\">"); builder.append("\">");
if (hasStringValue()) { if (hasStringValue()) {
builder.append(getStringValue()); builder.append(getStringValue());
} }
String val = null; String val = null;
if (hasReferenceValue() || hasAttributeValue()) { if (hasReferenceValue() || hasAttributeValue()) {
val = getValueAsReference(); val = getValueAsReference();
} }
if (val == null) { if (val == null) {
val = String.format("0x%08x", getValue()); val = HexUtil.toHex8(getValue());
} }
builder.append(val); builder.append(val);
builder.append("</item>"); builder.append("</item>");
return builder.toString(); return builder.toString();
} }
protected static StyleBagItem create(ResValueMap resValueMap) { protected static StyleBagItem create(ResValueMap resValueMap) {
if (resValueMap == null) { if (resValueMap == null) {
return null; return null;
} }
return new StyleBagItem(resValueMap); return new StyleBagItem(resValueMap);
} }
public static StyleBagItem create(ValueType valueType, int value) { public static StyleBagItem create(ValueType valueType, int value) {
if (valueType == null || valueType == ValueType.STRING) { if (valueType == null || valueType == ValueType.STRING) {
return null; return null;
} }
return new StyleBagItem(valueType, value); return new StyleBagItem(valueType, value);
} }
protected static StyleBagItem copyOf(ResValueMap resValueMap) { protected static StyleBagItem copyOf(ResValueMap resValueMap) {
ValueType valueType = resValueMap.getValueType(); ValueType valueType = resValueMap.getValueType();
if (valueType == ValueType.STRING) { if (valueType == ValueType.STRING) {
return new StyleBagItem(resValueMap.getDataAsPoolString()); return new StyleBagItem(resValueMap.getDataAsPoolString());
} else { } else {
return new StyleBagItem(valueType, resValueMap.getData()); return new StyleBagItem(valueType, resValueMap.getData());
} }
} }
public static StyleBagItem integer(int n) { public static StyleBagItem integer(int n) {
return new StyleBagItem(ValueType.INT_DEC, n); return new StyleBagItem(ValueType.INT_DEC, n);
} }
public static StyleBagItem string(TableString str) { public static StyleBagItem string(TableString str) {
if (str == null) { if (str == null) {
return null; return null;
} }
return new StyleBagItem(str); return new StyleBagItem(str);
} }
public static StyleBagItem reference(int resourceId) { public static StyleBagItem reference(int resourceId) {
return new StyleBagItem(ValueType.REFERENCE, resourceId); return new StyleBagItem(ValueType.REFERENCE, resourceId);
} }
public static StyleBagItem attribute(int resourceId) { public static StyleBagItem attribute(int resourceId) {
return new StyleBagItem(ValueType.ATTRIBUTE, resourceId); return new StyleBagItem(ValueType.ATTRIBUTE, resourceId);
} }
public static StyleBagItem encoded(ValueDecoder.EncodeResult encodeResult) { public static StyleBagItem encoded(ValueDecoder.EncodeResult encodeResult) {
if (encodeResult == null) { if (encodeResult == null) {
return null; return null;
} }
return create(encodeResult.valueType, encodeResult.value); return create(encodeResult.valueType, encodeResult.value);
} }
public static StyleBagItem color(String color) { public static StyleBagItem color(String color) {
return encoded(ValueDecoder.encodeColor(color)); return encoded(ValueDecoder.encodeColor(color));
} }
public static StyleBagItem dimensionOrFraction(String str) { public static StyleBagItem dimensionOrFraction(String str) {
return encoded(ValueDecoder.encodeDimensionOrFraction(str)); return encoded(ValueDecoder.encodeDimensionOrFraction(str));
} }
public static StyleBagItem createFloat(float n) { public static StyleBagItem createFloat(float n) {
return new StyleBagItem(ValueType.FLOAT, Float.floatToIntBits(n)); return new StyleBagItem(ValueType.FLOAT, Float.floatToIntBits(n));
} }
public static StyleBagItem enumOrFlag(AttributeBag attr, String valueString) { public static StyleBagItem enumOrFlag(AttributeBag attr, String valueString) {
return encoded(attr.encodeEnumOrFlagValue(valueString)); return encoded(attr.encodeEnumOrFlagValue(valueString));
} }
} }