mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-04-29 22:04:25 +02:00
V1.0.1
This commit is contained in:
parent
1763dfbc3c
commit
15e031fdeb
30
README.md
Normal file → Executable file
30
README.md
Normal file → Executable file
@ -7,22 +7,38 @@
|
||||
|
||||
public static void example() throws IOException {
|
||||
File inFile=new File("resources.arsc");
|
||||
BlockReader blockReader=new BlockReader(inFile);
|
||||
|
||||
TableBlock tableBlock=new TableBlock();
|
||||
tableBlock.readBytes(blockReader);
|
||||
tableBlock.readBytes(inFile);
|
||||
|
||||
// edit tableBlock as desired
|
||||
//edit tableBlock as desired, for example to change the package:
|
||||
PackageBlock packageBlock=tableBlock.getPackageArray().get(0);
|
||||
packageBlock.setPackageName("com.new.package.name");
|
||||
|
||||
//refresh to recalculate offsets
|
||||
tableBlock.refresh();
|
||||
|
||||
//save the edited table
|
||||
File outFile=new File("resources_out.arsc");
|
||||
OutputStream outputStream=new FileOutputStream(outFile, false);
|
||||
tableBlock.writeBytes(outFile);
|
||||
}
|
||||
|
||||
tableBlock.writeBytes(outputStream);
|
||||
public static void exampleManifest() throws IOException {
|
||||
File inFile=new File("AndroidManifest.xml");
|
||||
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
AndroidManifestBlock manifestBlock=new AndroidManifestBlock();
|
||||
manifestBlock.readBytes(file);
|
||||
|
||||
//edit AndroidManifest as desired, for example to change the package:
|
||||
|
||||
manifestBlock.setPackageName("com.new.package.name");
|
||||
|
||||
//refresh to recalculate offsets
|
||||
manifestBlock.refresh();
|
||||
|
||||
//save the edited table
|
||||
File outFile=new File("AndroidManifest_out.xml");
|
||||
manifestBlock.writeBytes(outFile);
|
||||
}
|
||||
|
||||
```
|
||||
|
12
build.gradle
Normal file → Executable file
12
build.gradle
Normal file → Executable file
@ -2,7 +2,7 @@
|
||||
apply plugin: 'java-library'
|
||||
|
||||
group 'com.reandroid.lib.arsc'
|
||||
version '1.0.0'
|
||||
version '1.0.1'
|
||||
|
||||
java {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
@ -18,12 +18,12 @@ if (JavaVersion.current().isJava8Compatible()) {
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
processResources {
|
||||
filesMatching('lib.properties') {
|
||||
expand('version': version)
|
||||
}
|
||||
}
|
||||
|
||||
processResources.inputs.property('version', version)
|
||||
processResources.expand('version': version)
|
||||
|
||||
|
0
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file → Executable file
0
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file → Executable file
0
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file → Executable file
0
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file → Executable file
0
gradlew.bat
vendored
Normal file → Executable file
0
gradlew.bat
vendored
Normal file → Executable file
0
settings.gradle
Normal file → Executable file
0
settings.gradle
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/BuildInfo.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/BuildInfo.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/EntryBlockArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/LibraryInfoArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/LibraryInfoArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/OffsetBlockArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/OffsetBlockArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/PackageArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/PackageArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/ResValueBagItemArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/ResValueBagItemArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/ResXmlAttributeArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/ResXmlAttributeArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/ResXmlChunkArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/ResXmlChunkArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/ResXmlIDArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/ResXmlIDArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/ResXmlStringArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/ResXmlStringArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/SpecBlockArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/SpecBlockArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/SpecStringArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/SpecStringArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/SpecTypePairArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/StringArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/StringArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/StyleArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/StyleArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/TableStringArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/TableStringArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/TypeBlockArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/TypeStringArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/array/TypeStringArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/base/Block.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/base/Block.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/base/BlockArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/base/BlockArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/base/BlockArrayCreator.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/base/BlockArrayCreator.java
Normal file → Executable file
12
src/main/java/com/reandroid/lib/arsc/base/BlockContainer.java
Normal file → Executable file
12
src/main/java/com/reandroid/lib/arsc/base/BlockContainer.java
Normal file → Executable file
@ -9,6 +9,18 @@ public abstract class BlockContainer<T extends Block> extends Block{
|
||||
public BlockContainer(){
|
||||
super();
|
||||
}
|
||||
|
||||
protected final BlockContainer getTopContainer(){
|
||||
Block parent=this;
|
||||
BlockContainer result=this;
|
||||
while (parent!=null){
|
||||
if(parent instanceof BlockContainer){
|
||||
result=(BlockContainer)parent;
|
||||
}
|
||||
parent=parent.getParent();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
protected abstract void onRefreshed();
|
||||
public final void refresh(){
|
||||
if(isNull()){
|
||||
|
0
src/main/java/com/reandroid/lib/arsc/base/BlockCounter.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/base/BlockCounter.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/base/BlockCreator.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/base/BlockCreator.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/BaseChunk.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/BaseChunk.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/BaseTypeBlock.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/BaseTypeBlock.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/ChunkType.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/ChunkType.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/LibraryBlock.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/LibraryBlock.java
Normal file → Executable file
66
src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java
Normal file → Executable file
66
src/main/java/com/reandroid/lib/arsc/chunk/PackageBlock.java
Normal file → Executable file
@ -4,7 +4,6 @@ import com.reandroid.lib.arsc.array.SpecTypePairArray;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.container.PackageLastBlocks;
|
||||
import com.reandroid.lib.arsc.container.SpecTypePair;
|
||||
import com.reandroid.lib.arsc.decoder.ResourceNameProvider;
|
||||
import com.reandroid.lib.arsc.group.EntryGroup;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.PackageName;
|
||||
@ -12,15 +11,13 @@ import com.reandroid.lib.arsc.item.ReferenceItem;
|
||||
import com.reandroid.lib.arsc.pool.SpecStringPool;
|
||||
import com.reandroid.lib.arsc.pool.TableStringPool;
|
||||
import com.reandroid.lib.arsc.pool.TypeStringPool;
|
||||
import com.reandroid.lib.arsc.value.BaseResValue;
|
||||
import com.reandroid.lib.arsc.value.EntryBlock;
|
||||
import com.reandroid.lib.arsc.value.LibraryInfo;
|
||||
import com.reandroid.lib.arsc.value.ResValueBag;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class PackageBlock extends BaseChunk implements ResourceNameProvider {
|
||||
public class PackageBlock extends BaseChunk {
|
||||
private final IntegerItem mPackageId;
|
||||
private final PackageName mPackageName;
|
||||
|
||||
@ -270,67 +267,6 @@ public class PackageBlock extends BaseChunk implements ResourceNameProvider {
|
||||
refreshKeyStrings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceFullName(int resId, boolean includePackageName) {
|
||||
EntryGroup entryGroup=getEntryGroup(resId);
|
||||
if(entryGroup==null){
|
||||
return null;
|
||||
}
|
||||
String type=entryGroup.getTypeName();
|
||||
if(type==null){
|
||||
return null;
|
||||
}
|
||||
String spec=entryGroup.getSpecName();
|
||||
if(spec==null){
|
||||
return null;
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append('@');
|
||||
if(includePackageName){
|
||||
builder.append(getPackageName());
|
||||
builder.append(':');
|
||||
}
|
||||
builder.append(type);
|
||||
builder.append('/');
|
||||
builder.append(spec);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceName(int resId, boolean includePackageName) {
|
||||
EntryGroup entryGroup=getEntryGroup(resId);
|
||||
if(entryGroup==null){
|
||||
return null;
|
||||
}
|
||||
String spec=entryGroup.getSpecName();
|
||||
if(spec==null){
|
||||
return null;
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
if(includePackageName){
|
||||
builder.append(getPackageName());
|
||||
builder.append(':');
|
||||
}
|
||||
builder.append(spec);
|
||||
return builder.toString();
|
||||
}
|
||||
@Override
|
||||
public ResValueBag getAttributeBag(int resId){
|
||||
EntryGroup entryGroup=getEntryGroup(resId);
|
||||
if(entryGroup==null){
|
||||
return null;
|
||||
}
|
||||
EntryBlock entryBlock=entryGroup.pickOne();
|
||||
if(entryBlock==null){
|
||||
return null;
|
||||
}
|
||||
BaseResValue resValue=entryBlock.getResValue();
|
||||
if(resValue instanceof ResValueBag){
|
||||
return (ResValueBag)resValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
|
0
src/main/java/com/reandroid/lib/arsc/chunk/SpecBlock.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/SpecBlock.java
Normal file → Executable file
96
src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java
Normal file → Executable file
96
src/main/java/com/reandroid/lib/arsc/chunk/TableBlock.java
Normal file → Executable file
@ -1,14 +1,16 @@
|
||||
package com.reandroid.lib.arsc.chunk;
|
||||
|
||||
import com.reandroid.lib.arsc.array.PackageArray;
|
||||
import com.reandroid.lib.arsc.decoder.ResourceNameProvider;
|
||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.pool.TableStringPool;
|
||||
import com.reandroid.lib.arsc.value.ResValueBag;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Collection;
|
||||
|
||||
public class TableBlock extends BaseChunk implements ResourceNameProvider {
|
||||
public class TableBlock extends BaseChunk {
|
||||
private final IntegerItem mPackageCount;
|
||||
private final TableStringPool mTableStringPool;
|
||||
private final PackageArray mPackageArray;
|
||||
@ -42,43 +44,34 @@ public class TableBlock extends BaseChunk implements ResourceNameProvider {
|
||||
protected void onChunkRefreshed() {
|
||||
refreshPackageCount();
|
||||
}
|
||||
@Override
|
||||
public String getResourceFullName(int resId, boolean includePackageName) {
|
||||
byte pkgId= (byte) ((resId>>24)&0xFF);
|
||||
if(pkgId==0){
|
||||
return null;
|
||||
}
|
||||
PackageBlock packageBlock=getPackageBlockById(pkgId);
|
||||
if(packageBlock!=null){
|
||||
return packageBlock.getResourceFullName(resId, includePackageName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceName(int resId, boolean includePackageName) {
|
||||
byte pkgId= (byte) ((resId>>24)&0xFF);
|
||||
if(pkgId==0){
|
||||
return null;
|
||||
public int onWriteBytes(OutputStream stream) throws IOException{
|
||||
int result=super.onWriteBytes(stream);
|
||||
stream.flush();
|
||||
stream.close();
|
||||
return result;
|
||||
}
|
||||
PackageBlock packageBlock=getPackageBlockById(pkgId);
|
||||
if(packageBlock!=null){
|
||||
return packageBlock.getResourceName(resId, includePackageName);
|
||||
public void readBytes(File file) throws IOException{
|
||||
BlockReader reader=new BlockReader(file);
|
||||
super.readBytes(reader);
|
||||
}
|
||||
return null;
|
||||
public void readBytes(InputStream inputStream) throws IOException{
|
||||
BlockReader reader=new BlockReader(inputStream);
|
||||
super.readBytes(reader);
|
||||
}
|
||||
@Override
|
||||
public ResValueBag getAttributeBag(int resId){
|
||||
byte pkgId= (byte) ((resId>>24)&0xFF);
|
||||
if(pkgId==0){
|
||||
return null;
|
||||
public final int writeBytes(File file) throws IOException{
|
||||
if(isNull()){
|
||||
throw new IOException("Can NOT save null block");
|
||||
}
|
||||
PackageBlock packageBlock=getPackageBlockById(pkgId);
|
||||
if(packageBlock!=null){
|
||||
return packageBlock.getAttributeBag(resId);
|
||||
File dir=file.getParentFile();
|
||||
if(dir!=null && !dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
return null;
|
||||
OutputStream outputStream=new FileOutputStream(file);
|
||||
return super.writeBytes(outputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
@ -89,4 +82,43 @@ public class TableBlock extends BaseChunk implements ResourceNameProvider {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
public static boolean isResTableBlock(File file){
|
||||
if(file==null){
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
InputStream inputStream=new FileInputStream(file);
|
||||
return isResTableBlock(inputStream);
|
||||
} catch (FileNotFoundException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static boolean isResTableBlock(InputStream inputStream){
|
||||
try {
|
||||
HeaderBlock headerBlock= BlockReader.readHeaderBlock(inputStream);
|
||||
return isResTableBlock(headerBlock);
|
||||
} catch (IOException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static boolean isResTableBlock(BlockReader blockReader){
|
||||
if(blockReader==null){
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
HeaderBlock headerBlock = blockReader.readHeaderBlock();
|
||||
return isResTableBlock(headerBlock);
|
||||
} catch (IOException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static boolean isResTableBlock(HeaderBlock headerBlock){
|
||||
if(headerBlock==null){
|
||||
return false;
|
||||
}
|
||||
ChunkType chunkType=headerBlock.getChunkType();
|
||||
return chunkType==ChunkType.TABLE;
|
||||
}
|
||||
|
||||
}
|
||||
|
0
src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/TypeBlock.java
Normal file → Executable file
@ -0,0 +1,166 @@
|
||||
package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.item.ResXmlString;
|
||||
import com.reandroid.lib.arsc.pool.ResXmlStringPool;
|
||||
import com.reandroid.lib.arsc.value.ValueType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AndroidManifestBlock extends ResXmlBlock{
|
||||
public AndroidManifestBlock(){
|
||||
super();
|
||||
}
|
||||
public List<String> getPermissions(){
|
||||
List<String> results=new ArrayList<>();
|
||||
ResXmlElement manifestElement=getManifestElement();
|
||||
if(manifestElement==null){
|
||||
return results;
|
||||
}
|
||||
List<ResXmlElement> permissionList = manifestElement.searchElementsByTagName(TAG_uses_permission);
|
||||
for(ResXmlElement permission:permissionList){
|
||||
ResXmlAttribute nameAttr = permission.searchAttributeByName(ATTR_android_name);
|
||||
if(nameAttr==null){
|
||||
continue;
|
||||
}
|
||||
String val=nameAttr.getValueString();
|
||||
if(val!=null){
|
||||
results.add(val);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
public String getPackageName(){
|
||||
return getManifestAttributeString(ATTR_PACKAGE);
|
||||
}
|
||||
public boolean setPackageName(String packageName){
|
||||
return setManifestAttributeString(ATTR_PACKAGE, packageName);
|
||||
}
|
||||
public Integer getCompileSdkVersion(){
|
||||
return getManifestAttributeInt(ATTR_compileSdkVersion);
|
||||
}
|
||||
public boolean setCompileSdkVersion(int val){
|
||||
return setManifestAttributeInt(ATTR_compileSdkVersion, val);
|
||||
}
|
||||
public String getCompileSdkVersionCodename(){
|
||||
return getManifestAttributeString(ATTR_compileSdkVersionCodename);
|
||||
}
|
||||
public boolean setCompileSdkVersionCodename(String val){
|
||||
return setManifestAttributeString(ATTR_compileSdkVersionCodename, val);
|
||||
}
|
||||
public Integer getVersionCode(){
|
||||
return getManifestAttributeInt(ATTR_versionCode);
|
||||
}
|
||||
public boolean setVersionCode(int val){
|
||||
return setManifestAttributeInt(ATTR_versionCode, val);
|
||||
}
|
||||
public String getVersionName(){
|
||||
return getManifestAttributeString(ATTR_versionName);
|
||||
}
|
||||
public boolean setVersionName(String packageName){
|
||||
return setManifestAttributeString(ATTR_versionName, packageName);
|
||||
}
|
||||
private String getManifestAttributeString(String name){
|
||||
ResXmlElement manifestElement=getManifestElement();
|
||||
if(manifestElement==null){
|
||||
return null;
|
||||
}
|
||||
ResXmlAttribute attribute= manifestElement.searchAttributeByName(name);
|
||||
if(attribute==null){
|
||||
return null;
|
||||
}
|
||||
int raw=attribute.getRawValue();
|
||||
ResXmlStringPool pool = getStringPool();
|
||||
ResXmlString resXmlString = pool.get(raw);
|
||||
if(resXmlString==null){
|
||||
return null;
|
||||
}
|
||||
return resXmlString.getHtml();
|
||||
}
|
||||
private boolean setManifestAttributeString(String name, String value){
|
||||
ResXmlElement manifestElement=getManifestElement();
|
||||
if(manifestElement==null){
|
||||
return false;
|
||||
}
|
||||
ResXmlAttribute attribute= manifestElement.searchAttributeByName(name);
|
||||
if(attribute==null){
|
||||
return false;
|
||||
}
|
||||
attribute.setValueType(ValueType.STRING);
|
||||
ResXmlString resXmlString=attribute.setValueString(value);
|
||||
return resXmlString!=null;
|
||||
}
|
||||
private boolean setManifestAttributeInt(String name, int value){
|
||||
ResXmlElement manifestElement=getManifestElement();
|
||||
if(manifestElement==null){
|
||||
return false;
|
||||
}
|
||||
ResXmlAttribute attribute= manifestElement.searchAttributeByName(name);
|
||||
if(attribute==null){
|
||||
return false;
|
||||
}
|
||||
attribute.setValueType(ValueType.INT_DEC);
|
||||
attribute.setValueString(String.valueOf(value));
|
||||
attribute.setRawValue(value);
|
||||
return true;
|
||||
}
|
||||
private Integer getManifestAttributeInt(String name){
|
||||
ResXmlElement manifestElement=getManifestElement();
|
||||
if(manifestElement==null){
|
||||
return null;
|
||||
}
|
||||
ResXmlAttribute attribute= manifestElement.searchAttributeByName(name);
|
||||
if(attribute==null){
|
||||
return null;
|
||||
}
|
||||
return attribute.getRawValue();
|
||||
}
|
||||
private ResXmlElement getManifestElement(){
|
||||
ResXmlElement manifestElement=getResXmlElement();
|
||||
if(manifestElement==null){
|
||||
return null;
|
||||
}
|
||||
if(!TAG_MANIFEST.equals(manifestElement.getTag())){
|
||||
return null;
|
||||
}
|
||||
return manifestElement;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append("{");
|
||||
builder.append(ATTR_PACKAGE).append("=").append(getPackageName());
|
||||
builder.append(", ").append(ATTR_versionCode).append("=").append(getVersionCode());
|
||||
builder.append(", ").append(ATTR_versionName).append("=").append(getVersionName());
|
||||
builder.append(", ").append(ATTR_compileSdkVersion).append("=").append(getCompileSdkVersion());
|
||||
builder.append(", ").append(ATTR_compileSdkVersionCodename).append("=").append(getCompileSdkVersionCodename());
|
||||
|
||||
List<String> allPermissions=getPermissions();
|
||||
builder.append(", PERMISSIONS[");
|
||||
boolean appendOnce=false;
|
||||
for(String permissions:allPermissions){
|
||||
if(appendOnce){
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append(permissions);
|
||||
appendOnce=true;
|
||||
}
|
||||
builder.append("]");
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
private static final String TAG_MANIFEST="manifest";
|
||||
private static final String TAG_uses_permission="uses-permission";
|
||||
|
||||
private static final String ATTR_compileSdkVersion="compileSdkVersion";
|
||||
private static final String ATTR_compileSdkVersionCodename="compileSdkVersionCodename";
|
||||
private static final String ATTR_installLocation="installLocation";
|
||||
private static final String ATTR_PACKAGE="package";
|
||||
private static final String ATTR_platformBuildVersionCode="platformBuildVersionCode";
|
||||
private static final String ATTR_platformBuildVersionName="platformBuildVersionName";
|
||||
private static final String ATTR_versionCode="versionCode";
|
||||
private static final String ATTR_versionName="versionName";
|
||||
|
||||
private static final String ATTR_android_name="name";
|
||||
}
|
17
src/main/java/com/reandroid/lib/arsc/chunk/xml/BaseXmlChunk.java
Normal file → Executable file
17
src/main/java/com/reandroid/lib/arsc/chunk/xml/BaseXmlChunk.java
Normal file → Executable file
@ -57,6 +57,15 @@ public class BaseXmlChunk extends BaseChunk {
|
||||
public int getStringReference(){
|
||||
return mStringReference.get();
|
||||
}
|
||||
public ResXmlString setString(String str){
|
||||
ResXmlStringPool pool = getStringPool();
|
||||
if(pool==null){
|
||||
return null;
|
||||
}
|
||||
ResXmlString xmlString = pool.getOrCreate(str);
|
||||
setStringReference(xmlString.getIndex());
|
||||
return xmlString;
|
||||
}
|
||||
|
||||
|
||||
public ResXmlStringPool getStringPool(){
|
||||
@ -82,6 +91,13 @@ public class BaseXmlChunk extends BaseChunk {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
ResXmlString getOrCreateResXmlString(String str){
|
||||
ResXmlStringPool stringPool=getStringPool();
|
||||
if(stringPool!=null){
|
||||
return stringPool.getOrCreate(str);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
String getString(int ref){
|
||||
ResXmlString xmlString=getResXmlString(ref);
|
||||
if(xmlString!=null){
|
||||
@ -96,6 +112,7 @@ public class BaseXmlChunk extends BaseChunk {
|
||||
public String getUri(){
|
||||
return getString(getNamespaceReference());
|
||||
}
|
||||
|
||||
public ResXmlElement getParentResXmlElement(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
|
92
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java
Normal file → Executable file
92
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlAttribute.java
Normal file → Executable file
@ -17,10 +17,10 @@ public class ResXmlAttribute extends FixedBlockContainer {
|
||||
private final IntegerItem mRawValue;
|
||||
public ResXmlAttribute() {
|
||||
super(7);
|
||||
mNamespaceReference =new IntegerItem();
|
||||
mNamespaceReference =new IntegerItem(-1);
|
||||
mNameReference =new IntegerItem();
|
||||
mValueStringReference =new IntegerItem();
|
||||
mNameType=new ShortItem();
|
||||
mNameType=new ShortItem((short) 0x0008);
|
||||
mReserved =new ByteItem();
|
||||
mValueTypeByte=new ByteItem();
|
||||
mRawValue=new IntegerItem();
|
||||
@ -75,6 +75,11 @@ public class ResXmlAttribute extends FixedBlockContainer {
|
||||
public ValueType getValueType(){
|
||||
return ValueType.valueOf(getValueTypeByte());
|
||||
}
|
||||
public void setValueType(ValueType valueType){
|
||||
if(valueType!=null){
|
||||
setValueTypeByte(valueType.getByte());
|
||||
}
|
||||
}
|
||||
public String getFullName(){
|
||||
String name=getName();
|
||||
if(name==null){
|
||||
@ -100,12 +105,86 @@ public class ResXmlAttribute extends FixedBlockContainer {
|
||||
}
|
||||
return startNamespace.getPrefix();
|
||||
}
|
||||
public ResXmlStartNamespace getStartNamespace(){
|
||||
ResXmlElement xmlElement=getParentResXmlElement();
|
||||
if(xmlElement==null){
|
||||
return null;
|
||||
}
|
||||
return xmlElement.getStartNamespaceByUriRef(getNamespaceReference());
|
||||
}
|
||||
public String getValueString(){
|
||||
return getString(getValueStringReference());
|
||||
}
|
||||
ResXmlString setValueString(String str){
|
||||
ResXmlString resXmlString=getOrCreateResXmlString(str);
|
||||
if(resXmlString==null){
|
||||
return null;
|
||||
}
|
||||
int ref=resXmlString.getIndex();
|
||||
setValueStringReference(ref);
|
||||
if(getValueType()==ValueType.STRING){
|
||||
setRawValue(ref);
|
||||
}
|
||||
return resXmlString;
|
||||
}
|
||||
public int getNameResourceID(){
|
||||
return getResourceId(getNameReference());
|
||||
}
|
||||
public boolean setName(String name, int resourceId){
|
||||
ResXmlStringPool stringPool=getStringPool();
|
||||
if(stringPool==null){
|
||||
return false;
|
||||
}
|
||||
String old=getName();
|
||||
if(resourceId==0){
|
||||
if(name.equals(old)){
|
||||
return false;
|
||||
}
|
||||
ResXmlString resXmlString=stringPool.getOrCreate(name);
|
||||
setNameReference(resXmlString.getIndex());
|
||||
return true;
|
||||
}
|
||||
ResXmlIDMap xmlIDMap=getResXmlIDMap();
|
||||
if(xmlIDMap==null){
|
||||
return false;
|
||||
}
|
||||
int oldId=getNameResourceID();
|
||||
if(oldId==resourceId){
|
||||
if(name.equals(old)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ResXmlID resXmlID=xmlIDMap.getByResId(resourceId);
|
||||
if(resXmlID!=null){
|
||||
int ref=resXmlID.getIndex();
|
||||
ResXmlString idName=stringPool.get(ref);
|
||||
if(idName != null){
|
||||
if(name.equals(idName.getHtml())){
|
||||
setNameReference(ref);
|
||||
}else {
|
||||
idName.set(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
int stringsCount=stringPool.countStrings();
|
||||
int idCount=xmlIDMap.getResXmlIDArray().childesCount();
|
||||
if(idCount>stringsCount){
|
||||
xmlIDMap.addResourceId(idCount, resourceId);;
|
||||
stringPool.getStringsArray().ensureSize(idCount+1);
|
||||
ResXmlString resXmlString=stringPool.get(idCount);
|
||||
resXmlString.set(name);
|
||||
setNameReference(idCount);
|
||||
return true;
|
||||
}
|
||||
xmlIDMap.addResourceId(stringsCount, resourceId);
|
||||
stringPool.getStringsArray().ensureSize(stringsCount+1);
|
||||
ResXmlString resXmlString=stringPool.get(stringsCount);
|
||||
resXmlString.set(name);
|
||||
setNameReference(stringsCount);
|
||||
return true;
|
||||
|
||||
}
|
||||
private int getResourceId(int ref){
|
||||
if(ref<0){
|
||||
return 0;
|
||||
@ -139,6 +218,13 @@ public class ResXmlAttribute extends FixedBlockContainer {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private ResXmlString getOrCreateResXmlString(String str){
|
||||
ResXmlStringPool stringPool=getStringPool();
|
||||
if(stringPool!=null){
|
||||
return stringPool.getOrCreate(str);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private ResXmlStringPool getStringPool(){
|
||||
ResXmlElement xmlElement=getParentResXmlElement();
|
||||
if(xmlElement!=null){
|
||||
@ -153,7 +239,7 @@ public class ResXmlAttribute extends FixedBlockContainer {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private ResXmlElement getParentResXmlElement(){
|
||||
public ResXmlElement getParentResXmlElement(){
|
||||
Block parent=getParent();
|
||||
while (parent!=null){
|
||||
if(parent instanceof ResXmlElement){
|
||||
|
146
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlBlock.java
Normal file → Executable file
146
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlBlock.java
Normal file → Executable file
@ -2,20 +2,94 @@ package com.reandroid.lib.arsc.chunk.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.chunk.BaseChunk;
|
||||
import com.reandroid.lib.arsc.container.SingleBlockContainer;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.pool.ResXmlStringPool;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ResXmlBlock extends BaseChunk {
|
||||
private final ResXmlStringPool mResXmlStringPool;
|
||||
private final ResXmlIDMap mResXmlIDMap;
|
||||
private final ResXmlElement mResXmlElement;
|
||||
private ResXmlElement mResXmlElement;
|
||||
private final SingleBlockContainer<ResXmlElement> mResXmlElementContainer;
|
||||
public ResXmlBlock() {
|
||||
super(ChunkType.XML,3);
|
||||
this.mResXmlStringPool=new ResXmlStringPool(true);
|
||||
this.mResXmlIDMap=new ResXmlIDMap();
|
||||
this.mResXmlElement=new ResXmlElement();
|
||||
this.mResXmlElementContainer=new SingleBlockContainer<>();
|
||||
this.mResXmlElementContainer.setItem(mResXmlElement);
|
||||
addChild(mResXmlStringPool);
|
||||
addChild(mResXmlIDMap);
|
||||
addChild(mResXmlElement);
|
||||
addChild(mResXmlElementContainer);
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
HeaderBlock headerBlock=reader.readHeaderBlock();
|
||||
if(headerBlock==null){
|
||||
return;
|
||||
}
|
||||
BlockReader chunkReader=reader.create(reader.getPosition(), headerBlock.getChunkSize());
|
||||
ChunkType chunkType=headerBlock.getChunkType();
|
||||
if(chunkType==ChunkType.XML){
|
||||
getHeaderBlock().readBytes(chunkReader);
|
||||
}else {
|
||||
throw new IOException("Not ResXmlBlock: "+reader+", Header="+headerBlock);
|
||||
}
|
||||
while (chunkReader.isAvailable()){
|
||||
boolean readOk=readNext(chunkReader);
|
||||
if(!readOk){
|
||||
break;
|
||||
}
|
||||
}
|
||||
reader.offset(headerBlock.getChunkSize());
|
||||
chunkReader.close();
|
||||
onChunkLoaded();
|
||||
}
|
||||
private boolean readNext(BlockReader reader) throws IOException {
|
||||
if(!reader.isAvailable()){
|
||||
return false;
|
||||
}
|
||||
int position=reader.getPosition();
|
||||
HeaderBlock headerBlock=reader.readHeaderBlock();
|
||||
if(headerBlock==null){
|
||||
return false;
|
||||
}
|
||||
ChunkType chunkType=headerBlock.getChunkType();
|
||||
if(chunkType==ChunkType.STRING){
|
||||
mResXmlStringPool.readBytes(reader);
|
||||
}else if(chunkType==ChunkType.XML_RESOURCE_MAP){
|
||||
mResXmlIDMap.readBytes(reader);
|
||||
}else if(isElementChunk(chunkType)){
|
||||
mResXmlElementContainer.readBytes(reader);
|
||||
return reader.isAvailable();
|
||||
}else {
|
||||
throw new IOException("Unexpected chunk "+headerBlock);
|
||||
}
|
||||
return reader.isAvailable() && position!=reader.getPosition();
|
||||
}
|
||||
private boolean isElementChunk(ChunkType chunkType){
|
||||
if(chunkType==ChunkType.XML_START_ELEMENT){
|
||||
return true;
|
||||
}
|
||||
if(chunkType==ChunkType.XML_END_ELEMENT){
|
||||
return true;
|
||||
}
|
||||
if(chunkType==ChunkType.XML_START_NAMESPACE){
|
||||
return true;
|
||||
}
|
||||
if(chunkType==ChunkType.XML_END_NAMESPACE){
|
||||
return true;
|
||||
}
|
||||
if(chunkType==ChunkType.XML_CDATA){
|
||||
return true;
|
||||
}
|
||||
if(chunkType==ChunkType.XML_LAST_CHUNK){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public ResXmlStringPool getStringPool(){
|
||||
return mResXmlStringPool;
|
||||
@ -26,8 +100,76 @@ public class ResXmlBlock extends BaseChunk {
|
||||
public ResXmlElement getResXmlElement(){
|
||||
return mResXmlElement;
|
||||
}
|
||||
public void setResXmlElement(ResXmlElement resXmlElement){
|
||||
this.mResXmlElement=resXmlElement;
|
||||
this.mResXmlElementContainer.setItem(resXmlElement);
|
||||
}
|
||||
@Override
|
||||
protected void onChunkRefreshed() {
|
||||
|
||||
}
|
||||
@Override
|
||||
public int onWriteBytes(OutputStream stream) throws IOException{
|
||||
int result=super.onWriteBytes(stream);
|
||||
stream.flush();
|
||||
stream.close();
|
||||
return result;
|
||||
}
|
||||
public void readBytes(File file) throws IOException{
|
||||
BlockReader reader=new BlockReader(file);
|
||||
super.readBytes(reader);
|
||||
}
|
||||
public void readBytes(InputStream inputStream) throws IOException{
|
||||
BlockReader reader=new BlockReader(inputStream);
|
||||
super.readBytes(reader);
|
||||
}
|
||||
public final int writeBytes(File file) throws IOException{
|
||||
if(isNull()){
|
||||
throw new IOException("Can NOT save null block");
|
||||
}
|
||||
File dir=file.getParentFile();
|
||||
if(dir!=null && !dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
OutputStream outputStream=new FileOutputStream(file);
|
||||
return super.writeBytes(outputStream);
|
||||
}
|
||||
|
||||
public static boolean isResXmlBlock(File file){
|
||||
if(file==null){
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
InputStream inputStream=new FileInputStream(file);
|
||||
return isResXmlBlock(inputStream);
|
||||
} catch (FileNotFoundException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static boolean isResXmlBlock(InputStream inputStream){
|
||||
try {
|
||||
HeaderBlock headerBlock=BlockReader.readHeaderBlock(inputStream);
|
||||
return isResXmlBlock(headerBlock);
|
||||
} catch (IOException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static boolean isResXmlBlock(BlockReader blockReader){
|
||||
if(blockReader==null){
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
HeaderBlock headerBlock = blockReader.readHeaderBlock();
|
||||
return isResXmlBlock(headerBlock);
|
||||
} catch (IOException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static boolean isResXmlBlock(HeaderBlock headerBlock){
|
||||
if(headerBlock==null){
|
||||
return false;
|
||||
}
|
||||
ChunkType chunkType=headerBlock.getChunkType();
|
||||
return chunkType==ChunkType.XML;
|
||||
}
|
||||
}
|
||||
|
114
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java
Normal file → Executable file
114
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlElement.java
Normal file → Executable file
@ -7,6 +7,7 @@ import com.reandroid.lib.arsc.container.FixedBlockContainer;
|
||||
import com.reandroid.lib.arsc.container.SingleBlockContainer;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.ResXmlString;
|
||||
import com.reandroid.lib.arsc.pool.ResXmlStringPool;
|
||||
|
||||
|
||||
@ -38,6 +39,42 @@ public class ResXmlElement extends FixedBlockContainer {
|
||||
addChild(4, mEndElementContainer);
|
||||
addChild(5, mEndNamespaceList);
|
||||
}
|
||||
public List<ResXmlElement> searchElementsByTagName(String name){
|
||||
List<ResXmlElement> results=new ArrayList<>();
|
||||
if(name==null){
|
||||
return results;
|
||||
}
|
||||
for(ResXmlElement child:listElements()){
|
||||
if(name.equals(child.getTag())||name.equals(child.getTagName())){
|
||||
results.add(child);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
public ResXmlAttribute searchAttributeByName(String name){
|
||||
ResXmlStartElement startElement=getStartElement();
|
||||
if(startElement!=null){
|
||||
return startElement.searchAttributeByName(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public void setTag(String tag){
|
||||
ResXmlStringPool pool = getStringPool();
|
||||
if(pool==null){
|
||||
return;
|
||||
}
|
||||
ensureStartEndElement();
|
||||
ResXmlStartElement start=getStartElement();
|
||||
String prefix=null;
|
||||
String name=tag;
|
||||
int i=tag.lastIndexOf(':');
|
||||
if(i>=0){
|
||||
prefix=tag.substring(0,i);
|
||||
i++;
|
||||
name=tag.substring(i);
|
||||
}
|
||||
start.setName(name);
|
||||
}
|
||||
public String getTagName(){
|
||||
ResXmlStartElement startElement=getStartElement();
|
||||
if(startElement!=null){
|
||||
@ -45,6 +82,27 @@ public class ResXmlElement extends FixedBlockContainer {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public String getTag(){
|
||||
ResXmlStartElement startElement=getStartElement();
|
||||
if(startElement!=null){
|
||||
return startElement.getName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public String getTagUri(){
|
||||
ResXmlStartElement startElement=getStartElement();
|
||||
if(startElement!=null){
|
||||
return startElement.getUri();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public String getTagPrefix(){
|
||||
ResXmlStartElement startElement=getStartElement();
|
||||
if(startElement!=null){
|
||||
return startElement.getPrefix();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public Collection<ResXmlAttribute> listResXmlAttributes(){
|
||||
ResXmlStartElement startElement=getStartElement();
|
||||
if(startElement!=null){
|
||||
@ -182,6 +240,22 @@ public class ResXmlElement extends FixedBlockContainer {
|
||||
start.setResXmlEndElement(end);
|
||||
end.setResXmlStartElement(start);
|
||||
}
|
||||
private void ensureStartEndElement(){
|
||||
ResXmlStartElement start=getStartElement();
|
||||
ResXmlEndElement end=getEndElement();
|
||||
if(start!=null && end!=null){
|
||||
return;
|
||||
}
|
||||
if(start==null){
|
||||
start=new ResXmlStartElement();
|
||||
setStartElement(start);
|
||||
}
|
||||
if(end==null){
|
||||
end=new ResXmlEndElement();
|
||||
setEndElement(end);
|
||||
}
|
||||
linkStartEndElement();
|
||||
}
|
||||
private void linkStartEndNameSpaces(){
|
||||
if(!isNamespaceBalanced()){
|
||||
return;
|
||||
@ -227,9 +301,9 @@ public class ResXmlElement extends FixedBlockContainer {
|
||||
unBalancedFinish(reader);
|
||||
}else {
|
||||
readBytes(reader);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
linkStartEnd();
|
||||
onFinishedRead(reader, headerBlock);
|
||||
}
|
||||
@ -245,7 +319,14 @@ public class ResXmlElement extends FixedBlockContainer {
|
||||
|
||||
}
|
||||
private void onFinishedUnexpected(BlockReader reader) throws IOException{
|
||||
throw new IOException("Unexpected finish reading: "+reader.toString());
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append("Unexpected finish reading: reader=").append(reader.toString());
|
||||
HeaderBlock header = reader.readHeaderBlock();
|
||||
if(header!=null){
|
||||
builder.append(", next header=");
|
||||
builder.append(header.toString());
|
||||
}
|
||||
throw new IOException(builder.toString());
|
||||
}
|
||||
private void onStartElement(BlockReader reader) throws IOException{
|
||||
if(hasStartElement()){
|
||||
@ -298,9 +379,25 @@ public class ResXmlElement extends FixedBlockContainer {
|
||||
throw new IOException("Unbalanced namespace: start="
|
||||
+mStartNamespaceList.size()+", end="+mEndNamespaceList.size());
|
||||
}
|
||||
|
||||
if(!isElementBalanced()){
|
||||
throw new IOException("Unbalanced element: hasStart="
|
||||
+hasStartElement()+", hasEnd="+hasEndElement());
|
||||
// Should not happen unless corrupted file, auto corrected above
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append("Unbalanced element: start=");
|
||||
ResXmlStartElement startElement=getStartElement();
|
||||
if(startElement!=null){
|
||||
builder.append(startElement);
|
||||
}else {
|
||||
builder.append("null");
|
||||
}
|
||||
builder.append(", end=");
|
||||
ResXmlEndElement endElement=getEndElement();
|
||||
if(endElement!=null){
|
||||
builder.append(endElement);
|
||||
}else {
|
||||
builder.append("null");
|
||||
}
|
||||
throw new IOException(builder.toString());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
@ -324,4 +421,13 @@ public class ResXmlElement extends FixedBlockContainer {
|
||||
}
|
||||
return "NULL";
|
||||
}
|
||||
static ResXmlElement newResXmlElement(String tag){
|
||||
ResXmlElement resXmlElement=new ResXmlElement();
|
||||
ResXmlStartElement startElement=new ResXmlStartElement();
|
||||
resXmlElement.setStartElement(startElement);
|
||||
ResXmlEndElement endElement=new ResXmlEndElement();
|
||||
resXmlElement.setEndElement(endElement);
|
||||
resXmlElement.setTag(tag);
|
||||
return resXmlElement;
|
||||
}
|
||||
}
|
||||
|
0
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlEndElement.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlEndElement.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlEndNamespace.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlEndNamespace.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlIDMap.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlIDMap.java
Normal file → Executable file
15
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java
Normal file → Executable file
15
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartElement.java
Normal file → Executable file
@ -3,6 +3,7 @@ package com.reandroid.lib.arsc.chunk.xml;
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.array.ResXmlAttributeArray;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.ResXmlString;
|
||||
import com.reandroid.lib.arsc.item.ShortItem;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -31,6 +32,17 @@ public class ResXmlStartElement extends BaseXmlChunk {
|
||||
addChild(mClassAttribute);
|
||||
addChild(mAttributeArray);
|
||||
}
|
||||
public ResXmlAttribute searchAttributeByName(String name){
|
||||
if(name==null){
|
||||
return null;
|
||||
}
|
||||
for(ResXmlAttribute attribute:listResXmlAttributes()){
|
||||
if(name.equals(attribute.getFullName()) || name.equals(attribute.getName())){
|
||||
return attribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public String getTagName(){
|
||||
String prefix=getPrefix();
|
||||
String name=getName();
|
||||
@ -39,6 +51,9 @@ public class ResXmlStartElement extends BaseXmlChunk {
|
||||
}
|
||||
return prefix+":"+name;
|
||||
}
|
||||
public void setName(String name){
|
||||
setString(name);
|
||||
}
|
||||
public Collection<ResXmlAttribute> listResXmlAttributes(){
|
||||
return getResXmlAttributeArray().listItems();
|
||||
}
|
||||
|
0
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartNamespace.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlStartNamespace.java
Normal file → Executable file
10
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlText.java
Normal file → Executable file
10
src/main/java/com/reandroid/lib/arsc/chunk/xml/ResXmlText.java
Normal file → Executable file
@ -3,6 +3,7 @@ package com.reandroid.lib.arsc.chunk.xml;
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.item.IntegerItem;
|
||||
import com.reandroid.lib.arsc.item.ResXmlString;
|
||||
import com.reandroid.lib.arsc.pool.ResXmlStringPool;
|
||||
|
||||
public class ResXmlText extends BaseXmlChunk {
|
||||
private final IntegerItem mReserved;
|
||||
@ -24,6 +25,15 @@ public class ResXmlText extends BaseXmlChunk {
|
||||
public void setTextReference(int ref){
|
||||
setNamespaceReference(ref);
|
||||
}
|
||||
public void setText(String text){
|
||||
ResXmlStringPool stringPool=getStringPool();
|
||||
if(stringPool==null){
|
||||
return;
|
||||
}
|
||||
ResXmlString resXmlString = stringPool.getOrCreate(text);
|
||||
int ref=resXmlString.getIndex();
|
||||
setTextReference(ref);
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
String txt=getText();
|
||||
|
0
src/main/java/com/reandroid/lib/arsc/container/BlockList.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/BlockList.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/ExpandableBlockContainer.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/ExpandableBlockContainer.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/FixedBlockContainer.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/FixedBlockContainer.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/PackageLastBlocks.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/PackageLastBlocks.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/ResValueContainer.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/ResValueContainer.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/SingleBlockContainer.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/SingleBlockContainer.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/container/SpecTypePair.java
Normal file → Executable file
@ -1,85 +0,0 @@
|
||||
package com.reandroid.lib.arsc.decoder;
|
||||
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
import com.reandroid.lib.arsc.value.ResValueBag;
|
||||
import com.reandroid.lib.arsc.value.ResValueBagItem;
|
||||
import com.reandroid.lib.arsc.value.ValueType;
|
||||
|
||||
public abstract class ResDecoder<InputBlock extends Block, Output> implements ResourceNameProvider{
|
||||
private final ResourceNameStore resourceNameStore;
|
||||
public ResDecoder(ResourceNameStore store){
|
||||
this.resourceNameStore=store;
|
||||
}
|
||||
public String decodeAttribute(int attrResId, int rawValue){
|
||||
ResValueBag valueBag=getAttributeBag(attrResId);
|
||||
if(valueBag==null){
|
||||
return null;
|
||||
}
|
||||
ResValueBagItem[] bagItems=valueBag.getBagItems();
|
||||
if(bagItems==null||bagItems.length==0){
|
||||
return null;
|
||||
}
|
||||
int len=bagItems.length;
|
||||
ResValueBagItem firstAttrChild=bagItems[0];
|
||||
if(len==1){
|
||||
return null;
|
||||
}
|
||||
if(isFlagAttr(firstAttrChild)){
|
||||
for(int i=1;i<len;i++){
|
||||
ResValueBagItem bagItem=bagItems[i];
|
||||
int data=bagItem.getData();
|
||||
if(data!=rawValue){
|
||||
continue;
|
||||
}
|
||||
int id=bagItem.getId();
|
||||
return getResourceName(id, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
boolean appendOnce=false;
|
||||
for(int i=1;i<len;i++){
|
||||
ResValueBagItem bagItem=bagItems[i];
|
||||
int data=bagItem.getData();
|
||||
if((data&rawValue)==0){
|
||||
continue;
|
||||
}
|
||||
int id=bagItem.getId();
|
||||
String name=getResourceName(id, false);
|
||||
if(appendOnce){
|
||||
builder.append("|");
|
||||
}
|
||||
builder.append(name);
|
||||
builder.append(data);
|
||||
appendOnce=true;
|
||||
}
|
||||
if(!appendOnce){
|
||||
return null;
|
||||
}
|
||||
builder.append(":");
|
||||
builder.append(rawValue);
|
||||
builder.append(firstAttrChild.getValueType());
|
||||
return builder.toString();
|
||||
}
|
||||
private boolean isFlagAttr(ResValueBagItem firstAttrChild){
|
||||
ValueType valueType=firstAttrChild.getValueType();
|
||||
return valueType==ValueType.FIRST_INT;
|
||||
}
|
||||
public ResourceNameStore getResourceNameStore() {
|
||||
return resourceNameStore;
|
||||
}
|
||||
@Override
|
||||
public String getResourceFullName(int resId, boolean includePackageName) {
|
||||
return getResourceNameStore().getResourceFullName(resId, includePackageName);
|
||||
}
|
||||
@Override
|
||||
public String getResourceName(int resId, boolean includePackageName) {
|
||||
return getResourceNameStore().getResourceName(resId, includePackageName);
|
||||
}
|
||||
@Override
|
||||
public ResValueBag getAttributeBag(int resId) {
|
||||
return getResourceNameStore().getAttributeBag(resId);
|
||||
}
|
||||
|
||||
public abstract Output decode(byte currentPackageId, InputBlock inputBlock);
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package com.reandroid.lib.arsc.decoder;
|
||||
|
||||
import com.reandroid.lib.arsc.value.ResValueBag;
|
||||
|
||||
|
||||
public interface ResourceNameProvider {
|
||||
String getResourceFullName(int resId, boolean includePackageName);
|
||||
String getResourceName(int resId, boolean includePackageName);
|
||||
ResValueBag getAttributeBag(int resId);
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package com.reandroid.lib.arsc.decoder;
|
||||
|
||||
import com.reandroid.lib.arsc.value.ResValueBag;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class ResourceNameStore implements ResourceNameProvider {
|
||||
private final Set<ResourceNameProvider> resourceNameProviders;
|
||||
public ResourceNameStore(){
|
||||
this.resourceNameProviders=new HashSet<>();
|
||||
}
|
||||
public void addResourceNameProvider(ResourceNameProvider provider){
|
||||
if(provider!=null){
|
||||
resourceNameProviders.add(provider);
|
||||
}
|
||||
}
|
||||
public Set<ResourceNameProvider> getResourceNameProviders(){
|
||||
return resourceNameProviders;
|
||||
}
|
||||
@Override
|
||||
public String getResourceFullName(int resId, boolean includePackageName) {
|
||||
for(ResourceNameProvider provider:this.resourceNameProviders){
|
||||
String name=provider.getResourceFullName(resId, includePackageName);
|
||||
if(name!=null){
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceName(int resId, boolean includePackageName) {
|
||||
for(ResourceNameProvider provider:this.resourceNameProviders){
|
||||
String name=provider.getResourceName(resId, includePackageName);
|
||||
if(name!=null){
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public ResValueBag getAttributeBag(int resId) {
|
||||
for(ResourceNameProvider provider:this.resourceNameProviders){
|
||||
ResValueBag resValueBag=provider.getAttributeBag(resId);
|
||||
if(resValueBag!=null){
|
||||
return resValueBag;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
496
src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java
Normal file → Executable file
496
src/main/java/com/reandroid/lib/arsc/decoder/ValueDecoder.java
Normal file → Executable file
@ -1,8 +1,181 @@
|
||||
package com.reandroid.lib.arsc.decoder;
|
||||
|
||||
import com.reandroid.lib.arsc.value.ValueType;
|
||||
import com.reandroid.lib.arsc.chunk.PackageBlock;
|
||||
import com.reandroid.lib.arsc.chunk.TableBlock;
|
||||
import com.reandroid.lib.arsc.group.EntryGroup;
|
||||
import com.reandroid.lib.arsc.item.TableString;
|
||||
import com.reandroid.lib.arsc.pool.TableStringPool;
|
||||
import com.reandroid.lib.arsc.value.*;
|
||||
import com.reandroid.lib.arsc.value.attribute.AttributeBag;
|
||||
import com.reandroid.lib.common.EntryStore;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class ValueDecoder {
|
||||
|
||||
public static String decodeAttributeName(EntryStore store, PackageBlock currentPackage, int resourceId){
|
||||
EntryGroup entryGroup=searchEntryGroup(store, currentPackage, resourceId);
|
||||
if(entryGroup==null){
|
||||
return String.format("@0x%08x", resourceId);
|
||||
}
|
||||
EntryBlock entryBlock=entryGroup.pickOne();
|
||||
if(entryBlock==null){
|
||||
return String.format("@0x%08x", resourceId);
|
||||
}
|
||||
String prefix=null;
|
||||
if(currentPackage!=null){
|
||||
String name=currentPackage.getPackageName();
|
||||
String other=entryBlock.getPackageBlock().getPackageName();
|
||||
if(!name.equals(other)){
|
||||
prefix=other+":";
|
||||
}
|
||||
}
|
||||
String name=entryGroup.getSpecName();
|
||||
if(prefix!=null){
|
||||
name=prefix+name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
public static String decodeAttribute(EntryStore store, int attrResId, int rawValue){
|
||||
AttributeBag attributeBag = getAttributeBag(store, attrResId);
|
||||
if(attributeBag==null){
|
||||
return null;
|
||||
}
|
||||
return attributeBag.decodeAttributeValue(store, rawValue);
|
||||
}
|
||||
|
||||
|
||||
public static String decodeEntryValue(EntryStore store, PackageBlock currentPackage, ValueType valueType, int data){
|
||||
if(store==null || currentPackage==null){
|
||||
return null;
|
||||
}
|
||||
if(valueType==ValueType.STRING){
|
||||
return decodeIntEntryString(currentPackage, data);
|
||||
}
|
||||
boolean is_reference=false;
|
||||
boolean is_attribute=false;
|
||||
if(valueType==ValueType.REFERENCE){
|
||||
if(data==0){
|
||||
return "@null";
|
||||
}
|
||||
is_reference=true;
|
||||
}
|
||||
if(valueType==ValueType.ATTRIBUTE){
|
||||
if(data==0){
|
||||
return "?null";
|
||||
}
|
||||
is_attribute=true;
|
||||
}
|
||||
if(is_reference || is_attribute){
|
||||
String ref=buildReferenceValue(store, valueType, currentPackage.getPackageName(), data);
|
||||
if(ref!=null){
|
||||
return ref;
|
||||
}
|
||||
char atOrQues=is_reference?'@':'?';
|
||||
ref=atOrQues+toHexResourceId(data);
|
||||
return ref;
|
||||
}
|
||||
return decode(valueType, data);
|
||||
}
|
||||
public static String decodeIntEntry(EntryStore store, EntryBlock entryBlock){
|
||||
if(entryBlock==null){
|
||||
return null;
|
||||
}
|
||||
BaseResValue baseResValue = entryBlock.getResValue();
|
||||
if(!(baseResValue instanceof ResValueInt)){
|
||||
return null;
|
||||
}
|
||||
ResValueInt resValueInt=(ResValueInt)baseResValue;
|
||||
return decodeIntEntry(store, resValueInt);
|
||||
}
|
||||
public static String decodeIntEntry(EntryStore store, ResValueInt resValueInt){
|
||||
if(resValueInt==null){
|
||||
return null;
|
||||
}
|
||||
EntryBlock parentEntry=resValueInt.getEntryBlock();
|
||||
if(parentEntry==null){
|
||||
return null;
|
||||
}
|
||||
ValueType valueType=resValueInt.getValueType();
|
||||
int data=resValueInt.getData();
|
||||
return decodeIntEntry(store, parentEntry, valueType, data);
|
||||
}
|
||||
public static String decodeIntEntry(EntryStore store, ResValueBagItem bagItem){
|
||||
if(bagItem==null){
|
||||
return null;
|
||||
}
|
||||
EntryBlock parentEntry=bagItem.getEntryBlock();
|
||||
if(parentEntry==null){
|
||||
return null;
|
||||
}
|
||||
ValueType valueType=bagItem.getValueType();
|
||||
int data=bagItem.getData();
|
||||
return decodeIntEntry(store, parentEntry, valueType, data);
|
||||
}
|
||||
public static String decodeIntEntry(EntryStore store, EntryBlock parentEntry, ValueType valueType, int data){
|
||||
if(valueType==ValueType.NULL){
|
||||
return "@empty";
|
||||
}
|
||||
if(valueType==ValueType.STRING){
|
||||
return decodeIntEntryString(parentEntry, data);
|
||||
}
|
||||
boolean is_reference=false;
|
||||
boolean is_attribute=false;
|
||||
if(valueType==ValueType.REFERENCE){
|
||||
if(data==0){
|
||||
return "@null";
|
||||
}
|
||||
is_reference=true;
|
||||
}
|
||||
if(valueType==ValueType.ATTRIBUTE){
|
||||
if(data==0){
|
||||
return "?null";
|
||||
}
|
||||
is_attribute=true;
|
||||
}
|
||||
if(is_reference || is_attribute){
|
||||
String ref=buildReferenceValue(store, parentEntry, valueType, data);
|
||||
if(ref!=null){
|
||||
return ref;
|
||||
}
|
||||
char atOrQues=is_reference?'@':'?';
|
||||
ref=atOrQues+toHexResourceId(data);
|
||||
return ref;
|
||||
}
|
||||
return decode(valueType, data);
|
||||
}
|
||||
public static String buildReferenceValue(EntryStore store, EntryBlock entryBlock){
|
||||
if(entryBlock==null){
|
||||
return null;
|
||||
}
|
||||
BaseResValue baseResValue = entryBlock.getResValue();
|
||||
if(!(baseResValue instanceof ResValueInt)){
|
||||
return null;
|
||||
}
|
||||
ResValueInt resValueInt=(ResValueInt)baseResValue;
|
||||
int resourceId=resValueInt.getData();
|
||||
ValueType valueType=resValueInt.getValueType();
|
||||
return buildReferenceValue(store, entryBlock, valueType, resourceId);
|
||||
}
|
||||
public static String decode(EntryStore entryStore, int currentPackageId, int nameResourceId, ValueType valueType, int rawVal){
|
||||
String currPackageName=getPackageName(entryStore, currentPackageId);
|
||||
String result=buildReferenceValue(entryStore, valueType, currPackageName, rawVal);
|
||||
if(result!=null){
|
||||
return result;
|
||||
}
|
||||
if(valueType==ValueType.STRING){
|
||||
// Should not happen the string could be in ResXmlBlock, but if you are lazy here it goes
|
||||
return decodeString(entryStore, currentPackageId, rawVal);
|
||||
}
|
||||
if(valueType==ValueType.FIRST_INT||valueType==ValueType.INT_HEX){
|
||||
result=decodeAttribute(entryStore, nameResourceId, rawVal);
|
||||
if(result!=null){
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return decode(valueType, rawVal);
|
||||
}
|
||||
public static String decode(ValueType valueType, int data){
|
||||
if(valueType==null){
|
||||
return null;
|
||||
@ -30,6 +203,229 @@ public class ValueDecoder {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static String buildReference(String currentPackageName,
|
||||
String referredPackageName,
|
||||
char atOrQues,
|
||||
String typeName,
|
||||
String resourceName){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
if(atOrQues!=0){
|
||||
builder.append(atOrQues);
|
||||
}
|
||||
if(!isEqualString(currentPackageName, referredPackageName)){
|
||||
if(!isEmpty(currentPackageName) && !isEmpty(referredPackageName)){
|
||||
builder.append(referredPackageName);
|
||||
if(!referredPackageName.endsWith(":")){
|
||||
builder.append(':');
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!isEmpty(typeName)){
|
||||
builder.append(typeName);
|
||||
builder.append('/');
|
||||
}
|
||||
builder.append(resourceName);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String buildReferenceValue(EntryStore store, EntryBlock entryBlock, ValueType valueType, int resourceId){
|
||||
if(entryBlock==null){
|
||||
return null;
|
||||
}
|
||||
EntryGroup value=searchEntryGroup(store, entryBlock, resourceId);
|
||||
if(value==null){
|
||||
return null;
|
||||
}
|
||||
return buildReferenceValue(valueType, entryBlock, value);
|
||||
}
|
||||
private static String buildReferenceValue(ValueType valueType, EntryBlock entryBlock, EntryGroup value){
|
||||
char atOrQues;
|
||||
if(valueType==ValueType.REFERENCE){
|
||||
atOrQues='@';
|
||||
}else if(valueType==ValueType.ATTRIBUTE){
|
||||
atOrQues='?';
|
||||
}else {
|
||||
atOrQues=0;
|
||||
}
|
||||
String currentPackageName=getPackageName(entryBlock);
|
||||
String referredPackageName=getPackageName(value);
|
||||
String typeName=value.getTypeName();
|
||||
String name=value.getSpecName();
|
||||
return buildReference(currentPackageName, referredPackageName, atOrQues, typeName, name);
|
||||
}
|
||||
private static String buildReferenceValue(EntryStore entryStore, ValueType valueType, String currentPackageName, int resourceId){
|
||||
char atOrQues;
|
||||
if(valueType==ValueType.REFERENCE){
|
||||
atOrQues='@';
|
||||
}else if(valueType==ValueType.ATTRIBUTE){
|
||||
atOrQues='?';
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
EntryGroup value=null;
|
||||
if(entryStore!=null){
|
||||
value=entryStore.getEntryGroup(resourceId);
|
||||
}
|
||||
if(value==null){
|
||||
return atOrQues+toHexResourceId(resourceId);
|
||||
}
|
||||
String referredPackageName=getPackageName(value);
|
||||
String typeName=value.getTypeName();
|
||||
String name=value.getSpecName();
|
||||
return buildReference(currentPackageName, referredPackageName, atOrQues, typeName, name);
|
||||
}
|
||||
private static String getPackageName(EntryStore entryStore, int packageOrResourceId){
|
||||
if(entryStore==null || packageOrResourceId==0){
|
||||
return null;
|
||||
}
|
||||
int pkgId=(packageOrResourceId>>24)&0xFF;
|
||||
if(pkgId==0){
|
||||
pkgId=packageOrResourceId;
|
||||
}
|
||||
Collection<PackageBlock> allPkg = entryStore.getPackageBlocks((byte) pkgId);
|
||||
if(allPkg==null){
|
||||
return null;
|
||||
}
|
||||
for(PackageBlock packageBlock:allPkg){
|
||||
String name=packageBlock.getPackageName();
|
||||
if(name!=null){
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static String getPackageName(EntryGroup entryGroup){
|
||||
if(entryGroup==null){
|
||||
return null;
|
||||
}
|
||||
return getPackageName(entryGroup.pickOne());
|
||||
}
|
||||
private static String getPackageName(EntryBlock entryBlock){
|
||||
if(entryBlock==null){
|
||||
return null;
|
||||
}
|
||||
PackageBlock packageBlock=entryBlock.getPackageBlock();
|
||||
if(packageBlock==null){
|
||||
return null;
|
||||
}
|
||||
return packageBlock.getPackageName();
|
||||
}
|
||||
private static EntryGroup searchEntryGroup(EntryStore store, EntryBlock entryBlock, int resourceId){
|
||||
EntryGroup entryGroup=searchEntryGroup(entryBlock, resourceId);
|
||||
if(entryGroup!=null){
|
||||
return entryGroup;
|
||||
}
|
||||
if(store==null){
|
||||
return null;
|
||||
}
|
||||
return store.getEntryGroup(resourceId);
|
||||
}
|
||||
private static EntryGroup searchEntryGroup(EntryBlock entryBlock, int resourceId){
|
||||
if(entryBlock==null){
|
||||
return null;
|
||||
}
|
||||
PackageBlock packageBlock=entryBlock.getPackageBlock();
|
||||
if(packageBlock==null){
|
||||
return null;
|
||||
}
|
||||
TableBlock tableBlock=packageBlock.getTableBlock();
|
||||
if(tableBlock==null){
|
||||
return null;
|
||||
}
|
||||
for(PackageBlock pkg:tableBlock.listPackages()){
|
||||
EntryGroup entryGroup=pkg.getEntryGroup(resourceId);
|
||||
if(entryGroup!=null){
|
||||
return entryGroup;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static EntryGroup searchEntryGroup(EntryStore store, PackageBlock packageBlock, int resourceId){
|
||||
if(packageBlock!=null){
|
||||
TableBlock tableBlock=packageBlock.getTableBlock();
|
||||
if(tableBlock!=null){
|
||||
for(PackageBlock pkg:tableBlock.listPackages()){
|
||||
EntryGroup entryGroup=pkg.getEntryGroup(resourceId);
|
||||
if(entryGroup!=null){
|
||||
return entryGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(store!=null){
|
||||
return store.getEntryGroup(resourceId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static String decodeIntEntryString(EntryBlock entryBlock, int data){
|
||||
if(entryBlock==null){
|
||||
return null;
|
||||
}
|
||||
PackageBlock packageBlock=entryBlock.getPackageBlock();
|
||||
if(packageBlock==null){
|
||||
return null;
|
||||
}
|
||||
TableBlock tableBlock=packageBlock.getTableBlock();
|
||||
if(tableBlock==null){
|
||||
return null;
|
||||
}
|
||||
TableStringPool pool = tableBlock.getTableStringPool();
|
||||
TableString tableString=pool.get(data);
|
||||
if(tableString==null){
|
||||
return null;
|
||||
}
|
||||
return tableString.getHtml();
|
||||
}
|
||||
private static String decodeString(EntryStore entryStore, int packageOrResourceId, int stringRef){
|
||||
if(entryStore==null||packageOrResourceId==0){
|
||||
return null;
|
||||
}
|
||||
int pkgId=(packageOrResourceId>>24)&0xFF;
|
||||
if(pkgId==0){
|
||||
pkgId=packageOrResourceId;
|
||||
}
|
||||
Collection<PackageBlock> allPkg = entryStore.getPackageBlocks((byte) pkgId);
|
||||
if(allPkg==null){
|
||||
return null;
|
||||
}
|
||||
TableString tableString=null;
|
||||
for(PackageBlock packageBlock:allPkg){
|
||||
TableBlock tableBlock=packageBlock.getTableBlock();
|
||||
if(tableBlock==null){
|
||||
continue;
|
||||
}
|
||||
TableString ts=tableBlock.getTableStringPool().get(stringRef);
|
||||
if(ts==null){
|
||||
continue;
|
||||
}
|
||||
if(tableString==null){
|
||||
tableString=ts;
|
||||
}else {
|
||||
// Duplicate result, could be from split apks
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if(tableString!=null){
|
||||
return tableString.getHtml();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static String decodeIntEntryString(PackageBlock packageBlock, int data){
|
||||
if(packageBlock==null){
|
||||
return null;
|
||||
}
|
||||
TableBlock tableBlock=packageBlock.getTableBlock();
|
||||
if(tableBlock==null){
|
||||
return null;
|
||||
}
|
||||
TableStringPool pool = tableBlock.getTableStringPool();
|
||||
TableString tableString=pool.get(data);
|
||||
if(tableString==null){
|
||||
return null;
|
||||
}
|
||||
return tableString.getHtml();
|
||||
}
|
||||
|
||||
private static String decodeHex(int rawVal){
|
||||
return String.format("0x%x", rawVal);
|
||||
}
|
||||
@ -99,6 +495,104 @@ public class ValueDecoder {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
private static String getResourceName(EntryStore store, EntryBlock entryBlock, int resourceId){
|
||||
if(entryBlock!=null){
|
||||
EntryGroup group=searchEntryGroup(entryBlock, resourceId);
|
||||
if(group!=null){
|
||||
String name=group.getSpecName();
|
||||
if(name!=null){
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(store==null){
|
||||
return null;
|
||||
}
|
||||
Collection<EntryGroup> foundGroups = store.getEntryGroups(resourceId);
|
||||
return pickResourceName(foundGroups);
|
||||
}
|
||||
private static String pickResourceName(Collection<EntryGroup> groups){
|
||||
if(groups==null){
|
||||
return null;
|
||||
}
|
||||
for(EntryGroup entryGroup:groups){
|
||||
String name=entryGroup.getSpecName();
|
||||
if(name!=null){
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static AttributeBag getAttributeBag(EntryStore store, int resourceId){
|
||||
ResValueBag resValueBag=getAttributeValueBag(store, resourceId);
|
||||
if(resValueBag==null){
|
||||
return null;
|
||||
}
|
||||
return AttributeBag.create(resValueBag);
|
||||
}
|
||||
private static ResValueBag getAttributeValueBag(EntryStore store, int resourceId){
|
||||
if(store==null){
|
||||
return null;
|
||||
}
|
||||
Collection<EntryGroup> foundGroups = store.getEntryGroups(resourceId);
|
||||
ResValueBag best=null;
|
||||
for(EntryGroup group:foundGroups){
|
||||
ResValueBag valueBag= getAttributeValueBag(group);
|
||||
best=chooseBest(best, valueBag);
|
||||
}
|
||||
return best;
|
||||
}
|
||||
private static ResValueBag getAttributeValueBag(EntryGroup entryGroup){
|
||||
if(entryGroup==null){
|
||||
return null;
|
||||
}
|
||||
ResValueBag best=null;
|
||||
Iterator<EntryBlock> iterator=entryGroup.iterator(true);
|
||||
while (iterator.hasNext()){
|
||||
EntryBlock entryBlock=iterator.next();
|
||||
ResValueBag valueBag= getAttributeValueBag(entryBlock);
|
||||
best=chooseBest(best, valueBag);
|
||||
}
|
||||
return best;
|
||||
}
|
||||
private static ResValueBag getAttributeValueBag(EntryBlock entryBlock){
|
||||
if(entryBlock==null){
|
||||
return null;
|
||||
}
|
||||
BaseResValue baseResValue = entryBlock.getResValue();
|
||||
if(baseResValue instanceof ResValueBag){
|
||||
return (ResValueBag) baseResValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static ResValueBag chooseBest(ResValueBag valueBag1, ResValueBag valueBag2){
|
||||
if(valueBag1==null){
|
||||
return valueBag2;
|
||||
}
|
||||
if(valueBag2==null){
|
||||
return valueBag1;
|
||||
}
|
||||
if(valueBag2.getCount()>valueBag1.getCount()){
|
||||
return valueBag2;
|
||||
}
|
||||
return valueBag1;
|
||||
}
|
||||
private static String toHexResourceId(int resourceId){
|
||||
return String.format("0x%08x", resourceId);
|
||||
}
|
||||
private static boolean isEqualString(String str1, String str2){
|
||||
if(isEmpty(str1)){
|
||||
return isEmpty(str2);
|
||||
}
|
||||
return str1.equals(str2);
|
||||
}
|
||||
private static boolean isEmpty(String str){
|
||||
if(str==null){
|
||||
return true;
|
||||
}
|
||||
str=str.trim();
|
||||
return str.length()==0;
|
||||
}
|
||||
|
||||
private static final String[] DIMENSION_UNIT_STRS = new String[] { "px", "dip", "sp", "pt", "in", "mm" };
|
||||
private static final float MANTISSA_MULT = 1.0f / (1 << 8);
|
||||
|
@ -1,139 +0,0 @@
|
||||
package com.reandroid.lib.arsc.decoder.xml;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlAttribute;
|
||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlBlock;
|
||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlElement;
|
||||
import com.reandroid.lib.arsc.chunk.xml.ResXmlText;
|
||||
import com.reandroid.lib.arsc.decoder.ResDecoder;
|
||||
import com.reandroid.lib.arsc.decoder.ResourceNameStore;
|
||||
import com.reandroid.lib.arsc.decoder.ValueDecoder;
|
||||
import com.reandroid.lib.arsc.value.ValueType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ResXmlDecoder extends ResDecoder<ResXmlBlock, String> {
|
||||
private byte mCurrentPackageId;
|
||||
public ResXmlDecoder(ResourceNameStore store){
|
||||
super(store);
|
||||
}
|
||||
private String decodeToString(ResXmlBlock resXmlBlock){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
builder.append("\n");
|
||||
String body=decodeToString(resXmlBlock.getResXmlElement());
|
||||
builder.append(body);
|
||||
return builder.toString();
|
||||
}
|
||||
private String decodeToString(ResXmlElement resXmlElement){
|
||||
if(resXmlElement==null){
|
||||
return null;
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
for(int i=0;i<resXmlElement.getDepth();i++){
|
||||
builder.append(' ');
|
||||
}
|
||||
builder.append('<');
|
||||
String tagName=resXmlElement.getTagName();
|
||||
builder.append(tagName);
|
||||
for(ResXmlAttribute attribute:resXmlElement.listResXmlAttributes()){
|
||||
builder.append(' ');
|
||||
String name=decodeAttributeFullName(attribute);
|
||||
builder.append(name);
|
||||
builder.append("=\"");
|
||||
String value=decodeAttributeValue(attribute);
|
||||
builder.append(value);
|
||||
builder.append("\"");
|
||||
}
|
||||
boolean useEndTag=false;
|
||||
ResXmlText resXmlText=resXmlElement.getResXmlText();
|
||||
String text=null;
|
||||
if(resXmlText!=null){
|
||||
useEndTag=true;
|
||||
text=resXmlText.getText();
|
||||
}
|
||||
List<ResXmlElement> childElements=resXmlElement.listElements();
|
||||
if(!useEndTag){
|
||||
useEndTag=childElements.size()>0;
|
||||
}
|
||||
if(!useEndTag){
|
||||
builder.append("/>");
|
||||
return builder.toString();
|
||||
}
|
||||
builder.append(">");
|
||||
if(text!=null){
|
||||
builder.append(text);
|
||||
}
|
||||
for(ResXmlElement child:childElements){
|
||||
builder.append("\n");
|
||||
String txtChild=decodeToString(child);
|
||||
builder.append(txtChild);
|
||||
}
|
||||
if(childElements.size()>0){
|
||||
builder.append("\n");
|
||||
for(int i=0;i<resXmlElement.getDepth();i++){
|
||||
builder.append(' ');
|
||||
}
|
||||
}
|
||||
builder.append("</");
|
||||
builder.append(tagName);
|
||||
builder.append(">");
|
||||
return builder.toString();
|
||||
}
|
||||
private String decodeAttributeValue(ResXmlAttribute attribute){
|
||||
ValueType valueType=attribute.getValueType();
|
||||
if(valueType==ValueType.FIRST_INT || valueType==ValueType.INT_HEX){
|
||||
int nameId=attribute.getNameResourceID();
|
||||
String val=decodeAttribute(nameId, attribute.getRawValue());
|
||||
if(val!=null){
|
||||
return val;
|
||||
}
|
||||
}
|
||||
if(valueType==ValueType.REFERENCE){
|
||||
int id=attribute.getRawValue();
|
||||
byte pkgId= (byte) ((id>>24)&0xFF);
|
||||
return getResourceFullName(id, pkgId!=getCurrentPackageId());
|
||||
}
|
||||
if(valueType==ValueType.STRING){
|
||||
return attribute.getValueString();
|
||||
}
|
||||
String val= ValueDecoder.decode(valueType, attribute.getRawValue());
|
||||
if(val!=null){
|
||||
return val;
|
||||
}
|
||||
return attribute.getRawValue()+"["+valueType+"]";
|
||||
}
|
||||
private String decodeAttributeFullName(ResXmlAttribute attribute){
|
||||
StringBuilder builder=new StringBuilder();
|
||||
String prefix=getRealAttributeNamePrefix(attribute);
|
||||
String name=getRealAttributeName(attribute);
|
||||
if(prefix!=null&&!name.contains(":")){
|
||||
builder.append(prefix);
|
||||
builder.append(':');
|
||||
}
|
||||
builder.append(name);
|
||||
return builder.toString();
|
||||
}
|
||||
private String getRealAttributeNamePrefix(ResXmlAttribute attribute){
|
||||
/* TODO readjust wrong/stripped attribute name prefix prefix; */
|
||||
return attribute.getNamePrefix();
|
||||
}
|
||||
private String getRealAttributeName(ResXmlAttribute attribute){
|
||||
int nameId=attribute.getNameResourceID();
|
||||
ResourceNameStore store=getResourceNameStore();
|
||||
byte pkgId= (byte) ((nameId>>24)&0xFF);
|
||||
String name=store.getResourceName(nameId, getCurrentPackageId()!=pkgId);
|
||||
if(name!=null){
|
||||
return name;
|
||||
}
|
||||
return attribute.getName();
|
||||
}
|
||||
private byte getCurrentPackageId(){
|
||||
return mCurrentPackageId;
|
||||
}
|
||||
@Override
|
||||
public String decode(byte currentPackageId, ResXmlBlock resXmlBlock) {
|
||||
this.mCurrentPackageId=currentPackageId;
|
||||
return decodeToString(resXmlBlock);
|
||||
}
|
||||
}
|
0
src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/group/EntryGroup.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/group/ItemGroup.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/group/ItemGroup.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/group/StringGroup.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/group/StringGroup.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/header/HeaderBlock.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/header/HeaderBlock.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/io/BlockLoad.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/io/BlockLoad.java
Normal file → Executable file
11
src/main/java/com/reandroid/lib/arsc/io/BlockReader.java
Normal file → Executable file
11
src/main/java/com/reandroid/lib/arsc/io/BlockReader.java
Normal file → Executable file
@ -324,4 +324,15 @@ public class BlockReader extends InputStream {
|
||||
System.arraycopy(arr2, 0, result, arr1.length, len);
|
||||
return result;
|
||||
}
|
||||
public static HeaderBlock readHeaderBlock(File file) throws IOException{
|
||||
InputStream inputStream=new FileInputStream(file);
|
||||
return readHeaderBlock(inputStream);
|
||||
}
|
||||
public static HeaderBlock readHeaderBlock(InputStream inputStream) throws IOException{
|
||||
byte[] buffer=new byte[8];
|
||||
inputStream.read(buffer);
|
||||
inputStream.close();
|
||||
BlockReader reader=new BlockReader(buffer);
|
||||
return reader.readHeaderBlock();
|
||||
}
|
||||
}
|
||||
|
0
src/main/java/com/reandroid/lib/arsc/item/BlockItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/BlockItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ByteArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ByteArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ByteItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ByteItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/IntegerArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/IntegerArray.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/IntegerItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/IntegerItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/PackageName.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/PackageName.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ReferenceItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ReferenceItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ResXmlID.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ResXmlString.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ResXmlString.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ShortItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/ShortItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/SpecString.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/SpecString.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/StringItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/StringItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/StyleItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/StyleItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/TableString.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/TableString.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/TypeString.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/item/TypeString.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/model/StyleSpanInfo.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/model/StyleSpanInfo.java
Normal file → Executable file
11
src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java
Normal file → Executable file
11
src/main/java/com/reandroid/lib/arsc/pool/BaseStringPool.java
Normal file → Executable file
@ -9,6 +9,7 @@ import com.reandroid.lib.arsc.group.StringGroup;
|
||||
import com.reandroid.lib.arsc.io.BlockLoad;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.*;
|
||||
import com.reandroid.lib.arsc.pool.builder.StyleBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
@ -63,6 +64,16 @@ public abstract class BaseStringPool<T extends StringItem> extends BaseChunk imp
|
||||
mUniqueMap=new HashMap<>();
|
||||
|
||||
}
|
||||
public void recreateStyles(){
|
||||
StyleArray styleArray = getStyleArray();
|
||||
//styleArray.clearChildes();
|
||||
StringArray<T> stringArray=getStringsArray();
|
||||
for(T stringItem:stringArray.listItems()){
|
||||
if(StyleBuilder.hasStyle(stringItem)){
|
||||
StyleBuilder.buildStyle(stringItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
public List<T> removeUnusedStrings(){
|
||||
return getStringsArray().removeUnusedStrings();
|
||||
}
|
||||
|
0
src/main/java/com/reandroid/lib/arsc/pool/PoolType.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/pool/PoolType.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/pool/ResXmlStringPool.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/pool/ResXmlStringPool.java
Normal file → Executable file
3
src/main/java/com/reandroid/lib/arsc/pool/SpecStringPool.java
Normal file → Executable file
3
src/main/java/com/reandroid/lib/arsc/pool/SpecStringPool.java
Normal file → Executable file
@ -15,4 +15,7 @@ public class SpecStringPool extends BaseStringPool<SpecString> {
|
||||
StringArray<SpecString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
return new SpecStringArray(offsets, itemCount, itemStart, is_utf8);
|
||||
}
|
||||
@Override
|
||||
public void recreateStyles(){
|
||||
}
|
||||
}
|
||||
|
0
src/main/java/com/reandroid/lib/arsc/pool/TableStringPool.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/pool/TableStringPool.java
Normal file → Executable file
3
src/main/java/com/reandroid/lib/arsc/pool/TypeStringPool.java
Normal file → Executable file
3
src/main/java/com/reandroid/lib/arsc/pool/TypeStringPool.java
Normal file → Executable file
@ -17,4 +17,7 @@ public class TypeStringPool extends BaseStringPool<TypeString> {
|
||||
StringArray<TypeString> newInstance(IntegerArray offsets, IntegerItem itemCount, IntegerItem itemStart, boolean is_utf8) {
|
||||
return new TypeStringArray(offsets, itemCount, itemStart, is_utf8);
|
||||
}
|
||||
@Override
|
||||
public void recreateStyles(){
|
||||
}
|
||||
}
|
||||
|
16
src/main/java/com/reandroid/lib/arsc/pool/builder/SpannedText.java
Executable file
16
src/main/java/com/reandroid/lib/arsc/pool/builder/SpannedText.java
Executable file
@ -0,0 +1,16 @@
|
||||
package com.reandroid.lib.arsc.pool.builder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SpannedText {
|
||||
private String mLeftText;
|
||||
private String mTag;
|
||||
private String mText;
|
||||
private String mRightText;
|
||||
private List<SpannedText> mChildes;
|
||||
public SpannedText(){
|
||||
}
|
||||
public void parse(int start, String text){
|
||||
int i=text.indexOf('<', start);
|
||||
}
|
||||
}
|
29
src/main/java/com/reandroid/lib/arsc/pool/builder/StyleBuilder.java
Executable file
29
src/main/java/com/reandroid/lib/arsc/pool/builder/StyleBuilder.java
Executable file
@ -0,0 +1,29 @@
|
||||
package com.reandroid.lib.arsc.pool.builder;
|
||||
|
||||
import com.reandroid.lib.arsc.item.StringItem;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class StyleBuilder {
|
||||
public static void buildStyle(StringItem stringItem){
|
||||
System.out.println(stringItem.toString());
|
||||
}
|
||||
public static boolean hasStyle(StringItem stringItem){
|
||||
if(stringItem==null){
|
||||
return false;
|
||||
}
|
||||
return hasStyle(stringItem.getHtml());
|
||||
}
|
||||
public static boolean hasStyle(String text){
|
||||
if(text==null){
|
||||
return false;
|
||||
}
|
||||
int i=text.indexOf('<');
|
||||
if(i<0){
|
||||
return false;
|
||||
}
|
||||
i=text.indexOf('>');
|
||||
return i>1;
|
||||
}
|
||||
private static final Pattern PATTERN_STYLE=Pattern.compile("");
|
||||
}
|
151
src/main/java/com/reandroid/lib/arsc/util/FrameworkTable.java
Normal file → Executable file
151
src/main/java/com/reandroid/lib/arsc/util/FrameworkTable.java
Normal file → Executable file
@ -1,9 +1,11 @@
|
||||
package com.reandroid.lib.arsc.util;
|
||||
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
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.group.EntryGroup;
|
||||
import com.reandroid.lib.arsc.header.HeaderBlock;
|
||||
import com.reandroid.lib.arsc.io.BlockReader;
|
||||
import com.reandroid.lib.arsc.item.ReferenceItem;
|
||||
import com.reandroid.lib.arsc.item.TableString;
|
||||
@ -15,9 +17,42 @@ import java.util.*;
|
||||
|
||||
public class FrameworkTable extends TableBlock {
|
||||
|
||||
private String mFrameworkTitle;
|
||||
private String mFrameworkName;
|
||||
private String mFrameworkVersion;
|
||||
public FrameworkTable(){
|
||||
super();
|
||||
}
|
||||
public String getFrameworkTitle(){
|
||||
if(mFrameworkTitle==null){
|
||||
mFrameworkTitle=loadProperty(PROP_TITLE);
|
||||
}
|
||||
return mFrameworkTitle;
|
||||
}
|
||||
public String getFrameworkName(){
|
||||
if(mFrameworkName==null){
|
||||
mFrameworkName=loadProperty(PROP_NAME);
|
||||
}
|
||||
return mFrameworkName;
|
||||
}
|
||||
public String getFrameworkVersion(){
|
||||
if(mFrameworkVersion==null){
|
||||
mFrameworkVersion=loadProperty(PROP_VERSION);
|
||||
}
|
||||
return mFrameworkVersion;
|
||||
}
|
||||
private void setFrameworkTitle(String value){
|
||||
mFrameworkTitle=null;
|
||||
writeProperty(PROP_TITLE, value);
|
||||
}
|
||||
public void setFrameworkName(String value){
|
||||
mFrameworkName=null;
|
||||
writeProperty(PROP_NAME, value);
|
||||
}
|
||||
public void setFrameworkVersion(String value){
|
||||
mFrameworkVersion=null;
|
||||
writeProperty(PROP_VERSION, value);
|
||||
}
|
||||
public int writeTable(File resourcesArscFile) throws IOException{
|
||||
File dir=resourcesArscFile.getParentFile();
|
||||
if(dir!=null && !dir.exists()){
|
||||
@ -49,7 +84,7 @@ public class FrameworkTable extends TableBlock {
|
||||
super.onReadBytes(reader);
|
||||
reader.close();
|
||||
}
|
||||
public void optimize(){
|
||||
public void optimize(String frameworkName, String frameworkVersion){
|
||||
Map<Integer, EntryGroup> groupMap=scanAllEntryGroups();
|
||||
for(EntryGroup group:groupMap.values()){
|
||||
List<EntryBlock> entryBlockList=getEntriesToRemove(group);
|
||||
@ -60,6 +95,9 @@ public class FrameworkTable extends TableBlock {
|
||||
pkg.refresh();
|
||||
}
|
||||
optimizeTableString();
|
||||
setFrameworkTitle(TITLE_STRING);
|
||||
setFrameworkName(frameworkName);
|
||||
setFrameworkVersion(frameworkVersion);
|
||||
refresh();
|
||||
}
|
||||
private void optimizeTableString(){
|
||||
@ -75,13 +113,14 @@ public class FrameworkTable extends TableBlock {
|
||||
}
|
||||
private void shrinkTableString(){
|
||||
TableStringPool tableStringPool=getTableStringPool();
|
||||
TableString zero=tableStringPool.get(0);
|
||||
zero.set("Framework string table");
|
||||
tableStringPool.getStringsArray().ensureSize(1);
|
||||
TableString title=tableStringPool.get(0);
|
||||
title.set(PROP_TITLE+":"+TITLE_STRING);
|
||||
for(TableString tableString:tableStringPool.getStringsArray().listItems()){
|
||||
if(tableString==zero){
|
||||
if(tableString==title){
|
||||
continue;
|
||||
}
|
||||
shrinkTableString(zero, tableString);
|
||||
shrinkTableString(title, tableString);
|
||||
}
|
||||
tableStringPool.refresh();
|
||||
}
|
||||
@ -141,4 +180,106 @@ public class FrameworkTable extends TableBlock {
|
||||
}
|
||||
return results;
|
||||
}
|
||||
private TableString writeProperty(String name, String value){
|
||||
if(!name.endsWith(":")){
|
||||
name=name+":";
|
||||
}
|
||||
if(value==null){
|
||||
value="";
|
||||
}
|
||||
if(!value.startsWith(name)){
|
||||
value=name+value;
|
||||
}
|
||||
TableString tableString=loadPropertyString(name);
|
||||
if(tableString!=null){
|
||||
tableString.set(value);
|
||||
}else {
|
||||
TableStringPool tableStringPool=getTableStringPool();
|
||||
tableString=tableStringPool.getOrCreate(value);
|
||||
}
|
||||
return tableString;
|
||||
}
|
||||
private String loadProperty(String name){
|
||||
if(name==null){
|
||||
return null;
|
||||
}
|
||||
if(!name.endsWith(":")){
|
||||
name=name+":";
|
||||
}
|
||||
TableString tableString=loadPropertyString(name);
|
||||
if(tableString==null){
|
||||
return null;
|
||||
}
|
||||
String str=tableString.get().trim();
|
||||
return str.substring(name.length());
|
||||
}
|
||||
private TableString loadPropertyString(String name){
|
||||
if(name==null){
|
||||
return null;
|
||||
}
|
||||
if(!name.endsWith(":")){
|
||||
name=name+":";
|
||||
}
|
||||
TableStringPool tableStringPool=getTableStringPool();
|
||||
int max=PROP_COUNT;
|
||||
for(int i=0;i<max;i++){
|
||||
TableString tableString=tableStringPool.get(i);
|
||||
if(tableString==null){
|
||||
break;
|
||||
}
|
||||
String str=tableString.get();
|
||||
if(str==null){
|
||||
continue;
|
||||
}
|
||||
str=str.trim();
|
||||
if(str.startsWith(name)){
|
||||
return tableString;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
HeaderBlock headerBlock=getHeaderBlock();
|
||||
if(headerBlock.getChunkType()!= ChunkType.TABLE){
|
||||
return super.toString();
|
||||
}
|
||||
StringBuilder builder=new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(": SIZE=").append(headerBlock.getChunkSize());
|
||||
String str=getFrameworkTitle();
|
||||
builder.append("\n");
|
||||
if(str==null){
|
||||
builder.append(PROP_TITLE).append(":null");
|
||||
}else {
|
||||
builder.append(str);
|
||||
}
|
||||
str=getFrameworkName();
|
||||
builder.append("\n ").append(PROP_NAME).append(":");
|
||||
if(str==null){
|
||||
builder.append("null");
|
||||
}else {
|
||||
builder.append(str);
|
||||
}
|
||||
str=getFrameworkVersion();
|
||||
builder.append("\n ").append(PROP_VERSION).append(":");
|
||||
if(str==null){
|
||||
builder.append("null");
|
||||
}else {
|
||||
builder.append(str);
|
||||
}
|
||||
Collection<PackageBlock> allPkg = listPackages();
|
||||
builder.append("\n PACKAGES=").append(allPkg.size());
|
||||
for(PackageBlock packageBlock:allPkg){
|
||||
builder.append("\n ");
|
||||
builder.append(String.format("0x%02x", packageBlock.getPackageId()));
|
||||
builder.append(":").append(packageBlock.getPackageName());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
private static final String TITLE_STRING="Framework table";
|
||||
private static final String PROP_TITLE="TITLE";
|
||||
private static final String PROP_NAME="NAME";
|
||||
private static final String PROP_VERSION="VERSION";
|
||||
private static final int PROP_COUNT=10;
|
||||
}
|
||||
|
0
src/main/java/com/reandroid/lib/arsc/value/BaseResValue.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/BaseResValue.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/BaseResValueItem.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/EntryBlock.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/LibraryInfo.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/LibraryInfo.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/ResConfig.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/ResConfig.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/ResConfigHelper.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/ResConfigHelper.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java
Normal file → Executable file
0
src/main/java/com/reandroid/lib/arsc/value/ResValueBag.java
Normal file → Executable file
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user