mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-05-02 15:14:26 +02:00
Implement resource values XML decoder
This commit is contained in:
parent
eb9d0a4651
commit
12f4d4f9d6
@ -16,36 +16,50 @@
|
|||||||
package com.reandroid.lib.apk;
|
package com.reandroid.lib.apk;
|
||||||
|
|
||||||
import com.reandroid.lib.arsc.chunk.PackageBlock;
|
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.AndroidManifestBlock;
|
||||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock;
|
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.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.EntryStore;
|
||||||
import com.reandroid.lib.common.Frameworks;
|
import com.reandroid.lib.common.Frameworks;
|
||||||
import com.reandroid.lib.common.TableEntryStore;
|
import com.reandroid.lib.common.TableEntryStore;
|
||||||
|
import com.reandroid.xml.XMLAttribute;
|
||||||
import com.reandroid.xml.XMLDocument;
|
import com.reandroid.xml.XMLDocument;
|
||||||
|
import com.reandroid.xml.XMLElement;
|
||||||
import com.reandroid.xml.XMLException;
|
import com.reandroid.xml.XMLException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
|
|
||||||
public class ApkModuleXmlDecoder {
|
public class ApkModuleXmlDecoder {
|
||||||
private final ApkModule apkModule;
|
private final ApkModule apkModule;
|
||||||
|
private final Map<Integer, Set<ResConfig>> decodedEntries;
|
||||||
public ApkModuleXmlDecoder(ApkModule apkModule){
|
public ApkModuleXmlDecoder(ApkModule apkModule){
|
||||||
this.apkModule=apkModule;
|
this.apkModule=apkModule;
|
||||||
|
this.decodedEntries = new HashMap<>();
|
||||||
}
|
}
|
||||||
public void decodeTo(File outDir)
|
public void decodeTo(File outDir)
|
||||||
throws IOException, XMLException {
|
throws IOException, XMLException {
|
||||||
|
this.decodedEntries.clear();
|
||||||
TableEntryStore entryStore=new TableEntryStore();
|
TableEntryStore entryStore=new TableEntryStore();
|
||||||
entryStore.add(Frameworks.getAndroid());
|
entryStore.add(Frameworks.getAndroid());
|
||||||
entryStore.add(apkModule.getTableBlock());
|
TableBlock tableBlock=apkModule.getTableBlock();
|
||||||
|
entryStore.add(tableBlock);
|
||||||
decodeAndroidManifest(entryStore, outDir);
|
decodeAndroidManifest(entryStore, outDir);
|
||||||
logMessage("Decoding resource files ...");
|
logMessage("Decoding resource files ...");
|
||||||
List<ResFile> resFileList=apkModule.listResFiles();
|
List<ResFile> resFileList=apkModule.listResFiles();
|
||||||
for(ResFile resFile:resFileList){
|
for(ResFile resFile:resFileList){
|
||||||
decodeResFile(entryStore, outDir, resFile);
|
decodeResFile(entryStore, outDir, resFile);
|
||||||
}
|
}
|
||||||
|
decodeValues(entryStore, outDir, tableBlock);
|
||||||
}
|
}
|
||||||
private void decodeResFile(EntryStore entryStore, File outDir, ResFile resFile)
|
private void decodeResFile(EntryStore entryStore, File outDir, ResFile resFile)
|
||||||
throws IOException, XMLException {
|
throws IOException, XMLException {
|
||||||
@ -73,6 +87,8 @@ public class ApkModuleXmlDecoder {
|
|||||||
FileOutputStream outputStream=new FileOutputStream(file);
|
FileOutputStream outputStream=new FileOutputStream(file);
|
||||||
resFile.getInputSource().write(outputStream);
|
resFile.getInputSource().write(outputStream);
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
|
|
||||||
|
addDecodedEntry(resFile.getEntryBlockList());
|
||||||
}
|
}
|
||||||
private void decodeResXml(EntryStore entryStore, File outDir, ResFile resFile)
|
private void decodeResXml(EntryStore entryStore, File outDir, ResFile resFile)
|
||||||
throws IOException, XMLException{
|
throws IOException, XMLException{
|
||||||
@ -90,6 +106,8 @@ public class ApkModuleXmlDecoder {
|
|||||||
logVerbose("Decoding: "+path);
|
logVerbose("Decoding: "+path);
|
||||||
XMLDocument xmlDocument=resXmlBlock.decodeToXml(entryStore, packageBlock.getId());
|
XMLDocument xmlDocument=resXmlBlock.decodeToXml(entryStore, packageBlock.getId());
|
||||||
xmlDocument.save(file, true);
|
xmlDocument.save(file, true);
|
||||||
|
|
||||||
|
addDecodedEntry(resFile.getEntryBlockList());
|
||||||
}
|
}
|
||||||
private void decodeAndroidManifest(EntryStore entryStore, File outDir)
|
private void decodeAndroidManifest(EntryStore entryStore, File outDir)
|
||||||
throws IOException, XMLException {
|
throws IOException, XMLException {
|
||||||
@ -104,6 +122,90 @@ public class ApkModuleXmlDecoder {
|
|||||||
XMLDocument xmlDocument=manifestBlock.decodeToXml(entryStore, currentPackageId);
|
XMLDocument xmlDocument=manifestBlock.decodeToXml(entryStore, currentPackageId);
|
||||||
xmlDocument.save(file, true);
|
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) {
|
private void logMessage(String msg) {
|
||||||
APKLogger apkLogger=apkModule.getApkLogger();
|
APKLogger apkLogger=apkModule.getApkLogger();
|
||||||
if(apkLogger!=null){
|
if(apkLogger!=null){
|
||||||
|
@ -36,6 +36,9 @@ public class ResFile {
|
|||||||
this.inputSource=inputSource;
|
this.inputSource=inputSource;
|
||||||
this.entryBlockList=entryBlockList;
|
this.entryBlockList=entryBlockList;
|
||||||
}
|
}
|
||||||
|
public List<EntryBlock> getEntryBlockList(){
|
||||||
|
return entryBlockList;
|
||||||
|
}
|
||||||
public String validateTypeDirectoryName(){
|
public String validateTypeDirectoryName(){
|
||||||
EntryBlock entryBlock=pickOne();
|
EntryBlock entryBlock=pickOne();
|
||||||
if(entryBlock==null){
|
if(entryBlock==null){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user