ARSCLib/src/main/java/com/reandroid/lib/apk/xmlencoder/EncodeMaterials.java
2023-01-17 05:10:43 -05:00

374 lines
14 KiB
Java

/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.lib.apk.xmlencoder;
import com.reandroid.lib.apk.APKLogger;
import com.reandroid.lib.apk.ResourceIds;
import com.reandroid.lib.arsc.chunk.PackageBlock;
import com.reandroid.lib.arsc.chunk.TableBlock;
import com.reandroid.lib.arsc.chunk.TypeBlock;
import com.reandroid.lib.arsc.container.SpecTypePair;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.group.EntryGroup;
import com.reandroid.lib.arsc.item.SpecString;
import com.reandroid.lib.arsc.util.FrameworkTable;
import com.reandroid.lib.arsc.value.EntryBlock;
import com.reandroid.lib.common.Frameworks;
import com.reandroid.lib.common.ResourceResolver;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
public class EncodeMaterials implements ResourceResolver {
private final Set<ResourceIds.Table.Package> packageIdSet = new HashSet<>();
private PackageBlock currentPackage;
private final Set<FrameworkTable> frameworkTables = new HashSet<>();
private APKLogger apkLogger;
private boolean mForceCreateNamespaces = true;
private Set<String> mFrameworkPackageNames;
public EncodeMaterials(){
}
public SpecString getSpecString(String name){
return currentPackage.getSpecStringPool()
.get(name)
.get(0);
}
public void addTableStringPool(Collection<String> stringList){
getCurrentPackage()
.getTableBlock()
.getTableStringPool()
.addStrings(stringList);
}
public int resolveAttributeNameReference(String refString){
String packageName = null;
String type = "attr";
String name = refString;
int i=refString.lastIndexOf(':');
if(i>=0){
packageName=refString.substring(0, i);
name=refString.substring(i+1);
}
if(EncodeUtil.isEmpty(packageName)
|| packageName.equals(getCurrentPackageName())
|| !isFrameworkPackageName(packageName)){
return resolveLocalResourceId(type, name);
}
return resolveFrameworkResourceId(packageName, type, name);
}
public EntryBlock getAttributeBlock(String refString){
String packageName = null;
String type = "attr";
String name = refString;
int i=refString.lastIndexOf(':');
if(i>=0){
packageName=refString.substring(0, i);
name=refString.substring(i+1);
}
if(EncodeUtil.isEmpty(packageName)
|| packageName.equals(getCurrentPackageName())
|| !isFrameworkPackageName(packageName)){
return getLocalEntryBlock(type, name);
}
return getFrameworkEntry(type, name);
}
public int resolveReference(String refString){
if("@null".equals(refString)){
return 0;
}
Matcher matcher = ValueDecoder.PATTERN_REFERENCE.matcher(refString);
if(!matcher.find()){
throw new EncodeException(
"Not proper reference string: '"+refString+"'");
}
String prefix=matcher.group(1);
String packageName = matcher.group(2);
if(packageName!=null && packageName.endsWith(":")){
packageName=packageName.substring(0, packageName.length()-1);
}
String type = matcher.group(4);
String name = matcher.group(5);
if(EncodeUtil.isEmpty(packageName)
|| packageName.equals(getCurrentPackageName())
|| !isFrameworkPackageName(packageName)){
return resolveLocalResourceId(type, name);
}
return resolveFrameworkResourceId(packageName, type, name);
}
public EntryBlock resolveEntryReference(String refString){
if("@null".equals(refString)){
return null;
}
Matcher matcher = ValueDecoder.PATTERN_REFERENCE.matcher(refString);
if(!matcher.find()){
return null;
}
String prefix=matcher.group(1);
String packageName = matcher.group(2);
if(packageName!=null && packageName.endsWith(":")){
packageName=packageName.substring(0, packageName.length()-1);
}
String type = matcher.group(4);
String name = matcher.group(5);
if(EncodeUtil.isEmpty(packageName)
|| packageName.equals(getCurrentPackageName())
|| !isFrameworkPackageName(packageName)){
return getLocalEntryBlock(type, name);
}
return getFrameworkEntry(packageName, type, name);
}
public int resolveLocalResourceId(String type, String name){
for(ResourceIds.Table.Package pkg:packageIdSet){
ResourceIds.Table.Package.Type.Entry entry = pkg.getEntry(type, name);
if(entry!=null){
return entry.getResourceId();
}
}
EntryGroup entryGroup=getLocalEntryGroup(type, name);
if(entryGroup!=null){
return entryGroup.getResourceId();
}
throw new EncodeException("Local entry not found: " +
"type="+type+
", name="+name);
}
public int resolveFrameworkResourceId(String type, String name){
EntryBlock entryBlock = getFrameworkEntry(type, name);
if(entryBlock!=null){
return entryBlock.getResourceId();
}
throw new EncodeException("Framework entry not found: " +
"type="+type+
", name="+name);
}
public int resolveFrameworkResourceId(String packageName, String type, String name){
EntryBlock entryBlock = getFrameworkEntry(packageName, type, name);
if(entryBlock!=null){
return entryBlock.getResourceId();
}
throw new EncodeException("Framework entry not found: " +
"package="+packageName+
", type="+type+
", name="+name);
}
public int resolveFrameworkResourceId(int packageId, String type, String name){
EntryBlock entryBlock = getFrameworkEntry(packageId, type, name);
if(entryBlock!=null){
return entryBlock.getResourceId();
}
throw new EncodeException("Framework entry not found: " +
"packageId="+String.format("0x%02x", packageId)+
", type="+type+
", name="+name);
}
public EntryGroup getLocalEntryGroup(String type, String name){
for(EntryGroup entryGroup : currentPackage.listEntryGroup()){
if(type.equals(entryGroup.getTypeName()) &&
name.equals(entryGroup.getSpecName())){
return entryGroup;
}
}
for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){
for(EntryGroup entryGroup : packageBlock.listEntryGroup()){
if(type.equals(entryGroup.getTypeName()) &&
name.equals(entryGroup.getSpecName())){
return entryGroup;
}
}
}
return null;
}
public EntryBlock getLocalEntryBlock(String type, String name){
for(EntryGroup entryGroup : currentPackage.listEntryGroup()){
if(type.equals(entryGroup.getTypeName()) &&
name.equals(entryGroup.getSpecName())){
return entryGroup.pickOne();
}
}
SpecTypePair specTypePair=currentPackage.searchByTypeName(type);
if(specTypePair!=null){
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
for(EntryBlock entryBlock:typeBlock.listEntries()){
if(name.equals(entryBlock.getName())){
return entryBlock;
}
}
break;
}
}
for(PackageBlock packageBlock:currentPackage.getTableBlock().listPackages()){
if(packageBlock==currentPackage){
continue;
}
specTypePair=packageBlock.searchByTypeName(type);
if(specTypePair!=null){
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
for(EntryBlock entryBlock:typeBlock.listEntries()){
if(name.equals(entryBlock.getName())){
return entryBlock;
}
}
break;
}
}
}
return null;
}
public EntryBlock getFrameworkEntry(String type, String name){
for(FrameworkTable table:frameworkTables){
EntryBlock entryBlock = table.searchEntryBlock(type, name);
if(entryBlock!=null){
return entryBlock;
}
}
return null;
}
private boolean isFrameworkPackageName(String packageName){
return getFrameworkPackageNames().contains(packageName);
}
private Set<String> getFrameworkPackageNames(){
if(mFrameworkPackageNames!=null){
return mFrameworkPackageNames;
}
Set<String> results=new HashSet<>();
for(FrameworkTable table:frameworkTables){
for(PackageBlock packageBlock:table.listPackages()){
results.add(packageBlock.getName());
}
}
mFrameworkPackageNames=results;
return results;
}
public EntryBlock getFrameworkEntry(String packageName, String type, String name){
for(FrameworkTable table:frameworkTables){
for(PackageBlock packageBlock:table.listPackages()){
if(packageName.equals(packageBlock.getName())){
EntryBlock entryBlock = table.searchEntryBlock(type, name);
if(entryBlock!=null){
return entryBlock;
}
}
}
}
return null;
}
public EntryBlock getFrameworkEntry(int packageId, String type, String name){
for(FrameworkTable table:frameworkTables){
for(PackageBlock packageBlock:table.listPackages()){
if(packageId==packageBlock.getId()){
EntryBlock entryBlock = table.searchEntryBlock(type, name);
if(entryBlock!=null){
return entryBlock;
}
}
}
}
return null;
}
public EncodeMaterials setForceCreateNamespaces(boolean force) {
this.mForceCreateNamespaces = force;
return this;
}
public EncodeMaterials addPackageIds(Collection<ResourceIds.Table.Package> packageIdList) {
this.packageIdSet.addAll(packageIdList);
return this;
}
public EncodeMaterials addPackageIds(ResourceIds.Table.Package packageIds) {
this.packageIdSet.add(packageIds);
return this;
}
public EncodeMaterials setCurrentPackage(PackageBlock currentPackage) {
this.currentPackage = currentPackage;
return this;
}
public EncodeMaterials addFramework(FrameworkTable frameworkTable) {
frameworkTable.loadResourceNameMap();
this.frameworkTables.add(frameworkTable);
this.mFrameworkPackageNames=null;
return this;
}
public EncodeMaterials setAPKLogger(APKLogger logger) {
this.apkLogger = logger;
return this;
}
public PackageBlock getCurrentPackage() {
return currentPackage;
}
public boolean isForceCreateNamespaces() {
return mForceCreateNamespaces;
}
public String getCurrentPackageName(){
return currentPackage.getName();
}
public int getCurrentPackageId(){
return currentPackage.getId();
}
@Override
public int resolveResourceId(String packageName, String type, String name) {
if(!isFrameworkPackageName(packageName)){
return resolveLocalResourceId(type, name);
}
return resolveFrameworkResourceId(packageName, type, name);
}
@Override
public int resolveResourceId(int packageId, String type, String name) {
if(packageId==getCurrentPackageId()){
return resolveLocalResourceId(type, name);
}
return resolveFrameworkResourceId(packageId, type, name);
}
public void logMessage(String msg) {
if(apkLogger!=null){
apkLogger.logMessage(msg);
}
}
public void logError(String msg, Throwable tr) {
if(apkLogger!=null){
apkLogger.logError(msg, tr);
}
}
public void logVerbose(String msg) {
if(apkLogger!=null){
apkLogger.logVerbose(msg);
}
}
public static EncodeMaterials create(TableBlock tableBlock){
PackageBlock packageBlock = tableBlock.pickOne();
if(packageBlock==null){
throw new EncodeException("No packages found on table block");
}
return create(packageBlock);
}
public static EncodeMaterials create(PackageBlock packageBlock){
ResourceIds resourceIds = new ResourceIds();
resourceIds.loadPackageBlock(packageBlock);
ResourceIds.Table.Package packageId = resourceIds.getTable().listPackages().get(0);
return new EncodeMaterials()
.addPackageIds(packageId)
.setCurrentPackage(packageBlock)
.addFramework(Frameworks.getAndroid());
}
}