Implement resource values XML decoder

This commit is contained in:
REAndroid 2022-12-29 15:21:33 -05:00
parent eb9d0a4651
commit 12f4d4f9d6
2 changed files with 108 additions and 3 deletions

View File

@ -16,36 +16,50 @@
package com.reandroid.lib.apk;
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.chunk.xml.AndroidManifestBlock;
import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock;
import com.reandroid.lib.arsc.container.SpecTypePair;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.value.EntryBlock;
import com.reandroid.lib.arsc.value.ResConfig;
import com.reandroid.lib.arsc.value.ResValueInt;
import com.reandroid.lib.arsc.value.ValueType;
import com.reandroid.lib.common.EntryStore;
import com.reandroid.lib.common.Frameworks;
import com.reandroid.lib.common.TableEntryStore;
import com.reandroid.xml.XMLAttribute;
import com.reandroid.xml.XMLDocument;
import com.reandroid.xml.XMLElement;
import com.reandroid.xml.XMLException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.*;
public class ApkModuleXmlDecoder {
public class ApkModuleXmlDecoder {
private final ApkModule apkModule;
private final Map<Integer, Set<ResConfig>> decodedEntries;
public ApkModuleXmlDecoder(ApkModule apkModule){
this.apkModule=apkModule;
this.decodedEntries = new HashMap<>();
}
public void decodeTo(File outDir)
throws IOException, XMLException {
this.decodedEntries.clear();
TableEntryStore entryStore=new TableEntryStore();
entryStore.add(Frameworks.getAndroid());
entryStore.add(apkModule.getTableBlock());
TableBlock tableBlock=apkModule.getTableBlock();
entryStore.add(tableBlock);
decodeAndroidManifest(entryStore, outDir);
logMessage("Decoding resource files ...");
List<ResFile> resFileList=apkModule.listResFiles();
for(ResFile resFile:resFileList){
decodeResFile(entryStore, outDir, resFile);
}
decodeValues(entryStore, outDir, tableBlock);
}
private void decodeResFile(EntryStore entryStore, File outDir, ResFile resFile)
throws IOException, XMLException {
@ -73,6 +87,8 @@ public class ApkModuleXmlDecoder {
FileOutputStream outputStream=new FileOutputStream(file);
resFile.getInputSource().write(outputStream);
outputStream.close();
addDecodedEntry(resFile.getEntryBlockList());
}
private void decodeResXml(EntryStore entryStore, File outDir, ResFile resFile)
throws IOException, XMLException{
@ -90,6 +106,8 @@ public class ApkModuleXmlDecoder {
logVerbose("Decoding: "+path);
XMLDocument xmlDocument=resXmlBlock.decodeToXml(entryStore, packageBlock.getId());
xmlDocument.save(file, true);
addDecodedEntry(resFile.getEntryBlockList());
}
private void decodeAndroidManifest(EntryStore entryStore, File outDir)
throws IOException, XMLException {
@ -104,6 +122,90 @@ public class ApkModuleXmlDecoder {
XMLDocument xmlDocument=manifestBlock.decodeToXml(entryStore, currentPackageId);
xmlDocument.save(file, true);
}
private void addDecodedEntry(Collection<EntryBlock> entryBlockList){
for(EntryBlock entryBlock:entryBlockList){
addDecodedEntry(entryBlock);
}
}
private void addDecodedEntry(EntryBlock entryBlock){
if(entryBlock.isNull()){
return;
}
int resourceId=entryBlock.getResourceId();
Set<ResConfig> resConfigSet=decodedEntries.get(resourceId);
if(resConfigSet==null){
resConfigSet=new HashSet<>();
decodedEntries.put(resourceId, resConfigSet);
}
resConfigSet.add(entryBlock.getResConfig());
}
private boolean containsDecodedEntry(EntryBlock entryBlock){
Set<ResConfig> resConfigSet=decodedEntries.get(entryBlock.getResourceId());
if(resConfigSet==null){
return false;
}
return resConfigSet.contains(entryBlock.getResConfig());
}
private void decodeValues(EntryStore entryStore, File outDir, TableBlock tableBlock) throws IOException {
for(PackageBlock packageBlock:tableBlock.listPackages()){
decodeValues(entryStore, outDir, packageBlock);
}
}
private void decodeValues(EntryStore entryStore, File outDir, PackageBlock packageBlock) throws IOException {
packageBlock.sortTypes();
for(SpecTypePair specTypePair: packageBlock.listAllSpecTypePair()){
for(TypeBlock typeBlock:specTypePair.listTypeBlocks()){
decodeValues(entryStore, outDir, typeBlock);
}
}
}
private void decodeValues(EntryStore entryStore, File outDir, TypeBlock typeBlock) throws IOException {
XMLDocument xmlDocument = new XMLDocument("resources");
XMLElement docElement = xmlDocument.getDocumentElement();
for(EntryBlock entryBlock:typeBlock.listEntries(true)){
if(containsDecodedEntry(entryBlock)){
continue;
}
docElement.addChild(decodeValue(entryStore, entryBlock));
}
if(docElement.getChildesCount()==0){
return;
}
File file=new File(outDir, typeBlock.getPackageBlock().getName());
file=new File(file, ApkUtil.RES_DIR_NAME);
file=new File(file, "values"+typeBlock.getQualifiers());
String type=typeBlock.getTypeName();
if(!type.endsWith("s")){
type=type+"s";
}
file=new File(file, type+".xml");
xmlDocument.save(file, false);
}
private XMLElement decodeValue(EntryStore entryStore, EntryBlock entryBlock){
XMLElement element=new XMLElement(entryBlock.getTypeName());
element.setResourceId(entryBlock.getResourceId());
if(!entryBlock.isEntryTypeBag()){
String name=entryBlock.getName();
String value;
ResValueInt resValueInt=(ResValueInt) entryBlock.getResValue();
if(resValueInt.getValueType()== ValueType.STRING){
value=resValueInt.getValueAsString();
}else {
value= ValueDecoder.decodeEntryValue(entryStore,
entryBlock.getPackageBlock(),
resValueInt.getValueType(),
resValueInt.getData());
}
XMLAttribute attribute=new XMLAttribute("name", name);
element.addAttribute(attribute);
element.setTextContent(value);
}else {
// TODO: implement bags entry decoder
return null;
}
return element;
}
private void logMessage(String msg) {
APKLogger apkLogger=apkModule.getApkLogger();
if(apkLogger!=null){

View File

@ -36,6 +36,9 @@ public class ResFile {
this.inputSource=inputSource;
this.entryBlockList=entryBlockList;
}
public List<EntryBlock> getEntryBlockList(){
return entryBlockList;
}
public String validateTypeDirectoryName(){
EntryBlock entryBlock=pickOne();
if(entryBlock==null){